base/sys/linux/
acpi_event.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::str;
6
7use thiserror::Error;
8use zerocopy::FromBytes;
9use zerocopy::Immutable;
10use zerocopy::KnownLayout;
11
12use super::netlink::*;
13
14const ACPI_EVENT_SIZE: usize = std::mem::size_of::<AcpiGenlEvent>();
15const GENL_HDRLEN: usize = std::mem::size_of::<GenlMsgHdr>();
16const NLA_HDRLEN: usize = std::mem::size_of::<NlAttr>();
17
18#[derive(Error, Debug)]
19pub enum AcpiEventError {
20    #[error("GenmsghdrCmd or NlAttrType inappropriate for acpi event")]
21    TypeAttrMissmatch,
22    #[error("Something goes wrong: msg_len {0} is not correct")]
23    InvalidMsgLen(usize),
24}
25type Result<T> = std::result::Result<T, AcpiEventError>;
26
27/// attributes of AcpiGenlFamily
28#[allow(dead_code)]
29enum NlAttrType {
30    AcpiGenlAttrUnspec,
31    AcpiGenlAttrEvent, // acpi_event (needed by user space)
32    AcpiGenlAttrMax,
33}
34
35/// commands supported by the AcpiGenlFamily
36#[allow(dead_code)]
37enum GenmsghdrCmd {
38    AcpiGenlCmdUnspec,
39    AcpiGenlCmdEvent, // kernel->user notifications for acpi_events
40    AcpiGenlCmdMax,
41}
42
43#[repr(C)]
44#[derive(Copy, Clone, FromBytes, Immutable, KnownLayout)]
45struct AcpiGenlEvent {
46    device_class: [u8; 20],
47    bus_id: [u8; 15],
48    _type: u32,
49    data: u32,
50}
51
52pub struct AcpiNotifyEvent {
53    pub device_class: String,
54    pub bus_id: String,
55    pub _type: u32,
56    pub data: u32,
57}
58
59impl AcpiNotifyEvent {
60    /// Create acpi event by decapsulating it from NetlinkMessage.
61    pub fn new(netlink_message: NetlinkMessage) -> Result<Self> {
62        let msg_len = netlink_message.data.len();
63        if msg_len != GENL_HDRLEN + NLA_HDRLEN + ACPI_EVENT_SIZE {
64            return Err(AcpiEventError::InvalidMsgLen(msg_len));
65        }
66
67        let (genl_hdr, nl_attr) = GenlMsgHdr::read_from_prefix(netlink_message.data)
68            .expect("unable to get GenlMsgHdr from slice");
69
70        let (nl_attr, body) =
71            NlAttr::read_from_prefix(nl_attr).expect("unable to get NlAttr from slice");
72
73        // Sanity check that the headers have correct for acpi event `cmd` and `_type`
74        if genl_hdr.cmd != GenmsghdrCmd::AcpiGenlCmdEvent as u8
75            || nl_attr._type != NlAttrType::AcpiGenlAttrEvent as u16
76        {
77            return Err(AcpiEventError::TypeAttrMissmatch);
78        }
79
80        let acpi_event =
81            AcpiGenlEvent::read_from_bytes(body).expect("unable to get AcpiGenlEvent from slice");
82
83        Ok(AcpiNotifyEvent {
84            device_class: strip_padding(&acpi_event.device_class).to_owned(),
85            bus_id: strip_padding(&acpi_event.bus_id).to_owned(),
86            _type: acpi_event._type,
87            data: acpi_event.data,
88        })
89    }
90}
91
92// Like `CStr::from_bytes_with_nul` but strips any bytes starting from first '\0'-byte and
93// returns &str. Panics if `b` doesn't contain any '\0' bytes.
94fn strip_padding(b: &[u8]) -> &str {
95    // It would be nice if we could use memchr here but that's locked behind an unstable gate.
96    let pos = b
97        .iter()
98        .position(|&c| c == 0)
99        .expect("`b` doesn't contain any nul bytes");
100
101    str::from_utf8(&b[..pos]).unwrap()
102}