Skip to main content

esp_idf_svc/ble/
gatt.rs

1//! NimBLE GATT: shared types and helpers.
2
3use core::ffi::c_int;
4
5#[cfg(esp_idf_bt_nimble_gatt_server)]
6use enumset::{EnumSet, EnumSetType};
7
8use crate::sys::*;
9
10use super::{BleError, ConnHandle};
11
12#[cfg(esp_idf_bt_nimble_gatt_client)]
13pub mod client;
14#[cfg(esp_idf_bt_nimble_gatt_server)]
15pub mod server;
16
17/// A GATT attribute handle (e.g. a characteristic's value handle).
18pub type AttrHandle = u16;
19
20/// Set the preferred ATT MTU. Safe to call before or after the host starts.
21pub fn set_preferred_mtu(mtu: u16) -> Result<(), BleError> {
22    BleError::from_raw(unsafe { ble_att_set_preferred_mtu(mtu) })
23}
24
25/// The negotiated ATT MTU for a connection
26pub fn att_mtu(conn_handle: ConnHandle) -> Result<u16, BleError> {
27    match unsafe { ble_att_mtu(conn_handle) } {
28        0 => Err(BleError::new(BLE_HS_ENOTCONN as c_int)),
29        mtu => Ok(mtu),
30    }
31}
32
33/// A GATT characteristic operation / permission flag (`BLE_GATT_CHR_F_*`).
34///
35/// The operation flags become the property bits of the characteristic declaration (and gate what
36/// the ATT server lets a peer do); the `*Enc` / `*Authen` / `*Author` flags additionally demand a
37/// security level before the access is dispatched to the
38/// [`gatts_subscribe`](crate::ble::BleDriver::gatts_subscribe) hook.
39#[cfg(esp_idf_bt_nimble_gatt_server)]
40#[derive(Debug, EnumSetType)]
41pub enum BleGattCharFlag {
42    /// The value may be broadcast (sets the Broadcast property bit).
43    Broadcast,
44    /// The peer may read the value.
45    Read,
46    /// The peer may write the value **without** a response (ATT Write Command). Independent of
47    /// [`Write`](Self::Write) — a peer cannot use write-without-response unless this flag is set —
48    /// but both kinds of write arrive as the same
49    /// [`GattsEvent::Write`](server::GattsEvent::Write), as NimBLE does not report which ATT
50    /// opcode carried them.
51    WriteNoRsp,
52    /// The peer may write the value **with** a response (ATT Write Request).
53    Write,
54    /// The value may be notified (unacknowledged). NimBLE adds the CCCD for you.
55    Notify,
56    /// The value may be indicated (acknowledged). NimBLE adds the CCCD for you.
57    Indicate,
58    /// The peer may write the value with a signature (ATT Signed Write Command).
59    AuthSignWrite,
60    /// Sets the "Reliable Write" extended property (prepare/execute writes). The queued writes are
61    /// coalesced by NimBLE and delivered as a single [`Write`](server::GattsEvent::Write).
62    ReliableWrite,
63    /// Sets the "Writable Auxiliaries" extended property (the Characteristic User Description
64    /// descriptor is writable).
65    AuxWrite,
66    /// Reads require an encrypted link.
67    ReadEnc,
68    /// Reads require an encrypted link from an authenticated (MITM-protected) pairing.
69    ReadAuthen,
70    /// Reads require authorization.
71    ///
72    /// **Not usable yet:** NimBLE asks the application to authorize the access through the
73    /// `BLE_GAP_EVENT_AUTHORIZE` GAP event, which this crate does not surface (it arrives as
74    /// [`GapEvent::Other`](crate::ble::gap::GapEvent::Other), whose response cannot be set), and
75    /// NimBLE rejects an unanswered request — so setting this flag currently fails every read with
76    /// "insufficient authorization".
77    ReadAuthor,
78    /// Writes require an encrypted link.
79    WriteEnc,
80    /// Writes require an encrypted link from an authenticated (MITM-protected) pairing.
81    WriteAuthen,
82    /// Writes require authorization. Carries the same caveat as [`ReadAuthor`](Self::ReadAuthor).
83    WriteAuthor,
84    /// Subscribing (writing the CCCD) requires an encrypted link.
85    #[cfg(esp_idf_version_at_least_5_5_0)]
86    NotifyIndicateEnc,
87    /// Subscribing (writing the CCCD) requires an encrypted link from an authenticated
88    /// (MITM-protected) pairing.
89    #[cfg(esp_idf_version_at_least_5_5_0)]
90    NotifyIndicateAuthen,
91    /// Subscribing (writing the CCCD) requires authorization. Carries the same caveat as
92    /// [`ReadAuthor`](Self::ReadAuthor).
93    #[cfg(esp_idf_version_at_least_5_5_0)]
94    NotifyIndicateAuthor,
95}
96
97#[cfg(esp_idf_bt_nimble_gatt_server)]
98impl BleGattCharFlag {
99    /// The raw NimBLE flag bit (`BLE_GATT_CHR_F_*`). `const`, so it can be used to build a static
100    /// service table (see the [`gatt_services!`](crate::gatt_services) macro).
101    pub const fn repr(self) -> ble_gatt_chr_flags {
102        match self {
103            Self::Broadcast => BLE_GATT_CHR_F_BROADCAST,
104            Self::Read => BLE_GATT_CHR_F_READ,
105            Self::WriteNoRsp => BLE_GATT_CHR_F_WRITE_NO_RSP,
106            Self::Write => BLE_GATT_CHR_F_WRITE,
107            Self::Notify => BLE_GATT_CHR_F_NOTIFY,
108            Self::Indicate => BLE_GATT_CHR_F_INDICATE,
109            Self::AuthSignWrite => BLE_GATT_CHR_F_AUTH_SIGN_WRITE,
110            Self::ReliableWrite => BLE_GATT_CHR_F_RELIABLE_WRITE,
111            Self::AuxWrite => BLE_GATT_CHR_F_AUX_WRITE,
112            Self::ReadEnc => BLE_GATT_CHR_F_READ_ENC,
113            Self::ReadAuthen => BLE_GATT_CHR_F_READ_AUTHEN,
114            Self::ReadAuthor => BLE_GATT_CHR_F_READ_AUTHOR,
115            Self::WriteEnc => BLE_GATT_CHR_F_WRITE_ENC,
116            Self::WriteAuthen => BLE_GATT_CHR_F_WRITE_AUTHEN,
117            Self::WriteAuthor => BLE_GATT_CHR_F_WRITE_AUTHOR,
118            // The CCCD-write permission flags only exist from ESP-IDF 5.5 on.
119            #[cfg(esp_idf_version_at_least_5_5_0)]
120            Self::NotifyIndicateEnc => BLE_GATT_CHR_F_NOTIFY_INDICATE_ENC,
121            #[cfg(esp_idf_version_at_least_5_5_0)]
122            Self::NotifyIndicateAuthen => BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHEN,
123            #[cfg(esp_idf_version_at_least_5_5_0)]
124            Self::NotifyIndicateAuthor => BLE_GATT_CHR_F_NOTIFY_INDICATE_AUTHOR,
125        }
126    }
127}
128
129#[cfg(esp_idf_bt_nimble_gatt_server)]
130impl From<BleGattCharFlag> for ble_gatt_chr_flags {
131    fn from(flag: BleGattCharFlag) -> Self {
132        flag.repr()
133    }
134}
135
136#[cfg(esp_idf_bt_nimble_gatt_server)]
137pub(crate) fn flags_to_repr(flags: EnumSet<BleGattCharFlag>) -> ble_gatt_chr_flags {
138    flags
139        .iter()
140        .fold(0, |acc, flag| acc | ble_gatt_chr_flags::from(flag))
141}