1use 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::{ConnParamUpdateReq, ConnParamUpdateRes};
28use crate::{bt_hci_duration, BleHostError, Error, Identity, PacketPool, Stack};
29
30#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub enum SecurityLevel {
37 NoEncryption,
39 Encrypted,
41 EncryptedAuthenticated,
43}
44
45impl SecurityLevel {
46 pub fn encrypted(&self) -> bool {
48 !matches!(self, SecurityLevel::NoEncryption)
49 }
50
51 pub fn authenticated(&self) -> bool {
53 matches!(self, SecurityLevel::EncryptedAuthenticated)
54 }
55}
56
57pub struct ConnectConfig<'d> {
59 pub scan_config: ScanConfig<'d>,
61 pub connect_params: ConnectParams,
63}
64
65pub struct ScanConfig<'d> {
67 pub active: bool,
69 pub filter_accept_list: &'d [(AddrKind, &'d BdAddr)],
71 pub phys: PhySet,
73 pub interval: Duration,
75 pub window: Duration,
77 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#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96#[derive(Eq, PartialEq, Copy, Clone)]
97#[repr(u8)]
98pub enum PhySet {
99 M1 = 1,
101 M2 = 2,
103 M1M2 = 3,
105 Coded = 4,
107 M1Coded = 5,
109 M2Coded = 6,
111 M1M2Coded = 7,
113}
114
115#[derive(Debug, Clone, PartialEq)]
117#[cfg_attr(feature = "defmt", derive(defmt::Format))]
118pub struct ConnectParams {
119 pub min_connection_interval: Duration,
121 pub max_connection_interval: Duration,
123 pub max_latency: u16,
125 pub min_event_length: Duration,
127 pub max_event_length: Duration,
129 pub supervision_timeout: Duration,
131}
132
133#[derive(Debug)]
135#[cfg_attr(feature = "defmt", derive(defmt::Format))]
136pub enum ConnectionEvent {
137 Disconnected {
139 reason: Status,
141 },
142 PhyUpdated {
144 tx_phy: PhyKind,
146 rx_phy: PhyKind,
148 },
149 ConnectionParamsUpdated {
151 conn_interval: Duration,
153 peripheral_latency: u16,
155 supervision_timeout: Duration,
157 },
158 DataLengthUpdated {
160 max_tx_octets: u16,
162 max_tx_time: u16,
164 max_rx_octets: u16,
166 max_rx_time: u16,
168 },
169 RequestConnectionParams {
174 min_connection_interval: Duration,
176 max_connection_interval: Duration,
178 max_latency: u16,
180 supervision_timeout: Duration,
182 },
183 #[cfg(feature = "security")]
184 PassKeyDisplay(PassKey),
186 #[cfg(feature = "security")]
187 PassKeyConfirm(PassKey),
189 #[cfg(feature = "security")]
190 PassKeyInput,
192 #[cfg(feature = "security")]
193 PairingComplete {
195 security_level: SecurityLevel,
197 bond: Option<BondInformation>,
199 },
200 #[cfg(feature = "security")]
201 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
218pub 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 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 pub fn is_connected(&self) -> bool {
276 self.manager.is_connected(self.index)
277 }
278
279 pub fn handle(&self) -> ConnHandle {
281 self.manager.handle(self.index)
282 }
283
284 pub fn att_mtu(&self) -> u16 {
286 self.get_att_mtu()
287 }
288
289 pub fn role(&self) -> LeConnRole {
291 self.manager.role(self.index)
292 }
293
294 pub fn peer_address(&self) -> BdAddr {
296 self.manager.peer_address(self.index)
297 }
298
299 pub fn peer_identity(&self) -> Identity {
301 self.manager.peer_identity(self.index)
302 }
303 pub fn request_security(&self) -> Result<(), Error> {
311 self.manager.request_security(self.index)
312 }
313
314 pub fn security_level(&self) -> Result<SecurityLevel, Error> {
316 self.manager.get_security_level(self.index)
317 }
318
319 pub fn bondable(&self) -> Result<bool, Error> {
323 self.manager.get_bondable(self.index)
324 }
325
326 pub fn set_bondable(&self, bondable: bool) -> Result<(), Error> {
341 self.manager.set_bondable(self.index, bondable)
342 }
343
344 pub fn pass_key_confirm(&self) -> Result<(), Error> {
346 self.manager.pass_key_confirm(self.index, true)
347 }
348
349 pub fn pass_key_cancel(&self) -> Result<(), Error> {
351 self.manager.pass_key_confirm(self.index, false)
352 }
353
354 pub fn pass_key_input(&self, pass_key: u32) -> Result<(), Error> {
356 self.manager.pass_key_input(self.index, pass_key)
357 }
358
359 pub fn disconnect(&self) {
361 self.manager
362 .request_disconnect(self.index, DisconnectReason::RemoteUserTerminatedConn);
363 }
364
365 #[cfg(feature = "connection-metrics")]
367 pub fn metrics<F: FnOnce(&ConnectionMetrics) -> R, R>(&self, f: F) -> R {
368 self.manager.metrics(self.index, f)
369 }
370
371 pub async fn rssi<T>(&self, stack: &Stack<'_, T, P>) -> Result<i8, BleHostError<T::Error>>
373 where
374 T: ControllerCmdSync<ReadRssi>,
375 {
376 let handle = self.handle();
377 let ret = stack.host.command(ReadRssi::new(handle)).await?;
378 Ok(ret.rssi)
379 }
380
381 pub async fn set_phy<T>(&self, stack: &Stack<'_, T, P>, phy: PhyKind) -> Result<(), BleHostError<T::Error>>
386 where
387 T: ControllerCmdAsync<LeSetPhy>,
388 {
389 let all_phys = AllPhys::new()
390 .set_has_no_rx_phy_preference(false)
391 .set_has_no_tx_phy_preference(false);
392 let mut mask = PhyMask::new()
393 .set_le_coded_preferred(false)
394 .set_le_1m_preferred(false)
395 .set_le_2m_preferred(false);
396 let mut options = PhyOptions::default();
397 match phy {
398 PhyKind::Le2M => {
399 mask = mask.set_le_2m_preferred(true);
400 }
401 PhyKind::Le1M => {
402 mask = mask.set_le_1m_preferred(true);
403 }
404 PhyKind::LeCoded => {
405 mask = mask.set_le_coded_preferred(true);
406 options = PhyOptions::S8CodingPreferred;
407 }
408 PhyKind::LeCodedS2 => {
409 mask = mask.set_le_coded_preferred(true);
410 options = PhyOptions::S2CodingPreferred;
411 }
412 }
413 stack
414 .host
415 .async_command(LeSetPhy::new(self.handle(), all_phys, mask, mask, options))
416 .await?;
417 Ok(())
418 }
419
420 pub async fn read_phy<T>(&self, stack: &Stack<'_, T, P>) -> Result<(PhyKind, PhyKind), BleHostError<T::Error>>
422 where
423 T: ControllerCmdSync<LeReadPhy>,
424 {
425 let res = stack.host.command(LeReadPhy::new(self.handle())).await?;
426 Ok((res.tx_phy, res.rx_phy))
427 }
428
429 pub async fn update_data_length<T>(
431 &self,
432 stack: &Stack<'_, T, P>,
433 length: u16,
434 time_us: u16,
435 ) -> Result<(), BleHostError<T::Error>>
436 where
437 T: ControllerCmdSync<LeSetDataLength> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
438 {
439 let handle = self.handle();
440 let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
442 if length <= 27 || features.supports_le_data_packet_length_extension() {
443 match stack.host.command(LeSetDataLength::new(handle, length, time_us)).await {
444 Ok(_) => Ok(()),
445 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
446 Err(crate::Error::Disconnected.into())
447 }
448 Err(e) => Err(e),
449 }
450 } else {
451 Err(BleHostError::BleHost(Error::InvalidValue))
452 }
453 }
454
455 pub async fn update_connection_params<T>(
457 &self,
458 stack: &Stack<'_, T, P>,
459 params: &ConnectParams,
460 ) -> Result<(), BleHostError<T::Error>>
461 where
462 T: ControllerCmdAsync<LeConnUpdate> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
463 {
464 let handle = self.handle();
465 let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
467 if features.supports_conn_parameters_request_procedure() || self.role() == LeConnRole::Central {
468 match stack.host.async_command(into_le_conn_update(handle, params)).await {
469 Ok(_) => return Ok(()),
470 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
471 return Err(crate::Error::Disconnected.into());
472 }
473 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNSUPPORTED_REMOTE_FEATURE))) => {
474 }
479 Err(e) => return Err(e),
480 }
481 }
482
483 #[cfg(feature = "connection-params-update")]
484 {
485 info!(
487 "Connection parameters request procedure not supported, use l2cap connection parameter update req instead"
488 );
489 let interval_min: bt_hci::param::Duration<1_250> = bt_hci_duration(params.min_connection_interval);
490 let interval_max: bt_hci::param::Duration<1_250> = bt_hci_duration(params.max_connection_interval);
491 let timeout: bt_hci::param::Duration<10_000> = bt_hci_duration(params.supervision_timeout);
492 let param = ConnParamUpdateReq {
493 interval_min: interval_min.as_u16(),
494 interval_max: interval_max.as_u16(),
495 latency: params.max_latency,
496 timeout: timeout.as_u16(),
497 };
498 stack.host.send_conn_param_update_req(handle, ¶m).await?;
499 }
500 Ok(())
501 }
502
503 #[cfg(feature = "connection-params-update")]
504 pub async fn accept_connection_params<T>(
508 &self,
509 stack: &Stack<'_, T, P>,
510 params: &ConnectParams,
511 ) -> Result<(), BleHostError<T::Error>>
512 where
513 T: ControllerCmdAsync<LeConnUpdate>
514 + ControllerCmdSync<LeReadLocalSupportedFeatures>
515 + ControllerCmdAsync<LeRemoteConnectionParameterRequestReply>
516 + ControllerCmdAsync<LeRemoteConnectionParameterRequestNegativeReply>,
517 {
518 let handle = self.handle();
519 if self.role() == LeConnRole::Central {
520 let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
521 match stack.host.async_command(into_le_conn_update(handle, params)).await {
522 Ok(_) => {
523 if features.supports_conn_parameters_request_procedure() {
524 let interval_min: bt_hci::param::Duration<1_250> =
525 bt_hci_duration(params.min_connection_interval);
526 let interval_max: bt_hci::param::Duration<1_250> =
527 bt_hci_duration(params.max_connection_interval);
528 let timeout: bt_hci::param::Duration<10_000> = bt_hci_duration(params.supervision_timeout);
529 if let Err(e) = stack
530 .host
531 .async_command(LeRemoteConnectionParameterRequestReply::new(
532 handle,
533 interval_min,
534 interval_max,
535 params.max_latency,
536 timeout,
537 bt_hci_duration(params.min_event_length),
538 bt_hci_duration(params.max_event_length),
539 ))
540 .await
541 {
542 return Err(e);
543 }
544 } else {
545 info!(
547 "Connection parameters request procedure not supported, using l2cap connection parameter update res instead"
548 );
549 let param = ConnParamUpdateRes { result: 0 };
550 stack.host.send_conn_param_update_res(handle, ¶m).await?;
551 }
552 Ok(())
553 }
554 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
555 Err(crate::Error::Disconnected.into())
556 }
557 Err(e) => {
558 info!("Connection parameters request procedure failed");
559 if features.supports_conn_parameters_request_procedure() {
560 stack
561 .host
562 .async_command(LeRemoteConnectionParameterRequestNegativeReply::new(
563 handle,
564 RemoteConnectionParamsRejectReason::UnacceptableConnParameters,
565 ))
566 .await?;
567 } else {
568 let param = ConnParamUpdateRes { result: 1 };
569 stack.host.send_conn_param_update_res(handle, ¶m).await?;
570 }
571 Err(e)
572 }
573 }
574 } else {
575 Err(crate::Error::NotSupported.into())
576 }
577 }
578
579 #[cfg(feature = "gatt")]
581 pub fn with_attribute_server<
582 'values,
583 'server,
584 M: RawMutex,
585 const ATT_MAX: usize,
586 const CCCD_MAX: usize,
587 const CONN_MAX: usize,
588 >(
589 self,
590 server: &'server AttributeServer<'values, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>,
591 ) -> Result<GattConnection<'stack, 'server, P>, Error> {
592 GattConnection::try_new(self, server)
593 }
594}
595
596fn into_le_conn_update(handle: ConnHandle, params: &ConnectParams) -> LeConnUpdate {
597 LeConnUpdate::new(
598 handle,
599 bt_hci_duration(params.min_connection_interval),
600 bt_hci_duration(params.max_connection_interval),
601 params.max_latency,
602 bt_hci_duration(params.supervision_timeout),
603 bt_hci_duration(params.min_event_length),
604 bt_hci_duration(params.max_event_length),
605 )
606}