• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 use std::str;
6 
7 use thiserror::Error;
8 use zerocopy::FromBytes;
9 use zerocopy::Immutable;
10 use zerocopy::KnownLayout;
11 
12 use super::netlink::*;
13 
14 const ACPI_EVENT_SIZE: usize = std::mem::size_of::<AcpiGenlEvent>();
15 const GENL_HDRLEN: usize = std::mem::size_of::<GenlMsgHdr>();
16 const NLA_HDRLEN: usize = std::mem::size_of::<NlAttr>();
17 
18 #[derive(Error, Debug)]
19 pub 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 }
25 type Result<T> = std::result::Result<T, AcpiEventError>;
26 
27 /// attributes of AcpiGenlFamily
28 #[allow(dead_code)]
29 enum 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)]
37 enum 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)]
45 struct AcpiGenlEvent {
46     device_class: [u8; 20],
47     bus_id: [u8; 15],
48     _type: u32,
49     data: u32,
50 }
51 
52 pub struct AcpiNotifyEvent {
53     pub device_class: String,
54     pub bus_id: String,
55     pub _type: u32,
56     pub data: u32,
57 }
58 
59 impl AcpiNotifyEvent {
60     /// Create acpi event by decapsulating it from NetlinkMessage.
new(netlink_message: NetlinkMessage) -> Result<Self>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.
strip_padding(b: &[u8]) -> &str94 fn 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 }
103