| // Copyright 2022 The ChromiumOS Authors. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::sync::Arc; |
| |
| use base::debug; |
| use base::error; |
| use base::info; |
| use base::AcpiNotifyEvent; |
| use base::NetlinkGenericSocket; |
| use sync::Mutex; |
| |
| use crate::acpi::ACPIPMError; |
| use crate::acpi::ACPIPMFixedEvent; |
| use crate::acpi::GpeResource; |
| use crate::acpi::Pm1Resource; |
| use crate::IrqLevelEvent; |
| |
| pub(crate) fn get_acpi_event_sock() -> Result<Option<NetlinkGenericSocket>, ACPIPMError> { |
| // Get group id corresponding to acpi_mc_group of acpi_event family |
| let nl_groups: u32; |
| match get_acpi_event_group() { |
| Some(group) if group > 0 => { |
| nl_groups = 1 << (group - 1); |
| info!("Listening on acpi_mc_group of acpi_event family"); |
| } |
| _ => { |
| return Err(ACPIPMError::AcpiMcGroupError); |
| } |
| } |
| |
| match NetlinkGenericSocket::new(nl_groups) { |
| Ok(acpi_sock) => Ok(Some(acpi_sock)), |
| Err(e) => { |
| return Err(ACPIPMError::AcpiEventSockError(e)); |
| } |
| } |
| } |
| |
| fn get_acpi_event_group() -> Option<u32> { |
| // Create netlink generic socket which will be used to query about given family name |
| let netlink_ctrl_sock = match NetlinkGenericSocket::new(0) { |
| Ok(sock) => sock, |
| Err(e) => { |
| error!("netlink generic socket creation error: {}", e); |
| return None; |
| } |
| }; |
| |
| let nlmsg_family_response = netlink_ctrl_sock |
| .family_name_query("acpi_event".to_string()) |
| .unwrap(); |
| return nlmsg_family_response.get_multicast_group_id("acpi_mc_group".to_string()); |
| } |
| |
| pub(crate) fn acpi_event_run( |
| acpi_event_sock: &Option<NetlinkGenericSocket>, |
| gpe0: &Arc<Mutex<GpeResource>>, |
| pm1: &Arc<Mutex<Pm1Resource>>, |
| sci_evt: &IrqLevelEvent, |
| ignored_gpe: &[u32], |
| ) { |
| let acpi_event_sock = acpi_event_sock.as_ref().unwrap(); |
| let nl_msg = match acpi_event_sock.recv() { |
| Ok(msg) => msg, |
| Err(e) => { |
| error!("recv returned with error {}", e); |
| return; |
| } |
| }; |
| |
| for netlink_message in nl_msg.iter() { |
| let acpi_event = match AcpiNotifyEvent::new(netlink_message) { |
| Ok(evt) => evt, |
| Err(e) => { |
| error!("Received netlink message is not an acpi_event, error {}", e); |
| continue; |
| } |
| }; |
| match acpi_event.device_class.as_str() { |
| "gpe" => { |
| acpi_event_handle_gpe( |
| acpi_event.data, |
| acpi_event._type, |
| gpe0, |
| sci_evt, |
| ignored_gpe, |
| ); |
| } |
| "button/power" => acpi_event_handle_power_button(acpi_event, pm1, sci_evt), |
| c => debug!("ignored acpi event {}", c), |
| }; |
| } |
| } |
| |
| const ACPI_BUTTON_NOTIFY_STATUS: u32 = 0x80; |
| |
| fn acpi_event_handle_power_button( |
| acpi_event: AcpiNotifyEvent, |
| pm1: &Arc<Mutex<Pm1Resource>>, |
| sci_evt: &IrqLevelEvent, |
| ) { |
| // If received power button event, emulate PM/PWRBTN_STS and trigger SCI |
| if acpi_event._type == ACPI_BUTTON_NOTIFY_STATUS && acpi_event.bus_id.contains("LNXPWRBN") { |
| let mut pm1 = pm1.lock(); |
| |
| pm1.status |= ACPIPMFixedEvent::PowerButton.bitmask(); |
| pm1.trigger_sci(sci_evt); |
| } |
| } |
| |
| fn acpi_event_handle_gpe( |
| gpe_number: u32, |
| _type: u32, |
| gpe0: &Arc<Mutex<GpeResource>>, |
| sci_evt: &IrqLevelEvent, |
| ignored_gpe: &[u32], |
| ) { |
| // If gpe event, emulate GPE and trigger SCI |
| if _type == 0 && gpe_number < 256 && !ignored_gpe.contains(&gpe_number) { |
| let mut gpe0 = gpe0.lock(); |
| let byte = gpe_number as usize / 8; |
| |
| if byte >= gpe0.status.len() { |
| error!("gpe_evt: GPE register {} does not exist", byte); |
| return; |
| } |
| gpe0.status[byte] |= 1 << (gpe_number % 8); |
| gpe0.trigger_sci(sci_evt); |
| } |
| } |