Skip to main content

thermal_netlink/
netlink_types.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// Command code definition of Netlink controller (thermal) family
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub enum GenlThermalCmd {
76    /// Notify from sample.
77    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/// Payload of thermal netlink server.
104#[derive(Clone, Debug, PartialEq, Eq)]
105pub struct GenlThermalPayload {
106    /// Command code of this message
107    pub cmd: GenlThermalCmd,
108    /// Netlink attributes in this message
109    pub nlas: Vec<ThermalAttr>,
110}
111
112#[derive(Clone, Debug, PartialEq, Eq)]
113pub struct GenlThermal {
114    pub payload: GenlThermalPayload,
115    /// The family ID of the message.
116    /// Assigned by the GenericNetlink server.
117    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}