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::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 #[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 pub fn is_connected(&self) -> bool {
281 self.manager.is_connected(self.index)
282 }
283
284 pub fn handle(&self) -> ConnHandle {
286 self.manager.handle(self.index)
287 }
288
289 pub fn att_mtu(&self) -> u16 {
291 self.get_att_mtu()
292 }
293
294 pub fn role(&self) -> LeConnRole {
296 self.manager.role(self.index)
297 }
298
299 pub fn peer_address(&self) -> BdAddr {
301 self.manager.peer_address(self.index)
302 }
303
304 pub fn peer_identity(&self) -> Identity {
306 self.manager.peer_identity(self.index)
307 }
308 pub fn request_security(&self) -> Result<(), Error> {
316 self.manager.request_security(self.index)
317 }
318
319 pub fn security_level(&self) -> Result<SecurityLevel, Error> {
321 self.manager.get_security_level(self.index)
322 }
323
324 pub fn bondable(&self) -> Result<bool, Error> {
328 self.manager.get_bondable(self.index)
329 }
330
331 pub fn set_bondable(&self, bondable: bool) -> Result<(), Error> {
346 self.manager.set_bondable(self.index, bondable)
347 }
348
349 pub fn pass_key_confirm(&self) -> Result<(), Error> {
351 self.manager.pass_key_confirm(self.index, true)
352 }
353
354 pub fn pass_key_cancel(&self) -> Result<(), Error> {
356 self.manager.pass_key_confirm(self.index, false)
357 }
358
359 pub fn pass_key_input(&self, pass_key: u32) -> Result<(), Error> {
361 self.manager.pass_key_input(self.index, pass_key)
362 }
363
364 pub fn disconnect(&self) {
366 self.manager
367 .request_disconnect(self.index, DisconnectReason::RemoteUserTerminatedConn);
368 }
369
370 #[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 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 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 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 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 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 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 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 }
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 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, ¶m).await?;
504 }
505 Ok(())
506 }
507
508 #[cfg(feature = "connection-params-update")]
509 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 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, ¶m).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, ¶m).await?;
575 }
576 Err(e)
577 }
578 }
579 } else {
580 Err(crate::Error::NotSupported.into())
581 }
582 }
583
584 #[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}