Skip to main content

report_device/
main.rs

1//! Reports a device with single on-ASIC temperature sensor in OCSD slot 2.
2//! On an ML350 Gen9, this corresponds to PCI slot 1, where the reported temperature
3//! is visible in iLO.
4//!
5//! Undefined behaviour may occur if this is run on other hardware.
6
7use {
8    ocsd::client::{base_address, OcsdContext},
9    ocsd::{
10        Celsius, MemoryMapped, OcsdDevice, OcsdDeviceHeader, OcsdSensor, OcsdSensorLocation,
11        OcsdSensorStatus, OcsdSensorType,
12    },
13    serde::{Deserialize, Serialize},
14    std::fs::OpenOptions,
15    std::sync::atomic::{self, AtomicBool, AtomicU16},
16    std::sync::Arc,
17    std::{cmp::min, time::Duration},
18};
19
20fn print_struct_bytes(bytes: &Vec<u8>) {
21    let num_chunks = bytes.len() / 8;
22
23    for chunk_idx in 0..num_chunks {
24        let max_idx = min((chunk_idx + 1) * 8, bytes.len());
25        for b in &bytes[chunk_idx * 8..max_idx] {
26            print!("{b:02x} ");
27        }
28        if chunk_idx % 2 == 0 {
29            print!(" ");
30        } else {
31            println!();
32        }
33    }
34}
35
36fn make_device(count: u16) -> OcsdDevice {
37    let header = OcsdDeviceHeader {
38        version: ocsd::DeviceVersion::Version1,
39        pci_bus: 0x04,
40        pci_device: 0x00,
41        flags_caps: 0x00000010,
42    };
43    println!("Device 2 header:");
44    print_struct_bytes(&header.to_bytes());
45
46    let bus: u8 = 0x04;
47
48    let sensor = OcsdSensor {
49        sensor_type: OcsdSensorType::Thermal,
50        sensor_location: OcsdSensorLocation::InternalToAsic,
51        configuration: 0x0000,
52        status: OcsdSensorStatus::WithChecksum
53            | OcsdSensorStatus::Present
54            | OcsdSensorStatus::NotFailed,
55        max_continuous_threshold: Celsius::new(80).unwrap(),
56        caution_threshold: Celsius::new(90).unwrap(),
57        reading: Celsius::new(40).unwrap(),
58        update_count: count,
59        bus: Some(bus),
60    };
61
62    println!("Device 2 Sensor 0:");
63    print_struct_bytes(&sensor.to_bytes());
64
65    OcsdDevice {
66        header,
67        sensors: [sensor, Default::default(), Default::default()],
68    }
69}
70
71#[derive(Serialize, Deserialize, Debug)]
72struct AppState {
73    count: u16,
74}
75
76fn main() {
77    match OcsdContext::new(base_address::ML350_GEN9) {
78        Ok(mut context) => {
79            let mut header = context.read_header();
80            println!("Header data before write:");
81            print_struct_bytes(&header.to_bytes());
82
83            // enable readings for device #2
84            header.buffers_in_use = 3;
85
86            println!("Ready to write:");
87            print_struct_bytes(&header.to_bytes());
88
89            context.write_header(&header);
90
91            let app_state: AppState = match OpenOptions::new().read(true).open("state.json") {
92                Ok(reader) => match serde_json::from_reader(reader) {
93                    Ok(app_state) => app_state,
94                    Err(err) => {
95                        println!("Couldn't load state: {:?}", err);
96                        println!("Using default.");
97                        AppState { count: 0 }
98                    }
99                },
100                Err(err) => {
101                    println!("Couldn't open state file: {:?}", err);
102                    println!("Using default.");
103                    AppState { count: 0 }
104                }
105            };
106            let count = Arc::new(AtomicU16::new(app_state.count));
107
108            let should_exit = Arc::new(AtomicBool::new(false));
109            let mut file = OpenOptions::new()
110                .create(true)
111                .truncate(true) // If the file already exists we want to overwrite the old data
112                .write(true)
113                .open("state.json")
114                .unwrap();
115
116            let should_exit_clone = should_exit.clone();
117            let count_clone = count.clone();
118            let _ = ctrlc::set_handler(move || {
119                serde_json::to_writer(
120                    &mut file,
121                    &AppState {
122                        count: (*count_clone).load(atomic::Ordering::Relaxed),
123                    },
124                )
125                .unwrap();
126                should_exit_clone.store(true, atomic::Ordering::Relaxed);
127            });
128
129            loop {
130                let device = make_device((*count).load(atomic::Ordering::Relaxed));
131                context.device_mappings[2].write(&device);
132
133                std::thread::sleep(Duration::from_millis(1000));
134                (*count).fetch_add(1, atomic::Ordering::Relaxed);
135
136                if should_exit.load(atomic::Ordering::Relaxed) {
137                    break;
138                };
139            }
140        }
141        Err(_) => {
142            println!(
143                "Unable to open OCSD header context in memory. Do you have access to /dev/mem?"
144            )
145        }
146    }
147}