ipmi_rs_core/connection/
mod.rs

1#![deny(missing_docs)]
2//! IPMI-connection related data.
3
4mod completion_code;
5use std::num::NonZeroU8;
6
7pub use completion_code::CompletionErrorCode;
8
9mod netfn;
10pub use netfn::NetFn;
11
12mod request;
13pub use request::{Request, RequestTargetAddress};
14
15mod response;
16pub use response::Response;
17
18/// The address of an IPMI module or sensor.
19#[derive(Copy, Clone, Debug, PartialEq)]
20pub struct Address(pub u8);
21
22/// A numbered channel.
23///
24/// The value of a channel is always less than `0xB`.
25#[derive(Copy, Clone, Debug, PartialEq)]
26pub struct ChannelNumber(NonZeroU8);
27
28impl ChannelNumber {
29    /// Create a new `ChannelNumber`.
30    ///
31    /// This function returns `None` if `value > 0xB`
32    pub fn new(value: NonZeroU8) -> Option<Self> {
33        if value.get() <= 0xB {
34            Some(Self(value))
35        } else {
36            None
37        }
38    }
39
40    /// Get the value of this `ChannelNumber`.
41    ///
42    /// It is guaranteed that values returned by
43    /// this function are less than or equal to `0xB`
44    pub fn value(&self) -> NonZeroU8 {
45        self.0
46    }
47}
48
49/// The channel on which an IPMI module or sensor is present.
50#[derive(Copy, Clone, Debug, PartialEq)]
51pub enum Channel {
52    /// The primary channel.
53    Primary,
54    /// A numbered channel.
55    Numbered(ChannelNumber),
56    /// The system channel.
57    System,
58    /// The current channel, for some definition of current.
59    Current,
60}
61
62impl Channel {
63    /// Create a new `Channel`.
64    ///
65    /// This function returns `None` for invalid channel values. `value` is invalid if `value == 0xC || value == 0xD || value > 0xF`.
66    pub fn new(value: u8) -> Option<Self> {
67        match value {
68            0 => Some(Self::Primary),
69            0xE => Some(Self::Current),
70            0xF => Some(Self::System),
71            v => Some(Self::Numbered(ChannelNumber::new(NonZeroU8::new(v)?)?)),
72        }
73    }
74
75    /// The number of this channel.
76    ///
77    /// This value is guaranteed to be less than or
78    /// equal to 0xF.
79    pub fn value(&self) -> u8 {
80        match self {
81            Channel::Primary => 0x0,
82            Channel::Numbered(v) => v.value().get(),
83            Channel::Current => 0xE,
84            Channel::System => 0xF,
85        }
86    }
87}
88
89impl core::fmt::Display for Channel {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Channel::Primary => write!(f, "Primary channel"),
93            Channel::Numbered(number) => write!(f, "Channel 0x{:01X}", number.value()),
94            Channel::Current => write!(f, "Current channel"),
95            Channel::System => write!(f, "System channel"),
96        }
97    }
98}
99
100/// The logical unit of an IPMI module/sensor.
101#[derive(Clone, Copy, Debug, PartialEq)]
102#[allow(missing_docs)]
103pub enum LogicalUnit {
104    Zero,
105    One,
106    Two,
107    Three,
108}
109
110impl LogicalUnit {
111    /// Construct a `LogicalUnit` from the two lowest bits of `value`,
112    /// ignoring all other bits.
113    pub fn from_low_bits(value: u8) -> Self {
114        let value = value & 0b11;
115
116        match value {
117            0b00 => Self::Zero,
118            0b01 => Self::One,
119            0b10 => Self::Two,
120            0b11 => Self::Three,
121            _ => unreachable!("Value bitmasked with 0b11 has value greater than 3"),
122        }
123    }
124}
125
126impl TryFrom<u8> for LogicalUnit {
127    type Error = ();
128
129    fn try_from(value: u8) -> Result<Self, Self::Error> {
130        if value <= 0b11 {
131            Ok(Self::from_low_bits(value))
132        } else {
133            Err(())
134        }
135    }
136}
137
138impl LogicalUnit {
139    /// Get a raw value describing this logical unit.
140    ///
141    /// This value is always in the range `0..=3`.
142    pub fn value(&self) -> u8 {
143        match self {
144            LogicalUnit::Zero => 0,
145            LogicalUnit::One => 1,
146            LogicalUnit::Two => 2,
147            LogicalUnit::Three => 3,
148        }
149    }
150}
151
152impl From<LogicalUnit> for u8 {
153    fn from(value: LogicalUnit) -> Self {
154        value.value()
155    }
156}
157
158/// A generic error indicating that the message did not contain
159/// enough data to constitute a valid response.
160#[derive(Debug, Clone, Copy, PartialEq)]
161pub struct NotEnoughData;
162
163/// A trait describing operations that can be performed on an IPMI connection.
164pub trait IpmiConnection {
165    /// The type of error the can occur when sending a [`Request`].
166    type SendError: core::fmt::Debug;
167    /// The type of error that can occur when receiving a [`Response`].
168    type RecvError: core::fmt::Debug;
169    /// The type of error the can occur when sending a [`Request`] or receiving a [`Response`].
170    type Error: core::fmt::Debug + From<Self::SendError> + From<Self::RecvError>;
171
172    /// Send `request` to the remote end of this connection.
173    fn send(&mut self, request: &mut Request) -> Result<(), Self::SendError>;
174
175    /// Receive a response from the remote end of this connection.
176    fn recv(&mut self) -> Result<Response, Self::RecvError>;
177
178    /// Send `request` to and reveive a response from the remote end of this connection.
179    fn send_recv(&mut self, request: &mut Request) -> Result<Response, Self::Error>;
180}
181
182/// The wire representation of an IPMI messag.e
183#[derive(Clone, Debug, PartialEq)]
184pub struct Message {
185    netfn: u8,
186    cmd: u8,
187    data: Vec<u8>,
188}
189
190impl Message {
191    /// Create a new request message with the provided `netfn`, `cmd` and `data`.
192    pub fn new_request(netfn: NetFn, cmd: u8, data: Vec<u8>) -> Self {
193        Self {
194            netfn: netfn.request_value(),
195            cmd,
196            data,
197        }
198    }
199
200    /// Create a new response message with the provided `netfn`, `cmd` and `data`.
201    pub fn new_response(netfn: NetFn, cmd: u8, data: Vec<u8>) -> Self {
202        Self {
203            netfn: netfn.response_value(),
204            cmd,
205            data,
206        }
207    }
208
209    /// Create a new message with the provided raw `netfn`, `cmd` and `data`.
210    pub fn new_raw(netfn: u8, cmd: u8, data: Vec<u8>) -> Self {
211        Self { netfn, cmd, data }
212    }
213
214    /// Get the netfn of the message.
215    pub fn netfn(&self) -> NetFn {
216        NetFn::from(self.netfn)
217    }
218
219    /// Get the raw netfn value for the message.
220    pub fn netfn_raw(&self) -> u8 {
221        self.netfn
222    }
223
224    /// Get the command value for this message.
225    pub fn cmd(&self) -> u8 {
226        self.cmd
227    }
228
229    /// Get a reference to the data for this message.
230    pub fn data(&self) -> &[u8] {
231        &self.data
232    }
233
234    /// Get a mutable reference to the data for this message.
235    pub fn data_mut(&mut self) -> &mut [u8] {
236        &mut self.data
237    }
238}
239
240/// An IPMI command that can be turned into a request, and whose response can be parsed
241/// from response data.
242pub trait IpmiCommand: Into<Message> {
243    /// The output of this command, i.e. the expected response type.
244    type Output;
245    /// The type of error that can occur while parsing the response for this
246    /// command.
247    type Error;
248
249    /// Handle the provided completion code `completion_code` and optionally provide
250    /// a special error in case of failure.
251    ///
252    /// Non-success completion codes for which this function returns `None` should be
253    /// handled by the caller of `parse_success_response`.
254    ///
255    /// The default implementation of this function performs no special handling
256    /// and returns `None`.
257    #[allow(unused)]
258    fn handle_completion_code(
259        completion_code: CompletionErrorCode,
260        data: &[u8],
261    ) -> Option<Self::Error> {
262        None
263    }
264
265    /// Try to parse the expected response for this command from the
266    /// provided `data`, assuming a successful completion code.
267    fn parse_success_response(data: &[u8]) -> Result<Self::Output, Self::Error>;
268
269    /// Get the intended target [`Address`] and [`Channel`] for this commmand.
270    fn target(&self) -> Option<(Address, Channel)> {
271        None
272    }
273}