1 ///! Rule group for tracking controller related issues.
2 use chrono::NaiveDateTime;
3 use std::collections::HashSet;
4 use std::convert::Into;
5 use std::io::Write;
6 use std::sync::LazyLock;
7
8 use crate::engine::{Rule, RuleGroup, Signal};
9 use crate::parser::{NewIndex, Packet, PacketChild};
10 use hcidoc_packets::hci::{CommandCompleteChild, ErrorCode, EventChild, LocalVersionInformation};
11
12 enum ControllerSignal {
13 HardwareError, // Controller reports HCI event: Hardware Error
14 LikelyExternalController, // Controller is not in the known list. Likely to be an external controller.
15 }
16
17 impl Into<&'static str> for ControllerSignal {
into(self) -> &'static str18 fn into(self) -> &'static str {
19 match self {
20 ControllerSignal::HardwareError => "HardwareError",
21 ControllerSignal::LikelyExternalController => "LikelyExternalController",
22 }
23 }
24 }
25
26 static KNOWN_CONTROLLER_NAMES: LazyLock<[String; 6]> = LazyLock::new(|| {
27 [
28 String::from("Bluemoon Universal Bluetooth Host Controller"), // AC7625
29 String::from("MTK MT7961 #1"), // MT7921LE/MT7921LS
30 String::from("MTK MT7922 #1"), // MT7922
31 String::from("RTK_BT_5.0"), // RTL8822CE
32 String::from("RT_BT"), // RTL8852AE
33 String::from(""), // AC9260/AC9560/AX200/AX201/AX203/AX211/MVL8897/QCA6174A3/QCA6174A5/QC_WCN6856
34 ]
35 });
36
37 const KNOWN_CONTROLLER_MANUFACTURERS: [u16; 5] = [
38 2, // Intel.
39 29, // Qualcomm
40 70, // MediaTek
41 72, // Marvell
42 93, // Realtek
43 ];
44
45 struct ControllerRule {
46 /// Pre-defined signals discovered in the logs.
47 signals: Vec<Signal>,
48
49 /// Interesting occurrences surfaced by this rule.
50 reportable: Vec<(NaiveDateTime, String)>,
51
52 /// All detected open_index.
53 controllers: HashSet<String>,
54 }
55
56 impl ControllerRule {
new() -> Self57 pub fn new() -> Self {
58 ControllerRule { signals: vec![], reportable: vec![], controllers: HashSet::new() }
59 }
60
report_hardware_error(&mut self, packet: &Packet)61 pub fn report_hardware_error(&mut self, packet: &Packet) {
62 self.signals.push(Signal {
63 index: packet.index,
64 ts: packet.ts.clone(),
65 tag: ControllerSignal::HardwareError.into(),
66 });
67
68 self.reportable.push((packet.ts, format!("controller reported hardware error")));
69 }
70
process_local_name(&mut self, local_name: &[u8; 248], packet: &Packet)71 fn process_local_name(&mut self, local_name: &[u8; 248], packet: &Packet) {
72 let null_index = local_name.iter().position(|&b| b == 0).unwrap_or(local_name.len());
73 match String::from_utf8(local_name[..null_index].to_vec()) {
74 Ok(name) => {
75 if !KNOWN_CONTROLLER_NAMES.contains(&name) {
76 self.signals.push(Signal {
77 index: packet.index,
78 ts: packet.ts,
79 tag: ControllerSignal::LikelyExternalController.into(),
80 })
81 }
82 }
83 Err(_) => self.signals.push(Signal {
84 index: packet.index,
85 ts: packet.ts,
86 tag: ControllerSignal::LikelyExternalController.into(),
87 }),
88 }
89 }
90
process_local_version(&mut self, version_info: &LocalVersionInformation, packet: &Packet)91 fn process_local_version(&mut self, version_info: &LocalVersionInformation, packet: &Packet) {
92 if !KNOWN_CONTROLLER_MANUFACTURERS.contains(&version_info.manufacturer_name) {
93 self.signals.push(Signal {
94 index: packet.index,
95 ts: packet.ts,
96 tag: ControllerSignal::LikelyExternalController.into(),
97 })
98 }
99 }
100
process_new_index(&mut self, new_index: &NewIndex, packet: &Packet)101 fn process_new_index(&mut self, new_index: &NewIndex, packet: &Packet) {
102 self.controllers.insert(new_index.get_addr_str());
103
104 if self.controllers.len() > 1 {
105 self.signals.push(Signal {
106 index: packet.index,
107 ts: packet.ts,
108 tag: ControllerSignal::LikelyExternalController.into(),
109 });
110 }
111 }
112 }
113
114 impl Rule for ControllerRule {
process(&mut self, packet: &Packet)115 fn process(&mut self, packet: &Packet) {
116 match &packet.inner {
117 PacketChild::HciEvent(ev) => match ev.specialize() {
118 EventChild::HardwareError(_ev) => {
119 self.report_hardware_error(&packet);
120 }
121 EventChild::CommandComplete(ev) => match ev.specialize() {
122 CommandCompleteChild::ReadLocalNameComplete(ev) => {
123 if ev.get_status() != ErrorCode::Success {
124 return;
125 }
126
127 self.process_local_name(ev.get_local_name(), &packet);
128 }
129 CommandCompleteChild::ReadLocalVersionInformationComplete(ev) => {
130 if ev.get_status() != ErrorCode::Success {
131 return;
132 }
133
134 self.process_local_version(ev.get_local_version_information(), &packet);
135 }
136 _ => {}
137 },
138 _ => {}
139 },
140 PacketChild::NewIndex(ni) => {
141 self.process_new_index(ni, &packet);
142 }
143 _ => {}
144 }
145 }
146
report(&self, writer: &mut dyn Write)147 fn report(&self, writer: &mut dyn Write) {
148 if self.reportable.len() > 0 {
149 let _ = writeln!(writer, "Controller report:");
150 for (ts, message) in self.reportable.iter() {
151 let _ = writeln!(writer, "[{:?}] {}", ts, message);
152 }
153 }
154 }
155
report_signals(&self) -> &[Signal]156 fn report_signals(&self) -> &[Signal] {
157 self.signals.as_slice()
158 }
159 }
160
161 /// Get a rule group with connection rules.
get_controllers_group() -> RuleGroup162 pub fn get_controllers_group() -> RuleGroup {
163 let mut group = RuleGroup::new();
164 group.add_rule(Box::new(ControllerRule::new()));
165
166 group
167 }
168