mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Refactoring led support data
This commit is contained in:
@@ -1,86 +1,89 @@
|
||||
use dmi_id::DMIID;
|
||||
use log::{error, info, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
use zbus::zvariant::{OwnedValue, Type, Value};
|
||||
|
||||
use crate::keyboard::AdvancedAuraType;
|
||||
use crate::{AuraModeNum, AuraZone};
|
||||
use crate::{AuraModeNum, AuraZone, PowerZones};
|
||||
|
||||
pub const ASUS_LED_MODE_CONF: &str = "/usr/share/asusd/aura_support.ron";
|
||||
pub const ASUS_LED_MODE_USER_CONF: &str = "/etc/asusd/asusd_user_ledmodes.ron";
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct LedSupportFile(Vec<LaptopLedData>);
|
||||
|
||||
/// The powerr zones this laptop supports
|
||||
#[typeshare]
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "u")
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Default, Copy, Clone)]
|
||||
pub enum PowerZones {
|
||||
/// The logo on some laptop lids
|
||||
#[default]
|
||||
Logo = 0,
|
||||
/// The full keyboard (not zones)
|
||||
Keyboard = 1,
|
||||
/// The lightbar, typically on the front of the laptop
|
||||
Lightbar = 2,
|
||||
/// The leds that may be placed around the edge of the laptop lid
|
||||
Lid = 3,
|
||||
/// The led strip on the rear of some laptops
|
||||
RearGlow = 4,
|
||||
/// On pre-2021 laptops there is either 1 or 2 zones used
|
||||
KeyboardAndLightbar = 5,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct LaptopLedData {
|
||||
/// Found via `cat /sys/class/dmi/id/board_name`, e.g `GU603ZW`.
|
||||
/// The match doesn't have to be the complete model number as it is
|
||||
/// typically broken down such:
|
||||
pub struct LedSupportData {
|
||||
/// This can be many different types of name:
|
||||
/// - `/sys/class/dmi/id/board_name` (must use for laptops)
|
||||
/// - The device name from `lsusb`
|
||||
/// - The product ID (usb only)
|
||||
///
|
||||
/// The laptop board_name is found via `cat /sys/class/dmi/id/board_name`,
|
||||
/// e.g `GU603ZW`. The match doesn't have to be the complete model
|
||||
/// number as it is typically broken down such:
|
||||
/// - GU = product
|
||||
/// - 603 = model/platform
|
||||
/// - Z = variant/year or perhaps dGPU model (such as RTX 3xxx)
|
||||
/// - W = possibly dGPU model (such as RTX 3060Ti)
|
||||
pub board_name: String,
|
||||
///
|
||||
/// If using a device name the match is similar to the above where it can be
|
||||
/// partial, so `ASUSTek Computer, Inc. ROG STRIX Arion` can be `STRIX
|
||||
/// Arion` for short. Case insensitive.
|
||||
///
|
||||
/// Example of using a product ID is:
|
||||
/// ```
|
||||
/// $ lsusb
|
||||
/// $ Bus 003 Device 003: ID 0b05:19b6 ASUSTek Computer, Inc. N-KEY Device
|
||||
/// ```
|
||||
/// here `19b6` is all that is required. Case insensitive.
|
||||
pub device_name: String,
|
||||
/// Keyboard or device LED layout, this is the name of the externally
|
||||
/// defined layout file. Optional, can be an empty string
|
||||
pub layout_name: String,
|
||||
/// If empty will default to `Static` mode
|
||||
pub basic_modes: Vec<AuraModeNum>,
|
||||
/// Available on some laptops. This is where the keyboard may be split in to
|
||||
/// 4 zones and may have a logo and lightbar.
|
||||
///
|
||||
/// Ignored if empty.
|
||||
pub basic_zones: Vec<AuraZone>,
|
||||
/// `Zoned` or `PerKey`.
|
||||
// TODO: remove and use layouts only
|
||||
pub advanced_type: AdvancedAuraType,
|
||||
/// If empty will default to `Keyboard` power zone
|
||||
pub power_zones: Vec<PowerZones>,
|
||||
}
|
||||
|
||||
impl LaptopLedData {
|
||||
pub fn get_data() -> Self {
|
||||
impl LedSupportData {
|
||||
/// Find the data for the device. This function will check DMI info for
|
||||
/// matches against laptops first, then will proceed with matching the
|
||||
/// `device_name` if there are no DMI matches.
|
||||
pub fn get_data(_device_name: &str) -> Self {
|
||||
let dmi = DMIID::new().unwrap_or_default();
|
||||
// let prod_family = dmi.product_family().expect("Could not get
|
||||
// product_family");
|
||||
|
||||
if let Some(modes) = LedSupportFile::load_from_supoprt_db() {
|
||||
if let Some(data) = modes.matcher(&dmi.board_name) {
|
||||
if let Some(data) = LedSupportFile::load_from_supoprt_db() {
|
||||
if let Some(data) = data.match_device(&dmi.board_name) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
info!("Using generic LED control for keyboard brightness only");
|
||||
LaptopLedData::default()
|
||||
LedSupportData::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct LedSupportFile(Vec<LedSupportData>);
|
||||
|
||||
impl LedSupportFile {
|
||||
pub fn get(&self) -> &[LaptopLedData] {
|
||||
pub fn get(&self) -> &[LedSupportData] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// The list is stored in ordered format, so the iterator must be reversed
|
||||
/// to ensure we match to *whole names* first before doing a glob match
|
||||
pub fn matcher(self, board_name: &str) -> Option<LaptopLedData> {
|
||||
fn match_device(&self, device_name: &str) -> Option<LedSupportData> {
|
||||
for config in self.0.iter().rev() {
|
||||
if board_name.contains(&config.board_name) {
|
||||
info!("LedSupport: Matched to {}", config.board_name);
|
||||
if device_name.contains(&config.device_name) {
|
||||
info!("LedSupport: Matched to {}", config.device_name);
|
||||
return Some(config.clone());
|
||||
}
|
||||
}
|
||||
@@ -125,7 +128,7 @@ impl LedSupportFile {
|
||||
);
|
||||
}
|
||||
}
|
||||
data.0.sort_by(|a, b| a.board_name.cmp(&b.board_name));
|
||||
data.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
|
||||
|
||||
if loaded {
|
||||
return Some(data);
|
||||
@@ -144,7 +147,7 @@ mod tests {
|
||||
|
||||
use ron::ser::PrettyConfig;
|
||||
|
||||
use super::LaptopLedData;
|
||||
use super::LedSupportData;
|
||||
use crate::aura_detection::{LedSupportFile, PowerZones};
|
||||
use crate::keyboard::{AdvancedAuraType, LedCode};
|
||||
// use crate::zoned::Zone;
|
||||
@@ -152,8 +155,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn check_data_parse() {
|
||||
let led = LaptopLedData {
|
||||
board_name: "Test".to_owned(),
|
||||
let led = LedSupportData {
|
||||
device_name: "Test".to_owned(),
|
||||
layout_name: "ga401".to_owned(),
|
||||
basic_modes: vec![AuraModeNum::Static],
|
||||
basic_zones: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
|
||||
@@ -176,7 +179,7 @@ mod tests {
|
||||
|
||||
// Ensure the data is sorted
|
||||
let mut tmp_sort = tmp.clone();
|
||||
tmp_sort.0.sort_by(|a, b| a.board_name.cmp(&b.board_name));
|
||||
tmp_sort.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
|
||||
if tmp != tmp_sort {
|
||||
let sorted =
|
||||
ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap();
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::slice::Iter;
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::aura_detection::LaptopLedData;
|
||||
use crate::aura_detection::LedSupportData;
|
||||
use crate::error::Error;
|
||||
use crate::keyboard::{AdvancedAuraType, LedCode};
|
||||
use crate::{AuraModeNum, AuraZone};
|
||||
@@ -278,7 +278,7 @@ impl KeyLayout {
|
||||
}
|
||||
|
||||
/// Find a layout matching the name in `LaptopLedData` in the provided dir
|
||||
pub fn find_layout(led_data: LaptopLedData, mut data_path: PathBuf) -> Result<Self, Error> {
|
||||
pub fn find_layout(led_data: LedSupportData, mut data_path: PathBuf) -> Result<Self, Error> {
|
||||
// TODO: locales
|
||||
let layout_name = if led_data.layout_name.is_empty() {
|
||||
"ga401q".to_owned() // Need some sort of default here due to ROGCC
|
||||
@@ -543,7 +543,7 @@ mod tests {
|
||||
.map_err(|e| {
|
||||
panic!(
|
||||
"Error checking {data_path:?} for {} : {e:?}",
|
||||
config.board_name
|
||||
config.device_name
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
@@ -551,7 +551,7 @@ mod tests {
|
||||
if let Err(e) = file.read_to_string(&mut buf) {
|
||||
panic!(
|
||||
"Error checking {data_path:?} for {} : {e:?}",
|
||||
config.board_name
|
||||
config.device_name
|
||||
)
|
||||
}
|
||||
if let Err(e) = ron::from_str::<KeyLayout>(&buf) {
|
||||
|
||||
@@ -9,8 +9,8 @@ use typeshare::typeshare;
|
||||
#[cfg(feature = "dbus")]
|
||||
use zbus::zvariant::{OwnedValue, Type, Value};
|
||||
|
||||
use crate::aura_detection::{LaptopLedData, PowerZones};
|
||||
use crate::AuraDeviceType;
|
||||
use crate::aura_detection::LedSupportData;
|
||||
use crate::{AuraDeviceType, PowerZones};
|
||||
|
||||
/// Meaning of this struct depends on the laptop generation.
|
||||
/// - 2021+, the struct is a single zone with 4 states
|
||||
@@ -133,7 +133,7 @@ impl AuraPowerState {
|
||||
| (self.sleep as u32) << (23 + 3)
|
||||
| (self.shutdown as u32) << (23 + 4)
|
||||
}
|
||||
PowerZones::KeyboardAndLightbar => 0,
|
||||
PowerZones::KeyboardAndLightbar | PowerZones::None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ impl LaptopAuraPower {
|
||||
}
|
||||
|
||||
// TODO: use support data to setup correct zones
|
||||
pub fn new(aura_type: AuraDeviceType, support_data: &LaptopLedData) -> Self {
|
||||
pub fn new(aura_type: AuraDeviceType, support_data: &LedSupportData) -> Self {
|
||||
match aura_type {
|
||||
AuraDeviceType::Unknown | AuraDeviceType::LaptopPost2021 => {
|
||||
let mut states = Vec::new();
|
||||
@@ -276,9 +276,9 @@ impl From<OldAuraPower> for u32 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::aura_detection::{LaptopLedData, PowerZones};
|
||||
use crate::aura_detection::LedSupportData;
|
||||
use crate::keyboard::{AuraPowerState, LaptopAuraPower};
|
||||
use crate::AuraDeviceType;
|
||||
use crate::{AuraDeviceType, PowerZones};
|
||||
|
||||
#[test]
|
||||
fn check_0x1866_control_bytes() {
|
||||
@@ -515,7 +515,8 @@ mod test {
|
||||
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
|
||||
|
||||
// All on
|
||||
let byte1 = LaptopAuraPower::new(AuraDeviceType::LaptopPost2021, &LaptopLedData::default());
|
||||
let byte1 =
|
||||
LaptopAuraPower::new(AuraDeviceType::LaptopPost2021, &LedSupportData::default());
|
||||
let out = to_binary_string(&byte1);
|
||||
assert_eq!(out, "11111111, 00011110, 00001111, 00001111");
|
||||
}
|
||||
|
||||
@@ -105,3 +105,28 @@ impl From<&str> for AuraDeviceType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The powerr zones this laptop supports
|
||||
#[typeshare]
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "u")
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Default, Copy, Clone)]
|
||||
pub enum PowerZones {
|
||||
/// The logo on some laptop lids
|
||||
Logo = 0,
|
||||
/// The full keyboard (not zones)
|
||||
#[default]
|
||||
Keyboard = 1,
|
||||
/// The lightbar, typically on the front of the laptop
|
||||
Lightbar = 2,
|
||||
/// The leds that may be placed around the edge of the laptop lid
|
||||
Lid = 3,
|
||||
/// The led strip on the rear of some laptops
|
||||
RearGlow = 4,
|
||||
/// On pre-2021 laptops there is either 1 or 2 zones used
|
||||
KeyboardAndLightbar = 5,
|
||||
None = 255,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user