From 1733ff96045a9213a04fcd0c6947e7ceb12c14c0 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Thu, 28 Dec 2023 22:27:52 +0100 Subject: [PATCH 01/27] feat(vendor event): Gatt EATT Bearer event --- src/vendor/event/mod.rs | 84 +++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 96edeaf..d6c3b87 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -246,6 +246,10 @@ pub enum VendorEvent { /// and an error response will be sent to the client, with the error code as specified by the /// application. AttPrepareWritePermitRequest(AttPrepareWritePermitRequest), + + /// This event informs the application of a change in status of the enhanced ATT bearer handled + /// by the special L2CAP channel. + GattEattBrearer(GattEattBrearer), } /// Enumeration of vendor-specific status codes. @@ -537,6 +541,9 @@ pub enum VendorError { /// [event](crate::vendor::event::command::VendorReturnParameters::GapGetBondedDevices): one of the address type bytes was /// invalid. Includes the invalid byte. BadBdAddrType(u8), + + /// For the [GATT EAT Bearer](crate::vendor::event::VendorEvent::GattEattBrearer) event: The EAB state was not recognized. + BadEabState(u8), } macro_rules! require_len { @@ -606,14 +613,14 @@ impl VendorEvent { 0x080A => Ok(VendorEvent::L2CapCommandReject(to_l2cap_command_reject( buffer, )?)), - 0x0810 => todo!(), - 0x0811 => todo!(), - 0x0812 => todo!(), - 0x0813 => todo!(), - 0x0814 => todo!(), - 0x0815 => todo!(), - 0x0816 => todo!(), - 0x0817 => todo!(), + // TODO: 0x0810 => todo!(), + // TODO: 0x0811 => todo!(), + // TODO: 0x0812 => todo!(), + // TODO: 0x0813 => todo!(), + // TODO: 0x0814 => todo!(), + // TODO: 0x0815 => todo!(), + // TODO: 0x0816 => todo!(), + // TODO: 0x0817 => todo!(), 0x0C01 => Ok(VendorEvent::GattAttributeModified( to_gatt_attribute_modified(buffer)?, )), @@ -673,11 +680,11 @@ impl VendorEvent { 0x0C18 => Ok(VendorEvent::AttPrepareWritePermitRequest( to_att_prepare_write_permit_request(buffer)?, )), - 0x0C19 => todo!(), - 0x0C1A => todo!(), - 0x0C1D => todo!(), - 0x0C1E => todo!(), - 0x0C1F => todo!(), + 0x0C19 => Ok(VendorEvent::GattEattBrearer(to_gatt_eatt_bearer(buffer)?)), + // TODO: 0x0C1A => todo!(), + // TODO: 0x0C1D => todo!(), + // TODO: 0x0C1E => todo!(), + // TODO: 0x0C1F => todo!(), _ => Err(crate::event::Error::Vendor(VendorError::UnknownEvent( event_code, ))), @@ -2029,13 +2036,15 @@ pub enum GattProcedureStatus { } impl TryFrom for GattProcedureStatus { - type Error = VendorError; + type Error = crate::event::Error; fn try_from(value: u8) -> Result { match value { 0x00 => Ok(GattProcedureStatus::Success), 0x41 => Ok(GattProcedureStatus::Failed), - _ => Err(VendorError::BadGattProcedureStatus(value)), + _ => Err(crate::event::Error::Vendor( + VendorError::BadGattProcedureStatus(value), + )), } } } @@ -2045,7 +2054,7 @@ fn to_gatt_procedure_complete(buffer: &[u8]) -> Result Result for EabState { + type Error = crate::event::Error; + + fn try_from(value: u8) -> Result { + match value { + 0x00 => Ok(EabState::AttBearerCreated), + 0x01 => Ok(EabState::AttBearerTerminated), + err => Err(crate::event::Error::Vendor(VendorError::BadEabState(err))), + } + } +} + +fn to_gatt_eatt_bearer(buffer: &[u8]) -> Result { + require_len!(buffer, 3); + + Ok(GattEattBrearer { + channel_index: buffer[0], + eab_state: EabState::try_from(buffer[1])?, + status: GattProcedureStatus::try_from(buffer[2])?, + }) +} From ab3aa7e27fd712b9cef7823f829954f65bd894b0 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Thu, 28 Dec 2023 23:13:54 +0100 Subject: [PATCH 02/27] feat(vendor event): add L2CAP COC Connect event --- src/vendor/event/mod.rs | 60 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index d6c3b87..8176cd6 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -117,6 +117,11 @@ pub enum VendorEvent { /// Command Reject packet). L2CapCommandReject(L2CapCommandReject), + /// This event is generated when receiving a valid Credit Based Connection Request packet. + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + L2CapCocConnect(L2CapCocConnect), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -613,7 +618,7 @@ impl VendorEvent { 0x080A => Ok(VendorEvent::L2CapCommandReject(to_l2cap_command_reject( buffer, )?)), - // TODO: 0x0810 => todo!(), + 0x0810 => Ok(VendorEvent::L2CapCocConnect(to_l2cap_coc_connect(buffer)?)), // TODO: 0x0811 => todo!(), // TODO: 0x0812 => todo!(), // TODO: 0x0813 => todo!(), @@ -2669,6 +2674,59 @@ fn to_l2cap_command_reject(buffer: &[u8]) -> Result Result { + require_len!(buffer, 10); + + Ok(L2CapCocConnect { + connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + spsm: LittleEndian::read_u16(&buffer[2..]), + mtu: LittleEndian::read_u16(&buffer[4..]), + mps: LittleEndian::read_u16(&buffer[6..]), + initial_credits: LittleEndian::read_u16(&buffer[8..]), + channel_number: buffer[10], + }) +} + #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// This event informs the application of a change in status of the Enhanced ATT From 7ecc075a9ff388e7a1df514762e25154a15aa56c Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Thu, 28 Dec 2023 23:25:48 +0100 Subject: [PATCH 03/27] feat(vendor event): add L2CAP COC Connect Cofirm event --- .gitignore | 1 + src/vendor/event/mod.rs | 70 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a821aa9..cfe35c1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /target **/*.rs.bk Cargo.lock +justfile \ No newline at end of file diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 8176cd6..6173ab4 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -122,6 +122,11 @@ pub enum VendorEvent { /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. L2CapCocConnect(L2CapCocConnect), + /// This event is generated when receiving a valid Credit Based Connection Response packet. + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + L2CapCocConnectConfirm(L2CapCocConnectConfirm), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -619,7 +624,9 @@ impl VendorEvent { buffer, )?)), 0x0810 => Ok(VendorEvent::L2CapCocConnect(to_l2cap_coc_connect(buffer)?)), - // TODO: 0x0811 => todo!(), + 0x0811 => Ok(VendorEvent::L2CapCocConnectConfirm( + to_l2cap_coc_connect_confirm(buffer)?, + )), // TODO: 0x0812 => todo!(), // TODO: 0x0813 => todo!(), // TODO: 0x0814 => todo!(), @@ -2727,6 +2734,67 @@ fn to_l2cap_coc_connect(buffer: &[u8]) -> Result Result { + require_len!(buffer, 12); + + Ok(L2CapCocConnectConfirm { + connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + spsm: LittleEndian::read_u16(&buffer[2..]), + mtu: LittleEndian::read_u16(&buffer[4..]), + mps: LittleEndian::read_u16(&buffer[6..]), + initial_credits: LittleEndian::read_u16(&buffer[8..]), + result: LittleEndian::read_u16(&buffer[10..]), + channel_number: buffer[12], + }) +} + #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// This event informs the application of a change in status of the Enhanced ATT From 3123ced37c8b33a139f8377ed9552ea2e46d0435 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 00:44:20 +0100 Subject: [PATCH 04/27] feat(vendor event): add L2CAP COC Reconfig event --- src/vendor/event/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 6173ab4..d92edd2 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -127,6 +127,11 @@ pub enum VendorEvent { /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. L2CapCocConnectConfirm(L2CapCocConnectConfirm), + /// This event is generated when receiving a valid Credit Based Reconfigure Request packet. + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + L2CapCocReconfig(L2CapCocReconfig), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -627,7 +632,9 @@ impl VendorEvent { 0x0811 => Ok(VendorEvent::L2CapCocConnectConfirm( to_l2cap_coc_connect_confirm(buffer)?, )), - // TODO: 0x0812 => todo!(), + 0x0812 => Ok(VendorEvent::L2CapCocReconfig(to_l2cap_coc_reconfig( + buffer, + )?)), // TODO: 0x0813 => todo!(), // TODO: 0x0814 => todo!(), // TODO: 0x0815 => todo!(), @@ -2795,6 +2802,53 @@ fn to_l2cap_coc_connect_confirm( }) } +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Credit Based Reconfigure Request packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2CapCocReconfig { + /// handle of the connection where this event occured. + pub connection_handle: ConnectionHandle, + /// Maximum Transmission Unit + /// + /// Values: + /// - 23 .. 65535 + pub mtu: u16, + /// Maximum Payload Size (in octets) + /// + /// Values: + /// - 23 .. 248 + pub mps: u16, + /// Number of channels to be created. If this parameter is + /// set to 0, it requests the creation of one LE credit based connection- + /// oriented channel. Otherwise, it requests the creation of one or more + /// enhanced credit based connection-oriented channels. + /// + /// Values: + /// - 0 .. 5 + pub channel_number: u8, + /// List of channel indexes for which the primitives apply. + pub channel_index_list: [u8; 246], +} + +fn to_l2cap_coc_reconfig(buffer: &[u8]) -> Result { + require_len_at_least!(buffer, 8); + + // TODO: how does one determine the length of channel_index_list? + let mut channel_index_list = [0; 246]; + let tmp = &buffer[7..]; + channel_index_list[..tmp.len()].copy_from_slice(tmp); + + Ok(L2CapCocReconfig { + connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + mtu: LittleEndian::read_u16(&buffer[2..]), + mps: LittleEndian::read_u16(&buffer[4..]), + channel_number: buffer[6], + channel_index_list, + }) +} + #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// This event informs the application of a change in status of the Enhanced ATT From ca8a20c2f69951a9e253e6f6feecb3054a7b946b Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 16:21:49 +0100 Subject: [PATCH 05/27] feat(vendor event): add L2CAP COC Reconfig Confirm event --- src/vendor/event/mod.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index d92edd2..ba6a5ee 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -132,6 +132,11 @@ pub enum VendorEvent { /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. L2CapCocReconfig(L2CapCocReconfig), + /// This event is generated when receiving a valid Credit Based Reconfigure Response packet. + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + L2CapCocReconfigConfirm(L2CapCocReconfigConfirm), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -635,7 +640,9 @@ impl VendorEvent { 0x0812 => Ok(VendorEvent::L2CapCocReconfig(to_l2cap_coc_reconfig( buffer, )?)), - // TODO: 0x0813 => todo!(), + 0x0813 => Ok(VendorEvent::L2CapCocReconfigConfirm( + to_l2cap_coc_reconfig_confirm(buffer)?, + )), // TODO: 0x0814 => todo!(), // TODO: 0x0815 => todo!(), // TODO: 0x0816 => todo!(), @@ -2849,6 +2856,33 @@ fn to_l2cap_coc_reconfig(buffer: &[u8]) -> Result Result { + require_len_at_least!(buffer, 4); + + Ok(L2CapCocReconfigConfirm { + connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + result: LittleEndian::read_u16(&buffer[2..]), + }) +} + #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// This event informs the application of a change in status of the Enhanced ATT From 3d90ebd790aa66cfb8450d5747542b7899b81793 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 16:25:43 +0100 Subject: [PATCH 06/27] feat(vendor event): add L2CAP COC Disconnect event --- src/vendor/event/mod.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index ba6a5ee..9807aca 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -137,6 +137,14 @@ pub enum VendorEvent { /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. L2CapCocReconfigConfirm(L2CapCocReconfigConfirm), + /// This event is generated when a connection-oriented channel is disconnected following an + /// L2CAP channel termination procedure. + /// + /// Includes the channel index of the connection oriented channel for which the primitive applies + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + L2CapCocDisconnect(u8), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -643,7 +651,10 @@ impl VendorEvent { 0x0813 => Ok(VendorEvent::L2CapCocReconfigConfirm( to_l2cap_coc_reconfig_confirm(buffer)?, )), - // TODO: 0x0814 => todo!(), + 0x0814 => Ok(VendorEvent::L2CapCocDisconnect({ + require_len!(buffer, 1); + buffer[0] + })), // TODO: 0x0815 => todo!(), // TODO: 0x0816 => todo!(), // TODO: 0x0817 => todo!(), From ac6dc5fbca98359dca1d850688b85a6c9e1868e5 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 16:31:43 +0100 Subject: [PATCH 07/27] feat(vendor event): add L2CAP COC Flow Control event --- src/vendor/event/mod.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 9807aca..275154a 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -145,6 +145,11 @@ pub enum VendorEvent { /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. L2CapCocDisconnect(u8), + /// This event is generated when receiving a valid Flow Control Credit signaling packet. + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + L2capCocFlowControl(L2capCocFlowControl), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -655,7 +660,9 @@ impl VendorEvent { require_len!(buffer, 1); buffer[0] })), - // TODO: 0x0815 => todo!(), + 0x0815 => Ok(VendorEvent::L2capCocFlowControl(to_l2cap_coc_flow_control( + buffer, + )?)), // TODO: 0x0816 => todo!(), // TODO: 0x0817 => todo!(), 0x0C01 => Ok(VendorEvent::GattAttributeModified( @@ -2894,6 +2901,32 @@ fn to_l2cap_coc_reconfig_confirm( }) } +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Flow Control Credit signaling packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2capCocFlowControl { + /// Index of the connection-oriented channel for which the primitive applies. + pub channel_index: u8, + /// Number of credits the receiving device can increment, corresponding to the + /// number of K-frames that can be sent to the peer device sending Flow Control + /// Credit packet. + /// + /// Values: + /// - 0 .. 65535 + pub credits: u16, +} + +fn to_l2cap_coc_flow_control(buffer: &[u8]) -> Result { + require_len!(buffer, 3); + + Ok(L2capCocFlowControl { + channel_index: buffer[0], + credits: LittleEndian::read_u16(&buffer[1..]), + }) +} + #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// This event informs the application of a change in status of the Enhanced ATT From aed20769f7d0cc2f165f5f7d8bca32932135b7cf Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 17:32:32 +0100 Subject: [PATCH 08/27] feat(vendor event): add L2CAP COC Rx Data event --- src/vendor/event/mod.rs | 44 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 275154a..84e6238 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -150,6 +150,16 @@ pub enum VendorEvent { /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. L2capCocFlowControl(L2capCocFlowControl), + /// This event is generated when receiving a valid K-frame packet on a connection-oriented channel + /// + /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. + /// + /// # Note: + /// For the first K-frame of the SDU, the information data contains the L2CAP SDU length coded in + /// two octets followed by the K-frame information payload. For the next K-frames of the SDU, the + /// information data only contains the K-frame information payload. + L2CapCocRxData(L2capCocRxData), + /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: /// - write without response @@ -663,7 +673,7 @@ impl VendorEvent { 0x0815 => Ok(VendorEvent::L2capCocFlowControl(to_l2cap_coc_flow_control( buffer, )?)), - // TODO: 0x0816 => todo!(), + 0x0816 => Ok(VendorEvent::L2CapCocRxData(to_l2cap_coc_rx_data(buffer)?)), // TODO: 0x0817 => todo!(), 0x0C01 => Ok(VendorEvent::GattAttributeModified( to_gatt_attribute_modified(buffer)?, @@ -2927,6 +2937,38 @@ fn to_l2cap_coc_flow_control(buffer: &[u8]) -> Result Result { + require_len_at_least!(buffer, 3); + + let length = LittleEndian::read_u16(&buffer[1..]); + let mut data = [0; 250]; + data[..length as usize].copy_from_slice(&buffer[3..]); + + Ok(L2capCocRxData { + channel_index: buffer[0], + length, + data, + }) +} #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// This event informs the application of a change in status of the Enhanced ATT From 2b60a93ee0931f201afacb85940932ffdccf68b7 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 17:52:40 +0100 Subject: [PATCH 09/27] feat(vendor L2CAP command): added L2CAP COC Connect command --- src/vendor/command/l2cap.rs | 68 ++++++++++++++++++++++++++++++++++++- src/vendor/event/mod.rs | 50 ++++----------------------- src/vendor/opcode.rs | 1 + 3 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index 82c59c9..806a4a4 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -4,7 +4,7 @@ extern crate byteorder; use crate::{ types::{ConnectionInterval, ExpectedConnectionLength}, - Controller, + ConnectionHandle, Controller, }; use byteorder::{ByteOrder, LittleEndian}; @@ -43,6 +43,11 @@ pub trait L2capCommands { &mut self, params: &ConnectionParameterUpdateResponse, ); + + /// This command sends a credit based connection request packet to the specified connection + /// + /// See Bluetooth Core specification Vol.3 Part A. + async fn coc_connect(&mut self, params: &L2CapCocConnect); } impl L2capCommands for T { @@ -57,6 +62,12 @@ impl L2capCommands for T { ConnectionParameterUpdateResponse, crate::vendor::opcode::L2CAP_CONN_PARAM_UPDATE_RESP ); + + impl_params!( + coc_connect, + L2CapCocConnect, + crate::vendor::opcode::L2CAP_COC_CONNECT + ); } /// Parameters for the @@ -123,3 +134,58 @@ impl ConnectionParameterUpdateResponse { bytes[15] = self.accepted as u8; } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Credit Based Connection +/// Request packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2CapCocConnect { + /// handle of the connection where this event occured. + pub conn_handle: ConnectionHandle, + /// Simplified Protocol/Service Multiplexer + /// + /// Values: + /// - 0x0000 .. 0x00FF + pub spsm: u16, + /// Maximum Transmission Unit + /// + /// Values: + /// - 23 .. 65535 + pub mtu: u16, + /// Maximum Payload Size (in octets) + /// + /// Values: + /// - 23 .. 248 + pub mps: u16, + /// Number of K-frames that can be received on the created channel(s) by + /// the L2CAP layer entity sending this packet. + /// + /// Values: + /// - 0 .. 65535 + pub initial_credits: u16, + /// Number of channels to be created. If this parameter is + /// set to 0, it requests the creation of one LE credit based connection- + /// oriented channel. Otherwise, it requests the creation of one or more + /// enhanced credit based connection-oriented channels. + /// + /// Values: + /// - 0 .. 5 + pub channel_number: u8, +} + +impl L2CapCocConnect { + const LENGTH: usize = 11; + + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert_eq!(bytes.len(), Self::LENGTH); + + LittleEndian::write_u16(&mut bytes[0..], self.conn_handle.0); + LittleEndian::write_u16(&mut bytes[2..], self.spsm); + LittleEndian::write_u16(&mut bytes[4..], self.mtu); + LittleEndian::write_u16(&mut bytes[6..], self.mps); + LittleEndian::write_u16(&mut bytes[8..], self.initial_credits); + bytes[10] = self.channel_number; + } +} diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 84e6238..253301b 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -158,7 +158,10 @@ pub enum VendorEvent { /// For the first K-frame of the SDU, the information data contains the L2CAP SDU length coded in /// two octets followed by the K-frame information payload. For the next K-frames of the SDU, the /// information data only contains the K-frame information payload. - L2CapCocRxData(L2capCocRxData), + L2capCocRxData(L2capCocRxData), + + /// Each time the + L2capCocTxPoolAvailable, /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: @@ -673,7 +676,7 @@ impl VendorEvent { 0x0815 => Ok(VendorEvent::L2capCocFlowControl(to_l2cap_coc_flow_control( buffer, )?)), - 0x0816 => Ok(VendorEvent::L2CapCocRxData(to_l2cap_coc_rx_data(buffer)?)), + 0x0816 => Ok(VendorEvent::L2capCocRxData(to_l2cap_coc_rx_data(buffer)?)), // TODO: 0x0817 => todo!(), 0x0C01 => Ok(VendorEvent::GattAttributeModified( to_gatt_attribute_modified(buffer)?, @@ -1041,6 +1044,7 @@ impl GapDeviceFound { pub use crate::event::AdvertisementEvent as GapDeviceFoundEvent; use super::command::gap::EventFlags; +use super::command::l2cap::L2CapCocConnect; fn to_gap_device_found(buffer: &[u8]) -> Result { const RSSI_UNAVAILABLE: i8 = 127; @@ -2723,51 +2727,11 @@ fn to_l2cap_command_reject(buffer: &[u8]) -> Result Result { require_len!(buffer, 10); Ok(L2CapCocConnect { - connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + conn_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), spsm: LittleEndian::read_u16(&buffer[2..]), mtu: LittleEndian::read_u16(&buffer[4..]), mps: LittleEndian::read_u16(&buffer[6..]), diff --git a/src/vendor/opcode.rs b/src/vendor/opcode.rs index a9f758b..3935a4c 100644 --- a/src/vendor/opcode.rs +++ b/src/vendor/opcode.rs @@ -145,5 +145,6 @@ vendor_opcodes! { { pub const L2CAP_CONN_PARAM_UPDATE_REQ = 0x01; pub const L2CAP_CONN_PARAM_UPDATE_RESP = 0x02; + pub const L2CAP_COC_CONNECT = 0x08; } } From bc01b804aef0a98ce865777f9054831882a47acb Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 18:07:18 +0100 Subject: [PATCH 10/27] feat(vendor L2CAP command): added L2CAP COC Connect Confirm command --- src/vendor/command/l2cap.rs | 73 ++++++++++++++++++++++++++++++++++++- src/vendor/event/mod.rs | 56 ++++------------------------ src/vendor/opcode.rs | 1 + 3 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index 806a4a4..2939fdd 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -44,10 +44,17 @@ pub trait L2capCommands { params: &ConnectionParameterUpdateResponse, ); - /// This command sends a credit based connection request packet to the specified connection + /// This command sends a credit-based connection request packet to the specified connection /// /// See Bluetooth Core specification Vol.3 Part A. async fn coc_connect(&mut self, params: &L2CapCocConnect); + + /// This command sends a credit-based connection response packet. It must be used upon receipt + /// of a connection request though [L2CAP COC Connection](crate::vendor::event::VendorEvent::L2CapCocConnect) + /// event. + /// + /// See Bluetooth Core specification Vol.3 Part A. + async fn coc_connect_confirm(&mut self, params: &L2CapCocConnectConfirm); } impl L2capCommands for T { @@ -68,6 +75,12 @@ impl L2capCommands for T { L2CapCocConnect, crate::vendor::opcode::L2CAP_COC_CONNECT ); + + impl_variable_length_params!( + coc_connect_confirm, + L2CapCocConnectConfirm, + crate::vendor::opcode::L2CAP_COC_CONNECT_CONFIRM + ); } /// Parameters for the @@ -189,3 +202,61 @@ impl L2CapCocConnect { bytes[10] = self.channel_number; } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Credit Based Connection Response packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2CapCocConnectConfirm { + /// handle of the connection where this event occured. + pub conn_handle: ConnectionHandle, + /// Maximum Transmission Unit + /// + /// Values: + /// - 23 .. 65535 + pub mtu: u16, + /// Maximum Payload Size (in octets) + /// + /// Values: + /// - 23 .. 248 + pub mps: u16, + /// Number of K-frames that can be received on the created channel(s) by + /// the L2CAP layer entity sending this packet. + /// + /// Values: + /// - 0 .. 65535 + pub initial_credits: u16, + /// This parameter indicates the outcome of the request. A value of 0x0000 + /// indicates success while a non zero value indicates the request is refused + /// + /// Values: + /// - 0x0000 .. 0x000C + pub result: u16, + /// Number of channels to be created. If this parameter is + /// set to 0, it requests the creation of one LE credit based connection- + /// oriented channel. Otherwise, it requests the creation of one or more + /// enhanced credit based connection-oriented channels. + /// + /// Values: + /// - 0 .. 5 + pub channel_number: u8, + /// List of channel indexes for which the primitives apply. + pub channel_index_list: [u8; 246], +} + +impl L2CapCocConnectConfirm { + const MAX_LENGTH: usize = 258; + + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert!(bytes.len() >= Self::MAX_LENGTH); + + LittleEndian::write_u16(&mut bytes[0..], self.conn_handle.0); + LittleEndian::write_u16(&mut bytes[2..], self.mtu); + LittleEndian::write_u16(&mut bytes[4..], self.mps); + LittleEndian::write_u16(&mut bytes[6..], self.initial_credits); + LittleEndian::write_u16(&mut bytes[8..], self.result); + bytes[10] = self.channel_number; + bytes[11..].copy_from_slice(&self.channel_index_list); + } +} diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 253301b..1201fcd 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -1044,7 +1044,7 @@ impl GapDeviceFound { pub use crate::event::AdvertisementEvent as GapDeviceFoundEvent; use super::command::gap::EventFlags; -use super::command::l2cap::L2CapCocConnect; +use super::command::l2cap::{L2CapCocConnect, L2CapCocConnectConfirm}; fn to_gap_device_found(buffer: &[u8]) -> Result { const RSSI_UNAVAILABLE: i8 = 127; @@ -2740,64 +2740,24 @@ fn to_l2cap_coc_connect(buffer: &[u8]) -> Result Result { require_len!(buffer, 12); + // TODO: how does one determine the length of channel_index_list? + let mut channel_index_list = [0; 246]; + let tmp = &buffer[7..]; + channel_index_list[..tmp.len()].copy_from_slice(tmp); + Ok(L2CapCocConnectConfirm { - connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), - spsm: LittleEndian::read_u16(&buffer[2..]), + conn_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), mtu: LittleEndian::read_u16(&buffer[4..]), mps: LittleEndian::read_u16(&buffer[6..]), initial_credits: LittleEndian::read_u16(&buffer[8..]), result: LittleEndian::read_u16(&buffer[10..]), channel_number: buffer[12], + channel_index_list, }) } diff --git a/src/vendor/opcode.rs b/src/vendor/opcode.rs index 3935a4c..4cb9d84 100644 --- a/src/vendor/opcode.rs +++ b/src/vendor/opcode.rs @@ -146,5 +146,6 @@ vendor_opcodes! { pub const L2CAP_CONN_PARAM_UPDATE_REQ = 0x01; pub const L2CAP_CONN_PARAM_UPDATE_RESP = 0x02; pub const L2CAP_COC_CONNECT = 0x08; + pub const L2CAP_COC_CONNECT_CONFIRM = 0x09; } } From c1b72fe6b4ab7f5254c4a8ac1c095fb3a73dd7b4 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 18:11:52 +0100 Subject: [PATCH 11/27] feat(vendor L2CAP command): added L2CAP COC Reconfig command --- src/vendor/command/l2cap.rs | 59 +++++++++++++++++++++++++++++++++++-- src/vendor/event/mod.rs | 33 ++------------------- src/vendor/opcode.rs | 1 + 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index 2939fdd..e0e840c 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -44,17 +44,22 @@ pub trait L2capCommands { params: &ConnectionParameterUpdateResponse, ); - /// This command sends a credit-based connection request packet to the specified connection + /// This command sends a Credit-Based Connection Request packet to the specified connection. /// /// See Bluetooth Core specification Vol.3 Part A. async fn coc_connect(&mut self, params: &L2CapCocConnect); - /// This command sends a credit-based connection response packet. It must be used upon receipt + /// This command sends a Credit-Based Connection Response packet. It must be used upon receipt /// of a connection request though [L2CAP COC Connection](crate::vendor::event::VendorEvent::L2CapCocConnect) /// event. /// /// See Bluetooth Core specification Vol.3 Part A. async fn coc_connect_confirm(&mut self, params: &L2CapCocConnectConfirm); + + /// This command sends a Credit-Based Reconfigure Request packet on the specified connection. + /// + /// See Bluetooth Core specification Vol.3 Part A. + async fn coc_reconfig(&mut self, params: &L2CapCocReconfig); } impl L2capCommands for T { @@ -81,6 +86,12 @@ impl L2capCommands for T { L2CapCocConnectConfirm, crate::vendor::opcode::L2CAP_COC_CONNECT_CONFIRM ); + + impl_variable_length_params!( + coc_reconfig, + L2CapCocReconfig, + crate::vendor::opcode::L2CAP_COC_RECONFIG + ); } /// Parameters for the @@ -260,3 +271,47 @@ impl L2CapCocConnectConfirm { bytes[11..].copy_from_slice(&self.channel_index_list); } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Credit Based Reconfigure Request packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2CapCocReconfig { + /// handle of the connection where this event occured. + pub conn_handle: ConnectionHandle, + /// Maximum Transmission Unit + /// + /// Values: + /// - 23 .. 65535 + pub mtu: u16, + /// Maximum Payload Size (in octets) + /// + /// Values: + /// - 23 .. 248 + pub mps: u16, + /// Number of channels to be created. If this parameter is + /// set to 0, it requests the creation of one LE credit based connection- + /// oriented channel. Otherwise, it requests the creation of one or more + /// enhanced credit based connection-oriented channels. + /// + /// Values: + /// - 0 .. 5 + pub channel_number: u8, + /// List of channel indexes for which the primitives apply. + pub channel_index_list: [u8; 246], +} + +impl L2CapCocReconfig { + const MAX_LENGTH: usize = 254; + + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert!(bytes.len() >= Self::MAX_LENGTH); + + LittleEndian::write_u16(&mut bytes[0..], self.conn_handle.0); + LittleEndian::write_u16(&mut bytes[2..], self.mtu); + LittleEndian::write_u16(&mut bytes[4..], self.mps); + bytes[6] = self.channel_number; + bytes[7..].copy_from_slice(&self.channel_index_list); + } +} diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 1201fcd..694686c 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -14,6 +14,7 @@ use core::mem; use core::time::Duration; pub use crate::types::{ConnectionInterval, ConnectionIntervalError}; +use crate::vendor::command::l2cap::L2CapCocReconfig; pub use crate::{BdAddr, BdAddrType, ConnectionHandle}; /// Vendor-specific events for the STM32WB5x radio coprocessor. @@ -2761,36 +2762,6 @@ fn to_l2cap_coc_connect_confirm( }) } -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// This event is generated when receiving a valid Credit Based Reconfigure Request packet. -/// -/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. -pub struct L2CapCocReconfig { - /// handle of the connection where this event occured. - pub connection_handle: ConnectionHandle, - /// Maximum Transmission Unit - /// - /// Values: - /// - 23 .. 65535 - pub mtu: u16, - /// Maximum Payload Size (in octets) - /// - /// Values: - /// - 23 .. 248 - pub mps: u16, - /// Number of channels to be created. If this parameter is - /// set to 0, it requests the creation of one LE credit based connection- - /// oriented channel. Otherwise, it requests the creation of one or more - /// enhanced credit based connection-oriented channels. - /// - /// Values: - /// - 0 .. 5 - pub channel_number: u8, - /// List of channel indexes for which the primitives apply. - pub channel_index_list: [u8; 246], -} - fn to_l2cap_coc_reconfig(buffer: &[u8]) -> Result { require_len_at_least!(buffer, 8); @@ -2800,7 +2771,7 @@ fn to_l2cap_coc_reconfig(buffer: &[u8]) -> Result Date: Fri, 29 Dec 2023 18:16:05 +0100 Subject: [PATCH 12/27] feat(vendor L2CAP command): added L2CAP COC Reconfig Confirm command --- src/vendor/command/l2cap.rs | 40 +++++++++++++++++++++++++++++++++++++ src/vendor/event/mod.rs | 20 ++----------------- src/vendor/opcode.rs | 1 + 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index e0e840c..acae88c 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -60,6 +60,13 @@ pub trait L2capCommands { /// /// See Bluetooth Core specification Vol.3 Part A. async fn coc_reconfig(&mut self, params: &L2CapCocReconfig); + + /// This command sends a Credit-Based Reconfigure Response packet. It must be use upon receipt + /// of a Credit-Based Reconfigure Request through + /// [L2CAP COC Reconfigure](crate::vendor::event::VendorEvent::L2CapCocReconfig) event. + /// + /// See Bluetooth Core specification Vol.3 Part A. + async fn coc_reconfig_confirm(&mut self, params: &L2CapCocReconfigConfirm); } impl L2capCommands for T { @@ -92,6 +99,12 @@ impl L2capCommands for T { L2CapCocReconfig, crate::vendor::opcode::L2CAP_COC_RECONFIG ); + + impl_params!( + coc_reconfig_confirm, + L2CapCocReconfigConfirm, + crate::vendor::opcode::L2CAP_COC_RECONFIG_CONFIRM + ); } /// Parameters for the @@ -315,3 +328,30 @@ impl L2CapCocReconfig { bytes[7..].copy_from_slice(&self.channel_index_list); } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Credit Based Reconfigure Response packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2CapCocReconfigConfirm { + /// handle of the connection where this event occured. + pub conn_handle: ConnectionHandle, + /// This parameter indicates the outcome of the request. A value of 0x0000 + /// indicates success while a non zero value indicates the request is refused + /// + /// Values: + /// - 0x0000 .. 0x000C + pub result: u16, +} + +impl L2CapCocReconfigConfirm { + const LENGTH: usize = 4; + + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert_eq!(bytes.len(), Self::LENGTH); + + LittleEndian::write_u16(&mut bytes[0..], self.conn_handle.0); + LittleEndian::write_u16(&mut bytes[2..], self.result); + } +} diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 694686c..556a1e1 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -1045,7 +1045,7 @@ impl GapDeviceFound { pub use crate::event::AdvertisementEvent as GapDeviceFoundEvent; use super::command::gap::EventFlags; -use super::command::l2cap::{L2CapCocConnect, L2CapCocConnectConfirm}; +use super::command::l2cap::{L2CapCocConnect, L2CapCocConnectConfirm, L2CapCocReconfigConfirm}; fn to_gap_device_found(buffer: &[u8]) -> Result { const RSSI_UNAVAILABLE: i8 = 127; @@ -2779,29 +2779,13 @@ fn to_l2cap_coc_reconfig(buffer: &[u8]) -> Result Result { require_len_at_least!(buffer, 4); Ok(L2CapCocReconfigConfirm { - connection_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + conn_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), result: LittleEndian::read_u16(&buffer[2..]), }) } diff --git a/src/vendor/opcode.rs b/src/vendor/opcode.rs index afd430e..96e9461 100644 --- a/src/vendor/opcode.rs +++ b/src/vendor/opcode.rs @@ -148,5 +148,6 @@ vendor_opcodes! { pub const L2CAP_COC_CONNECT = 0x08; pub const L2CAP_COC_CONNECT_CONFIRM = 0x09; pub const L2CAP_COC_RECONFIG = 0x0A; + pub const L2CAP_COC_RECONFIG_CONFIRM = 0x0B; } } From e8830b028c8ca392f60a8d5d23227322e60961cd Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 19:42:52 +0100 Subject: [PATCH 13/27] feat(vendor L2CAP command): added L2CAP COC Disconnect command --- src/vendor/command/l2cap.rs | 18 ++++++++++++++++++ src/vendor/event/mod.rs | 22 +++++++++++----------- src/vendor/opcode.rs | 1 + 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index acae88c..05f33f8 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -67,6 +67,16 @@ pub trait L2capCommands { /// /// See Bluetooth Core specification Vol.3 Part A. async fn coc_reconfig_confirm(&mut self, params: &L2CapCocReconfigConfirm); + + /// This command sends a Disconnection Request signaling packet on the specified connection-oriented + /// channel. + /// + /// See Bluetooth Core specification Vol.3 Part A. + /// + /// # Generated events + /// A [L2CAP COC Disconnection](crate::vendor::event::VendorEvent::L2CapCocDisconnect) event is + /// received when the disconnection of the channel is effective. + async fn coc_disconnect(&mut self, channel_index: u8); } impl L2capCommands for T { @@ -105,6 +115,14 @@ impl L2capCommands for T { L2CapCocReconfigConfirm, crate::vendor::opcode::L2CAP_COC_RECONFIG_CONFIRM ); + + async fn coc_disconnect(&mut self, channel_index: u8) { + self.controller_write( + crate::vendor::opcode::L2CAP_COC_DISCONNECT, + &[channel_index], + ) + .await + } } /// Parameters for the diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 556a1e1..039a34f 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -149,7 +149,7 @@ pub enum VendorEvent { /// This event is generated when receiving a valid Flow Control Credit signaling packet. /// /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. - L2capCocFlowControl(L2capCocFlowControl), + L2CapCocFlowControl(L2CapCocFlowControl), /// This event is generated when receiving a valid K-frame packet on a connection-oriented channel /// @@ -159,10 +159,10 @@ pub enum VendorEvent { /// For the first K-frame of the SDU, the information data contains the L2CAP SDU length coded in /// two octets followed by the K-frame information payload. For the next K-frames of the SDU, the /// information data only contains the K-frame information payload. - L2capCocRxData(L2capCocRxData), + L2CapCocRxData(L2CapCocRxData), /// Each time the - L2capCocTxPoolAvailable, + L2CapCocTxPoolAvailable, /// This event is generated to the application by the ATT server when a client modifies any /// attribute on the server, as consequence of one of the following ATT procedures: @@ -674,10 +674,10 @@ impl VendorEvent { require_len!(buffer, 1); buffer[0] })), - 0x0815 => Ok(VendorEvent::L2capCocFlowControl(to_l2cap_coc_flow_control( + 0x0815 => Ok(VendorEvent::L2CapCocFlowControl(to_l2cap_coc_flow_control( buffer, )?)), - 0x0816 => Ok(VendorEvent::L2capCocRxData(to_l2cap_coc_rx_data(buffer)?)), + 0x0816 => Ok(VendorEvent::L2CapCocRxData(to_l2cap_coc_rx_data(buffer)?)), // TODO: 0x0817 => todo!(), 0x0C01 => Ok(VendorEvent::GattAttributeModified( to_gatt_attribute_modified(buffer)?, @@ -2795,7 +2795,7 @@ fn to_l2cap_coc_reconfig_confirm( /// This event is generated when receiving a valid Flow Control Credit signaling packet. /// /// See Bluetooth spec. v.5.4 [Vol 3, Part A]. -pub struct L2capCocFlowControl { +pub struct L2CapCocFlowControl { /// Index of the connection-oriented channel for which the primitive applies. pub channel_index: u8, /// Number of credits the receiving device can increment, corresponding to the @@ -2807,10 +2807,10 @@ pub struct L2capCocFlowControl { pub credits: u16, } -fn to_l2cap_coc_flow_control(buffer: &[u8]) -> Result { +fn to_l2cap_coc_flow_control(buffer: &[u8]) -> Result { require_len!(buffer, 3); - Ok(L2capCocFlowControl { + Ok(L2CapCocFlowControl { channel_index: buffer[0], credits: LittleEndian::read_u16(&buffer[1..]), }) @@ -2826,7 +2826,7 @@ fn to_l2cap_coc_flow_control(buffer: &[u8]) -> Result Result { +fn to_l2cap_coc_rx_data(buffer: &[u8]) -> Result { require_len_at_least!(buffer, 3); let length = LittleEndian::read_u16(&buffer[1..]); let mut data = [0; 250]; data[..length as usize].copy_from_slice(&buffer[3..]); - Ok(L2capCocRxData { + Ok(L2CapCocRxData { channel_index: buffer[0], length, data, diff --git a/src/vendor/opcode.rs b/src/vendor/opcode.rs index 96e9461..0d7a8d0 100644 --- a/src/vendor/opcode.rs +++ b/src/vendor/opcode.rs @@ -149,5 +149,6 @@ vendor_opcodes! { pub const L2CAP_COC_CONNECT_CONFIRM = 0x09; pub const L2CAP_COC_RECONFIG = 0x0A; pub const L2CAP_COC_RECONFIG_CONFIRM = 0x0B; + pub const L2CAP_COC_DISCONNECT = 0x0C; } } From 9c9c5a62d44828102a02098a0de46f153bff70ad Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 19:46:11 +0100 Subject: [PATCH 14/27] feat(vendor L2CAP command): added L2CAP COC Flow Control command --- src/vendor/command/l2cap.rs | 40 +++++++++++++++++++++++++++++++++++++ src/vendor/event/mod.rs | 21 +++---------------- src/vendor/opcode.rs | 1 + 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index 05f33f8..054615e 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -77,6 +77,12 @@ pub trait L2capCommands { /// A [L2CAP COC Disconnection](crate::vendor::event::VendorEvent::L2CapCocDisconnect) event is /// received when the disconnection of the channel is effective. async fn coc_disconnect(&mut self, channel_index: u8); + + /// This command sends a Flow Control Credit signaling packet on the specified connection-oriented + /// channel. + /// + /// See Bluetooth Core specification Vol.3 Part A. + async fn coc_flow_control(&mut self, params: &L2CapCocFlowControl); } impl L2capCommands for T { @@ -123,6 +129,12 @@ impl L2capCommands for T { ) .await } + + impl_params!( + coc_flow_control, + L2CapCocFlowControl, + crate::vendor::opcode::L2CAP_COC_FLOW_CONTROL + ); } /// Parameters for the @@ -373,3 +385,31 @@ impl L2CapCocReconfigConfirm { LittleEndian::write_u16(&mut bytes[2..], self.result); } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// This event is generated when receiving a valid Flow Control Credit signaling packet. +/// +/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. +pub struct L2CapCocFlowControl { + /// Index of the connection-oriented channel for which the primitive applies. + pub channel_index: u8, + /// Number of credits the receiving device can increment, corresponding to the + /// number of K-frames that can be sent to the peer device sending Flow Control + /// Credit packet. + /// + /// Values: + /// - 0 .. 65535 + pub credits: u16, +} + +impl L2CapCocFlowControl { + const LENGTH: usize = 3; + + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert_eq!(bytes.len(), Self::LENGTH); + + bytes[0] = self.channel_index; + LittleEndian::write_u16(&mut bytes[1..], self.credits); + } +} diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 039a34f..0b758fc 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -1045,7 +1045,9 @@ impl GapDeviceFound { pub use crate::event::AdvertisementEvent as GapDeviceFoundEvent; use super::command::gap::EventFlags; -use super::command::l2cap::{L2CapCocConnect, L2CapCocConnectConfirm, L2CapCocReconfigConfirm}; +use super::command::l2cap::{ + L2CapCocConnect, L2CapCocConnectConfirm, L2CapCocFlowControl, L2CapCocReconfigConfirm, +}; fn to_gap_device_found(buffer: &[u8]) -> Result { const RSSI_UNAVAILABLE: i8 = 127; @@ -2790,23 +2792,6 @@ fn to_l2cap_coc_reconfig_confirm( }) } -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// This event is generated when receiving a valid Flow Control Credit signaling packet. -/// -/// See Bluetooth spec. v.5.4 [Vol 3, Part A]. -pub struct L2CapCocFlowControl { - /// Index of the connection-oriented channel for which the primitive applies. - pub channel_index: u8, - /// Number of credits the receiving device can increment, corresponding to the - /// number of K-frames that can be sent to the peer device sending Flow Control - /// Credit packet. - /// - /// Values: - /// - 0 .. 65535 - pub credits: u16, -} - fn to_l2cap_coc_flow_control(buffer: &[u8]) -> Result { require_len!(buffer, 3); diff --git a/src/vendor/opcode.rs b/src/vendor/opcode.rs index 0d7a8d0..5fac205 100644 --- a/src/vendor/opcode.rs +++ b/src/vendor/opcode.rs @@ -150,5 +150,6 @@ vendor_opcodes! { pub const L2CAP_COC_RECONFIG = 0x0A; pub const L2CAP_COC_RECONFIG_CONFIRM = 0x0B; pub const L2CAP_COC_DISCONNECT = 0x0C; + pub const L2CAP_COC_FLOW_CONTROL = 0x0D; } } From 1798b4dace91742381f1502b2e3909577de4ed7d Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 19:53:22 +0100 Subject: [PATCH 15/27] feat(vendor L2CAP command): added L2CAP COC Tx Data command --- src/vendor/command/l2cap.rs | 40 +++++++++++++++++++++++++++++++++++++ src/vendor/opcode.rs | 1 + 2 files changed, 41 insertions(+) diff --git a/src/vendor/command/l2cap.rs b/src/vendor/command/l2cap.rs index 054615e..f366ad7 100644 --- a/src/vendor/command/l2cap.rs +++ b/src/vendor/command/l2cap.rs @@ -83,6 +83,19 @@ pub trait L2capCommands { /// /// See Bluetooth Core specification Vol.3 Part A. async fn coc_flow_control(&mut self, params: &L2CapCocFlowControl); + + /// This command sends a K-frame packet on the specified connection-oriented channel. + /// + /// See Bluetooth Core specification Vol.3 Part A. + /// + /// # Note + /// for the first K-frame of the SDU, the Information data shall contain + /// the L2CAP SDU Length coded on two octets followed by the K-frame information + /// payload. For the next K-frames of the SDU, the Information data shall only + /// contain the K-frame information payload. + /// The Length value must not exceed (BLE_CMD_MAX_PARAM_LEN - 3) i.e. 252 for + /// BLE_CMD_MAX_PARAM_LEN default value. + async fn coc_tx_data(&mut self, params: &L2CapCocTxData); } impl L2capCommands for T { @@ -135,6 +148,12 @@ impl L2capCommands for T { L2CapCocFlowControl, crate::vendor::opcode::L2CAP_COC_FLOW_CONTROL ); + + impl_variable_length_params!( + coc_tx_data, + L2CapCocTxData, + crate::vendor::opcode::L2CAP_COC_TX_DATA + ); } /// Parameters for the @@ -413,3 +432,24 @@ impl L2CapCocFlowControl { LittleEndian::write_u16(&mut bytes[1..], self.credits); } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Parameter for the [coc_tx_data](L2capCommands::coc_tx_data) command +pub struct L2CapCocTxData { + pub channel_index: u8, + pub length: u16, + pub data: [u8; 252], +} + +impl L2CapCocTxData { + const MAX_LENGTH: usize = 256; + + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert!(bytes.len() >= Self::MAX_LENGTH); + + bytes[0] = self.channel_index; + LittleEndian::write_u16(&mut bytes[1..], self.length); + bytes[3..].copy_from_slice(&self.data); + } +} diff --git a/src/vendor/opcode.rs b/src/vendor/opcode.rs index 5fac205..04146e0 100644 --- a/src/vendor/opcode.rs +++ b/src/vendor/opcode.rs @@ -151,5 +151,6 @@ vendor_opcodes! { pub const L2CAP_COC_RECONFIG_CONFIRM = 0x0B; pub const L2CAP_COC_DISCONNECT = 0x0C; pub const L2CAP_COC_FLOW_CONTROL = 0x0D; + pub const L2CAP_COC_TX_DATA = 0x0E; } } From 6178a1177d56da8b6ff8adb1b89bcf13a6f85433 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Fri, 29 Dec 2023 19:57:56 +0100 Subject: [PATCH 16/27] feat(vendor event): add L2CAP COC Tx Pool Available event --- src/vendor/event/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 0b758fc..1daef15 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -161,7 +161,9 @@ pub enum VendorEvent { /// information data only contains the K-frame information payload. L2CapCocRxData(L2CapCocRxData), - /// Each time the + /// Each time the [L2CAO COC Tx Data](crate::vendor::command::l2cap::L2capCommands::coc_tx_data) command + /// raises the error code [Insufficient Resources](VendorStatus::InsufficientResources) (0x64), this event + /// is generated as soon as there is a free buffer available for sending K-frames. L2CapCocTxPoolAvailable, /// This event is generated to the application by the ATT server when a client modifies any @@ -678,7 +680,7 @@ impl VendorEvent { buffer, )?)), 0x0816 => Ok(VendorEvent::L2CapCocRxData(to_l2cap_coc_rx_data(buffer)?)), - // TODO: 0x0817 => todo!(), + 0x0817 => Ok(VendorEvent::L2CapCocTxPoolAvailable), 0x0C01 => Ok(VendorEvent::GattAttributeModified( to_gatt_attribute_modified(buffer)?, )), From 452c1fe30bb84f2a098e49f405cec52ca44de960 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 00:11:59 +0100 Subject: [PATCH 17/27] feat(vendor event): add GATT multi notification event --- src/vendor/event/mod.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 1daef15..e615477 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -299,6 +299,9 @@ pub enum VendorEvent { /// This event informs the application of a change in status of the enhanced ATT bearer handled /// by the special L2CAP channel. GattEattBrearer(GattEattBrearer), + + /// This event is generated when a Multiple Handle Value Notification is received from the server. + GattMultiNotification(GattMultiNotification), } /// Enumeration of vendor-specific status codes. @@ -741,7 +744,9 @@ impl VendorEvent { to_att_prepare_write_permit_request(buffer)?, )), 0x0C19 => Ok(VendorEvent::GattEattBrearer(to_gatt_eatt_bearer(buffer)?)), - // TODO: 0x0C1A => todo!(), + 0x0C1A => Ok(VendorEvent::GattMultiNotification( + to_gatt_multi_notification(buffer)?, + )), // TODO: 0x0C1D => todo!(), // TODO: 0x0C1E => todo!(), // TODO: 0x0C1F => todo!(), @@ -2877,3 +2882,34 @@ fn to_gatt_eatt_bearer(buffer: &[u8]) -> Result Result { + require_len_at_least!(buffer, 6); + + let data_len = LittleEndian::read_u16(&buffer[4..]); + let mut data = [0; 247]; + data[..data_len as usize].copy_from_slice(&buffer[4..]); + + Ok(GattMultiNotification { + conn_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + offset: LittleEndian::read_u16(&buffer[2..]), + data_len, + data, + }) +} From 2ccee609b1ef96ec91786c855923a6ee31aed38c Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 01:37:01 +0100 Subject: [PATCH 18/27] feat(vendor event): add GATT Notification Complete event --- src/vendor/event/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index e615477..4dcf18d 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -302,6 +302,13 @@ pub enum VendorEvent { /// This event is generated when a Multiple Handle Value Notification is received from the server. GattMultiNotification(GattMultiNotification), + + /// This event is generated on server side after the transmission of all notifications linked with + /// the a local update of a characteristic value (if it is enabled at the creation of the characteristic + /// with [GATT Notify Notification Completion](crate::vendor::command::gatt::CharacteristicEvent) mask + /// and if the characteristic supports notifications). + // TODO: update crate::vendor::command::gatt::CharacteristicEvent + GattNotificationComplete(AttributeHandle), } /// Enumeration of vendor-specific status codes. @@ -747,6 +754,11 @@ impl VendorEvent { 0x0C1A => Ok(VendorEvent::GattMultiNotification( to_gatt_multi_notification(buffer)?, )), + 0x0C1B => Ok(VendorEvent::GattNotificationComplete({ + require_len!(buffer, 2); + AttributeHandle(LittleEndian::read_u16(buffer)) + })), + // TODO: 0x0C1C => todo!(), // TODO: 0x0C1D => todo!(), // TODO: 0x0C1E => todo!(), // TODO: 0x0C1F => todo!(), From 1e08cc6d3cd8bdc25e1bb6fac710567958e026fa Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 01:44:49 +0100 Subject: [PATCH 19/27] feat(vendor event): add GATT Read Ext event --- src/vendor/event/mod.rs | 54 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 4dcf18d..7cf6151 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -309,6 +309,16 @@ pub enum VendorEvent { /// and if the characteristic supports notifications). // TODO: update crate::vendor::command::gatt::CharacteristicEvent GattNotificationComplete(AttributeHandle), + + /// When it is enabled with [set_event_mast](crate::vendor::command::gatt::GattCommands::set_event_mask), + /// this event is generated instead of [ATT Read Response](VendorEvent::AttReadResponse) / + /// [ATT Read Blob Response](VendorEvent::AttReadBlobResponse) / + /// [ATT Read Multiple Response](VendorEvent::AttReadMultipleResponse). + /// + /// This event should be used instead of those events when `ATT_MTU > + /// (BLE_EVT_MAX_PARAM_LEN - 4)` i.e. `ATT_MTU > 251` for `BLE_EVT_MAX_PARAM_LEN` + /// default value. + GattReadExt(GattReadExt), } /// Enumeration of vendor-specific status codes. @@ -758,8 +768,7 @@ impl VendorEvent { require_len!(buffer, 2); AttributeHandle(LittleEndian::read_u16(buffer)) })), - // TODO: 0x0C1C => todo!(), - // TODO: 0x0C1D => todo!(), + 0x0C1D => Ok(VendorEvent::GattReadExt(to_gatt_read_ext(buffer)?)), // TODO: 0x0C1E => todo!(), // TODO: 0x0C1F => todo!(), _ => Err(crate::event::Error::Vendor(VendorError::UnknownEvent( @@ -2925,3 +2934,44 @@ fn to_gatt_multi_notification(buffer: &[u8]) -> Result Result { + require_len_at_least!(buffer, 6); + + let value_len = LittleEndian::read_u16(&buffer[4..]) as usize; + require_len!(buffer, 6 + value_len); + + let mut value_buf = [0; MAX_ATTRIBUTE_VALUE_LEN]; + value_buf[..value_len].copy_from_slice(&buffer[6..]); + + Ok(GattReadExt { + conn_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + offset: LittleEndian::read_u16(&buffer[2..]), + value_len, + value_buf, + }) +} + +impl GattReadExt { + pub fn value(&self) -> &[u8] { + &self.value_buf[..self.value_len] + } +} From 036841c7a520c93ea47201c1b82f3d70aa64ff23 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 17:24:23 +0100 Subject: [PATCH 20/27] feat(vendor event): add GATT Indication Ext event --- src/vendor/event/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 7cf6151..ebf2a3f 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -319,6 +319,14 @@ pub enum VendorEvent { /// (BLE_EVT_MAX_PARAM_LEN - 4)` i.e. `ATT_MTU > 251` for `BLE_EVT_MAX_PARAM_LEN` /// default value. GattReadExt(GattReadExt), + + /// When it is enabled with [set_event_mast](crate::vendor::command::gatt::GattCommands::set_event_mask), + /// this event is generated instead of [GATT Indication](VendorEvent::GattIndication) event. + /// + /// This event should be used instead of `ACI_GATT_INDICATION_EVENT` when `ATT_MTU + /// > (BLE_EVT_MAX_PARAM_LEN - 4)` i.e. `ATT_MTU > 251` for `BLE_EVT_MAX_PARAM_LEN` + /// default value. + GattIndicationExt(GattIndicationExt), } /// Enumeration of vendor-specific status codes. @@ -769,7 +777,9 @@ impl VendorEvent { AttributeHandle(LittleEndian::read_u16(buffer)) })), 0x0C1D => Ok(VendorEvent::GattReadExt(to_gatt_read_ext(buffer)?)), - // TODO: 0x0C1E => todo!(), + 0x0C1E => Ok(VendorEvent::GattIndicationExt(to_gatt_indication_ext( + buffer, + )?)), // TODO: 0x0C1F => todo!(), _ => Err(crate::event::Error::Vendor(VendorError::UnknownEvent( event_code, @@ -2975,3 +2985,47 @@ impl GattReadExt { &self.value_buf[..self.value_len] } } + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Defines data returned by [GATT Indication Ext](VendorEvent::GattIndicationExt) event +pub struct GattIndicationExt { + /// The connection handle related to the event. + pub conn_handle: ConnectionHandle, + /// The handle of the attribute + pub attribute_handle: AttributeHandle, + /// - Bits 14-0: offset in octets from which Attribute_Value data + /// starts. + /// - Bit 15 is used as flag: when set to 1 it indicates that more + /// data are to come (fragmented event in case of long attribute data). + pub offset: u16, + + // Number of valid bytes in value_buf + value_len: usize, + // Current value of the attribute. Only the first value_len bytes are valid. + value_buf: [u8; MAX_ATTRIBUTE_VALUE_LEN], +} + +fn to_gatt_indication_ext(buffer: &[u8]) -> Result { + require_len_at_least!(buffer, 6); + + let value_len = LittleEndian::read_u16(&buffer[6..]) as usize; + require_len!(buffer, 8 + value_len); + + let mut value_buf = [0; MAX_ATTRIBUTE_VALUE_LEN]; + value_buf[..value_len].copy_from_slice(&buffer[8..]); + + Ok(GattIndicationExt { + conn_handle: ConnectionHandle(LittleEndian::read_u16(&buffer[0..])), + attribute_handle: AttributeHandle(LittleEndian::read_u16(&buffer[2..])), + offset: LittleEndian::read_u16(&buffer[4..]), + value_len, + value_buf, + }) +} + +impl GattIndicationExt { + pub fn value(&self) -> &[u8] { + &self.value_buf[..self.value_len] + } +} From ea9f37ddca2c5c88cdb99a6e9657582627e99bfe Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 17:29:23 +0100 Subject: [PATCH 21/27] feat(vendor event): add GATT Notification Ext event --- src/vendor/event/mod.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index ebf2a3f..9fef9e1 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -310,6 +310,10 @@ pub enum VendorEvent { // TODO: update crate::vendor::command::gatt::CharacteristicEvent GattNotificationComplete(AttributeHandle), + // TODO: there is probably a better way to handle Read/Indication/Notification extended events, given + // TODO: that the offset param contains extra information + // TODO: also, the Read event has a different structure from Indication/Notification, unlike non-extended + // TODO: events /// When it is enabled with [set_event_mast](crate::vendor::command::gatt::GattCommands::set_event_mask), /// this event is generated instead of [ATT Read Response](VendorEvent::AttReadResponse) / /// [ATT Read Blob Response](VendorEvent::AttReadBlobResponse) / @@ -326,7 +330,15 @@ pub enum VendorEvent { /// This event should be used instead of `ACI_GATT_INDICATION_EVENT` when `ATT_MTU /// > (BLE_EVT_MAX_PARAM_LEN - 4)` i.e. `ATT_MTU > 251` for `BLE_EVT_MAX_PARAM_LEN` /// default value. - GattIndicationExt(GattIndicationExt), + GattIndicationExt(AttributeValueExt), + + /// When it is enabled with [set_event_mast](crate::vendor::command::gatt::GattCommands::set_event_mask), + /// this event is generated instead of [GATT Notification](VendorEvent::GattNotification) event. + /// + /// This event should be used instead of `ACI_GATT_INDICATION_EVENT` when `ATT_MTU + /// > (BLE_EVT_MAX_PARAM_LEN - 4)` i.e. `ATT_MTU > 251` for `BLE_EVT_MAX_PARAM_LEN` + /// default value. + GattNotificationExt(AttributeValueExt), } /// Enumeration of vendor-specific status codes. @@ -777,10 +789,12 @@ impl VendorEvent { AttributeHandle(LittleEndian::read_u16(buffer)) })), 0x0C1D => Ok(VendorEvent::GattReadExt(to_gatt_read_ext(buffer)?)), - 0x0C1E => Ok(VendorEvent::GattIndicationExt(to_gatt_indication_ext( + 0x0C1E => Ok(VendorEvent::GattIndicationExt(to_attribute_value_ext( + buffer, + )?)), + 0x0C1F => Ok(VendorEvent::GattNotificationExt(to_attribute_value_ext( buffer, )?)), - // TODO: 0x0C1F => todo!(), _ => Err(crate::event::Error::Vendor(VendorError::UnknownEvent( event_code, ))), @@ -2988,8 +3002,9 @@ impl GattReadExt { #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Defines data returned by [GATT Indication Ext](VendorEvent::GattIndicationExt) event -pub struct GattIndicationExt { +/// Defines data returned by [GATT Indication Ext](VendorEvent::GattIndicationExt) or +/// [GATT Notification Ext](VendorEvent::GattNotificationExt) event +pub struct AttributeValueExt { /// The connection handle related to the event. pub conn_handle: ConnectionHandle, /// The handle of the attribute @@ -3006,7 +3021,7 @@ pub struct GattIndicationExt { value_buf: [u8; MAX_ATTRIBUTE_VALUE_LEN], } -fn to_gatt_indication_ext(buffer: &[u8]) -> Result { +fn to_attribute_value_ext(buffer: &[u8]) -> Result { require_len_at_least!(buffer, 6); let value_len = LittleEndian::read_u16(&buffer[6..]) as usize; @@ -3015,7 +3030,7 @@ fn to_gatt_indication_ext(buffer: &[u8]) -> Result Result &[u8] { &self.value_buf[..self.value_len] } From 7b351d4cc272c80b6dca3a5fc777be33cde89849 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 19:17:41 +0100 Subject: [PATCH 22/27] doc: Connection Handle range for Enhanced ATT bearer now ends at 0xEA3F --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 7ba928d..36ca03f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -480,7 +480,7 @@ impl core::convert::From for u8 { /// /// Values: /// - 0x0000 .. 0xEFFF: Unenhanced ATT bearer -/// - 0xEA00 .. 0xEA1F: Enhanced ATT bearer (the LSB-byte of the parameter is +/// - 0xEA00 .. 0xEA3F: Enhanced ATT bearer (the LSB-byte of the parameter is /// the connection oriented channel index) #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 1a20bd3d1b977db32b75ce6127d4340ad6f0d829 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 19:32:19 +0100 Subject: [PATCH 23/27] feat(vendor GATT commands): added Notify Notification Complete characterisitc event --- src/vendor/command/gatt.rs | 6 ++++++ src/vendor/event/mod.rs | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vendor/command/gatt.rs b/src/vendor/command/gatt.rs index d818aa0..1530dfb 100644 --- a/src/vendor/command/gatt.rs +++ b/src/vendor/command/gatt.rs @@ -1516,6 +1516,9 @@ bitflags::bitflags! { /// The application will be notified when a read request of any type is got for this /// attribute. const CONFIRM_READ = 0x04; + + /// The application will be notified when a notification is complete + const NOTIFY_NOTIFICATION_COMPLETE = 0x08; } } @@ -1533,6 +1536,9 @@ defmt::bitflags! { /// The application will be notified when a read request of any type is got for this /// attribute. const CONFIRM_READ = 0x04; + + /// The application will be notified when a notification is complete + const NOTIFY_NOTIFICATION_COMPLETE = 0x08; } } diff --git a/src/vendor/event/mod.rs b/src/vendor/event/mod.rs index 9fef9e1..6541100 100644 --- a/src/vendor/event/mod.rs +++ b/src/vendor/event/mod.rs @@ -307,7 +307,6 @@ pub enum VendorEvent { /// the a local update of a characteristic value (if it is enabled at the creation of the characteristic /// with [GATT Notify Notification Completion](crate::vendor::command::gatt::CharacteristicEvent) mask /// and if the characteristic supports notifications). - // TODO: update crate::vendor::command::gatt::CharacteristicEvent GattNotificationComplete(AttributeHandle), // TODO: there is probably a better way to handle Read/Indication/Notification extended events, given From e3160a70d2c7cd10636af4118d227ceccfaeea95 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 19:43:55 +0100 Subject: [PATCH 24/27] doc: HAL set config data parameters are updated --- src/vendor/command/hal.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vendor/command/hal.rs b/src/vendor/command/hal.rs index 929a41c..935a00b 100644 --- a/src/vendor/command/hal.rs +++ b/src/vendor/command/hal.rs @@ -224,11 +224,15 @@ pub struct ConfigData { ///- 0x00: CONFIG_DATA_PUBADDR_OFFSET; /// Bluetooth public address; 6 bytes ///- 0x08: CONFIG_DATA_ER_OFFSET; - /// Encryption root key used to derive LTK and CSRK; 16 bytes + /// Encryption root key used to derive LTK (legacy) and CSRK; 16 bytes ///- 0x18: CONFIG_DATA_IR_OFFSET; - /// Identity root key used to derive LTK and CSRK; 16 bytes + /// Identity root key used to derive DHK (legacy) and IRK; 16 bytes ///- 0x2E: CONFIG_DATA_RANDOM_ADDRESS_OFFSET; /// Static Random Address; 6 bytes + ///- 0x34: CONFIG_DATA_GAP_ADD_REC_NBR_OFFSET; + /// GAP service additional record number; 1 byte + ///- 0x35: CONFIG_DATA_SC_KEY_TYPE_OFFSET; + /// Secure Connection key type (0: "normal", 1: "debug"); 1 byte ///- 0xB0: CONFIG_DATA_SMP_MODE_OFFSET; /// SMP mode (0: "normal", 1: "bypass", 2: "no blacklist"); 1 byte ///- 0xC0: CONFIG_DATA_LL_SCAN_CHAN_MAP_OFFSET (only for STM32WB); From 37e4223d9a84e27712673472c35e6724034a4f16 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 30 Dec 2023 22:08:27 +0100 Subject: [PATCH 25/27] feat(LE command): add Set Controller To Host Flow Control command --- src/host/mod.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ src/opcode.rs | 1 + 2 files changed, 54 insertions(+) diff --git a/src/host/mod.rs b/src/host/mod.rs index 87ea5df..0b6eb7a 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -186,6 +186,29 @@ pub trait HostHci { power_level_type: TxPowerLevel, ); + /// This command is used by the Host to turn flow control on and off for data and/or voice + /// sent in the direction from the Controller to the Host. + /// + /// If the flow control is turned off, the Host should not send the + /// [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) command. + /// That command will be ignored by the Controller it is sent by the Host and flow control is + /// off. + /// + /// If flow control is turned on for HCI ACL Data Packets and off for HCI synchronous + /// Data Packets, [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) + /// commands sent by the Host should only contain [Connection Handles](ConnectionHandle) + /// for ACL connections. + /// + /// If flow control is turned off for HCI ACL Data Packets and on for HCI synchronous Data Packets, + /// [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) commands sent + /// by the Host should only contain [Connection Handles](ConnectionHandle) for synchronous connections. + /// + /// If flow control is turned on for HCI ACL Data Packets and HCI synchronous Data Packets, + /// the Host will send [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) + /// commands both for ACL connections and synchronous connections. + /// The [Flow Control](FlowControl) parameter shall only be changed if no connections exist. + async fn set_controller_to_host_flow_control(&mut self, flow_control: FlowControl); + /// This command reads the values for the version information for the local Controller. /// /// Defined in Bluetooth Specification Vol 2, Part E, Section 7.4.1. @@ -1154,6 +1177,14 @@ where .await; } + async fn set_controller_to_host_flow_control(&mut self, flow_control: FlowControl) { + self.controller_write( + crate::opcode::SET_CONTROLLER_TO_HOST_FLOW_CONTROL, + &[flow_control as u8], + ) + .await; + } + async fn read_local_version_information(&mut self) { self.controller_write(crate::opcode::READ_LOCAL_VERSION_INFO, &[]) .await; @@ -1657,6 +1688,28 @@ pub enum TxPowerLevel { Maximum = 0x01, } +/// For the [set_controller_to_host_flow_control](HostHci::set_controller_to_host_flow_control) command, the +/// allowed values for flow control. +/// +/// See Bluetooth spec. v.5.4 [Vol 4, Part E, 7.3.38]. +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FlowControl { + /// Flow control off in direction from controller to host. `default` + #[default] + Off = 0x00, + /// Flow control on for HCI ACL Data Packets and off for HCI synchronous. + /// Data Packets in direction from Controller to Host. + HciAclDataOnly = 0x01, + /// Flow control off for HCI ACL Data Packets and on for HCI synchronous. + /// Data Packets in direction from Controller to Host. + HciSyncDataOnly = 0x02, + /// control on both for HCI ACL Data Packets and HCI synchronous. + /// Data Packets in direction from Controller to Host. + Both = 0x03, +} + #[cfg(not(feature = "defmt"))] bitflags::bitflags! { /// Event flags defined for the [`le_set_event_mask`](HostHci::le_set_event_mask) command. diff --git a/src/opcode.rs b/src/opcode.rs index c933c82..bb6a692 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -49,6 +49,7 @@ opcodes! { pub const SET_EVENT_MASK = 0x0001; pub const RESET = 0x0003; pub const READ_TX_POWER_LEVEL = 0x002D; + pub const SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x031; } InfoParam = 0x0004; From 90d31c0f8f461823a7d62d9bbe003db6d6cd845c Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Mon, 1 Jan 2024 18:30:10 +0100 Subject: [PATCH 26/27] feat(LE commad): add Host Buffer Size commad --- src/host/mod.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++--- src/opcode.rs | 1 + 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/host/mod.rs b/src/host/mod.rs index 0b6eb7a..61d0ba2 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -190,25 +190,46 @@ pub trait HostHci { /// sent in the direction from the Controller to the Host. /// /// If the flow control is turned off, the Host should not send the - /// [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) command. + /// [Number of Completed Packets](HostHci::number_of_completed_packets) command. /// That command will be ignored by the Controller it is sent by the Host and flow control is /// off. /// /// If flow control is turned on for HCI ACL Data Packets and off for HCI synchronous - /// Data Packets, [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) + /// Data Packets, [Number of Completed Packets](HostHci::number_of_completed_packets) /// commands sent by the Host should only contain [Connection Handles](ConnectionHandle) /// for ACL connections. /// /// If flow control is turned off for HCI ACL Data Packets and on for HCI synchronous Data Packets, - /// [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) commands sent + /// [Number of Completed Packets](HostHci::number_of_completed_packets) commands sent /// by the Host should only contain [Connection Handles](ConnectionHandle) for synchronous connections. /// /// If flow control is turned on for HCI ACL Data Packets and HCI synchronous Data Packets, - /// the Host will send [Host Number of Completed Packets](HostHci::host_number_of_completed_packets) + /// the Host will send [Number of Completed Packets](HostHci::number_of_completed_packets) /// commands both for ACL connections and synchronous connections. /// The [Flow Control](FlowControl) parameter shall only be changed if no connections exist. async fn set_controller_to_host_flow_control(&mut self, flow_control: FlowControl); + /// This command is used by the Host to notify the Controller about the Maximum size of the data portion + /// of HCI ACL and Synchronous Sata Packets sent from the controller to the Host. + /// + /// The Controller shal segment the data to be transmitted from the Controller to the Host according + /// to these sizes, so that the HCI Data Packets will contain data with up to these sizes. + /// + /// This command also notifies the Controller about the total number of HCI ACL and Synchronous Data Packets + /// that can be storede in the data buffers of the Host. + /// + /// If flow control from the Controller to the Host is turned off, and this command has not been issued + /// by the Host, this means the controller will send HCI Data Packets to the Host with any lengths the + /// Controlller wants to use, and it is assumed that the data buffer sizes of the Host are unlimited. + /// + /// If flow control from the Controller to the Host is turned on, this command shall after a power-on + /// or a reset always be sent by the Host before the first + /// [Number of Completed Packets](HostHci::number_of_completed_packets) command is sent. + /// + /// The [Set Controller to Host Flow Control](HostHci::set_controller_to_host_flow_control) commad + /// is used to turn flow control on or off. + async fn host_buffer_size(&mut self, params: HostBufferSize); + /// This command reads the values for the version information for the local Controller. /// /// Defined in Bluetooth Specification Vol 2, Part E, Section 7.4.1. @@ -1185,6 +1206,13 @@ where .await; } + async fn host_buffer_size(&mut self, params: HostBufferSize) { + let mut bytes = [0; 6]; + params.copy_into_slice(&mut bytes); + self.controller_write(crate::opcode::HOST_BUFFER_SIZE, &bytes) + .await; + } + async fn read_local_version_information(&mut self) { self.controller_write(crate::opcode::READ_LOCAL_VERSION_INFO, &[]) .await; @@ -1710,6 +1738,56 @@ pub enum FlowControl { Both = 0x03, } +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Parameters for the [host_buffer_size](HostHci::host_buffer_size) commad +/// +/// # Note: +/// The [Host ACL Data Packet Length](HostBufferSize::acl_data_packet_length) and +/// [Host Synchronous Data Packet Length](HostBufferSize::sync_data_packet_length) command parameters +/// do not include the length of the HCI Data Packet header. +/// +/// See Bluetooth spec. v.5.4 [Vol 4, Part E, 7.3.39]. +pub struct HostBufferSize { + /// Maximum length (in octets) of the data portion of each HCI ACL Data Packet that the host is able + /// to accept. + /// + /// this parameter will be used to determine the size of the L2CAP segments contained in the ACL Data + /// Packets, which are transferred from the Controller to the Host. + /// + /// Values: + /// - 251 .. 65535 + pub acl_data_packet_length: u16, + /// Maximum length (in octets) of the data portion of each HCI Synchronous Data Packet that the Host + /// is able to accept. `NOT USED` + /// + /// This parameter is used to determine the maximum size of HCI Synchronous Data Packets. Both the Host + /// and the Controller shall support command and event packets, zhere the data portion (excluding header) + /// contained in the packet is 255 octets is size. + pub sync_data_packet_length: u8, + /// The total number of HCI ACL Data Packets that can be stored in the data buffers of the Host. + /// + /// This parameter contains the total number of HCI ACL Data Packets that can be stored in the data buffers + /// of the Host. The Controller will determine how the buffers are to be divided between different + /// [Connection Handles](ConnectionHandle). + pub total_acl_data_packets: u16, + /// Total number of HCI Synchronous Data Packets that can be stored in the data buffers of the Host. `NOT USED` + /// + /// This parameter gives the save information for HCI Synchronous Data Packets. + pub total_sync_data_packets: u16, +} + +impl HostBufferSize { + fn copy_into_slice(&self, bytes: &mut [u8]) { + assert_eq!(bytes.len(), 6); + + LittleEndian::write_u16(&mut bytes[0..], self.acl_data_packet_length); + bytes[2] = self.sync_data_packet_length; + LittleEndian::write_u16(&mut bytes[3..], self.total_acl_data_packets); + LittleEndian::write_u16(&mut bytes[5..], self.total_sync_data_packets); + } +} + #[cfg(not(feature = "defmt"))] bitflags::bitflags! { /// Event flags defined for the [`le_set_event_mask`](HostHci::le_set_event_mask) command. diff --git a/src/opcode.rs b/src/opcode.rs index bb6a692..7fb5e1d 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -50,6 +50,7 @@ opcodes! { pub const RESET = 0x0003; pub const READ_TX_POWER_LEVEL = 0x002D; pub const SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x031; + pub const HOST_BUFFER_SIZE = 0x033; } InfoParam = 0x0004; From 414210518ca3b732cce509a9b129a95a12decbc0 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Tue, 2 Jan 2024 18:40:13 +0100 Subject: [PATCH 27/27] feat(LE command): add Number of Completed Packets command --- src/event/mod.rs | 28 +++++++++++++++++++++++++--- src/host/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ src/opcode.rs | 1 + 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/event/mod.rs b/src/event/mod.rs index 1468f77..2ca7436 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -596,15 +596,15 @@ fn to_hardware_error(payload: &[u8]) -> Result { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NumberOfCompletedPackets { /// Number of connection handles whose data is sent in this event. - num_handles: usize, + pub(crate) num_handles: usize, /// Data buffer for the event. - data_buf: [u8; NUMBER_OF_COMPLETED_PACKETS_MAX_LEN], + pub(crate) data_buf: [u8; NUMBER_OF_COMPLETED_PACKETS_MAX_LEN], } // The maximum number of bytes in the buffer is the max HCI packet size (255) less the other data in // the packet. -const NUMBER_OF_COMPLETED_PACKETS_MAX_LEN: usize = 254; +pub(crate) const NUMBER_OF_COMPLETED_PACKETS_MAX_LEN: usize = 254; impl Debug for NumberOfCompletedPackets { fn fmt(&self, f: &mut Formatter) -> FmtResult { @@ -617,6 +617,28 @@ impl Debug for NumberOfCompletedPackets { } impl NumberOfCompletedPackets { + pub fn new(items: impl Iterator) -> Self { + let mut data_buf = [0; NUMBER_OF_COMPLETED_PACKETS_MAX_LEN]; + + // let num_handles = &items.count(); + let mut index = 0; + let mut num_handles = 0; + for item in items { + LittleEndian::write_u16(&mut data_buf[index..], item.conn_handle.0); + LittleEndian::write_u16( + &mut data_buf[index + 2..], + item.num_completed_packets as u16, + ); + index += 4; + num_handles += 1; + } + + Self { + num_handles, + data_buf, + } + } + /// Returns an iterator over the connection handle-number of completed packet pairs. pub fn iter(&self) -> NumberOfCompletedPacketsIterator { NumberOfCompletedPacketsIterator { diff --git a/src/host/mod.rs b/src/host/mod.rs index 61d0ba2..bce8f87 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -8,6 +8,7 @@ //! support sending the packet ID, as `uart` does. In that case, it would make sense to also remove //! `uart` and move its contents up one level. +use crate::event::{NumberOfCompletedPackets, NUMBER_OF_COMPLETED_PACKETS_MAX_LEN}; use crate::ConnectionHandle; use byteorder::{ByteOrder, LittleEndian}; use core::convert::Into; @@ -230,6 +231,37 @@ pub trait HostHci { /// is used to turn flow control on or off. async fn host_buffer_size(&mut self, params: HostBufferSize); + /// This command is used by the Host to indicate to the Controller the number of HCI Data Packets + /// that have been completed for each [Connection Handle](ConnectionHandle) since the previous + /// [Number of Completed Packets](HostHci::number_of_completed_packets) command was sent to + /// the Controller. This means that the corresponding buffer space has been freed in the Host. + /// + /// Based on this information, and the + /// [Total Number of ACL Data Packets](HostBufferSize::total_acl_data_packets) and + /// [Total Number of Synchronous Data Packets](HostBufferSize::total_sync_data_packets) + /// parameters of the [Host Buffer Size](HostHci::host_buffer_size) command, the Controller can + /// determine for which [Connection Handles](ConnectionHandle) the following HCI Data Packets + /// should be sent to the Host. + /// + /// The command should only be issued by the Host if flow control in the direction from the Controller + /// to the host is on and there is at least one connection, or if the Controller is in local loopback + /// mode. Otherwise, the command will will be ignored by the Controller. + /// + /// When the Host has completed one or more HCI Data Packet(s) it shall send a + /// [Number of Completed Packets](HostHci::number_of_completed_packets) command to the Controller, + /// unitl it finally reports that all pending HCI Data Packets have been completed. The frequency + /// at which this command is sent. + /// + /// # Note: + /// This command is a special command in the sense that no event is normally generated after the + /// command has completed. The command may be sent at any time by the Host when there is at leat + /// one connection, or if the Controller is in local loopback mode independent of other commands. + /// The normal flow control for commands is not used for the + /// [Number of Complete Packets](HostHci::number_of_completed_packets) command. + /// + /// See Bluetooth spec. v.5.4 [Vol 4, Part E, 7.3.40]. + async fn number_of_completed_packets(&mut self, params: NumberOfCompletedPackets); + /// This command reads the values for the version information for the local Controller. /// /// Defined in Bluetooth Specification Vol 2, Part E, Section 7.4.1. @@ -1213,6 +1245,14 @@ where .await; } + async fn number_of_completed_packets(&mut self, params: NumberOfCompletedPackets) { + let mut bytes = [0; NUMBER_OF_COMPLETED_PACKETS_MAX_LEN + 1]; + bytes[0] = params.num_handles as u8; + bytes[1..].copy_from_slice(¶ms.data_buf); + self.controller_write(crate::opcode::NUMBER_OF_COMPLETED_PACKETS, &bytes) + .await; + } + async fn read_local_version_information(&mut self) { self.controller_write(crate::opcode::READ_LOCAL_VERSION_INFO, &[]) .await; diff --git a/src/opcode.rs b/src/opcode.rs index 7fb5e1d..94802c9 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -51,6 +51,7 @@ opcodes! { pub const READ_TX_POWER_LEVEL = 0x002D; pub const SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x031; pub const HOST_BUFFER_SIZE = 0x033; + pub const NUMBER_OF_COMPLETED_PACKETS = 0x035; } InfoParam = 0x0004;