thinkpad fn+fx key driver
PATCH 1/1 thinkpad fn+fx key driver
Changes since last version:
- use the acpi handle provided by acpi_bus_register_driver, this way it should work on more models.
- correct handling of return value from acpi_bus_register_driver
Signature · 1 line
Erik Rigtorp <[email protected]>
drivers/acpi/thinkpad_acpi.c new file +265 -0
@@ -0,0 +1,265 @@ +/* +* IBM Thinkpad Fn+Fx key driver +* (C) 2004 Erik Rigtorp <[email protected]> +* All Rights Reserved +* +* This code was inspired from the acpi button and thoshiba drivers. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +MODULE_AUTHOR ("Erik Rigtorp" ); +MODULE_DESCRIPTION ("IBM Thinkpad Fn+Fx key driver" ); +MODULE_LICENSE ("GPL" ); + +#define LOGPREFIX "thinkpad_acpi: " + +/* Switch between LCD and VGA video output */ +#define METHOD_VID_VSWT "\\_SB.PCI0.AGP.VID.VSWT" + +/* Toggle screen expansion */ +#define METHOD_EC_VEXP "\\_SB.PCI0.LPC.EC.VEXP" + +/* device(HKEY) definitions */ +#define HKEY_HID "IBM0068" +#define HKEY_NOTIFY 0x80 + +#define DRIVER_VERSION "1.1" +#define DRIVER_NAME "IBM Thinkpad Fn+Fx key driver" +#define DEVICE_NAME "IBM Thinkpad keys" +#define DEVICE_CLASS "hkey" + +static int acpi_thinkpad_add (struct acpi_device *device); +static int acpi_thinkpad_remove (struct acpi_device *device,int type); + +static struct acpi_driver acpi_thinkpad_driver = { + .name =DRIVER_NAME , + .class =DEVICE_CLASS , + .ids =HKEY_HID , + .ops = { + .add = acpi_thinkpad_add, + .remove = acpi_thinkpad_remove, + }, +}; + +struct acpi_thinkpad { +acpi_handle handle; +struct acpi_device *device; +}; + +/* Activate/deactivate handling of keys +* @val 1 to activate, 0 to deactivate */ +static int handle_keys (int val,acpi_handle handle) +{ +struct acpi_object_list params; +union acpi_object objs[1 ]; +acpi_status status; + + params.count =1 ; + params.pointer = objs; + objs[0 ].type =ACPI_TYPE_INTEGER ; +if (val) + objs[0 ].integer.value =1 ; +else + objs[0 ].integer.value =0 ; + + status =acpi_evaluate_object (handle,"MHKC" , + ¶ms,0 ); +return (status ==AE_OK ); +} + +/* Activate/deactivate Fn+Fx key +* @key the x in Fx +* @state 1 for active, 0 for disabled/firmware */ +static int set_key (unsigned int key,unsigned int state,acpi_handle handle) +{ +struct acpi_object_list params; +union acpi_object args[2 ]; +acpi_status status; + +if (state) + state =1 ; + + params.count =2 ; + params.pointer = args; + args[0 ].type =ACPI_TYPE_INTEGER ; + args[0 ].integer.value = key; + args[1 ].type =ACPI_TYPE_INTEGER ; + args[1 ].integer.value = state; + + status =acpi_evaluate_object (handle,"MHKM" , ¶ms,0 ); +return (status ==AE_OK ); +} + +/* Gets value of the pressed key */ +static int get_key (acpi_handle handle) +{ +acpi_status status; +unsigned long key; + + status =acpi_evaluate_integer (handle,"MHKP" ,NULL , &key); +if (ACPI_FAILURE (status)) { + printk(KERN_INFO LOGPREFIX "acpi evaluate error on %s\n", + "HKEY.MHKP"); + printk(KERN_INFO LOGPREFIX "error getting hotkey status\n"); +return (-EFAULT ); + } + +return (key); +} + +void acpi_thinkpad_notify (acpi_handle handle,u32 event,void *data) +{ +struct acpi_thinkpad *thinkpad = (struct acpi_thinkpad *) data; +int status; + +if (!thinkpad || !thinkpad->device) +return ; + +switch (event) { +case HKEY_NOTIFY : + status =get_key (handle); +if (status >0 ) + { +if (status ==0x1004 ) +acpi_evaluate_object (handle,"MHKS" ,NULL , +NULL ); +else if (status ==0x1007 ) +acpi_evaluate_object (NULL ,METHOD_VID_VSWT , +NULL ,NULL ); +else if (status ==0x1005 ) +acpi_evaluate_object (handle,"DTGL" ,NULL , +NULL ); + +acpi_bus_generate_event (thinkpad->device, event, + status); + } +break ; +default : +/* nothing to do */ +break ; + } + +return ; +} + +static int acpi_thinkpad_add (struct acpi_device *device) +{ +int result =0 ; +acpi_status status =AE_OK ; +struct acpi_thinkpad *thinkpad =NULL ; + +if (!device) +return (-EINVAL ); + + thinkpad =kmalloc (sizeof (struct acpi_thinkpad ),GFP_KERNEL ); +if (!thinkpad) +return (-ENOMEM ); + +memset (thinkpad,0 ,sizeof (struct acpi_thinkpad )); + + thinkpad->device = device; + thinkpad->handle = device->handle; +acpi_driver_data (device) = thinkpad; +strcpy (acpi_device_name (device),DEVICE_NAME ); +strcpy (acpi_device_class (device),DEVICE_CLASS ); + +/* activate handling of extra Fn keys (indicate driver is present) */ +handle_keys (1 , thinkpad->handle); + +set_key (5 ,1 , thinkpad->handle); +set_key (7 ,1 , thinkpad->handle); +set_key (8 ,1 , thinkpad->handle); +set_key (9 ,1 , thinkpad->handle); + + status =acpi_install_notify_handler (thinkpad->handle, +ACPI_DEVICE_NOTIFY , + acpi_thinkpad_notify, + thinkpad); +if (ACPI_FAILURE (status)) { + printk(KERN_INFO LOGPREFIX "Error registering notify handler\n"); + result = -ENODEV ; + } + +if (result) +kfree (thinkpad); + +return (result); +} + +static int acpi_thinkpad_remove (struct acpi_device *device,int type) +{ +acpi_status status =0 ; +struct acpi_thinkpad *thinkpad =NULL ; + +if (!device || !acpi_driver_data (device)) +return (-EINVAL ); + + thinkpad =acpi_driver_data (device); + status =acpi_remove_notify_handler (thinkpad->handle, +ACPI_DEVICE_NOTIFY , + acpi_thinkpad_notify); + +/* disable handling of extra Fn keys */ +handle_keys (0 , thinkpad->handle); + +/* return handling of these keys to the firmware */ +set_key (5 ,0 , thinkpad->handle); +set_key (7 ,0 , thinkpad->handle); +set_key (8 ,0 , thinkpad->handle); +set_key (9 ,0 , thinkpad->handle); + +if (ACPI_FAILURE (status)) + printk(KERN_INFO LOGPREFIX "Error removing notify handler\n"); + +kfree (thinkpad); + +return (0 ); +} + +static int __init acpi_thinkpad_init (void ) +{ +acpi_status result =AE_OK ; + +if (acpi_disabled) +return -ENODEV ; + + result =acpi_bus_register_driver (&acpi_thinkpad_driver); +if (result !=1 ) +return -ENODEV ; + + printk(KERN_INFO LOGPREFIX "ACPI IBM Thinkpad Fn+Fx key driver version %s\n", DRIVER_VERSION); + +return 0 ; +} + +static void __exit acpi_thinkpad_exit(void) +{ +acpi_bus_unregister_driver (&acpi_thinkpad_driver); +return ; +} + +module_init (acpi_thinkpad_init); +module_exit (acpi_thinkpad_exit);
drivers/acpi/Makefile +1 -0
@@ -47,4 +47,5 @@ obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o +obj-$(CONFIG_ACPI_THINKPAD) += thinkpad_acpi.o obj-$(CONFIG_ACPI_BUS) += scan.o motherboard.o
drivers/acpi/Kconfig +11 -0
@@ -175,6 +175,17 @@ driver is still under development, so if your laptop is unsupported or something works not quite as expected, please use the mailing list available on the above page ([email protected]) + +config ACPI_THINKPAD + tristate "Thinkpad Fn+Fx key driver" + depends on X86 + depends on ACPI_INTERPRETER + default m + ---help--- + This driver adds support for the Fn+Fx keys on modern Thinkpad + laptops. + + If you have a IBM Thinkpad laptop say Y or M here. config ACPI_TOSHIBA tristate "Toshiba Laptop Extras"