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::FromZeroes;
10
11 use super::netlink::*;
12
13 const ACPI_EVENT_SIZE: usize = std::mem::size_of::<AcpiGenlEvent>();
14 const GENL_HDRLEN: usize = std::mem::size_of::<GenlMsgHdr>();
15 const NLA_HDRLEN: usize = std::mem::size_of::<NlAttr>();
16
17 #[derive(Error, Debug)]
18 pub enum AcpiEventError {
19 #[error("GenmsghdrCmd or NlAttrType inappropriate for acpi event")]
20 TypeAttrMissmatch,
21 #[error("Something goes wrong: msg_len {0} is not correct")]
22 InvalidMsgLen(usize),
23 }
24 type Result<T> = std::result::Result<T, AcpiEventError>;
25
26 /// attributes of AcpiGenlFamily
27 #[allow(dead_code)]
28 enum NlAttrType {
29 AcpiGenlAttrUnspec,
30 AcpiGenlAttrEvent, // acpi_event (needed by user space)
31 AcpiGenlAttrMax,
32 }
33
34 /// commands supported by the AcpiGenlFamily
35 #[allow(dead_code)]
36 enum GenmsghdrCmd {
37 AcpiGenlCmdUnspec,
38 AcpiGenlCmdEvent, // kernel->user notifications for acpi_events
39 AcpiGenlCmdMax,
40 }
41
42 #[repr(C)]
43 #[derive(Copy, Clone, FromZeroes, FromBytes)]
44 struct AcpiGenlEvent {
45 device_class: [::std::os::raw::c_char; 20usize],
46 bus_id: [::std::os::raw::c_char; 15usize],
47 _type: u32,
48 data: u32,
49 }
50
51 pub struct AcpiNotifyEvent {
52 pub device_class: String,
53 pub bus_id: String,
54 pub _type: u32,
55 pub data: u32,
56 }
57
58 impl AcpiNotifyEvent {
59 /// Create acpi event by decapsulating it from NetlinkMessage.
new(netlink_message: NetlinkMessage) -> Result<Self>60 pub fn new(netlink_message: NetlinkMessage) -> Result<Self> {
61 let msg_len = netlink_message.data.len();
62 if msg_len != GENL_HDRLEN + NLA_HDRLEN + ACPI_EVENT_SIZE {
63 return Err(AcpiEventError::InvalidMsgLen(msg_len));
64 }
65
66 let genl_hdr = GenlMsgHdr::read_from(&netlink_message.data[..GENL_HDRLEN])
67 .expect("unable to get GenlMsgHdr from slice");
68
69 let nlattr_end = GENL_HDRLEN + NLA_HDRLEN;
70 let nl_attr = NlAttr::read_from(&netlink_message.data[GENL_HDRLEN..nlattr_end])
71 .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 = AcpiGenlEvent::read_from(&netlink_message.data[nlattr_end..msg_len])
81 .expect("unable to get AcpiGenlEvent from slice");
82
83 // The raw::c_char is either i8 or u8 which is known portability issue:
84 // https://github.com/rust-lang/rust/issues/79089,
85 // before using device_class further cast it to u8.
86 let device_class: &[u8; 20usize] =
87 // SAFETY: trivially safe
88 unsafe { ::std::mem::transmute(&acpi_event.device_class) };
89 // SAFETY: trivially safe
90 let bus_id: &[u8; 15usize] = unsafe { ::std::mem::transmute(&acpi_event.bus_id) };
91
92 Ok(AcpiNotifyEvent {
93 device_class: strip_padding(device_class).to_owned(),
94 bus_id: strip_padding(bus_id).to_owned(),
95 _type: acpi_event._type,
96 data: acpi_event.data,
97 })
98 }
99 }
100
101 // Like `CStr::from_bytes_with_nul` but strips any bytes starting from first '\0'-byte and
102 // returns &str. Panics if `b` doesn't contain any '\0' bytes.
strip_padding(b: &[u8]) -> &str103 fn strip_padding(b: &[u8]) -> &str {
104 // It would be nice if we could use memchr here but that's locked behind an unstable gate.
105 let pos = b
106 .iter()
107 .position(|&c| c == 0)
108 .expect("`b` doesn't contain any nul bytes");
109
110 str::from_utf8(&b[..pos]).unwrap()
111 }
112