1 // Copyright 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::ffi;
16 use crate::packets::{hci, llcp};
17 use pdl_runtime::Packet as _;
18 use std::collections::HashMap;
19 use std::convert::{TryFrom, TryInto};
20
21 #[derive(Clone, Copy, Debug)]
22 enum IsoDataPath {
23 Hci,
24 }
25
26 // Description of CIS configuration parameters:
27 //
28 // - ISO_Interval (multiple of 1.25ms)
29 // ISO_Interval is the time between the CIS anchor points of adjacent CIS
30 // events. ISO_Interval is equal for all CISes in a CIG.
31 // - Sub_Interval (ms)
32 // Sub_Interval is the time between start of two consecutive
33 // subevents of a CIS
34 // - NSE
35 // NSE is the maximum number of subevents in each CIS event.
36 // - BN (Burst Number)
37 // BN is the number of payloads expected in each CIS event.
38 // Each CIS event has NSE - BN retransmission slots.
39 // - FT (Flush Timeout)
40 // The Flush Timeout (FT) parameter is the maximum number of CIS events
41 // that may be used to transmit (and retransmit) a given payload
42 // - Framed
43 // Framed indicates whether the CIS carries framed or unframed data; the
44 // value shall be the same in both directions.
45 // Unframed PDUs shall only be used when the ISO_Interval is equal to
46 // or an integer multiple of the SDU_Interval.
47 //
48 // For the purpose of emulating the CISes, the intervals between CIS subevents,
49 // and different CIS events are ignored, leading to a number of approximations:
50 //
51 // - CIG_Sync_Delay = ISO_Interval
52 // The CIG synchronization point is the same as the next event anchor
53 // - CIS_Sync_Delay = ISO_Interval
54 // All CISes start on the CIG anchor.
55 // - BN = NSE
56 // Unless otherwise specified in test commands.
57 // No retransmission slots.
58 // - FT = 1
59 // All PDUs are sent within one event.
60 // - Sub_Interval = ISO_Interval / NSE
61
62 #[allow(non_camel_case_types)]
63 type microseconds = u32;
64
65 #[allow(non_camel_case_types)]
66 type slots = u16;
67
68 /// CIG configuration.
69 #[derive(Clone, Debug, Default)]
70 struct CigConfig {
71 // CIG parameters.
72 iso_interval: slots,
73 sdu_interval_c_to_p: microseconds,
74 sdu_interval_p_to_c: microseconds,
75 ft_c_to_p: u8,
76 ft_p_to_c: u8,
77 framed: bool,
78 // True when the CIG can still be configured.
79 configurable: bool,
80 }
81
82 /// CIS configuration.
83 #[derive(Clone, Debug, Default)]
84 struct CisConfig {
85 // CIS parameters.
86 // cf Vol 6, Part B § 4.5.13.1 CIS parameters.
87 max_sdu_c_to_p: u16,
88 max_sdu_p_to_c: u16,
89 phy_c_to_p: u8,
90 phy_p_to_c: u8,
91 nse: Option<u8>,
92 bn_c_to_p: Option<u8>,
93 bn_p_to_c: Option<u8>,
94 max_pdu_c_to_p: Option<u16>,
95 max_pdu_p_to_c: Option<u16>,
96 }
97
98 /// CIG configuration.
99 #[derive(Clone, Debug, Default)]
100 struct CisRequest {
101 cig_id: u8,
102 cis_id: u8,
103 acl_connection_handle: u16,
104 cis_connection_handle: u16,
105 }
106
107 #[derive(Debug, Clone, PartialEq, Eq)]
108 pub enum CisState {
109 Configuration,
110 PendingRsp,
111 PendingAccept,
112 PendingInd,
113 Connected,
114 }
115
116 /// Full CIS configuration parameters, evaluated when the CIS is created
117 /// or established.
118 #[derive(Clone, Debug)]
119 struct CisParameters {
120 cig_sync_delay: microseconds,
121 cis_sync_delay: microseconds,
122 phy_c_to_p: u8,
123 phy_p_to_c: u8,
124 nse: u8,
125 bn_c_to_p: u8,
126 bn_p_to_c: u8,
127 ft_c_to_p: u8,
128 ft_p_to_c: u8,
129 max_pdu_c_to_p: u16,
130 max_pdu_p_to_c: u16,
131 max_sdu_c_to_p: u16,
132 max_sdu_p_to_c: u16,
133 sdu_interval_c_to_p: microseconds,
134 sdu_interval_p_to_c: microseconds,
135 iso_interval: slots,
136 sub_interval: microseconds,
137 framed: bool,
138 }
139
140 impl CisParameters {
new(cig_config: &CigConfig, cis_config: &CisConfig) -> CisParameters141 fn new(cig_config: &CigConfig, cis_config: &CisConfig) -> CisParameters {
142 let bn_c_to_p: u8 = cis_config
143 .bn_c_to_p
144 .unwrap_or_else(|| ((cis_config.max_sdu_c_to_p + 250) / 251).try_into().unwrap());
145 let bn_p_to_c: u8 = cis_config
146 .bn_p_to_c
147 .unwrap_or_else(|| ((cis_config.max_sdu_p_to_c + 250) / 251).try_into().unwrap());
148 let nse = cis_config.nse.unwrap_or(std::cmp::max(bn_c_to_p, bn_p_to_c));
149 let max_pdu_c_to_p = cis_config.max_pdu_c_to_p.unwrap_or(251);
150 let max_pdu_p_to_c = cis_config.max_pdu_p_to_c.unwrap_or(251);
151 let sub_interval = (cig_config.iso_interval as u32 * 1250) / nse as u32;
152
153 // Select one phy in the enabled mask with
154 // the priority LE 2M > LE 1M > LE Coded.
155 fn select_phy(phys: u8) -> u8 {
156 match phys {
157 0x2 | 0x3 | 0x6 | 0x7 => 0x2,
158 0x1 | 0x5 => 0x1,
159 0x4 => 0x4,
160 0x0 => panic!(), // Not allowed by parameter LE Set Cig Parameters validation
161 _ => unreachable!(),
162 }
163 }
164
165 let phy_c_to_p = select_phy(cis_config.phy_c_to_p);
166 let phy_p_to_c = select_phy(cis_config.phy_p_to_c);
167
168 CisParameters {
169 cig_sync_delay: cig_config.iso_interval as u32 * 1250,
170 cis_sync_delay: cig_config.iso_interval as u32 * 1250,
171 phy_c_to_p,
172 phy_p_to_c,
173 nse,
174 bn_c_to_p,
175 bn_p_to_c,
176 ft_c_to_p: cig_config.ft_c_to_p,
177 ft_p_to_c: cig_config.ft_p_to_c,
178 max_pdu_c_to_p,
179 max_pdu_p_to_c,
180 max_sdu_c_to_p: cis_config.max_sdu_c_to_p,
181 max_sdu_p_to_c: cis_config.max_sdu_p_to_c,
182 sdu_interval_c_to_p: cig_config.sdu_interval_c_to_p,
183 sdu_interval_p_to_c: cig_config.sdu_interval_p_to_c,
184 iso_interval: cig_config.iso_interval,
185 framed: cig_config.framed,
186 sub_interval,
187 }
188 }
189
phy_c_to_p(&self) -> hci::SecondaryPhyType190 fn phy_c_to_p(&self) -> hci::SecondaryPhyType {
191 match self.phy_c_to_p {
192 0x1 => hci::SecondaryPhyType::Le1m,
193 0x2 => hci::SecondaryPhyType::Le2m,
194 0x4 => hci::SecondaryPhyType::LeCoded,
195 _ => unreachable!(),
196 }
197 }
198
phy_p_to_c(&self) -> hci::SecondaryPhyType199 fn phy_p_to_c(&self) -> hci::SecondaryPhyType {
200 match self.phy_p_to_c {
201 0x1 => hci::SecondaryPhyType::Le1m,
202 0x2 => hci::SecondaryPhyType::Le2m,
203 0x4 => hci::SecondaryPhyType::LeCoded,
204 _ => unreachable!(),
205 }
206 }
207
transport_latency_c_to_p(&self) -> microseconds208 fn transport_latency_c_to_p(&self) -> microseconds {
209 transport_latency(
210 self.cig_sync_delay,
211 self.iso_interval,
212 self.ft_c_to_p,
213 self.sdu_interval_c_to_p,
214 self.framed,
215 )
216 }
217
transport_latency_p_to_c(&self) -> microseconds218 fn transport_latency_p_to_c(&self) -> microseconds {
219 transport_latency(
220 self.cig_sync_delay,
221 self.iso_interval,
222 self.ft_p_to_c,
223 self.sdu_interval_p_to_c,
224 self.framed,
225 )
226 }
227 }
228
229 /// Established CIS configuration.
230 #[derive(Clone)]
231 pub struct Cis {
232 pub cig_id: u8,
233 pub cis_id: u8,
234 pub role: hci::Role,
235 pub cis_connection_handle: u16,
236 pub acl_connection_handle: Option<u16>,
237 pub state: CisState,
238 parameters: Option<CisParameters>,
239 iso_data_path_c_to_p: Option<IsoDataPath>,
240 iso_data_path_p_to_c: Option<IsoDataPath>,
241 }
242
243 impl Cis {
max_sdu_tx(&self) -> Option<u16>244 pub fn max_sdu_tx(&self) -> Option<u16> {
245 self.parameters.as_ref().map(|parameters| match self.role {
246 hci::Role::Central => parameters.max_sdu_c_to_p,
247 hci::Role::Peripheral => parameters.max_sdu_p_to_c,
248 })
249 }
250 }
251
252 /// ISO manager state.
253 pub struct IsoManager {
254 /// CIG configuration.
255 cig_config: HashMap<u8, CigConfig>,
256 /// CIS configuration.
257 cis_config: HashMap<(u8, u8), CisConfig>,
258 /// Mapping from ACL connection handle to connection role.
259 acl_connections: HashMap<u16, hci::Role>,
260 /// Mapping from CIS connection handle to a CIS connection
261 /// opened as central (initiated a LL_CIS_REQ) or peripheral
262 /// (accepted with LL_CIS_RSP). Central connections are in the
263 /// configuration state as long as HCI LE Create CIS has not been
264 /// invoked.
265 cis_connections: HashMap<u16, Cis>,
266 /// Pending CIS connection requests, initiated from the command
267 /// HCI LE Create CIS.
268 cis_connection_requests: Vec<CisRequest>,
269 /// Link layer callbacks.
270 ops: ffi::ControllerOps,
271 }
272
273 impl IsoManager {
new(ops: ffi::ControllerOps) -> IsoManager274 pub fn new(ops: ffi::ControllerOps) -> IsoManager {
275 IsoManager {
276 ops,
277 cig_config: Default::default(),
278 cis_config: Default::default(),
279 acl_connections: Default::default(),
280 cis_connections: Default::default(),
281 cis_connection_requests: Default::default(),
282 }
283 }
284
add_acl_connection(&mut self, acl_connection_handle: u16, role: hci::Role)285 pub fn add_acl_connection(&mut self, acl_connection_handle: u16, role: hci::Role) {
286 self.acl_connections.insert(acl_connection_handle, role);
287 }
288
remove_acl_connection(&mut self, acl_connection_handle: u16)289 pub fn remove_acl_connection(&mut self, acl_connection_handle: u16) {
290 self.acl_connections.remove(&acl_connection_handle);
291 }
292
293 // Returns the first unused handle in the range 0xe00..0xefe.
new_cis_connection_handle(&self) -> u16294 fn new_cis_connection_handle(&self) -> u16 {
295 (0xe00..0xefe).find(|handle| !self.cis_connections.contains_key(handle)).unwrap()
296 }
297
298 // Insert a new CIS connection, optionally allocating an handle for the
299 // selected CIG_Id, CIS_Id, Role triplet. Returns the handle for the connection.
new_cis_connection(&mut self, cig_id: u8, cis_id: u8, role: hci::Role) -> u16300 fn new_cis_connection(&mut self, cig_id: u8, cis_id: u8, role: hci::Role) -> u16 {
301 let cis_connection_handle = self
302 .cis_connections
303 .values()
304 .filter_map(|cis| {
305 (cis.cig_id == cig_id && cis.cis_id == cis_id && cis.role == role)
306 .then_some(cis.cis_connection_handle)
307 })
308 .next();
309 cis_connection_handle.unwrap_or_else(|| {
310 let cis_connection_handle = self.new_cis_connection_handle();
311 self.cis_connections.insert(
312 cis_connection_handle,
313 Cis {
314 cig_id,
315 cis_id,
316 role,
317 cis_connection_handle,
318 state: CisState::Configuration,
319 acl_connection_handle: None,
320 parameters: None,
321 iso_data_path_c_to_p: None,
322 iso_data_path_p_to_c: None,
323 },
324 );
325 cis_connection_handle
326 })
327 }
328
send_hci_event<E: Into<hci::Event>>(&self, event: E)329 fn send_hci_event<E: Into<hci::Event>>(&self, event: E) {
330 self.ops.send_hci_event(&event.into().encode_to_vec().unwrap())
331 }
332
send_llcp_packet<P: Into<llcp::LlcpPacket>>(&self, acl_connection_handle: u16, packet: P)333 fn send_llcp_packet<P: Into<llcp::LlcpPacket>>(&self, acl_connection_handle: u16, packet: P) {
334 self.ops.send_llcp_packet(acl_connection_handle, &packet.into().encode_to_vec().unwrap())
335 }
336
get_le_features(&self) -> u64337 fn get_le_features(&self) -> u64 {
338 self.ops.get_le_features()
339 }
340
supported_phys(&self) -> u8341 fn supported_phys(&self) -> u8 {
342 let le_features = self.get_le_features();
343 let mut supported_phys = 0x1;
344 if (le_features & hci::LLFeaturesBits::Le2mPhy as u64) != 0 {
345 supported_phys |= 0x2;
346 }
347 if (le_features & hci::LLFeaturesBits::LeCodedPhy as u64) != 0 {
348 supported_phys |= 0x4;
349 }
350 supported_phys
351 }
352
connected_isochronous_stream_host_support(&self) -> bool353 fn connected_isochronous_stream_host_support(&self) -> bool {
354 (self.get_le_features() & hci::LLFeaturesBits::ConnectedIsochronousStreamHostSupport as u64)
355 != 0
356 }
357
get_cis_connection_handle<F>(&self, predicate: F) -> Option<u16> where F: Fn(&Cis) -> bool,358 pub fn get_cis_connection_handle<F>(&self, predicate: F) -> Option<u16>
359 where
360 F: Fn(&Cis) -> bool,
361 {
362 self.cis_connections
363 .iter()
364 .filter(|(_, cis)| predicate(cis))
365 .map(|(handle, _)| handle)
366 .next()
367 .cloned()
368 }
369
get_cis(&self, cis_connection_handle: u16) -> Option<&Cis>370 pub fn get_cis(&self, cis_connection_handle: u16) -> Option<&Cis> {
371 self.cis_connections.get(&cis_connection_handle)
372 }
373
374 /// Start the next CIS connection request, if any.
deque_cis_connection_request(&mut self)375 fn deque_cis_connection_request(&mut self) {
376 if let Some(request) = self.cis_connection_requests.pop() {
377 let cis_config = self.cis_config.get(&(request.cig_id, request.cis_id)).unwrap();
378 let cig_config = self.cig_config.get(&request.cig_id).unwrap();
379 let parameters = CisParameters::new(cig_config, cis_config);
380
381 self.send_llcp_packet(
382 request.acl_connection_handle,
383 llcp::CisReqBuilder {
384 cig_id: request.cig_id,
385 cis_id: request.cis_id,
386 phy_c_to_p: parameters.phy_c_to_p,
387 phy_p_to_c: parameters.phy_p_to_c,
388 framed: cig_config.framed as u8,
389 max_sdu_c_to_p: cis_config.max_sdu_c_to_p,
390 max_sdu_p_to_c: cis_config.max_sdu_p_to_c,
391 sdu_interval_c_to_p: cig_config.sdu_interval_c_to_p,
392 sdu_interval_p_to_c: cig_config.sdu_interval_p_to_c,
393 max_pdu_c_to_p: parameters.max_pdu_c_to_p,
394 max_pdu_p_to_c: parameters.max_pdu_p_to_c,
395 nse: parameters.nse,
396 sub_interval: parameters.sub_interval,
397 bn_c_to_p: parameters.bn_c_to_p,
398 bn_p_to_c: parameters.bn_p_to_c,
399 ft_c_to_p: cig_config.ft_c_to_p,
400 ft_p_to_c: cig_config.ft_p_to_c,
401 iso_interval: cig_config.iso_interval,
402 cis_offset_min: 0,
403 cis_offset_max: 0,
404 conn_event_count: 0,
405 },
406 );
407
408 let cis = self.cis_connections.get_mut(&request.cis_connection_handle).unwrap();
409 cis.acl_connection_handle = Some(request.acl_connection_handle);
410 cis.state = CisState::PendingRsp;
411 cis.parameters = Some(parameters);
412 }
413 }
414
hci_le_set_cig_parameters(&mut self, packet: hci::LeSetCigParameters)415 pub fn hci_le_set_cig_parameters(&mut self, packet: hci::LeSetCigParameters) {
416 let cig_id: u8 = packet.get_cig_id();
417 let sdu_interval_c_to_p: u32 = packet.get_sdu_interval_c_to_p();
418 let sdu_interval_p_to_c: u32 = packet.get_sdu_interval_p_to_c();
419 let framed: bool = packet.get_framing() == hci::Enable::Enabled;
420 let max_transport_latency_c_to_p: u16 = packet.get_max_transport_latency_c_to_p();
421 let max_transport_latency_p_to_c: u16 = packet.get_max_transport_latency_p_to_c();
422 let cis_config: &[hci::CisParametersConfig] = packet.get_cis_config();
423
424 let command_complete = |status| hci::LeSetCigParametersCompleteBuilder {
425 status,
426 cig_id,
427 connection_handle: vec![],
428 num_hci_command_packets: 1,
429 };
430
431 // If the Host issues this command when the CIG is not in the configurable
432 // state, the Controller shall return the error code
433 // Command Disallowed (0x0C).
434 if !self.cig_config.get(&cig_id).map(|cig| cig.configurable).unwrap_or(true) {
435 println!("CIG ({}) is no longer in the configurable state", cig_id);
436 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
437 }
438
439 for cis in cis_config {
440 let cis_id = cis.cis_id;
441 let cis_connection = self.cis_connections.values().find(|cis| {
442 cis.cis_id == cis_id && cis.cig_id == cig_id && cis.role == hci::Role::Central
443 });
444 let (iso_data_path_c_to_p, iso_data_path_p_to_c) = cis_connection
445 .map(|cis| (cis.iso_data_path_c_to_p, cis.iso_data_path_p_to_c))
446 .unwrap_or((None, None));
447
448 // The PHY_C_To_P[i] parameter identifies which PHY to use for transmission
449 // from the Central to the Peripheral. The Host shall set at least one bit in this
450 // parameter and the Controller shall pick a PHY from the bits that are set.
451 if cis.phy_c_to_p == 0 || cis.phy_p_to_c == 0 {
452 println!(
453 "CIS ({}) does not configure PHys ({:x}, {:x})",
454 cis.cis_id, cis.phy_c_to_p, cis.phy_p_to_c
455 );
456 return self.send_hci_event(command_complete(
457 hci::ErrorCode::UnsupportedFeatureOrParameterValue,
458 ));
459 }
460
461 // If the Host sets, in the PHY_C_To_P[i] or PHY_P_To_C[i] parameters, a bit
462 // for a PHY that the Controller does not support, including a bit that is
463 // reserved for future use, the Controller shall return the error code
464 // Unsupported Feature or Parameter Value (0x11).
465 if (cis.phy_c_to_p & !self.supported_phys()) != 0
466 || (cis.phy_p_to_c & !self.supported_phys()) != 0
467 {
468 println!(
469 "CIS ({}) configures unsupported PHYs ({:x}, {:x})",
470 cis.cis_id, cis.phy_c_to_p, cis.phy_p_to_c
471 );
472 return self.send_hci_event(command_complete(
473 hci::ErrorCode::UnsupportedFeatureOrParameterValue,
474 ));
475 }
476
477 // If a CIS configuration that is being modified has a data path set in
478 // the Central to Peripheral direction and the Host has specified
479 // that Max_SDU_C_To_P[i] shall be set to zero, the Controller shall
480 // return the error code Command Disallowed (0x0C).
481 if cis.max_sdu_c_to_p == 0 && iso_data_path_c_to_p.is_some() {
482 println!(
483 "CIS ({}) has a data path for C->P but Max_SDU_C_To_P is zero",
484 cis.cis_id
485 );
486 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
487 }
488
489 // If a CIS configuration that is being modified has a data path set in the
490 // Peripheral to Central direction and the Host has specified that
491 // Max_SDU_P_To_C[i] shall be set to zero, the Controller shall return
492 // the error code Command Disallowed (0x0C).
493 if cis.max_sdu_p_to_c == 0 && iso_data_path_p_to_c.is_some() {
494 println!(
495 "CIS ({}) has a data path for P->C but Max_SDU_P_To_C is zero",
496 cis.cis_id
497 );
498 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
499 }
500
501 if cis.max_sdu_c_to_p > 0xfff || cis.max_sdu_p_to_c > 0xfff {
502 println!(
503 "invalid Max_SDU C->P ({}) or Max_SDU P->C ({}) for CIS ({})",
504 cis.max_sdu_c_to_p, cis.max_sdu_p_to_c, cis_id
505 );
506 return self
507 .send_hci_event(command_complete(hci::ErrorCode::InvalidHciCommandParameters));
508 }
509 }
510
511 let configures_c_to_p = cis_config.iter().any(|cis| cis.max_sdu_c_to_p != 0)
512 || self.cis_config.iter().any(|(key, cis)| {
513 key.0 == cig_id
514 && cis.max_sdu_c_to_p != 0
515 && cis_config.iter().all(|new_cis| new_cis.cis_id != key.1)
516 });
517 let configures_p_to_c = cis_config.iter().any(|cis| cis.max_sdu_p_to_c != 0)
518 || self.cis_config.iter().any(|(key, cis)| {
519 key.0 == cig_id
520 && cis.max_sdu_p_to_c != 0
521 && cis_config.iter().all(|new_cis| new_cis.cis_id != key.1)
522 });
523
524 // If the Host specifies an invalid combination of CIS parameters, the
525 // Controller shall return the error code Unsupported Feature or
526 // Parameter Value (0x11).
527 if (configures_c_to_p && !(0xff..=0xf_ffff).contains(&sdu_interval_c_to_p))
528 || (configures_p_to_c && !(0xff..=0xf_ffff).contains(&sdu_interval_p_to_c))
529 {
530 println!(
531 "invalid SDU_Interval C->P ({}) or SDU_Interval P->C ({})",
532 sdu_interval_c_to_p, sdu_interval_p_to_c
533 );
534 return self
535 .send_hci_event(command_complete(hci::ErrorCode::InvalidHciCommandParameters));
536 }
537 if (configures_c_to_p && !(0x5..=0xfa0).contains(&max_transport_latency_c_to_p))
538 || (configures_p_to_c && !(0x5..=0xfa0).contains(&max_transport_latency_p_to_c))
539 {
540 println!(
541 "invalid Max_Transport_Latency C->P ({}) or Max_Transport_Latency P->C ({})",
542 max_transport_latency_c_to_p, max_transport_latency_p_to_c
543 );
544 return self
545 .send_hci_event(command_complete(hci::ErrorCode::InvalidHciCommandParameters));
546 }
547
548 let Some(iso_interval) = iso_interval(
549 sdu_interval_c_to_p,
550 sdu_interval_p_to_c,
551 framed,
552 max_transport_latency_c_to_p as u32 * 1000,
553 max_transport_latency_p_to_c as u32 * 1000,
554 ) else {
555 println!(
556 "ISO_Interval cannot be chosen that fulfills the requirement from the CIG parameters");
557 return self.send_hci_event(command_complete(
558 hci::ErrorCode::UnsupportedFeatureOrParameterValue,
559 ));
560 };
561
562 // If the Status return parameter is non-zero, then the state of the CIG
563 // and its CIS configurations shall not be changed by the command.
564 // If the CIG did not already exist, it shall not be created.
565 let cig = self.cig_config.entry(cig_id).or_default();
566 let mut cis_connection_handles = vec![];
567 cig.iso_interval = iso_interval;
568 cig.sdu_interval_c_to_p = sdu_interval_c_to_p;
569 cig.sdu_interval_p_to_c = sdu_interval_p_to_c;
570 cig.ft_c_to_p = 1;
571 cig.ft_p_to_c = 1;
572 cig.framed = framed;
573
574 for cis_config in cis_config {
575 self.cis_config.insert(
576 (cig_id, cis_config.cis_id),
577 CisConfig {
578 max_sdu_c_to_p: cis_config.max_sdu_c_to_p,
579 max_sdu_p_to_c: cis_config.max_sdu_p_to_c,
580 phy_c_to_p: cis_config.phy_c_to_p,
581 phy_p_to_c: cis_config.phy_p_to_c,
582 nse: None,
583 bn_c_to_p: None,
584 bn_p_to_c: None,
585 max_pdu_c_to_p: None,
586 max_pdu_p_to_c: None,
587 },
588 );
589
590 let cis_connection_handle =
591 self.new_cis_connection(cig_id, cis_config.cis_id, hci::Role::Central);
592 cis_connection_handles.push(cis_connection_handle);
593 }
594
595 self.send_hci_event(hci::LeSetCigParametersCompleteBuilder {
596 status: hci::ErrorCode::Success,
597 cig_id,
598 connection_handle: cis_connection_handles,
599 num_hci_command_packets: 1,
600 })
601 }
602
hci_le_set_cig_parameters_test(&mut self, packet: hci::LeSetCigParametersTest)603 pub fn hci_le_set_cig_parameters_test(&mut self, packet: hci::LeSetCigParametersTest) {
604 let cig_id: u8 = packet.get_cig_id();
605 let sdu_interval_c_to_p: u32 = packet.get_sdu_interval_c_to_p();
606 let sdu_interval_p_to_c: u32 = packet.get_sdu_interval_p_to_c();
607 let ft_c_to_p: u8 = packet.get_ft_c_to_p();
608 let ft_p_to_c: u8 = packet.get_ft_p_to_c();
609 let iso_interval: u16 = packet.get_iso_interval();
610 let framed: bool = packet.get_framing() == hci::Enable::Enabled;
611 let cis_config: &[hci::LeCisParametersTestConfig] = packet.get_cis_config();
612
613 let command_complete = |status| hci::LeSetCigParametersTestCompleteBuilder {
614 status,
615 cig_id,
616 connection_handle: vec![],
617 num_hci_command_packets: 1,
618 };
619
620 // If the Host issues this command when the CIG is not in the configurable
621 // state, the Controller shall return the error code
622 // Command Disallowed (0x0C).
623 if !self.cig_config.get(&cig_id).map(|cig| cig.configurable).unwrap_or(true) {
624 println!("CIG ({}) is no longer in the configurable state", cig_id);
625 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
626 }
627
628 for cis in cis_config {
629 let cis_id = cis.cis_id;
630 let cis_connection = self.cis_connections.values().find(|cis| {
631 cis.cis_id == cis_id && cis.cig_id == cig_id && cis.role == hci::Role::Central
632 });
633 let (iso_data_path_c_to_p, iso_data_path_p_to_c) = cis_connection
634 .map(|cis| (cis.iso_data_path_c_to_p, cis.iso_data_path_p_to_c))
635 .unwrap_or((None, None));
636
637 // The PHY_C_To_P[i] parameter identifies which PHY to use for transmission
638 // from the Central to the Peripheral. The Host shall set at least one bit in this
639 // parameter and the Controller shall pick a PHY from the bits that are set.
640 if cis.phy_c_to_p == 0 || cis.phy_p_to_c == 0 {
641 println!(
642 "CIS ({}) does not configure PHys ({:x}, {:x})",
643 cis.cis_id, cis.phy_c_to_p, cis.phy_p_to_c
644 );
645 return self.send_hci_event(command_complete(
646 hci::ErrorCode::UnsupportedFeatureOrParameterValue,
647 ));
648 }
649
650 // If the Host sets, in the PHY_C_To_P[i] or PHY_P_To_C[i] parameters, a bit
651 // for a PHY that the Controller does not support, including a bit that is
652 // reserved for future use, the Controller shall return the error code
653 // Unsupported Feature or Parameter Value (0x11).
654 if (cis.phy_c_to_p & !self.supported_phys()) != 0
655 || (cis.phy_p_to_c & !self.supported_phys()) != 0
656 {
657 println!(
658 "CIS ({}) configures unsupported PHYs ({:x}, {:x})",
659 cis.cis_id, cis.phy_c_to_p, cis.phy_p_to_c
660 );
661 return self.send_hci_event(command_complete(
662 hci::ErrorCode::UnsupportedFeatureOrParameterValue,
663 ));
664 }
665
666 // If a CIS configuration that is being modified has a data path set in
667 // the Central to Peripheral direction and the Host has specified
668 // that Max_SDU_C_To_P[i] shall be set to zero, the Controller shall
669 // return the error code Command Disallowed (0x0C).
670 if cis.max_sdu_c_to_p == 0 && iso_data_path_c_to_p.is_some() {
671 println!(
672 "CIS ({}) has a data path for C->P but Max_SDU_C_To_P is zero",
673 cis.cis_id
674 );
675 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
676 }
677
678 // If a CIS configuration that is being modified has a data path set in the
679 // Peripheral to Central direction and the Host has specified that
680 // Max_SDU_P_To_C[i] shall be set to zero, the Controller shall return
681 // the error code Command Disallowed (0x0C).
682 if cis.max_sdu_p_to_c == 0 && iso_data_path_p_to_c.is_some() {
683 println!(
684 "CIS ({}) has a data path for P->C but Max_SDU_P_To_C is zero",
685 cis.cis_id
686 );
687 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
688 }
689
690 if cis.max_sdu_c_to_p > 0xfff || cis.max_sdu_p_to_c > 0xfff {
691 println!(
692 "invalid Max_SDU C->P ({}) or Max_SDU P->C ({}) for CIS ({})",
693 cis.max_sdu_c_to_p, cis.max_sdu_p_to_c, cis_id
694 );
695 return self
696 .send_hci_event(command_complete(hci::ErrorCode::InvalidHciCommandParameters));
697 }
698 }
699
700 let configures_c_to_p = cis_config.iter().any(|cis| cis.max_sdu_c_to_p != 0)
701 || self.cis_config.iter().any(|(key, cis)| {
702 key.0 == cig_id
703 && cis.max_sdu_c_to_p != 0
704 && cis_config.iter().all(|new_cis| new_cis.cis_id != key.1)
705 });
706 let configures_p_to_c = cis_config.iter().any(|cis| cis.max_sdu_p_to_c != 0)
707 || self.cis_config.iter().any(|(key, cis)| {
708 key.0 == cig_id
709 && cis.max_sdu_p_to_c != 0
710 && cis_config.iter().all(|new_cis| new_cis.cis_id != key.1)
711 });
712
713 // If the Host specifies an invalid combination of CIS parameters, the
714 // Controller shall return the error code Unsupported Feature or
715 // Parameter Value (0x11).
716 if (configures_c_to_p && !(0xff..=0xf_ffff).contains(&sdu_interval_c_to_p))
717 || (configures_p_to_c && !(0xff..=0xf_ffff).contains(&sdu_interval_p_to_c))
718 {
719 println!(
720 "invalid SDU_Interval C->P ({}) or SDU_Interval P->C ({})",
721 sdu_interval_c_to_p, sdu_interval_p_to_c
722 );
723 return self
724 .send_hci_event(command_complete(hci::ErrorCode::InvalidHciCommandParameters));
725 }
726
727 // If the Status return parameter is non-zero, then the state of the CIG
728 // and its CIS configurations shall not be changed by the command.
729 // If the CIG did not already exist, it shall not be created.
730 let cig = self.cig_config.entry(cig_id).or_default();
731 let mut cis_connection_handles = vec![];
732 cig.iso_interval = iso_interval;
733 cig.sdu_interval_c_to_p = sdu_interval_c_to_p;
734 cig.sdu_interval_p_to_c = sdu_interval_p_to_c;
735 cig.ft_c_to_p = ft_c_to_p;
736 cig.ft_p_to_c = ft_p_to_c;
737 cig.framed = framed;
738
739 for cis_config in cis_config {
740 self.cis_config.insert(
741 (cig_id, cis_config.cis_id),
742 CisConfig {
743 max_sdu_c_to_p: cis_config.max_sdu_c_to_p,
744 max_sdu_p_to_c: cis_config.max_sdu_p_to_c,
745 phy_c_to_p: cis_config.phy_c_to_p,
746 phy_p_to_c: cis_config.phy_p_to_c,
747 nse: Some(cis_config.nse),
748 bn_c_to_p: Some(cis_config.bn_c_to_p),
749 bn_p_to_c: Some(cis_config.bn_p_to_c),
750 max_pdu_c_to_p: Some(cis_config.max_pdu_c_to_p),
751 max_pdu_p_to_c: Some(cis_config.max_pdu_p_to_c),
752 },
753 );
754
755 let cis_connection_handle =
756 self.new_cis_connection(cig_id, cis_config.cis_id, hci::Role::Central);
757 cis_connection_handles.push(cis_connection_handle);
758 }
759
760 self.send_hci_event(hci::LeSetCigParametersTestCompleteBuilder {
761 status: hci::ErrorCode::Success,
762 cig_id,
763 connection_handle: cis_connection_handles,
764 num_hci_command_packets: 1,
765 })
766 }
767
hci_le_remove_cig(&mut self, packet: hci::LeRemoveCig)768 pub fn hci_le_remove_cig(&mut self, packet: hci::LeRemoveCig) {
769 let cig_id: u8 = packet.get_cig_id();
770
771 let command_complete =
772 |status| hci::LeRemoveCigCompleteBuilder { status, cig_id, num_hci_command_packets: 1 };
773
774 // If the Host issues this command with a CIG_ID that does not exist, the
775 // Controller shall return the error code Unknown Connection Identifier (0x02).
776 if !self.cig_config.contains_key(&cig_id) {
777 println!("CIG ({}) does not exist", cig_id);
778 return self.send_hci_event(command_complete(hci::ErrorCode::UnknownConnection));
779 }
780
781 // If the Host tries to remove a CIG which is in the active state,
782 // then the Controller shall return the error code
783 // Command Disallowed (0x0C).
784 if self.cis_connections.values().any(|cis| {
785 cis.role == hci::Role::Central
786 && cis.cig_id == cig_id
787 && cis.state != CisState::Configuration
788 }) {
789 println!("CIG ({}) cannot be removed as it is in active state", cig_id);
790 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
791 }
792
793 // Clear the CIG configuration.
794 self.cig_config.remove(&cig_id);
795 self.cis_config.retain(|key, _| key.0 != cig_id);
796
797 // Remove the CIS connections.
798 self.cis_connections
799 .retain(|_, cis| cis.role != hci::Role::Central || cis.cig_id != cig_id);
800
801 self.send_hci_event(command_complete(hci::ErrorCode::Success))
802 }
803
hci_le_create_cis(&mut self, packet: hci::LeCreateCis)804 pub fn hci_le_create_cis(&mut self, packet: hci::LeCreateCis) {
805 let cis_config: &[hci::LeCreateCisConfig] = packet.get_cis_config();
806 let mut cis_connection_requests: Vec<CisRequest> = vec![];
807
808 let command_status =
809 |status| hci::LeCreateCisStatusBuilder { status, num_hci_command_packets: 1 };
810
811 for cis_config in cis_config {
812 match self.acl_connections.get(&cis_config.acl_connection_handle) {
813 // If any ACL_Connection_Handle[i] is not the handle of an existing ACL
814 // connection, the Controller shall return the error code Unknown Connection
815 // Identifier (0x02).
816 None => {
817 println!(
818 "cannot create LE CIS with unknown ACL connection handle {}",
819 cis_config.acl_connection_handle
820 );
821 return self.send_hci_event(command_status(hci::ErrorCode::UnknownConnection));
822 }
823 // If the Host issues this command on an ACL_Connection_Handle where the
824 // Controller is the Peripheral, the Controller shall return the error code
825 // Command Disallowed (0x0C).
826 Some(hci::Role::Peripheral) => {
827 println!(
828 "the ACL connection handle {} is for a peripheral connection",
829 cis_config.acl_connection_handle
830 );
831 return self.send_hci_event(command_status(
832 hci::ErrorCode::InvalidHciCommandParameters,
833 ));
834 }
835 Some(hci::Role::Central) => (),
836 }
837
838 // If any CIS_Connection_Handle[i] is not the handle of a CIS or CIS
839 // configuration, the Controller shall return the error code Unknown Connection
840 // Identifier (0x02).
841 let Some(cis) = self.cis_connections.get(&cis_config.cis_connection_handle) else {
842 println!(
843 "cannot create LE CIS with unknown CIS connection handle {}",
844 cis_config.cis_connection_handle
845 );
846 return self.send_hci_event(command_status(hci::ErrorCode::UnknownConnection));
847 };
848
849 // If the Host attempts to create a CIS that has already been created, the
850 // Controller shall return the error code Connection Already Exists (0x0B).
851 if cis.state != CisState::Configuration {
852 println!(
853 "cannot create LE CIS with CIS connection handle {} as it is already connected",
854 cis_config.cis_connection_handle
855 );
856 return self
857 .send_hci_event(command_status(hci::ErrorCode::ConnectionAlreadyExists));
858 }
859
860 // If two different elements of the CIS_Connection_Handle arrayed parameter
861 // identify the same CIS, the Controller shall return the error code
862 // Invalid HCI Command Parameters (0x12).
863 if cis_connection_requests
864 .iter()
865 .any(|request| request.cis_connection_handle == cis_config.cis_connection_handle)
866 {
867 println!(
868 "the CIS connection handle {} is requested twice",
869 cis_config.cis_connection_handle
870 );
871 return self
872 .send_hci_event(command_status(hci::ErrorCode::InvalidHciCommandParameters));
873 }
874
875 cis_connection_requests.push(CisRequest {
876 cis_connection_handle: cis_config.cis_connection_handle,
877 acl_connection_handle: cis_config.acl_connection_handle,
878 cig_id: cis.cig_id,
879 cis_id: cis.cis_id,
880 });
881 }
882
883 // If the Host issues this command before all the HCI_LE_CIS_Established
884 // events from the previous use of the command have been generated, the
885 // Controller shall return the error code Command Disallowed (0x0C).
886 if !self.cis_connection_requests.is_empty() {
887 println!("another LE Create CIS request is already pending");
888 return self.send_hci_event(command_status(hci::ErrorCode::CommandDisallowed));
889 }
890
891 // If the Host issues this command when the Connected Isochronous Stream
892 // (Host Support) feature bit (see [Vol 6] Part B, Section 4.6.27) is not set,
893 // the Controller shall return the error code Command Disallowed (0x0C).
894 if !self.connected_isochronous_stream_host_support() {
895 println!("the feature bit Connected Isochronous Stream (Host Support) is not set");
896 return self.send_hci_event(command_status(hci::ErrorCode::CommandDisallowed));
897 }
898
899 // Update the pending CIS request list.
900 cis_connection_requests.reverse();
901 self.cis_connection_requests = cis_connection_requests;
902
903 // Send the first connection request.
904 self.deque_cis_connection_request();
905 self.send_hci_event(command_status(hci::ErrorCode::Success))
906 }
907
hci_le_accept_cis_request(&mut self, packet: hci::LeAcceptCisRequest)908 pub fn hci_le_accept_cis_request(&mut self, packet: hci::LeAcceptCisRequest) {
909 let connection_handle: u16 = packet.get_connection_handle();
910
911 let command_status =
912 |status| hci::LeAcceptCisRequestStatusBuilder { status, num_hci_command_packets: 1 };
913
914 // If the Peripheral’s Host issues this command with a
915 // Connection_Handle that does not exist, or the Connection_Handle
916 // is not for a CIS, the Controller shall return the error code
917 // Unknown Connection Identifier (0x02).
918 if !self.cis_connections.contains_key(&connection_handle) {
919 println!(
920 "cannot accept LE CIS request with invalid connection handle {}",
921 connection_handle
922 );
923 return self.send_hci_event(command_status(hci::ErrorCode::UnknownConnection));
924 }
925
926 let cis = self.cis_connections.get_mut(&connection_handle).unwrap();
927
928 // If the Central’s Host issues this command, the Controller shall
929 // return the error code Command Disallowed (0x0C).
930 if cis.role == hci::Role::Central {
931 println!(
932 "cannot accept LE CIS request with central connection handle {}",
933 connection_handle
934 );
935 return self.send_hci_event(command_status(hci::ErrorCode::CommandDisallowed));
936 }
937
938 // If the Peripheral's Host issues this command with a Connection_Handle
939 // for a CIS that has already been established or that already has an
940 // HCI_LE_Accept_CIS_Request or HCI_LE_Reject_CIS_Request command in progress,
941 // the Controller shall return the error code Command Disallowed (0x0C).
942 if cis.state != CisState::PendingAccept {
943 println!(
944 "cannot accept LE CIS request for non-pending connection handle {}",
945 connection_handle
946 );
947 return self.send_hci_event(command_status(hci::ErrorCode::CommandDisallowed));
948 }
949
950 // Update local state.
951 cis.state = CisState::PendingInd;
952
953 // Send back LL_CIS_RSP to accept the request.
954 let acl_connection_handle = cis.acl_connection_handle.unwrap();
955 self.send_llcp_packet(
956 acl_connection_handle,
957 llcp::CisRspBuilder {
958 cis_offset_min: 0,
959 cis_offset_max: 0xffffff,
960 conn_event_count: 0,
961 },
962 );
963
964 self.send_hci_event(command_status(hci::ErrorCode::Success))
965 }
966
hci_le_reject_cis_request(&mut self, packet: hci::LeRejectCisRequest)967 pub fn hci_le_reject_cis_request(&mut self, packet: hci::LeRejectCisRequest) {
968 let connection_handle: u16 = packet.get_connection_handle();
969
970 let command_complete = |status| hci::LeRejectCisRequestCompleteBuilder {
971 status,
972 connection_handle,
973 num_hci_command_packets: 1,
974 };
975
976 // If the Peripheral’s Host issues this command with a
977 // Connection_Handle that does not exist, or the Connection_Handle
978 // is not for a CIS, the Controller shall return the error code
979 // Unknown Connection Identifier (0x02).
980 if !self.cis_connections.contains_key(&connection_handle) {
981 println!(
982 "cannot accept LE CIS request with invalid connection handle {}",
983 connection_handle
984 );
985 return self.send_hci_event(command_complete(hci::ErrorCode::UnknownConnection));
986 }
987
988 let cis = self.cis_connections.get(&connection_handle).unwrap();
989
990 // If the Central’s Host issues this command, the Controller shall
991 // return the error code Command Disallowed (0x0C).
992 if cis.role == hci::Role::Central {
993 println!(
994 "cannot accept LE CIS request with central connection handle {}",
995 connection_handle
996 );
997 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
998 }
999
1000 // If the Peripheral's Host issues this command with a Connection_Handle
1001 // for a CIS that has already been established or that already has an
1002 // HCI_LE_Accept_CIS_Request or HCI_LE_Reject_CIS_Request command in progress,
1003 // the Controller shall return the error code Command Disallowed (0x0C).
1004 if cis.state != CisState::PendingAccept {
1005 println!(
1006 "cannot accept LE CIS request for non-pending connection handle {}",
1007 connection_handle
1008 );
1009 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1010 }
1011
1012 // Update local state.
1013 let acl_connection_handle = cis.acl_connection_handle.unwrap();
1014 self.cis_connections.remove(&connection_handle);
1015
1016 // Send back LL_CIS_RSP to reject the request.
1017 let error_code = if packet.get_reason() == hci::ErrorCode::Success {
1018 hci::ErrorCode::RemoteUserTerminatedConnection
1019 } else {
1020 packet.get_reason()
1021 };
1022 self.send_llcp_packet(
1023 acl_connection_handle,
1024 llcp::RejectExtIndBuilder {
1025 reject_opcode: llcp::Opcode::LlCisReq as u8,
1026 error_code: error_code as u8,
1027 },
1028 );
1029
1030 self.send_hci_event(command_complete(hci::ErrorCode::Success))
1031 }
1032
hci_le_setup_iso_data_path(&mut self, packet: hci::LeSetupIsoDataPath)1033 pub fn hci_le_setup_iso_data_path(&mut self, packet: hci::LeSetupIsoDataPath) {
1034 let connection_handle: u16 = packet.get_connection_handle();
1035 let codec_configuration = packet.get_codec_configuration();
1036
1037 let command_complete = |status| hci::LeSetupIsoDataPathCompleteBuilder {
1038 status,
1039 connection_handle,
1040 num_hci_command_packets: 1,
1041 };
1042
1043 // If the Host attempts to set a data path with a Connection Handle that does not
1044 // exist or that is not for a CIS, CIS configuration, or BIS, the Controller shall
1045 // return the error code Unknown Connection Identifier (0x02).
1046 let Some(cis) = self.cis_connections.get_mut(&connection_handle) else {
1047 println!("the CIS connection handle 0x{:x} is not assigned", connection_handle);
1048 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1049 };
1050
1051 let (c_to_p_direction, p_to_c_direction) = if cis.role == hci::Role::Central {
1052 (hci::DataPathDirection::Output, hci::DataPathDirection::Input)
1053 } else {
1054 (hci::DataPathDirection::Input, hci::DataPathDirection::Output)
1055 };
1056
1057 // If the Host issues this command more than once for the same
1058 // Connection_Handle and direction before issuing the HCI_LE_Remove_ISO_Data_-
1059 // Path command for that Connection_Handle and direction, the Controller shall
1060 // return the error code Command Disallowed (0x0C).
1061 if cis.iso_data_path_c_to_p.is_some()
1062 && packet.get_data_path_direction() == c_to_p_direction
1063 {
1064 println!("C->P ISO data path already configured for ({}, {})", cis.cig_id, cis.cis_id);
1065 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1066 }
1067 if cis.iso_data_path_p_to_c.is_some()
1068 && packet.get_data_path_direction() == p_to_c_direction
1069 {
1070 println!("P->C ISO data path already configured for ({}, {})", cis.cig_id, cis.cis_id);
1071 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1072 }
1073
1074 // If the Host issues this command for a CIS on a Peripheral before it has issued
1075 // the HCI_LE_Accept_CIS_Request command for that CIS, then the Controller
1076 // shall return the error code Command Disallowed (0x0C).
1077 if cis.role == hci::Role::Peripheral && cis.state == CisState::PendingAccept {
1078 println!("setup ISO data path sent before accepting the CIS request");
1079 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1080 }
1081
1082 // If the Host issues this command for a vendor-specific data transport path that
1083 // has not been configured using the HCI_Configure_Data_Path command, the
1084 // Controller shall return the error code Command Disallowed (0x0C).
1085
1086 // If the Host attempts to set an output data path using a connection handle that is
1087 // for an Isochronous Broadcaster, for an input data path on a Synchronized
1088 // Receiver, or for a data path for the direction on a unidirectional CIS where BN
1089 // is set to 0, the Controller shall return the error code Command Disallowed
1090 // (0x0C).
1091
1092 // If the Host issues this command with Codec_Configuration_Length non-zero
1093 // and Codec_ID set to transparent air mode, the Controller shall return the error
1094 // code Invalid HCI Command Parameters (0x12).
1095 if !codec_configuration.is_empty() && packet.get_codec_id() == 0x3 {
1096 println!("Codec Configuration is not empty and Codec ID is for transparent air mode");
1097 return self
1098 .send_hci_event(command_complete(hci::ErrorCode::InvalidHciCommandParameters));
1099 }
1100
1101 // If the Host issues this command with codec-related parameters that exceed the
1102 // bandwidth and latency allowed on the established CIS or BIS identified by the
1103 // Connection_Handle parameter, the Controller shall return the error code
1104 // Invalid HCI Command Parameters (0x12).
1105
1106 if packet.get_data_path_direction() == c_to_p_direction {
1107 cis.iso_data_path_c_to_p = Some(IsoDataPath::Hci);
1108 } else {
1109 cis.iso_data_path_p_to_c = Some(IsoDataPath::Hci);
1110 }
1111
1112 self.send_hci_event(command_complete(hci::ErrorCode::Success))
1113 }
1114
hci_le_remove_iso_data_path(&mut self, packet: hci::LeRemoveIsoDataPath)1115 pub fn hci_le_remove_iso_data_path(&mut self, packet: hci::LeRemoveIsoDataPath) {
1116 let connection_handle: u16 = packet.get_connection_handle();
1117 let data_path_direction = packet.get_remove_data_path_direction();
1118
1119 let command_complete = |status| hci::LeRemoveIsoDataPathCompleteBuilder {
1120 status,
1121 connection_handle,
1122 num_hci_command_packets: 1,
1123 };
1124
1125 // If the Host issues this command with a Connection_Handle that does not exist
1126 // or is not for a CIS, CIS configuration, or BIS, the Controller shall return the
1127 // error code Unknown Connection Identifier (0x02).
1128 let Some(cis) = self.cis_connections.get_mut(&connection_handle) else {
1129 println!("the CIS connection handle 0x{:x} is not assigned", connection_handle);
1130 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1131 };
1132
1133 let (remove_c_to_p, remove_p_to_c) = if cis.role == hci::Role::Central {
1134 (
1135 data_path_direction == hci::RemoveDataPathDirection::Output
1136 || data_path_direction == hci::RemoveDataPathDirection::InputAndOutput,
1137 data_path_direction == hci::RemoveDataPathDirection::Input
1138 || data_path_direction == hci::RemoveDataPathDirection::InputAndOutput,
1139 )
1140 } else {
1141 (
1142 data_path_direction == hci::RemoveDataPathDirection::Input
1143 || data_path_direction == hci::RemoveDataPathDirection::InputAndOutput,
1144 data_path_direction == hci::RemoveDataPathDirection::Output
1145 || data_path_direction == hci::RemoveDataPathDirection::InputAndOutput,
1146 )
1147 };
1148
1149 // If the Host issues this command for a data path that has not been set up (using
1150 // the HCI_LE_Setup_ISO_Data_Path command), the Controller shall return the
1151 // error code Command Disallowed (0x0C)
1152 if cis.iso_data_path_c_to_p.is_none() && remove_c_to_p {
1153 println!("attempted to remove Iso Data Path C->P but it is not configured");
1154 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1155 }
1156 if cis.iso_data_path_p_to_c.is_none() && remove_p_to_c {
1157 println!("attempted to remove Iso Data Path P->C but it is not configured");
1158 return self.send_hci_event(command_complete(hci::ErrorCode::CommandDisallowed));
1159 }
1160
1161 if remove_c_to_p {
1162 cis.iso_data_path_c_to_p = None;
1163 }
1164 if remove_p_to_c {
1165 cis.iso_data_path_p_to_c = None;
1166 }
1167
1168 self.send_hci_event(command_complete(hci::ErrorCode::Success))
1169 }
1170
hci_disconnect(&mut self, packet: hci::Disconnect)1171 pub fn hci_disconnect(&mut self, packet: hci::Disconnect) {
1172 let connection_handle: u16 = packet.get_connection_handle();
1173 let command_status =
1174 |status| hci::DisconnectStatusBuilder { status, num_hci_command_packets: 1 };
1175
1176 let Some(cis) = self.cis_connections.get(&connection_handle).cloned() else {
1177 return self.send_hci_event(command_status(hci::ErrorCode::UnknownConnection));
1178 };
1179
1180 // If, on the Central, the Host issues this command before issuing the
1181 // HCI_LE_Create_CIS command for the same CIS, then the Controller shall
1182 // return the error code Command Disallowed (0x0C).
1183 // If, on the Peripheral, the Host issues this command before the Controller has
1184 // generated the HCI_LE_CIS_Established event for that CIS, then the Controller
1185 // shall return the error code Command Disallowed (0x0C).
1186 if !matches!(cis.state, CisState::Connected | CisState::PendingRsp) {
1187 println!(
1188 "cannot disconnect CIS connection with handle {} because it is not connected",
1189 connection_handle
1190 );
1191 return self.send_hci_event(command_status(hci::ErrorCode::CommandDisallowed));
1192 }
1193
1194 if cis.role == hci::Role::Central {
1195 self.cis_connections
1196 .entry(connection_handle)
1197 .and_modify(|cis| cis.state = CisState::Configuration);
1198 } else {
1199 self.cis_connections.remove(&connection_handle);
1200 }
1201
1202 self.send_llcp_packet(
1203 cis.acl_connection_handle.unwrap(),
1204 llcp::CisTerminateIndBuilder {
1205 cig_id: cis.cig_id,
1206 cis_id: cis.cis_id,
1207 error_code: packet.get_reason().into(),
1208 },
1209 );
1210
1211 self.send_hci_event(command_status(hci::ErrorCode::Success));
1212 self.send_hci_event(hci::DisconnectionCompleteBuilder {
1213 status: hci::ErrorCode::Success,
1214 connection_handle,
1215 reason: hci::ErrorCode::ConnectionTerminatedByLocalHost,
1216 });
1217 }
1218
ll_cis_req(&mut self, acl_connection_handle: u16, packet: llcp::CisReq)1219 pub fn ll_cis_req(&mut self, acl_connection_handle: u16, packet: llcp::CisReq) {
1220 let cis_connection_handle = self.new_cis_connection_handle();
1221 self.cis_connections.insert(
1222 cis_connection_handle,
1223 Cis {
1224 cig_id: packet.get_cig_id(),
1225 cis_id: packet.get_cis_id(),
1226 role: hci::Role::Peripheral,
1227 acl_connection_handle: Some(acl_connection_handle),
1228 cis_connection_handle,
1229 state: CisState::PendingAccept,
1230 iso_data_path_c_to_p: None,
1231 iso_data_path_p_to_c: None,
1232 parameters: Some(CisParameters {
1233 cig_sync_delay: 0,
1234 cis_sync_delay: 0,
1235 phy_c_to_p: packet.get_phy_c_to_p(),
1236 phy_p_to_c: packet.get_phy_p_to_c(),
1237 nse: packet.get_nse(),
1238 bn_c_to_p: packet.get_bn_c_to_p(),
1239 bn_p_to_c: packet.get_bn_p_to_c(),
1240 ft_c_to_p: packet.get_ft_c_to_p(),
1241 ft_p_to_c: packet.get_ft_p_to_c(),
1242 max_pdu_c_to_p: packet.get_max_pdu_c_to_p(),
1243 max_pdu_p_to_c: packet.get_max_pdu_p_to_c(),
1244 max_sdu_c_to_p: packet.get_max_sdu_c_to_p(),
1245 max_sdu_p_to_c: packet.get_max_sdu_p_to_c(),
1246 sdu_interval_c_to_p: packet.get_sdu_interval_c_to_p(),
1247 sdu_interval_p_to_c: packet.get_sdu_interval_p_to_c(),
1248 iso_interval: packet.get_iso_interval(),
1249 sub_interval: packet.get_sub_interval(),
1250 framed: packet.get_framed() != 0,
1251 }),
1252 },
1253 );
1254
1255 self.send_hci_event(hci::LeCisRequestBuilder {
1256 acl_connection_handle,
1257 cis_connection_handle,
1258 cig_id: packet.get_cig_id(),
1259 cis_id: packet.get_cis_id(),
1260 })
1261 }
1262
ll_cis_rsp(&mut self, acl_connection_handle: u16, _packet: llcp::CisRsp)1263 pub fn ll_cis_rsp(&mut self, acl_connection_handle: u16, _packet: llcp::CisRsp) {
1264 let cis_connection_handle = self.get_cis_connection_handle(|cis| {
1265 cis.acl_connection_handle == Some(acl_connection_handle)
1266 && cis.role == hci::Role::Central
1267 && cis.state == CisState::PendingRsp
1268 });
1269
1270 if let Some(cis_connection_handle) = cis_connection_handle {
1271 self.cis_connections
1272 .entry(cis_connection_handle)
1273 .and_modify(|cis| cis.state = CisState::Connected);
1274 let cis = self.cis_connections.get(&cis_connection_handle).unwrap();
1275 let parameters = cis.parameters.as_ref().unwrap();
1276 self.send_llcp_packet(
1277 acl_connection_handle,
1278 llcp::CisIndBuilder {
1279 aa: 0,
1280 cis_offset: 0,
1281 cig_sync_delay: parameters.cig_sync_delay,
1282 cis_sync_delay: parameters.cis_sync_delay,
1283 conn_event_count: 0,
1284 },
1285 );
1286 self.send_hci_event(hci::LeCisEstablishedBuilder {
1287 status: hci::ErrorCode::Success,
1288 connection_handle: cis_connection_handle,
1289 cig_sync_delay: parameters.cig_sync_delay,
1290 cis_sync_delay: parameters.cis_sync_delay,
1291 transport_latency_c_to_p: parameters.transport_latency_c_to_p(),
1292 transport_latency_p_to_c: parameters.transport_latency_p_to_c(),
1293 phy_c_to_p: parameters.phy_c_to_p(),
1294 phy_p_to_c: parameters.phy_p_to_c(),
1295 nse: parameters.nse,
1296 bn_c_to_p: parameters.bn_c_to_p,
1297 bn_p_to_c: parameters.bn_p_to_c,
1298 ft_c_to_p: parameters.ft_c_to_p,
1299 ft_p_to_c: parameters.ft_p_to_c,
1300 max_pdu_c_to_p: parameters.max_pdu_c_to_p as u8,
1301 max_pdu_p_to_c: parameters.max_pdu_p_to_c as u8,
1302 iso_interval: parameters.iso_interval,
1303 });
1304 // Start the next pending connection request.
1305 self.deque_cis_connection_request();
1306 } else {
1307 println!("skipping out of place packet LL_CIS_RSP");
1308 }
1309 }
1310
ll_reject_ext_ind(&mut self, acl_connection_handle: u16, packet: llcp::RejectExtInd)1311 pub fn ll_reject_ext_ind(&mut self, acl_connection_handle: u16, packet: llcp::RejectExtInd) {
1312 if packet.get_reject_opcode() != llcp::Opcode::LlCisReq as u8 {
1313 return;
1314 }
1315
1316 let cis_connection_handle = self.get_cis_connection_handle(|cis| {
1317 cis.acl_connection_handle == Some(acl_connection_handle)
1318 && cis.role == hci::Role::Central
1319 && cis.state == CisState::PendingRsp
1320 });
1321
1322 if let Some(cis_connection_handle) = cis_connection_handle {
1323 let cis = self.cis_connections.get_mut(&cis_connection_handle).unwrap();
1324 cis.state = CisState::Configuration;
1325 cis.parameters = None;
1326 self.send_hci_event(hci::LeCisEstablishedBuilder {
1327 status: hci::ErrorCode::RemoteUserTerminatedConnection,
1328 connection_handle: cis_connection_handle,
1329 cig_sync_delay: 0,
1330 cis_sync_delay: 0,
1331 transport_latency_c_to_p: 0,
1332 transport_latency_p_to_c: 0,
1333 phy_c_to_p: hci::SecondaryPhyType::NoPackets,
1334 phy_p_to_c: hci::SecondaryPhyType::NoPackets,
1335 nse: 0,
1336 bn_p_to_c: 0,
1337 bn_c_to_p: 0,
1338 ft_p_to_c: 0,
1339 ft_c_to_p: 0,
1340 max_pdu_p_to_c: 0,
1341 max_pdu_c_to_p: 0,
1342 iso_interval: 0,
1343 });
1344 // Start the next pending connection request.
1345 self.deque_cis_connection_request();
1346 } else {
1347 println!("skipping out of place packet LL_CIS_IND");
1348 }
1349 }
1350
ll_cis_ind(&mut self, acl_connection_handle: u16, packet: llcp::CisInd)1351 pub fn ll_cis_ind(&mut self, acl_connection_handle: u16, packet: llcp::CisInd) {
1352 let cis_connection_handle = self.get_cis_connection_handle(|cis| {
1353 cis.acl_connection_handle == Some(acl_connection_handle)
1354 && cis.role == hci::Role::Peripheral
1355 && cis.state == CisState::PendingInd
1356 });
1357
1358 if let Some(cis_connection_handle) = cis_connection_handle {
1359 self.cis_connections.entry(cis_connection_handle).and_modify(|cis| {
1360 cis.state = CisState::Connected;
1361 let parameters = cis.parameters.as_mut().unwrap();
1362 parameters.cig_sync_delay = packet.get_cig_sync_delay();
1363 parameters.cis_sync_delay = packet.get_cis_sync_delay();
1364 });
1365 let cis = self.cis_connections.get(&cis_connection_handle).unwrap();
1366 let parameters = cis.parameters.as_ref().unwrap();
1367 self.send_hci_event(hci::LeCisEstablishedBuilder {
1368 status: hci::ErrorCode::Success,
1369 connection_handle: cis_connection_handle,
1370 cig_sync_delay: parameters.cig_sync_delay,
1371 cis_sync_delay: parameters.cis_sync_delay,
1372 transport_latency_c_to_p: parameters.transport_latency_c_to_p(),
1373 transport_latency_p_to_c: parameters.transport_latency_p_to_c(),
1374 phy_c_to_p: parameters.phy_c_to_p(),
1375 phy_p_to_c: parameters.phy_p_to_c(),
1376 nse: parameters.nse,
1377 bn_p_to_c: parameters.bn_c_to_p,
1378 bn_c_to_p: parameters.bn_p_to_c,
1379 ft_p_to_c: parameters.ft_c_to_p,
1380 ft_c_to_p: parameters.ft_p_to_c,
1381 max_pdu_p_to_c: parameters.max_pdu_c_to_p as u8,
1382 max_pdu_c_to_p: parameters.max_pdu_p_to_c as u8,
1383 iso_interval: parameters.iso_interval,
1384 });
1385 } else {
1386 println!("skipping out of place packet LL_CIS_IND");
1387 }
1388 }
1389
ll_cis_terminate_ind( &mut self, acl_connection_handle: u16, packet: llcp::CisTerminateInd, )1390 pub fn ll_cis_terminate_ind(
1391 &mut self,
1392 acl_connection_handle: u16,
1393 packet: llcp::CisTerminateInd,
1394 ) {
1395 let cis_connection_handle = self.get_cis_connection_handle(|cis| {
1396 cis.acl_connection_handle == Some(acl_connection_handle)
1397 && cis.cig_id == packet.get_cig_id()
1398 && cis.cis_id == packet.get_cis_id()
1399 });
1400
1401 if let Some(cis_connection_handle) = cis_connection_handle {
1402 self.send_hci_event(hci::DisconnectionCompleteBuilder {
1403 status: hci::ErrorCode::Success,
1404 connection_handle: cis_connection_handle,
1405 reason: hci::ErrorCode::try_from(packet.get_error_code()).unwrap(),
1406 });
1407 self.cis_connections.remove(&cis_connection_handle);
1408 } else {
1409 println!("skipping out of place packet LL_CIS_TERMINATE_IND");
1410 }
1411 }
1412 }
1413
1414 /// Derive a valid ISO_Interval for a CIG based on the
1415 /// LE Set Cig Parameters command input. SDU_Interval, Max_Transport_Latency are
1416 /// provided microseconds.
iso_interval( sdu_interval_c_to_p: microseconds, sdu_interval_p_to_c: microseconds, framed: bool, max_transport_latency_c_to_p: microseconds, max_transport_latency_p_to_c: microseconds, ) -> Option<slots>1417 fn iso_interval(
1418 sdu_interval_c_to_p: microseconds,
1419 sdu_interval_p_to_c: microseconds,
1420 framed: bool,
1421 max_transport_latency_c_to_p: microseconds,
1422 max_transport_latency_p_to_c: microseconds,
1423 ) -> Option<slots> {
1424 if framed {
1425 let iso_interval = std::cmp::max(sdu_interval_c_to_p, sdu_interval_p_to_c);
1426 Some(((iso_interval + 1249) / 1250) as u16)
1427 } else {
1428 // Unframed PDUs shall only be used when the ISO_Interval is equal to
1429 // or an integer multiple of the SDU_Interval and a constant time offset
1430 // alignment is maintained between the SDU generation and the timing in
1431 // the isochronous transport.
1432 let iso_interval = num_integer::lcm(
1433 1250,
1434 match (sdu_interval_c_to_p, sdu_interval_p_to_c) {
1435 (0, 0) => panic!(),
1436 (0, _) => sdu_interval_p_to_c,
1437 (_, 0) => sdu_interval_c_to_p,
1438 _ => num_integer::lcm(sdu_interval_c_to_p, sdu_interval_p_to_c),
1439 },
1440 );
1441 let min_transport_latency_c_to_p = 2 * iso_interval - sdu_interval_c_to_p;
1442 let min_transport_latency_p_to_c = 2 * iso_interval - sdu_interval_p_to_c;
1443
1444 ((iso_interval / 1250) <= u16::MAX as u32
1445 && (sdu_interval_c_to_p == 0
1446 || min_transport_latency_c_to_p <= max_transport_latency_c_to_p)
1447 && (sdu_interval_p_to_c == 0
1448 || min_transport_latency_p_to_c <= max_transport_latency_p_to_c))
1449 .then_some((iso_interval / 1250) as u16)
1450 }
1451 }
1452
1453 /// Compute the transport latency for a CIG based on the
1454 /// configuration parameters. CIG_Sync_Delay, SDU_Interval are provided
1455 /// in microseconds, ISO_Interval in multiple of 1.25ms,
transport_latency( cig_sync_delay: microseconds, iso_interval: slots, ft: u8, sdu_interval: microseconds, framed: bool, ) -> microseconds1456 fn transport_latency(
1457 cig_sync_delay: microseconds,
1458 iso_interval: slots,
1459 ft: u8,
1460 sdu_interval: microseconds,
1461 framed: bool,
1462 ) -> microseconds {
1463 let iso_interval = iso_interval as u32 * 1250;
1464 if framed {
1465 cig_sync_delay + ft as u32 * iso_interval + sdu_interval
1466 } else {
1467 cig_sync_delay + ft as u32 * iso_interval - sdu_interval
1468 }
1469 }
1470
1471 #[cfg(test)]
1472 mod test {
1473 use crate::llcp::iso::*;
1474
1475 #[test]
test_iso_interval()1476 fn test_iso_interval() {
1477 assert!(iso_interval(0x7530, 0x7530, false, 0x7530, 0x7530).is_some());
1478 assert!(iso_interval(0x7530, 0, false, 0x7530, 0x7530).is_some());
1479 assert!(iso_interval(0x7530, 0x7530, false, 0x7000, 0x7000).is_none());
1480 }
1481 }
1482