2011-02-17 21:44:09 +03:00
/*
* WMI hotkeys support for Dell All - In - One series
*
* 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
*/
2011-03-30 02:21:37 +04:00
2011-02-17 21:44:09 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/input.h>
# include <linux/input/sparse-keymap.h>
# include <acpi/acpi_drivers.h>
# include <linux/acpi.h>
# include <linux/string.h>
MODULE_DESCRIPTION ( " WMI hotkeys driver for Dell All-In-One series " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
# define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
static const char * dell_wmi_aio_guids [ ] = {
EVENT_GUID1 ,
EVENT_GUID2 ,
NULL
} ;
MODULE_ALIAS ( " wmi: " EVENT_GUID1 ) ;
MODULE_ALIAS ( " wmi: " EVENT_GUID2 ) ;
static const struct key_entry dell_wmi_aio_keymap [ ] = {
{ KE_KEY , 0xc0 , { KEY_VOLUMEUP } } ,
{ KE_KEY , 0xc1 , { KEY_VOLUMEDOWN } } ,
{ KE_END , 0 }
} ;
static struct input_dev * dell_wmi_aio_input_dev ;
static void dell_wmi_aio_notify ( u32 value , void * context )
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
acpi_status status ;
status = wmi_get_event_data ( value , & response ) ;
if ( status ! = AE_OK ) {
pr_info ( " bad event status 0x%x \n " , status ) ;
return ;
}
obj = ( union acpi_object * ) response . pointer ;
if ( obj ) {
unsigned int scancode ;
switch ( obj - > type ) {
case ACPI_TYPE_INTEGER :
/* Most All-In-One correctly return integer scancode */
scancode = obj - > integer . value ;
sparse_keymap_report_event ( dell_wmi_aio_input_dev ,
scancode , 1 , true ) ;
break ;
case ACPI_TYPE_BUFFER :
/* Broken machines return the scancode in a buffer */
if ( obj - > buffer . pointer & & obj - > buffer . length > 0 ) {
scancode = obj - > buffer . pointer [ 0 ] ;
sparse_keymap_report_event (
dell_wmi_aio_input_dev ,
scancode , 1 , true ) ;
}
break ;
}
}
kfree ( obj ) ;
}
static int __init dell_wmi_aio_input_setup ( void )
{
int err ;
dell_wmi_aio_input_dev = input_allocate_device ( ) ;
if ( ! dell_wmi_aio_input_dev )
return - ENOMEM ;
dell_wmi_aio_input_dev - > name = " Dell AIO WMI hotkeys " ;
dell_wmi_aio_input_dev - > phys = " wmi/input0 " ;
dell_wmi_aio_input_dev - > id . bustype = BUS_HOST ;
err = sparse_keymap_setup ( dell_wmi_aio_input_dev ,
dell_wmi_aio_keymap , NULL ) ;
if ( err ) {
pr_err ( " Unable to setup input device keymap \n " ) ;
goto err_free_dev ;
}
err = input_register_device ( dell_wmi_aio_input_dev ) ;
if ( err ) {
pr_info ( " Unable to register input device \n " ) ;
goto err_free_keymap ;
}
return 0 ;
err_free_keymap :
sparse_keymap_free ( dell_wmi_aio_input_dev ) ;
err_free_dev :
input_free_device ( dell_wmi_aio_input_dev ) ;
return err ;
}
static const char * dell_wmi_aio_find ( void )
{
int i ;
for ( i = 0 ; dell_wmi_aio_guids [ i ] ! = NULL ; i + + )
if ( wmi_has_guid ( dell_wmi_aio_guids [ i ] ) )
return dell_wmi_aio_guids [ i ] ;
return NULL ;
}
static int __init dell_wmi_aio_init ( void )
{
int err ;
const char * guid ;
guid = dell_wmi_aio_find ( ) ;
if ( ! guid ) {
2011-03-30 02:21:37 +04:00
pr_warn ( " No known WMI GUID found \n " ) ;
2011-02-17 21:44:09 +03:00
return - ENXIO ;
}
err = dell_wmi_aio_input_setup ( ) ;
if ( err )
return err ;
err = wmi_install_notify_handler ( guid , dell_wmi_aio_notify , NULL ) ;
if ( err ) {
pr_err ( " Unable to register notify handler - %d \n " , err ) ;
sparse_keymap_free ( dell_wmi_aio_input_dev ) ;
input_unregister_device ( dell_wmi_aio_input_dev ) ;
return err ;
}
return 0 ;
}
static void __exit dell_wmi_aio_exit ( void )
{
const char * guid ;
guid = dell_wmi_aio_find ( ) ;
wmi_remove_notify_handler ( guid ) ;
sparse_keymap_free ( dell_wmi_aio_input_dev ) ;
input_unregister_device ( dell_wmi_aio_input_dev ) ;
}
module_init ( dell_wmi_aio_init ) ;
module_exit ( dell_wmi_aio_exit ) ;