Skip to main content

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