diff --git a/examples/apps/src/ble_advertise_multiple.rs b/examples/apps/src/ble_advertise_multiple.rs index 36cef61b..874ab6e9 100644 --- a/examples/apps/src/ble_advertise_multiple.rs +++ b/examples/apps/src/ble_advertise_multiple.rs @@ -24,11 +24,9 @@ where let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); info!("Our address = {:?}", address); - let mut resources: HostResources = - HostResources::new(PacketQos::None); - let (_, mut peripheral, _, mut runner) = trouble_host::new(controller, &mut resources) - .set_random_address(address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let (mut peripheral, _, mut runner) = stack.build(); let mut adv_data = [0; 31]; let len = AdStructure::encode_slice( diff --git a/examples/apps/src/ble_bas_central.rs b/examples/apps/src/ble_bas_central.rs index 6b1c2e3e..2b260563 100644 --- a/examples/apps/src/ble_bas_central.rs +++ b/examples/apps/src/ble_bas_central.rs @@ -17,11 +17,9 @@ where let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); info!("Our address = {:?}", address); - let mut resources: HostResources = - HostResources::new(PacketQos::None); - let (stack, _, mut central, mut runner) = trouble_host::new(controller, &mut resources) - .set_random_address(address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let (_, mut central, mut runner) = stack.build(); // NOTE: Modify this to match the address of the peripheral you want to connect to. // Currently it matches the address used by the peripheral examples @@ -42,7 +40,7 @@ where let conn = central.connect(&config).await.unwrap(); info!("Connected, creating gatt client"); - let client = GattClient::::new(stack, &conn).await.unwrap(); + let client = GattClient::::new(&stack, &conn).await.unwrap(); let _ = join(client.task(), async { info!("Looking for battery service"); diff --git a/examples/apps/src/ble_bas_peripheral.rs b/examples/apps/src/ble_bas_peripheral.rs index 366db80b..fab0cd0c 100644 --- a/examples/apps/src/ble_bas_peripheral.rs +++ b/examples/apps/src/ble_bas_peripheral.rs @@ -38,11 +38,9 @@ where let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); info!("Our address = {:?}", address); - let mut resources: HostResources = - HostResources::new(PacketQos::None); - let (stack, mut peripheral, _, runner) = trouble_host::new(controller, &mut resources) - .set_random_address(address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let (mut peripheral, _, runner) = stack.build(); info!("Starting advertising and GATT service"); let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig { @@ -57,7 +55,7 @@ where Ok(conn) => { // set up tasks when the connection is established to a central, so they don't run when no one is connected. let a = gatt_events_task(&server, &conn); - let b = custom_task(&server, &conn, stack); + let b = custom_task(&server, &conn, &stack); // run until any task ends (usually because the connection has been closed), // then return to advertising state. select(a, b).await; @@ -176,7 +174,7 @@ async fn advertise<'a, C: Controller>( /// This task will notify the connected central of a counter value every 2 seconds. /// It will also read the RSSI value every 2 seconds. /// and will stop when the connection is closed by the central or an error occurs. -async fn custom_task(server: &Server<'_>, conn: &Connection<'_>, stack: Stack<'_, C>) { +async fn custom_task(server: &Server<'_>, conn: &Connection<'_>, stack: &Stack<'_, C>) { let mut tick: u8 = 0; let level = server.battery_service.level; loop { diff --git a/examples/apps/src/ble_l2cap_central.rs b/examples/apps/src/ble_l2cap_central.rs index fa1caf06..96e8b47c 100644 --- a/examples/apps/src/ble_l2cap_central.rs +++ b/examples/apps/src/ble_l2cap_central.rs @@ -17,12 +17,9 @@ where let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); info!("Our address = {:?}", address); - let mut resources: HostResources = - HostResources::new(PacketQos::None); - - let (stack, _, mut central, mut runner) = trouble_host::new(controller, &mut resources) - .set_random_address(address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let (_, mut central, mut runner) = stack.build(); // NOTE: Modify this to match the address of the peripheral you want to connect to. // Currently it matches the address used by the peripheral examples @@ -42,18 +39,18 @@ where let conn = central.connect(&config).await.unwrap(); info!("Connected, creating l2cap channel"); const PAYLOAD_LEN: usize = 27; - let mut ch1 = L2capChannel::create(stack, &conn, 0x2349, &Default::default()) + let mut ch1 = L2capChannel::create(&stack, &conn, 0x2349, &Default::default()) .await .unwrap(); info!("New l2cap channel created, sending some data!"); for i in 0..10 { let tx = [i; PAYLOAD_LEN]; - ch1.send::<_, PAYLOAD_LEN>(stack, &tx).await.unwrap(); + ch1.send::<_, PAYLOAD_LEN>(&stack, &tx).await.unwrap(); } info!("Sent data, waiting for them to be sent back"); let mut rx = [0; PAYLOAD_LEN]; for i in 0..10 { - let len = ch1.receive(stack, &mut rx).await.unwrap(); + let len = ch1.receive(&stack, &mut rx).await.unwrap(); assert_eq!(len, rx.len()); assert_eq!(rx, [i; PAYLOAD_LEN]); } diff --git a/examples/apps/src/ble_l2cap_peripheral.rs b/examples/apps/src/ble_l2cap_peripheral.rs index f66389c7..483f5ce2 100644 --- a/examples/apps/src/ble_l2cap_peripheral.rs +++ b/examples/apps/src/ble_l2cap_peripheral.rs @@ -12,16 +12,13 @@ pub async fn run(controller: C) where C: Controller, { - let mut resources: HostResources = - HostResources::new(PacketQos::None); - // Hardcoded peripheral address let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); info!("Our address = {:?}", address); - let (stack, mut peripheral, _, mut runner) = trouble_host::new(controller, &mut resources) - .set_random_address(address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let (mut peripheral, _, mut runner) = stack.build(); let mut adv_data = [0; 31]; AdStructure::encode_slice( @@ -50,7 +47,7 @@ where info!("Connection established"); - let mut ch1 = L2capChannel::accept(stack, &conn, &[0x2349], &Default::default()) + let mut ch1 = L2capChannel::accept(&stack, &conn, &[0x2349], &Default::default()) .await .unwrap(); @@ -60,7 +57,7 @@ where const PAYLOAD_LEN: usize = 27; let mut rx = [0; PAYLOAD_LEN]; for i in 0..10 { - let len = ch1.receive(stack, &mut rx).await.unwrap(); + let len = ch1.receive(&stack, &mut rx).await.unwrap(); assert_eq!(len, rx.len()); assert_eq!(rx, [i; PAYLOAD_LEN]); } @@ -69,7 +66,7 @@ where Timer::after(Duration::from_secs(1)).await; for i in 0..10 { let tx = [i; PAYLOAD_LEN]; - ch1.send::<_, PAYLOAD_LEN>(stack, &tx).await.unwrap(); + ch1.send::<_, PAYLOAD_LEN>(&stack, &tx).await.unwrap(); } info!("L2CAP data echoed"); diff --git a/examples/tests/tests/ble_l2cap_central.rs b/examples/tests/tests/ble_l2cap_central.rs index a1a01cc6..c734acc7 100644 --- a/examples/tests/tests/ble_l2cap_central.rs +++ b/examples/tests/tests/ble_l2cap_central.rs @@ -48,10 +48,9 @@ async fn run_l2cap_central_test(labels: &[(&str, &str)], firmware: &str) { let peripheral = tokio::task::spawn_local(async move { let controller_peripheral = serial::create_controller(&peripheral).await; - let mut resources: HostResources = HostResources::new(PacketQos::None); - let (stack, mut peripheral, _central, mut runner) = trouble_host::new(controller_peripheral, &mut resources) - .set_random_address(peripheral_address) - .build(); + let mut resources: HostResources<2, 4, 27> = HostResources::new(); + let stack = trouble_host::new(controller_peripheral, &mut resources).set_random_address(peripheral_address); + let (mut peripheral, _central, mut runner) = stack.build(); select! { r = runner.run() => { @@ -79,7 +78,7 @@ async fn run_l2cap_central_test(labels: &[(&str, &str)], firmware: &str) { let conn = acceptor.accept().await?; println!("[peripheral] connected"); - let mut ch1 = L2capChannel::accept(stack, &conn, &[0x2349], &Default::default()).await?; + let mut ch1 = L2capChannel::accept(&stack, &conn, &[0x2349], &Default::default()).await?; println!("[peripheral] channel created"); @@ -87,7 +86,7 @@ async fn run_l2cap_central_test(labels: &[(&str, &str)], firmware: &str) { // Size of payload we're expecting let mut rx = [0; PAYLOAD_LEN]; for i in 0..10 { - let len = ch1.receive(stack, &mut rx).await?; + let len = ch1.receive(&stack, &mut rx).await?; assert_eq!(len, rx.len()); assert_eq!(rx, [i; PAYLOAD_LEN]); } @@ -95,7 +94,7 @@ async fn run_l2cap_central_test(labels: &[(&str, &str)], firmware: &str) { for i in 0..10 { let tx = [i; PAYLOAD_LEN]; - ch1.send::<_, PAYLOAD_LEN>(stack, &tx).await?; + ch1.send::<_, PAYLOAD_LEN>(&stack, &tx).await?; } println!("[peripheral] data sent"); token.cancel(); diff --git a/examples/tests/tests/ble_l2cap_peripheral.rs b/examples/tests/tests/ble_l2cap_peripheral.rs index 726947dc..05fede83 100644 --- a/examples/tests/tests/ble_l2cap_peripheral.rs +++ b/examples/tests/tests/ble_l2cap_peripheral.rs @@ -45,9 +45,9 @@ async fn run_l2cap_peripheral_test(labels: &[(&str, &str)], firmware: &str) { let peripheral_address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); let central = tokio::task::spawn_local(async move { let controller_central = serial::create_controller(¢ral).await; - let mut resources: HostResources = HostResources::new(PacketQos::None); - let (stack, _peripheral, mut central, mut runner) = - trouble_host::new(controller_central, &mut resources).build(); + let mut resources: HostResources<2, 4, 27> = HostResources::new(); + let stack = trouble_host::new(controller_central, &mut resources); + let (_peripheral, mut central, mut runner) = stack.build(); select! { r = runner.run() => { r @@ -67,16 +67,16 @@ async fn run_l2cap_peripheral_test(labels: &[(&str, &str)], firmware: &str) { let conn = central.connect(&config).await.unwrap(); log::info!("[central] connected"); const PAYLOAD_LEN: usize = 27; - let mut ch1 = L2capChannel::create(stack, &conn, 0x2349, &Default::default()).await?; + let mut ch1 = L2capChannel::create(&stack, &conn, 0x2349, &Default::default()).await?; log::info!("[central] channel created"); for i in 0..10 { let tx = [i; PAYLOAD_LEN]; - ch1.send::<_, PAYLOAD_LEN>(stack, &tx).await?; + ch1.send::<_, PAYLOAD_LEN>(&stack, &tx).await?; } log::info!("[central] data sent"); let mut rx = [0; PAYLOAD_LEN]; for i in 0..10 { - let len = ch1.receive(stack, &mut rx).await?; + let len = ch1.receive(&stack, &mut rx).await?; assert_eq!(len, rx.len()); assert_eq!(rx, [i; PAYLOAD_LEN]); } diff --git a/host/src/central.rs b/host/src/central.rs index f3249694..446c1ce0 100644 --- a/host/src/central.rs +++ b/host/src/central.rs @@ -1,5 +1,5 @@ //! Functionality for the BLE central role. -use crate::connection::{ConnectConfig, Connection}; +use crate::connection::{ConnectConfig, Connection, PhySet}; use crate::{BleHostError, Error, Stack}; use bt_hci::cmd::le::{LeAddDeviceToFilterAcceptList, LeClearFilterAcceptList, LeCreateConn, LeExtCreateConn}; use bt_hci::controller::{Controller, ControllerCmdAsync, ControllerCmdSync}; @@ -7,20 +7,19 @@ use bt_hci::param::{AddrKind, BdAddr, InitiatingPhy, LeConnRole, PhyParams}; #[cfg(feature = "controller-host-flow-control")] use bt_hci::param::{ConnHandleCompletedPackets, ControllerToHostFlowControl}; use embassy_futures::select::{select, Either}; -use embassy_time::Duration; /// A type implementing the BLE central role. -pub struct Central<'d, C: Controller> { - pub(crate) stack: Stack<'d, C>, +pub struct Central<'stack, C: Controller> { + pub(crate) stack: &'stack Stack<'stack, C>, } -impl<'d, C: Controller> Central<'d, C> { - pub(crate) fn new(stack: Stack<'d, C>) -> Self { +impl<'stack, C: Controller> Central<'stack, C> { + pub(crate) fn new(stack: &'stack Stack<'stack, C>) -> Self { Self { stack } } /// Attempt to create a connection with the provided config. - pub async fn connect(&mut self, config: &ConnectConfig<'_>) -> Result, BleHostError> + pub async fn connect(&mut self, config: &ConnectConfig<'_>) -> Result, BleHostError> where C: ControllerCmdSync + ControllerCmdSync @@ -30,7 +29,7 @@ impl<'d, C: Controller> Central<'d, C> { return Err(Error::InvalidValue.into()); } - let host = self.stack.host; + let host = &self.stack.host; let _drop = crate::host::OnDrop::new(|| { host.connect_command_state.cancel(true); }); @@ -70,7 +69,10 @@ impl<'d, C: Controller> Central<'d, C> { } /// Attempt to create a connection with the provided config. - pub async fn connect_ext(&mut self, config: &ConnectConfig<'_>) -> Result, BleHostError> + pub async fn connect_ext( + &mut self, + config: &ConnectConfig<'_>, + ) -> Result, BleHostError> where C: ControllerCmdSync + ControllerCmdSync @@ -80,7 +82,7 @@ impl<'d, C: Controller> Central<'d, C> { return Err(Error::InvalidValue.into()); } - let host = self.stack.host; + let host = &self.stack.host; // Ensure no other connect ongoing. let _drop = crate::host::OnDrop::new(|| { host.connect_command_state.cancel(true); @@ -133,7 +135,7 @@ impl<'d, C: Controller> Central<'d, C> { where C: ControllerCmdSync + ControllerCmdSync, { - let host = self.stack.host; + let host = &self.stack.host; host.command(LeClearFilterAcceptList::new()).await?; for entry in filter_accept_list { host.command(LeAddDeviceToFilterAcceptList::new(entry.0, *entry.1)) @@ -160,53 +162,3 @@ pub(crate) fn create_phy_params(phy: P, phys: PhySet) -> PhyParams

{ }; phy_params } - -/// Scanner configuration. -pub struct ScanConfig<'d> { - /// Active scanning. - pub active: bool, - /// List of addresses to accept. - pub filter_accept_list: &'d [(AddrKind, &'d BdAddr)], - /// PHYs to scan on. - pub phys: PhySet, - /// Scan interval. - pub interval: Duration, - /// Scan window. - pub window: Duration, - /// Scan timeout. - pub timeout: Duration, -} - -impl Default for ScanConfig<'_> { - fn default() -> Self { - Self { - active: true, - filter_accept_list: &[], - phys: PhySet::M1, - interval: Duration::from_secs(1), - window: Duration::from_secs(1), - timeout: Duration::from_secs(0), - } - } -} - -/// PHYs to scan on. -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Eq, PartialEq, Copy, Clone)] -#[repr(u8)] -pub enum PhySet { - /// 1Mbps phy - M1 = 1, - /// 2Mbps phy - M2 = 2, - /// 1Mbps + 2Mbps phys - M1M2 = 3, - /// Coded phy (125kbps, S=8) - Coded = 4, - /// 1Mbps and Coded phys - M1Coded = 5, - /// 2Mbps and Coded phys - M2Coded = 6, - /// 1Mbps, 2Mbps and Coded phys - M1M2Coded = 7, -} diff --git a/host/src/channel_manager.rs b/host/src/channel_manager.rs index 509885cf..9fe5f7ea 100644 --- a/host/src/channel_manager.rs +++ b/host/src/channel_manager.rs @@ -12,13 +12,13 @@ use embassy_sync::waitqueue::WakerRegistration; use crate::cursor::WriteCursor; use crate::host::{AclSender, BleHost}; use crate::l2cap::L2capChannel; -use crate::packet_pool::{AllocId, DynamicPacketPool, Packet}; +use crate::packet_pool::{Packet, Pool}; use crate::pdu::Pdu; use crate::types::l2cap::{ CommandRejectRes, ConnParamUpdateReq, ConnParamUpdateRes, DisconnectionReq, DisconnectionRes, L2capHeader, L2capSignalCode, L2capSignalHeader, LeCreditConnReq, LeCreditConnRes, LeCreditConnResultCode, LeCreditFlowInd, }; -use crate::{BleHostError, Error}; +use crate::{config, BleHostError, Error}; const BASE_ID: u16 = 0x40; @@ -31,37 +31,37 @@ struct State<'d> { } /// Channel manager for L2CAP channels used directly by clients. -pub struct ChannelManager<'d, const RXQ: usize> { - pool: &'d dyn DynamicPacketPool<'d>, +pub struct ChannelManager<'d> { + pool: &'d dyn Pool, state: RefCell>, - inbound: &'d mut [PacketChannel<'d, RXQ>], + inbound: &'d mut [PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>], } -pub(crate) struct PacketChannel<'d, const QLEN: usize> { - chan: Channel>, QLEN>, +pub(crate) struct PacketChannel { + chan: Channel, QLEN>, } #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ChannelIndex(u8); -impl<'d, const QLEN: usize> PacketChannel<'d, QLEN> { +impl PacketChannel { #[allow(clippy::declare_interior_mutable_const)] - pub(crate) const NEW: PacketChannel<'d, QLEN> = PacketChannel { chan: Channel::new() }; + pub(crate) const NEW: PacketChannel = PacketChannel { chan: Channel::new() }; pub fn close(&self) -> Result<(), ()> { self.chan.try_send(None).map_err(|_| ()) } - pub async fn send(&self, pdu: Pdu<'d>) { + pub async fn send(&self, pdu: Pdu) { self.chan.send(Some(pdu)).await; } - pub fn try_send(&self, pdu: Pdu<'d>) -> Result<(), Error> { + pub fn try_send(&self, pdu: Pdu) -> Result<(), Error> { self.chan.try_send(Some(pdu)).map_err(|_| Error::OutOfMemory) } - pub async fn receive(&self) -> Option> { + pub async fn receive(&self) -> Option { self.chan.receive().await } @@ -94,11 +94,11 @@ impl State<'_> { } } -impl<'d, const RXQ: usize> ChannelManager<'d, RXQ> { +impl<'d> ChannelManager<'d> { pub fn new( - pool: &'d dyn DynamicPacketPool<'d>, + pool: &'d dyn Pool, channels: &'d mut [ChannelStorage], - inbound: &'d mut [PacketChannel<'d, RXQ>], + inbound: &'d mut [PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>], ) -> Self { Self { pool, @@ -178,7 +178,7 @@ impl<'d, const RXQ: usize> ChannelManager<'d, RXQ> { chan.mtu = mtu; chan.flow_control = CreditFlowControl::new( credit_flow, - initial_credits.unwrap_or(self.pool.min_available(AllocId::from_channel(chan.cid)) as u16), + initial_credits.unwrap_or(self.pool.available() as u16), ); chan.state = ChannelState::Connected; let mps = chan.mps; @@ -237,7 +237,7 @@ impl<'d, const RXQ: usize> ChannelManager<'d, RXQ> { // Allocate space for our new channel. let idx = self.alloc(conn, |storage| { cid = storage.cid; - credits = initial_credits.unwrap_or(self.pool.min_available(AllocId::from_channel(storage.cid)) as u16); + credits = initial_credits.unwrap_or(self.pool.available() as u16); storage.psm = psm; storage.mps = mps; storage.mtu = mtu; @@ -300,7 +300,7 @@ impl<'d, const RXQ: usize> ChannelManager<'d, RXQ> { } /// Dispatch an incoming L2CAP packet to the appropriate channel. - pub(crate) fn dispatch(&self, header: L2capHeader, packet: Packet<'d>) -> Result<(), Error> { + pub(crate) fn dispatch(&self, header: L2capHeader, packet: Packet) -> Result<(), Error> { if header.channel < BASE_ID { return Err(Error::InvalidChannelId); } @@ -522,7 +522,7 @@ impl<'d, const RXQ: usize> ChannelManager<'d, RXQ> { Ok(pos) } - async fn receive_pdu(&self, chan: ChannelIndex) -> Result, Error> { + async fn receive_pdu(&self, chan: ChannelIndex) -> Result { match self.inbound[chan.0 as usize].receive().await { Some(pdu) => Ok(pdu), None => Err(Error::ChannelClosed), @@ -625,7 +625,7 @@ impl<'d, const RXQ: usize> ChannelManager<'d, RXQ> { &self, index: ChannelIndex, ble: &BleHost<'d, T>, - mut packet: Packet<'d>, + mut packet: Packet, ) -> Result<(), BleHostError> { let (conn, cid, credits) = self.with_mut(|state| { let chan = &mut state.channels[index.0 as usize]; @@ -800,7 +800,7 @@ pub(crate) trait DynamicChannelManager { fn print(&self, index: ChannelIndex, f: defmt::Formatter); } -impl DynamicChannelManager for ChannelManager<'_, RXQ> { +impl DynamicChannelManager for ChannelManager<'_> { fn inc_ref(&self, index: ChannelIndex) { ChannelManager::inc_ref(self, index) } @@ -1010,12 +1010,11 @@ mod tests { use super::*; use crate::mock_controller::MockController; - use crate::packet_pool::Qos; use crate::HostResources; #[test] fn channel_refcount() { - let mut resources: HostResources = HostResources::new(Qos::None); + let mut resources: HostResources<2, 2, 27> = HostResources::new(); let ble = MockController::new(); let builder = crate::new(ble, &mut resources); diff --git a/host/src/connection.rs b/host/src/connection.rs index 5575386b..47e6a0af 100644 --- a/host/src/connection.rs +++ b/host/src/connection.rs @@ -1,11 +1,11 @@ //! BLE connection. + use bt_hci::cmd::le::LeConnUpdate; use bt_hci::cmd::status::ReadRssi; use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync}; -use bt_hci::param::{BdAddr, ConnHandle, DisconnectReason, LeConnRole, Status}; +use bt_hci::param::{AddrKind, BdAddr, ConnHandle, DisconnectReason, LeConnRole, Status}; use embassy_time::Duration; -use crate::central::ScanConfig; use crate::connection_manager::ConnectionManager; use crate::pdu::Pdu; use crate::{BleHostError, Error, Stack}; @@ -18,6 +18,56 @@ pub struct ConnectConfig<'d> { pub connect_params: ConnectParams, } +/// Scan/connect configuration. +pub struct ScanConfig<'d> { + /// Active scanning. + pub active: bool, + /// List of addresses to accept. + pub filter_accept_list: &'d [(AddrKind, &'d BdAddr)], + /// PHYs to scan on. + pub phys: PhySet, + /// Scan interval. + pub interval: Duration, + /// Scan window. + pub window: Duration, + /// Scan timeout. + pub timeout: Duration, +} + +impl Default for ScanConfig<'_> { + fn default() -> Self { + Self { + active: true, + filter_accept_list: &[], + phys: PhySet::M1, + interval: Duration::from_secs(1), + window: Duration::from_secs(1), + timeout: Duration::from_secs(0), + } + } +} + +/// PHYs to scan on. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Eq, PartialEq, Copy, Clone)] +#[repr(u8)] +pub enum PhySet { + /// 1Mbps phy + M1 = 1, + /// 2Mbps phy + M2 = 2, + /// 1Mbps + 2Mbps phys + M1M2 = 3, + /// Coded phy (125kbps, S=8) + Coded = 4, + /// 1Mbps and Coded phys + M1Coded = 5, + /// 2Mbps and Coded phys + M2Coded = 6, + /// 1Mbps, 2Mbps and Coded phys + M1M2Coded = 7, +} + /// Connection parameters. pub struct ConnectParams { /// Minimum connection interval. @@ -32,8 +82,19 @@ pub struct ConnectParams { pub supervision_timeout: Duration, } -/// An event -pub enum ConnectionEvent<'d> { +#[cfg(not(feature = "gatt"))] +/// A connection event. +pub enum ConnectionEvent { + /// Connection disconnected. + Disconnected { + /// The reason (status code) for the disconnect. + reason: Status, + }, +} + +/// A connection event. +#[cfg(feature = "gatt")] +pub enum ConnectionEvent<'stack> { /// Connection disconnected. Disconnected { /// The reason (status code) for the disconnect. @@ -42,11 +103,20 @@ pub enum ConnectionEvent<'d> { /// GATT event. Gatt { /// The event that was returned - #[cfg(feature = "gatt")] - data: crate::gatt::GattData<'d>, - #[cfg(not(feature = "gatt"))] - /// Connection handle for the event - connection: Connection<'d>, + data: crate::gatt::GattData<'stack>, + }, +} + +pub(crate) enum ConnectionEventData { + /// Connection disconnected. + Disconnected { + /// The reason (status code) for the disconnect. + reason: Status, + }, + /// GATT event. + Gatt { + /// The event that was returned + data: Pdu, }, } @@ -65,9 +135,9 @@ impl Default for ConnectParams { /// Handle to a BLE connection. /// /// When the last reference to a connection is dropped, the connection is automatically disconnected. -pub struct Connection<'d> { +pub struct Connection<'stack> { index: u8, - manager: &'d ConnectionManager<'d>, + manager: &'stack ConnectionManager<'stack>, } impl Clone for Connection<'_> { @@ -83,8 +153,8 @@ impl Drop for Connection<'_> { } } -impl<'d> Connection<'d> { - pub(crate) fn new(index: u8, manager: &'d ConnectionManager<'d>) -> Self { +impl<'stack> Connection<'stack> { + pub(crate) fn new(index: u8, manager: &'stack ConnectionManager<'stack>) -> Self { Self { index, manager } } @@ -96,26 +166,41 @@ impl<'d> Connection<'d> { self.manager.get_att_mtu(self.index) } - pub(crate) async fn send(&self, pdu: Pdu<'d>) { + pub(crate) async fn send(&self, pdu: Pdu) { self.manager.send(self.index, pdu).await } - pub(crate) fn try_send(&self, pdu: Pdu<'d>) -> Result<(), Error> { + pub(crate) fn try_send(&self, pdu: Pdu) -> Result<(), Error> { self.manager.try_send(self.index, pdu) } - pub(crate) async fn post_event(&self, event: ConnectionEvent<'d>) { + pub(crate) async fn post_event(&self, event: ConnectionEventData) { self.manager.post_event(self.index, event).await } #[cfg(feature = "gatt")] - pub(crate) fn alloc_tx(&self) -> Result, Error> { + pub(crate) fn alloc_tx(&self) -> Result { self.manager.alloc_tx() } /// Wait for next connection event. - pub async fn next(&self) -> ConnectionEvent<'d> { - self.manager.next(self.index).await + #[cfg(not(feature = "gatt"))] + pub async fn next(&self) -> ConnectionEvent { + match self.manager.next(self.index).await { + ConnectionEventData::Disconnected { reason } => ConnectionEvent::Disconnected { reason }, + ConnectionEventData::Gatt { data } => unreachable!(), + } + } + + /// Wait for next connection event. + #[cfg(feature = "gatt")] + pub async fn next(&self) -> ConnectionEvent<'stack> { + match self.manager.next(self.index).await { + ConnectionEventData::Disconnected { reason } => ConnectionEvent::Disconnected { reason }, + ConnectionEventData::Gatt { data } => ConnectionEvent::Gatt { + data: crate::gatt::GattData::new(data, self.clone()), + }, + } } /// Check if still connected @@ -150,7 +235,7 @@ impl<'d> Connection<'d> { } /// The RSSI value for this connection. - pub async fn rssi(&self, stack: Stack<'_, T>) -> Result> + pub async fn rssi(&self, stack: &Stack<'_, T>) -> Result> where T: ControllerCmdSync, { @@ -162,7 +247,7 @@ impl<'d> Connection<'d> { /// Update connection parameters for this connection. pub async fn update_connection_params( &self, - stack: Stack<'_, T>, + stack: &Stack<'_, T>, params: ConnectParams, ) -> Result<(), BleHostError> where diff --git a/host/src/connection_manager.rs b/host/src/connection_manager.rs index f8286ada..c2e76ac6 100644 --- a/host/src/connection_manager.rs +++ b/host/src/connection_manager.rs @@ -7,9 +7,9 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::WakerRegistration; -use crate::connection::{Connection, ConnectionEvent}; +use crate::connection::{Connection, ConnectionEventData}; #[cfg(feature = "gatt")] -use crate::packet_pool::{DynamicPacketPool, Packet, GENERIC_ID}; +use crate::packet_pool::{Packet, Pool}; use crate::pdu::Pdu; use crate::{config, Error}; @@ -40,22 +40,45 @@ impl State<'_> { } } -pub(crate) type EventChannel<'d> = Channel, { config::CONNECTION_EVENT_QUEUE_SIZE }>; +pub(crate) struct EventChannel { + chan: Channel, +} + +impl EventChannel { + #[allow(clippy::declare_interior_mutable_const)] + pub(crate) const NEW: EventChannel = EventChannel { chan: Channel::new() }; + + pub async fn receive(&self) -> ConnectionEventData { + self.chan.receive().await + } + + pub async fn send(&self, event: ConnectionEventData) { + self.chan.send(event).await; + } + + pub fn try_send(&self, event: ConnectionEventData) -> Result<(), Error> { + self.chan.try_send(event).map_err(|_| Error::OutOfMemory) + } + + pub fn clear(&self) { + self.chan.clear(); + } +} pub(crate) struct ConnectionManager<'d> { state: RefCell>, - events: &'d mut [EventChannel<'d>], - outbound: Channel), { config::L2CAP_TX_QUEUE_SIZE }>, + events: &'d mut [EventChannel], + outbound: Channel, #[cfg(feature = "gatt")] - tx_pool: &'d dyn DynamicPacketPool<'d>, + tx_pool: &'d dyn Pool, } impl<'d> ConnectionManager<'d> { pub(crate) fn new( connections: &'d mut [ConnectionStorage], - events: &'d mut [EventChannel<'d>], + events: &'d mut [EventChannel], default_att_mtu: u16, - #[cfg(feature = "gatt")] tx_pool: &'d dyn DynamicPacketPool<'d>, + #[cfg(feature = "gatt")] tx_pool: &'d dyn Pool, ) -> Self { Self { state: RefCell::new(State { @@ -94,15 +117,15 @@ impl<'d> ConnectionManager<'d> { }) } - pub(crate) async fn next(&self, index: u8) -> ConnectionEvent<'d> { + pub(crate) async fn next(&self, index: u8) -> ConnectionEventData { self.events[index as usize].receive().await } - pub(crate) async fn post_event(&self, index: u8, event: ConnectionEvent<'d>) { + pub(crate) async fn post_event(&self, index: u8, event: ConnectionEventData) { self.events[index as usize].send(event).await } - pub(crate) fn post_handle_event(&self, handle: ConnHandle, event: ConnectionEvent<'d>) -> Result<(), Error> { + pub(crate) fn post_handle_event(&self, handle: ConnHandle, event: ConnectionEventData) -> Result<(), Error> { let index = self.with_mut(|state| { for (index, entry) in state.connections.iter().enumerate() { if entry.state == ConnectionState::Connected && Some(handle) == entry.handle { @@ -218,7 +241,7 @@ impl<'d> ConnectionManager<'d> { for (idx, storage) in state.connections.iter_mut().enumerate() { if Some(h) == storage.handle && storage.state != ConnectionState::Disconnected { storage.state = ConnectionState::Disconnected; - let _ = self.events[idx].try_send(ConnectionEvent::Disconnected { reason }); + let _ = self.events[idx].try_send(ConnectionEventData::Disconnected { reason }); #[cfg(feature = "connection-metrics")] storage.metrics.reset(); return Ok(()); @@ -417,26 +440,26 @@ impl<'d> ConnectionManager<'d> { self.with_mut(|state| state.connections[index as usize].att_mtu) } - pub(crate) async fn send(&self, index: u8, pdu: Pdu<'d>) { + pub(crate) async fn send(&self, index: u8, pdu: Pdu) { let handle = self.with_mut(|state| state.connections[index as usize].handle.unwrap()); self.outbound.send((handle, pdu)).await } #[cfg(feature = "gatt")] - pub(crate) fn alloc_tx(&self) -> Result, Error> { - self.tx_pool.alloc(GENERIC_ID).ok_or(Error::OutOfMemory) + pub(crate) fn alloc_tx(&self) -> Result { + self.tx_pool.alloc().ok_or(Error::OutOfMemory) } - pub(crate) fn try_send(&self, index: u8, pdu: Pdu<'d>) -> Result<(), Error> { + pub(crate) fn try_send(&self, index: u8, pdu: Pdu) -> Result<(), Error> { let handle = self.with_mut(|state| state.connections[index as usize].handle.unwrap()); self.outbound.try_send((handle, pdu)).map_err(|_| Error::OutOfMemory) } - pub(crate) fn try_outbound(&self, handle: ConnHandle, pdu: Pdu<'d>) -> Result<(), Error> { + pub(crate) fn try_outbound(&self, handle: ConnHandle, pdu: Pdu) -> Result<(), Error> { self.outbound.try_send((handle, pdu)).map_err(|_| Error::OutOfMemory) } - pub(crate) async fn outbound(&self) -> (ConnHandle, Pdu<'d>) { + pub(crate) async fn outbound(&self) -> (ConnHandle, Pdu) { self.outbound.receive().await } @@ -663,7 +686,6 @@ impl Drop for PacketGrant<'_, '_> { mod tests { use super::*; use crate::prelude::PacketPool; - use crate::PacketQos; extern crate std; use std::boxed::Box; @@ -674,8 +696,8 @@ mod tests { fn setup() -> &'static ConnectionManager<'static> { let storage = Box::leak(Box::new([ConnectionStorage::DISCONNECTED; 3])); - let events = Box::leak(Box::new([const { EventChannel::new() }; 3])); - let pool = Box::leak(Box::new(PacketPool::::new(PacketQos::None))); + let events = Box::leak(Box::new([EventChannel::NEW; 3])); + let pool = Box::leak(Box::new(PacketPool::<27, 8>::new())); let mgr = ConnectionManager::new(&mut storage[..], &mut events[..], 23, pool); Box::leak(Box::new(mgr)) } @@ -820,6 +842,7 @@ mod tests { unwrap!(mgr.disconnected(ConnHandle::new(2), Status::UNSPECIFIED)); // Check that we get an event + use crate::connection::ConnectionEvent; assert!(matches!( block_on(peripheral.next()), ConnectionEvent::Disconnected { diff --git a/host/src/gatt.rs b/host/src/gatt.rs index 53b71c69..804ca348 100644 --- a/host/src/gatt.rs +++ b/host/src/gatt.rs @@ -19,26 +19,20 @@ use crate::attribute::{AttributeData, Characteristic, CharacteristicProp, Uuid, use crate::attribute_server::{AttributeServer, DynamicAttributeServer}; use crate::connection::Connection; use crate::cursor::{ReadCursor, WriteCursor}; -use crate::packet_pool::{DynamicPacketPool, GENERIC_ID}; use crate::pdu::Pdu; use crate::types::gatt_traits::{FromGattError, GattValue}; use crate::types::l2cap::L2capHeader; use crate::{config, BleHostError, Error, Stack}; /// A GATT payload ready for processing. -pub struct GattData<'d> { - pdu: Pdu<'d>, - tx_pool: &'d dyn DynamicPacketPool<'d>, - connection: Connection<'d>, +pub struct GattData<'stack> { + pdu: Pdu, + connection: Connection<'stack>, } -impl<'d> GattData<'d> { - pub(crate) fn new(pdu: Pdu<'d>, tx_pool: &'d dyn DynamicPacketPool<'d>, connection: Connection<'d>) -> Self { - Self { - pdu, - tx_pool, - connection, - } +impl<'stack> GattData<'stack> { + pub(crate) fn new(pdu: Pdu, connection: Connection<'stack>) -> Self { + Self { pdu, connection } } /// Get the raw request. @@ -49,7 +43,7 @@ impl<'d> GattData<'d> { /// Respond directly to request. pub async fn reply(self, rsp: AttRsp<'_>) -> Result<(), Error> { - let pdu = respond(&self.connection, self.tx_pool, rsp)?; + let pdu = respond(&self.connection, rsp)?; self.connection.send(pdu).await; Ok(()) } @@ -61,12 +55,11 @@ impl<'d> GattData<'d> { pub async fn process<'m, 'server, M: RawMutex, const MAX: usize>( self, server: &'m AttributeServer<'server, M, MAX>, - ) -> Result>, Error> { + ) -> Result>, Error> { let att = self.request(); match att { AttReq::Write { handle, data: _ } => Ok(Some(GattEvent::Write(WriteEvent { value_handle: handle, - tx_pool: self.tx_pool, pdu: Some(self.pdu), connection: self.connection, server, @@ -74,7 +67,6 @@ impl<'d> GattData<'d> { AttReq::WriteCmd { handle, data: _ } => Ok(Some(GattEvent::Write(WriteEvent { value_handle: handle, - tx_pool: self.tx_pool, pdu: Some(self.pdu), connection: self.connection, server, @@ -82,7 +74,6 @@ impl<'d> GattData<'d> { AttReq::Read { handle } => Ok(Some(GattEvent::Read(ReadEvent { value_handle: handle, - tx_pool: self.tx_pool, pdu: Some(self.pdu), connection: self.connection, server, @@ -90,14 +81,13 @@ impl<'d> GattData<'d> { AttReq::ReadBlob { handle, offset } => Ok(Some(GattEvent::Read(ReadEvent { value_handle: handle, - tx_pool: self.tx_pool, pdu: Some(self.pdu), connection: self.connection, server, }))), _ => { // Process it now since the user will not - let reply = process_accept(self.pdu, &self.connection, server, self.tx_pool)?; + let reply = process_accept(self.pdu, &self.connection, server)?; reply.send().await; Ok(None) } @@ -106,23 +96,22 @@ impl<'d> GattData<'d> { } /// An event returned while processing GATT requests. -pub enum GattEvent<'d, 'server> { +pub enum GattEvent<'stack, 'server> { /// A characteristic was read. - Read(ReadEvent<'d, 'server>), + Read(ReadEvent<'stack, 'server>), /// A characteristic was written. - Write(WriteEvent<'d, 'server>), + Write(WriteEvent<'stack, 'server>), } /// An event returned while processing GATT requests. -pub struct ReadEvent<'d, 'server> { +pub struct ReadEvent<'stack, 'server> { value_handle: u16, - connection: Connection<'d>, + connection: Connection<'stack>, server: &'server dyn DynamicAttributeServer, - tx_pool: &'d dyn DynamicPacketPool<'d>, - pdu: Option>, + pdu: Option, } -impl<'d> ReadEvent<'d, '_> { +impl<'stack> ReadEvent<'stack, '_> { /// Characteristic handle that was read pub fn handle(&self) -> u16 { self.value_handle @@ -131,57 +120,35 @@ impl<'d> ReadEvent<'d, '_> { /// Accept the event, making it processed by the server. /// /// Automatically called if drop() is invoked. - pub fn accept(mut self) -> Result, Error> { + pub fn accept(mut self) -> Result, Error> { let handle = self.handle(); - process( - &mut self.pdu, - handle, - &self.connection, - self.server, - self.tx_pool, - Ok(()), - ) + process(&mut self.pdu, handle, &self.connection, self.server, Ok(())) } /// Reject the event with the provided error code, it will not be processed by the attribute server. - pub fn reject(mut self, err: AttErrorCode) -> Result, Error> { + pub fn reject(mut self, err: AttErrorCode) -> Result, Error> { let handle = self.handle(); - process( - &mut self.pdu, - handle, - &self.connection, - self.server, - self.tx_pool, - Err(err), - ) + process(&mut self.pdu, handle, &self.connection, self.server, Err(err)) } } impl Drop for ReadEvent<'_, '_> { fn drop(&mut self) { let handle = self.handle(); - let _ = process( - &mut self.pdu, - handle, - &self.connection, - self.server, - self.tx_pool, - Ok(()), - ); + let _ = process(&mut self.pdu, handle, &self.connection, self.server, Ok(())); } } /// An event returned while processing GATT requests. -pub struct WriteEvent<'d, 'server> { +pub struct WriteEvent<'stack, 'server> { /// Characteristic handle that was written. value_handle: u16, - pdu: Option>, - connection: Connection<'d>, - tx_pool: &'d dyn DynamicPacketPool<'d>, + pdu: Option, + connection: Connection<'stack>, server: &'server dyn DynamicAttributeServer, } -impl<'d> WriteEvent<'d, '_> { +impl<'stack> WriteEvent<'stack, '_> { /// Characteristic handle that was read pub fn handle(&self) -> u16 { self.value_handle @@ -201,72 +168,49 @@ impl<'d> WriteEvent<'d, '_> { /// Accept the event, making it processed by the server. /// /// Automatically called if drop() is invoked. - pub fn accept(mut self) -> Result, Error> { + pub fn accept(mut self) -> Result, Error> { let handle = self.handle(); - process( - &mut self.pdu, - handle, - &self.connection, - self.server, - self.tx_pool, - Ok(()), - ) + process(&mut self.pdu, handle, &self.connection, self.server, Ok(())) } /// Reject the event with the provided error code, it will not be processed by the attribute server. - pub fn reject(mut self, err: AttErrorCode) -> Result, Error> { + pub fn reject(mut self, err: AttErrorCode) -> Result, Error> { let handle = self.handle(); - process( - &mut self.pdu, - handle, - &self.connection, - self.server, - self.tx_pool, - Err(err), - ) + process(&mut self.pdu, handle, &self.connection, self.server, Err(err)) } } impl Drop for WriteEvent<'_, '_> { fn drop(&mut self) { let handle = self.handle(); - let _ = process( - &mut self.pdu, - handle, - &self.connection, - self.server, - self.tx_pool, - Ok(()), - ); + let _ = process(&mut self.pdu, handle, &self.connection, self.server, Ok(())); } } -fn process<'d>( - pdu: &mut Option>, +fn process<'stack>( + pdu: &mut Option, handle: u16, - connection: &Connection<'d>, + connection: &Connection<'stack>, server: &dyn DynamicAttributeServer, - tx_pool: &'d dyn DynamicPacketPool<'d>, result: Result<(), AttErrorCode>, -) -> Result, Error> { +) -> Result, Error> { if let Some(pdu) = pdu.take() { match result { - Ok(_) => process_accept(pdu, connection, server, tx_pool), - Err(code) => process_reject(pdu, handle, connection, tx_pool, code), + Ok(_) => process_accept(pdu, connection, server), + Err(code) => process_reject(pdu, handle, connection, code), } } else { Ok(Reply::new(connection.clone(), None)) } } -fn process_accept<'d>( - pdu: Pdu<'d>, - connection: &Connection<'d>, +fn process_accept<'stack>( + pdu: Pdu, + connection: &Connection<'stack>, server: &dyn DynamicAttributeServer, - tx_pool: &'d dyn DynamicPacketPool<'d>, -) -> Result, Error> { +) -> Result, Error> { let att = unwrap!(AttReq::decode(pdu.as_ref())); - let mut tx = tx_pool.alloc(GENERIC_ID).ok_or(Error::OutOfMemory)?; + let mut tx = connection.alloc_tx()?; let mut w = WriteCursor::new(tx.as_mut()); let (mut header, mut data) = w.split(4)?; if let Some(written) = server.process(connection, &att, data.write_buf())? { @@ -283,26 +227,21 @@ fn process_accept<'d>( } } -fn process_reject<'d>( - pdu: Pdu<'d>, +fn process_reject<'stack>( + pdu: Pdu, handle: u16, - connection: &Connection<'d>, - tx_pool: &'d dyn DynamicPacketPool<'d>, + connection: &Connection<'stack>, code: AttErrorCode, -) -> Result, Error> { +) -> Result, Error> { // We know it has been checked, therefore this cannot fail let request = pdu.as_ref()[0]; let rsp = AttRsp::Error { request, handle, code }; - let pdu = respond(connection, tx_pool, rsp)?; + let pdu = respond(connection, rsp)?; Ok(Reply::new(connection.clone(), Some(pdu))) } -fn respond<'d>( - conn: &Connection<'d>, - tx_pool: &'d dyn DynamicPacketPool<'d>, - rsp: AttRsp<'_>, -) -> Result, Error> { - let mut tx = tx_pool.alloc(GENERIC_ID).ok_or(Error::OutOfMemory)?; +fn respond<'stack>(conn: &Connection<'stack>, rsp: AttRsp<'_>) -> Result { + let mut tx = conn.alloc_tx()?; let mut w = WriteCursor::new(tx.as_mut()); let (mut header, mut data) = w.split(4)?; data.write(rsp)?; @@ -319,13 +258,13 @@ fn respond<'d>( /// /// The reply may be sent immediately or queued for sending later. To guarantee delivery of a reply /// in case of a full outbound queue, the async send() should be used rather than relying on the Drop implementation. -pub struct Reply<'d> { - connection: Connection<'d>, - pdu: Option>, +pub struct Reply<'stack> { + connection: Connection<'stack>, + pdu: Option, } -impl<'d> Reply<'d> { - fn new(connection: Connection<'d>, pdu: Option>) -> Self { +impl<'stack> Reply<'stack> { + fn new(connection: Connection<'stack>, pdu: Option) -> Self { Self { connection, pdu } } @@ -382,10 +321,10 @@ const NOTIF_QSIZE: usize = config::GATT_CLIENT_NOTIFICATION_QUEUE_SIZE; /// A GATT client capable of using the GATT protocol. pub struct GattClient<'reference, T: Controller, const MAX_SERVICES: usize, const L2CAP_MTU: usize = 27> { known_services: RefCell>, - rx: DynamicReceiver<'reference, (ConnHandle, Pdu<'reference>)>, - stack: Stack<'reference, T>, + rx: DynamicReceiver<'reference, (ConnHandle, Pdu)>, + stack: &'reference Stack<'reference, T>, connection: Connection<'reference>, - response_channel: Channel), 1>, + response_channel: Channel, notifications: PubSubChannel, NOTIF_QSIZE, MAX_NOTIF, 1>, } @@ -417,13 +356,13 @@ pub struct ServiceHandle { /// Trait with behavior for a gatt client. pub(crate) trait Client<'d, E> { /// Perform a gatt request and return the response. - fn request(&self, req: AttReq<'_>) -> impl Future, BleHostError>>; + fn request(&self, req: AttReq<'_>) -> impl Future>>; } impl<'reference, T: Controller, const MAX_SERVICES: usize, const L2CAP_MTU: usize> Client<'reference, T::Error> for GattClient<'reference, T, MAX_SERVICES, L2CAP_MTU> { - async fn request(&self, req: AttReq<'_>) -> Result, BleHostError> { + async fn request(&self, req: AttReq<'_>) -> Result> { let header = L2capHeader { channel: crate::types::l2cap::L2CAP_CID_ATT, length: req.size() as u16, @@ -449,7 +388,7 @@ impl<'reference, C: Controller, const MAX_SERVICES: usize, const L2CAP_MTU: usiz { /// Creates a GATT client capable of processing the GATT protocol using the provided table of attributes. pub async fn new( - stack: Stack<'reference, C>, + stack: &'reference Stack<'reference, C>, connection: &Connection<'reference>, ) -> Result, BleHostError> { let l2cap = L2capHeader { channel: 4, length: 3 }; diff --git a/host/src/host.rs b/host/src/host.rs index e6623dcf..f2d747fb 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -36,11 +36,11 @@ use futures::pin_mut; use crate::channel_manager::{ChannelManager, ChannelStorage, PacketChannel}; use crate::command::CommandState; #[cfg(feature = "gatt")] -use crate::connection::ConnectionEvent; +use crate::connection::ConnectionEventData; use crate::connection_manager::{ConnectionManager, ConnectionStorage, EventChannel, PacketGrant}; use crate::cursor::WriteCursor; use crate::l2cap::sar::{PacketReassembly, SarType}; -use crate::packet_pool::{AllocId, DynamicPacketPool}; +use crate::packet_pool::Pool; use crate::pdu::Pdu; use crate::types::l2cap::{ L2capHeader, L2capSignal, L2capSignalHeader, L2CAP_CID_ATT, L2CAP_CID_DYN_START, L2CAP_CID_LE_U_SIGNAL, @@ -61,12 +61,12 @@ pub(crate) struct BleHost<'d, T> { pub(crate) controller: T, pub(crate) connections: ConnectionManager<'d>, pub(crate) reassembly: PacketReassembly<'d>, - pub(crate) channels: ChannelManager<'d, { config::L2CAP_RX_QUEUE_SIZE }>, + pub(crate) channels: ChannelManager<'d>, #[cfg(feature = "gatt")] - pub(crate) att_client: Channel), { config::L2CAP_RX_QUEUE_SIZE }>, - pub(crate) rx_pool: &'d dyn DynamicPacketPool<'d>, + pub(crate) att_client: Channel, + pub(crate) rx_pool: &'d dyn Pool, #[cfg(feature = "gatt")] - pub(crate) tx_pool: &'d dyn DynamicPacketPool<'d>, + pub(crate) tx_pool: &'d dyn Pool, pub(crate) advertise_state: AdvState<'d>, pub(crate) advertise_command_state: CommandState, @@ -224,13 +224,13 @@ where #[allow(clippy::too_many_arguments)] pub(crate) fn new( controller: T, - rx_pool: &'d dyn DynamicPacketPool<'d>, - #[cfg(feature = "gatt")] tx_pool: &'d dyn DynamicPacketPool<'d>, + rx_pool: &'d dyn Pool, + #[cfg(feature = "gatt")] tx_pool: &'d dyn Pool, connections: &'d mut [ConnectionStorage], - events: &'d mut [EventChannel<'d>], + events: &'d mut [EventChannel], channels: &'d mut [ChannelStorage], - channels_rx: &'d mut [PacketChannel<'d, { config::L2CAP_RX_QUEUE_SIZE }>], - sar: &'d mut [SarType<'d>], + channels_rx: &'d mut [PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>], + sar: &'d mut [SarType], advertise_handles: &'d mut [AdvHandleState], ) -> Self { Self { @@ -326,7 +326,7 @@ where true } - fn handle_acl(&'d self, acl: AclPacket<'_>) -> Result<(), Error> { + fn handle_acl(&self, acl: AclPacket<'_>) -> Result<(), Error> { self.connections.received(acl.handle())?; let (header, mut packet) = match acl.boundary_flag() { AclPacketBoundary::FirstFlushable => { @@ -347,7 +347,7 @@ where return Ok(()); } - let Some(mut p) = self.rx_pool.alloc(AllocId::from_channel(header.channel)) else { + let Some(mut p) = self.rx_pool.alloc() else { info!("No memory for packets on channel {}", header.channel); return Err(Error::OutOfMemory); }; @@ -403,16 +403,10 @@ where #[cfg(feature = "gatt")] match a { Ok(att::Att::Req(_)) => { - if let Some(connection) = self.connections.get_connected_handle(acl.handle()) { - let event = ConnectionEvent::Gatt { - data: crate::gatt::GattData::new( - Pdu::new(packet, header.length as usize), - self.tx_pool, - connection, - ), - }; - self.connections.post_handle_event(acl.handle(), event)?; - } + let event = ConnectionEventData::Gatt { + data: Pdu::new(packet, header.length as usize), + }; + self.connections.post_handle_event(acl.handle(), event)?; } Ok(att::Att::Rsp(_)) => { if let Err(e) = self @@ -503,21 +497,21 @@ pub struct Runner<'d, C: Controller> { /// The receiver part of the host runner. pub struct RxRunner<'d, C: Controller> { - stack: Stack<'d, C>, + stack: &'d Stack<'d, C>, } /// The control part of the host runner. pub struct ControlRunner<'d, C: Controller> { - stack: Stack<'d, C>, + stack: &'d Stack<'d, C>, } /// The transmit part of the host runner. pub struct TxRunner<'d, C: Controller> { - stack: Stack<'d, C>, + stack: &'d Stack<'d, C>, } impl<'d, C: Controller> Runner<'d, C> { - pub(crate) fn new(stack: Stack<'d, C>) -> Self { + pub(crate) fn new(stack: &'d Stack<'d, C>) -> Self { Self { rx: RxRunner { stack }, control: ControlRunner { stack }, @@ -610,7 +604,7 @@ impl<'d, C: Controller> RxRunner<'d, C> { C: ControllerCmdSync + for<'t> ControllerCmdSync>, { const MAX_HCI_PACKET_LEN: usize = 259; - let host = self.stack.host; + let host = &self.stack.host; // use embassy_time::Instant; // let mut last = Instant::now(); loop { @@ -832,7 +826,7 @@ impl<'d, C: Controller> ControlRunner<'d, C> { + for<'t> ControllerCmdSync> + ControllerCmdSync, { - let host = self.stack.host; + let host = &self.stack.host; Reset::new().exec(&host.controller).await?; if let Some(addr) = host.address { @@ -967,7 +961,7 @@ impl<'d, C: Controller> ControlRunner<'d, C> { impl<'d, C: Controller> TxRunner<'d, C> { /// Run the transmit loop for the host. pub async fn run(&mut self) -> Result<(), BleHostError> { - let host = self.stack.host; + let host = &self.stack.host; let params = host.initialized.get().await; loop { let (conn, pdu) = host.connections.outbound().await; diff --git a/host/src/l2cap.rs b/host/src/l2cap.rs index 0a758583..24b4e374 100644 --- a/host/src/l2cap.rs +++ b/host/src/l2cap.rs @@ -73,14 +73,14 @@ impl<'d> L2capChannel<'d> { /// If there are no available credits to send, waits until more credits are available. pub async fn send( &mut self, - stack: Stack<'_, T>, + stack: &Stack<'_, T>, buf: &[u8], ) -> Result<(), BleHostError> { let mut p_buf = [0u8; TX_MTU]; stack .host .channels - .send(self.index, buf, &mut p_buf[..], stack.host) + .send(self.index, buf, &mut p_buf[..], &stack.host) .await } @@ -92,14 +92,14 @@ impl<'d> L2capChannel<'d> { /// If there are no available credits to send, returns Error::Busy. pub fn try_send( &mut self, - stack: Stack<'_, T>, + stack: &Stack<'_, T>, buf: &[u8], ) -> Result<(), BleHostError> { let mut p_buf = [0u8; TX_MTU]; stack .host .channels - .try_send(self.index, buf, &mut p_buf[..], stack.host) + .try_send(self.index, buf, &mut p_buf[..], &stack.host) } /// Receive data on this channel and copy it into the buffer. @@ -107,15 +107,15 @@ impl<'d> L2capChannel<'d> { /// The length provided buffer slice must be equal or greater to the agreed MTU. pub async fn receive( &mut self, - stack: Stack<'_, T>, + stack: &Stack<'_, T>, buf: &mut [u8], ) -> Result> { - stack.host.channels.receive(self.index, buf, stack.host).await + stack.host.channels.receive(self.index, buf, &stack.host).await } /// Await an incoming connection request matching the list of PSM. pub async fn accept( - stack: Stack<'d, T>, + stack: &'d Stack<'d, T>, connection: &Connection<'_>, psm: &[u16], config: &L2capChannelConfig, @@ -130,14 +130,14 @@ impl<'d> L2capChannel<'d> { config.mtu, config.flow_policy, config.initial_credits, - stack.host, + &stack.host, ) .await } /// Create a new connection request with the provided PSM. pub async fn create( - stack: Stack<'d, T>, + stack: &'d Stack<'d, T>, connection: &Connection<'_>, psm: u16, config: &L2capChannelConfig, @@ -152,7 +152,7 @@ where { config.mtu, config.flow_policy, config.initial_credits, - stack.host, + &stack.host, ) .await } diff --git a/host/src/l2cap/sar.rs b/host/src/l2cap/sar.rs index 7b720395..52f7b2d8 100644 --- a/host/src/l2cap/sar.rs +++ b/host/src/l2cap/sar.rs @@ -6,13 +6,13 @@ use crate::packet_pool::Packet; use crate::types::l2cap::L2capHeader; use crate::Error; -pub(crate) struct AssembledPacket<'d> { - packet: Packet<'d>, +pub(crate) struct AssembledPacket { + packet: Packet, written: usize, } -impl<'d> AssembledPacket<'d> { - pub(crate) fn new(packet: Packet<'d>, initial: usize) -> Self { +impl AssembledPacket { + pub(crate) fn new(packet: Packet, initial: usize) -> Self { Self { packet, written: initial, @@ -32,7 +32,7 @@ impl<'d> AssembledPacket<'d> { self.written } - pub(crate) fn finalize(self, header: L2capHeader) -> Result<(L2capHeader, Packet<'d>), Error> { + pub(crate) fn finalize(self, header: L2capHeader) -> Result<(L2capHeader, Packet), Error> { if header.length as usize != self.written { return Err(Error::InvalidValue); } @@ -40,14 +40,14 @@ impl<'d> AssembledPacket<'d> { } } -pub(crate) type SarType<'d> = Option<(ConnHandle, L2capHeader, AssembledPacket<'d>)>; +pub(crate) type SarType = Option<(ConnHandle, L2capHeader, AssembledPacket)>; // Handles reassembling of packets pub struct PacketReassembly<'d> { - handles: RefCell<&'d mut [SarType<'d>]>, + handles: RefCell<&'d mut [SarType]>, } impl<'d> PacketReassembly<'d> { - pub fn new(handles: &'d mut [Option<(ConnHandle, L2capHeader, AssembledPacket<'d>)>]) -> Self { + pub fn new(handles: &'d mut [Option<(ConnHandle, L2capHeader, AssembledPacket)>]) -> Self { Self { handles: RefCell::new(handles), //[Self::EMPTY; CONNS]), } @@ -57,7 +57,7 @@ impl<'d> PacketReassembly<'d> { /// /// Returns InvalidState if there is already an ongoing reassembly for this connection /// Returns InsufficientSpace if there is no space for this reassembly - pub fn init(&self, handle: ConnHandle, header: L2capHeader, p: Packet<'d>, initial: usize) -> Result<(), Error> { + pub fn init(&self, handle: ConnHandle, header: L2capHeader, p: Packet, initial: usize) -> Result<(), Error> { let mut state = self.handles.borrow_mut(); // Sanity check @@ -93,7 +93,7 @@ impl<'d> PacketReassembly<'d> { /// Updates any in progress packet assembly for the connection /// /// If the reassembly is complete, the l2cap header + packet is returned. - pub fn update(&self, handle: ConnHandle, data: &[u8]) -> Result)>, Error> { + pub fn update(&self, handle: ConnHandle, data: &[u8]) -> Result, Error> { let mut state = self.handles.borrow_mut(); for entry in state.iter_mut() { diff --git a/host/src/lib.rs b/host/src/lib.rs index 2c7de96d..f01cab2f 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -17,13 +17,12 @@ use bt_hci::cmd::status::ReadRssi; use bt_hci::cmd::{AsyncCmd, SyncCmd}; pub use bt_hci::param::{AddrKind, BdAddr, LeConnRole as Role}; use bt_hci::FromHciBytesError; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use crate::att::AttErrorCode; use crate::channel_manager::{ChannelStorage, PacketChannel}; use crate::connection_manager::{ConnectionStorage, EventChannel}; use crate::l2cap::sar::SarType; -use crate::packet_pool::{PacketPool, Qos}; +use crate::packet_pool::PacketPool; mod fmt; @@ -31,6 +30,7 @@ mod fmt; compile_error!("Must enable at least one of the `central` or `peripheral` features"); mod att; +#[cfg(feature = "central")] pub mod central; mod channel_manager; mod codec; @@ -40,10 +40,15 @@ mod connection_manager; mod cursor; pub mod packet_pool; mod pdu; +#[cfg(feature = "peripheral")] pub mod peripheral; pub mod types; -pub use packet_pool::Qos as PacketQos; +#[cfg(feature = "peripheral")] +use peripheral::*; + +#[cfg(feature = "central")] +use central::*; pub mod advertise; pub mod connection; @@ -57,9 +62,7 @@ pub mod scan; pub(crate) mod mock_controller; pub(crate) mod host; -pub use central::*; use host::{AdvHandleState, BleHost, HostMetrics, Runner}; -pub use peripheral::*; #[allow(missing_docs)] pub mod prelude { @@ -86,7 +89,7 @@ pub mod prelude { pub use crate::gatt::*; pub use crate::host::{ControlRunner, HostMetrics, Runner, RxRunner, TxRunner}; pub use crate::l2cap::*; - pub use crate::packet_pool::{PacketPool, Qos as PacketQos}; + pub use crate::packet_pool::PacketPool; #[cfg(feature = "peripheral")] pub use crate::peripheral::*; #[cfg(feature = "scan")] @@ -301,33 +304,32 @@ impl< /// /// The l2cap packet pool is used by the host to handle inbound data, by allocating space for /// incoming packets and dispatching to the appropriate connection and channel. -pub struct HostResources< - C: Controller, - const CONNS: usize, - const CHANNELS: usize, - const L2CAP_MTU: usize, - const ADV_SETS: usize = 1, -> { - qos: Qos, - rx_pool: MaybeUninit>, +pub struct HostResources { + rx_pool: MaybeUninit>, #[cfg(feature = "gatt")] - tx_pool: MaybeUninit>, + tx_pool: MaybeUninit>, connections: MaybeUninit<[ConnectionStorage; CONNS]>, - events: MaybeUninit<[EventChannel<'static>; CONNS]>, + events: MaybeUninit<[EventChannel; CONNS]>, channels: MaybeUninit<[ChannelStorage; CHANNELS]>, - channels_rx: MaybeUninit<[PacketChannel<'static, { config::L2CAP_RX_QUEUE_SIZE }>; CHANNELS]>, - sar: MaybeUninit<[SarType<'static>; CONNS]>, + channels_rx: MaybeUninit<[PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>; CHANNELS]>, + sar: MaybeUninit<[SarType; CONNS]>, advertise_handles: MaybeUninit<[AdvHandleState; ADV_SETS]>, - inner: MaybeUninit>, } -impl - HostResources +impl Default + for HostResources { - /// Create a new instance of host resources with the provided QoS requirements for packets. - pub fn new(qos: Qos) -> Self { + fn default() -> Self { + Self::new() + } +} + +impl + HostResources +{ + /// Create a new instance of host resources. + pub const fn new() -> Self { Self { - qos, rx_pool: MaybeUninit::uninit(), #[cfg(feature = "gatt")] tx_pool: MaybeUninit::uninit(), @@ -337,7 +339,6 @@ impl( controller: C, - resources: &'d mut HostResources, -) -> Builder<'d, C> { + resources: &'resources mut HostResources, +) -> Stack<'resources, C> { unsafe fn transmute_slice(x: &mut [T]) -> &'static mut [T] { core::mem::transmute(x) } // Safety: - // - HostResources has the same lifetime as the returned Builder. + // - HostResources has the exceeding lifetime as the returned Stack. // - Internal lifetimes are elided (made 'static) to simplify API usage // - This _should_ be OK, because there are no references held to the resources // when the stack is shut down. - use crate::packet_pool::DynamicPacketPool; - let rx_pool: &'d dyn DynamicPacketPool<'d> = &*resources.rx_pool.write(PacketPool::new(resources.qos)); - let rx_pool = unsafe { - core::mem::transmute::<&'d dyn DynamicPacketPool<'d>, &'static dyn DynamicPacketPool<'static>>(rx_pool) - }; + use crate::packet_pool::Pool; + let rx_pool: &'resources dyn Pool = &*resources.rx_pool.write(PacketPool::new()); + let rx_pool = unsafe { core::mem::transmute::<&'resources dyn Pool, &'static dyn Pool>(rx_pool) }; #[cfg(feature = "gatt")] - let tx_pool: &'d dyn DynamicPacketPool<'d> = &*resources.tx_pool.write(PacketPool::new(PacketQos::None)); + let tx_pool: &'resources dyn Pool = &*resources.tx_pool.write(PacketPool::new()); #[cfg(feature = "gatt")] - let tx_pool = unsafe { - core::mem::transmute::<&'d dyn DynamicPacketPool<'d>, &'static dyn DynamicPacketPool<'static>>(tx_pool) - }; - - let connections = &mut *resources.connections.write([ConnectionStorage::DISCONNECTED; CONNS]); - let connections = unsafe { transmute_slice(connections) }; - let events = &mut *resources.events.write([const { EventChannel::new() }; CONNS]); - let events = unsafe { transmute_slice(events) }; + let tx_pool = unsafe { core::mem::transmute::<&'resources dyn Pool, &'static dyn Pool>(tx_pool) }; + + use crate::l2cap::sar::AssembledPacket; + use crate::types::l2cap::L2capHeader; + use bt_hci::param::ConnHandle; + let connections: &mut [ConnectionStorage] = + &mut *resources.connections.write([ConnectionStorage::DISCONNECTED; CONNS]); + let connections: &'resources mut [ConnectionStorage] = unsafe { transmute_slice(connections) }; + + let events: &mut [EventChannel] = &mut *resources.events.write([EventChannel::NEW; CONNS]); + let events: &'resources mut [EventChannel] = unsafe { transmute_slice(events) }; + let channels = &mut *resources.channels.write([ChannelStorage::DISCONNECTED; CHANNELS]); - let channels = unsafe { transmute_slice(channels) }; - let channels_rx = &mut *resources.channels_rx.write([PacketChannel::NEW; CHANNELS]); - let channels_rx = unsafe { transmute_slice(channels_rx) }; + let channels: &'static mut [ChannelStorage] = unsafe { transmute_slice(channels) }; + + let channels_rx: &mut [PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>] = + &mut *resources.channels_rx.write([PacketChannel::NEW; CHANNELS]); + let channels_rx: &'static mut [PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>] = + unsafe { transmute_slice(channels_rx) }; let sar = &mut *resources.sar.write([const { None }; CONNS]); - let sar = unsafe { transmute_slice(sar) }; + let sar: &'static mut [Option<(ConnHandle, L2capHeader, AssembledPacket)>] = unsafe { transmute_slice(sar) }; let advertise_handles = &mut *resources.advertise_handles.write([AdvHandleState::None; ADV_SETS]); - let advertise_handles = unsafe { transmute_slice(advertise_handles) }; - let host = BleHost::new( + let advertise_handles: &'static mut [AdvHandleState] = unsafe { transmute_slice(advertise_handles) }; + let host: BleHost<'_, C> = BleHost::new( controller, rx_pool, #[cfg(feature = "gatt")] @@ -402,53 +408,40 @@ pub fn new< advertise_handles, ); - let host = &mut *resources.inner.write(host); - let host = unsafe { core::mem::transmute::<&mut BleHost<'_, C>, &'d mut BleHost<'d, C>>(host) }; - Builder { host } + Stack { host } } -/// Type for configuring the BLE host. -pub struct Builder<'d, C: Controller> { - host: &'d mut BleHost<'d, C>, +/// Contains the host stack +pub struct Stack<'stack, C> { + host: BleHost<'stack, C>, } -impl<'d, C: Controller> Builder<'d, C> { +impl<'stack, C: Controller> Stack<'stack, C> { /// Set the random address used by this host. - pub fn set_random_address(self, address: Address) -> Self { + pub fn set_random_address(mut self, address: Address) -> Self { self.host.address.replace(address); self } /// Build the stack. #[cfg(all(feature = "central", feature = "peripheral"))] - pub fn build(self) -> (Stack<'d, C>, Peripheral<'d, C>, Central<'d, C>, Runner<'d, C>) { - let stack = Stack::new(self.host); - (stack, Peripheral::new(stack), Central::new(stack), Runner::new(stack)) + pub fn build(&'stack self) -> (Peripheral<'stack, C>, Central<'stack, C>, Runner<'stack, C>) { + let stack = self; + (Peripheral::new(stack), Central::new(stack), Runner::new(stack)) } /// Build the stack. #[cfg(all(not(feature = "central"), feature = "peripheral"))] - pub fn build(self) -> (Stack<'d, C>, Peripheral<'d, C>, Runner<'d, C>) { - let stack = Stack::new(self.host); - (stack, Peripheral::new(stack), Runner::new(stack)) + pub fn build(&'stack self) -> (Peripheral<'stack, C>, Runner<'stack, C>) { + let stack = self; + (Peripheral::new(stack), Runner::new(stack)) } /// Build the stack. #[cfg(all(feature = "central", not(feature = "peripheral")))] - pub fn build(self) -> (Stack<'d, C>, Central<'d, C>, Runner<'d, C>) { - let stack = Stack::new(self.host); - (stack, Central::new(stack), Runner::new(stack)) - } -} - -/// Handle to the BLE stack. -pub struct Stack<'d, C> { - host: &'d BleHost<'d, C>, -} - -impl<'d, C: Controller> Stack<'d, C> { - pub(crate) fn new(host: &'d BleHost<'d, C>) -> Self { - Self { host } + pub fn build(&'stack self) -> (Central<'stack, C>, Runner<'stack, C>) { + let stack = self; + (Central::new(stack), Runner::new(stack)) } /// Run a HCI command and return the response. @@ -479,11 +472,3 @@ impl<'d, C: Controller> Stack<'d, C> { self.host.log_status(verbose); } } - -impl Clone for Stack<'_, C> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for Stack<'_, C> {} diff --git a/host/src/packet_pool.rs b/host/src/packet_pool.rs index 6723e16b..1272afcc 100644 --- a/host/src/packet_pool.rs +++ b/host/src/packet_pool.rs @@ -1,35 +1,9 @@ //! A packet pool for allocating and freeing packet buffers with quality of service policy. use core::cell::RefCell; -use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; -use crate::types::l2cap::{L2CAP_CID_ATT, L2CAP_CID_DYN_START}; - -// Generic client ID used by ATT PDU -pub(crate) const GENERIC_ID: AllocId = AllocId(0); - -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct AllocId(usize); - -impl AllocId { - pub(crate) fn dynamic(idx: usize) -> AllocId { - // Dynamic range starts at 2 - AllocId(1 + idx) - } - - pub(crate) fn from_channel(cid: u16) -> AllocId { - match cid { - L2CAP_CID_ATT => GENERIC_ID, - cid if cid >= L2CAP_CID_DYN_START => Self::dynamic((cid - L2CAP_CID_DYN_START) as usize), - cid => { - panic!("unexpected channel id {}", cid); - } - } - } -} - struct PacketBuf { buf: [u8; MTU], free: bool, @@ -46,73 +20,23 @@ impl PacketBuf { } } -/// Quality of service policy for packet allocation -#[derive(Clone, Copy, Default)] -pub enum Qos { - /// Distribute evenly among client - Fair, - /// Reserve at least N packets for each client - Guaranteed(usize), - /// No guarantees - #[default] - None, -} - -struct State { +struct State { packets: [PacketBuf; N], - usage: [usize; CLIENTS], } -impl State { +impl State { pub(crate) const fn new() -> Self { Self { packets: [PacketBuf::NEW; N], - usage: [0; CLIENTS], } } - // Guaranteed available - fn min_available(&self, qos: Qos, client: AllocId) -> usize { - let min = match qos { - Qos::None => N.saturating_sub(self.usage.iter().sum()), - Qos::Fair => (N / CLIENTS).saturating_sub(self.usage[client.0]), - Qos::Guaranteed(n) => { - let usage = self.usage[client.0]; - n.saturating_sub(usage) - } - }; - // info!("Min available for {}: {} (usage: {})", client.0, min, usage[client.0]); - min - } - - fn available(&self, qos: Qos, client: AllocId) -> usize { - let available = match qos { - Qos::None => N.saturating_sub(self.usage.iter().sum()), - Qos::Fair => (N / CLIENTS).saturating_sub(self.usage[client.0]), - Qos::Guaranteed(n) => { - // Reserved for clients that should have minimum - let reserved = n * self.usage.iter().filter(|c| **c == 0).count(); - let reserved = reserved - - if self.usage[client.0] < n { - n - self.usage[client.0] - } else { - 0 - }; - let usage = reserved + self.usage.iter().sum::(); - N.saturating_sub(usage) - } - }; - // info!("Available for {}: {} (usage {})", client.0, available, usage[client.0]); - available - } - - fn alloc(&mut self, id: AllocId) -> Option { + fn alloc(&mut self) -> Option { for (idx, packet) in self.packets.iter_mut().enumerate() { if packet.free { // info!("[{}] alloc {}", id.0, idx); packet.free = false; packet.buf.iter_mut().for_each(|b| *b = 0); - self.usage[id.0] += 1; return Some(PacketRef { idx, buf: &mut packet.buf[..], @@ -122,105 +46,86 @@ impl State usize { + self.packets.iter().filter(|p| p.free).count() } } /// A packet pool holds a pool of packet buffers that can be dynamically allocated /// and free'd. -/// -/// The pool has a concept QoS to control quota for multiple clients. -pub struct PacketPool { - state: Mutex>>, - qos: Qos, +pub struct PacketPool { + state: Mutex>>, } -impl PacketPool { +impl Default for PacketPool { + fn default() -> Self { + Self::new() + } +} + +impl PacketPool { /// Create a new packet pool with the given QoS policy - pub fn new(qos: Qos) -> Self { - // Need at least 1 for gatt - assert!(CLIENTS >= 1); - match qos { - Qos::None => {} - Qos::Fair => { - assert!(N >= CLIENTS); - } - Qos::Guaranteed(n) => { - assert!(N >= n); - } - } + pub fn new() -> Self { Self { state: Mutex::new(RefCell::new(State::new())), - qos, } } - fn alloc(&self, id: AllocId) -> Option { + fn alloc(&self) -> Option { self.state.lock(|state| { let mut state = state.borrow_mut(); - let available = state.available(self.qos, id); - if available == 0 { - return None; - } - - state.alloc(id).map(|p_ref| Packet { - client: id, + state.alloc().map(|p_ref| Packet { p_ref: Some(p_ref), pool: self, }) }) } - fn free(&self, id: AllocId, p_ref: PacketRef) { + fn free(&self, p_ref: PacketRef) { self.state.lock(|state| { let mut state = state.borrow_mut(); - state.free(id, p_ref); + state.free(p_ref); }); } - fn min_available(&self, id: AllocId) -> usize { - self.state.lock(|state| { - let state = state.borrow(); - state.min_available(self.qos, id) - }) - } - - fn available(&self, id: AllocId) -> usize { + fn available(&self) -> usize { self.state.lock(|state| { - let state = state.borrow(); - state.available(self.qos, id) + let mut state = state.borrow_mut(); + state.available() }) } } -pub(crate) trait DynamicPacketPool<'d> { - fn alloc(&'d self, id: AllocId) -> Option>; - fn free(&self, id: AllocId, r: PacketRef); - fn available(&self, id: AllocId) -> usize; - fn min_available(&self, id: AllocId) -> usize; +/// Type erased packet pool +pub(crate) trait Pool { + /// Allocate a packet + /// + /// Returns None if out of memory. + fn alloc(&self) -> Option; + /// Free a packet given it's reference. + fn free(&self, r: PacketRef); + /// Check for available packets. + fn available(&self) -> usize; + /// Check packet size. fn mtu(&self) -> usize; } -impl<'d, M: RawMutex, const MTU: usize, const N: usize, const CLIENTS: usize> DynamicPacketPool<'d> - for PacketPool -{ - fn alloc(&'d self, id: AllocId) -> Option> { - PacketPool::alloc(self, id) +impl Pool for PacketPool { + fn alloc(&self) -> Option { + PacketPool::alloc(self) } - fn min_available(&self, id: AllocId) -> usize { - PacketPool::min_available(self, id) + fn free(&self, r: PacketRef) { + PacketPool::free(self, r) } - fn available(&self, id: AllocId) -> usize { - PacketPool::available(self, id) - } - - fn free(&self, id: AllocId, r: PacketRef) { - PacketPool::free(self, id, r) + fn available(&self) -> usize { + PacketPool::available(self) } fn mtu(&self) -> usize { @@ -228,18 +133,19 @@ impl<'d, M: RawMutex, const MTU: usize, const N: usize, const CLIENTS: usize> Dy } } +#[repr(C)] pub(crate) struct PacketRef { idx: usize, buf: *mut [u8], } -pub(crate) struct Packet<'d> { - client: AllocId, +#[repr(C)] +pub(crate) struct Packet { p_ref: Option, - pool: &'d dyn DynamicPacketPool<'d>, + pool: *const dyn Pool, } -impl Packet<'_> { +impl Packet { pub(crate) fn len(&self) -> usize { self.as_ref().len() } @@ -249,22 +155,23 @@ impl Packet<'_> { } } -impl Drop for Packet<'_> { +impl Drop for Packet { fn drop(&mut self) { if let Some(r) = self.p_ref.take() { - self.pool.free(self.client, r); + let pool = unsafe { &*self.pool }; + pool.free(r); } } } -impl AsRef<[u8]> for Packet<'_> { +impl AsRef<[u8]> for Packet { fn as_ref(&self) -> &[u8] { let p = self.p_ref.as_ref().unwrap(); unsafe { &(*p.buf)[..] } } } -impl AsMut<[u8]> for Packet<'_> { +impl AsMut<[u8]> for Packet { fn as_mut(&mut self) -> &mut [u8] { let p = self.p_ref.as_mut().unwrap(); unsafe { &mut (*p.buf)[..] } @@ -273,110 +180,34 @@ impl AsMut<[u8]> for Packet<'_> { #[cfg(test)] mod tests { - use embassy_sync::blocking_mutex::raw::NoopRawMutex; use static_cell::StaticCell; use super::*; - #[test] - fn test_fair_qos() { - static POOL: StaticCell> = StaticCell::new(); - let pool = POOL.init(PacketPool::new(Qos::Fair)); - - let a1 = pool.alloc(AllocId(0)); - assert!(a1.is_some()); - let a2 = pool.alloc(AllocId(0)); - assert!(a2.is_some()); - assert!(pool.alloc(AllocId(0)).is_none()); - drop(a2); - let a3 = pool.alloc(AllocId(0)); - assert!(a3.is_some()); - - let b1 = pool.alloc(AllocId(1)); - assert!(b1.is_some()); - - let c1 = pool.alloc(AllocId(2)); - assert!(c1.is_some()); - } - #[test] fn test_none_qos() { - static POOL: StaticCell> = StaticCell::new(); - let pool = POOL.init(PacketPool::new(Qos::None)); + static POOL: StaticCell> = StaticCell::new(); + let pool = POOL.init(PacketPool::new()); - let a1 = pool.alloc(AllocId(0)); + let a1 = pool.alloc(); assert!(a1.is_some()); - let a2 = pool.alloc(AllocId(0)); + let a2 = pool.alloc(); assert!(a2.is_some()); - let a3 = pool.alloc(AllocId(0)); + let a3 = pool.alloc(); assert!(a3.is_some()); - let a4 = pool.alloc(AllocId(0)); + let a4 = pool.alloc(); assert!(a4.is_some()); - let a5 = pool.alloc(AllocId(0)); + let a5 = pool.alloc(); assert!(a5.is_some()); - let a6 = pool.alloc(AllocId(0)); + let a6 = pool.alloc(); assert!(a6.is_some()); - let a7 = pool.alloc(AllocId(0)); + let a7 = pool.alloc(); assert!(a7.is_some()); - let b1 = pool.alloc(AllocId(1)); + let b1 = pool.alloc(); assert!(b1.is_some()); - let b2 = pool.alloc(AllocId(1)); + let b2 = pool.alloc(); assert!(b2.is_none()); } - - #[test] - fn test_guaranteed_qos() { - static POOL: StaticCell> = StaticCell::new(); - let pool = POOL.init(PacketPool::new(Qos::Guaranteed(1))); - - let a1 = pool.alloc(AllocId(0)); - assert!(a1.is_some()); - let a2 = pool.alloc(AllocId(0)); - assert!(a2.is_some()); - let a3 = pool.alloc(AllocId(0)); - assert!(a3.is_some()); - let a4 = pool.alloc(AllocId(0)); - assert!(a4.is_some()); - let a5 = pool.alloc(AllocId(0)); - assert!(a5.is_some()); - // Needs at least 3 for the other clients - assert!(pool.alloc(AllocId(0)).is_none()); - - let b1 = pool.alloc(AllocId(1)); - assert!(b1.is_some()); - assert!(pool.alloc(AllocId(1)).is_none()); - - let c1 = pool.alloc(AllocId(2)); - assert!(c1.is_some()); - assert!(pool.alloc(AllocId(2)).is_none()); - - let d1 = pool.alloc(AllocId(3)); - assert!(d1.is_some()); - assert!(pool.alloc(AllocId(3)).is_none()); - } - - #[test] - fn test_guaranteed_qos_many() { - static POOL: StaticCell> = StaticCell::new(); - let pool = POOL.init(PacketPool::new(Qos::Guaranteed(1))); - - let a1 = pool.alloc(AllocId(0)); - assert!(a1.is_some()); - // Needs at least 1 for the other clients - assert!(pool.alloc(AllocId(0)).is_none()); - - let b1 = pool.alloc(AllocId(1)); - assert!(b1.is_some()); - assert!(pool.alloc(AllocId(1)).is_none()); - - let c1 = pool.alloc(AllocId(2)); - assert!(c1.is_some()); - assert!(pool.alloc(AllocId(2)).is_none()); - - let d1 = pool.alloc(AllocId(3)); - assert!(d1.is_some()); - assert!(pool.alloc(AllocId(3)).is_none()); - } } diff --git a/host/src/pdu.rs b/host/src/pdu.rs index 7998bfad..551da23a 100644 --- a/host/src/pdu.rs +++ b/host/src/pdu.rs @@ -1,23 +1,23 @@ use crate::packet_pool::Packet; -pub(crate) struct Pdu<'d> { - pub packet: Packet<'d>, +pub(crate) struct Pdu { + pub packet: Packet, pub len: usize, } -impl<'d> Pdu<'d> { - pub(crate) fn new(packet: Packet<'d>, len: usize) -> Self { +impl Pdu { + pub(crate) fn new(packet: Packet, len: usize) -> Self { Self { packet, len } } } -impl AsRef<[u8]> for Pdu<'_> { +impl AsRef<[u8]> for Pdu { fn as_ref(&self) -> &[u8] { &self.packet.as_ref()[..self.len] } } -impl AsMut<[u8]> for Pdu<'_> { +impl AsMut<[u8]> for Pdu { fn as_mut(&mut self) -> &mut [u8] { &mut self.packet.as_mut()[..self.len] } diff --git a/host/src/peripheral.rs b/host/src/peripheral.rs index 4625b7c0..788ef3a6 100644 --- a/host/src/peripheral.rs +++ b/host/src/peripheral.rs @@ -14,11 +14,11 @@ use crate::{Address, BleHostError, Error, Stack}; /// Type which implements the BLE peripheral role. pub struct Peripheral<'d, C: Controller> { - stack: Stack<'d, C>, + stack: &'d Stack<'d, C>, } impl<'d, C: Controller> Peripheral<'d, C> { - pub(crate) fn new(stack: Stack<'d, C>) -> Self { + pub(crate) fn new(stack: &'d Stack<'d, C>) -> Self { Self { stack } } @@ -34,7 +34,7 @@ impl<'d, C: Controller> Peripheral<'d, C> { + for<'t> ControllerCmdSync + for<'t> ControllerCmdSync, { - let host = self.stack.host; + let host = &self.stack.host; // Ensure no other advertise ongoing. let drop = crate::host::OnDrop::new(|| { @@ -128,7 +128,7 @@ impl<'d, C: Controller> Peripheral<'d, C> { + for<'t> ControllerCmdSync>, { assert_eq!(sets.len(), handles.len()); - let host = self.stack.host; + let host = &self.stack.host; // Check host supports the required advertisement sets { let result = host.command(LeReadNumberOfSupportedAdvSets::new()).await?; @@ -219,7 +219,7 @@ impl<'d, C: Controller> Peripheral<'d, C> { /// Handle to an active advertiser which can accept connections. pub struct Advertiser<'d, C: Controller> { - stack: Stack<'d, C>, + stack: &'d Stack<'d, C>, extended: bool, done: bool, } diff --git a/host/src/scan.rs b/host/src/scan.rs index d84a30dc..a2e02b73 100644 --- a/host/src/scan.rs +++ b/host/src/scan.rs @@ -1,6 +1,6 @@ //! Scan config. -use crate::central::ScanConfig; use crate::command::CommandState; +use crate::connection::ScanConfig; use crate::host::ScanState; use crate::BleHostError; use crate::Error; @@ -54,7 +54,7 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { + ControllerCmdSync + ControllerCmdSync, { - let host = self.central.stack.host; + let host = &self.central.stack.host; let drop = crate::host::OnDrop::new(|| { host.scan_command_state.cancel(false); host.scan_state.stop(); @@ -68,7 +68,7 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { scan_window: config.window.into(), }; let phy_params = crate::central::create_phy_params(scanning, config.phys); - let host = self.central.stack.host; + let host = &self.central.stack.host; host.command(LeSetExtScanParams::new( host.address.map(|s| s.kind).unwrap_or(AddrKind::PUBLIC), if config.filter_accept_list.is_empty() { @@ -118,7 +118,7 @@ impl<'d, C: Controller, const BUFFER_SIZE: usize> Scanner<'d, C, BUFFER_SIZE> { + ControllerCmdSync + ControllerCmdSync, { - let host = self.central.stack.host; + let host = &self.central.stack.host; let drop = crate::host::OnDrop::new(|| { host.scan_command_state.cancel(false); host.scan_state.stop(); diff --git a/host/tests/common.rs b/host/tests/common.rs index 78d47d68..4004a754 100644 --- a/host/tests/common.rs +++ b/host/tests/common.rs @@ -7,6 +7,7 @@ use tokio::io::{ReadHalf, WriteHalf}; use tokio::time::Duration; use tokio_serial::{DataBits, Parity, SerialStream, StopBits}; +#[allow(dead_code)] pub type Controller = ExternalController< SerialTransport>, FromTokio>>, 10, diff --git a/host/tests/gatt.rs b/host/tests/gatt.rs index 02584ee1..f3c29460 100644 --- a/host/tests/gatt.rs +++ b/host/tests/gatt.rs @@ -31,10 +31,10 @@ async fn gatt_client_server() { let peripheral = local.spawn_local(async move { let controller_peripheral = common::create_controller(&peripheral).await; - let mut resources: HostResources = HostResources::new(PacketQos::None); - let (_stack, mut peripheral, _central, mut runner) = trouble_host::new(controller_peripheral, &mut resources) - .set_random_address(peripheral_address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller_peripheral, &mut resources) + .set_random_address(peripheral_address); + let (mut peripheral, _, mut runner) = stack.build(); let id = b"Trouble"; let appearance = [0x80, 0x07]; @@ -127,10 +127,9 @@ async fn gatt_client_server() { // Spawn central let central = local.spawn_local(async move { let controller_central = common::create_controller(¢ral).await; - let mut resources: HostResources = - HostResources::new(PacketQos::None); - let (stack, _peripheral, mut central, mut runner) = - trouble_host::new(controller_central, &mut resources).build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller_central, &mut resources); + let (_, mut central, mut runner) = stack.build(); select! { r = runner.run() => { @@ -152,7 +151,7 @@ async fn gatt_client_server() { tokio::time::sleep(Duration::from_secs(5)).await; println!("[central] creating gatt client"); - let client = GattClient::::new(stack, &conn).await.unwrap(); + let client = GattClient::::new(&stack, &conn).await.unwrap(); select! { r = async { diff --git a/host/tests/gatt_derive.rs b/host/tests/gatt_derive.rs index e14d3698..5ebf44cd 100644 --- a/host/tests/gatt_derive.rs +++ b/host/tests/gatt_derive.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use std::time::Duration; use embassy_sync::blocking_mutex::raw::NoopRawMutex; @@ -70,10 +71,11 @@ async fn gatt_client_server() { let peripheral = local.spawn_local(async move { let controller_peripheral = common::create_controller(&peripheral).await; - let mut resources: HostResources = HostResources::new(PacketQos::None); - let (_, mut peripheral, _central, mut runner) = trouble_host::new(controller_peripheral, &mut resources) - .set_random_address(peripheral_address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller_peripheral, &mut resources) + .set_random_address(peripheral_address); + let (mut peripheral, _, mut runner) = stack.build(); + let gap = GapConfig::Peripheral(PeripheralConfig { name: &name, appearance: &appearance::power_device::GENERIC_POWER_DEVICE, @@ -154,10 +156,9 @@ async fn gatt_client_server() { // Spawn central let central = local.spawn_local(async move { let controller_central = common::create_controller(¢ral).await; - let mut resources: HostResources = - HostResources::new(PacketQos::None); - let (stack, _peripheral, mut central, mut runner) = - trouble_host::new(controller_central, &mut resources).build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller_central, &mut resources); + let (_, mut central, mut runner) = stack.build(); select! { r = runner.run() => { @@ -179,7 +180,7 @@ async fn gatt_client_server() { tokio::time::sleep(Duration::from_secs(5)).await; println!("[central] creating gatt client"); - let client = GattClient::::new(stack, &conn).await.unwrap(); + let client = GattClient::::new(&stack, &conn).await.unwrap(); select! { r = async { diff --git a/host/tests/l2cap.rs b/host/tests/l2cap.rs index 912f3f34..1a39a58c 100644 --- a/host/tests/l2cap.rs +++ b/host/tests/l2cap.rs @@ -26,10 +26,10 @@ async fn l2cap_connection_oriented_channels() { let peripheral = local.spawn_local(async move { let controller_peripheral = common::create_controller(&peripheral).await; - let mut resources: HostResources = HostResources::new(PacketQos::None); - let (stack, mut peripheral, _central, mut runner) = trouble_host::new(controller_peripheral, &mut resources) - .set_random_address(peripheral_address) - .build(); + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller_peripheral, &mut resources) + .set_random_address(peripheral_address); + let (mut peripheral, _, mut runner) = stack.build(); select! { r = runner.run() => { @@ -57,14 +57,14 @@ async fn l2cap_connection_oriented_channels() { let conn = acceptor.accept().await?; println!("[peripheral] connected"); - let mut ch1 = L2capChannel::accept(stack, &conn, &[0x2349], &Default::default()).await?; + let mut ch1 = L2capChannel::accept(&stack, &conn, &[0x2349], &Default::default()).await?; println!("[peripheral] channel created"); // Size of payload we're expecting let mut rx = [0; PAYLOAD_LEN]; for i in 0..10 { - let len = ch1.receive(stack, &mut rx).await?; + let len = ch1.receive(&stack, &mut rx).await?; assert_eq!(len, rx.len()); assert_eq!(rx, [i; PAYLOAD_LEN]); } @@ -72,7 +72,7 @@ async fn l2cap_connection_oriented_channels() { for i in 0..10 { let tx = [i; PAYLOAD_LEN]; - ch1.send::<_, MTU>(stack, &tx).await?; + ch1.send::<_, MTU>(&stack, &tx).await?; } println!("[peripheral] data sent"); break; @@ -87,10 +87,10 @@ async fn l2cap_connection_oriented_channels() { // Spawn central let central = local.spawn_local(async move { let controller_central = common::create_controller(¢ral).await; - let mut resources: HostResources = - HostResources::new(PacketQos::None); - let (stack, _peripheral, mut central, mut runner) = - trouble_host::new(controller_central, &mut resources).build(); + let mut resources: HostResources = HostResources::new(); + + let stack = trouble_host::new(controller_central, &mut resources); + let (_, mut central, mut runner) = stack.build(); select! { r = runner.run() => { @@ -110,16 +110,16 @@ async fn l2cap_connection_oriented_channels() { loop { let conn = central.connect(&config).await.unwrap(); println!("[central] connected"); - let mut ch1 = L2capChannel::create(stack, &conn, 0x2349, &Default::default()).await?; + let mut ch1 = L2capChannel::create(&stack, &conn, 0x2349, &Default::default()).await?; println!("[central] channel created"); for i in 0..10 { let tx = [i; PAYLOAD_LEN]; - ch1.send::<_, MTU>(stack, &tx).await?; + ch1.send::<_, MTU>(&stack, &tx).await?; } println!("[central] data sent"); let mut rx = [0; PAYLOAD_LEN]; for i in 0..10 { - let len = ch1.receive(stack, &mut rx).await?; + let len = ch1.receive(&stack, &mut rx).await?; assert_eq!(len, rx.len()); assert_eq!(rx, [i; PAYLOAD_LEN]); }