1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use std::num::NonZeroU16;

use nonmax::NonMaxU8;

use crate::connection::{IpmiCommand, Message, NetFn, ParseResponseError};

use super::{record::Record, RecordId};

/// Get a device SDR.
///
/// This command must be used in accordance with the IPMI spec, i.e.
/// all SDRs must be obtained sequentially. It is recommended that you use
/// the [`Ipmi::sdrs`] function for this.
///
/// [`Ipmi::sdrs`]: crate::Ipmi::sdrs
#[derive(Debug, Clone, Copy)]
pub struct GetDeviceSdr {
    reservation_id: Option<NonZeroU16>,
    record_id: RecordId,
    offset: u8,
    bytes_to_read: Option<NonMaxU8>,
}

impl GetDeviceSdr {
    pub fn new(reservation_id: Option<NonZeroU16>, record_id: RecordId) -> Self {
        Self {
            reservation_id,
            record_id,
            // Always read all bytes
            offset: 0,
            bytes_to_read: None,
        }
    }
}

impl From<GetDeviceSdr> for Message {
    fn from(value: GetDeviceSdr) -> Self {
        let mut data = vec![0u8; 6];

        data[0..2].copy_from_slice(
            &value
                .reservation_id
                .map(NonZeroU16::get)
                .unwrap_or(0)
                .to_le_bytes(),
        );

        data[2..4].copy_from_slice(&value.record_id.value().to_le_bytes());
        data[4] = value.offset;
        data[5] = value.bytes_to_read.map(|v| v.get()).unwrap_or(0xFF);

        Message::new_request(NetFn::Storage, 0x23, data)
    }
}

impl IpmiCommand for GetDeviceSdr {
    type Output = RecordInfo;

    type Error = ();

    fn parse_response(
        completion_code: crate::connection::CompletionCode,
        data: &[u8],
    ) -> Result<Self::Output, ParseResponseError<Self::Error>> {
        Self::check_cc_success(completion_code)?;

        RecordInfo::parse(data).ok_or(ParseResponseError::NotEnoughData)
    }
}

#[derive(Debug, Clone)]
pub struct RecordInfo {
    pub next_entry: RecordId,
    pub record: Record,
}

impl RecordInfo {
    pub fn parse(data: &[u8]) -> Option<Self> {
        if data.len() < 9 {
            return None;
        }

        let next_entry = RecordId::new_raw(u16::from_le_bytes([data[0], data[1]]));
        let data = &data[2..];

        Record::parse(data).map(|record| Self { next_entry, record })
    }
}