Skip to main content

ocsd/client/
mod.rs

1//! Client interface for interacting with the OCSD buffer via /dev/mem on Linux.
2
3pub mod base_address;
4mod error;
5
6use devmem::Mapping;
7use error::MappingError;
8
9use crate::protocol::{MemoryMapped, OcsdDevice, OcsdHeader};
10
11const OCSD_HEADER_SIZE: usize = 0x40;
12
13/// Context representing the complete OCSD buffer, including header and all devices
14pub struct OcsdContext {
15    header_mapping: Mapping,
16    /// Vec of device contexts, each corresponding to a slice of the OCSD buffer.
17    /// All are open and available following construction of the [OcsdContext].
18    pub device_mappings: Vec<OcsdDeviceContext>,
19}
20
21/// Context representing a single OCSD device
22pub struct OcsdDeviceContext {
23    mapping: Mapping,
24    device_size: u8,
25}
26
27impl OcsdHeader {
28    fn open_device_mapping(&self, device_index: u8) -> Result<Mapping, MappingError> {
29        if device_index >= self.max_option_cards {
30            return Err(MappingError::new(format!(
31                "requested device index {} doesn't fit max number of option cards {}",
32                device_index, self.max_option_cards
33            )));
34        }
35        let start_address = self.buffer_start_address as usize
36            + (self.one_option_card_size as usize * device_index as usize);
37        unsafe {
38            Mapping::new(start_address, self.one_option_card_size as usize).map_err(|_| {
39                MappingError::new(format!(
40                    "unable to open device mapping at {:x}",
41                    start_address
42                ))
43            })
44        }
45    }
46}
47
48impl OcsdContext {
49    /// Create a new [OcsdContext] given a provided base address.
50    /// The header will be read and parsed to determine the number of available
51    /// option card slots.
52    pub fn new(base_address: usize) -> Result<Self, MappingError> {
53        let header_mapping_result = unsafe { Mapping::new(base_address, OCSD_HEADER_SIZE) };
54        match header_mapping_result {
55            Ok(mut header_mapping) => {
56                let init_header = Self::_read_header(&mut header_mapping);
57                let mut device_mappings: Vec<OcsdDeviceContext> = Vec::new();
58
59                for i in 0..init_header.max_option_cards {
60                    match init_header.open_device_mapping(i) {
61                        Ok(device_mapping) => device_mappings.push(OcsdDeviceContext {
62                            mapping: device_mapping,
63                            device_size: init_header.one_option_card_size,
64                        }),
65                        Err(e) => return Err(e),
66                    }
67                }
68
69                Ok(Self {
70                    header_mapping,
71                    device_mappings,
72                })
73            }
74            Err(_) => Err(MappingError::new(format!(
75                "unable to open ocsd header at {:x}",
76                base_address
77            ))),
78        }
79    }
80
81    fn _read_header(header_mapping: &mut Mapping) -> OcsdHeader {
82        let mut header_data: Vec<u8> = vec![0x00; OCSD_HEADER_SIZE];
83        header_mapping.copy_into_slice(&mut header_data);
84        OcsdHeader::from_bytes(&header_data)
85    }
86
87    /// Re-read and parse the header from the OCSD buffer.
88    pub fn read_header(&mut self) -> OcsdHeader {
89        Self::_read_header(&mut self.header_mapping)
90    }
91
92    /// Replace the header in the OCSD buffer with the one provided.
93    pub fn write_header(&mut self, device: &OcsdHeader) {
94        self.header_mapping.copy_from_slice(&device.to_bytes());
95    }
96}
97
98impl OcsdDeviceContext {
99    /// Read and parse this device from the OCSD buffer.
100    pub fn read(&mut self) -> OcsdDevice {
101        let mut device_data: Vec<u8> = vec![0x00; self.device_size as usize];
102        self.mapping.copy_into_slice(&mut device_data);
103        OcsdDevice::from_bytes(&device_data)
104    }
105
106    /// Replace the device data in the OCSD buffer with that provided.
107    pub fn write(&mut self, device: &OcsdDevice) {
108        self.mapping.copy_from_slice(&device.to_bytes());
109    }
110}