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 = "gatt")]
10use embassy_sync::blocking_mutex::raw::RawMutex;
11use embassy_time::Duration;
12
13use crate::connection_manager::ConnectionManager;
14#[cfg(feature = "connection-metrics")]
15pub use crate::connection_manager::Metrics as ConnectionMetrics;
16use crate::pdu::Pdu;
17#[cfg(feature = "gatt")]
18use crate::prelude::{AttributeServer, GattConnection};
19#[cfg(feature = "security")]
20use crate::security_manager::{BondInformation, PassKey};
21use crate::types::l2cap::{ConnParamUpdateReq, ConnParamUpdateRes};
22use crate::{bt_hci_duration, BleHostError, Error, Identity, PacketPool, Stack};
23
24#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30pub enum SecurityLevel {
31 NoEncryption,
33 Encrypted,
35 EncryptedAuthenticated,
37}
38
39impl SecurityLevel {
40 pub fn encrypted(&self) -> bool {
42 !matches!(self, SecurityLevel::NoEncryption)
43 }
44
45 pub fn authenticated(&self) -> bool {
47 matches!(self, SecurityLevel::EncryptedAuthenticated)
48 }
49}
50
51pub struct ConnectConfig<'d> {
53 pub scan_config: ScanConfig<'d>,
55 pub connect_params: ConnectParams,
57}
58
59pub struct ScanConfig<'d> {
61 pub active: bool,
63 pub filter_accept_list: &'d [(AddrKind, &'d BdAddr)],
65 pub phys: PhySet,
67 pub interval: Duration,
69 pub window: Duration,
71 pub timeout: Duration,
73}
74
75impl Default for ScanConfig<'_> {
76 fn default() -> Self {
77 Self {
78 active: true,
79 filter_accept_list: &[],
80 phys: PhySet::M1,
81 interval: Duration::from_secs(1),
82 window: Duration::from_secs(1),
83 timeout: Duration::from_secs(0),
84 }
85 }
86}
87
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90#[derive(Eq, PartialEq, Copy, Clone)]
91#[repr(u8)]
92pub enum PhySet {
93 M1 = 1,
95 M2 = 2,
97 M1M2 = 3,
99 Coded = 4,
101 M1Coded = 5,
103 M2Coded = 6,
105 M1M2Coded = 7,
107}
108
109#[derive(Debug, Clone, PartialEq)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112pub struct ConnectParams {
113 pub min_connection_interval: Duration,
115 pub max_connection_interval: Duration,
117 pub max_latency: u16,
119 pub min_event_length: Duration,
121 pub max_event_length: Duration,
123 pub supervision_timeout: Duration,
125}
126
127#[derive(Debug)]
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130pub enum ConnectionEvent {
131 Disconnected {
133 reason: Status,
135 },
136 PhyUpdated {
138 tx_phy: PhyKind,
140 rx_phy: PhyKind,
142 },
143 ConnectionParamsUpdated {
145 conn_interval: Duration,
147 peripheral_latency: u16,
149 supervision_timeout: Duration,
151 },
152 DataLengthUpdated {
154 max_tx_octets: u16,
156 max_tx_time: u16,
158 max_rx_octets: u16,
160 max_rx_time: u16,
162 },
163 RequestConnectionParams {
165 min_connection_interval: Duration,
167 max_connection_interval: Duration,
169 max_latency: u16,
171 supervision_timeout: Duration,
173 },
174 #[cfg(feature = "security")]
175 PassKeyDisplay(PassKey),
177 #[cfg(feature = "security")]
178 PassKeyConfirm(PassKey),
180 #[cfg(feature = "security")]
181 PassKeyInput,
183 #[cfg(feature = "security")]
184 PairingComplete {
186 security_level: SecurityLevel,
188 bond: Option<BondInformation>,
190 },
191 #[cfg(feature = "security")]
192 PairingFailed(Error),
194}
195
196impl Default for ConnectParams {
197 fn default() -> Self {
198 Self {
199 min_connection_interval: Duration::from_millis(80),
200 max_connection_interval: Duration::from_millis(80),
201 max_latency: 0,
202 min_event_length: Duration::from_secs(0),
203 max_event_length: Duration::from_secs(0),
204 supervision_timeout: Duration::from_secs(8),
205 }
206 }
207}
208
209pub struct Connection<'stack, P: PacketPool> {
213 index: u8,
214 manager: &'stack ConnectionManager<'stack, P>,
215}
216
217impl<P: PacketPool> Clone for Connection<'_, P> {
218 fn clone(&self) -> Self {
219 self.manager.inc_ref(self.index);
220 Connection::new(self.index, self.manager)
221 }
222}
223
224impl<P: PacketPool> Drop for Connection<'_, P> {
225 fn drop(&mut self) {
226 self.manager.dec_ref(self.index);
227 }
228}
229
230impl<'stack, P: PacketPool> Connection<'stack, P> {
231 pub(crate) fn new(index: u8, manager: &'stack ConnectionManager<'stack, P>) -> Self {
232 Self { index, manager }
233 }
234
235 pub(crate) fn set_att_mtu(&self, mtu: u16) {
236 self.manager.set_att_mtu(self.index, mtu);
237 }
238
239 pub(crate) fn get_att_mtu(&self) -> u16 {
240 self.manager.get_att_mtu(self.index)
241 }
242
243 pub(crate) async fn send(&self, pdu: Pdu<P::Packet>) {
244 self.manager.send(self.index, pdu).await
245 }
246
247 pub(crate) fn try_send(&self, pdu: Pdu<P::Packet>) -> Result<(), Error> {
248 self.manager.try_send(self.index, pdu)
249 }
250
251 pub(crate) async fn post_event(&self, event: ConnectionEvent) {
252 self.manager.post_event(self.index, event).await
253 }
254
255 pub async fn next(&self) -> ConnectionEvent {
257 self.manager.next(self.index).await
258 }
259
260 #[cfg(feature = "gatt")]
261 pub(crate) async fn next_gatt(&self) -> Pdu<P::Packet> {
262 self.manager.next_gatt(self.index).await
263 }
264
265 pub fn is_connected(&self) -> bool {
267 self.manager.is_connected(self.index)
268 }
269
270 pub fn handle(&self) -> ConnHandle {
272 self.manager.handle(self.index)
273 }
274
275 pub fn att_mtu(&self) -> u16 {
277 self.get_att_mtu()
278 }
279
280 pub fn role(&self) -> LeConnRole {
282 self.manager.role(self.index)
283 }
284
285 pub fn peer_address(&self) -> BdAddr {
287 self.manager.peer_address(self.index)
288 }
289
290 pub fn peer_identity(&self) -> Identity {
292 self.manager.peer_identity(self.index)
293 }
294 pub fn request_security(&self) -> Result<(), Error> {
302 self.manager.request_security(self.index)
303 }
304
305 pub fn security_level(&self) -> Result<SecurityLevel, Error> {
307 self.manager.get_security_level(self.index)
308 }
309
310 pub fn bondable(&self) -> Result<bool, Error> {
314 self.manager.get_bondable(self.index)
315 }
316
317 pub fn set_bondable(&self, bondable: bool) -> Result<(), Error> {
332 self.manager.set_bondable(self.index, bondable)
333 }
334
335 pub fn pass_key_confirm(&self) -> Result<(), Error> {
337 self.manager.pass_key_confirm(self.index, true)
338 }
339
340 pub fn pass_key_cancel(&self) -> Result<(), Error> {
342 self.manager.pass_key_confirm(self.index, false)
343 }
344
345 pub fn pass_key_input(&self, pass_key: u32) -> Result<(), Error> {
347 self.manager.pass_key_input(self.index, pass_key)
348 }
349
350 pub fn disconnect(&self) {
352 self.manager
353 .request_disconnect(self.index, DisconnectReason::RemoteUserTerminatedConn);
354 }
355
356 #[cfg(feature = "connection-metrics")]
358 pub fn metrics<F: FnOnce(&ConnectionMetrics) -> R, R>(&self, f: F) -> R {
359 self.manager.metrics(self.index, f)
360 }
361
362 pub async fn rssi<T>(&self, stack: &Stack<'_, T, P>) -> Result<i8, BleHostError<T::Error>>
364 where
365 T: ControllerCmdSync<ReadRssi>,
366 {
367 let handle = self.handle();
368 let ret = stack.host.command(ReadRssi::new(handle)).await?;
369 Ok(ret.rssi)
370 }
371
372 pub async fn set_phy<T>(&self, stack: &Stack<'_, T, P>, phy: PhyKind) -> Result<(), BleHostError<T::Error>>
377 where
378 T: ControllerCmdAsync<LeSetPhy>,
379 {
380 let all_phys = AllPhys::new()
381 .set_has_no_rx_phy_preference(false)
382 .set_has_no_tx_phy_preference(false);
383 let mut mask = PhyMask::new()
384 .set_le_coded_preferred(false)
385 .set_le_1m_preferred(false)
386 .set_le_2m_preferred(false);
387 let mut options = PhyOptions::default();
388 match phy {
389 PhyKind::Le2M => {
390 mask = mask.set_le_2m_preferred(true);
391 }
392 PhyKind::Le1M => {
393 mask = mask.set_le_1m_preferred(true);
394 }
395 PhyKind::LeCoded => {
396 mask = mask.set_le_coded_preferred(true);
397 options = PhyOptions::S8CodingPreferred;
398 }
399 PhyKind::LeCodedS2 => {
400 mask = mask.set_le_coded_preferred(true);
401 options = PhyOptions::S2CodingPreferred;
402 }
403 }
404 stack
405 .host
406 .async_command(LeSetPhy::new(self.handle(), all_phys, mask, mask, options))
407 .await?;
408 Ok(())
409 }
410
411 pub async fn read_phy<T>(&self, stack: &Stack<'_, T, P>) -> Result<(PhyKind, PhyKind), BleHostError<T::Error>>
413 where
414 T: ControllerCmdSync<LeReadPhy>,
415 {
416 let res = stack.host.command(LeReadPhy::new(self.handle())).await?;
417 Ok((res.tx_phy, res.rx_phy))
418 }
419
420 pub async fn update_data_length<T>(
422 &self,
423 stack: &Stack<'_, T, P>,
424 length: u16,
425 time_us: u16,
426 ) -> Result<(), BleHostError<T::Error>>
427 where
428 T: ControllerCmdSync<LeSetDataLength> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
429 {
430 let handle = self.handle();
431 let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
433 if length <= 27 || features.supports_le_data_packet_length_extension() {
434 match stack.host.command(LeSetDataLength::new(handle, length, time_us)).await {
435 Ok(_) => Ok(()),
436 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
437 Err(crate::Error::Disconnected.into())
438 }
439 Err(e) => Err(e),
440 }
441 } else {
442 Err(BleHostError::BleHost(Error::InvalidValue))
443 }
444 }
445
446 pub async fn update_connection_params<T>(
448 &self,
449 stack: &Stack<'_, T, P>,
450 params: &ConnectParams,
451 ) -> Result<(), BleHostError<T::Error>>
452 where
453 T: ControllerCmdAsync<LeConnUpdate> + ControllerCmdSync<LeReadLocalSupportedFeatures>,
454 {
455 let handle = self.handle();
456 let features = stack.host.command(LeReadLocalSupportedFeatures::new()).await?;
458 if features.supports_conn_parameters_request_procedure() || self.role() == LeConnRole::Central {
459 match stack.host.async_command(into_le_conn_update(handle, params)).await {
460 Ok(_) => return Ok(()),
461 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
462 return Err(crate::Error::Disconnected.into());
463 }
464 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNSUPPORTED_REMOTE_FEATURE))) => {
465 }
470 Err(e) => return Err(e),
471 }
472 }
473
474 info!(
476 "Connection parameters request procedure not supported, use l2cap connection parameter update req instead"
477 );
478 let interval_min: bt_hci::param::Duration<1_250> = bt_hci_duration(params.min_connection_interval);
479 let interva_max: bt_hci::param::Duration<1_250> = bt_hci_duration(params.max_connection_interval);
480 let timeout: bt_hci::param::Duration<10_000> = bt_hci_duration(params.supervision_timeout);
481 let param = ConnParamUpdateReq {
482 interval_min: interval_min.as_u16(),
483 interval_max: interva_max.as_u16(),
484 latency: params.max_latency,
485 timeout: timeout.as_u16(),
486 };
487 stack.host.send_conn_param_update_req(handle, ¶m).await
488 }
489
490 pub async fn accept_connection_params<T>(
492 &self,
493 stack: &Stack<'_, T, P>,
494 params: &ConnectParams,
495 ) -> Result<(), BleHostError<T::Error>>
496 where
497 T: ControllerCmdAsync<LeConnUpdate>,
498 {
499 let handle = self.handle();
500 if self.role() == LeConnRole::Central {
501 match stack.host.async_command(into_le_conn_update(handle, params)).await {
502 Ok(_) => {
503 info!(
505 "Connection parameters request procedure not supported, use l2cap connection parameter update res instead"
506 );
507 let param = ConnParamUpdateRes { result: 0 };
508 stack.host.send_conn_param_update_res(handle, ¶m).await?;
509 Ok(())
510 }
511 Err(BleHostError::BleHost(crate::Error::Hci(bt_hci::param::Error::UNKNOWN_CONN_IDENTIFIER))) => {
512 Err(crate::Error::Disconnected.into())
513 }
514 Err(e) => {
515 info!("Connection parameters request procedure failed");
516 let param = ConnParamUpdateRes { result: 1 };
517 stack.host.send_conn_param_update_res(handle, ¶m).await?;
518 Err(e)
519 }
520 }
521 } else {
522 Err(crate::Error::NotSupported.into())
523 }
524 }
525
526 #[cfg(feature = "gatt")]
528 pub fn with_attribute_server<
529 'values,
530 'server,
531 M: RawMutex,
532 const ATT_MAX: usize,
533 const CCCD_MAX: usize,
534 const CONN_MAX: usize,
535 >(
536 self,
537 server: &'server AttributeServer<'values, M, P, ATT_MAX, CCCD_MAX, CONN_MAX>,
538 ) -> Result<GattConnection<'stack, 'server, P>, Error> {
539 GattConnection::try_new(self, server)
540 }
541}
542
543fn into_le_conn_update(handle: ConnHandle, params: &ConnectParams) -> LeConnUpdate {
544 LeConnUpdate::new(
545 handle,
546 bt_hci_duration(params.min_connection_interval),
547 bt_hci_duration(params.max_connection_interval),
548 params.max_latency,
549 bt_hci_duration(params.supervision_timeout),
550 bt_hci_duration(params.min_event_length),
551 bt_hci_duration(params.max_event_length),
552 )
553}