thermal_netlink/
netlink_types.rs1use anyhow::Context;
6use linux_uapi::{
7 THERMAL_GENL_FAMILY_NAME, THERMAL_GENL_VERSION,
8 thermal_genl_attr_THERMAL_GENL_ATTR_TZ_ID as THERMAL_GENL_ATTR_TZ_ID,
9 thermal_genl_attr_THERMAL_GENL_ATTR_TZ_TEMP as THERMAL_GENL_ATTR_TZ_TEMP,
10 thermal_genl_sampling_THERMAL_GENL_SAMPLING_TEMP as THERMAL_GENL_SAMPLING_TEMP,
11};
12use netlink_packet_generic::{GenlFamily, GenlHeader};
13use netlink_packet_utils::byteorder::{ByteOrder, NativeEndian};
14use netlink_packet_utils::nla::{Nla, NlaBuffer, NlasIterator};
15use netlink_packet_utils::parsers::parse_u32;
16use netlink_packet_utils::{DecodeError, Emitable, Parseable, ParseableParametrized};
17use std::mem::size_of_val;
18
19pub fn celsius_to_millicelsius(temp: f32) -> f32 {
20 temp * 1000.0
21}
22
23pub fn millicelsius_to_celsius(temp: f32) -> f32 {
24 temp / 1000.0
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub enum ThermalAttr {
29 ThermalZoneId(u32),
30 ThermalZoneTemp(u32),
31}
32
33impl Nla for ThermalAttr {
34 fn value_len(&self) -> usize {
35 use ThermalAttr::*;
36 match self {
37 ThermalZoneId(v) => size_of_val(v),
38 ThermalZoneTemp(v) => size_of_val(v),
39 }
40 }
41
42 fn kind(&self) -> u16 {
43 use ThermalAttr::*;
44 match self {
45 ThermalZoneId(_) => THERMAL_GENL_ATTR_TZ_ID as u16,
46 ThermalZoneTemp(_) => THERMAL_GENL_ATTR_TZ_TEMP as u16,
47 }
48 }
49
50 fn emit_value(&self, buffer: &mut [u8]) {
51 use ThermalAttr::*;
52 match self {
53 ThermalZoneId(id) => NativeEndian::write_u32(buffer, *id),
54 ThermalZoneTemp(temp) => NativeEndian::write_u32(buffer, *temp),
55 };
56 }
57}
58
59impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for ThermalAttr {
60 type Error = DecodeError;
61 fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
62 let payload = buf.value();
63 let kind = buf.kind() as u32;
64
65 Ok(match kind {
66 THERMAL_GENL_ATTR_TZ_ID => Self::ThermalZoneId(parse_u32(payload)?),
67 THERMAL_GENL_ATTR_TZ_TEMP => Self::ThermalZoneTemp(parse_u32(payload)?),
68 kind => return Err(DecodeError::from(format!("Unknown NLA type: {kind}"))),
69 })
70 }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub enum GenlThermalCmd {
76 ThermalGenlSamplingTemp,
78}
79
80impl From<GenlThermalCmd> for u8 {
81 fn from(cmd: GenlThermalCmd) -> u8 {
82 use GenlThermalCmd::*;
83 match cmd {
84 ThermalGenlSamplingTemp => THERMAL_GENL_SAMPLING_TEMP as u8,
85 }
86 }
87}
88
89impl TryFrom<u8> for GenlThermalCmd {
90 type Error = DecodeError;
91
92 fn try_from(value: u8) -> Result<Self, Self::Error> {
93 use GenlThermalCmd::*;
94 let cmd_code = value as u32;
95
96 Ok(match cmd_code {
97 THERMAL_GENL_SAMPLING_TEMP => ThermalGenlSamplingTemp,
98 cmd => return Err(DecodeError::from(format!("Unknown thermal command: {cmd}"))),
99 })
100 }
101}
102
103#[derive(Clone, Debug, PartialEq, Eq)]
105pub struct GenlThermalPayload {
106 pub cmd: GenlThermalCmd,
108 pub nlas: Vec<ThermalAttr>,
110}
111
112#[derive(Clone, Debug, PartialEq, Eq)]
113pub struct GenlThermal {
114 pub payload: GenlThermalPayload,
115 pub family_id: u16,
118}
119
120impl GenlFamily for GenlThermal {
121 fn family_name() -> &'static str {
122 THERMAL_GENL_FAMILY_NAME.to_str().expect("family name has invalid UTF-8 data")
123 }
124
125 fn family_id(&self) -> u16 {
126 self.family_id
127 }
128
129 fn command(&self) -> u8 {
130 self.payload.cmd.into()
131 }
132
133 fn version(&self) -> u8 {
134 THERMAL_GENL_VERSION as u8
135 }
136}
137
138impl Emitable for GenlThermal {
139 fn emit(&self, buffer: &mut [u8]) {
140 self.payload.nlas.as_slice().emit(buffer)
141 }
142
143 fn buffer_len(&self) -> usize {
144 self.payload.nlas.as_slice().buffer_len()
145 }
146}
147
148impl ParseableParametrized<[u8], GenlHeader> for GenlThermalPayload {
149 type Error = DecodeError;
150 fn parse_with_param(buf: &[u8], header: GenlHeader) -> Result<Self, DecodeError> {
151 let nlas = NlasIterator::new(buf)
152 .map(|nla| {
153 nla.map_err(|err| DecodeError::Nla(err)).and_then(|nla| ThermalAttr::parse(&nla))
154 })
155 .collect::<Result<Vec<_>, _>>()
156 .context("failed to parse thermal message attributes")?;
157
158 Ok(Self { cmd: header.cmd.try_into()?, nlas })
159 }
160}