Skip to content

Commit

Permalink
Reduce buffer usage for l2cap signalling
Browse files Browse the repository at this point in the history
  • Loading branch information
lulf committed Apr 22, 2024
1 parent 518e934 commit 9ee4cb8
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 308 deletions.
53 changes: 27 additions & 26 deletions host/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use bt_hci::param::{
AddrKind, AdvChannelMap, AdvHandle, AdvKind, BdAddr, ConnHandle, DisconnectReason, EventMask, FilterDuplicates,
InitiatingPhy, LeEventMask, Operation, PhyParams, ScanningPhy,
};
use bt_hci::ControllerToHostPacket;
use bt_hci::{ControllerToHostPacket, FromHciBytes, WriteHci};
use embassy_futures::select::{select, Either};
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::channel::Channel;
Expand All @@ -29,12 +29,14 @@ use crate::advertise::{Advertisement, AdvertisementConfig, RawAdvertisement};
use crate::channel_manager::ChannelManager;
use crate::connection::{ConnectConfig, Connection};
use crate::connection_manager::{ConnectionInfo, ConnectionManager};
use crate::cursor::{ReadCursor, WriteCursor};
use crate::cursor::WriteCursor;
use crate::l2cap::sar::PacketReassembly;
use crate::packet_pool::{AllocId, DynamicPacketPool, PacketPool, Qos};
use crate::pdu::Pdu;
use crate::scan::{PhySet, ScanConfig, ScanReport};
use crate::types::l2cap::{L2capHeader, L2capLeSignal, L2CAP_CID_ATT, L2CAP_CID_DYN_START, L2CAP_CID_LE_U_SIGNAL};
use crate::types::l2cap::{
L2capHeader, L2capSignal, L2capSignalHeader, L2CAP_CID_ATT, L2CAP_CID_DYN_START, L2CAP_CID_LE_U_SIGNAL,
};
#[cfg(feature = "gatt")]
use crate::{attribute::AttributeTable, gatt::GattServer};
use crate::{AdapterError, Address, Error};
Expand Down Expand Up @@ -530,14 +532,12 @@ where
async fn handle_acl(&self, acl: AclPacket<'_>) -> Result<(), Error> {
let (header, packet) = match acl.boundary_flag() {
AclPacketBoundary::FirstFlushable => {
let (header, data) = L2capHeader::decode(&acl)?;
let (header, data) = L2capHeader::from_hci_bytes(acl.data())?;

// Avoids using the packet buffer for signalling packets
if header.channel == L2CAP_CID_LE_U_SIGNAL {
assert!(data.len() == header.length as usize);
let mut r = ReadCursor::new(data);
let signal: L2capLeSignal = r.read()?;
self.channels.control(acl.handle(), signal).await?;
self.channels.control(acl.handle(), &data).await?;
return Ok(());
}

Expand Down Expand Up @@ -851,28 +851,29 @@ impl<'d, T: Controller> HciController<'d, T> {
Ok(())
}

pub(crate) async fn signal(
pub(crate) async fn signal<D: L2capSignal>(
&self,
handle: ConnHandle,
response: &L2capLeSignal,
identifier: u8,
signal: &D,
p_buf: &mut [u8],
) -> Result<(), AdapterError<T::Error>> {
// TODO: Refactor signal to avoid encode/decode
// info!("[{}] sending signal: {:?}", handle, response);
let mut tx = [0; 32];
let mut w = WriteCursor::new(&mut tx);
let (mut header, mut body) = w.split(4)?;

body.write_ref(response)?;

// TODO: Move into l2cap packet type
header.write(body.len() as u16)?;
header.write(L2CAP_CID_LE_U_SIGNAL)?;
let len = header.len() + body.len();

header.finish();
body.finish();
w.finish();
self.send(handle, &tx[..len]).await?;
let header = L2capSignalHeader {
identifier,
code: D::code(),
length: signal.size() as u16,
};
let l2cap = L2capHeader {
channel: D::channel(),
length: header.size() as u16 + header.length,
};

let mut w = WriteCursor::new(p_buf);
w.write_hci(&l2cap)?;
w.write_hci(&header)?;
w.write_hci(signal)?;

self.send(handle, w.finish()).await?;

Ok(())
}
Expand Down
127 changes: 64 additions & 63 deletions host/src/channel_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use core::task::{Context, Poll};

use bt_hci::controller::Controller;
use bt_hci::param::ConnHandle;
use bt_hci::FromHciBytes;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::channel::Channel;
Expand All @@ -14,8 +15,8 @@ use crate::cursor::{ReadCursor, WriteCursor};
use crate::packet_pool::{AllocId, DynamicPacketPool, Packet};
use crate::pdu::Pdu;
use crate::types::l2cap::{
L2capHeader, L2capLeSignal, L2capLeSignalData, LeCreditConnReq, LeCreditConnRes, LeCreditConnResultCode,
LeCreditFlowInd,
CommandRejectRes, DisconnectionReq, DisconnectionRes, L2capHeader, L2capSignalCode, L2capSignalHeader,
LeCreditConnReq, LeCreditConnRes, LeCreditConnResultCode, LeCreditFlowInd,
};
use crate::{AdapterError, Error};

Expand Down Expand Up @@ -300,31 +301,34 @@ impl<
})
.await;

let response = L2capLeSignal::new(
req_id,
L2capLeSignalData::LeCreditConnRes(LeCreditConnRes {
mps: state.mps,
dcid: state.cid,
mtu,
credits: 0,
result: LeCreditConnResultCode::Success,
}),
);

controller.signal(conn, &response).await?;
let mut tx = [0; 18];
controller
.signal(
conn,
req_id,
&LeCreditConnRes {
mps: state.mps,
dcid: state.cid,
mtu,
credits: 0,
result: LeCreditConnResultCode::Success,
},
&mut tx[..],
)
.await?;

// Send initial credits
let next_req_id = self.next_request_id();

controller
.signal(
conn,
&L2capLeSignal::new(
next_req_id,
L2capLeSignalData::LeCreditFlowInd(LeCreditFlowInd {
cid: state.cid,
credits: state.flow_control.available(),
}),
),
next_req_id,
&LeCreditFlowInd {
cid: state.cid,
credits: state.flow_control.available(),
},
&mut tx[..],
)
.await?;

Expand Down Expand Up @@ -358,19 +362,19 @@ impl<
}
})?;
//info!("Created connect state with idx cid {}", cid);
//
let mut tx = [0; 18];

let command = LeCreditConnReq {
psm,
mps: self.pool.mtu() as u16 - 4,
scid: cid,
mtu,
credits: 0,
};

let command = L2capLeSignal::new(
req_id,
L2capLeSignalData::LeCreditConnReq(LeCreditConnReq {
psm,
mps: self.pool.mtu() as u16 - 4,
scid: cid,
mtu,
credits: 0,
}),
);
//info!("Signal packet to remote: {:?}", command);
controller.signal(conn, &command).await?;
controller.signal(conn, req_id, &command, &mut tx[..]).await?;
// info!("Sent signal packet to remote, awaiting response");

let (idx, cid) = poll_fn(|cx| {
Expand All @@ -397,14 +401,8 @@ impl<

// Send initial credits
let next_req_id = self.next_request_id();
controller
.signal(
conn,
&L2capLeSignal::new(
next_req_id,
L2capLeSignalData::LeCreditFlowInd(LeCreditFlowInd { cid, credits }),
),
)
let req = controller
.signal(conn, next_req_id, &LeCreditFlowInd { cid, credits }, &mut tx[..])
.await?;

// info!("Done!");
Expand Down Expand Up @@ -444,28 +442,31 @@ impl<
Ok(())
}

pub async fn control(&self, conn: ConnHandle, signal: L2capLeSignal) -> Result<(), Error> {
pub async fn control(&self, conn: ConnHandle, data: &[u8]) -> Result<(), Error> {
// info!("Inbound signal: {:?}", signal);
match signal.data {
L2capLeSignalData::LeCreditConnReq(req) => {
let (header, data) = L2capSignalHeader::from_hci_bytes(data)?;
match header.code {
L2capSignalCode::LeCreditConnReq => {
let req = LeCreditConnReq::from_hci_bytes_complete(data)?;
self.peer_connect(|i, c| PeerConnectingState {
conn,
cid: c,
psm: req.psm,
request_id: signal.id,
request_id: header.identifier,
peer_cid: req.scid,
offered_credits: req.credits,
mps: req.mps,
mtu: req.mtu,
})?;
Ok(())
}
L2capLeSignalData::LeCreditConnRes(res) => {
L2capSignalCode::LeCreditConnRes => {
let res = LeCreditConnRes::from_hci_bytes_complete(data)?;
// info!("Got response to create request: {:?}", res);
match res.result {
LeCreditConnResultCode::Success => {
// Must be a response of a previous request which should already by allocated a channel for
self.connected(signal.id, |idx, req| ConnectedState {
self.connected(header.identifier, |idx, req| ConnectedState {
conn: req.conn,
cid: req.cid,
psm: req.psm,
Expand All @@ -484,24 +485,29 @@ impl<
}
}
}
L2capLeSignalData::LeCreditFlowInd(req) => {
L2capSignalCode::LeCreditFlowInd => {
let req = LeCreditFlowInd::from_hci_bytes_complete(data)?;
self.remote_credits(req.cid, req.credits)?;
Ok(())
}
L2capLeSignalData::CommandRejectRes(reject) => {
L2capSignalCode::CommandRejectRes => {
let (reject, _) = CommandRejectRes::from_hci_bytes(data)?;
warn!("Rejected: {:?}", reject);
Ok(())
}
L2capLeSignalData::DisconnectionReq(req) => {
L2capSignalCode::DisconnectionReq => {
let req = DisconnectionReq::from_hci_bytes_complete(data)?;
info!("Disconnect request: {:?}!", req);
self.disconnect(req.dcid)?;
Ok(())
}
L2capLeSignalData::DisconnectionRes(res) => {
L2capSignalCode::DisconnectionRes => {
let res = DisconnectionRes::from_hci_bytes_complete(data)?;
warn!("Disconnection result!");
self.disconnected(res.dcid)?;
Ok(())
}
_ => Err(Error::NotSupported),
}
}

Expand Down Expand Up @@ -558,8 +564,7 @@ impl<

let mut remaining = remaining as usize - data.len();

drop(packet);
self.flow_control(cid, hci).await?;
self.flow_control(cid, hci, packet.packet).await?;
//info!(
// "Total size of PDU is {}, read buffer size is {} remaining; {}",
// len,
Expand All @@ -576,8 +581,7 @@ impl<
pos += to_copy;
}
remaining -= packet.len;
drop(packet);
self.flow_control(cid, hci).await?;
self.flow_control(cid, hci, packet.packet).await?;
}

// info!("Total reserved {} bytes", pos);
Expand Down Expand Up @@ -658,6 +662,7 @@ impl<
&self,
cid: u16,
hci: &HciController<'_, T>,
mut packet: Packet<'_>,
) -> Result<(), AdapterError<T::Error>> {
let (conn, credits) = self.state.lock(|state| {
let mut state = state.borrow_mut();
Expand All @@ -673,15 +678,11 @@ impl<
})?;

if let Some(credits) = credits {
let next_req_id = self.next_request_id();
hci.signal(
conn,
&L2capLeSignal::new(
next_req_id,
L2capLeSignalData::LeCreditFlowInd(LeCreditFlowInd { cid, credits }),
),
)
.await?;
let identifier = self.next_request_id();
let signal = LeCreditFlowInd { cid, credits };

// Reuse packet buffer for signalling data to save the extra TX buffer
hci.signal(conn, identifier, &signal, packet.as_mut()).await?;
}
Ok(())
}
Expand Down
14 changes: 14 additions & 0 deletions host/src/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Module for cursors over a byte slice.
//!

use bt_hci::WriteHci;

use crate::codec::{Decode, Encode, Error};

// Not a byte writer. It is just a cursor to track where a byte slice is being written.
Expand Down Expand Up @@ -57,6 +59,18 @@ impl<'d> WriteCursor<'d> {
}
}

/// Write fixed sized type
pub fn write_hci<E: WriteHci>(&mut self, data: &E) -> Result<(), Error> {
if self.available() < data.size() {
Err(Error::InsufficientSpace)
} else {
data.write_hci(&mut self.data[self.pos..self.pos + data.size()])
.map_err(|_| Error::InsufficientSpace)?;
self.pos += data.size();
Ok(())
}
}

pub fn write_ref<E: Encode>(&mut self, data: &E) -> Result<(), Error> {
if self.available() < data.size() {
Err(Error::InsufficientSpace)
Expand Down
Loading

0 comments on commit 9ee4cb8

Please sign in to comment.