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:
		
					parent
					
						
							
								1d198f26c9
							
						
					
				
			
			
				commit
				
					
						420f9739a6
					
				
			
		
					 3 changed files with 111 additions and 3 deletions
				
			
		|  | @ -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: | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
							
								
								
									
										15
									
								
								include/linux/thinkpad_acpi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								include/linux/thinkpad_acpi.h
									
										
									
									
									
										Normal 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 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 David Henningsson
				David Henningsson