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