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