summaryrefslogtreecommitdiff
path: root/floss
diff options
context:
space:
mode:
Diffstat (limited to 'floss')
-rw-r--r--floss/hcidoc/src/groups/informational.rs183
1 files changed, 136 insertions, 47 deletions
diff --git a/floss/hcidoc/src/groups/informational.rs b/floss/hcidoc/src/groups/informational.rs
index dbaa6c8ae3..1827b64282 100644
--- a/floss/hcidoc/src/groups/informational.rs
+++ b/floss/hcidoc/src/groups/informational.rs
@@ -83,6 +83,24 @@ impl AddressType {
}
}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+enum Transport {
+ Unknown,
+ BREDR,
+ LE,
+}
+
+impl fmt::Display for Transport {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let str = match self {
+ Transport::Unknown => "??",
+ Transport::BREDR => "BR",
+ Transport::LE => "LE",
+ };
+ write!(f, "{}", str)
+ }
+}
+
#[derive(Clone, Copy, PartialEq)]
enum InitiatorType {
Unknown,
@@ -124,8 +142,8 @@ struct DeviceInformation {
names: HashSet<String>,
address: Address,
address_type: AddressType,
- acls: Vec<AclInformation>,
- acl_state: AclState,
+ acls: HashMap<Transport, Vec<AclInformation>>,
+ acl_state: HashMap<Transport, AclState>,
}
impl DeviceInformation {
@@ -134,30 +152,52 @@ impl DeviceInformation {
names: HashSet::new(),
address: address,
address_type: AddressType::None,
- acls: vec![],
- acl_state: AclState::None,
+ acls: HashMap::from([(Transport::BREDR, vec![]), (Transport::LE, vec![])]),
+ acl_state: HashMap::from([
+ (Transport::BREDR, AclState::None),
+ (Transport::LE, AclState::None),
+ ]),
}
}
- fn is_connection_active(&self) -> bool {
+ fn is_connection_active(&self, transport: Transport) -> bool {
+ if transport == Transport::Unknown {
+ return false;
+ }
+
// not empty and last connection's end time is not set.
- return !self.acls.is_empty() && self.acls.last().unwrap().end_time == INVALID_TS;
+ return !self.acls[&transport].is_empty()
+ && self.acls[&transport].last().unwrap().end_time == INVALID_TS;
}
- fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation {
- if !self.is_connection_active() {
- let acl = AclInformation::new(*handle);
- self.acls.push(acl);
+ fn get_or_allocate_connection(
+ &mut self,
+ handle: ConnectionHandle,
+ transport: Transport,
+ ) -> &mut AclInformation {
+ assert_ne!(transport, Transport::Unknown, "device allocating unknown transport");
+ if !self.is_connection_active(transport) {
+ let acl = AclInformation::new(handle, transport);
+ self.acls.get_mut(&transport).unwrap().push(acl);
}
- return self.acls.last_mut().unwrap();
+ return self.acls.get_mut(&transport).unwrap().last_mut().unwrap();
}
- fn report_connection_start(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) {
- let mut acl = AclInformation::new(handle);
- let initiator = self.acl_state.get_connection_initiator();
+ fn report_connection_start(
+ &mut self,
+ handle: ConnectionHandle,
+ transport: Transport,
+ ts: NaiveDateTime,
+ ) {
+ if transport == Transport::Unknown {
+ return;
+ }
+
+ let mut acl = AclInformation::new(handle, transport);
+ let initiator = self.acl_state[&transport].get_connection_initiator();
acl.report_start(initiator, ts);
- self.acls.push(acl);
- self.acl_state = AclState::Connected;
+ self.acls.get_mut(&transport).unwrap().push(acl);
+ self.acl_state.insert(transport, AclState::Connected);
}
fn report_connection_end(
@@ -166,9 +206,25 @@ impl DeviceInformation {
initiator: InitiatorType,
ts: NaiveDateTime,
) {
- let acl = self.get_or_allocate_connection(&handle);
- acl.report_end(initiator, ts);
- self.acl_state = AclState::None;
+ for transport in [Transport::BREDR, Transport::LE] {
+ if self.is_connection_active(transport) {
+ if self.acls[&transport].last().unwrap().handle == handle {
+ self.acls
+ .get_mut(&transport)
+ .unwrap()
+ .last_mut()
+ .unwrap()
+ .report_end(initiator, ts);
+ self.acl_state.insert(transport, AclState::None);
+ return;
+ }
+ }
+ }
+
+ eprintln!(
+ "device {} receive disconnection of handle {} without corresponding connection at {}",
+ self.address, handle, ts
+ );
}
fn print_names(names: &HashSet<String>) -> String {
@@ -190,7 +246,10 @@ impl fmt::Display for DeviceInformation {
device_names = DeviceInformation::print_names(&self.names),
num_connections = self.acls.len()
);
- for acl in &self.acls {
+ for acl in &self.acls[&Transport::BREDR] {
+ let _ = write!(f, "{}", acl);
+ }
+ for acl in &self.acls[&Transport::LE] {
let _ = write!(f, "{}", acl);
}
@@ -209,6 +268,7 @@ struct AclInformation {
start_time: NaiveDateTime,
end_time: NaiveDateTime,
handle: ConnectionHandle,
+ transport: Transport,
start_initiator: InitiatorType,
end_initiator: InitiatorType,
active_profiles: HashMap<ProfileId, ProfileInformation>,
@@ -218,11 +278,12 @@ struct AclInformation {
}
impl AclInformation {
- pub fn new(handle: ConnectionHandle) -> Self {
+ pub fn new(handle: ConnectionHandle, transport: Transport) -> Self {
AclInformation {
start_time: INVALID_TS,
end_time: INVALID_TS,
- handle: handle,
+ handle,
+ transport,
start_initiator: InitiatorType::Unknown,
end_initiator: InitiatorType::Unknown,
active_profiles: HashMap::new(),
@@ -387,7 +448,8 @@ impl fmt::Display for AclInformation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let _ = writeln!(
f,
- " Handle: {handle}, {timestamp_initiator_info}",
+ " Handle: {handle} ({transport}), {timestamp_initiator_info}",
+ transport = self.transport,
handle = self.handle,
timestamp_initiator_info = print_timestamps_and_initiator(
self.start_time,
@@ -566,23 +628,28 @@ impl InformationalRule {
fn get_or_allocate_unknown_connection(
&mut self,
- handle: &ConnectionHandle,
+ handle: ConnectionHandle,
+ transport: Transport,
) -> &mut AclInformation {
- if !self.unknown_connections.contains_key(handle) {
- self.unknown_connections.insert(*handle, AclInformation::new(*handle));
+ if !self.unknown_connections.contains_key(&handle) {
+ self.unknown_connections.insert(handle, AclInformation::new(handle, transport));
}
- return self.unknown_connections.get_mut(handle).unwrap();
+ return self.unknown_connections.get_mut(&handle).unwrap();
}
- fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation {
- if !self.handles.contains_key(&handle) {
- let conn = self.get_or_allocate_unknown_connection(&handle);
+ fn get_or_allocate_connection(
+ &mut self,
+ handle: ConnectionHandle,
+ transport: Transport,
+ ) -> &mut AclInformation {
+ if !self.handles.contains_key(&handle) || transport == Transport::Unknown {
+ let conn = self.get_or_allocate_unknown_connection(handle, transport);
return conn;
}
- let address = &self.handles.get(handle).unwrap().clone();
+ let address = &self.handles.get(&handle).unwrap().clone();
let device = self.get_or_allocate_device(address);
- return device.get_or_allocate_connection(handle);
+ return device.get_or_allocate_connection(handle, transport);
}
fn report_address_type(&mut self, address: &Address, address_type: AddressType) {
@@ -595,19 +662,20 @@ impl InformationalRule {
device.names.insert(name.into());
}
- fn report_acl_state(&mut self, address: &Address, state: AclState) {
+ fn report_acl_state(&mut self, address: &Address, transport: Transport, state: AclState) {
let device = self.get_or_allocate_device(address);
- device.acl_state = state;
+ device.acl_state.insert(transport, state);
}
fn report_connection_start(
&mut self,
address: &Address,
handle: ConnectionHandle,
+ transport: Transport,
ts: NaiveDateTime,
) {
let device = self.get_or_allocate_device(address);
- device.report_connection_start(handle, ts);
+ device.report_connection_start(handle, transport, ts);
self.handles.insert(handle, *address);
self.pending_disconnections.remove(&handle);
}
@@ -624,14 +692,14 @@ impl InformationalRule {
}
let device = self.devices.get_mut(address).unwrap();
- if !device.is_connection_active() {
+ if !device.is_connection_active(Transport::BREDR) {
// SCO is connected, but ACL is not. This is weird, but let's ignore for simplicity.
eprintln!("[{}] SCO is connected, but ACL is not.", address);
return;
}
// Whatever handle value works here - we aren't allocating a new one.
- let acl = device.get_or_allocate_connection(&0);
+ let acl = device.get_or_allocate_connection(0, Transport::BREDR);
let acl_handle = acl.handle;
// We need to listen the HCI commands to determine the correct initiator.
// Here we just assume host for simplicity.
@@ -654,7 +722,7 @@ impl InformationalRule {
// This might be a SCO disconnection event, so check that first
if self.sco_handles.contains_key(&handle) {
let acl_handle = self.sco_handles[&handle];
- let conn = self.get_or_allocate_connection(&acl_handle);
+ let conn = self.get_or_allocate_connection(acl_handle, Transport::BREDR);
// in case of HFP failure, the initiator here would be set to peer, which is incorrect,
// but when printing we detect by the timestamp that it was a failure anyway.
conn.report_profile_end(
@@ -677,7 +745,7 @@ impl InformationalRule {
self.sco_handles.retain(|_sco_handle, acl_handle| *acl_handle != handle);
} else {
// Unknown device.
- let conn = self.get_or_allocate_unknown_connection(&handle);
+ let conn = self.get_or_allocate_unknown_connection(handle, Transport::Unknown);
conn.report_end(initiator, ts);
}
}
@@ -731,7 +799,7 @@ impl InformationalRule {
initiator: InitiatorType,
ts: NaiveDateTime,
) {
- let conn = self.get_or_allocate_connection(&handle);
+ let conn = self.get_or_allocate_connection(handle, Transport::BREDR);
conn.report_l2cap_conn_req(psm, cid, initiator, ts);
}
@@ -747,7 +815,7 @@ impl InformationalRule {
if status == ConnectionResponseResult::Pending {
return;
}
- let conn = self.get_or_allocate_connection(&handle);
+ let conn = self.get_or_allocate_connection(handle, Transport::BREDR);
let cid_info = CidInformation { host_cid, peer_cid };
conn.report_l2cap_conn_rsp(status, cid_info, initiator, ts);
}
@@ -760,7 +828,7 @@ impl InformationalRule {
initiator: InitiatorType,
ts: NaiveDateTime,
) {
- let conn = self.get_or_allocate_connection(&handle);
+ let conn = self.get_or_allocate_connection(handle, Transport::BREDR);
let cid_info = CidInformation { host_cid, peer_cid };
conn.report_l2cap_disconn_rsp(cid_info, initiator, ts);
}
@@ -774,6 +842,7 @@ impl Rule for InformationalRule {
self.report_connection_start(
&ev.get_bd_addr(),
ev.get_connection_handle(),
+ Transport::BREDR,
packet.ts,
);
@@ -830,10 +899,15 @@ impl Rule for InformationalRule {
}
// Determining LE initiator is complex, for simplicity assume host inits.
- self.report_acl_state(&ev.get_peer_address(), AclState::Initiating);
+ self.report_acl_state(
+ &ev.get_peer_address(),
+ Transport::LE,
+ AclState::Initiating,
+ );
self.report_connection_start(
&ev.get_peer_address(),
ev.get_connection_handle(),
+ Transport::LE,
packet.ts,
);
self.report_address_type(&ev.get_peer_address(), AddressType::LE);
@@ -845,10 +919,15 @@ impl Rule for InformationalRule {
}
// Determining LE initiator is complex, for simplicity assume host inits.
- self.report_acl_state(&ev.get_peer_address(), AclState::Initiating);
+ self.report_acl_state(
+ &ev.get_peer_address(),
+ Transport::LE,
+ AclState::Initiating,
+ );
self.report_connection_start(
&ev.get_peer_address(),
ev.get_connection_handle(),
+ Transport::LE,
packet.ts,
);
self.report_address_type(&ev.get_peer_address(), AddressType::LE);
@@ -881,11 +960,19 @@ impl Rule for InformationalRule {
self.report_reset(packet.ts);
}
CommandChild::CreateConnection(cmd) => {
- self.report_acl_state(&cmd.get_bd_addr(), AclState::Initiating);
+ self.report_acl_state(
+ &cmd.get_bd_addr(),
+ Transport::BREDR,
+ AclState::Initiating,
+ );
self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR);
}
CommandChild::AcceptConnectionRequest(cmd) => {
- self.report_acl_state(&cmd.get_bd_addr(), AclState::Accepting);
+ self.report_acl_state(
+ &cmd.get_bd_addr(),
+ Transport::BREDR,
+ AclState::Accepting,
+ );
self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR);
}
CommandChild::Disconnect(cmd) => {
@@ -1001,7 +1088,9 @@ impl Rule for InformationalRule {
* (5) Address, alphabetically
*/
fn sort_addresses(a: &DeviceInformation, b: &DeviceInformation) -> Ordering {
- let connection_order = a.acls.is_empty().cmp(&b.acls.is_empty());
+ let a_empty = a.acls[&Transport::BREDR].is_empty() && a.acls[&Transport::LE].is_empty();
+ let b_empty = b.acls[&Transport::BREDR].is_empty() && b.acls[&Transport::LE].is_empty();
+ let connection_order = a_empty.cmp(&b_empty);
if connection_order != Ordering::Equal {
return connection_order;
}