-
Hi. I just started using this library, and so far it seems pretty awesome. However, I'm currently stuck! For context, I'm trying to see if I can port a program called NXBT to Rust. NXBT makes your bluetooth adapter act like a Nintendo Switch compatible controller. Nintendo Switch controllers use BR/EDR over two L2CAP SeqPacket sockets: the control channel for device status / info, and the interrupt channel for actual data communication (see: server.py). I started with the L2CAP server example, and now have this: //! Opens a listening L2CAP socket, accepts connections and echos incoming data.
use bluer::l2cap::{SeqPacketListener, SocketAddr};
use std::time::Duration;
use tokio::{
io::{AsyncBufReadExt, BufReader},
time::sleep,
};
const PSM_ITR: u16 = 19;
const PSM_CTL: u16 = 17;
const HELLO_MSG: &[u8] = b"\xa1\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
#[tokio::main]
async fn main() -> bluer::Result<()> {
env_logger::init();
let session = bluer::Session::new().await?;
let adapter = session.default_adapter().await?;
let adapter_addr = adapter.address().await?;
let adapter_addr_type = adapter.address_type().await?;
let interrupt_socket = SocketAddr::new(adapter_addr, adapter_addr_type, PSM_ITR);
let control_socket = SocketAddr::new(adapter_addr, adapter_addr_type, PSM_CTL);
let interrupt_listener = SeqPacketListener::bind(interrupt_socket).await?;
let control_listener = SeqPacketListener::bind(control_socket).await?;
println!("Listening on PSM {}. Press enter to quit.", interrupt_listener.as_ref().local_addr()?.psm);
let stdin = BufReader::new(tokio::io::stdin());
let mut lines = stdin.lines();
loop {
println!("\nWaiting for connection...");
let (mut stream, sa) = tokio::select! {
l = interrupt_listener.accept() => {
println!("Interrupt Channel Connection Accepted");
match l {
Ok(v) => v,
Err(err) => {
println!("Accepting connection failed: {}", &err);
continue;
}}
},
_ = lines.next_line() => break,
};
println!("Waiting for connection to control channel");
tokio::select! {
_ = control_listener.accept() => (),
_ = lines.next_line() => break,
};
println!("About to receive");
let recv_mtu = stream.as_ref().recv_mtu()?;
println!("Accepted connection from {:?} with receive MTU {} bytes", &sa, &recv_mtu);
println!("Sending hello");
if let Err(err) = stream.send(HELLO_MSG).await {
println!("Write failed: {}", &err);
continue;
}
loop {
let buf_size = 1024;
let mut buf = vec![0; buf_size as _];
let n = match stream.recv(&mut buf).await {
Ok(0) => {
println!("Switch reponded with no data");
continue;
}
Ok(n) => n,
Err(err) => {
println!("Read failed: {}", &err);
continue;
}
};
println!("Switch says:\n{:?}\nwhich is {n} bytes long", buf);
}
}
sleep(Duration::from_secs(1)).await;
Ok(())
} When testing it, I'm running NXBT with a breakpoint just before the socket setup, then running my Rust program. The idea being that NXBT can set everything up, and my program just listens on the sockets. This worked when I ran a separate python program. When I run the above code after NXBT, the Switch does actually show a notification for a new controller, so you'd think it would accept the connection and begin exchanging data, but it is stuck in the first tokio::select, and never prints the "Interrupt Channel Connection Accepted" message. Any ideas? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
Aha, I got it! Of course I figure out right after posting my question 😃. Instead of: let adapter_addr_type = adapter.address_type().await?; which for me returns let adapter_addr_type = AddressType::BrEdr; and it worked! |
Beta Was this translation helpful? Give feedback.
-
Also note that you are immediately closing the control channel after accepting it. For it to stay open, you must assign the result to a variable, like you did with the interrupt listener. |
Beta Was this translation helpful? Give feedback.
Aha, I got it! Of course I figure out right after posting my question 😃.
Instead of:
which for me returns
AddressType::LePublic
, I specified the address type directly:and it worked!