Skip to content

Commit

Permalink
Merge pull request #199 from embassy-rs/simplify-gatt-mtu
Browse files Browse the repository at this point in the history
Perform l2cap fragmentation at lower layer
  • Loading branch information
lulf authored Dec 17, 2024
2 parents 8fe20e3 + 3b6f752 commit 2357bde
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 77 deletions.
35 changes: 16 additions & 19 deletions host-macros/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned};
use syn::{meta::ParseNestedMeta, parse_quote, spanned::Spanned, Expr, Result};

/// MTU for a legacy BLE packet
const LEGACY_BLE_MTU: usize = 27;

#[derive(Default)]
pub(crate) struct ServerArgs {
mutex_type: Option<syn::Type>,
attribute_table_size: Option<Expr>,
mtu: Option<Expr>,
}

impl ServerArgs {
Expand All @@ -28,18 +24,25 @@ impl ServerArgs {
.as_str()
{
"mutex_type" => {
let buffer = meta.value().map_err(|_| Error::custom("mutex_type must be followed by `= [type]`. e.g. mutex_type = NoopRawMutex".to_string()))?;
let buffer = meta.value().map_err(|_| {
Error::custom(
"mutex_type must be followed by `= [type]`. e.g. mutex_type = NoopRawMutex".to_string(),
)
})?;
self.mutex_type = Some(buffer.parse()?);
}
"attribute_table_size" => {
let buffer = meta.value().map_err(|_| Error::custom("attribute_table_size must be followed by `= [size]`. e.g. attribute_table_size = 32".to_string()))?;
let buffer = meta.value().map_err(|_| {
Error::custom(
"attribute_table_size must be followed by `= [size]`. e.g. attribute_table_size = 32"
.to_string(),
)
})?;
self.attribute_table_size = Some(buffer.parse()?);
}
"mtu" => {
let buffer = meta.value().map_err(|_| Error::custom("mtu must be followed by `= [size]`. e.g. mtu = 27".to_string()))?;
self.mtu = Some(buffer.parse()?);
}
other => return Err(meta.error(format!("Unsupported server property: '{other}'.\nSupported properties are: mutex_type, attribute_table_size, mtu"))),
other => return Err(meta.error(format!(
"Unsupported server property: '{other}'.\nSupported properties are: mutex_type, attribute_table_size"
))),
}
Ok(())
}
Expand All @@ -63,12 +66,6 @@ impl ServerBuilder {
let mutex_type = self.arguments.mutex_type.unwrap_or(syn::Type::Verbatim(quote!(
embassy_sync::blocking_mutex::raw::NoopRawMutex
)));
let mtu = if let Some(value) = self.arguments.mtu {
value
} else {
let tokens = quote!(#LEGACY_BLE_MTU);
parse_quote!(#tokens)
};

let mut code_service_definition = TokenStream2::new();
let mut code_service_init = TokenStream2::new();
Expand Down Expand Up @@ -112,7 +109,7 @@ impl ServerBuilder {

#visibility struct #name<'reference, 'values>
{
server: GattServer<'reference, 'values, #mutex_type, _ATTRIBUTE_TABLE_SIZE, #mtu>,
server: GattServer<'reference, 'values, #mutex_type, _ATTRIBUTE_TABLE_SIZE>,
#code_service_definition
}

Expand Down Expand Up @@ -177,7 +174,7 @@ impl ServerBuilder {

impl<'reference, 'values> core::ops::Deref for #name<'reference, 'values>
{
type Target = GattServer<'reference, 'values, #mutex_type, _ATTRIBUTE_TABLE_SIZE, #mtu>;
type Target = GattServer<'reference, 'values, #mutex_type, _ATTRIBUTE_TABLE_SIZE>;

fn deref(&self) -> &Self::Target {
&self.server
Expand Down
49 changes: 14 additions & 35 deletions host/src/gatt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,21 @@ use crate::types::l2cap::L2capHeader;
use crate::{config, BleHostError, Error, Stack};

/// A GATT server capable of processing the GATT protocol using the provided table of attributes.
pub struct GattServer<'reference, 'values, M: RawMutex, const MAX: usize, const ATT_MTU: usize> {
pub struct GattServer<'reference, 'values, M: RawMutex, const MAX: usize> {
server: AttributeServer<'values, M, MAX>,
tx: DynamicSender<'reference, (ConnHandle, Pdu<'reference>)>,
rx: DynamicReceiver<'reference, (ConnHandle, Pdu<'reference>)>,
connections: &'reference ConnectionManager<'reference>,
pool: &'reference dyn GlobalPacketPool<'reference>,
}

impl<'reference, 'values, M: RawMutex, const MAX: usize, const ATT_MTU: usize>
GattServer<'reference, 'values, M, MAX, ATT_MTU>
{
impl<'reference, 'values, M: RawMutex, const MAX: usize> GattServer<'reference, 'values, M, MAX> {
/// Creates a GATT server capable of processing the GATT protocol using the provided table of attributes.
pub fn new<C: Controller>(stack: Stack<'reference, C>, table: AttributeTable<'values, M, MAX>) -> Self {
stack.host.connections.set_default_att_mtu(ATT_MTU as u16);
stack
.host
.connections
.set_default_att_mtu(stack.host.rx_pool.mtu() as u16 - 3);
use crate::attribute_server::AttributeServer;

Self {
Expand All @@ -61,8 +62,8 @@ impl<'reference, 'values, M: RawMutex, const MAX: usize, const ATT_MTU: usize>
if let Some(connection) = self.connections.get_connected_handle(handle) {
match AttReq::decode(pdu.as_ref()) {
Ok(att) => {
let mut tx = [0; ATT_MTU];
let mut w = WriteCursor::new(&mut tx);
let mut tx = self.pool.alloc(ATT_ID).ok_or(Error::OutOfMemory)?;
let mut w = WriteCursor::new(tx.as_mut());
let (mut header, mut data) = w.split(4)?;

match self.server.process(&connection, &att, data.write_buf()) {
Expand All @@ -85,19 +86,8 @@ impl<'reference, 'values, M: RawMutex, const MAX: usize, const ATT_MTU: usize>
_ => None,
};

warn!("[gatt] responding to handle {:?} {} bytes", handle, len);
let mut first = true;
for chunk in tx[..len].chunks(self.pool.mtu()) {
let mut p = self.pool.alloc(ATT_ID).ok_or(Error::OutOfMemory)?;
p.as_mut()[..chunk.len()].copy_from_slice(chunk);
let pdu = if !first {
Pdu::new(p, chunk.len()).segment()
} else {
Pdu::new(p, chunk.len())
};
self.tx.send((handle, pdu)).await;
first = false;
}
let pdu = Pdu::new(tx, len);
self.tx.send((handle, pdu)).await;
if let Some(event) = event {
connection
.post_event(ConnectionEvent::Gatt {
Expand Down Expand Up @@ -144,8 +134,8 @@ impl<'reference, 'values, M: RawMutex, const MAX: usize, const ATT_MTU: usize>
return Ok(());
}

let mut tx = [0; ATT_MTU];
let mut w = WriteCursor::new(&mut tx[..]);
let mut tx = self.pool.alloc(ATT_ID).ok_or(Error::OutOfMemory)?;
let mut w = WriteCursor::new(tx.as_mut());
let (mut header, mut data) = w.split(4)?;
data.write(ATT_HANDLE_VALUE_NTF)?;
data.write(characteristic.handle)?;
Expand All @@ -155,19 +145,8 @@ impl<'reference, 'values, M: RawMutex, const MAX: usize, const ATT_MTU: usize>
header.write(4_u16)?;
let total = header.len() + data.len();

let mut first = true;
for chunk in tx[..total].chunks(self.pool.mtu()) {
let mut p = self.pool.alloc(ATT_ID).ok_or(Error::OutOfMemory)?;
p.as_mut()[..chunk.len()].copy_from_slice(chunk);
let pdu = if !first {
Pdu::new(p, chunk.len()).segment()
} else {
Pdu::new(p, chunk.len())
};

self.tx.send((conn, pdu)).await;
first = false;
}
let pdu = Pdu::new(tx, total);
self.tx.send((conn, pdu)).await;
Ok(())
}

Expand Down
37 changes: 26 additions & 11 deletions host/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use crate::{att, config, Address, BleHostError, Error, Stack};
/// The host performs connection management, l2cap channel management, and
/// multiplexes events and data across connections and l2cap channels.
pub(crate) struct BleHost<'d, T> {
initialized: OnceLock<()>,
initialized: OnceLock<InitialState>,
metrics: RefCell<HostMetrics>,
pub(crate) address: Option<Address>,
pub(crate) controller: T,
Expand All @@ -77,6 +77,11 @@ pub(crate) struct BleHost<'d, T> {
pub(crate) connect_command_state: CommandState<bool>,
}

#[derive(Clone, Copy)]
pub(crate) struct InitialState {
acl_max: usize,
}

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug)]
pub(crate) enum AdvHandleState {
Expand Down Expand Up @@ -793,7 +798,10 @@ impl<'d, C: Controller> ControlRunner<'d, C> {
info!("[host] filter accept list size: {}", ret);

let ret = LeReadBufferSize::new().exec(&host.controller).await?;
info!("[host] setting txq to {}", ret.total_num_le_acl_data_packets as usize);
info!(
"[host] setting txq to {}, fragmenting at {}",
ret.total_num_le_acl_data_packets as usize, ret.le_acl_data_packet_length as usize
);
host.connections
.set_link_credits(ret.total_num_le_acl_data_packets as usize);

Expand All @@ -819,7 +827,9 @@ impl<'d, C: Controller> ControlRunner<'d, C> {
.await?;
}

let _ = host.initialized.init(());
let _ = host.initialized.init(InitialState {
acl_max: ret.le_acl_data_packet_length as usize,
});
info!("[host] initialized");

loop {
Expand Down Expand Up @@ -869,19 +879,24 @@ impl<'d, C: Controller> TxRunner<'d, C> {
/// Run the transmit loop for the host.
pub async fn run(&mut self) -> Result<(), BleHostError<C::Error>> {
let host = self.stack.host;
let params = host.initialized.get().await;
loop {
let (conn, pdu) = host.outbound.receive().await;
match host.acl(conn, 1).await {
Ok(mut sender) => {
if let Err(e) = sender.send(pdu.as_ref(), pdu.first).await {
warn!("[host] error sending outbound pdu");
let mut first = true;
for chunk in pdu.as_ref().chunks(params.acl_max) {
match host.acl(conn, 1).await {
Ok(mut sender) => {
if let Err(e) = sender.send(chunk, first).await {
warn!("[host] error sending outbound pdu");
return Err(e);
}
first = false;
}
Err(e) => {
warn!("[host] error requesting sending outbound pdu");
return Err(e);
}
}
Err(e) => {
warn!("[host] error requesting sending outbound pdu");
return Err(e);
}
}
}
}
Expand Down
11 changes: 1 addition & 10 deletions host/src/pdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,11 @@ use crate::packet_pool::Packet;
pub(crate) struct Pdu<'d> {
pub packet: Packet<'d>,
pub len: usize,
pub first: bool,
}

impl<'d> Pdu<'d> {
pub(crate) fn new(packet: Packet<'d>, len: usize) -> Self {
Self {
packet,
len,
first: true,
}
}

pub(crate) fn segment(self) -> Self {
Self { first: false, ..self }
Self { packet, len }
}
}

Expand Down
2 changes: 1 addition & 1 deletion host/tests/gatt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async fn gatt_client_server() {
&mut value
).build();

let server = GattServer::<NoopRawMutex, 10, 27>::new(stack, table);
let server = GattServer::<NoopRawMutex, 10>::new(stack, table);
select! {
r = runner.run() => {
r
Expand Down
2 changes: 1 addition & 1 deletion host/tests/gatt_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const VALUE_UUID: Uuid = Uuid::new_long([
0x00, 0x00, 0x10, 0x01, 0xb0, 0xcd, 0x11, 0xec, 0x87, 0x1f, 0xd4, 0x5d, 0xdf, 0x13, 0x88, 0x40,
]);

#[gatt_server(mutex_type = NoopRawMutex, attribute_table_size = 10, mtu = 27)]
#[gatt_server(mutex_type = NoopRawMutex, attribute_table_size = 10)]
struct Server {
service: CustomService,
}
Expand Down

0 comments on commit 2357bde

Please sign in to comment.