Skip to main content

trouble_host/
connection.rs

1//! BLE connection.
2
3use bt_hci::cmd::le::{
4    LeConnUpdate, LeConnectionRateRequest, LeFrameSpaceUpdate, LeReadLocalSupportedFeatures, LeReadPhy,
5    LeSetDataLength, LeSetPhy,
6};
7use bt_hci::cmd::status::ReadRssi;
8use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync};
9use bt_hci::param::{
10    AllPhys, ConnHandle, DisconnectReason, FilterDuplicates, FrameSpaceInitiator, LeConnRole, PhyKind, PhyMask,
11    PhyOptions, SpacingTypes, Status,
12};
13#[cfg(feature = "connection-params-update")]
14use bt_hci::{
15    cmd::le::{LeRemoteConnectionParameterRequestNegativeReply, LeRemoteConnectionParameterRequestReply},
16    param::RemoteConnectionParamsRejectReason,
17};
18#[cfg(feature = "gatt")]
19use embassy_sync::blocking_mutex::raw::RawMutex;
20use embassy_time::Duration;
21
22use crate::connection_manager::ConnectionManager;
23#[cfg(feature = "connection-metrics")]
24pub use crate::connection_manager::Metrics as ConnectionMetrics;
25use crate::pdu::Pdu;
26#[cfg(feature = "gatt")]
27use crate::prelude::{AttributeServer, GattConnection};
28#[cfg(feature = "security")]
29use crate::security_manager::{BondInformation, PassKey};
30use crate::types::l2cap::ConnParamUpdateRes;
31use crate::{bt_hci_duration, Address, BleHostError, Error, Identity, PacketPool, Stack};
32
33/// Security level of a connection
34///
35/// This describes the various security levels that are supported.
36///
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
39#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40pub enum SecurityLevel {
41    /// No encryption and no authentication. All connections start on this security level.
42    NoEncryption,
43    /// Encrypted but not authenticated communication. Does not provide MITM protection.
44    Encrypted,
45    /// Encrypted and authenticated security level. MITM protected.
46    EncryptedAuthenticated,
47}
48
49impl SecurityLevel {
50    /// Check if the security level is encrypted.
51    pub fn encrypted(&self) -> bool {
52        !matches!(self, SecurityLevel::NoEncryption)
53    }
54
55    /// Check if the security level is authenticated.
56    pub fn authenticated(&self) -> bool {
57        matches!(self, SecurityLevel::EncryptedAuthenticated)
58    }
59}
60
61/// Connection configuration.
62pub struct ConnectConfig<'d> {
63    /// Scan configuration to use while connecting.
64    pub scan_config: ScanConfig<'d>,
65    /// Parameters to use for the connection.
66    pub connect_params: RequestedConnParams,
67}
68
69/// Scan/connect configuration.
70pub struct ScanConfig<'d> {
71    /// Active scanning.
72    pub active: bool,
73    /// List of addresses to accept.
74    pub filter_accept_list: &'d [Address],
75    /// PHYs to scan on.
76    pub phys: PhySet,
77    /// Scan interval.
78    pub interval: Duration,
79    /// Scan window.
80    pub window: Duration,
81    /// Scan timeout.
82    pub timeout: Duration,
83    /// Duplicate advertising filtering.
84    pub filter_duplicates: FilterDuplicates,
85}
86
87impl Default for ScanConfig<'_> {
88    fn default() -> Self {
89        Self {
90            active: true,
91            filter_accept_list: &[],
92            phys: PhySet::M1,
93            interval: Duration::from_secs(1),
94            window: Duration::from_secs(1),
95            timeout: Duration::from_secs(0),
96            filter_duplicates: FilterDuplicates::Disabled,
97        }
98    }
99}
100
101/// PHYs to scan on.
102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
103#[derive(Eq, PartialEq, Copy, Clone)]
104#[repr(u8)]
105pub enum PhySet {
106    /// 1Mbps phy
107    M1 = 1,
108    /// 2Mbps phy
109    M2 = 2,
110    /// 1Mbps + 2Mbps phys
111    M1M2 = 3,
112    /// Coded phy (125kbps, S=8)
113    Coded = 4,
114    /// 1Mbps and Coded phys
115    M1Coded = 5,
116    /// 2Mbps and Coded phys
117    M2Coded = 6,
118    /// 1Mbps, 2Mbps and Coded phys
119    M1M2Coded = 7,
120}
121
122/// Requested parameters for a connection.
123#[derive(Debug, Clone, PartialEq)]
124#[cfg_attr(feature = "defmt", derive(defmt::Format))]
125pub struct RequestedConnParams {
126    /// Minimum connection interval.
127    pub min_connection_interval: Duration,
128    /// Maximum connection interval.
129    pub max_connection_interval: Duration,
130    /// Maximum slave latency.
131    pub max_latency: u16,
132    /// Event length.
133    pub min_event_length: Duration,
134    /// Event length.
135    pub max_event_length: Duration,
136    /// Supervision timeout.
137    pub supervision_timeout: Duration,
138}
139
140impl RequestedConnParams {
141    /// Check if the connection parameters are valid
142    pub fn is_valid(&self) -> bool {
143        self.min_connection_interval <= self.max_connection_interval
144            && self.min_connection_interval >= Duration::from_micros(7_500)
145            && self.max_connection_interval <= Duration::from_secs(4)
146            && self.max_latency < 500
147            && self.min_event_length <= self.max_event_length
148            && self.supervision_timeout >= Duration::from_millis(100)
149            && self.supervision_timeout <= Duration::from_millis(32_000)
150            && self.supervision_timeout.as_micros()
151                > 2 * u64::from(self.max_latency + 1) * self.max_connection_interval.as_micros()
152    }
153}
154
155/// Current parameters for a connection.
156#[derive(Default, Debug, Clone, Copy)]
157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
158pub struct ConnParams {
159    /// Connection interval.
160    pub conn_interval: Duration,
161    /// Peripheral latency.
162    pub peripheral_latency: u16,
163    /// Supervision timeout.
164    pub supervision_timeout: Duration,
165}
166
167/// Connection rate parameters.
168#[derive(Debug, Clone, PartialEq)]
169#[cfg_attr(feature = "defmt", derive(defmt::Format))]
170pub struct ConnectRateParams {
171    /// Minimum connection interval.
172    pub min_connection_interval: Duration,
173    /// Maximum connection interval.
174    pub max_connection_interval: Duration,
175    /// Minimum subrate factor.
176    pub subrate_min: u16,
177    /// Maximum subrate factor.
178    pub subrate_max: u16,
179    /// Maximum slave latency.
180    pub max_latency: u16,
181    /// Number of continuation events allowed for subrate processing.
182    pub continuation_number: u16,
183    /// Supervision timeout.
184    pub supervision_timeout: Duration,
185    /// Minimum connection event length.
186    pub min_ce_length: Duration,
187    /// Maximum connection event length.
188    pub max_ce_length: Duration,
189}
190
191/// A connection event.
192#[derive(Debug)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194pub enum ConnectionEvent {
195    /// Connection disconnected.
196    Disconnected {
197        /// The reason (status code) for the disconnect.
198        reason: Status,
199    },
200    /// The phy settings was updated for this connection.
201    PhyUpdated {
202        /// The TX phy.
203        tx_phy: PhyKind,
204        /// The RX phy.
205        rx_phy: PhyKind,
206    },
207    /// The phy settings was updated for this connection.
208    ConnectionParamsUpdated {
209        /// Connection interval.
210        conn_interval: Duration,
211        /// Peripheral latency.
212        peripheral_latency: u16,
213        /// Supervision timeout.
214        supervision_timeout: Duration,
215    },
216    /// The data length was changed for this connection.
217    DataLengthUpdated {
218        /// Max TX octets.
219        max_tx_octets: u16,
220        /// Max TX time.
221        max_tx_time: u16,
222        /// Max RX octets.
223        max_rx_octets: u16,
224        /// Max RX time.
225        max_rx_time: u16,
226    },
227    /// The frame space was updated for this connection.
228    FrameSpaceUpdated {
229        /// The negotiated frame space value.
230        frame_space: Duration,
231        /// Who initiated the frame space update.
232        initiator: FrameSpaceInitiator,
233        /// PHYs affected.
234        phys: PhyMask,
235        /// Spacing types affected.
236        spacing_types: SpacingTypes,
237    },
238    /// Connection rate has been changed.
239    ConnectionRateChanged {
240        /// Connection interval.
241        conn_interval: Duration,
242        /// Subrate factor.
243        subrate_factor: u16,
244        /// Peripheral latency.
245        peripheral_latency: u16,
246        /// Continuation number.
247        continuation_number: u16,
248        /// Supervision timeout.
249        supervision_timeout: Duration,
250    },
251    /// A request to change the connection parameters.
252    ///
253    /// [`ConnectionParamsRequest::accept()`] or [`ConnectionParamsRequest::reject()`]
254    /// must be called to respond to the request.
255    RequestConnectionParams(ConnectionParamsRequest),
256    #[cfg(feature = "security")]
257    /// Request to display a pass key
258    PassKeyDisplay(PassKey),
259    #[cfg(feature = "security")]
260    /// Request to display and confirm a pass key
261    PassKeyConfirm(PassKey),
262    #[cfg(feature = "security")]
263    /// Request to make the user input the pass key
264    PassKeyInput,
265    #[cfg(feature = "security")]
266    /// Pairing completed
267    PairingComplete {
268        /// Security level of this pairing
269        security_level: SecurityLevel,
270        /// Bond information if the devices create a bond with this pairing.
271        bond: Option<BondInformation>,
272    },
273    #[cfg(feature = "security")]
274    /// Pairing completed
275    PairingFailed(Error),
276    #[cfg(feature = "security")]
277    /// The peer has lost its bond (received pairing request for a bonded peer).
278    BondLost,
279    #[cfg(feature = "security")]
280    /// The link is now encrypted. Fires once per encryption-enable transition,
281    /// for both fresh pairings and resumed bonded sessions. For pairings, this
282    /// fires alongside `PairingComplete`.
283    Encrypted {
284        /// Security level achieved by the encryption.
285        security_level: SecurityLevel,
286        /// Bond information if encryption was achieved using a stored bond.
287        bond: Option<BondInformation>,
288    },
289    #[cfg(feature = "security")]
290    /// OOB data is requested during pairing. Respond with [`Connection::provide_oob_data()`].
291    OobRequest,
292}
293
294/// A connection parameters update request
295#[derive(Debug)]
296#[cfg_attr(feature = "defmt", derive(defmt::Format))]
297pub struct ConnectionParamsRequest {
298    params: RequestedConnParams,
299    handle: ConnHandle,
300    responded: bool,
301    #[cfg(feature = "connection-params-update")]
302    l2cap: bool,
303}
304
305impl ConnectionParamsRequest {
306    pub(crate) fn new(
307        params: RequestedConnParams,
308        handle: ConnHandle,
309        #[cfg(feature = "connection-params-update")] l2cap: bool,
310    ) -> Self {
311        Self {
312            params,
313            handle,
314            responded: false,
315            #[cfg(feature = "connection-params-update")]
316            l2cap,
317        }
318    }
319
320    /// Get the parameters being requested.
321    pub fn params(&self) -> &RequestedConnParams {
322        &self.params
323    }
324}
325
326#[cfg(not(feature = "connection-params-update"))]
327impl ConnectionParamsRequest {
328    /// Accept the connection parameters update request.
329    ///
330    /// If `params` is `None`, use the parameters requested by the peer.
331    pub async fn accept<C, P: PacketPool>(
332        mut self,
333        params: Option<&RequestedConnParams>,
334        stack: &Stack<'_, C, P>,
335    ) -> Result<(), BleHostError<C::Error>>
336    where
337        C: crate::Controller,
338    {
339        self.responded = true;
340
341        let params = params.unwrap_or(&self.params);
342        if !params.is_valid() {
343            return self.reject(stack).await;
344        }
345
346        match stack.host.async_command(into_le_conn_update(self.handle, params)).await {
347            Ok(()) => {
348                let param = ConnParamUpdateRes { result: 0 };
349                stack.host.send_conn_param_update_res(self.handle, &param).await
350            }
351            Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
352                Err(crate::Error::Disconnected.into())
353            }
354            Err(e) => {
355                info!("Connection parameters request procedure failed");
356                if let Err(e) = self.reject(stack).await {
357                    warn!("Failed to reject ConnParamRequest after failure");
358                }
359                Err(e)
360            }
361        }
362    }
363
364    /// Reject the connection parameters update request
365    pub async fn reject<C, P: PacketPool>(mut self, stack: &Stack<'_, C, P>) -> Result<(), BleHostError<C::Error>>
366    where
367        C: crate::Controller,
368    {
369        self.responded = true;
370        let param = ConnParamUpdateRes { result: 1 };
371        stack.host.send_conn_param_update_res(self.handle, &param).await
372    }
373}
374
375#[cfg(feature = "connection-params-update")]
376impl ConnectionParamsRequest {
377    /// Accept the connection parameters update request.
378    ///
379    /// If `params` is `None`, use the parameters requested by the peer.
380    pub async fn accept<C, P: PacketPool>(
381        mut self,
382        params: Option<&RequestedConnParams>,
383        stack: &Stack<'_, C, P>,
384    ) -> Result<(), BleHostError<C::Error>>
385    where
386        C: crate::Controller
387            + ControllerCmdAsync<LeRemoteConnectionParameterRequestReply>
388            + ControllerCmdAsync<LeRemoteConnectionParameterRequestNegativeReply>,
389    {
390        self.responded = true;
391
392        let params = params.unwrap_or(&self.params);
393        if !params.is_valid() {
394            return self.reject(stack).await;
395        }
396
397        match stack.host.async_command(into_le_conn_update(self.handle, params)).await {
398            Ok(()) => {
399                if self.l2cap {
400                    // Use L2CAP signaling to update connection parameters
401                    let param = ConnParamUpdateRes { result: 0 };
402                    stack.host.send_conn_param_update_res(self.handle, &param).await
403                } else {
404                    let interval_min: bt_hci::param::Duration<1_250> = bt_hci_duration(params.min_connection_interval);
405                    let interval_max: bt_hci::param::Duration<1_250> = bt_hci_duration(params.max_connection_interval);
406                    let timeout: bt_hci::param::Duration<10_000> = bt_hci_duration(params.supervision_timeout);
407                    stack
408                        .host
409                        .async_command(LeRemoteConnectionParameterRequestReply::new(
410                            self.handle,
411                            interval_min,
412                            interval_max,
413                            params.max_latency,
414                            timeout,
415                            bt_hci_duration(params.min_event_length),
416                            bt_hci_duration(params.max_event_length),
417                        ))
418                        .await
419                }
420            }
421            Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
422                Err(crate::Error::Disconnected.into())
423            }
424            Err(e) => {
425                info!("Connection parameters request procedure failed");
426                if let Err(e) = self.reject(stack).await {
427                    warn!("Failed to reject ConnParamRequest after failure");
428                }
429                Err(e)
430            }
431        }
432    }
433
434    /// Reject the connection parameters update request
435    pub async fn reject<C, P: PacketPool>(mut self, stack: &Stack<'_, C, P>) -> Result<(), BleHostError<C::Error>>
436    where
437        C: crate::Controller + ControllerCmdAsync<LeRemoteConnectionParameterRequestNegativeReply>,
438    {
439        self.responded = true;
440        if self.l2cap {
441            let param = ConnParamUpdateRes { result: 1 };
442            stack.host.send_conn_param_update_res(self.handle, &param).await
443        } else {
444            stack
445                .host
446                .async_command(LeRemoteConnectionParameterRequestNegativeReply::new(
447                    self.handle,
448                    RemoteConnectionParamsRejectReason::UnacceptableConnParameters,
449                ))
450                .await
451        }
452    }
453}
454
455impl Drop for ConnectionParamsRequest {
456    fn drop(&mut self) {
457        if !self.responded {
458            error!("ConnParamRequest dropped without being acccepted/rejected");
459        }
460    }
461}
462
463impl Default for RequestedConnParams {
464    fn default() -> Self {
465        Self {
466            min_connection_interval: Duration::from_millis(80),
467            max_connection_interval: Duration::from_millis(80),
468            max_latency: 0,
469            min_event_length: Duration::from_secs(0),
470            max_event_length: Duration::from_secs(0),
471            supervision_timeout: Duration::from_secs(8),
472        }
473    }
474}
475
476impl ConnParams {
477    pub(crate) const fn new() -> Self {
478        Self {
479            conn_interval: Duration::from_ticks(0),
480            peripheral_latency: 0,
481            supervision_timeout: Duration::from_ticks(0),
482        }
483    }
484}
485
486/// Handle to a BLE connection.
487///
488/// When the last reference to a connection is dropped, the connection is automatically disconnected.
489pub struct Connection<'stack, P: PacketPool> {
490    index: u8,
491    manager: &'stack ConnectionManager<'stack, P>,
492}
493
494impl<P: PacketPool> Clone for Connection<'_, P> {
495    fn clone(&self) -> Self {
496        self.manager.inc_ref(self.index);
497        Connection::new(self.index, self.manager)
498    }
499}
500
501impl<P: PacketPool> Drop for Connection<'_, P> {
502    fn drop(&mut self) {
503        self.manager.dec_ref(self.index);
504    }
505}
506
507impl<'stack, P: PacketPool> Connection<'stack, P> {
508    pub(crate) fn new(index: u8, manager: &'stack ConnectionManager<'stack, P>) -> Self {
509        Self { index, manager }
510    }
511
512    pub(crate) fn set_att_mtu(&self, mtu: u16) {
513        self.manager.set_att_mtu(self.index, mtu);
514    }
515
516    pub(crate) fn get_att_mtu(&self) -> u16 {
517        self.manager.get_att_mtu(self.index)
518    }
519
520    pub(crate) fn set_l2cap_listening(&self, listening: bool) {
521        self.manager.set_l2cap_listening(self.index, listening);
522    }
523
524    pub(crate) async fn send(&self, pdu: Pdu<P::Packet>) {
525        self.manager.send(self.index, pdu).await
526    }
527
528    pub(crate) fn try_send(&self, pdu: Pdu<P::Packet>) -> Result<(), Error> {
529        self.manager.try_send(self.index, pdu)
530    }
531
532    pub(crate) async fn post_event(&self, event: ConnectionEvent) {
533        self.manager.post_event(self.index, event).await
534    }
535
536    /// Wait for next connection event.
537    pub async fn next(&self) -> ConnectionEvent {
538        self.manager.next(self.index).await
539    }
540
541    #[cfg(feature = "gatt")]
542    pub(crate) async fn next_gatt(&self) -> Pdu<P::Packet> {
543        self.manager.next_gatt(self.index).await
544    }
545
546    #[cfg(feature = "gatt")]
547    pub(crate) async fn next_gatt_client(&self) -> Option<Pdu<P::Packet>> {
548        self.manager.next_gatt_client(self.index).await
549    }
550
551    #[cfg(feature = "gatt")]
552    pub(crate) async fn acquire_indication_slot(&self) -> Result<(), Error> {
553        self.manager.acquire_indication_slot(self.index).await
554    }
555
556    #[cfg(feature = "gatt")]
557    pub(crate) fn release_indication_slot(&self) {
558        self.manager.release_indication_slot(self.index)
559    }
560
561    #[cfg(feature = "gatt")]
562    pub(crate) async fn wait_indication_confirmation(&self) -> Result<(), Error> {
563        self.manager.wait_indication_confirmation(self.index).await
564    }
565
566    /// Check if still connected
567    pub fn is_connected(&self) -> bool {
568        self.manager.is_connected(self.index)
569    }
570
571    /// Connection handle of this connection.
572    pub fn handle(&self) -> ConnHandle {
573        self.manager.handle(self.index)
574    }
575
576    /// Expose the att_mtu.
577    pub fn att_mtu(&self) -> u16 {
578        self.get_att_mtu()
579    }
580
581    /// The connection role for this connection.
582    pub fn role(&self) -> LeConnRole {
583        self.manager.role(self.index)
584    }
585
586    /// The peer address for this connection.
587    pub fn peer_address(&self) -> Address {
588        self.manager.peer_address(self.index)
589    }
590
591    /// The peer identity key for this connection.
592    pub fn peer_identity(&self) -> Identity {
593        self.manager.peer_identity(self.index)
594    }
595
596    /// The current connection params
597    pub fn params(&self) -> ConnParams {
598        self.manager.params(self.index)
599    }
600
601    /// Request a certain security level
602    ///
603    /// For a peripheral this may cause the peripheral to send a security request. For a central
604    /// this may cause the central to send a pairing request.
605    ///
606    /// If the link is already encrypted then this will always generate an error.
607    ///
608    pub fn request_security(&self) -> Result<(), Error> {
609        self.manager.request_security(self.index, true)
610    }
611
612    /// Check if the peer is a bonded device.
613    #[cfg(feature = "security")]
614    pub fn is_bonded_peer(&self) -> bool {
615        self.manager.is_bonded_peer(self.index)
616    }
617
618    /// Try to enable encryption with a bonded peer if it is not yet established
619    #[cfg(feature = "security")]
620    pub(crate) async fn try_enable_encryption(&self) -> Result<(), Error> {
621        self.manager.try_enable_encryption(self.index).await
622    }
623
624    /// Get the encrypted state of the connection
625    pub fn security_level(&self) -> Result<SecurityLevel, Error> {
626        self.manager.get_security_level(self.index)
627    }
628
629    /// Get the negotiated encryption key length for this connection
630    #[cfg(feature = "legacy-pairing")]
631    pub fn encryption_key_len(&self) -> Result<u8, Error> {
632        self.manager.get_encryption_key_len(self.index)
633    }
634
635    /// Get whether the connection is set as bondable or not.
636    ///
637    /// This is only relevant before pairing has started.
638    pub fn bondable(&self) -> Result<bool, Error> {
639        self.manager.get_bondable(self.index)
640    }
641
642    /// Set whether the connection is bondable or not.
643    ///
644    /// By default a connection is **not** bondable.
645    ///
646    /// This must be set before pairing is initiated. Once the pairing procedure has started
647    /// this field is ignored.
648    ///
649    /// If both peripheral and central are bondable then the [`ConnectionEvent::PairingComplete`]
650    /// event contains the bond information for the pairing. This bond information should be stored
651    /// in non-volatile memory and restored on reboot using [`Stack::add_bond_information()`].
652    ///
653    /// If any party in a pairing is not bondable the [`ConnectionEvent::PairingComplete`] contains
654    /// a `None` entry for the `bond` member.
655    ///
656    pub fn set_bondable(&self, bondable: bool) -> Result<(), Error> {
657        self.manager.set_bondable(self.index, bondable)
658    }
659
660    /// Confirm that the displayed pass key matches the one displayed on the other party
661    pub fn pass_key_confirm(&self) -> Result<(), Error> {
662        self.manager.pass_key_confirm(self.index, true)
663    }
664
665    /// The displayed pass key does not match the one displayed on the other party
666    pub fn pass_key_cancel(&self) -> Result<(), Error> {
667        self.manager.pass_key_confirm(self.index, false)
668    }
669
670    /// Input the pairing pass key
671    pub fn pass_key_input(&self, pass_key: u32) -> Result<(), Error> {
672        self.manager.pass_key_input(self.index, pass_key)
673    }
674
675    /// Set whether OOB data is available for this connection.
676    ///
677    /// When set to `true`, the pairing procedure will use out-of-band authentication
678    /// if the peer also supports it (LESC) or if both sides have OOB (legacy).
679    ///
680    /// This must be set before pairing is initiated.
681    #[cfg(feature = "security")]
682    pub fn set_oob_available(&self, available: bool) -> Result<(), Error> {
683        self.manager.set_oob_available(self.index, available)
684    }
685
686    /// Provide OOB data during pairing.
687    ///
688    /// Call this when [`ConnectionEvent::OobRequest`] is received. Both the local
689    /// and peer OOB data must be provided.
690    #[cfg(feature = "security")]
691    pub fn provide_oob_data(
692        &self,
693        local_oob: crate::security_manager::OobData,
694        peer_oob: crate::security_manager::OobData,
695    ) -> Result<(), Error> {
696        self.manager.provide_oob_data(self.index, local_oob, peer_oob)
697    }
698
699    /// Request connection to be disconnected.
700    pub fn disconnect(&self) {
701        self.manager
702            .request_disconnect(self.index, DisconnectReason::RemoteUserTerminatedConn);
703    }
704
705    /// Read metrics for this connection
706    #[cfg(feature = "connection-metrics")]
707    pub fn metrics<F: FnOnce(&ConnectionMetrics) -> R, R>(&self, f: F) -> R {
708        self.manager.metrics(self.index, f)
709    }
710
711    #[cfg(feature = "att-queued-writes")]
712    pub(crate) fn prepare_write(&self, handle: u16, offset: u16, value: &[u8]) -> Result<(), crate::att::AttErrorCode> {
713        self.manager.prepare_write(self.index, handle, offset, value)
714    }
715
716    #[cfg(feature = "att-queued-writes")]
717    pub(crate) fn with_prepare_write<F, R>(&self, f: F) -> R
718    where
719        F: FnOnce(&crate::connection_manager::PrepareWriteState) -> R,
720    {
721        self.manager.with_prepare_write(self.index, f)
722    }
723
724    #[cfg(feature = "att-queued-writes")]
725    pub(crate) fn clear_prepare_write(&self) {
726        self.manager.clear_prepare_write(self.index)
727    }
728
729    /// The RSSI value for this connection.
730    pub async fn rssi<T>(&self, stack: &Stack<'_, T, P>) -> Result<i8, BleHostError<T::Error>>
731    where
732        T: ControllerCmdSync<ReadRssi>,
733    {
734        let handle = self.handle();
735        let ret = stack.host.command(ReadRssi::new(handle)).await?;
736        Ok(ret.rssi)
737    }
738
739    /// Update phy for this connection.
740    ///
741    /// This updates both TX and RX phy of the connection. For more fine grained control,
742    /// use the LeSetPhy HCI command directly.
743    pub async fn set_phy<T>(&self, stack: &Stack<'_, T, P>, phy: PhyKind) -> Result<(), BleHostError<T::Error>>
744    where
745        T: ControllerCmdAsync<LeSetPhy>,
746    {
747        let all_phys = AllPhys::new()
748            .set_has_no_rx_phy_preference(false)
749            .set_has_no_tx_phy_preference(false);
750        let mut mask = PhyMask::new()
751            .set_le_coded_phy(false)
752            .set_le_1m_phy(false)
753            .set_le_2m_phy(false);
754        let mut options = PhyOptions::default();
755        match phy {
756            PhyKind::Le2M => {
757                mask = mask.set_le_2m_phy(true);
758            }
759            PhyKind::Le1M => {
760                mask = mask.set_le_1m_phy(true);
761            }
762            PhyKind::LeCoded => {
763                mask = mask.set_le_coded_phy(true);
764                options = PhyOptions::S8CodingPreferred;
765            }
766            PhyKind::LeCodedS2 => {
767                mask = mask.set_le_coded_phy(true);
768                options = PhyOptions::S2CodingPreferred;
769            }
770        }
771        stack
772            .host
773            .async_command(LeSetPhy::new(self.handle(), all_phys, mask, mask, options))
774            .await?;
775        Ok(())
776    }
777
778    /// Read the current phy used for the connection.
779    pub async fn read_phy<T>(&self, stack: &Stack<'_, T, P>) -> Result<(PhyKind, PhyKind), BleHostError<T::Error>>
780    where
781        T: ControllerCmdSync<LeReadPhy>,
782    {
783        let res = stack.host.command(LeReadPhy::new(self.handle())).await?;
784        Ok((res.tx_phy, res.rx_phy))
785    }
786
787    /// Update data length for this connection.
788    pub async fn update_data_length<T>(
789        &self,
790        stack: &Stack<'_, T, P>,
791        length: u16,
792        time_us: u16,
793    ) -> Result<(), BleHostError<T::Error>>
794    where
795        T: ControllerCmdSync<LeSetDataLength> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
796    {
797        let handle = self.handle();
798        // First, check the local supported features to ensure that the connection update is supported.
799        let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
800        if length <= 27 || features.supports_le_data_packet_length_extension() {
801            match stack.host.command(LeSetDataLength::new(handle, length, time_us)).await {
802                Ok(_) => Ok(()),
803                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
804                    Err(crate::Error::Disconnected.into())
805                }
806                Err(e) => Err(e),
807            }
808        } else {
809            Err(BleHostError::BleHost(Error::InvalidValue))
810        }
811    }
812
813    /// Update connection parameters for this connection.
814    pub async fn update_connection_params<T>(
815        &self,
816        stack: &Stack<'_, T, P>,
817        params: &RequestedConnParams,
818    ) -> Result<(), BleHostError<T::Error>>
819    where
820        T: ControllerCmdAsync<LeConnUpdate> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
821    {
822        let handle = self.handle();
823        // First, check the local supported features to ensure that the connection update is supported.
824        let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
825        if features.supports_conn_parameters_request_procedure() || self.role() == LeConnRole::Central {
826            match stack.host.async_command(into_le_conn_update(handle, params)).await {
827                Ok(_) => return Ok(()),
828                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
829                    return Err(crate::Error::Disconnected.into());
830                }
831                Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNSUPPORTED_REMOTE_FEATURE))) => {
832                    // We tried to send the request as a periperhal but the remote central does not support procedure.
833                    // Use the L2CAP signaling method below instead.
834                    // This code path should never be reached when acting as a central. If a bugged controller implementation
835                    // returns this error code we transmit an invalid L2CAP signal which then is rejected by the remote.
836                }
837                Err(e) => return Err(e),
838            }
839        }
840
841        if self.role() == LeConnRole::Peripheral || cfg!(feature = "connection-params-update") {
842            use crate::types::l2cap::ConnParamUpdateReq;
843            // Use L2CAP signaling to update connection parameters
844            info!(
845                "Connection parameters request procedure not supported, use l2cap connection parameter update req instead"
846            );
847            let interval_min: bt_hci::param::Duration<1_250> = bt_hci_duration(params.min_connection_interval);
848            let interval_max: bt_hci::param::Duration<1_250> = bt_hci_duration(params.max_connection_interval);
849            let timeout: bt_hci::param::Duration<10_000> = bt_hci_duration(params.supervision_timeout);
850            let param = ConnParamUpdateReq {
851                interval_min: interval_min.as_u16(),
852                interval_max: interval_max.as_u16(),
853                latency: params.max_latency,
854                timeout: timeout.as_u16(),
855            };
856            stack.host.send_conn_param_update_req(handle, &param).await?;
857        }
858        Ok(())
859    }
860
861    /// Update frame space for this connection.
862    pub async fn update_frame_space<T>(
863        &self,
864        stack: &Stack<'_, T, P>,
865        frame_space_min: Duration,
866        frame_space_max: Duration,
867        phys: PhyMask,
868        spacing_types: SpacingTypes,
869    ) -> Result<(), BleHostError<T::Error>>
870    where
871        T: ControllerCmdSync<LeFrameSpaceUpdate>,
872    {
873        let handle = self.handle();
874        let frame_space_min_dur = bt_hci_duration(frame_space_min);
875        let frame_space_max_dur = bt_hci_duration(frame_space_max);
876        match stack
877            .host
878            .command(LeFrameSpaceUpdate::new(
879                handle,
880                frame_space_min_dur,
881                frame_space_max_dur,
882                phys,
883                spacing_types,
884            ))
885            .await
886        {
887            Ok(_) => Ok(()),
888            Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
889                Err(crate::Error::Disconnected.into())
890            }
891            Err(e) => Err(e),
892        }
893    }
894
895    /// Request a change in connection rate (interval and subrate) for this connection.
896    pub async fn request_connection_rate<T>(
897        &self,
898        stack: &Stack<'_, T, P>,
899        conn_rate_params: &ConnectRateParams,
900    ) -> Result<(), BleHostError<T::Error>>
901    where
902        T: ControllerCmdSync<LeConnectionRateRequest>,
903    {
904        let handle = self.handle();
905        let min_interval = bt_hci_duration(conn_rate_params.min_connection_interval);
906        let max_interval = bt_hci_duration(conn_rate_params.max_connection_interval);
907        let timeout = bt_hci_duration(conn_rate_params.supervision_timeout);
908        let min_ce = bt_hci_duration(conn_rate_params.min_ce_length);
909        let max_ce = bt_hci_duration(conn_rate_params.max_ce_length);
910        match stack
911            .host
912            .command(LeConnectionRateRequest::new(
913                handle,
914                min_interval,
915                max_interval,
916                conn_rate_params.subrate_min,
917                conn_rate_params.subrate_max,
918                conn_rate_params.max_latency,
919                conn_rate_params.continuation_number,
920                timeout,
921                min_ce,
922                max_ce,
923            ))
924            .await
925        {
926            Ok(_) => Ok(()),
927            Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
928                Err(crate::Error::Disconnected.into())
929            }
930            Err(e) => Err(e),
931        }
932    }
933
934    /// Transform BLE connection into a `GattConnection`
935    #[cfg(feature = "gatt")]
936    pub fn with_attribute_server<'values, 'server, M: RawMutex, const ATT_MAX: usize, const CONN_MAX: usize>(
937        self,
938        server: &'server AttributeServer<'values, M, P, ATT_MAX, CONN_MAX>,
939    ) -> Result<GattConnection<'stack, 'server, P>, Error> {
940        GattConnection::try_new(self, server)
941    }
942}
943
944fn into_le_conn_update(handle: ConnHandle, params: &RequestedConnParams) -> LeConnUpdate {
945    LeConnUpdate::new(
946        handle,
947        bt_hci_duration(params.min_connection_interval),
948        bt_hci_duration(params.max_connection_interval),
949        params.max_latency,
950        bt_hci_duration(params.supervision_timeout),
951        bt_hci_duration(params.min_event_length),
952        bt_hci_duration(params.max_event_length),
953    )
954}