From b762fac45a174a55fa5b8a0d6e46a521a2074873 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 23 May 2024 11:19:48 +0200 Subject: [PATCH 1/6] add features for l2cap pool size and queue len --- examples/apache-nimble/src/main.rs | 2 +- .../nrf-sdc/src/bin/ble_advertise_multiple.rs | 8 +- .../nrf-sdc/src/bin/ble_bas_peripheral.rs | 8 +- examples/nrf-sdc/src/bin/ble_l2cap_central.rs | 8 +- .../nrf-sdc/src/bin/ble_l2cap_peripheral.rs | 8 +- examples/serial-hci/src/main.rs | 2 +- host/Cargo.toml | 31 +++++- host/build.rs | 94 +++++++++++++++++++ host/gen_config.py | 82 ++++++++++++++++ host/src/config.rs | 57 +++++++++++ host/src/gatt.rs | 41 ++++---- host/src/host.rs | 36 +++---- host/src/lib.rs | 1 + host/tests/l2cap.rs | 4 +- 14 files changed, 306 insertions(+), 76 deletions(-) create mode 100644 host/build.rs create mode 100644 host/gen_config.py create mode 100644 host/src/config.rs diff --git a/examples/apache-nimble/src/main.rs b/examples/apache-nimble/src/main.rs index c14d6051..73b99ddb 100644 --- a/examples/apache-nimble/src/main.rs +++ b/examples/apache-nimble/src/main.rs @@ -30,7 +30,7 @@ async fn main(spawner: embassy_executor::Spawner) { embassy_nrf::init(conf); apache_nimble::initialize_nimble(); - static HOST_RESOURCE: StaticCell> = StaticCell::new(); + static HOST_RESOURCE: StaticCell> = StaticCell::new(); let host_resources = HOST_RESOURCE.init(BleHostResources::new(PacketQos::None)); let controller = apache_nimble::controller::NimbleController::new(); diff --git a/examples/nrf-sdc/src/bin/ble_advertise_multiple.rs b/examples/nrf-sdc/src/bin/ble_advertise_multiple.rs index dc8c7909..a953fc42 100644 --- a/examples/nrf-sdc/src/bin/ble_advertise_multiple.rs +++ b/examples/nrf-sdc/src/bin/ble_advertise_multiple.rs @@ -50,9 +50,6 @@ const CONNECTIONS_MAX: usize = 1; /// Max number of L2CAP channels. const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att -/// Number of packets available in the pool -const PACKET_POOL_SIZE: usize = 10; - fn build_sdc<'d, const N: usize>( p: nrf_sdc::Peripherals<'d>, rng: &'d RngPool, @@ -107,9 +104,8 @@ async fn main(spawner: Spawner) { info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; - static HOST_RESOURCES: StaticCell< - BleHostResources, - > = StaticCell::new(); + static HOST_RESOURCES: StaticCell> = + StaticCell::new(); let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); let mut ble: BleHost<'_, _> = BleHost::new(sdc, host_resources); diff --git a/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs b/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs index 6df97f3d..2feda4a9 100644 --- a/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs +++ b/examples/nrf-sdc/src/bin/ble_bas_peripheral.rs @@ -49,9 +49,6 @@ const CONNECTIONS_MAX: usize = 1; /// Max number of L2CAP channels. const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att -/// Number of packets available in the pool -const PACKET_POOL_SIZE: usize = 10; - fn build_sdc<'d, const N: usize>( p: nrf_sdc::Peripherals<'d>, rng: &'d RngPool, @@ -105,9 +102,8 @@ async fn main(spawner: Spawner) { info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; - static HOST_RESOURCES: StaticCell< - BleHostResources, - > = StaticCell::new(); + static HOST_RESOURCES: StaticCell> = + StaticCell::new(); let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); let mut ble: BleHost<'_, _> = BleHost::new(sdc, host_resources); diff --git a/examples/nrf-sdc/src/bin/ble_l2cap_central.rs b/examples/nrf-sdc/src/bin/ble_l2cap_central.rs index 143d948c..b023acee 100644 --- a/examples/nrf-sdc/src/bin/ble_l2cap_central.rs +++ b/examples/nrf-sdc/src/bin/ble_l2cap_central.rs @@ -55,9 +55,6 @@ const CONNECTIONS_MAX: usize = 1; /// Max number of L2CAP channels. const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC -/// Number of packets available in the pool -const PACKET_POOL_SIZE: usize = (L2CAP_TXQ + L2CAP_RXQ) as usize; - fn build_sdc<'d, const N: usize>( p: nrf_sdc::Peripherals<'d>, rng: &'d RngPool, @@ -112,9 +109,8 @@ async fn main(spawner: Spawner) { info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; - static HOST_RESOURCES: StaticCell< - BleHostResources, - > = StaticCell::new(); + static HOST_RESOURCES: StaticCell> = + StaticCell::new(); let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); let mut ble: BleHost<'_, _> = BleHost::new(sdc, host_resources); diff --git a/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs b/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs index 09c3244d..17a889e2 100644 --- a/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs +++ b/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs @@ -54,9 +54,6 @@ const CONNECTIONS_MAX: usize = 1; /// Max number of L2CAP channels. const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC -/// Number of packets available in the pool -const PACKET_POOL_SIZE: usize = (L2CAP_TXQ + L2CAP_RXQ) as usize; - fn build_sdc<'d, const N: usize>( p: nrf_sdc::Peripherals<'d>, rng: &'d RngPool, @@ -111,9 +108,8 @@ async fn main(spawner: Spawner) { info!("Our address = {:02x}", my_addr()); Timer::after(Duration::from_millis(200)).await; - static HOST_RESOURCES: StaticCell< - BleHostResources, - > = StaticCell::new(); + static HOST_RESOURCES: StaticCell> = + StaticCell::new(); let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); let mut ble: BleHost<'_, _> = BleHost::new(sdc, host_resources); diff --git a/examples/serial-hci/src/main.rs b/examples/serial-hci/src/main.rs index 3feb2d9f..7d976297 100644 --- a/examples/serial-hci/src/main.rs +++ b/examples/serial-hci/src/main.rs @@ -54,7 +54,7 @@ async fn main() { let driver: SerialTransport = SerialTransport::new(reader, writer); let controller: ExternalController<_, 10> = ExternalController::new(driver); - static HOST_RESOURCES: StaticCell> = StaticCell::new(); + static HOST_RESOURCES: StaticCell> = StaticCell::new(); let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources); diff --git a/host/Cargo.toml b/host/Cargo.toml index 07e3e491..7c3ceea7 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -36,10 +36,35 @@ env_logger = "0.11" critical-section = { version = "1", features = ["std"] } static_cell = "2.1.0" -[features] -defmt = [ "dep:defmt" ] -gatt = [] [patch.crates-io] bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", branch = "main" } embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", branch = "main" } + + +[features] +defmt = [ "dep:defmt" ] +gatt = [] + +# BEGIN AUTOGENERATED CONFIG FEATURES +# Generated by gen_config.py. DO NOT EDIT. +l2cap-rx-queue-size-1 = [] # Default +l2cap-rx-queue-size-2 = [] +l2cap-rx-queue-size-4 = [] +l2cap-rx-queue-size-8 = [] +l2cap-rx-queue-size-16 = [] +l2cap-rx-queue-size-32 = [] +l2cap-rx-queue-size-64 = [] + +l2cap-rx-packet-pool-size-1 = [] +l2cap-rx-packet-pool-size-2 = [] # Default +l2cap-rx-packet-pool-size-4 = [] +l2cap-rx-packet-pool-size-8 = [] +l2cap-rx-packet-pool-size-16 = [] +l2cap-rx-packet-pool-size-32 = [] +l2cap-rx-packet-pool-size-64 = [] +l2cap-rx-packet-pool-size-128 = [] +l2cap-rx-packet-pool-size-256 = [] +l2cap-rx-packet-pool-size-512 = [] + +# END AUTOGENERATED CONFIG FEATURES diff --git a/host/build.rs b/host/build.rs new file mode 100644 index 00000000..eb763279 --- /dev/null +++ b/host/build.rs @@ -0,0 +1,94 @@ +use std::collections::HashMap; +use std::fmt::Write; +use std::path::PathBuf; +use std::{env, fs}; + +static CONFIGS: &[(&str, usize)] = &[ + // BEGIN AUTOGENERATED CONFIG FEATURES + // Generated by gen_config.py. DO NOT EDIT. + ("L2CAP_RX_QUEUE_SIZE", 1), + ("L2CAP_RX_PACKET_POOL_SIZE", 2), + // END AUTOGENERATED CONFIG FEATURES +]; + +struct ConfigState { + value: usize, + seen_feature: bool, + seen_env: bool, +} + +fn main() { + let crate_name = env::var("CARGO_PKG_NAME") + .unwrap() + .to_ascii_uppercase() + .replace('-', "_"); + + // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any + // other file changed. + println!("cargo:rerun-if-changed=build.rs"); + + // Rebuild if config envvar changed. + for (name, _) in CONFIGS { + println!("cargo:rerun-if-env-changed={crate_name}_{name}"); + } + + let mut configs = HashMap::new(); + for (name, default) in CONFIGS { + configs.insert( + *name, + ConfigState { + value: *default, + seen_env: false, + seen_feature: false, + }, + ); + } + + let prefix = format!("{crate_name}_"); + for (var, value) in env::vars() { + if let Some(name) = var.strip_prefix(&prefix) { + let Some(cfg) = configs.get_mut(name) else { + panic!("Unknown env var {name}") + }; + + let Ok(value) = value.parse::() else { + panic!("Invalid value for env var {name}: {value}") + }; + + cfg.value = value; + cfg.seen_env = true; + } + + if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") { + if let Some(i) = feature.rfind('_') { + let name = &feature[..i]; + let value = &feature[i + 1..]; + if let Some(cfg) = configs.get_mut(name) { + let Ok(value) = value.parse::() else { + panic!("Invalid value for feature {name}: {value}") + }; + + // envvars take priority. + if !cfg.seen_env { + if cfg.seen_feature { + panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value); + } + + cfg.value = value; + cfg.seen_feature = true; + } + } + } + } + } + + let mut data = String::new(); + + for (name, cfg) in &configs { + writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap(); + } + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); + fs::write(out_file, data).unwrap(); +} diff --git a/host/gen_config.py b/host/gen_config.py new file mode 100644 index 00000000..49acefe6 --- /dev/null +++ b/host/gen_config.py @@ -0,0 +1,82 @@ +import os + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +features = [] + + +def feature(name, default, min=None, max=None, pow2=None, vals=None, factors=[]): + if vals is None: + assert min is not None + assert max is not None + + vals = set() + val = min + while val <= max: + vals.add(val) + for f in factors: + if val*f <= max: + vals.add(val*f) + if (pow2 == True or (isinstance(pow2, int) and val >= pow2)) and val > 0: + val *= 2 + else: + val += 1 + vals.add(default) + vals = sorted(list(vals)) + + features.append( + { + "name": name, + "default": default, + "vals": vals, + } + ) + + +feature("l2cap_rx_queue_size", default=1, min=1, max=64, pow2=True) +feature("l2cap_rx_packet_pool_size", default=2, min=1, max=512, pow2=True) + +# ========= Update Cargo.toml + +things = "" +for f in features: + name = f["name"].replace("_", "-") + for val in f["vals"]: + things += f"{name}-{val} = []" + if val == f["default"]: + things += " # Default" + things += "\n" + things += "\n" + +SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n" +HELP = "# Generated by gen_config.py. DO NOT EDIT.\n" +with open("Cargo.toml", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after +with open("Cargo.toml", "w") as f: + f.write(data) + + +# ========= Update build.rs + +things = "" +for f in features: + name = f["name"].upper() + things += f' ("{name}", {f["default"]}),\n' + +SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n" +HELP = " // Generated by gen_config.py. DO NOT EDIT.\n" +with open("build.rs", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + \ + things + " " + SEPARATOR_END + after +with open("build.rs", "w") as f: + f.write(data) diff --git a/host/src/config.rs b/host/src/config.rs new file mode 100644 index 00000000..99bb5d0e --- /dev/null +++ b/host/src/config.rs @@ -0,0 +1,57 @@ +//! Compile-time configuration. +//! +//! `trouble` has some configuration settings that are set at compile time. +//! +//! They can be set in two ways: +//! +//! - Via Cargo features: enable a feature like `-`. `name` must be in lowercase and +//! use dashes instead of underscores. For example. `max-page-count-1024`. Only a selection of values +//! is available, check `Cargo.toml` for the list. +//! - Via environment variables at build time: set the variable named `TROUBLE_HOST_`. For example +//! `TROUBLE_HOST_L2CAP_RX_QUEUE_SIZE=1 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. +//! Any value can be set, unlike with Cargo features. +//! +//! Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting +//! with different values, compilation fails. +//! +//! ## Compatibility warning +//! +//! Changing ANY of these configuration settings changes the on-disk format of the database. If you change +//! them, you won't be able to read databases written with a different configuration. +//! +//! Currently, mounting doesn't check the on-disk database uses the same configuration. Mounting a database +//! with a different configuration might succeed and then cause fun errors later, perhaps very rarely. +//! Always remember to format your flash device every time you change them. + +mod raw { + #![allow(unused)] + include!(concat!(env!("OUT_DIR"), "/config.rs")); +} + +// ======== L2CAP parameters + +/// L2CAP RX queue size +/// +/// This is the rx queue size of every l2cap channel. Every channel have to be able +/// to buffer at least 1 packet, but if the controller already does buffering this +/// may be sufficient. +/// +/// If the controller does not support rx buffering, increasing this value will allow +/// a higher throughput between the controller and host. +/// +/// Default: 1. +pub const L2CAP_RX_QUEUE_SIZE: usize = raw::L2CAP_RX_QUEUE_SIZE; + +/// L2CAP RX packet pool size +/// +/// This is the rx packet pool size of every l2cap channel. There has to be at least +/// 1 packet that can be allocated, but the pool is shared among different channels. +/// +/// If the rx queue size is adjusted, consider adjusting the rx packet pool size as well, +/// taking the number of channels and per-channel queue size into account. +/// +/// Ensuring fair access to the pool is done configuring the QoS policy when creating +/// the host resources. +/// +/// Default: 1. +pub const L2CAP_RX_PACKET_POOL_SIZE: usize = raw::L2CAP_RX_PACKET_POOL_SIZE; diff --git a/host/src/gatt.rs b/host/src/gatt.rs index 84370282..a6fc7fe4 100644 --- a/host/src/gatt.rs +++ b/host/src/gatt.rs @@ -12,31 +12,34 @@ use crate::connection::Connection; use crate::connection_manager::DynamicConnectionManager; use crate::cursor::WriteCursor; use crate::host::BleHost; -use crate::packet_pool::{AllocId, GlobalPacketPool}; use crate::pdu::Pdu; use crate::{BleHostError, Error}; -pub struct GattServer<'reference, 'values, 'resources, M: RawMutex, T: Controller, const MAX: usize> { +pub struct GattServer< + 'reference, + 'values, + 'resources, + M: RawMutex, + T: Controller, + const MAX: usize, + const ATT_MTU: usize = 23, +> { pub(crate) server: AttributeServer<'reference, 'values, M, MAX>, pub(crate) rx: DynamicReceiver<'reference, (ConnHandle, Pdu)>, pub(crate) tx: &'reference BleHost<'resources, T>, - pub(crate) pool_id: AllocId, - pub(crate) pool: &'static dyn GlobalPacketPool, pub(crate) connections: &'reference dyn DynamicConnectionManager, } -impl<'reference, 'values, 'resources, M: RawMutex, T: Controller, const MAX: usize> - GattServer<'reference, 'values, 'resources, M, T, MAX> +impl<'reference, 'values, 'resources, M: RawMutex, T: Controller, const MAX: usize, const ATT_MTU: usize> + GattServer<'reference, 'values, 'resources, M, T, MAX, ATT_MTU> { pub async fn next(&self) -> Result, BleHostError> { loop { let (handle, pdu) = self.rx.receive().await; match Att::decode(pdu.as_ref()) { Ok(att) => { - let Some(mut response) = self.pool.alloc(self.pool_id) else { - return Err(Error::OutOfMemory.into()); - }; - let mut w = WriteCursor::new(response.as_mut()); + let mut tx = [0; ATT_MTU]; + let mut w = WriteCursor::new(&mut tx); let (mut header, mut data) = w.split(4)?; match self.server.process(handle, att, data.write_buf()) { @@ -47,11 +50,7 @@ impl<'reference, 'values, 'resources, M: RawMutex, T: Controller, const MAX: usi header.write(written as u16)?; header.write(4_u16)?; let len = header.len() + data.len(); - self.tx - .acl(handle, 1) - .await? - .send(Pdu::new(response, len).as_ref()) - .await?; + self.tx.acl(handle, 1).await?.send(&tx[..len]).await?; } Ok(None) => { debug!("No response sent"); @@ -89,10 +88,8 @@ impl<'reference, 'values, 'resources, M: RawMutex, T: Controller, const MAX: usi return Ok(()); } - let Some(mut packet) = self.pool.alloc(self.pool_id) else { - return Err(Error::OutOfMemory.into()); - }; - let mut w = WriteCursor::new(packet.as_mut()); + let mut tx = [0; ATT_MTU]; + let mut w = WriteCursor::new(&mut tx[..]); let (mut header, mut data) = w.split(4)?; data.write(ATT_HANDLE_VALUE_NTF_OPTCODE)?; data.write(handle.handle)?; @@ -101,11 +98,7 @@ impl<'reference, 'values, 'resources, M: RawMutex, T: Controller, const MAX: usi header.write(data.len() as u16)?; header.write(4_u16)?; let total = header.len() + data.len(); - self.tx - .acl(conn, 1) - .await? - .send(Pdu::new(packet, total).as_ref()) - .await?; + self.tx.acl(conn, 1).await?.send(&tx[..total]).await?; Ok(()) } } diff --git a/host/src/host.rs b/host/src/host.rs index 26912f2a..b436fbe2 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -42,31 +42,27 @@ use crate::scan::{PhySet, ScanConfig, ScanReport}; use crate::types::l2cap::{ L2capHeader, L2capSignal, L2capSignalHeader, L2CAP_CID_ATT, L2CAP_CID_DYN_START, L2CAP_CID_LE_U_SIGNAL, }; -use crate::{att, Address, BleHostError, Error}; +use crate::{att, config, Address, BleHostError, Error}; #[cfg(feature = "gatt")] use crate::{attribute::AttributeTable, gatt::GattServer}; -const L2CAP_RXQ: usize = 1; - /// BleHostResources holds the resources used by the host. /// -/// The packet pool is used by the host to multiplex data streams, by allocating space for +/// 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 BleHostResources { - pool: PacketPool, +pub struct BleHostResources { + rx_pool: PacketPool, connections: [ConnectionStorage; CONNS], channels: [ChannelStorage; CHANNELS], - channels_rx: [PacketChannel; CHANNELS], + channels_rx: [PacketChannel<{ config::L2CAP_RX_QUEUE_SIZE }>; CHANNELS], sar: [SarType; CONNS], } -impl - BleHostResources -{ +impl BleHostResources { /// Create a new instance of host resources with the provided QoS requirements for packets. pub fn new(qos: Qos) -> Self { Self { - pool: PacketPool::new(qos), + rx_pool: PacketPool::new(qos), connections: [ConnectionStorage::DISCONNECTED; CONNS], sar: [EMPTY_SAR; CONNS], channels: [ChannelStorage::DISCONNECTED; CHANNELS], @@ -94,9 +90,9 @@ pub struct BleHost<'d, T> { pub(crate) controller: T, pub(crate) connections: ConnectionManager<'d>, pub(crate) reassembly: PacketReassembly<'d>, - pub(crate) channels: ChannelManager<'d, L2CAP_RXQ>, + pub(crate) channels: ChannelManager<'d, { config::L2CAP_RX_QUEUE_SIZE }>, pub(crate) att_inbound: Channel, - pub(crate) pool: &'static dyn GlobalPacketPool, + pub(crate) rx_pool: &'static dyn GlobalPacketPool, outbound: Channel, pub(crate) scanner: Channel, 1>, @@ -119,9 +115,9 @@ where /// /// The host requires a HCI driver (a particular HCI-compatible controller implementing the required traits), and /// a reference to resources that are created outside the host but which the host is the only accessor of. - pub fn new( + pub fn new( controller: T, - host_resources: &'static mut BleHostResources, + host_resources: &'static mut BleHostResources, ) -> Self { Self { address: None, @@ -131,11 +127,11 @@ where connections: ConnectionManager::new(&mut host_resources.connections[..]), reassembly: PacketReassembly::new(&mut host_resources.sar[..]), channels: ChannelManager::new( - &host_resources.pool, + &host_resources.rx_pool, &mut host_resources.channels[..], &mut host_resources.channels_rx[..], ), - pool: &host_resources.pool, + rx_pool: &host_resources.rx_pool, att_inbound: Channel::new(), scanner: Channel::new(), advertiser_terminations: Channel::new(), @@ -594,8 +590,6 @@ where use crate::attribute_server::AttributeServer; GattServer { server: AttributeServer::new(table), - pool: self.pool, - pool_id: crate::packet_pool::ATT_ID, rx: self.att_inbound.receiver().into(), tx: self, connections: &self.connections, @@ -622,7 +616,7 @@ where return Ok(()); } - let Some(mut p) = self.pool.alloc(AllocId::from_channel(header.channel)) else { + let Some(mut p) = self.rx_pool.alloc(AllocId::from_channel(header.channel)) else { info!("No memory for packets on channel {}", header.channel); return Err(Error::OutOfMemory.into()); }; @@ -738,7 +732,7 @@ where info!("BleHost address set to {:?}", addr.addr); } - HostBufferSize::new(self.pool.mtu() as u16, self.pool.mtu() as u8, 1, 1) + HostBufferSize::new(self.rx_pool.mtu() as u16, 0, config::L2CAP_RX_QUEUE_SIZE as u16, 0) .exec(&self.controller) .await?; diff --git a/host/src/lib.rs b/host/src/lib.rs index 06fb1405..6ad44399 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -18,6 +18,7 @@ mod fmt; mod att; mod channel_manager; mod codec; +pub mod config; mod connection_manager; mod cursor; mod packet_pool; diff --git a/host/tests/l2cap.rs b/host/tests/l2cap.rs index 91591059..6b5034da 100644 --- a/host/tests/l2cap.rs +++ b/host/tests/l2cap.rs @@ -70,7 +70,7 @@ async fn l2cap_connection_oriented_channels() { let peripheral = local.spawn_local(async move { let controller_peripheral = create_controller(&peripheral).await; - static RESOURCES: StaticCell> = StaticCell::new(); + static RESOURCES: StaticCell> = StaticCell::new(); let host_resources = RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); let mut adapter: BleHost<'_, _> = BleHost::new(controller_peripheral, host_resources); @@ -132,7 +132,7 @@ async fn l2cap_connection_oriented_channels() { // Spawn central let central = local.spawn_local(async move { let controller_central = create_controller(¢ral).await; - static RESOURCES: StaticCell> = StaticCell::new(); + static RESOURCES: StaticCell> = StaticCell::new(); let host_resources = RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); let adapter: BleHost<'_, _> = BleHost::new(controller_central, host_resources); From 1b22369d861769b12bf9705da81fcb306b681ae2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 23 May 2024 11:35:23 +0200 Subject: [PATCH 2/6] default to no qos --- examples/nrf-sdc/src/bin/ble_l2cap_central.rs | 2 +- examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs | 2 +- host/tests/l2cap.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/nrf-sdc/src/bin/ble_l2cap_central.rs b/examples/nrf-sdc/src/bin/ble_l2cap_central.rs index b023acee..edc03be3 100644 --- a/examples/nrf-sdc/src/bin/ble_l2cap_central.rs +++ b/examples/nrf-sdc/src/bin/ble_l2cap_central.rs @@ -111,7 +111,7 @@ async fn main(spawner: Spawner) { static HOST_RESOURCES: StaticCell> = StaticCell::new(); - let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); + let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); let mut ble: BleHost<'_, _> = BleHost::new(sdc, host_resources); ble.set_random_address(my_addr()); diff --git a/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs b/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs index 17a889e2..3c187941 100644 --- a/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs +++ b/examples/nrf-sdc/src/bin/ble_l2cap_peripheral.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) { static HOST_RESOURCES: StaticCell> = StaticCell::new(); - let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); + let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); let mut ble: BleHost<'_, _> = BleHost::new(sdc, host_resources); ble.set_random_address(my_addr()); diff --git a/host/tests/l2cap.rs b/host/tests/l2cap.rs index 6b5034da..5a0567be 100644 --- a/host/tests/l2cap.rs +++ b/host/tests/l2cap.rs @@ -71,7 +71,7 @@ async fn l2cap_connection_oriented_channels() { let controller_peripheral = create_controller(&peripheral).await; static RESOURCES: StaticCell> = StaticCell::new(); - let host_resources = RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); + let host_resources = RESOURCES.init(BleHostResources::new(PacketQos::None)); let mut adapter: BleHost<'_, _> = BleHost::new(controller_peripheral, host_resources); adapter.set_random_address(peripheral_address); From 3d562c33566d25c0aca911bc038e67e9f23f98c7 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 23 May 2024 11:44:06 +0200 Subject: [PATCH 3/6] remove sleep in test --- host/tests/l2cap.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/host/tests/l2cap.rs b/host/tests/l2cap.rs index 5a0567be..0e39043b 100644 --- a/host/tests/l2cap.rs +++ b/host/tests/l2cap.rs @@ -114,7 +114,6 @@ async fn l2cap_connection_oriented_channels() { } println!("[peripheral] data received"); - tokio::time::sleep(Duration::from_secs(1)).await; for i in 0..10 { let tx = [i; PAYLOAD_LEN]; ch1.send(&adapter, &tx).await?; From b46a6efdf6b96ae0a454d920317c23ddd9b1e3c4 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 23 May 2024 12:16:19 +0200 Subject: [PATCH 4/6] use packet pool as indicator of packets handled by host --- host/src/host.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/host/src/host.rs b/host/src/host.rs index b436fbe2..27db305b 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -732,9 +732,14 @@ where info!("BleHost address set to {:?}", addr.addr); } - HostBufferSize::new(self.rx_pool.mtu() as u16, 0, config::L2CAP_RX_QUEUE_SIZE as u16, 0) - .exec(&self.controller) - .await?; + HostBufferSize::new( + self.rx_pool.mtu() as u16, + 0, + config::L2CAP_RX_PACKET_POOL_SIZE as u16, + 0, + ) + .exec(&self.controller) + .await?; SetEventMask::new( EventMask::new() From ff4aa1d09fb092e0b120c55fb651918c05638161 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 23 May 2024 12:16:40 +0200 Subject: [PATCH 5/6] use no qos for both central and periph in test --- host/tests/l2cap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/tests/l2cap.rs b/host/tests/l2cap.rs index 0e39043b..82431645 100644 --- a/host/tests/l2cap.rs +++ b/host/tests/l2cap.rs @@ -132,7 +132,7 @@ async fn l2cap_connection_oriented_channels() { let central = local.spawn_local(async move { let controller_central = create_controller(¢ral).await; static RESOURCES: StaticCell> = StaticCell::new(); - let host_resources = RESOURCES.init(BleHostResources::new(PacketQos::Guaranteed(4))); + let host_resources = RESOURCES.init(BleHostResources::new(PacketQos::None)); let adapter: BleHost<'_, _> = BleHost::new(controller_central, host_resources); From ab5014554753ea0ac5b1f9586d0cb3c91b6e5111 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 23 May 2024 12:20:40 +0200 Subject: [PATCH 6/6] add asserts to ensure packet pool is correclty configured --- host/src/packet_pool.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/host/src/packet_pool.rs b/host/src/packet_pool.rs index 8f8be5e2..b9f15448 100644 --- a/host/src/packet_pool.rs +++ b/host/src/packet_pool.rs @@ -143,6 +143,15 @@ impl Packet 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); + } + } Self { state: Mutex::new(State::new()), qos,