//! Classic ACL manager
-use bt_hci::{Address, CommandSender};
+use bt_hci::{Address, CommandSender, EventRegistry};
+use bt_packets::hci::EventChild::ConnectionComplete;
use bt_packets::hci::{
ClockOffsetValid, CreateConnectionBuilder, CreateConnectionCancelBuilder,
- CreateConnectionRoleSwitch, PageScanRepetitionMode,
+ CreateConnectionRoleSwitch, ErrorCode, EventCode, PageScanRepetitionMode, Role,
};
use gddi::{module, provides, Stoppable};
+use log::warn;
+use std::collections::HashMap;
use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio::select;
-use tokio::sync::mpsc::{channel, Sender};
-use tokio::sync::oneshot;
+use tokio::sync::mpsc::{channel, Receiver, Sender};
+use tokio::sync::{oneshot, Mutex};
module! {
classic_acl_module,
#[derive(Clone, Stoppable)]
pub struct AclManager {
req_tx: Sender<Request>,
+ /// High level events from AclManager
+ pub evt_rx: Arc<Mutex<Receiver<Event>>>,
+}
+
+/// Events generated by AclManager
+#[derive(Debug)]
+pub enum Event {
+ /// Connection was successful - provides the newly created connection
+ ConnectSuccess(Connection),
+ /// Locally initialted connection was not successful - indicates address & reason
+ ConnectFail {
+ /// Address of the failed connection
+ addr: Address,
+ /// Reason of the failed connection
+ reason: ErrorCode,
+ },
+}
+
+/// A classic ACL connection
+#[derive(Debug)]
+pub struct Connection {
+ addr: Address,
+ shared: Arc<Mutex<ConnectionShared>>,
+}
+
+struct ConnectionInternal {
+ addr: Address,
+ #[allow(dead_code)]
+ shared: Arc<Mutex<ConnectionShared>>,
+}
+
+#[derive(Debug)]
+struct ConnectionShared {
+ role: Role,
}
impl AclManager {
CancelConnect { addr: Address, fut: oneshot::Sender<()> },
}
+#[derive(Eq, PartialEq)]
+enum PendingConnect {
+ Outgoing(Address),
+ #[allow(dead_code)]
+ Incoming(Address),
+ None,
+}
+
+impl PendingConnect {
+ fn take(&mut self) -> Self {
+ std::mem::replace(self, PendingConnect::None)
+ }
+}
+
#[provides]
-async fn provide_acl_manager(mut hci: CommandSender, rt: Arc<Runtime>) -> AclManager {
+async fn provide_acl_manager(
+ mut hci: CommandSender,
+ mut events: EventRegistry,
+ rt: Arc<Runtime>,
+) -> AclManager {
let (req_tx, mut req_rx) = channel::<Request>(10);
+ let (conn_evt_tx, conn_evt_rx) = channel::<Event>(10);
rt.spawn(async move {
- let mut pending_connects: Vec<Address> = Vec::new();
- let mut outgoing_connect: Option<Address> = None;
+ let mut connections: HashMap<u16, ConnectionInternal> = HashMap::new();
+ let mut connect_queue: Vec<Address> = Vec::new();
+ let mut pending = PendingConnect::None;
+
+ let (evt_tx, mut evt_rx) = channel(3);
+ events.register(EventCode::ConnectionComplete, evt_tx).await;
+
loop {
select! {
Some(req) = req_rx.recv() => {
match req {
Request::Connect { addr } => {
- if outgoing_connect.is_none() {
- outgoing_connect = Some(addr);
+ if connections.values().any(|c| c.addr == addr) {
+ warn!("already connected: {}", addr);
+ }
+ if let PendingConnect::None = pending {
+ pending = PendingConnect::Outgoing(addr);
hci.send(build_create_connection(addr)).await;
} else {
- pending_connects.insert(0, addr);
+ connect_queue.insert(0, addr);
}
},
Request::CancelConnect { addr, fut } => {
- pending_connects.retain(|p| *p != addr);
- if outgoing_connect == Some(addr) {
+ connect_queue.retain(|p| *p != addr);
+ if pending == PendingConnect::Outgoing(addr) {
hci.send(CreateConnectionCancelBuilder { bd_addr: addr }).await;
}
fut.send(()).unwrap();
}
}
}
+ Some(evt) = evt_rx.recv() => {
+ match evt.specialize() {
+ ConnectionComplete(evt) => {
+ let addr = evt.get_bd_addr();
+ let status = evt.get_status();
+ let handle = evt.get_connection_handle();
+ let role = match pending.take() {
+ PendingConnect::Outgoing(a) if a == addr => Role::Central,
+ PendingConnect::Incoming(a) if a == addr => Role::Peripheral,
+ _ => panic!("No prior connection request for {}", addr),
+ };
+
+ match status {
+ ErrorCode::Success => {
+ let shared = Arc::new(Mutex::new(ConnectionShared { role }));
+ assert!(connections.insert(
+ handle,
+ ConnectionInternal { addr, shared: shared.clone() }
+ ).is_none());
+
+ conn_evt_tx.send(Event::ConnectSuccess(Connection {
+ addr,
+ shared
+ })).await.unwrap();
+ },
+ _ => conn_evt_tx.send(Event::ConnectFail { addr, reason: status }).await.unwrap(),
+ }
+ },
+ _ => unimplemented!(),
+ }
+ }
}
}
});
- AclManager { req_tx }
+ AclManager { req_tx, evt_rx: Arc::new(Mutex::new(conn_evt_rx)) }
}
fn build_create_connection(bd_addr: Address) -> CreateConnectionBuilder {