[alsa-devel] [RFC PATCH 1/2] thinkpad-acpi: Add mute and mic-mute LED functionality

Takashi Iwai tiwai at suse.de
Wed Oct 16 15:09:17 CEST 2013


At Wed, 16 Oct 2013 14:15:35 +0200,
David Henningsson wrote:
> 
> Questions / notes:
> 
> This patch supersedes the second of Alex Hung's patches posted earlier at
> http://www.spinics.net/lists/platform-driver-x86/msg04673.html
> 
> Not sure if thinkpad_acpi should be dropped into include/linux
> though, any better suggestion?
> 
> Should TPACPI_VERSION be increased because we added a new LED driver?
> 
> Signed-off-by: David Henningsson <david.henningsson at canonical.com>
> ---
>  drivers/platform/x86/thinkpad_acpi.c |   94 +++++++++++++++++++++++++++++++++-
>  include/linux/thinkpad_acpi.h        |   10 ++++
>  2 files changed, 103 insertions(+), 1 deletion(-)
>  create mode 100644 include/linux/thinkpad_acpi.h
> 
> diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
> index 03ca6c1..ecdfeae 100644
> --- a/drivers/platform/x86/thinkpad_acpi.c
> +++ b/drivers/platform/x86/thinkpad_acpi.c
> @@ -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,93 @@ static struct ibm_struct fan_driver_data = {
>  	.resume = fan_resume,
>  };
>  
> +/*************************************************************************
> + * Mute LED subdriver
> + */
> +
> +#define MUTE_LED_INDEX 0
> +#define MICMUTE_LED_INDEX 1
> +
> +static int mute_led_on_off(int whichled, int state)
> +{
> +	int output;
> +	acpi_handle temp;
> +
> +	acpi_string m;
> +	if (whichled == MICMUTE_LED_INDEX) {
> +		state = state > 0 ? 2 : 0;
> +		m = "MMTS";
> +	} else {
> +		state = state > 0 ? 1 : 0;
> +		m = "SSMS";
> +	}
> +
> +	if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, m, &temp))) {
> +		pr_warn("Thinkpad ACPI has no %s interface.\n", m);
> +		return -EIO;
> +	}
> +
> +	if (!acpi_evalf(hkey_handle, &output, m, "dd", state))
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static unsigned int mute_led_state;
> +static unsigned int micmute_led_state;
> +
> +int tpacpi_mute_led_set(int on)
> +{
> +	int err;
> +	int state = on ? 1 : 0;
> +	if (mute_led_state < 0 || mute_led_state == state)
> +		return mute_led_state;
> +	err = mute_led_on_off(MUTE_LED_INDEX, state);
> +	mute_led_state = err ? err : state;
> +	return err;
> +}
> +EXPORT_SYMBOL(tpacpi_mute_led_set);
> +
> +int tpacpi_micmute_led_set(int on)
> +{
> +	int err;
> +	int state = on ? 1 : 0;
> +	if (micmute_led_state < 0 || micmute_led_state == state)
> +		return micmute_led_state;
> +	err = mute_led_on_off(MICMUTE_LED_INDEX, state);
> +	micmute_led_state = err ? err : state;
> +	return err;
> +}
> +EXPORT_SYMBOL(tpacpi_micmute_led_set);
> +
> +static int mute_led_init(struct ibm_init_struct *iibm)
> +{
> +	acpi_handle temp;
> +
> +	if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, "MMTG", &temp)))
> +		micmute_led_state = mute_led_on_off(MICMUTE_LED_INDEX, 0);
> +	else
> +		micmute_led_state = -ENODEV;
> +
> +	if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, "GSMS", &temp)))
> +		mute_led_state = mute_led_on_off(MUTE_LED_INDEX, 0);
> +	else
> +		mute_led_state = -ENODEV;
> +
> +	return 0;
> +}
> +
> +static void mute_led_exit(void)
> +{
> +	tpacpi_mute_led_set(0);
> +	tpacpi_micmute_led_set(0);
> +}
> +
> +static struct ibm_struct mute_led_driver_data = {
> +	.name = "mute_led",
> +	.exit = mute_led_exit,
> +};


How about managing with a table?  For example,

struct tp_led_table {
	acpi_string name;
	int on_value;
	int off_value;
	int state;
};

static struct tp_led_table led_tables[] = {
	[TP_LED_MUTE] = {
		.name = "SSMS",
		.on_value = 1,
		.off_value = 0,
	},
	[TP_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;

	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 >= TP_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 < TP_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 < TP_LED_MAX; i++)
		tpacpi_led_set(i, false);
}


Also, the exported symbol should be marked with *_GPL().


thanks,

Takashi


More information about the Alsa-devel mailing list