2008-06-20 23:26:11 +04:00
/*
* HID driver for some microsoft " special " devices
*
* Copyright ( c ) 1999 Andreas Gal
* Copyright ( c ) 2000 - 2005 Vojtech Pavlik < vojtech @ suse . cz >
* Copyright ( c ) 2005 Michael Haboustak < mike - @ cinci . rr . com > for Concept2 , Inc
* Copyright ( c ) 2006 - 2007 Jiri Kosina
* Copyright ( c ) 2007 Paul Walmsley
* Copyright ( c ) 2008 Jiri Slaby
*/
/*
* 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 .
*/
# include <linux/device.h>
# include <linux/input.h>
# include <linux/hid.h>
# include <linux/module.h>
# include "hid-ids.h"
# define MS_HIDINPUT 0x01
# define MS_ERGONOMY 0x02
# define MS_PRESENTER 0x04
# define MS_RDESC 0x08
# define MS_NOGET 0x10
/*
* Microsoft Wireless Desktop Receiver ( Model 1028 ) has several
* ' Usage Min / Max ' where it ought to have ' Physical Min / Max '
*/
static void ms_report_fixup ( struct hid_device * hdev , __u8 * rdesc ,
unsigned int rsize )
{
unsigned long quirks = ( unsigned long ) hid_get_drvdata ( hdev ) ;
if ( ( quirks & MS_RDESC ) & & rsize = = 571 & & rdesc [ 284 ] = = 0x19 & &
rdesc [ 286 ] = = 0x2a & & rdesc [ 304 ] = = 0x19 & &
rdesc [ 306 ] = = 0x29 & & rdesc [ 352 ] = = 0x1a & &
rdesc [ 355 ] = = 0x2a & & rdesc [ 557 ] = = 0x19 & &
rdesc [ 559 ] = = 0x29 ) {
dev_info ( & hdev - > dev , " fixing up Microsoft Wireless Receiver "
" Model 1028 report descriptor \n " ) ;
rdesc [ 284 ] = rdesc [ 304 ] = rdesc [ 557 ] = 0x35 ;
rdesc [ 352 ] = 0x36 ;
rdesc [ 286 ] = rdesc [ 355 ] = 0x46 ;
rdesc [ 306 ] = rdesc [ 559 ] = 0x45 ;
}
}
# define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
EV_KEY , ( c ) )
static int ms_ergonomy_kb_quirk ( struct hid_input * hi , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
struct input_dev * input = hi - > input ;
switch ( usage - > hid & HID_USAGE ) {
case 0xfd06 : ms_map_key_clear ( KEY_CHAT ) ; break ;
case 0xfd07 : ms_map_key_clear ( KEY_PHONE ) ; break ;
case 0xff05 :
set_bit ( EV_REP , input - > evbit ) ;
ms_map_key_clear ( KEY_F13 ) ;
set_bit ( KEY_F14 , input - > keybit ) ;
set_bit ( KEY_F15 , input - > keybit ) ;
set_bit ( KEY_F16 , input - > keybit ) ;
set_bit ( KEY_F17 , input - > keybit ) ;
set_bit ( KEY_F18 , input - > keybit ) ;
default :
return 0 ;
}
return 1 ;
}
static int ms_presenter_8k_quirk ( struct hid_input * hi , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
set_bit ( EV_REP , hi - > input - > evbit ) ;
switch ( usage - > hid & HID_USAGE ) {
case 0xfd08 : ms_map_key_clear ( KEY_FORWARD ) ; break ;
case 0xfd09 : ms_map_key_clear ( KEY_BACK ) ; break ;
case 0xfd0b : ms_map_key_clear ( KEY_PLAYPAUSE ) ; break ;
case 0xfd0e : ms_map_key_clear ( KEY_CLOSE ) ; break ;
case 0xfd0f : ms_map_key_clear ( KEY_PLAY ) ; break ;
default :
return 0 ;
}
return 1 ;
}
static int ms_input_mapping ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
unsigned long quirks = ( unsigned long ) hid_get_drvdata ( hdev ) ;
if ( ( usage - > hid & HID_USAGE_PAGE ) ! = HID_UP_MSVENDOR )
return 0 ;
if ( quirks & MS_ERGONOMY ) {
int ret = ms_ergonomy_kb_quirk ( hi , usage , bit , max ) ;
if ( ret )
return ret ;
}
if ( ( quirks & MS_PRESENTER ) & &
ms_presenter_8k_quirk ( hi , usage , bit , max ) )
return 1 ;
return 0 ;
}
static int ms_event ( struct hid_device * hdev , struct hid_field * field ,
struct hid_usage * usage , __s32 value )
{
unsigned long quirks = ( unsigned long ) hid_get_drvdata ( hdev ) ;
if ( ! ( hdev - > claimed & HID_CLAIMED_INPUT ) | | ! field - > hidinput | |
! usage - > type )
return 0 ;
/* Handling MS keyboards special buttons */
if ( quirks & MS_ERGONOMY & & usage - > hid = = ( HID_UP_MSVENDOR | 0xff05 ) ) {
struct input_dev * input = field - > hidinput - > input ;
static unsigned int last_key = 0 ;
unsigned int key = 0 ;
switch ( value ) {
case 0x01 : key = KEY_F14 ; break ;
case 0x02 : key = KEY_F15 ; break ;
case 0x04 : key = KEY_F16 ; break ;
case 0x08 : key = KEY_F17 ; break ;
case 0x10 : key = KEY_F18 ; break ;
}
if ( key ) {
input_event ( input , usage - > type , key , 1 ) ;
last_key = key ;
} else
input_event ( input , usage - > type , last_key , 0 ) ;
return 1 ;
}
return 0 ;
}
static int ms_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
unsigned long quirks = id - > driver_data ;
int ret ;
hid_set_drvdata ( hdev , ( void * ) quirks ) ;
if ( quirks & MS_NOGET )
hdev - > quirks | = HID_QUIRK_NOGET ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
dev_err ( & hdev - > dev , " parse failed \n " ) ;
goto err_free ;
}
2008-06-27 02:04:24 +04:00
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT | ( ( quirks & MS_HIDINPUT ) ?
HID_CONNECT_HIDINPUT_FORCE : 0 ) ) ;
2008-06-20 23:26:11 +04:00
if ( ret ) {
dev_err ( & hdev - > dev , " hw start failed \n " ) ;
goto err_free ;
}
return 0 ;
err_free :
return ret ;
}
static const struct hid_device_id ms_devices [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_SIDEWINDER_GV ) ,
. driver_data = MS_HIDINPUT } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_NE4K ) ,
. driver_data = MS_ERGONOMY } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_LK6K ) ,
. driver_data = MS_ERGONOMY | MS_RDESC } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_PRESENTER_8K_USB ) ,
. driver_data = MS_PRESENTER } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 ) ,
. driver_data = MS_NOGET } ,
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_PRESENTER_8K_BT ) ,
. driver_data = MS_PRESENTER } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , ms_devices ) ;
static struct hid_driver ms_driver = {
. name = " microsoft " ,
. id_table = ms_devices ,
. report_fixup = ms_report_fixup ,
. input_mapping = ms_input_mapping ,
. event = ms_event ,
. probe = ms_probe ,
} ;
static int ms_init ( void )
{
return hid_register_driver ( & ms_driver ) ;
}
static void ms_exit ( void )
{
hid_unregister_driver ( & ms_driver ) ;
}
module_init ( ms_init ) ;
module_exit ( ms_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
HID_COMPAT_LOAD_DRIVER ( microsoft ) ;