1 // Copyright 2022 The Chromium OS 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
5 use std::str;
6
7 use super::netlink::*;
8 use thiserror::Error;
9
10 use data_model::DataInit;
11
12 const ACPI_EVENT_SIZE: usize = std::mem::size_of::<AcpiGenlEvent>();
13 const GENL_HDRLEN: usize = std::mem::size_of::<GenlMsgHdr>();
14 const NLA_HDRLEN: usize = std::mem::size_of::<NlAttr>();
15
16 #[derive(Error, Debug)]
17 pub enum AcpiEventError {
18 #[error("GenmsghdrCmd or NlAttrType inappropriate for acpi event")]
19 TypeAttrMissmatch,
20 #[error("Something goes wrong: msg_len {0} is not correct")]
21 InvalidMsgLen(usize),
22 }
23 type Result<T> = std::result::Result<T, AcpiEventError>;
24
25 /// attributes of AcpiGenlFamily
26 #[allow(dead_code)]
27 enum NlAttrType {
28 AcpiGenlAttrUnspec,
29 AcpiGenlAttrEvent, // acpi_event (needed by user space)
30 AcpiGenlAttrMax,
31 }
32
33 /// commands supported by the AcpiGenlFamily
34 #[allow(dead_code)]
35 enum GenmsghdrCmd {
36 AcpiGenlCmdUnspec,
37 AcpiGenlCmdEvent, // kernel->user notifications for acpi_events
38 AcpiGenlCmdMax,
39 }
40
41 #[repr(C)]
42 #[derive(Copy, Clone)]
43 struct AcpiGenlEvent {
44 device_class: [::std::os::raw::c_char; 20usize],
45 bus_id: [::std::os::raw::c_char; 15usize],
46 _type: u32,
47 data: u32,
48 }
49 unsafe impl DataInit for AcpiGenlEvent {}
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::from_slice(&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::from_slice(&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::from_slice(&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 unsafe { ::std::mem::transmute(&acpi_event.device_class) };
88 let bus_id: &[u8; 15usize] = unsafe { ::std::mem::transmute(&acpi_event.bus_id) };
89
90 Ok(AcpiNotifyEvent {
91 device_class: strip_padding(device_class).to_owned(),
92 bus_id: strip_padding(bus_id).to_owned(),
93 _type: acpi_event._type,
94 data: acpi_event.data,
95 })
96 }
97 }
98
99 // Like `CStr::from_bytes_with_nul` but strips any bytes starting from first '\0'-byte and
100 // returns &str. Panics if `b` doesn't contain any '\0' bytes.
strip_padding(b: &[u8]) -> &str101 fn strip_padding(b: &[u8]) -> &str {
102 // It would be nice if we could use memchr here but that's locked behind an unstable gate.
103 let pos = b
104 .iter()
105 .position(|&c| c == 0)
106 .expect("`b` doesn't contain any nul bytes");
107
108 str::from_utf8(&b[..pos]).unwrap()
109 }
110