2006-12-08 20:40:44 +03:00
/*
* Copyright ( c ) 2000 - 2001 Vojtech Pavlik
2007-02-21 21:27:49 +03:00
* Copyright ( c ) 2006 - 2007 Jiri Kosina
2006-12-08 20:40:44 +03:00
*
* HID to Linux Input mapping
*/
/*
* 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
*
* Should you need to contact me , the author , you can do so either by
* e - mail - mail your message to < vojtech @ ucw . cz > , or by paper mail :
* Vojtech Pavlik , Simunkova 1594 , Prague 8 , 182 00 Czech Republic
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/hid.h>
2007-01-25 13:43:31 +03:00
# include <linux/hid-debug.h>
2006-12-08 20:40:44 +03:00
# define unk KEY_UNKNOWN
static const unsigned char hid_keyboard [ 256 ] = {
0 , 0 , 0 , 0 , 30 , 48 , 46 , 32 , 18 , 33 , 34 , 35 , 23 , 36 , 37 , 38 ,
50 , 49 , 24 , 25 , 16 , 19 , 31 , 20 , 22 , 47 , 17 , 45 , 21 , 44 , 2 , 3 ,
4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 28 , 1 , 14 , 15 , 57 , 12 , 13 , 26 ,
27 , 43 , 43 , 39 , 40 , 41 , 51 , 52 , 53 , 58 , 59 , 60 , 61 , 62 , 63 , 64 ,
65 , 66 , 67 , 68 , 87 , 88 , 99 , 70 , 119 , 110 , 102 , 104 , 111 , 107 , 109 , 106 ,
105 , 108 , 103 , 69 , 98 , 55 , 74 , 78 , 96 , 79 , 80 , 81 , 75 , 76 , 77 , 71 ,
72 , 73 , 82 , 83 , 86 , 127 , 116 , 117 , 183 , 184 , 185 , 186 , 187 , 188 , 189 , 190 ,
191 , 192 , 193 , 194 , 134 , 138 , 130 , 132 , 128 , 129 , 131 , 137 , 133 , 135 , 136 , 113 ,
115 , 114 , unk , unk , unk , 121 , unk , 89 , 93 , 124 , 92 , 94 , 95 , unk , unk , unk ,
122 , 123 , 90 , 91 , 85 , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk ,
unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk ,
2007-08-09 15:24:11 +04:00
unk , unk , unk , unk , unk , unk , 179 , 180 , unk , unk , unk , unk , unk , unk , unk , unk ,
2006-12-08 20:40:44 +03:00
unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk ,
unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk , unk ,
29 , 42 , 56 , 125 , 97 , 54 , 100 , 126 , 164 , 166 , 165 , 163 , 161 , 115 , 114 , 113 ,
150 , 158 , 159 , 128 , 136 , 177 , 178 , 176 , 142 , 152 , 173 , 140 , unk , unk , unk , unk
} ;
static const struct {
__s32 x ;
__s32 y ;
} hid_hat_to_axis [ ] = { { 0 , 0 } , { 0 , - 1 } , { 1 , - 1 } , { 1 , 0 } , { 1 , 1 } , { 0 , 1 } , { - 1 , 1 } , { - 1 , 0 } , { - 1 , - 1 } } ;
2008-05-16 13:49:18 +04:00
# define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
# define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
# define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
# define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
2006-12-08 20:40:44 +03:00
2008-05-16 13:49:18 +04:00
# define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
& max , EV_ABS , ( c ) )
# define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
& max , EV_KEY , ( c ) )
2006-12-08 20:40:44 +03:00
2007-05-09 12:57:20 +04:00
static inline int match_scancode ( int code , int scancode )
{
if ( scancode = = 0 )
return 1 ;
return ( ( code & ( HID_USAGE_PAGE | HID_USAGE ) ) = = scancode ) ;
}
static inline int match_keycode ( int code , int keycode )
{
if ( keycode = = 0 )
return 1 ;
return ( code = = keycode ) ;
}
static struct hid_usage * hidinput_find_key ( struct hid_device * hid ,
int scancode , int keycode )
{
int i , j , k ;
struct hid_report * report ;
struct hid_usage * usage ;
for ( k = HID_INPUT_REPORT ; k < = HID_OUTPUT_REPORT ; k + + ) {
list_for_each_entry ( report , & hid - > report_enum [ k ] . report_list , list ) {
for ( i = 0 ; i < report - > maxfield ; i + + ) {
for ( j = 0 ; j < report - > field [ i ] - > maxusage ; j + + ) {
usage = report - > field [ i ] - > usage + j ;
if ( usage - > type = = EV_KEY & &
match_scancode ( usage - > hid , scancode ) & &
match_keycode ( usage - > code , keycode ) )
return usage ;
}
}
}
}
return NULL ;
}
static int hidinput_getkeycode ( struct input_dev * dev , int scancode ,
int * keycode )
{
2007-10-31 14:33:26 +03:00
struct hid_device * hid = input_get_drvdata ( dev ) ;
2007-05-09 12:57:20 +04:00
struct hid_usage * usage ;
2007-08-09 16:04:56 +04:00
2007-05-09 12:57:20 +04:00
usage = hidinput_find_key ( hid , scancode , 0 ) ;
if ( usage ) {
* keycode = usage - > code ;
return 0 ;
}
return - EINVAL ;
}
static int hidinput_setkeycode ( struct input_dev * dev , int scancode ,
int keycode )
{
2007-10-31 14:33:26 +03:00
struct hid_device * hid = input_get_drvdata ( dev ) ;
2007-05-09 12:57:20 +04:00
struct hid_usage * usage ;
int old_keycode ;
2007-08-09 16:04:56 +04:00
2007-05-09 12:57:20 +04:00
if ( keycode < 0 | | keycode > KEY_MAX )
return - EINVAL ;
2007-08-09 16:04:56 +04:00
2007-05-09 12:57:20 +04:00
usage = hidinput_find_key ( hid , scancode , 0 ) ;
if ( usage ) {
old_keycode = usage - > code ;
usage - > code = keycode ;
2007-08-09 16:04:56 +04:00
2007-05-09 12:57:20 +04:00
clear_bit ( old_keycode , dev - > keybit ) ;
set_bit ( usage - > code , dev - > keybit ) ;
2007-05-30 17:07:13 +04:00
dbg_hid ( KERN_DEBUG " Assigned keycode %d to HID usage code %x \n " , keycode , scancode ) ;
2007-05-09 12:57:20 +04:00
/* Set the keybit for the old keycode if the old keycode is used
* by another key */
if ( hidinput_find_key ( hid , 0 , old_keycode ) )
set_bit ( old_keycode , dev - > keybit ) ;
2007-08-09 16:04:56 +04:00
2007-05-09 12:57:20 +04:00
return 0 ;
}
2007-08-09 16:04:56 +04:00
2007-05-09 12:57:20 +04:00
return - EINVAL ;
}
2006-12-08 20:40:44 +03:00
static void hidinput_configure_usage ( struct hid_input * hidinput , struct hid_field * field ,
struct hid_usage * usage )
{
struct input_dev * input = hidinput - > input ;
2007-05-09 12:17:31 +04:00
struct hid_device * device = input_get_drvdata ( input ) ;
2008-07-31 13:09:37 +04:00
int max = 0 , code ;
2006-12-08 20:40:44 +03:00
unsigned long * bit = NULL ;
field - > hidinput = hidinput ;
if ( field - > flags & HID_MAIN_ITEM_CONSTANT )
goto ignore ;
2007-08-12 01:39:42 +04:00
/* only LED usages are supported in output fields */
if ( field - > report_type = = HID_OUTPUT_REPORT & &
( usage - > hid & HID_USAGE_PAGE ) ! = HID_UP_LED ) {
goto ignore ;
}
2008-05-16 13:49:16 +04:00
if ( device - > driver - > input_mapping ) {
int ret = device - > driver - > input_mapping ( device , hidinput , field ,
usage , & bit , & max ) ;
if ( ret > 0 )
goto mapped ;
if ( ret < 0 )
goto ignore ;
}
2006-12-08 20:40:44 +03:00
switch ( usage - > hid & HID_USAGE_PAGE ) {
2008-06-19 01:55:41 +04:00
case HID_UP_UNDEFINED :
goto ignore ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_UP_KEYBOARD :
set_bit ( EV_REP , input - > evbit ) ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( ( usage - > hid & HID_USAGE ) < 256 ) {
if ( ! hid_keyboard [ usage - > hid & HID_USAGE ] ) goto ignore ;
map_key_clear ( hid_keyboard [ usage - > hid & HID_USAGE ] ) ;
} else
map_key ( KEY_UNKNOWN ) ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_UP_BUTTON :
code = ( ( usage - > hid - 1 ) & 0xf ) ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
switch ( field - > application ) {
case HID_GD_MOUSE :
case HID_GD_POINTER : code + = 0x110 ; break ;
case HID_GD_JOYSTICK : code + = 0x120 ; break ;
case HID_GD_GAMEPAD : code + = 0x130 ; break ;
default :
switch ( field - > physical ) {
case HID_GD_MOUSE :
case HID_GD_POINTER : code + = 0x110 ; break ;
case HID_GD_JOYSTICK : code + = 0x120 ; break ;
case HID_GD_GAMEPAD : code + = 0x130 ; break ;
default : code + = 0x100 ;
2006-12-08 20:40:44 +03:00
}
2008-06-19 01:55:41 +04:00
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
map_key ( code ) ;
break ;
case HID_UP_SIMULATION :
switch ( usage - > hid & 0xffff ) {
case 0xba : map_abs ( ABS_RUDDER ) ; break ;
case 0xbb : map_abs ( ABS_THROTTLE ) ; break ;
case 0xc4 : map_abs ( ABS_GAS ) ; break ;
case 0xc5 : map_abs ( ABS_BRAKE ) ; break ;
case 0xc8 : map_abs ( ABS_WHEEL ) ; break ;
default : goto ignore ;
}
break ;
case HID_UP_GENDESK :
if ( ( usage - > hid & 0xf0 ) = = 0x80 ) { /* SystemControl */
switch ( usage - > hid & 0xf ) {
case 0x1 : map_key_clear ( KEY_POWER ) ; break ;
case 0x2 : map_key_clear ( KEY_SLEEP ) ; break ;
case 0x3 : map_key_clear ( KEY_WAKEUP ) ; break ;
default : goto unknown ;
2006-12-08 20:40:44 +03:00
}
break ;
2008-06-19 01:55:41 +04:00
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
if ( ( usage - > hid & 0xf0 ) = = 0x90 ) { /* D-pad */
2006-12-08 20:40:44 +03:00
switch ( usage - > hid ) {
2008-06-19 01:55:41 +04:00
case HID_GD_UP : usage - > hat_dir = 1 ; break ;
case HID_GD_DOWN : usage - > hat_dir = 5 ; break ;
case HID_GD_RIGHT : usage - > hat_dir = 3 ; break ;
case HID_GD_LEFT : usage - > hat_dir = 7 ; break ;
default : goto unknown ;
2006-12-08 20:40:44 +03:00
}
2008-06-19 01:55:41 +04:00
if ( field - > dpad ) {
map_abs ( field - > dpad ) ;
goto ignore ;
2007-01-15 19:28:47 +03:00
}
2008-06-19 01:55:41 +04:00
map_abs ( ABS_HAT0X ) ;
2006-12-08 20:40:44 +03:00
break ;
2008-06-19 01:55:41 +04:00
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
switch ( usage - > hid ) {
/* These usage IDs map directly to the usage codes. */
case HID_GD_X : case HID_GD_Y : case HID_GD_Z :
case HID_GD_RX : case HID_GD_RY : case HID_GD_RZ :
case HID_GD_SLIDER : case HID_GD_DIAL : case HID_GD_WHEEL :
if ( field - > flags & HID_MAIN_ITEM_RELATIVE )
map_rel ( usage - > hid & 0xf ) ;
else
map_abs ( usage - > hid & 0xf ) ;
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GD_HATSWITCH :
usage - > hat_min = field - > logical_minimum ;
usage - > hat_max = field - > logical_maximum ;
map_abs ( ABS_HAT0X ) ;
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_GD_START : map_key_clear ( BTN_START ) ; break ;
case HID_GD_SELECT : map_key_clear ( BTN_SELECT ) ; break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
default : goto unknown ;
}
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
break ;
case HID_UP_LED :
switch ( usage - > hid & 0xffff ) { /* HID-Value: */
case 0x01 : map_led ( LED_NUML ) ; break ; /* "Num Lock" */
case 0x02 : map_led ( LED_CAPSL ) ; break ; /* "Caps Lock" */
case 0x03 : map_led ( LED_SCROLLL ) ; break ; /* "Scroll Lock" */
case 0x04 : map_led ( LED_COMPOSE ) ; break ; /* "Compose" */
case 0x05 : map_led ( LED_KANA ) ; break ; /* "Kana" */
case 0x27 : map_led ( LED_SLEEP ) ; break ; /* "Stand-By" */
case 0x4c : map_led ( LED_SUSPEND ) ; break ; /* "System Suspend" */
case 0x09 : map_led ( LED_MUTE ) ; break ; /* "Mute" */
case 0x4b : map_led ( LED_MISC ) ; break ; /* "Generic Indicator" */
case 0x19 : map_led ( LED_MAIL ) ; break ; /* "Message Waiting" */
case 0x4d : map_led ( LED_CHARGING ) ; break ; /* "External Power Connected" */
default : goto ignore ;
}
break ;
case HID_UP_DIGITIZER :
switch ( usage - > hid & 0xff ) {
case 0x30 : /* TipPressure */
if ( ! test_bit ( BTN_TOUCH , input - > keybit ) ) {
device - > quirks | = HID_QUIRK_NOTOUCH ;
set_bit ( EV_KEY , input - > evbit ) ;
set_bit ( BTN_TOUCH , input - > keybit ) ;
2006-12-08 20:40:44 +03:00
}
2008-06-19 01:55:41 +04:00
map_abs_clear ( ABS_PRESSURE ) ;
2006-12-08 20:40:44 +03:00
break ;
2008-06-19 01:55:41 +04:00
case 0x32 : /* InRange */
switch ( field - > physical & 0xff ) {
case 0x21 : map_key ( BTN_TOOL_MOUSE ) ; break ;
case 0x22 : map_key ( BTN_TOOL_FINGER ) ; break ;
default : map_key ( BTN_TOOL_PEN ) ; break ;
2006-12-08 20:40:44 +03:00
}
break ;
2008-06-19 01:55:41 +04:00
case 0x3c : /* Invert */
map_key_clear ( BTN_TOOL_RUBBER ) ;
2006-12-08 20:40:44 +03:00
break ;
2008-06-19 01:55:41 +04:00
case 0x33 : /* Touch */
case 0x42 : /* TipSwitch */
case 0x43 : /* TipSwitch2 */
device - > quirks & = ~ HID_QUIRK_NOTOUCH ;
map_key_clear ( BTN_TOUCH ) ;
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case 0x44 : /* BarrelSwitch */
map_key_clear ( BTN_STYLUS ) ;
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
default : goto unknown ;
}
break ;
case HID_UP_CONSUMER : /* USB HUT v1.1, pages 56-62 */
switch ( usage - > hid & HID_USAGE ) {
case 0x000 : goto ignore ;
case 0x034 : map_key_clear ( KEY_SLEEP ) ; break ;
case 0x036 : map_key_clear ( BTN_MISC ) ; break ;
case 0x040 : map_key_clear ( KEY_MENU ) ; break ;
case 0x045 : map_key_clear ( KEY_RADIO ) ; break ;
case 0x083 : map_key_clear ( KEY_LAST ) ; break ;
case 0x088 : map_key_clear ( KEY_PC ) ; break ;
case 0x089 : map_key_clear ( KEY_TV ) ; break ;
case 0x08a : map_key_clear ( KEY_WWW ) ; break ;
case 0x08b : map_key_clear ( KEY_DVD ) ; break ;
case 0x08c : map_key_clear ( KEY_PHONE ) ; break ;
case 0x08d : map_key_clear ( KEY_PROGRAM ) ; break ;
case 0x08e : map_key_clear ( KEY_VIDEOPHONE ) ; break ;
case 0x08f : map_key_clear ( KEY_GAMES ) ; break ;
case 0x090 : map_key_clear ( KEY_MEMO ) ; break ;
case 0x091 : map_key_clear ( KEY_CD ) ; break ;
case 0x092 : map_key_clear ( KEY_VCR ) ; break ;
case 0x093 : map_key_clear ( KEY_TUNER ) ; break ;
case 0x094 : map_key_clear ( KEY_EXIT ) ; break ;
case 0x095 : map_key_clear ( KEY_HELP ) ; break ;
case 0x096 : map_key_clear ( KEY_TAPE ) ; break ;
case 0x097 : map_key_clear ( KEY_TV2 ) ; break ;
case 0x098 : map_key_clear ( KEY_SAT ) ; break ;
case 0x09a : map_key_clear ( KEY_PVR ) ; break ;
case 0x09c : map_key_clear ( KEY_CHANNELUP ) ; break ;
case 0x09d : map_key_clear ( KEY_CHANNELDOWN ) ; break ;
case 0x0a0 : map_key_clear ( KEY_VCR2 ) ; break ;
case 0x0b0 : map_key_clear ( KEY_PLAY ) ; break ;
case 0x0b1 : map_key_clear ( KEY_PAUSE ) ; break ;
case 0x0b2 : map_key_clear ( KEY_RECORD ) ; break ;
case 0x0b3 : map_key_clear ( KEY_FASTFORWARD ) ; break ;
case 0x0b4 : map_key_clear ( KEY_REWIND ) ; break ;
case 0x0b5 : map_key_clear ( KEY_NEXTSONG ) ; break ;
case 0x0b6 : map_key_clear ( KEY_PREVIOUSSONG ) ; break ;
case 0x0b7 : map_key_clear ( KEY_STOPCD ) ; break ;
case 0x0b8 : map_key_clear ( KEY_EJECTCD ) ; break ;
case 0x0bc : map_key_clear ( KEY_MEDIA_REPEAT ) ; break ;
case 0x0cd : map_key_clear ( KEY_PLAYPAUSE ) ; break ;
case 0x0e0 : map_abs_clear ( ABS_VOLUME ) ; break ;
case 0x0e2 : map_key_clear ( KEY_MUTE ) ; break ;
case 0x0e5 : map_key_clear ( KEY_BASSBOOST ) ; break ;
case 0x0e9 : map_key_clear ( KEY_VOLUMEUP ) ; break ;
case 0x0ea : map_key_clear ( KEY_VOLUMEDOWN ) ; break ;
case 0x182 : map_key_clear ( KEY_BOOKMARKS ) ; break ;
case 0x183 : map_key_clear ( KEY_CONFIG ) ; break ;
case 0x184 : map_key_clear ( KEY_WORDPROCESSOR ) ; break ;
case 0x185 : map_key_clear ( KEY_EDITOR ) ; break ;
case 0x186 : map_key_clear ( KEY_SPREADSHEET ) ; break ;
case 0x187 : map_key_clear ( KEY_GRAPHICSEDITOR ) ; break ;
case 0x188 : map_key_clear ( KEY_PRESENTATION ) ; break ;
case 0x189 : map_key_clear ( KEY_DATABASE ) ; break ;
case 0x18a : map_key_clear ( KEY_MAIL ) ; break ;
case 0x18b : map_key_clear ( KEY_NEWS ) ; break ;
case 0x18c : map_key_clear ( KEY_VOICEMAIL ) ; break ;
case 0x18d : map_key_clear ( KEY_ADDRESSBOOK ) ; break ;
case 0x18e : map_key_clear ( KEY_CALENDAR ) ; break ;
case 0x191 : map_key_clear ( KEY_FINANCE ) ; break ;
case 0x192 : map_key_clear ( KEY_CALC ) ; break ;
case 0x194 : map_key_clear ( KEY_FILE ) ; break ;
case 0x196 : map_key_clear ( KEY_WWW ) ; break ;
case 0x19c : map_key_clear ( KEY_LOGOFF ) ; break ;
case 0x19e : map_key_clear ( KEY_COFFEE ) ; break ;
case 0x1a6 : map_key_clear ( KEY_HELP ) ; break ;
case 0x1a7 : map_key_clear ( KEY_DOCUMENTS ) ; break ;
case 0x1ab : map_key_clear ( KEY_SPELLCHECK ) ; break ;
case 0x1b6 : map_key_clear ( KEY_MEDIA ) ; break ;
case 0x1b7 : map_key_clear ( KEY_SOUND ) ; break ;
case 0x1bc : map_key_clear ( KEY_MESSENGER ) ; break ;
case 0x1bd : map_key_clear ( KEY_INFO ) ; break ;
case 0x201 : map_key_clear ( KEY_NEW ) ; break ;
case 0x202 : map_key_clear ( KEY_OPEN ) ; break ;
case 0x203 : map_key_clear ( KEY_CLOSE ) ; break ;
case 0x204 : map_key_clear ( KEY_EXIT ) ; break ;
case 0x207 : map_key_clear ( KEY_SAVE ) ; break ;
case 0x208 : map_key_clear ( KEY_PRINT ) ; break ;
case 0x209 : map_key_clear ( KEY_PROPS ) ; break ;
case 0x21a : map_key_clear ( KEY_UNDO ) ; break ;
case 0x21b : map_key_clear ( KEY_COPY ) ; break ;
case 0x21c : map_key_clear ( KEY_CUT ) ; break ;
case 0x21d : map_key_clear ( KEY_PASTE ) ; break ;
case 0x21f : map_key_clear ( KEY_FIND ) ; break ;
case 0x221 : map_key_clear ( KEY_SEARCH ) ; break ;
case 0x222 : map_key_clear ( KEY_GOTO ) ; break ;
case 0x223 : map_key_clear ( KEY_HOMEPAGE ) ; break ;
case 0x224 : map_key_clear ( KEY_BACK ) ; break ;
case 0x225 : map_key_clear ( KEY_FORWARD ) ; break ;
case 0x226 : map_key_clear ( KEY_STOP ) ; break ;
case 0x227 : map_key_clear ( KEY_REFRESH ) ; break ;
case 0x22a : map_key_clear ( KEY_BOOKMARKS ) ; break ;
case 0x22d : map_key_clear ( KEY_ZOOMIN ) ; break ;
case 0x22e : map_key_clear ( KEY_ZOOMOUT ) ; break ;
case 0x22f : map_key_clear ( KEY_ZOOMRESET ) ; break ;
case 0x233 : map_key_clear ( KEY_SCROLLUP ) ; break ;
case 0x234 : map_key_clear ( KEY_SCROLLDOWN ) ; break ;
case 0x238 : map_rel ( REL_HWHEEL ) ; break ;
case 0x25f : map_key_clear ( KEY_CANCEL ) ; break ;
case 0x279 : map_key_clear ( KEY_REDO ) ; break ;
case 0x289 : map_key_clear ( KEY_REPLY ) ; break ;
case 0x28b : map_key_clear ( KEY_FORWARDMAIL ) ; break ;
case 0x28c : map_key_clear ( KEY_SEND ) ; break ;
default : goto ignore ;
}
break ;
case HID_UP_HPVENDOR : /* Reported on a Dutch layout HP5308 */
set_bit ( EV_REP , input - > evbit ) ;
switch ( usage - > hid & HID_USAGE ) {
case 0x021 : map_key_clear ( KEY_PRINT ) ; break ;
case 0x070 : map_key_clear ( KEY_HP ) ; break ;
case 0x071 : map_key_clear ( KEY_CAMERA ) ; break ;
case 0x072 : map_key_clear ( KEY_SOUND ) ; break ;
case 0x073 : map_key_clear ( KEY_QUESTION ) ; break ;
case 0x080 : map_key_clear ( KEY_EMAIL ) ; break ;
case 0x081 : map_key_clear ( KEY_CHAT ) ; break ;
case 0x082 : map_key_clear ( KEY_SEARCH ) ; break ;
case 0x083 : map_key_clear ( KEY_CONNECT ) ; break ;
case 0x084 : map_key_clear ( KEY_FINANCE ) ; break ;
case 0x085 : map_key_clear ( KEY_SPORT ) ; break ;
case 0x086 : map_key_clear ( KEY_SHOP ) ; break ;
default : goto ignore ;
}
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_UP_MSVENDOR :
goto ignore ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_UP_CUSTOM : /* Reported on Logitech and Apple USB keyboards */
set_bit ( EV_REP , input - > evbit ) ;
goto ignore ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
case HID_UP_LOGIVENDOR :
goto ignore ;
case HID_UP_PID :
switch ( usage - > hid & HID_USAGE ) {
case 0xa4 : map_key_clear ( BTN_DEAD ) ; break ;
default : goto ignore ;
}
break ;
2006-12-08 20:40:44 +03:00
2008-06-19 01:55:41 +04:00
default :
unknown :
if ( field - > report_size = = 1 ) {
if ( field - > report - > type = = HID_OUTPUT_REPORT ) {
map_led ( LED_MISC ) ;
2006-12-08 20:40:44 +03:00
break ;
}
2008-06-19 01:55:41 +04:00
map_key ( BTN_MISC ) ;
2006-12-08 20:40:44 +03:00
break ;
2008-06-19 01:55:41 +04:00
}
if ( field - > flags & HID_MAIN_ITEM_RELATIVE ) {
map_rel ( REL_MISC ) ;
break ;
}
map_abs ( ABS_MISC ) ;
break ;
2006-12-08 20:40:44 +03:00
}
2007-11-22 17:18:18 +03:00
mapped :
2008-05-16 13:49:16 +04:00
if ( device - > driver - > input_mapped & & device - > driver - > input_mapped ( device ,
hidinput , field , usage , & bit , & max ) < 0 )
goto ignore ;
2006-12-08 20:40:44 +03:00
set_bit ( usage - > type , input - > evbit ) ;
while ( usage - > code < = max & & test_and_set_bit ( usage - > code , bit ) )
usage - > code = find_next_zero_bit ( bit , max + 1 , usage - > code ) ;
if ( usage - > code > max )
goto ignore ;
if ( usage - > type = = EV_ABS ) {
int a = field - > logical_minimum ;
int b = field - > logical_maximum ;
if ( ( device - > quirks & HID_QUIRK_BADPAD ) & & ( usage - > code = = ABS_X | | usage - > code = = ABS_Y ) ) {
a = field - > logical_minimum = 0 ;
b = field - > logical_maximum = 255 ;
}
if ( field - > application = = HID_GD_GAMEPAD | | field - > application = = HID_GD_JOYSTICK )
input_set_abs_params ( input , usage - > code , a , b , ( b - a ) > > 8 , ( b - a ) > > 4 ) ;
else input_set_abs_params ( input , usage - > code , a , b , 0 , 0 ) ;
}
if ( usage - > type = = EV_ABS & &
( usage - > hat_min < usage - > hat_max | | usage - > hat_dir ) ) {
int i ;
for ( i = usage - > code ; i < usage - > code + 2 & & i < = max ; i + + ) {
input_set_abs_params ( input , i , - 1 , 1 , 0 , 0 ) ;
set_bit ( i , input - > absbit ) ;
}
if ( usage - > hat_dir & & ! field - > dpad )
field - > dpad = usage - > code ;
}
2007-07-04 18:45:59 +04:00
/* for those devices which produce Consumer volume usage as relative,
* we emulate pressing volumeup / volumedown appropriate number of times
* in hidinput_hid_event ( )
*/
if ( ( usage - > type = = EV_ABS ) & & ( field - > flags & HID_MAIN_ITEM_RELATIVE ) & &
( usage - > code = = ABS_VOLUME ) ) {
set_bit ( KEY_VOLUMEUP , input - > keybit ) ;
set_bit ( KEY_VOLUMEDOWN , input - > keybit ) ;
}
2007-08-20 14:13:34 +04:00
if ( usage - > type = = EV_KEY ) {
set_bit ( EV_MSC , input - > evbit ) ;
set_bit ( MSC_SCAN , input - > mscbit ) ;
}
2006-12-08 20:40:44 +03:00
ignore :
return ;
2009-06-12 17:20:55 +04:00
2006-12-08 20:40:44 +03:00
}
void hidinput_hid_event ( struct hid_device * hid , struct hid_field * field , struct hid_usage * usage , __s32 value )
{
struct input_dev * input ;
2007-10-14 22:35:40 +04:00
unsigned * quirks = & hid - > quirks ;
2006-12-08 20:40:44 +03:00
if ( ! field - > hidinput )
return ;
input = field - > hidinput - > input ;
if ( ! usage - > type )
return ;
if ( usage - > hat_min < usage - > hat_max | | usage - > hat_dir ) {
int hat_dir = usage - > hat_dir ;
if ( ! hat_dir )
hat_dir = ( value - usage - > hat_min ) * 8 / ( usage - > hat_max - usage - > hat_min + 1 ) + 1 ;
if ( hat_dir < 0 | | hat_dir > 8 ) hat_dir = 0 ;
input_event ( input , usage - > type , usage - > code , hid_hat_to_axis [ hat_dir ] . x ) ;
input_event ( input , usage - > type , usage - > code + 1 , hid_hat_to_axis [ hat_dir ] . y ) ;
return ;
}
if ( usage - > hid = = ( HID_UP_DIGITIZER | 0x003c ) ) { /* Invert */
* quirks = value ? ( * quirks | HID_QUIRK_INVERT ) : ( * quirks & ~ HID_QUIRK_INVERT ) ;
return ;
}
if ( usage - > hid = = ( HID_UP_DIGITIZER | 0x0032 ) ) { /* InRange */
if ( value ) {
input_event ( input , usage - > type , ( * quirks & HID_QUIRK_INVERT ) ? BTN_TOOL_RUBBER : usage - > code , 1 ) ;
return ;
}
input_event ( input , usage - > type , usage - > code , 0 ) ;
input_event ( input , usage - > type , BTN_TOOL_RUBBER , 0 ) ;
return ;
}
if ( usage - > hid = = ( HID_UP_DIGITIZER | 0x0030 ) & & ( * quirks & HID_QUIRK_NOTOUCH ) ) { /* Pressure */
int a = field - > logical_minimum ;
int b = field - > logical_maximum ;
input_event ( input , EV_KEY , BTN_TOUCH , value > a + ( ( b - a ) > > 3 ) ) ;
}
if ( usage - > hid = = ( HID_UP_PID | 0x83UL ) ) { /* Simultaneous Effects Max */
2007-05-30 17:07:13 +04:00
dbg_hid ( " Maximum Effects - %d \n " , value ) ;
2006-12-08 20:40:44 +03:00
return ;
}
if ( usage - > hid = = ( HID_UP_PID | 0x7fUL ) ) {
2007-05-30 17:07:13 +04:00
dbg_hid ( " PID Pool Report \n " ) ;
2006-12-08 20:40:44 +03:00
return ;
}
if ( ( usage - > type = = EV_KEY ) & & ( usage - > code = = 0 ) ) /* Key 0 is "unassigned", not KEY_UNKNOWN */
return ;
2007-07-04 18:45:59 +04:00
if ( ( usage - > type = = EV_ABS ) & & ( field - > flags & HID_MAIN_ITEM_RELATIVE ) & &
( usage - > code = = ABS_VOLUME ) ) {
int count = abs ( value ) ;
int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN ;
int i ;
for ( i = 0 ; i < count ; i + + ) {
input_event ( input , EV_KEY , direction , 1 ) ;
input_sync ( input ) ;
input_event ( input , EV_KEY , direction , 0 ) ;
input_sync ( input ) ;
}
return ;
}
2007-08-20 14:13:34 +04:00
/* report the usage code as scancode if the key status has changed */
if ( usage - > type = = EV_KEY & & ! ! test_bit ( usage - > code , input - > key ) ! = value )
input_event ( input , EV_MSC , MSC_SCAN , usage - > hid ) ;
2007-08-09 15:24:11 +04:00
2006-12-08 20:40:44 +03:00
input_event ( input , usage - > type , usage - > code , value ) ;
if ( ( field - > flags & HID_MAIN_ITEM_RELATIVE ) & & ( usage - > type = = EV_KEY ) )
input_event ( input , usage - > type , usage - > code , 0 ) ;
}
void hidinput_report_event ( struct hid_device * hid , struct hid_report * report )
{
struct hid_input * hidinput ;
list_for_each_entry ( hidinput , & hid - > inputs , list )
input_sync ( hidinput - > input ) ;
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hidinput_report_event ) ;
2006-12-08 20:40:44 +03:00
2006-12-08 20:40:53 +03:00
int hidinput_find_field ( struct hid_device * hid , unsigned int type , unsigned int code , struct hid_field * * field )
2006-12-08 20:40:44 +03:00
{
struct hid_report * report ;
int i , j ;
list_for_each_entry ( report , & hid - > report_enum [ HID_OUTPUT_REPORT ] . report_list , list ) {
for ( i = 0 ; i < report - > maxfield ; i + + ) {
* field = report - > field [ i ] ;
for ( j = 0 ; j < ( * field ) - > maxusage ; j + + )
if ( ( * field ) - > usage [ j ] . type = = type & & ( * field ) - > usage [ j ] . code = = code )
return j ;
}
}
return - 1 ;
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hidinput_find_field ) ;
2006-12-08 20:40:44 +03:00
2007-01-24 13:54:19 +03:00
static int hidinput_open ( struct input_dev * dev )
{
2007-05-09 12:17:31 +04:00
struct hid_device * hid = input_get_drvdata ( dev ) ;
2008-05-16 13:49:16 +04:00
return hid - > ll_driver - > open ( hid ) ;
2007-01-24 13:54:19 +03:00
}
static void hidinput_close ( struct input_dev * dev )
{
2007-05-09 12:17:31 +04:00
struct hid_device * hid = input_get_drvdata ( dev ) ;
2008-05-16 13:49:16 +04:00
hid - > ll_driver - > close ( hid ) ;
2007-01-24 13:54:19 +03:00
}
2006-12-08 20:40:44 +03:00
/*
* Register the input device ; print a message .
* Configure the input layer interface
* Read all reports and initialize the absolute field values .
*/
2008-06-27 02:04:24 +04:00
int hidinput_connect ( struct hid_device * hid , unsigned int force )
2006-12-08 20:40:44 +03:00
{
struct hid_report * report ;
struct hid_input * hidinput = NULL ;
struct input_dev * input_dev ;
int i , j , k ;
2007-01-11 17:51:17 +03:00
int max_report_type = HID_OUTPUT_REPORT ;
2006-12-08 20:40:44 +03:00
INIT_LIST_HEAD ( & hid - > inputs ) ;
2008-06-27 02:04:24 +04:00
if ( ! force ) {
for ( i = 0 ; i < hid - > maxcollection ; i + + ) {
struct hid_collection * col = & hid - > collection [ i ] ;
if ( col - > type = = HID_COLLECTION_APPLICATION | |
col - > type = = HID_COLLECTION_PHYSICAL )
if ( IS_INPUT_APPLICATION ( col - > usage ) )
break ;
}
2006-12-08 20:40:44 +03:00
2008-06-27 02:04:24 +04:00
if ( i = = hid - > maxcollection )
return - 1 ;
}
2006-12-08 20:40:44 +03:00
2007-01-11 17:51:17 +03:00
if ( hid - > quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS )
max_report_type = HID_INPUT_REPORT ;
for ( k = HID_INPUT_REPORT ; k < = max_report_type ; k + + )
2006-12-08 20:40:44 +03:00
list_for_each_entry ( report , & hid - > report_enum [ k ] . report_list , list ) {
if ( ! report - > maxfield )
continue ;
if ( ! hidinput ) {
hidinput = kzalloc ( sizeof ( * hidinput ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! hidinput | | ! input_dev ) {
kfree ( hidinput ) ;
input_free_device ( input_dev ) ;
2007-05-30 17:07:13 +04:00
err_hid ( " Out of memory during hid input probe " ) ;
2007-10-30 15:02:44 +03:00
goto out_unwind ;
2006-12-08 20:40:44 +03:00
}
2007-05-09 12:17:31 +04:00
input_set_drvdata ( input_dev , hid ) ;
2008-05-16 13:49:16 +04:00
input_dev - > event =
hid - > ll_driver - > hidinput_input_event ;
2007-01-24 13:54:19 +03:00
input_dev - > open = hidinput_open ;
input_dev - > close = hidinput_close ;
2007-05-09 12:57:20 +04:00
input_dev - > setkeycode = hidinput_setkeycode ;
input_dev - > getkeycode = hidinput_getkeycode ;
2006-12-08 20:40:44 +03:00
input_dev - > name = hid - > name ;
input_dev - > phys = hid - > phys ;
input_dev - > uniq = hid - > uniq ;
2006-12-08 20:40:53 +03:00
input_dev - > id . bustype = hid - > bus ;
input_dev - > id . vendor = hid - > vendor ;
input_dev - > id . product = hid - > product ;
input_dev - > id . version = hid - > version ;
2008-05-16 13:49:15 +04:00
input_dev - > dev . parent = hid - > dev . parent ;
2006-12-08 20:40:44 +03:00
hidinput - > input = input_dev ;
list_add_tail ( & hidinput - > list , & hid - > inputs ) ;
}
for ( i = 0 ; i < report - > maxfield ; i + + )
for ( j = 0 ; j < report - > field [ i ] - > maxusage ; j + + )
hidinput_configure_usage ( hidinput , report - > field [ i ] ,
report - > field [ i ] - > usage + j ) ;
if ( hid - > quirks & HID_QUIRK_MULTI_INPUT ) {
/* This will leave hidinput NULL, so that it
* allocates another one if we have more inputs on
* the same interface . Some devices ( e . g . Happ ' s
* UGCI ) cram a lot of unrelated inputs into the
* same interface . */
hidinput - > report = report ;
2007-10-30 15:02:44 +03:00
if ( input_register_device ( hidinput - > input ) )
goto out_cleanup ;
2006-12-08 20:40:44 +03:00
hidinput = NULL ;
}
}
2007-10-30 15:02:44 +03:00
if ( hidinput & & input_register_device ( hidinput - > input ) )
goto out_cleanup ;
2006-12-08 20:40:44 +03:00
return 0 ;
2007-10-30 15:02:44 +03:00
out_cleanup :
input_free_device ( hidinput - > input ) ;
kfree ( hidinput ) ;
out_unwind :
/* unwind the ones we already registered */
hidinput_disconnect ( hid ) ;
return - 1 ;
2006-12-08 20:40:44 +03:00
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hidinput_connect ) ;
2006-12-08 20:40:44 +03:00
void hidinput_disconnect ( struct hid_device * hid )
{
struct hid_input * hidinput , * next ;
list_for_each_entry_safe ( hidinput , next , & hid - > inputs , list ) {
list_del ( & hidinput - > list ) ;
input_unregister_device ( hidinput - > input ) ;
kfree ( hidinput ) ;
}
}
2006-12-08 20:40:53 +03:00
EXPORT_SYMBOL_GPL ( hidinput_disconnect ) ;