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