trouble_host/
connection.rs

1//! BLE connection.
2
3use bt_hci::cmd::le::{LeConnUpdate, LeReadPhy, LeSetPhy};
4use bt_hci::cmd::status::ReadRssi;
5use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync};
6use bt_hci::param::{
7    AddrKind, AllPhys, BdAddr, ConnHandle, DisconnectReason, LeConnRole, PhyKind, PhyMask, PhyOptions, Status,
8};
9#[cfg(feature = "gatt")]
10use embassy_sync::blocking_mutex::raw::RawMutex;
11use embassy_time::Duration;
12
13use crate::connection_manager::ConnectionManager;
14#[cfg(feature = "connection-metrics")]
15pub use crate::connection_manager::Metrics as ConnectionMetrics;
16use crate::pdu::Pdu;
17#[cfg(feature = "gatt")]
18use crate::prelude::{AttributeServer, GattConnection};
19#[cfg(feature = "security")]
20use crate::security_manager::BondInformation;
21use crate::{BleHostError, Error, Identity, PacketPool, Stack};
22
23/// Connection configuration.
24pub struct ConnectConfig<'d> {
25    /// Scan configuration to use while connecting.
26    pub scan_config: ScanConfig<'d>,
27    /// Parameters to use for the connection.
28    pub connect_params: ConnectParams,
29}
30
31/// Scan/connect configuration.
32pub struct ScanConfig<'d> {
33    /// Active scanning.
34    pub active: bool,
35    /// List of addresses to accept.
36    pub filter_accept_list: &'d [(AddrKind, &'d BdAddr)],
37    /// PHYs to scan on.
38    pub phys: PhySet,
39    /// Scan interval.
40    pub interval: Duration,
41    /// Scan window.
42    pub window: Duration,
43    /// Scan timeout.
44    pub timeout: Duration,
45}
46
47impl Default for ScanConfig<'_> {
48    fn default() -> Self {
49        Self {
50            active: true,
51            filter_accept_list: &[],
52            phys: PhySet::M1,
53            interval: Duration::from_secs(1),
54            window: Duration::from_secs(1),
55            timeout: Duration::from_secs(0),
56        }
57    }
58}
59
60/// PHYs to scan on.
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62#[derive(Eq, PartialEq, Copy, Clone)]
63#[repr(u8)]
64pub enum PhySet {
65    /// 1Mbps phy
66    M1 = 1,
67    /// 2Mbps phy
68    M2 = 2,
69    /// 1Mbps + 2Mbps phys
70    M1M2 = 3,
71    /// Coded phy (125kbps, S=8)
72    Coded = 4,
73    /// 1Mbps and Coded phys
74    M1Coded = 5,
75    /// 2Mbps and Coded phys
76    M2Coded = 6,
77    /// 1Mbps, 2Mbps and Coded phys
78    M1M2Coded = 7,
79}
80
81/// Connection parameters.
82pub struct ConnectParams {
83    /// Minimum connection interval.
84    pub min_connection_interval: Duration,
85    /// Maximum connection interval.
86    pub max_connection_interval: Duration,
87    /// Maximum slave latency.
88    pub max_latency: u16,
89    /// Event length.
90    pub event_length: Duration,
91    /// Supervision timeout.
92    pub supervision_timeout: Duration,
93}
94
95/// A connection event.
96#[derive(Debug)]
97pub enum ConnectionEvent {
98    /// Connection disconnected.
99    Disconnected {
100        /// The reason (status code) for the disconnect.
101        reason: Status,
102    },
103    /// The phy settings was updated for this connection.
104    PhyUpdated {
105        /// The TX phy.
106        tx_phy: PhyKind,
107        /// The RX phy.
108        rx_phy: PhyKind,
109    },
110    /// The phy settings was updated for this connection.
111    ConnectionParamsUpdated {
112        /// Connection interval.
113        conn_interval: Duration,
114        /// Peripheral latency.
115        peripheral_latency: u16,
116        /// Supervision timeout.
117        supervision_timeout: Duration,
118    },
119    #[cfg(feature = "security")]
120    /// Bonded event.
121    Bonded {
122        /// Bond info for this connection
123        bond_info: BondInformation,
124    },
125}
126
127impl Default for ConnectParams {
128    fn default() -> Self {
129        Self {
130            min_connection_interval: Duration::from_millis(80),
131            max_connection_interval: Duration::from_millis(80),
132            max_latency: 0,
133            event_length: Duration::from_secs(0),
134            supervision_timeout: Duration::from_secs(8),
135        }
136    }
137}
138
139/// Handle to a BLE connection.
140///
141/// When the last reference to a connection is dropped, the connection is automatically disconnected.
142pub struct Connection<'stack, P: PacketPool> {
143    index: u8,
144    manager: &'stack ConnectionManager<'stack, P>,
145}
146
147impl<P: PacketPool> Clone for Connection<'_, P> {
148    fn clone(&self) -> Self {
149        self.manager.inc_ref(self.index);
150        Connection::new(self.index, self.manager)
151    }
152}
153
154impl<P: PacketPool> Drop for Connection<'_, P> {
155    fn drop(&mut self) {
156        self.manager.dec_ref(self.index);
157    }
158}
159
160impl<'stack, P: PacketPool> Connection<'stack, P> {
161    pub(crate) fn new(index: u8, manager: &'stack ConnectionManager<'stack, P>) -> Self {
162        Self { index, manager }
163    }
164
165    pub(crate) fn set_att_mtu(&self, mtu: u16) {
166        self.manager.set_att_mtu(self.index, mtu);
167    }
168
169    pub(crate) fn get_att_mtu(&self) -> u16 {
170        self.manager.get_att_mtu(self.index)
171    }
172
173    pub(crate) async fn send(&self, pdu: Pdu<P::Packet>) {
174        self.manager.send(self.index, pdu).await
175    }
176
177    pub(crate) fn try_send(&self, pdu: Pdu<P::Packet>) -> Result<(), Error> {
178        self.manager.try_send(self.index, pdu)
179    }
180
181    pub(crate) async fn post_event(&self, event: ConnectionEvent) {
182        self.manager.post_event(self.index, event).await
183    }
184
185    /// Wait for next connection event.
186    pub async fn next(&self) -> ConnectionEvent {
187        self.manager.next(self.index).await
188    }
189
190    #[cfg(feature = "gatt")]
191    pub(crate) async fn next_gatt(&self) -> Pdu<P::Packet> {
192        self.manager.next_gatt(self.index).await
193    }
194
195    /// Check if still connected
196    pub fn is_connected(&self) -> bool {
197        self.manager.is_connected(self.index)
198    }
199
200    /// Connection handle of this connection.
201    pub fn handle(&self) -> ConnHandle {
202        self.manager.handle(self.index)
203    }
204
205    /// Expose the att_mtu.
206    pub fn att_mtu(&self) -> u16 {
207        self.get_att_mtu()
208    }
209
210    /// The connection role for this connection.
211    pub fn role(&self) -> LeConnRole {
212        self.manager.role(self.index)
213    }
214
215    /// The peer address for this connection.
216    pub fn peer_address(&self) -> BdAddr {
217        self.manager.peer_address(self.index)
218    }
219
220    /// The peer identity key for this connection.
221    pub fn peer_identity(&self) -> Identity {
222        self.manager.peer_identity(self.index)
223    }
224
225    /// Get the encrypted state of the connection
226    pub fn encrypted(&self) -> bool {
227        self.manager.get_encrypted(self.index)
228    }
229
230    /// Request connection to be disconnected.
231    pub fn disconnect(&self) {
232        self.manager
233            .request_disconnect(self.index, DisconnectReason::RemoteUserTerminatedConn);
234    }
235
236    /// Read metrics for this connection
237    #[cfg(feature = "connection-metrics")]
238    pub fn metrics<F: FnOnce(&ConnectionMetrics) -> R, R>(&self, f: F) -> R {
239        self.manager.metrics(self.index, f)
240    }
241
242    /// The RSSI value for this connection.
243    pub async fn rssi<T>(&self, stack: &Stack<'_, T, P>) -> Result<i8, BleHostError<T::Error>>
244    where
245        T: ControllerCmdSync<ReadRssi>,
246    {
247        let handle = self.handle();
248        let ret = stack.host.command(ReadRssi::new(handle)).await?;
249        Ok(ret.rssi)
250    }
251
252    /// Update phy for this connection.
253    ///
254    /// This updates both TX and RX phy of the connection. For more fine grained control,
255    /// use the LeSetPhy HCI command directly.
256    pub async fn set_phy<T>(&self, stack: &Stack<'_, T, P>, phy: PhyKind) -> Result<(), BleHostError<T::Error>>
257    where
258        T: ControllerCmdAsync<LeSetPhy>,
259    {
260        let all_phys = AllPhys::new()
261            .set_has_no_rx_phy_preference(false)
262            .set_has_no_tx_phy_preference(false);
263        let mut mask = PhyMask::new()
264            .set_le_coded_preferred(false)
265            .set_le_1m_preferred(false)
266            .set_le_2m_preferred(false);
267        let mut options = PhyOptions::default();
268        match phy {
269            PhyKind::Le2M => {
270                mask = mask.set_le_2m_preferred(true);
271            }
272            PhyKind::Le1M => {
273                mask = mask.set_le_1m_preferred(true);
274            }
275            PhyKind::LeCoded => {
276                mask = mask.set_le_coded_preferred(true);
277                options = PhyOptions::S8CodingPreferred;
278            }
279            PhyKind::LeCodedS2 => {
280                mask = mask.set_le_coded_preferred(true);
281                options = PhyOptions::S2CodingPreferred;
282            }
283        }
284        stack
285            .host
286            .async_command(LeSetPhy::new(self.handle(), all_phys, mask, mask, options))
287            .await?;
288        Ok(())
289    }
290
291    /// Read the current phy used for the connection.
292    pub async fn read_phy<T>(&self, stack: &Stack<'_, T, P>) -> Result<(PhyKind, PhyKind), BleHostError<T::Error>>
293    where
294        T: ControllerCmdSync<LeReadPhy>,
295    {
296        let res = stack.host.command(LeReadPhy::new(self.handle())).await?;
297        Ok((res.tx_phy, res.rx_phy))
298    }
299
300    /// Update connection parameters for this connection.
301    pub async fn update_connection_params<T>(
302        &self,
303        stack: &Stack<'_, T, P>,
304        params: &ConnectParams,
305    ) -> Result<(), BleHostError<T::Error>>
306    where
307        T: ControllerCmdAsync<LeConnUpdate>,
308    {
309        let handle = self.handle();
310        match stack
311            .host
312            .async_command(LeConnUpdate::new(
313                handle,
314                params.min_connection_interval.into(),
315                params.max_connection_interval.into(),
316                params.max_latency,
317                params.supervision_timeout.into(),
318                params.event_length.into(),
319                params.event_length.into(),
320            ))
321            .await
322        {
323            Ok(_) => Ok(()),
324            Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
325                Err(crate::Error::Disconnected.into())
326            }
327            Err(e) => Err(e),
328        }
329    }
330
331    /// Transform BLE connection into a `GattConnection`
332    #[cfg(feature = "gatt")]
333    pub fn with_attribute_server<
334        'values,
335        'server,
336        M: RawMutex,
337        const ATT_MAX: usize,
338        const CCCD_MAX: usize,
339        const CONN_MAX: usize,
340    >(
341        self,
342        server: &'server AttributeServer<'values, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>,
343    ) -> Result<GattConnection<'stack, 'server, P>, Error> {
344        GattConnection::try_new(self, server)
345    }
346}