From 3e210ee5cd289ac0c789f97d95ac1b6284f95b28 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 18 Jun 2025 12:02:11 +0800 Subject: [PATCH 1/3] feat(l2cap): add LeConnParamUpdateReq types Signed-off-by: Haobo Gu --- host/src/types/l2cap.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/host/src/types/l2cap.rs b/host/src/types/l2cap.rs index 9568e863..4781692d 100644 --- a/host/src/types/l2cap.rs +++ b/host/src/types/l2cap.rs @@ -106,6 +106,47 @@ impl TryFrom for L2capSignalCode { } } +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct LeConnParamUpdateReq { + pub interval_min: u16, + pub interval_max: u16, + pub latency: u16, + pub timeout: u16, +} + +unsafe impl FixedSizeValue for LeConnParamUpdateReq { + fn is_valid(data: &[u8]) -> bool { + true + } +} +impl L2capSignal for LeConnParamUpdateReq { + fn code() -> L2capSignalCode { + L2capSignalCode::LeCreditConnReq + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy)] +#[repr(u16)] +pub enum LeConnParamUpdateRes { + Accepted = 0x0000, + Rejected = 0x0001, +} + +unsafe impl FixedSizeValue for LeConnParamUpdateRes { + fn is_valid(data: &[u8]) -> bool { + true + } +} + +impl L2capSignal for LeConnParamUpdateRes { + fn code() -> L2capSignalCode { + L2capSignalCode::LeCreditConnRes + } +} + #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] #[derive(Debug, Clone, Copy)] From 91b0368090250047f206f197d67280139620ad1e Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 18 Jun 2025 12:02:57 +0800 Subject: [PATCH 2/3] feat(host): use L2CAP signaling when conn parameter update procedure is not supported Signed-off-by: Haobo Gu --- host/src/channel_manager.rs | 2 +- host/src/connection.rs | 66 ++++++++++++++++++++++++++----------- host/src/types/l2cap.rs | 41 ----------------------- 3 files changed, 48 insertions(+), 61 deletions(-) diff --git a/host/src/channel_manager.rs b/host/src/channel_manager.rs index e2a24d75..f17cbe13 100644 --- a/host/src/channel_manager.rs +++ b/host/src/channel_manager.rs @@ -109,7 +109,7 @@ impl<'d, P: PacketPool> ChannelManager<'d, P> { } } - fn next_request_id(&self) -> u8 { + pub(crate) fn next_request_id(&self) -> u8 { self.state.borrow_mut().next_request_id() } diff --git a/host/src/connection.rs b/host/src/connection.rs index a47e2e7f..7977a1b3 100644 --- a/host/src/connection.rs +++ b/host/src/connection.rs @@ -1,6 +1,6 @@ //! BLE connection. -use bt_hci::cmd::le::{LeConnUpdate, LeReadPhy, LeSetPhy}; +use bt_hci::cmd::le::{LeConnUpdate, LeReadLocalSupportedFeatures, LeReadPhy, LeReadRemoteFeatures, LeSetPhy}; use bt_hci::cmd::status::ReadRssi; use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync}; use bt_hci::param::{ @@ -18,6 +18,7 @@ use crate::pdu::Pdu; use crate::prelude::{AttributeServer, GattConnection}; #[cfg(feature = "security")] use crate::security_manager::BondInformation; +use crate::types::l2cap::ConnParamUpdateReq; use crate::{BleHostError, Error, Identity, PacketPool, Stack}; /// Connection configuration. @@ -304,27 +305,54 @@ impl<'stack, P: PacketPool> Connection<'stack, P> { params: &ConnectParams, ) -> Result<(), BleHostError> where - T: ControllerCmdAsync, + T: ControllerCmdAsync + + ControllerCmdSync + + ControllerCmdAsync, { let handle = self.handle(); - match stack - .host - .async_command(LeConnUpdate::new( - handle, - params.min_connection_interval.into(), - params.max_connection_interval.into(), - params.max_latency, - params.supervision_timeout.into(), - params.event_length.into(), - params.event_length.into(), - )) - .await - { - Ok(_) => Ok(()), - Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => { - Err(crate::Error::Disconnected.into()) + // First, check the local supported features to ensure that the connection update is supported. + let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?; + // TODO: How to read from `LeReadRemoteFeaturesComplete` event? + // let remote_features = stack.host.async_command(LeReadRemoteFeatures::new(handle)).await?; + if features.supports_conn_parameters_request_procedure() || self.role() == LeConnRole::Central { + match stack + .host + .async_command(LeConnUpdate::new( + handle, + params.min_connection_interval.into(), + params.max_connection_interval.into(), + params.max_latency, + params.supervision_timeout.into(), + params.event_length.into(), + params.event_length.into(), + )) + .await + { + Ok(_) => Ok(()), + Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => { + Err(crate::Error::Disconnected.into()) + } + Err(e) => Err(e), } - Err(e) => Err(e), + } else { + // Use L2CAP signaling to update connection parameters + info!("Connection parameters request procedure not supported, use l2cap connection parameter update req instead"); + let identifier = stack.host.channels.next_request_id(); + let interval_min: bt_hci::param::Duration<1_250> = params.min_connection_interval.into(); + let interva_max: bt_hci::param::Duration<1_250> = params.max_connection_interval.into(); + let timeout: bt_hci::param::Duration<10_000> = params.supervision_timeout.into(); + let command = ConnParamUpdateReq { + interval_min: interval_min.as_u16(), + interval_max: interva_max.as_u16(), + latency: params.max_latency, + timeout: timeout.as_u16(), + }; + let mut tx = [0; 16]; + stack + .host + .l2cap_signal(handle, identifier, &command, &mut tx[..]) + .await?; + Ok(()) } } diff --git a/host/src/types/l2cap.rs b/host/src/types/l2cap.rs index 4781692d..9568e863 100644 --- a/host/src/types/l2cap.rs +++ b/host/src/types/l2cap.rs @@ -106,47 +106,6 @@ impl TryFrom for L2capSignalCode { } } -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct LeConnParamUpdateReq { - pub interval_min: u16, - pub interval_max: u16, - pub latency: u16, - pub timeout: u16, -} - -unsafe impl FixedSizeValue for LeConnParamUpdateReq { - fn is_valid(data: &[u8]) -> bool { - true - } -} -impl L2capSignal for LeConnParamUpdateReq { - fn code() -> L2capSignalCode { - L2capSignalCode::LeCreditConnReq - } -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, Clone, Copy)] -#[repr(u16)] -pub enum LeConnParamUpdateRes { - Accepted = 0x0000, - Rejected = 0x0001, -} - -unsafe impl FixedSizeValue for LeConnParamUpdateRes { - fn is_valid(data: &[u8]) -> bool { - true - } -} - -impl L2capSignal for LeConnParamUpdateRes { - fn code() -> L2capSignalCode { - L2capSignalCode::LeCreditConnRes - } -} - #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] #[derive(Debug, Clone, Copy)] From d20905f1dad9b383c31afb45766926810616f673 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 20 Jun 2025 00:21:24 +0800 Subject: [PATCH 3/3] refactor(host): change `next_request_id` back to private, remove code which won't be implemented now Signed-off-by: Haobo Gu --- host/src/channel_manager.rs | 13 ++++++++++++- host/src/connection.rs | 18 ++++-------------- host/src/host.rs | 12 ++++++++++-- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/host/src/channel_manager.rs b/host/src/channel_manager.rs index f17cbe13..5a1d7555 100644 --- a/host/src/channel_manager.rs +++ b/host/src/channel_manager.rs @@ -109,7 +109,7 @@ impl<'d, P: PacketPool> ChannelManager<'d, P> { } } - pub(crate) fn next_request_id(&self) -> u8 { + fn next_request_id(&self) -> u8 { self.state.borrow_mut().next_request_id() } @@ -726,6 +726,17 @@ impl<'d, P: PacketPool> ChannelManager<'d, P> { Ok(()) } + pub(crate) async fn send_conn_param_update_req( + &self, + handle: ConnHandle, + host: &BleHost<'d, T, P>, + param: &ConnParamUpdateReq, + ) -> Result<(), BleHostError> { + let identifier = self.next_request_id(); + let mut tx = [0; 16]; + host.l2cap_signal(handle, identifier, param, &mut tx[..]).await + } + fn connected_channel_params(&self, index: ChannelIndex) -> Result<(ConnHandle, u16, u16, u16), Error> { let state = self.state.borrow(); let chan = &state.channels[index.0 as usize]; diff --git a/host/src/connection.rs b/host/src/connection.rs index 7977a1b3..289c06d4 100644 --- a/host/src/connection.rs +++ b/host/src/connection.rs @@ -1,6 +1,6 @@ //! BLE connection. -use bt_hci::cmd::le::{LeConnUpdate, LeReadLocalSupportedFeatures, LeReadPhy, LeReadRemoteFeatures, LeSetPhy}; +use bt_hci::cmd::le::{LeConnUpdate, LeReadLocalSupportedFeatures, LeReadPhy, LeSetPhy}; use bt_hci::cmd::status::ReadRssi; use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync}; use bt_hci::param::{ @@ -305,15 +305,11 @@ impl<'stack, P: PacketPool> Connection<'stack, P> { params: &ConnectParams, ) -> Result<(), BleHostError> where - T: ControllerCmdAsync - + ControllerCmdSync - + ControllerCmdAsync, + T: ControllerCmdAsync + ControllerCmdSync, { let handle = self.handle(); // First, check the local supported features to ensure that the connection update is supported. let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?; - // TODO: How to read from `LeReadRemoteFeaturesComplete` event? - // let remote_features = stack.host.async_command(LeReadRemoteFeatures::new(handle)).await?; if features.supports_conn_parameters_request_procedure() || self.role() == LeConnRole::Central { match stack .host @@ -337,22 +333,16 @@ impl<'stack, P: PacketPool> Connection<'stack, P> { } else { // Use L2CAP signaling to update connection parameters info!("Connection parameters request procedure not supported, use l2cap connection parameter update req instead"); - let identifier = stack.host.channels.next_request_id(); let interval_min: bt_hci::param::Duration<1_250> = params.min_connection_interval.into(); let interva_max: bt_hci::param::Duration<1_250> = params.max_connection_interval.into(); let timeout: bt_hci::param::Duration<10_000> = params.supervision_timeout.into(); - let command = ConnParamUpdateReq { + let param = ConnParamUpdateReq { interval_min: interval_min.as_u16(), interval_max: interva_max.as_u16(), latency: params.max_latency, timeout: timeout.as_u16(), }; - let mut tx = [0; 16]; - stack - .host - .l2cap_signal(handle, identifier, &command, &mut tx[..]) - .await?; - Ok(()) + stack.host.send_conn_param_update_req(handle, ¶m).await } } diff --git a/host/src/host.rs b/host/src/host.rs index c5da3205..7d8bf379 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -45,8 +45,8 @@ use crate::pdu::Pdu; #[cfg(feature = "security")] use crate::security_manager::SecurityEventData; use crate::types::l2cap::{ - L2capHeader, L2capSignal, L2capSignalHeader, L2CAP_CID_ATT, L2CAP_CID_DYN_START, L2CAP_CID_LE_U_SECURITY_MANAGER, - L2CAP_CID_LE_U_SIGNAL, + ConnParamUpdateReq, L2capHeader, L2capSignal, L2capSignalHeader, L2CAP_CID_ATT, L2CAP_CID_DYN_START, + L2CAP_CID_LE_U_SECURITY_MANAGER, L2CAP_CID_LE_U_SIGNAL, }; use crate::{att, Address, BleHostError, Error, PacketPool, Stack}; @@ -578,6 +578,14 @@ where }) } + pub(crate) async fn send_conn_param_update_req( + &self, + handle: ConnHandle, + param: &ConnParamUpdateReq, + ) -> Result<(), BleHostError> { + self.channels.send_conn_param_update_req(handle, self, param).await + } + /// Read current host metrics pub(crate) fn metrics R, R>(&self, f: F) -> R { let m = self.metrics.borrow();