trouble_host/
connection.rs

1//! BLE connection.
2
3use bt_hci::cmd::le::{LeConnUpdate, LeReadLocalSupportedFeatures, LeReadPhy, LeSetDataLength, 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, PassKey};
21use crate::types::l2cap::{ConnParamUpdateReq, ConnParamUpdateRes};
22use crate::{bt_hci_duration, BleHostError, Error, Identity, PacketPool, Stack};
23
24/// Security level of a connection
25///
26/// This describes the various security levels that are supported.
27///
28#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30pub enum SecurityLevel {
31    /// No encryption and no authentication. All connections start on this security level.
32    NoEncryption,
33    /// Encrypted but not authenticated communication. Does not provide MITM protection.
34    Encrypted,
35    /// Encrypted and authenticated security level. MITM protected.
36    EncryptedAuthenticated,
37}
38
39impl SecurityLevel {
40    /// Check if the security level is encrypted.
41    pub fn encrypted(&self) -> bool {
42        !matches!(self, SecurityLevel::NoEncryption)
43    }
44
45    /// Check if the security level is authenticated.
46    pub fn authenticated(&self) -> bool {
47        matches!(self, SecurityLevel::EncryptedAuthenticated)
48    }
49}
50
51/// Connection configuration.
52pub struct ConnectConfig<'d> {
53    /// Scan configuration to use while connecting.
54    pub scan_config: ScanConfig<'d>,
55    /// Parameters to use for the connection.
56    pub connect_params: ConnectParams,
57}
58
59/// Scan/connect configuration.
60pub struct ScanConfig<'d> {
61    /// Active scanning.
62    pub active: bool,
63    /// List of addresses to accept.
64    pub filter_accept_list: &'d [(AddrKind, &'d BdAddr)],
65    /// PHYs to scan on.
66    pub phys: PhySet,
67    /// Scan interval.
68    pub interval: Duration,
69    /// Scan window.
70    pub window: Duration,
71    /// Scan timeout.
72    pub timeout: Duration,
73}
74
75impl Default for ScanConfig<'_> {
76    fn default() -> Self {
77        Self {
78            active: true,
79            filter_accept_list: &[],
80            phys: PhySet::M1,
81            interval: Duration::from_secs(1),
82            window: Duration::from_secs(1),
83            timeout: Duration::from_secs(0),
84        }
85    }
86}
87
88/// PHYs to scan on.
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90#[derive(Eq, PartialEq, Copy, Clone)]
91#[repr(u8)]
92pub enum PhySet {
93    /// 1Mbps phy
94    M1 = 1,
95    /// 2Mbps phy
96    M2 = 2,
97    /// 1Mbps + 2Mbps phys
98    M1M2 = 3,
99    /// Coded phy (125kbps, S=8)
100    Coded = 4,
101    /// 1Mbps and Coded phys
102    M1Coded = 5,
103    /// 2Mbps and Coded phys
104    M2Coded = 6,
105    /// 1Mbps, 2Mbps and Coded phys
106    M1M2Coded = 7,
107}
108
109/// Connection parameters.
110#[derive(Debug, Clone, PartialEq)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112pub struct ConnectParams {
113    /// Minimum connection interval.
114    pub min_connection_interval: Duration,
115    /// Maximum connection interval.
116    pub max_connection_interval: Duration,
117    /// Maximum slave latency.
118    pub max_latency: u16,
119    /// Event length.
120    pub min_event_length: Duration,
121    /// Event length.
122    pub max_event_length: Duration,
123    /// Supervision timeout.
124    pub supervision_timeout: Duration,
125}
126
127/// A connection event.
128#[derive(Debug)]
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130pub enum ConnectionEvent {
131    /// Connection disconnected.
132    Disconnected {
133        /// The reason (status code) for the disconnect.
134        reason: Status,
135    },
136    /// The phy settings was updated for this connection.
137    PhyUpdated {
138        /// The TX phy.
139        tx_phy: PhyKind,
140        /// The RX phy.
141        rx_phy: PhyKind,
142    },
143    /// The phy settings was updated for this connection.
144    ConnectionParamsUpdated {
145        /// Connection interval.
146        conn_interval: Duration,
147        /// Peripheral latency.
148        peripheral_latency: u16,
149        /// Supervision timeout.
150        supervision_timeout: Duration,
151    },
152    /// The data length was changed for this connection.
153    DataLengthUpdated {
154        /// Max TX octets.
155        max_tx_octets: u16,
156        /// Max TX time.
157        max_tx_time: u16,
158        /// Max RX octets.
159        max_rx_octets: u16,
160        /// Max RX time.
161        max_rx_time: u16,
162    },
163    /// A request to change the connection parameters.
164    RequestConnectionParams {
165        /// Minimum connection interval.
166        min_connection_interval: Duration,
167        /// Maximum connection interval.
168        max_connection_interval: Duration,
169        /// Maximum slave latency.
170        max_latency: u16,
171        /// Supervision timeout.
172        supervision_timeout: Duration,
173    },
174    #[cfg(feature = "security")]
175    /// Request to display a pass key
176    PassKeyDisplay(PassKey),
177    #[cfg(feature = "security")]
178    /// Request to display and confirm a pass key
179    PassKeyConfirm(PassKey),
180    #[cfg(feature = "security")]
181    /// Request to make the user input the pass key
182    PassKeyInput,
183    #[cfg(feature = "security")]
184    /// Pairing completed
185    PairingComplete {
186        /// Security level of this pairing
187        security_level: SecurityLevel,
188        /// Bond information if the devices create a bond with this pairing.
189        bond: Option<BondInformation>,
190    },
191    #[cfg(feature = "security")]
192    /// Pairing completed
193    PairingFailed(Error),
194}
195
196impl Default for ConnectParams {
197    fn default() -> Self {
198        Self {
199            min_connection_interval: Duration::from_millis(80),
200            max_connection_interval: Duration::from_millis(80),
201            max_latency: 0,
202            min_event_length: Duration::from_secs(0),
203            max_event_length: Duration::from_secs(0),
204            supervision_timeout: Duration::from_secs(8),
205        }
206    }
207}
208
209/// Handle to a BLE connection.
210///
211/// When the last reference to a connection is dropped, the connection is automatically disconnected.
212pub struct Connection<'stack, P: PacketPool> {
213    index: u8,
214    manager: &'stack ConnectionManager<'stack, P>,
215}
216
217impl<P: PacketPool> Clone for Connection<'_, P> {
218    fn clone(&self) -> Self {
219        self.manager.inc_ref(self.index);
220        Connection::new(self.index, self.manager)
221    }
222}
223
224impl<P: PacketPool> Drop for Connection<'_, P> {
225    fn drop(&mut self) {
226        self.manager.dec_ref(self.index);
227    }
228}
229
230impl<'stack, P: PacketPool> Connection<'stack, P> {
231    pub(crate) fn new(index: u8, manager: &'stack ConnectionManager<'stack, P>) -> Self {
232        Self { index, manager }
233    }
234
235    pub(crate) fn set_att_mtu(&self, mtu: u16) {
236        self.manager.set_att_mtu(self.index, mtu);
237    }
238
239    pub(crate) fn get_att_mtu(&self) -> u16 {
240        self.manager.get_att_mtu(self.index)
241    }
242
243    pub(crate) async fn send(&self, pdu: Pdu<P::Packet>) {
244        self.manager.send(self.index, pdu).await
245    }
246
247    pub(crate) fn try_send(&self, pdu: Pdu<P::Packet>) -> Result<(), Error> {
248        self.manager.try_send(self.index, pdu)
249    }
250
251    pub(crate) async fn post_event(&self, event: ConnectionEvent) {
252        self.manager.post_event(self.index, event).await
253    }
254
255    /// Wait for next connection event.
256    pub async fn next(&self) -> ConnectionEvent {
257        self.manager.next(self.index).await
258    }
259
260    #[cfg(feature = "gatt")]
261    pub(crate) async fn next_gatt(&self) -> Pdu<P::Packet> {
262        self.manager.next_gatt(self.index).await
263    }
264
265    /// Check if still connected
266    pub fn is_connected(&self) -> bool {
267        self.manager.is_connected(self.index)
268    }
269
270    /// Connection handle of this connection.
271    pub fn handle(&self) -> ConnHandle {
272        self.manager.handle(self.index)
273    }
274
275    /// Expose the att_mtu.
276    pub fn att_mtu(&self) -> u16 {
277        self.get_att_mtu()
278    }
279
280    /// The connection role for this connection.
281    pub fn role(&self) -> LeConnRole {
282        self.manager.role(self.index)
283    }
284
285    /// The peer address for this connection.
286    pub fn peer_address(&self) -> BdAddr {
287        self.manager.peer_address(self.index)
288    }
289
290    /// The peer identity key for this connection.
291    pub fn peer_identity(&self) -> Identity {
292        self.manager.peer_identity(self.index)
293    }
294    /// Request a certain security level
295    ///
296    /// For a peripheral this may cause the peripheral to send a security request. For a central
297    /// this may cause the central to send a pairing request.
298    ///
299    /// If the link is already encrypted then this will always generate an error.
300    ///
301    pub fn request_security(&self) -> Result<(), Error> {
302        self.manager.request_security(self.index)
303    }
304
305    /// Get the encrypted state of the connection
306    pub fn security_level(&self) -> Result<SecurityLevel, Error> {
307        self.manager.get_security_level(self.index)
308    }
309
310    /// Get whether the connection is set as bondable or not.
311    ///
312    /// This is only relevant before pairing has started.
313    pub fn bondable(&self) -> Result<bool, Error> {
314        self.manager.get_bondable(self.index)
315    }
316
317    /// Set whether the connection is bondable or not.
318    ///
319    /// By default a connection is **not** bondable.
320    ///
321    /// This must be set before pairing is initiated. Once the pairing procedure has started
322    /// this field is ignored.
323    ///
324    /// If both peripheral and central are bondable then the [`ConnectionEvent::PairingComplete`]
325    /// event contains the bond information for the pairing. This bond information should be stored
326    /// in non-volatile memory and restored on reboot using [`Stack::add_bond_information()`].
327    ///
328    /// If any party in a pairing is not bondable the [`ConnectionEvent::PairingComplete`] contains
329    /// a `None` entry for the `bond` member.
330    ///
331    pub fn set_bondable(&self, bondable: bool) -> Result<(), Error> {
332        self.manager.set_bondable(self.index, bondable)
333    }
334
335    /// Confirm that the displayed pass key matches the one displayed on the other party
336    pub fn pass_key_confirm(&self) -> Result<(), Error> {
337        self.manager.pass_key_confirm(self.index, true)
338    }
339
340    /// The displayed pass key does not match the one displayed on the other party
341    pub fn pass_key_cancel(&self) -> Result<(), Error> {
342        self.manager.pass_key_confirm(self.index, false)
343    }
344
345    /// Input the pairing pass key
346    pub fn pass_key_input(&self, pass_key: u32) -> Result<(), Error> {
347        self.manager.pass_key_input(self.index, pass_key)
348    }
349
350    /// Request connection to be disconnected.
351    pub fn disconnect(&self) {
352        self.manager
353            .request_disconnect(self.index, DisconnectReason::RemoteUserTerminatedConn);
354    }
355
356    /// Read metrics for this connection
357    #[cfg(feature = "connection-metrics")]
358    pub fn metrics<F: FnOnce(&ConnectionMetrics) -> R, R>(&self, f: F) -> R {
359        self.manager.metrics(self.index, f)
360    }
361
362    /// The RSSI value for this connection.
363    pub async fn rssi<T>(&self, stack: &Stack<'_, T, P>) -> Result<i8, BleHostError<T::Error>>
364    where
365        T: ControllerCmdSync<ReadRssi>,
366    {
367        let handle = self.handle();
368        let ret = stack.host.command(ReadRssi::new(handle)).await?;
369        Ok(ret.rssi)
370    }
371
372    /// Update phy for this connection.
373    ///
374    /// This updates both TX and RX phy of the connection. For more fine grained control,
375    /// use the LeSetPhy HCI command directly.
376    pub async fn set_phy<T>(&self, stack: &Stack<'_, T, P>, phy: PhyKind) -> Result<(), BleHostError<T::Error>>
377    where
378        T: ControllerCmdAsync<LeSetPhy>,
379    {
380        let all_phys = AllPhys::new()
381            .set_has_no_rx_phy_preference(false)
382            .set_has_no_tx_phy_preference(false);
383        let mut mask = PhyMask::new()
384            .set_le_coded_preferred(false)
385            .set_le_1m_preferred(false)
386            .set_le_2m_preferred(false);
387        let mut options = PhyOptions::default();
388        match phy {
389            PhyKind::Le2M => {
390                mask = mask.set_le_2m_preferred(true);
391            }
392            PhyKind::Le1M => {
393                mask = mask.set_le_1m_preferred(true);
394            }
395            PhyKind::LeCoded => {
396                mask = mask.set_le_coded_preferred(true);
397                options = PhyOptions::S8CodingPreferred;
398            }
399            PhyKind::LeCodedS2 => {
400                mask = mask.set_le_coded_preferred(true);
401                options = PhyOptions::S2CodingPreferred;
402            }
403        }
404        stack
405            .host
406            .async_command(LeSetPhy::new(self.handle(), all_phys, mask, mask, options))
407            .await?;
408        Ok(())
409    }
410
411    /// Read the current phy used for the connection.
412    pub async fn read_phy<T>(&self, stack: &Stack<'_, T, P>) -> Result<(PhyKind, PhyKind), BleHostError<T::Error>>
413    where
414        T: ControllerCmdSync<LeReadPhy>,
415    {
416        let res = stack.host.command(LeReadPhy::new(self.handle())).await?;
417        Ok((res.tx_phy, res.rx_phy))
418    }
419
420    /// Update data length for this connection.
421    pub async fn update_data_length<T>(
422        &self,
423        stack: &Stack<'_, T, P>,
424        length: u16,
425        time_us: u16,
426    ) -> Result<(), BleHostError<T::Error>>
427    where
428        T: ControllerCmdSync<LeSetDataLength> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
429    {
430        let handle = self.handle();
431        // First, check the local supported features to ensure that the connection update is supported.
432        let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
433        if length <= 27 || features.supports_le_data_packet_length_extension() {
434            match stack.host.command(LeSetDataLength::new(handle, length, time_us)).await {
435                Ok(_) => Ok(()),
436                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
437                    Err(crate::Error::Disconnected.into())
438                }
439                Err(e) => Err(e),
440            }
441        } else {
442            Err(BleHostError::BleHost(Error::InvalidValue))
443        }
444    }
445
446    /// Update connection parameters for this connection.
447    pub async fn update_connection_params<T>(
448        &self,
449        stack: &Stack<'_, T, P>,
450        params: &ConnectParams,
451    ) -> Result<(), BleHostError<T::Error>>
452    where
453        T: ControllerCmdAsync<LeConnUpdate> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
454    {
455        let handle = self.handle();
456        // First, check the local supported features to ensure that the connection update is supported.
457        let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
458        if features.supports_conn_parameters_request_procedure() || self.role() == LeConnRole::Central {
459            match stack.host.async_command(into_le_conn_update(handle, params)).await {
460                Ok(_) => return Ok(()),
461                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
462                    return Err(crate::Error::Disconnected.into());
463                }
464                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNSUPPORTED_REMOTE_FEATURE))) => {
465                    // We tried to send the request as a periperhal but the remote central does not support procedure.
466                    // Use the L2CAP signaling method below instead.
467                    // This code path should never be reached when acting as a central. If a bugged controller implementation
468                    // returns this error code we transmit an invalid L2CAP signal which then is rejected by the remote.
469                }
470                Err(e) => return Err(e),
471            }
472        }
473
474        // Use L2CAP signaling to update connection parameters
475        info!(
476            "Connection parameters request procedure not supported, use l2cap connection parameter update req instead"
477        );
478        let interval_min: bt_hci::param::Duration<1_250> = bt_hci_duration(params.min_connection_interval);
479        let interva_max: bt_hci::param::Duration<1_250> = bt_hci_duration(params.max_connection_interval);
480        let timeout: bt_hci::param::Duration<10_000> = bt_hci_duration(params.supervision_timeout);
481        let param = ConnParamUpdateReq {
482            interval_min: interval_min.as_u16(),
483            interval_max: interva_max.as_u16(),
484            latency: params.max_latency,
485            timeout: timeout.as_u16(),
486        };
487        stack.host.send_conn_param_update_req(handle, &param).await
488    }
489
490    /// Respond to updated parameters.
491    pub async fn accept_connection_params<T>(
492        &self,
493        stack: &Stack<'_, T, P>,
494        params: &ConnectParams,
495    ) -> Result<(), BleHostError<T::Error>>
496    where
497        T: ControllerCmdAsync<LeConnUpdate>,
498    {
499        let handle = self.handle();
500        if self.role() == LeConnRole::Central {
501            match stack.host.async_command(into_le_conn_update(handle, params)).await {
502                Ok(_) => {
503                    // Use L2CAP signaling to update connection parameters
504                    info!(
505                        "Connection parameters request procedure not supported, use l2cap connection parameter update res instead"
506                    );
507                    let param = ConnParamUpdateRes { result: 0 };
508                    stack.host.send_conn_param_update_res(handle, &param).await?;
509                    Ok(())
510                }
511                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
512                    Err(crate::Error::Disconnected.into())
513                }
514                Err(e) => {
515                    info!("Connection parameters request procedure failed");
516                    let param = ConnParamUpdateRes { result: 1 };
517                    stack.host.send_conn_param_update_res(handle, &param).await?;
518                    Err(e)
519                }
520            }
521        } else {
522            Err(crate::Error::NotSupported.into())
523        }
524    }
525
526    /// Transform BLE connection into a `GattConnection`
527    #[cfg(feature = "gatt")]
528    pub fn with_attribute_server<
529        'values,
530        'server,
531        M: RawMutex,
532        const ATT_MAX: usize,
533        const CCCD_MAX: usize,
534        const CONN_MAX: usize,
535    >(
536        self,
537        server: &'server AttributeServer<'values, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>,
538    ) -> Result<GattConnection<'stack, 'server, P>, Error> {
539        GattConnection::try_new(self, server)
540    }
541}
542
543fn into_le_conn_update(handle: ConnHandle, params: &ConnectParams) -> LeConnUpdate {
544    LeConnUpdate::new(
545        handle,
546        bt_hci_duration(params.min_connection_interval),
547        bt_hci_duration(params.max_connection_interval),
548        params.max_latency,
549        bt_hci_duration(params.supervision_timeout),
550        bt_hci_duration(params.min_event_length),
551        bt_hci_duration(params.max_event_length),
552    )
553}