devices/sys/linux/
acpi.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::sync::Arc;
6
7use base::debug;
8use base::error;
9use base::info;
10use base::AcpiNotifyEvent;
11use base::NetlinkGenericSocket;
12use sync::Mutex;
13
14use crate::acpi::ACPIPMError;
15use crate::acpi::GpeResource;
16use crate::acpi::ACPIPM_GPE_MAX;
17use crate::AcAdapter;
18use crate::IrqLevelEvent;
19
20pub(crate) fn get_acpi_event_sock() -> Result<Option<NetlinkGenericSocket>, ACPIPMError> {
21    // Get group id corresponding to acpi_mc_group of acpi_event family
22    let nl_groups: u32;
23    match get_acpi_event_group() {
24        Some(group) if group > 0 => {
25            nl_groups = 1 << (group - 1);
26            info!("Listening on acpi_mc_group of acpi_event family");
27        }
28        _ => {
29            return Err(ACPIPMError::AcpiMcGroupError);
30        }
31    }
32
33    match NetlinkGenericSocket::new(nl_groups) {
34        Ok(acpi_sock) => Ok(Some(acpi_sock)),
35        Err(e) => Err(ACPIPMError::AcpiEventSockError(e)),
36    }
37}
38
39fn get_acpi_event_group() -> Option<u32> {
40    // Create netlink generic socket which will be used to query about given family name
41    let netlink_ctrl_sock = match NetlinkGenericSocket::new(0) {
42        Ok(sock) => sock,
43        Err(e) => {
44            error!("netlink generic socket creation error: {}", e);
45            return None;
46        }
47    };
48
49    let nlmsg_family_response = netlink_ctrl_sock
50        .family_name_query("acpi_event".to_string())
51        .unwrap();
52    nlmsg_family_response.get_multicast_group_id("acpi_mc_group".to_string())
53}
54
55pub(crate) fn acpi_event_run(
56    sci_evt: &IrqLevelEvent,
57    acpi_event_sock: &Option<NetlinkGenericSocket>,
58    gpe0: &Arc<Mutex<GpeResource>>,
59    ignored_gpe: &[u32],
60    ac_adapter: &Option<Arc<Mutex<AcAdapter>>>,
61) {
62    let acpi_event_sock = acpi_event_sock.as_ref().unwrap();
63    let nl_msg = match acpi_event_sock.recv() {
64        Ok(msg) => msg,
65        Err(e) => {
66            error!("recv returned with error {}", e);
67            return;
68        }
69    };
70
71    for netlink_message in nl_msg.iter() {
72        let acpi_event = match AcpiNotifyEvent::new(netlink_message) {
73            Ok(evt) => evt,
74            Err(e) => {
75                error!("Received netlink message is not an acpi_event, error {}", e);
76                continue;
77            }
78        };
79        match acpi_event.device_class.as_str() {
80            "gpe" => {
81                acpi_event_handle_gpe(acpi_event.data, acpi_event._type, gpe0, ignored_gpe);
82            }
83            "ac_adapter" => {
84                if let Some(ac_adapter) = ac_adapter {
85                    // Currently we only support Status change event - other are ignored
86                    if acpi_event._type == 0x80 {
87                        // Set acex
88                        let ac_gpe_nr = {
89                            let mut ac_adapter = ac_adapter.lock();
90                            ac_adapter.acex = acpi_event.data;
91                            ac_adapter.gpe_nr
92                        };
93
94                        // Generate GPE
95                        debug!(
96                            "getting ac_adapter event {} type {} and triggering GPE {}",
97                            acpi_event.data, acpi_event._type, ac_gpe_nr
98                        );
99                        let mut gpe0 = gpe0.lock();
100                        match gpe0.set_active(ac_gpe_nr) {
101                            Ok(_) => gpe0.trigger_sci(sci_evt),
102                            Err(e) => error!("{}", e),
103                        }
104                    }
105                }
106            }
107            c => debug!("ignored acpi event {}", c),
108        };
109    }
110}
111
112fn acpi_event_handle_gpe(
113    gpe_number: u32,
114    _type: u32,
115    gpe0: &Arc<Mutex<GpeResource>>,
116    ignored_gpe: &[u32],
117) {
118    // If gpe event fired in the host, notify registered GpeNotify listeners
119    if _type == 0 && gpe_number <= ACPIPM_GPE_MAX as u32 && !ignored_gpe.contains(&gpe_number) {
120        if let Some(notify_devs) = gpe0.lock().gpe_notify.get(&gpe_number) {
121            for notify_dev in notify_devs.iter() {
122                notify_dev.lock().notify();
123            }
124        }
125    }
126}