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 ) 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"
2011-07-11 22:42:52 +04:00
# define MS_HIDINPUT 0x01
# define MS_ERGONOMY 0x02
# define MS_PRESENTER 0x04
# define MS_RDESC 0x08
# define MS_NOGET 0x10
# define MS_DUPLICATE_USAGES 0x20
2012-10-19 15:28:46 +04:00
# define MS_RDESC_3K 0x40
2008-06-20 23:26:11 +04:00
2010-08-06 23:03:06 +04:00
static __u8 * ms_report_fixup ( struct hid_device * hdev , __u8 * rdesc ,
unsigned int * rsize )
2008-06-20 23:26:11 +04:00
{
unsigned long quirks = ( unsigned long ) hid_get_drvdata ( hdev ) ;
2012-10-19 15:28:46 +04:00
/*
* Microsoft Wireless Desktop Receiver ( Model 1028 ) has
* ' Usage Min / Max ' where it ought to have ' Physical Min / Max '
*/
2010-08-06 23:03:06 +04:00
if ( ( quirks & MS_RDESC ) & & * rsize = = 571 & & rdesc [ 557 ] = = 0x19 & &
2008-06-20 23:26:11 +04:00
rdesc [ 559 ] = = 0x29 ) {
2010-12-10 06:29:03 +03:00
hid_info ( hdev , " fixing up Microsoft Wireless Receiver Model 1028 report descriptor \n " ) ;
2009-01-14 05:03:21 +03:00
rdesc [ 557 ] = 0x35 ;
rdesc [ 559 ] = 0x45 ;
2008-06-20 23:26:11 +04:00
}
2012-10-19 15:28:46 +04:00
/* the same as above (s/usage/physical/) */
2012-11-12 13:16:09 +04:00
if ( ( quirks & MS_RDESC_3K ) & & * rsize = = 106 & & rdesc [ 94 ] = = 0x19 & &
rdesc [ 95 ] = = 0x00 & & rdesc [ 96 ] = = 0x29 & &
rdesc [ 97 ] = = 0xff ) {
2012-10-19 15:28:46 +04:00
rdesc [ 94 ] = 0x35 ;
rdesc [ 96 ] = 0x45 ;
}
2010-08-06 23:03:06 +04:00
return rdesc ;
2008-06-20 23:26:11 +04:00
}
# 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 ;
2014-01-29 20:57:42 +04:00
if ( ( usage - > hid & HID_USAGE_PAGE ) ! = HID_UP_MSVENDOR )
return 0 ;
2008-06-20 23:26:11 +04:00
switch ( usage - > hid & HID_USAGE ) {
case 0xfd06 : ms_map_key_clear ( KEY_CHAT ) ; break ;
case 0xfd07 : ms_map_key_clear ( KEY_PHONE ) ; break ;
2014-01-29 20:57:43 +04:00
case 0xff00 :
/* Special keypad keys */
ms_map_key_clear ( KEY_KPEQUAL ) ;
set_bit ( KEY_KPLEFTPAREN , input - > keybit ) ;
set_bit ( KEY_KPRIGHTPAREN , input - > keybit ) ;
break ;
case 0xff01 :
/* Scroll wheel */
hid_map_usage_clear ( hi , usage , bit , max , EV_REL , REL_WHEEL ) ;
break ;
case 0xff02 :
/*
* This byte contains a copy of the modifier keys byte of a
* standard hid keyboard report , as send by interface 0
* ( this usage is found on interface 1 ) .
*
* This byte only gets send when another key in the same report
* changes state , and as such is useless , ignore it .
*/
return - 1 ;
2008-06-20 23:26:11 +04:00
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 ) ;
2013-12-15 21:25:26 +04:00
break ;
2008-06-20 23:26:11 +04:00
default :
return 0 ;
}
return 1 ;
}
static int ms_presenter_8k_quirk ( struct hid_input * hi , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
2014-01-29 20:57:42 +04:00
if ( ( usage - > hid & HID_USAGE_PAGE ) ! = HID_UP_MSVENDOR )
return 0 ;
2008-06-20 23:26:11 +04:00
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 ( 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 ;
}
2011-07-11 22:42:52 +04:00
static int ms_input_mapped ( 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 ( quirks & MS_DUPLICATE_USAGES )
clear_bit ( usage - > code , * bit ) ;
return 0 ;
}
2008-06-20 23:26:11 +04:00
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 ) ;
2014-01-29 20:57:43 +04:00
struct input_dev * input ;
2008-06-20 23:26:11 +04:00
if ( ! ( hdev - > claimed & HID_CLAIMED_INPUT ) | | ! field - > hidinput | |
! usage - > type )
return 0 ;
2014-01-29 20:57:43 +04:00
input = field - > hidinput - > input ;
2008-06-20 23:26:11 +04:00
/* Handling MS keyboards special buttons */
2014-01-29 20:57:43 +04:00
if ( quirks & MS_ERGONOMY & & usage - > hid = = ( HID_UP_MSVENDOR | 0xff00 ) ) {
/* Special keypad keys */
input_report_key ( input , KEY_KPEQUAL , value & 0x01 ) ;
input_report_key ( input , KEY_KPLEFTPAREN , value & 0x02 ) ;
input_report_key ( input , KEY_KPRIGHTPAREN , value & 0x04 ) ;
return 1 ;
}
if ( quirks & MS_ERGONOMY & & usage - > hid = = ( HID_UP_MSVENDOR | 0xff01 ) ) {
/* Scroll wheel */
int step = ( ( value & 0x60 ) > > 5 ) + 1 ;
switch ( value & 0x1f ) {
case 0x01 :
input_report_rel ( input , REL_WHEEL , step ) ;
break ;
case 0x1f :
input_report_rel ( input , REL_WHEEL , - step ) ;
break ;
}
return 1 ;
}
2008-06-20 23:26:11 +04:00
if ( quirks & MS_ERGONOMY & & usage - > hid = = ( HID_UP_MSVENDOR | 0xff05 ) ) {
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 ) {
2010-12-10 06:29:03 +03:00
hid_err ( hdev , " parse failed \n " ) ;
2008-06-20 23:26:11 +04:00
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 ) {
2010-12-10 06:29:03 +03:00
hid_err ( hdev , " hw start failed \n " ) ;
2008-06-20 23:26:11 +04:00
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 } ,
2014-01-29 20:57:43 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_OFFICE_KB ) ,
. driver_data = MS_ERGONOMY } ,
2008-06-20 23:26:11 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_NE4K ) ,
. driver_data = MS_ERGONOMY } ,
2013-04-29 08:27:31 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_NE4K_JP ) ,
. driver_data = MS_ERGONOMY } ,
2008-06-20 23:26:11 +04:00
{ 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 } ,
2011-06-14 00:08:18 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K ) ,
2012-10-19 15:28:46 +04:00
. driver_data = MS_ERGONOMY | MS_RDESC_3K } ,
2008-06-20 23:26:11 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 ) ,
. driver_data = MS_NOGET } ,
2011-07-11 22:42:52 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MICROSOFT , USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 ) ,
. driver_data = MS_DUPLICATE_USAGES } ,
2008-06-20 23:26:11 +04:00
{ 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 ,
2011-07-11 22:42:52 +04:00
. input_mapped = ms_input_mapped ,
2008-06-20 23:26:11 +04:00
. event = ms_event ,
. probe = ms_probe ,
} ;
2012-12-18 02:28:26 +04:00
module_hid_driver ( ms_driver ) ;
2008-06-20 23:26:11 +04:00
MODULE_LICENSE ( " GPL " ) ;