diff --git a/examples/apps/src/ble_bas_peripheral.rs b/examples/apps/src/ble_bas_peripheral.rs index fcb88552..e6b6ab47 100644 --- a/examples/apps/src/ble_bas_peripheral.rs +++ b/examples/apps/src/ble_bas_peripheral.rs @@ -145,7 +145,7 @@ async fn advertise<'values, 'server, C: Controller>( let len = AdStructure::encode_slice( &[ AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]), AdStructure::CompleteLocalName(name.as_bytes()), ], &mut advertiser_data[..], diff --git a/examples/apps/src/ble_bas_peripheral_auth.rs b/examples/apps/src/ble_bas_peripheral_auth.rs index 6a98b95f..c87c5e65 100644 --- a/examples/apps/src/ble_bas_peripheral_auth.rs +++ b/examples/apps/src/ble_bas_peripheral_auth.rs @@ -197,7 +197,7 @@ async fn advertise<'values, 'server, C: Controller>( let len = AdStructure::encode_slice( &[ AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]), AdStructure::CompleteLocalName(name.as_bytes()), ], &mut advertiser_data[..], diff --git a/examples/apps/src/ble_bas_peripheral_bonding.rs b/examples/apps/src/ble_bas_peripheral_bonding.rs index 716b7d0b..341d08ba 100644 --- a/examples/apps/src/ble_bas_peripheral_bonding.rs +++ b/examples/apps/src/ble_bas_peripheral_bonding.rs @@ -337,7 +337,7 @@ async fn advertise<'values, 'server, C: Controller>( let len = AdStructure::encode_slice( &[ AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[ + AdStructure::IncompleteServiceUuids16(&[ service::BATTERY.to_le_bytes(), service::HUMAN_INTERFACE_DEVICE.to_le_bytes(), ]), diff --git a/examples/apps/src/ble_bas_peripheral_pass_key.rs b/examples/apps/src/ble_bas_peripheral_pass_key.rs index cfcb131a..af67fbfb 100644 --- a/examples/apps/src/ble_bas_peripheral_pass_key.rs +++ b/examples/apps/src/ble_bas_peripheral_pass_key.rs @@ -181,7 +181,7 @@ async fn advertise<'values, 'server, C: Controller>( let len = AdStructure::encode_slice( &[ AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]), AdStructure::CompleteLocalName(name.as_bytes()), ], &mut advertiser_data[..], diff --git a/examples/apps/src/ble_bas_peripheral_sec.rs b/examples/apps/src/ble_bas_peripheral_sec.rs index 876a9aee..1d5b8a5c 100644 --- a/examples/apps/src/ble_bas_peripheral_sec.rs +++ b/examples/apps/src/ble_bas_peripheral_sec.rs @@ -177,7 +177,7 @@ async fn advertise<'values, 'server, C: Controller>( let len = AdStructure::encode_slice( &[ AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]), AdStructure::CompleteLocalName(name.as_bytes()), ], &mut advertiser_data[..], diff --git a/host/src/advertise.rs b/host/src/advertise.rs index bd44797b..adf9d383 100644 --- a/host/src/advertise.rs +++ b/host/src/advertise.rs @@ -360,13 +360,32 @@ pub enum AdStructure<'a> { /// Must not be used in scan response data. Flags(u8), - /// List of 16-bit service UUIDs. + /// Incomplete List of 16-bit service UUIDs. /// The UUID data matches the ble network's endian order (should be little endian). - ServiceUuids16(&'a [[u8; 2]]), + IncompleteServiceUuids16(&'a [[u8; 2]]), - /// List of 128-bit service UUIDs. + /// Complete List of 16-bit service UUIDs. /// The UUID data matches the ble network's endian order (should be little endian). - ServiceUuids128(&'a [[u8; 16]]), + CompleteServiceUuids16(&'a [[u8; 2]]), + + /// Incomplete List of 32-bit service UUIDs. + /// The UUID data matches the ble network's endian order (should be little endian). + IncompleteServiceUuids32(&'a [[u8; 4]]), + + /// Complete List of 32-bit service UUIDs. + /// The UUID data matches the ble network's endian order (should be little endian). + CompleteServiceUuids32(&'a [[u8; 4]]), + + /// Incomplete List of 128-bit service UUIDs. + /// The UUID data matches the ble network's endian order (should be little endian). + IncompleteServiceUuids128(&'a [[u8; 16]]), + + /// Complete List of 128-bit service UUIDs. + /// The UUID data matches the ble network's endian order (should be little endian). + CompleteServiceUuids128(&'a [[u8; 16]]), + + /// Sets the Tx Power Level in dBm. + TxPowerLevel(i8), /// Service data with 16-bit service UUID. /// The UUID data matches the ble network's endian order (should be little endian). @@ -417,13 +436,37 @@ impl AdStructure<'_> { AdStructure::Flags(flags) => { w.append(&[0x02, 0x01, *flags])?; } - AdStructure::ServiceUuids16(uuids) => { + AdStructure::IncompleteServiceUuids16(uuids) => { w.append(&[(uuids.len() * 2 + 1) as u8, 0x02])?; for uuid in uuids.iter() { w.write_ref(&Uuid::Uuid16(*uuid))?; } } - AdStructure::ServiceUuids128(uuids) => { + AdStructure::CompleteServiceUuids16(uuids) => { + w.append(&[(uuids.len() * 2 + 1) as u8, 0x03])?; + for uuid in uuids.iter() { + w.write_ref(&Uuid::Uuid16(*uuid))?; + } + } + AdStructure::IncompleteServiceUuids32(uuids) => { + w.append(&[(uuids.len() * 4 + 1) as u8, 0x04])?; + for uuid in uuids.iter() { + w.write_ref(&Uuid::Uuid32(*uuid))?; + } + } + AdStructure::CompleteServiceUuids32(uuids) => { + w.append(&[(uuids.len() * 4 + 1) as u8, 0x05])?; + for uuid in uuids.iter() { + w.write_ref(&Uuid::Uuid32(*uuid))?; + } + } + AdStructure::IncompleteServiceUuids128(uuids) => { + w.append(&[(uuids.len() * 16 + 1) as u8, 0x06])?; + for uuid in uuids.iter() { + w.write_ref(&Uuid::Uuid128(*uuid))?; + } + } + AdStructure::CompleteServiceUuids128(uuids) => { w.append(&[(uuids.len() * 16 + 1) as u8, 0x07])?; for uuid in uuids.iter() { w.write_ref(&Uuid::Uuid128(*uuid))?; @@ -437,6 +480,9 @@ impl AdStructure<'_> { w.append(&[(name.len() + 1) as u8, 0x09])?; w.append(name)?; } + AdStructure::TxPowerLevel(power) => { + w.append(&[0x02, 0x0a, *power as u8])?; + } AdStructure::ServiceData16 { uuid, data } => { w.append(&[(data.len() + 3) as u8, 0x16])?; w.write(Uuid::Uuid16(*uuid))?; @@ -485,24 +531,48 @@ impl<'d> AdStructureIter<'d> { // Flags 0x01 => Ok(AdStructure::Flags(data[0])), // Incomplete List of 16-bit Service or Service Class UUIDs - // 0x02 => + 0x02 => match zerocopy::FromBytes::ref_from_bytes(data) { + Ok(x) => Ok(AdStructure::IncompleteServiceUuids16(x)), + Err(e) => { + let _ = zerocopy::SizeError::from(e); + Err(codec::Error::InvalidValue) + } + }, // Complete List of 16-bit Service or Service Class UUIDs 0x03 => match zerocopy::FromBytes::ref_from_bytes(data) { - Ok(x) => Ok(AdStructure::ServiceUuids16(x)), + Ok(x) => Ok(AdStructure::CompleteServiceUuids16(x)), Err(e) => { let _ = zerocopy::SizeError::from(e); Err(codec::Error::InvalidValue) } }, // Incomplete List of 32-bit Service or Service Class UUIDs - // 0x04 => + 0x04 => match zerocopy::FromBytes::ref_from_bytes(data) { + Ok(x) => Ok(AdStructure::IncompleteServiceUuids32(x)), + Err(e) => { + let _ = zerocopy::SizeError::from(e); + Err(codec::Error::InvalidValue) + } + }, // Complete List of 32-bit Service or Service Class UUIDs - // 0x05 + 0x05 => match zerocopy::FromBytes::ref_from_bytes(data) { + Ok(x) => Ok(AdStructure::CompleteServiceUuids32(x)), + Err(e) => { + let _ = zerocopy::SizeError::from(e); + Err(codec::Error::InvalidValue) + } + }, // Incomplete List of 128-bit Service or Service Class UUIDs - // 0x06 + 0x06 => match zerocopy::FromBytes::ref_from_bytes(data) { + Ok(x) => Ok(AdStructure::IncompleteServiceUuids128(x)), + Err(e) => { + let _ = zerocopy::SizeError::from(e); + Err(codec::Error::InvalidValue) + } + }, // Complete List of 128-bit Service or Service Class UUIDs 0x07 => match zerocopy::FromBytes::ref_from_bytes(data) { - Ok(x) => Ok(AdStructure::ServiceUuids128(x)), + Ok(x) => Ok(AdStructure::CompleteServiceUuids128(x)), Err(e) => { let _ = zerocopy::SizeError::from(e); Err(codec::Error::InvalidValue) @@ -512,8 +582,9 @@ impl<'d> AdStructureIter<'d> { 0x08 => Ok(AdStructure::ShortenedLocalName(data)), // Complete Local Name 0x09 => Ok(AdStructure::CompleteLocalName(data)), + // Tx Power Level + 0x0a => Ok(AdStructure::TxPowerLevel(data[0] as i8)), /* - 0x0A Tx Power Level 0x0D Class of Device 0x0E Simple Pairing Hash C-192 0x0F Simple Pairing Randomizer R-192 @@ -594,7 +665,7 @@ mod tests { assert!(AdStructure::encode_slice( &[ AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]), AdStructure::CompleteLocalName(b"12345678901234567890123"), ], &mut adv_data[..], diff --git a/host/src/types/uuid.rs b/host/src/types/uuid.rs index 0e969137..df79641a 100644 --- a/host/src/types/uuid.rs +++ b/host/src/types/uuid.rs @@ -1,6 +1,6 @@ //! UUID types. -use bt_hci::uuid::{BluetoothUuid128, BluetoothUuid16}; +use bt_hci::uuid::{BluetoothUuid128, BluetoothUuid16, BluetoothUuid32}; use crate::codec::{Decode, Encode, Error, Type}; @@ -10,6 +10,8 @@ use crate::codec::{Decode, Encode, Error, Type}; pub enum Uuid { /// 16-bit UUID Uuid16([u8; 2]), + /// 32-bit UUID + Uuid32([u8; 4]), /// 128-bit UUID Uuid128([u8; 16]), } @@ -20,6 +22,12 @@ impl From for Uuid { } } +impl From for Uuid { + fn from(data: bt_hci::uuid::BluetoothUuid32) -> Self { + Uuid::Uuid32(data.into()) + } +} + impl From for Uuid { fn from(data: bt_hci::uuid::BluetoothUuid128) -> Self { Uuid::Uuid128(data.into()) @@ -38,6 +46,18 @@ impl From<[u8; 16]> for Uuid { } } +impl From<[u8; 4]> for Uuid { + fn from(data: [u8; 4]) -> Self { + Uuid::Uuid32(data) + } +} + +impl From for Uuid { + fn from(data: u32) -> Self { + Uuid::Uuid32(data.to_le_bytes()) + } +} + impl From<[u8; 2]> for Uuid { fn from(data: [u8; 2]) -> Self { Uuid::Uuid16(data) @@ -65,6 +85,7 @@ impl Uuid { pub fn bytes(&self, data: &mut [u8]) { match self { Uuid::Uuid16(uuid) => data.copy_from_slice(uuid), + Uuid::Uuid32(uuid) => data.copy_from_slice(uuid), Uuid::Uuid128(uuid) => data.copy_from_slice(uuid), } } @@ -73,13 +94,15 @@ impl Uuid { pub fn get_type(&self) -> u8 { match self { Uuid::Uuid16(_) => 0x01, - Uuid::Uuid128(_) => 0x02, + Uuid::Uuid32(_) => 0x02, + Uuid::Uuid128(_) => 0x03, } } pub(crate) fn size(&self) -> usize { match self { Uuid::Uuid16(_) => 6, + Uuid::Uuid32(_) => 8, Uuid::Uuid128(_) => 20, } } @@ -96,6 +119,7 @@ impl Uuid { pub fn as_raw(&self) -> &[u8] { match self { Uuid::Uuid16(uuid) => uuid, + Uuid::Uuid32(uuid) => uuid, Uuid::Uuid128(uuid) => uuid, } } @@ -108,6 +132,7 @@ impl TryFrom<&[u8]> for Uuid { match value.len() { // Slice length has already been verified, so unwrap can be used 2 => Ok(Uuid::Uuid16(value.try_into().unwrap())), + 4 => Ok(Uuid::Uuid32(value.try_into().unwrap())), 16 => { let mut bytes = [0; 16]; bytes.copy_from_slice(value);