1use crate::{
2 connection::{Channel, LogicalUnit},
3 fmt::LogItem,
4 log_vec, Loggable,
5};
6
7use super::Timestamp;
8
9mod get_alloc_info;
10pub use get_alloc_info::{AllocInfo as SelAllocInfo, GetAllocInfo as GetSelAllocInfo};
11
12mod get_entry;
13pub use get_entry::{EntryInfo as SelEntryInfo, GetEntry as GetSelEntry};
14
15mod get_info;
16pub use get_info::{Command as SelCommand, GetInfo as GetSelInfo, Info as SelInfo};
17
18#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct RecordId(u16);
20
21impl RecordId {
22 pub const FIRST: Self = Self(0x0000);
23 pub const LAST: Self = Self(0xFFFF);
24
25 pub fn new(id: u16) -> Option<Self> {
26 if RecordId(id) == Self::FIRST || RecordId(id) == Self::LAST {
27 None
28 } else {
29 Some(Self(id))
30 }
31 }
32
33 pub(crate) fn new_raw(id: u16) -> Self {
34 RecordId(id)
35 }
36
37 pub fn value(&self) -> u16 {
38 self.0
39 }
40
41 pub fn is_first(&self) -> bool {
42 self == &Self::FIRST
43 }
44
45 pub fn is_last(&self) -> bool {
46 self == &Self::LAST
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq)]
51enum SelRecordType {
52 System,
53 TimestampedOem(u8),
54 NonTimestampedOem(u8),
55 Unknown(u8),
56}
57
58impl From<u8> for SelRecordType {
59 fn from(value: u8) -> Self {
60 match value {
61 0x02 => Self::System,
62 0xC0..=0xDF => Self::TimestampedOem(value),
63 0xE0..=0xFF => Self::NonTimestampedOem(value),
64 v => Self::Unknown(v),
65 }
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq)]
70pub enum EventGenerator {
71 RqSAAndLun {
72 i2c_addr: u8,
73 channel_number: Channel,
74 lun: LogicalUnit,
75 },
76 SoftwareId {
77 software_id: u8,
78 channel_number: Channel,
79 },
80}
81
82impl From<(u8, u8)> for EventGenerator {
83 fn from(value: (u8, u8)) -> Self {
84 let is_software_id = (value.0 & 0x1) == 0x1;
85 let i2c_or_sid = (value.0 >> 1) & 0x7F;
86
87 let channel_number = Channel::new((value.1 >> 4) & 0xF).unwrap();
89
90 if is_software_id {
91 Self::SoftwareId {
92 software_id: i2c_or_sid,
93 channel_number,
94 }
95 } else {
96 let lun = LogicalUnit::from_low_bits(value.1);
97
98 Self::RqSAAndLun {
99 i2c_addr: i2c_or_sid,
100 channel_number,
101 lun,
102 }
103 }
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq)]
108pub enum EventMessageRevision {
109 V2_0,
110 V1_0,
111 Unknown(u8),
112}
113
114impl From<u8> for EventMessageRevision {
115 fn from(value: u8) -> Self {
116 match value {
117 0x04 => Self::V2_0,
118 0x03 => Self::V1_0,
119 v => Self::Unknown(v),
120 }
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq)]
125pub enum EventDirection {
126 Assert,
127 Deassert,
128}
129
130#[derive(Debug, Clone, PartialEq)]
131pub enum Entry {
132 System {
133 record_id: RecordId,
134 timestamp: Timestamp,
135 generator_id: EventGenerator,
136 event_message_format: EventMessageRevision,
137 sensor_type: u8,
138 sensor_number: u8,
139 event_direction: EventDirection,
140 event_type: u8,
141 event_data: [u8; 3],
142 },
143 OemTimestamped {
144 record_id: RecordId,
145 ty: u8,
146 timestamp: Timestamp,
147 manufacturer_id: u32,
148 data: [u8; 6],
149 },
150 OemNotTimestamped {
151 record_id: RecordId,
152 ty: u8,
153 data: [u8; 13],
154 },
155}
156
157#[derive(Debug, Clone, Copy, PartialEq)]
158pub enum ParseEntryError {
159 NotEnoughData,
160 UnknownRecordType(u8),
161}
162
163impl Entry {
164 pub fn parse(data: &[u8]) -> Result<Self, ParseEntryError> {
165 if data.len() < 15 {
166 return Err(ParseEntryError::NotEnoughData);
167 }
168
169 let record_id = RecordId(u16::from_le_bytes([data[0], data[1]]));
170 let record_type = SelRecordType::from(data[2]);
171 let timestamp = u32::from_le_bytes([data[3], data[4], data[5], data[6]]);
172
173 match record_type {
174 SelRecordType::System => {
175 let generator_id = EventGenerator::from((data[7], data[8]));
176 let event_message_format = EventMessageRevision::from(data[9]);
177 let sensor_type = data[10];
178 let sensor_number = data[11];
179 let event_direction = if (data[12] & 0x80) == 0x80 {
180 EventDirection::Assert
181 } else {
182 EventDirection::Deassert
183 };
184 let event_type = data[12] & 0x7F;
185 let event_data = [data[13], data[14], data[15]];
186 Ok(Self::System {
187 record_id,
188 timestamp: Timestamp::from(timestamp),
189 generator_id,
190 event_message_format,
191 sensor_type,
192 sensor_number,
193 event_direction,
194 event_type,
195 event_data,
196 })
197 }
198 SelRecordType::TimestampedOem(v) => Ok(Self::OemTimestamped {
199 record_id,
200 ty: v,
201 timestamp: Timestamp::from(timestamp),
202 manufacturer_id: u32::from_le_bytes([data[7], data[8], data[9], 0]),
203 data: [data[10], data[11], data[12], data[13], data[14], data[15]],
204 }),
205 SelRecordType::NonTimestampedOem(v) => Ok(Self::OemNotTimestamped {
206 record_id,
207 ty: v,
208 data: [
209 data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10],
210 data[11], data[12], data[13], data[14], data[15],
211 ],
212 }),
213 SelRecordType::Unknown(v) => Err(ParseEntryError::UnknownRecordType(v)),
214 }
215 }
216}
217
218impl Loggable for Entry {
219 fn as_log(&self) -> Vec<LogItem> {
220 match self {
221 Entry::System {
222 record_id,
223 timestamp,
224 generator_id,
225 event_message_format,
226 sensor_type,
227 sensor_number,
228 event_direction,
229 event_type,
230 event_data,
231 } => {
232 let format = match event_message_format {
233 EventMessageRevision::V2_0 => "2.0".into(),
234 EventMessageRevision::V1_0 => "1.0".into(),
235 EventMessageRevision::Unknown(v) => format!("Unknown (0x{:02X})", v),
236 };
237
238 let event_dir = match event_direction {
239 EventDirection::Assert => "Asserted",
240 EventDirection::Deassert => "Deasserted",
241 };
242
243 log_vec![
244 (0, "SEL entry"),
245 (1, "Record type", "System (0x02)"),
246 (1, "Record ID", format!("0x{:04X}", record_id.value())),
247 (1, "Time", timestamp),
248 (1, "Generator", format!("{:?}", generator_id)),
249 (1, "Format revision", format),
250 (1, "Sensor type", format!("0x{sensor_type:02X}")),
251 (1, "Sensor number", format!("0x{sensor_number:02X}")),
252 (1, "Assertion state", event_dir),
253 (1, "Event type", format!("0x{event_type:02X}")),
254 (1, "Data", format!("{event_data:02X?}")),
255 ]
256 }
257 Entry::OemTimestamped {
258 record_id,
259 ty,
260 timestamp,
261 manufacturer_id,
262 data,
263 } => {
264 log_vec![
265 (0, "SEL entry"),
266 (1, "Record type", format!("Timestamped OEM (0x{ty:08X})")),
267 (1, "Record ID", format!("0x{:04X}", record_id.value())),
268 (1, "Type", format!("{ty:02X}")),
269 (1, "Timestamp", timestamp),
270 (1, "Manufacturer ID", format!("{manufacturer_id:02X?}")),
271 (1, "Data", format!("{data:02X?}")),
272 ]
273 }
274 Entry::OemNotTimestamped {
275 record_id,
276 ty,
277 data,
278 } => {
279 log_vec![
280 (0, "SEL entry"),
281 (1, "Record type", format!("Not timestamp OEM (0x{ty:08X}")),
282 (1, "Record ID", format!("0x{:04X}", record_id.value())),
283 (1, "Type", format!("0x{ty:02X}")),
284 (1, "Data", format!("{data:02X?}"))
285 ]
286 }
287 }
288 }
289}