thinkpad-acpi: Add mute and mic-mute LED functionality

The LEDs are currently not visible to userspace, for security
reasons. They are exported through thinkpad_acpi.h for use by the
snd-hda-intel driver.

Thanks to Alex Hung <alex.hung@canonical.com> and Takashi Iwai
<tiwai@suse.de> for writing parts of this patch.

Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
David Henningsson 2013-10-16 23:10:31 +02:00 committed by Takashi Iwai
parent 1d198f26c9
commit 420f9739a6
3 changed files with 111 additions and 3 deletions

View File

@ -1,7 +1,7 @@
ThinkPad ACPI Extras Driver
Version 0.24
December 11th, 2009
Version 0.25
October 16th, 2013
Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled.
Distributions must never enable this option. Individual users that
are aware of the consequences are welcome to enabling it.
Audio mute and microphone mute LEDs are supported, but currently not
visible to userspace. They are used by the snd-hda-intel audio driver.
procfs notes:
The available commands are:

View File

@ -23,7 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define TPACPI_VERSION "0.24"
#define TPACPI_VERSION "0.25"
#define TPACPI_SYSFS_VERSION 0x020700
/*
@ -88,6 +88,7 @@
#include <linux/pci_ids.h>
#include <linux/thinkpad_acpi.h>
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = {
.resume = fan_resume,
};
/*************************************************************************
* Mute LED subdriver
*/
struct tp_led_table {
acpi_string name;
int on_value;
int off_value;
int state;
};
static struct tp_led_table led_tables[] = {
[TPACPI_LED_MUTE] = {
.name = "SSMS",
.on_value = 1,
.off_value = 0,
},
[TPACPI_LED_MICMUTE] = {
.name = "MMTS",
.on_value = 2,
.off_value = 0,
},
};
static int mute_led_on_off(struct tp_led_table *t, bool state)
{
acpi_handle temp;
int output;
if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) {
pr_warn("Thinkpad ACPI has no %s interface.\n", t->name);
return -EIO;
}
if (!acpi_evalf(hkey_handle, &output, t->name, "dd",
state ? t->on_value : t->off_value))
return -EIO;
t->state = state;
return state;
}
int tpacpi_led_set(int whichled, bool on)
{
struct tp_led_table *t;
if (whichled < 0 || whichled >= TPACPI_LED_MAX)
return -EINVAL;
t = &led_tables[whichled];
if (t->state < 0 || t->state == on)
return t->state;
return mute_led_on_off(t, on);
}
EXPORT_SYMBOL_GPL(tpacpi_led_set);
static int mute_led_init(struct ibm_init_struct *iibm)
{
acpi_handle temp;
int i;
for (i = 0; i < TPACPI_LED_MAX; i++) {
struct tp_led_table *t = &led_tables[i];
if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp)))
mute_led_on_off(t, false);
else
t->state = -ENODEV;
}
return 0;
}
static void mute_led_exit(void)
{
int i;
for (i = 0; i < TPACPI_LED_MAX; i++)
tpacpi_led_set(i, false);
}
static struct ibm_struct mute_led_driver_data = {
.name = "mute_led",
.exit = mute_led_exit,
};
/****************************************************************************
****************************************************************************
*
@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.init = fan_init,
.data = &fan_driver_data,
},
{
.init = mute_led_init,
.data = &mute_led_driver_data,
},
};
static int __init set_ibm_param(const char *val, struct kernel_param *kp)

View File

@ -0,0 +1,15 @@
#ifndef __THINKPAD_ACPI_H__
#define __THINKPAD_ACPI_H__
/* These two functions return 0 if success, or negative error code
(e g -ENODEV if no led present) */
enum {
TPACPI_LED_MUTE,
TPACPI_LED_MICMUTE,
TPACPI_LED_MAX,
};
int tpacpi_led_set(int whichled, bool on);
#endif