[PATCH RFT] platform/x86: acer-wmi: add mic-mute LED via SetLedIndicator
Aleksandrs Bogdanovs <[email protected]>
| Newsgroups | org.kernel.vger.platform-driver-x86 |
|---|---|
| Message-ID | <[email protected]> |
Hello, This is an [RFT] (request for testing) patch adding mic-mute LED support to acer-wmi for the Acer Aspire A517-53. The mic-mute key LED is driven by SetLedIndicator (WmiMethodId 12) of the UtilityFunction class, GUID FE1DBBDA-3014-4856-870C-5B3A744BF341. The ACPI method (WMBL, case 0x0C) only copies an 8-byte input buffer into BLCP and triggers SMI port 0xD0, so all logic lives in BIOS SMM and the buffer format is undocumented. The attached patch wires an acer-wmi::micmute LED classdev (default trigger audio-micmute) calling SetLedIndicator, and exposes the id/state bytes as module parameters (micmute_led, micmute_id, micmute_on, micmute_off) so the encoding can be found without a rebuild. The LED is not registered by default. I do not run Windows and cannot capture the argument the Acer utility passes to SetLedIndicator. I am looking for a tester with the same model who can trace that call (WMI/ETW trace or API Monitor) for mic-mute on and off. Full analysis, decoded BMOF, disassembled SSDT, an acpidump and a sysfs probe module are available here: https://www.besttradersclub.com/downloads/ The patch builds and loads cleanly on current kernels; see the commit message in the attachment. Thanks, Aleksandrs Bogdanovs
acer-wmi-micmute-led.patch
(text/x-patch, 6 KB)
From: Aleksandrs Bogdanovs <[email protected]> Subject: [PATCH RFT] platform/x86: acer-wmi: add mic-mute LED via SetLedIndicator On the Acer Aspire A517-53 (and relatives) the mic-mute key has an LED that Windows lights to reflect the microphone state. The key itself is a plain keyboard event (atkbd scancode 0x66), so the firmware does not drive the LED automatically; the Windows Acer utility caught the key and toggled the LED via a WMI method. The LED is controlled by SetLedIndicator (WmiMethodId 12) of the UtilityFunction class (GUID FE1DBBDA-3014-4856-870C-5B3A744BF341). The ACPI implementation (WMBL, case 0x0C) only copies an 8-byte input buffer into BLCP and triggers SMI port 0xD0 (CTID='BL'); all logic lives in BIOS SMM and the input buffer format is undocumented. This patch wires up an "acer-wmi::micmute" LED classdev (default trigger audio-micmute) calling SetLedIndicator. Because the buffer encoding is only known to the Windows utility, the id/state bytes are exposed as module parameters (micmute_led, micmute_id, micmute_on, micmute_off) so the encoding can be found without a rebuild. The LED is not registered by default. This is sent as [RFT]: I do not run Windows and cannot capture the argument the Acer utility passes. I am looking for a tester with the same model who can trace the SetLedIndicator call (WMI/ETW trace or API Monitor) for mic-mute on/off. Full analysis, decoded BMOF, disassembled SSDT, an acpidump and a sysfs probe module are available; see the attached archive. Signed-off-by: Aleksandrs Bogdanovs <[email protected]> --- drivers/platform/x86/acer-wmi.c | 85 +++++++++++++++++++++++++++++++++ --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -109,6 +109,9 @@ #define WMID_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A" #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" #define WMID_GUID4 "7A4DDFE7-5B5D-40B4-8595-4408E0CC7F56" +/* UtilityFunction class: holds Get/SetLedIndicator (methods 11/12) */ +#define WMID_UTIL_GUID "FE1DBBDA-3014-4856-870C-5B3A744BF341" +#define WMID_UTIL_SET_LED 12 /* * Acer ACPI event GUIDs @@ -336,6 +339,22 @@ static bool predator_v4; static u64 supported_sensors; +/* + * Mic-mute LED (Acer Aspire A517-53 and relatives). + * Driven by SetLedIndicator (WmiMethodId 12) of the UtilityFunction class, + * GUID FE1DBBDA-3014-4856-870C-5B3A744BF341. The actual work happens in BIOS + * SMM (the WMBL ACPI method copies an 8-byte buffer into BLCP and triggers SMI + * port 0xD0, CTID='BL'), so the exact input format is undocumented. The module + * parameters below allow tuning the encoding at load time without a rebuild: + * micmute_led = 1 register the LED (disabled by default) + * micmute_id = indicator id (buffer byte 0) + * micmute_on/off = state value (buffer byte 1) + */ +static bool micmute_led; +static int micmute_id = 0x0c; +static int micmute_on = 0x01; +static int micmute_off = 0x00; + module_param(mailled, int, 0444); module_param(brightness, int, 0444); module_param(threeg, int, 0444); @@ -344,7 +363,15 @@ module_param(ec_raw_mode, bool, 0444); module_param(cycle_gaming_thermal_profile, bool, 0644); module_param(predator_v4, bool, 0444); +module_param(micmute_led, bool, 0444); +module_param(micmute_id, int, 0444); +module_param(micmute_on, int, 0444); +module_param(micmute_off, int, 0444); MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); +MODULE_PARM_DESC(micmute_led, "Register mic-mute LED via SetLedIndicator (experimental)"); +MODULE_PARM_DESC(micmute_id, "Mic-mute LED indicator id (byte 0 of SetLedIndicator buffer)"); +MODULE_PARM_DESC(micmute_on, "Mic-mute LED 'on' value (byte 1)"); +MODULE_PARM_DESC(micmute_off, "Mic-mute LED 'off' value (byte 1)"); MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); MODULE_PARM_DESC(force_series, "Force a different laptop series"); @@ -1946,6 +1973,57 @@ } /* + * Mic-mute LED via UtilityFunction::SetLedIndicator. + * The method takes an 8-byte buffer; byte 0 = indicator id, byte 1 = state + * (values are configurable through the micmute_id/on/off module parameters + * because the exact format is defined by the SMM firmware and undocumented). + */ +static acpi_status wmid_set_led_indicator(u64 val) +{ + struct acpi_buffer input = { (acpi_size)sizeof(val), &val }; + + return wmi_evaluate_method(WMID_UTIL_GUID, 0, WMID_UTIL_SET_LED, + &input, NULL); +} + +static int micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + u64 buf = ((u64)(value ? micmute_on : micmute_off) << 8) | + (micmute_id & 0xff); + acpi_status status; + + status = wmid_set_led_indicator(buf); + return ACPI_SUCCESS(status) ? 0 : -EIO; +} + +static struct led_classdev micmute_led_cdev = { + .name = "acer-wmi::micmute", + .max_brightness = 1, + .brightness_set_blocking = micmute_led_set, + .default_trigger = "audio-micmute", +}; + +static int acer_micmute_led_init(struct device *dev) +{ + if (!micmute_led) + return 0; + if (!wmi_has_guid(WMID_UTIL_GUID)) { + pr_info("micmute LED requested but UtilityFunction WMI GUID absent\n"); + return 0; + } + return led_classdev_register(dev, &micmute_led_cdev); +} + +static void acer_micmute_led_exit(void) +{ + if (!micmute_led || !wmi_has_guid(WMID_UTIL_GUID)) + return; + micmute_led_set(&micmute_led_cdev, LED_OFF); + led_classdev_unregister(&micmute_led_cdev); +} + +/* * Backlight device */ static struct backlight_device *acer_backlight_device; @@ -2775,6 +2853,10 @@ goto error_mailled; } + err = acer_micmute_led_init(&device->dev); + if (err) + goto error_micmute; + if (has_cap(ACER_CAP_BRIGHTNESS)) { err = acer_backlight_init(&device->dev); if (err) @@ -2806,6 +2888,8 @@ if (has_cap(ACER_CAP_BRIGHTNESS)) acer_backlight_exit(); error_brightness: + acer_micmute_led_exit(); +error_micmute: if (has_cap(ACER_CAP_MAILLED)) acer_led_exit(); error_mailled: @@ -2816,6 +2900,7 @@ { if (has_cap(ACER_CAP_MAILLED)) acer_led_exit(); + acer_micmute_led_exit(); if (has_cap(ACER_CAP_BRIGHTNESS)) acer_backlight_exit();