1use bt_hci::cmd::le::{LeAddDeviceToFilterAcceptList, LeClearFilterAcceptList, LeCreateConn, LeExtCreateConn};
3use bt_hci::controller::{Controller, ControllerCmdAsync, ControllerCmdSync};
4use bt_hci::param::{AddrKind, BdAddr, InitiatingPhy, LeConnRole, PhyParams};
5use embassy_futures::select::{select, Either};
6
7use crate::connection::{ConnectConfig, Connection, PhySet};
8use crate::{bt_hci_duration, BleHostError, Error, PacketPool, Stack};
9
10pub struct Central<'stack, C, P: PacketPool> {
12 pub(crate) stack: &'stack Stack<'stack, C, P>,
13}
14
15impl<'stack, C: Controller, P: PacketPool> Central<'stack, C, P> {
16 pub(crate) fn new(stack: &'stack Stack<'stack, C, P>) -> Self {
17 Self { stack }
18 }
19
20 pub async fn connect(&mut self, config: &ConnectConfig<'_>) -> Result<Connection<'stack, P>, BleHostError<C::Error>>
22 where
23 C: ControllerCmdSync<LeClearFilterAcceptList>
24 + ControllerCmdSync<LeAddDeviceToFilterAcceptList>
25 + ControllerCmdAsync<LeCreateConn>,
26 {
27 if config.scan_config.filter_accept_list.is_empty() {
28 return Err(Error::ConfigFilterAcceptListIsEmpty.into());
29 }
30
31 let host = &self.stack.host;
32 let _drop = crate::host::OnDrop::new(|| {
33 host.connect_command_state.cancel(true);
34 });
35 host.connect_command_state.request().await;
36
37 self.set_accept_filter(config.scan_config.filter_accept_list).await?;
38
39 host.async_command(LeCreateConn::new(
40 bt_hci_duration(config.scan_config.interval),
41 bt_hci_duration(config.scan_config.window),
42 true,
43 AddrKind::PUBLIC,
44 BdAddr::default(),
45 host.address.map(|a| a.kind).unwrap_or(AddrKind::PUBLIC),
46 bt_hci_duration(config.connect_params.min_connection_interval),
47 bt_hci_duration(config.connect_params.max_connection_interval),
48 config.connect_params.max_latency,
49 bt_hci_duration(config.connect_params.supervision_timeout),
50 bt_hci_duration(config.connect_params.min_event_length),
51 bt_hci_duration(config.connect_params.max_event_length),
52 ))
53 .await?;
54 match select(
55 host.connections
56 .accept(LeConnRole::Central, config.scan_config.filter_accept_list),
57 host.connect_command_state.wait_idle(),
58 )
59 .await
60 {
61 Either::First(conn) => {
62 _drop.defuse();
63 host.connect_command_state.done();
64 Ok(conn)
65 }
66 Either::Second(_) => Err(Error::Timeout.into()),
67 }
68 }
69
70 pub async fn connect_ext(
72 &mut self,
73 config: &ConnectConfig<'_>,
74 ) -> Result<Connection<'stack, P>, BleHostError<C::Error>>
75 where
76 C: ControllerCmdSync<LeClearFilterAcceptList>
77 + ControllerCmdSync<LeAddDeviceToFilterAcceptList>
78 + ControllerCmdAsync<LeExtCreateConn>,
79 {
80 if config.scan_config.filter_accept_list.is_empty() {
81 return Err(Error::ConfigFilterAcceptListIsEmpty.into());
82 }
83
84 let host = &self.stack.host;
85 let _drop = crate::host::OnDrop::new(|| {
87 host.connect_command_state.cancel(true);
88 });
89 host.connect_command_state.request().await;
90
91 self.set_accept_filter(config.scan_config.filter_accept_list).await?;
92
93 let initiating = InitiatingPhy {
94 scan_interval: bt_hci_duration(config.scan_config.interval),
95 scan_window: bt_hci_duration(config.scan_config.window),
96 conn_interval_min: bt_hci_duration(config.connect_params.min_connection_interval),
97 conn_interval_max: bt_hci_duration(config.connect_params.max_connection_interval),
98 max_latency: config.connect_params.max_latency,
99 supervision_timeout: bt_hci_duration(config.connect_params.supervision_timeout),
100 min_ce_len: bt_hci_duration(config.connect_params.min_event_length),
101 max_ce_len: bt_hci_duration(config.connect_params.max_event_length),
102 };
103 let phy_params = create_phy_params(initiating, config.scan_config.phys);
104
105 host.async_command(LeExtCreateConn::new(
106 true,
107 host.address.map(|a| a.kind).unwrap_or(AddrKind::PUBLIC),
108 AddrKind::PUBLIC,
109 BdAddr::default(),
110 phy_params,
111 ))
112 .await?;
113
114 match select(
115 host.connections
116 .accept(LeConnRole::Central, config.scan_config.filter_accept_list),
117 host.connect_command_state.wait_idle(),
118 )
119 .await
120 {
121 Either::First(conn) => {
122 _drop.defuse();
123 host.connect_command_state.done();
124 Ok(conn)
125 }
126 Either::Second(_) => Err(Error::Timeout.into()),
127 }
128 }
129
130 pub(crate) async fn set_accept_filter(
131 &mut self,
132 filter_accept_list: &[(AddrKind, &BdAddr)],
133 ) -> Result<(), BleHostError<C::Error>>
134 where
135 C: ControllerCmdSync<LeClearFilterAcceptList> + ControllerCmdSync<LeAddDeviceToFilterAcceptList>,
136 {
137 let host = &self.stack.host;
138 host.command(LeClearFilterAcceptList::new()).await?;
139 for entry in filter_accept_list {
140 host.command(LeAddDeviceToFilterAcceptList::new(entry.0, *entry.1))
141 .await?;
142 }
143 Ok(())
144 }
145}
146
147pub(crate) fn create_phy_params<P: Copy>(phy: P, phys: PhySet) -> PhyParams<P> {
148 let phy_params: PhyParams<P> = PhyParams {
149 le_1m_phy: match phys {
150 PhySet::M1 | PhySet::M1M2 | PhySet::M1Coded | PhySet::M1M2Coded => Some(phy),
151 _ => None,
152 },
153 le_2m_phy: match phys {
154 PhySet::M2 | PhySet::M1M2 | PhySet::M2Coded | PhySet::M1M2Coded => Some(phy),
155 _ => None,
156 },
157 le_coded_phy: match phys {
158 PhySet::M2Coded | PhySet::Coded | PhySet::M1Coded | PhySet::M1M2Coded => Some(phy),
159 _ => None,
160 },
161 };
162 phy_params
163}