1use crate::{
2 connection::{IpmiCommand, Message, NetFn, ParseResponseError},
3 fmt::LogItem,
4 log_vec,
5 storage::Timestamp,
6 Loggable,
7};
8
9pub struct GetRepositoryInfo;
10
11impl From<GetRepositoryInfo> for Message {
12 fn from(_: GetRepositoryInfo) -> Self {
13 Message::new_request(NetFn::Storage, 0x20, Vec::new())
14 }
15}
16
17impl IpmiCommand for GetRepositoryInfo {
18 type Output = RepositoryInfo;
19
20 type Error = ();
21
22 fn parse_response(
23 completion_code: crate::connection::CompletionCode,
24 data: &[u8],
25 ) -> Result<Self::Output, ParseResponseError<Self::Error>> {
26 Self::check_cc_success(completion_code)?;
27
28 RepositoryInfo::parse(data).ok_or(ParseResponseError::NotEnoughData)
29 }
30}
31
32#[derive(Clone, Copy, Debug, PartialEq)]
33pub enum FreeSpace {
34 Full,
35 AtLeast { bytes: u16 },
36 Unspecified,
37}
38
39impl From<u16> for FreeSpace {
40 fn from(value: u16) -> Self {
41 match value {
42 0x0000 => Self::Full,
43 0xFFFF => Self::Unspecified,
44 v => Self::AtLeast { bytes: v },
45 }
46 }
47}
48
49impl core::fmt::Display for FreeSpace {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 FreeSpace::Full => write!(f, "Full"),
53 FreeSpace::AtLeast { bytes } => write!(f, "At least {} bytes", bytes),
54 FreeSpace::Unspecified => write!(f, "Unspecified"),
55 }
56 }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq)]
60pub enum Operation {
61 ModalityUnspecified,
62 NonModalUpdate,
63 ModalUpdate,
64 NonModalAndModalUpdate,
65 Delete,
66 PartialAdd,
67 Reserve,
68 GetAllocInfo,
69}
70
71#[derive(Clone, Debug)]
72pub struct RepositoryInfo {
73 pub version_major: u8,
74 pub version_minor: u8,
75 pub record_count: u16,
76 pub free_space: FreeSpace,
77 pub most_recent_addition: Timestamp,
78 pub most_recent_erase: Timestamp,
79 pub overflow: bool,
80 pub supported_ops: Vec<Operation>,
81}
82
83impl RepositoryInfo {
84 pub fn parse(v: &[u8]) -> Option<Self> {
85 let version_minor = (v[0] & 0xF0) >> 4;
86 let version_major = v[0] & 0x0F;
87 let record_count = u16::from_le_bytes([v[1], v[2]]);
88 let free_space = FreeSpace::from(u16::from_le_bytes([v[3], v[4]]));
89 let most_recent_addition = Timestamp::from(u32::from_le_bytes([v[5], v[6], v[7], v[8]]));
90 let most_recent_erase = Timestamp::from(u32::from_le_bytes([v[9], v[10], v[11], v[12]]));
91 let overflow = (v[13] & 0x80) == 0x80;
92
93 let modality = v[13] & 0x60 >> 5;
94 let modality = match modality {
95 0b00 => Operation::ModalityUnspecified,
96 0b01 => Operation::NonModalUpdate,
97 0b10 => Operation::ModalUpdate,
98 0b11 => Operation::NonModalAndModalUpdate,
99 _ => unreachable!(),
100 };
101
102 let mut ops = Vec::with_capacity(5);
103 ops.push(modality);
104
105 for (offset, command) in [
106 Operation::GetAllocInfo,
107 Operation::Reserve,
108 Operation::PartialAdd,
109 Operation::Delete,
110 ]
111 .into_iter()
112 .enumerate()
113 {
114 if v[13] & (1 << offset) == (1 << offset) {
115 ops.push(command);
116 }
117 }
118
119 Some(Self {
120 version_major,
121 version_minor,
122 record_count,
123 free_space,
124 most_recent_addition,
125 most_recent_erase,
126 overflow,
127 supported_ops: ops,
128 })
129 }
130}
131
132impl Loggable for RepositoryInfo {
133 fn as_log(&self) -> Vec<LogItem> {
134 let Self {
135 version_major,
136 version_minor,
137 record_count,
138 free_space,
139 most_recent_addition,
140 most_recent_erase,
141 overflow,
142 supported_ops,
143 } = self;
144
145 let (v_maj, v_min) = (version_major, version_minor);
146
147 log_vec![
148 (0, "SDR Repository Information"),
149 (1, "Version", format!("{v_maj}.{v_min}")),
150 (1, "Record count", record_count),
151 (1, "Free space", free_space),
152 (1, "Most recent add", most_recent_addition),
153 (1, "Most recent erase", most_recent_erase),
154 (1, "SDR Overflow", overflow),
155 (1, "Supported ops", format!("{supported_ops:?}"))
156 ]
157 }
158}