Skip to main content

trouble_host/
gap.rs

1//! ## Generic Access Profile
2//!
3//! This profile defines the generic procedures related to discovery of
4//! Bluetooth devices (idle mode procedures) and link management aspects
5//! of connecting to Bluetooth devices (connecting mode procedures).
6//! It also defines procedures related to use of different security levels.
7//! In addition, this profile includes common format requirements for
8//! parameters accessible on the user interface level.
9
10use embassy_sync::blocking_mutex::raw::RawMutex;
11use heapless::String;
12use static_cell::StaticCell;
13
14use crate::prelude::*;
15
16/// Advertising packet is limited to 31 bytes. 9 of these are used by other GAP data, leaving 22 bytes for the Device Name characteristic
17const DEVICE_NAME_MAX_LENGTH: usize = 22;
18
19/// The number of attributes added by the GAP and GATT services
20/// GAP_SERVICE:                      1
21/// ├── DEVICE_NAME:                  2
22/// ├── APPEARANCE:                   2
23/// └── CENTRAL_ADDRESS_RESOLUTION:   2 (security+central only)
24/// GATT_SERVICE:                   + 1
25///                                 ---
26///                                 = 6 (or 8 with security+central)
27#[cfg(not(feature = "security"))]
28pub const GAP_SERVICE_ATTRIBUTE_COUNT: usize = 6;
29/// The number of attributes added by the GAP and GATT services (with security)
30#[cfg(all(feature = "security", feature = "central"))]
31pub const GAP_SERVICE_ATTRIBUTE_COUNT: usize = 8;
32
33/// Configuration for the GAP Service.
34pub enum GapConfig<'a> {
35    /// Peripheral device configuration.
36    Peripheral(PeripheralConfig<'a>),
37    /// Central device configuration.
38    Central(CentralConfig<'a>),
39}
40
41/// Configuration for a peripheral device GAP Service.
42pub struct PeripheralConfig<'a> {
43    /// The name of the peripheral device.
44    pub name: &'a str,
45    /// The representation of the external appearance of the device.
46    ///
47    /// Example: `&appearance::sensor::GENERIC_SENSOR.`
48    pub appearance: &'a BluetoothUuid16,
49    // TODO: Add more GAP parameters
50    // pub preferred_connection_parameters: Option<ConnectionParameters>,
51}
52
53/// Configuration for a central device GAP Service.
54pub struct CentralConfig<'a> {
55    /// The name of the central device.
56    pub name: &'a str,
57    /// The representation of the external appearance of the device.
58    ///
59    /// Example: `&appearance::sensor::GENERIC_SENSOR`
60    pub appearance: &'a BluetoothUuid16,
61    // TODO: Add more GAP parameters
62}
63
64impl<'a> GapConfig<'a> {
65    /// Create a default peripheral configuration.
66    ///
67    /// This configuration will use the `UNKNOWN` appearance.
68    pub fn default(name: &'a str) -> Self {
69        GapConfig::Peripheral(PeripheralConfig {
70            name,
71            appearance: &appearance::UNKNOWN,
72        })
73    }
74
75    /// Add the GAP config to the attribute table
76    pub fn build<M: RawMutex, const MAX: usize>(
77        self,
78        table: &mut AttributeTable<'a, M, MAX>,
79    ) -> Result<(), &'static str> {
80        match self {
81            GapConfig::Peripheral(config) => config.build(table),
82            GapConfig::Central(config) => config.build(table),
83        }
84    }
85}
86
87impl<'a> PeripheralConfig<'a> {
88    /// Add the peripheral GAP config to the attribute table
89    fn build<M: RawMutex, const MAX: usize>(self, table: &mut AttributeTable<'a, M, MAX>) -> Result<(), &'static str> {
90        static PERIPHERAL_NAME: StaticCell<String<DEVICE_NAME_MAX_LENGTH>> = StaticCell::new();
91        let peripheral_name = PERIPHERAL_NAME.init(String::new());
92        peripheral_name
93            .push_str(self.name)
94            .map_err(|_| "Device name is too long. Max length is 22 bytes")?;
95
96        let mut gap_builder = table.add_service(Service::new(service::GAP));
97        gap_builder.add_characteristic_ro(characteristic::DEVICE_NAME, peripheral_name);
98        gap_builder.add_characteristic_ro(characteristic::APPEARANCE, self.appearance);
99        #[cfg(all(feature = "security", feature = "central"))]
100        gap_builder.add_characteristic_ro(characteristic::CENTRAL_ADDRESS_RESOLUTION, &1u8);
101        gap_builder.build();
102
103        table.add_service(Service::new(service::GATT));
104
105        Ok(())
106    }
107}
108
109impl<'a> CentralConfig<'a> {
110    /// Add the peripheral GAP config to the attribute table
111    fn build<M: RawMutex, const MAX: usize>(self, table: &mut AttributeTable<'a, M, MAX>) -> Result<(), &'static str> {
112        static CENTRAL_NAME: StaticCell<String<DEVICE_NAME_MAX_LENGTH>> = StaticCell::new();
113        let central_name = CENTRAL_NAME.init(String::new());
114        central_name
115            .push_str(self.name)
116            .map_err(|_| "Device name is too long. Max length is 22 bytes")?;
117
118        let mut gap_builder = table.add_service(Service::new(service::GAP));
119        gap_builder.add_characteristic_ro(characteristic::DEVICE_NAME, central_name);
120        gap_builder.add_characteristic_ro(characteristic::APPEARANCE, self.appearance);
121        #[cfg(all(feature = "security", feature = "central"))]
122        gap_builder.add_characteristic_ro(characteristic::CENTRAL_ADDRESS_RESOLUTION, &1u8);
123        gap_builder.build();
124
125        table.add_service(Service::new(service::GATT));
126
127        Ok(())
128    }
129}