ipmi_rs_core/storage/sel/
mod.rs

1use core::fmt;
2
3use crate::connection::{Channel, LogicalUnit};
4
5use super::Timestamp;
6use crate::storage::sdr::{decode_event, EventData, SensorType};
7
8mod clear;
9pub use clear::{ClearSel, ClearSelAction, ErasureProgress};
10
11mod get_alloc_info;
12pub use get_alloc_info::{AllocInfo as SelAllocInfo, GetAllocInfo as SelGetAllocInfo};
13
14mod get_entry;
15pub use get_entry::{EntryInfo as SelEntryInfo, GetEntry as GetSelEntry};
16
17mod get_info;
18pub use get_info::{Command as SelCommand, GetInfo as GetSelInfo, Info as SelInfo};
19
20mod reserve;
21pub use reserve::ReserveSel;
22
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct RecordId(u16);
25
26impl RecordId {
27    pub const FIRST: Self = Self(0x0000);
28    pub const LAST: Self = Self(0xFFFF);
29
30    pub fn new(id: u16) -> Option<Self> {
31        if RecordId(id) == Self::FIRST || RecordId(id) == Self::LAST {
32            None
33        } else {
34            Some(Self(id))
35        }
36    }
37
38    pub(crate) fn new_raw(id: u16) -> Self {
39        RecordId(id)
40    }
41
42    pub fn value(&self) -> u16 {
43        self.0
44    }
45
46    pub fn is_first(&self) -> bool {
47        self == &Self::FIRST
48    }
49
50    pub fn is_last(&self) -> bool {
51        self == &Self::LAST
52    }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq)]
56enum SelRecordType {
57    System,
58    TimestampedOem(u8),
59    NonTimestampedOem(u8),
60    Unknown(u8),
61}
62
63impl From<u8> for SelRecordType {
64    fn from(value: u8) -> Self {
65        match value {
66            0x02 => Self::System,
67            0xC0..=0xDF => Self::TimestampedOem(value),
68            0xE0..=0xFF => Self::NonTimestampedOem(value),
69            v => Self::Unknown(v),
70        }
71    }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq)]
75pub enum EventGenerator {
76    RqSAAndLun {
77        i2c_addr: u8,
78        channel_number: Channel,
79        lun: LogicalUnit,
80    },
81    SoftwareId {
82        software_id: u8,
83        channel_number: Channel,
84    },
85}
86
87impl TryFrom<(u8, u8)> for EventGenerator {
88    type Error = ParseEntryError;
89
90    fn try_from(value: (u8, u8)) -> Result<Self, Self::Error> {
91        let is_software_id = (value.0 & 0x1) == 0x1;
92        let i2c_or_sid = (value.0 >> 1) & 0x7F;
93        let channel_value = (value.1 >> 4) & 0xF;
94
95        let channel_number =
96            Channel::new(channel_value).ok_or(ParseEntryError::InvalidChannel(channel_value))?;
97
98        if is_software_id {
99            Ok(Self::SoftwareId {
100                software_id: i2c_or_sid,
101                channel_number,
102            })
103        } else {
104            let lun = LogicalUnit::from_low_bits(value.1);
105
106            Ok(Self::RqSAAndLun {
107                i2c_addr: i2c_or_sid,
108                channel_number,
109                lun,
110            })
111        }
112    }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq)]
116pub enum EventMessageRevision {
117    V2_0,
118    V1_0,
119    Unknown(u8),
120}
121
122impl From<u8> for EventMessageRevision {
123    fn from(value: u8) -> Self {
124        match value {
125            0x04 => Self::V2_0,
126            0x03 => Self::V1_0,
127            v => Self::Unknown(v),
128        }
129    }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq)]
133pub enum EventDirection {
134    Assert,
135    Deassert,
136}
137
138#[derive(Debug, Clone, PartialEq)]
139pub enum Entry {
140    System {
141        record_id: RecordId,
142        timestamp: Timestamp,
143        generator_id: EventGenerator,
144        event_message_format: EventMessageRevision,
145        sensor_type: u8,
146        sensor_number: u8,
147        event_direction: EventDirection,
148        event_type: u8,
149        event_data: EventData,
150    },
151    OemTimestamped {
152        record_id: RecordId,
153        ty: u8,
154        timestamp: Timestamp,
155        manufacturer_id: u32,
156        data: [u8; 6],
157    },
158    OemNotTimestamped {
159        record_id: RecordId,
160        ty: u8,
161        data: [u8; 13],
162    },
163}
164
165#[derive(Debug, Clone, Copy, PartialEq)]
166pub enum ParseEntryError {
167    NotEnoughData,
168    UnknownRecordType(u8),
169    InvalidChannel(u8),
170}
171
172impl Entry {
173    pub fn event_description(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            Entry::System {
176                event_type,
177                sensor_type,
178                event_data,
179                ..
180            } => decode_event(
181                f,
182                *event_type,
183                SensorType::from(*sensor_type),
184                event_data.offset,
185            ),
186            _ => Ok(()),
187        }
188    }
189
190    pub fn parse(data: &[u8]) -> Result<Self, ParseEntryError> {
191        if data.len() < 16 {
192            return Err(ParseEntryError::NotEnoughData);
193        }
194
195        let record_id = RecordId(u16::from_le_bytes([data[0], data[1]]));
196        let record_type = SelRecordType::from(data[2]);
197        let timestamp = u32::from_le_bytes([data[3], data[4], data[5], data[6]]);
198
199        match record_type {
200            SelRecordType::System => {
201                let generator_id = EventGenerator::try_from((data[7], data[8]))?;
202                let event_message_format = EventMessageRevision::from(data[9]);
203                let sensor_type = data[10];
204                let sensor_number = data[11];
205                let event_direction = if (data[12] & 0x80) == 0x80 {
206                    EventDirection::Deassert
207                } else {
208                    EventDirection::Assert
209                };
210                let event_type = data[12] & 0x7F;
211                let event_data = EventData::parse(&[data[13], data[14], data[15]]);
212                Ok(Self::System {
213                    record_id,
214                    timestamp: Timestamp::from(timestamp),
215                    generator_id,
216                    event_message_format,
217                    sensor_type,
218                    sensor_number,
219                    event_direction,
220                    event_type,
221                    event_data,
222                })
223            }
224            SelRecordType::TimestampedOem(v) => Ok(Self::OemTimestamped {
225                record_id,
226                ty: v,
227                timestamp: Timestamp::from(timestamp),
228                manufacturer_id: u32::from_le_bytes([data[7], data[8], data[9], 0]),
229                data: [data[10], data[11], data[12], data[13], data[14], data[15]],
230            }),
231            SelRecordType::NonTimestampedOem(v) => Ok(Self::OemNotTimestamped {
232                record_id,
233                ty: v,
234                data: [
235                    data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10],
236                    data[11], data[12], data[13], data[14], data[15],
237                ],
238            }),
239            SelRecordType::Unknown(v) => Err(ParseEntryError::UnknownRecordType(v)),
240        }
241    }
242}