[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();
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.