《浅析usbhid驱动hidinput_connect细则》// 该函数主要是设置hidinput->input中的keybit,ledbit,他们的第usage->code位置1表示,
// 位值为1表示该位对应的国际键值将被input有效使用,
// 同时设置usage->code对应的鼠标键盘国际标准值,设置usage->type值为EV_KEY﹑EV_LED等.[luther.gliethttp]
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage)
{
struct input_dev *input = hidinput->input;
struct hid_device *device = input_get_drvdata(input);
int max = 0, code, ret;
unsigned long *bit = NULL;
field->hidinput = hidinput;
dbg_hid("Mapping: ");
hid_resolv_usage(usage->hid);
dbg_hid_line(" ---> ");
if (field->flags & HID_MAIN_ITEM_CONSTANT)
goto ignore;
/* only LED usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT &&
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
dbg_hid_line(" [non-LED output field] ");
goto ignore;
}
/* handle input mappings for quirky devices */
ret = hidinput_mapping_quirks(usage, input, &bit, &max);
if (ret)
goto mapped;
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_UNDEFINED:
goto ignore;
case HID_UP_KEYBOARD:
set_bit(EV_REP, input->evbit); // 具备EV_REP重复按键功能
if ((usage->hid & HID_USAGE) < 256) {
if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
map_key_clear(hid_keyboard[usage->hid & HID_USAGE]); // 该usage->hid对应的hid_keyboard真实键盘code值[luther.gliethttp]
} else // 最后usage->code = hid_keyboard[usage->hid & HID_USAGE];
map_key(KEY_UNKNOWN);
break;
case HID_UP_BUTTON:
code = ((usage->hid - 1) & 0xf);
switch (field->application) {
case HID_GD_MOUSE:
case HID_GD_POINTER: code += 0x110; break; // 转为国际标准上该功能对应的code值[luther.gliethttp]
case HID_GD_JOYSTICK: code += 0x120; break; // 鼠标左﹑右和中键国际标准code值
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;
}
}
/* Special handling for Logitech Cordless Desktop */
if (field->application != HID_GD_MOUSE) {
if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) {
int hid = usage->hid & HID_USAGE;
if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0)
code = logitech_expanded_keymap[hid];
}
} else {
if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) {
int hid = usage->hid & HID_USAGE;
if (hid == 7 || hid == 8)
goto ignore;
}
}
map_key(code); // 为该usage赋上国际对应code值,usage->code = code;[luther.gliethttp]
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 */ // 含有系统控制键信息,那么为usage赋值
switch (usage->hid & 0xf) {
case 0x1: map_key_clear(KEY_POWER); break; // usage->code = power键值
case 0x2: map_key_clear(KEY_SLEEP); break; // usage->code = sleep键值
case 0x3: map_key_clear(KEY_WAKEUP); break; // usage->code = wakeup键值[luther.gliethttp]
default: goto unknown;
}
break;
}
if ((usage->hid & 0xf0) == 0x90) { /* D-pad */
switch (usage->hid) {
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;
}
if (field->dpad) {
map_abs(field->dpad);
goto ignore;
}
map_abs(ABS_HAT0X);
break;
}
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;
case HID_GD_HATSWITCH:
usage->hat_min = field->logical_minimum;
usage->hat_max = field->logical_maximum;
map_abs(ABS_HAT0X);
break;
case HID_GD_START: map_key_clear(BTN_START); break;
case HID_GD_SELECT: map_key_clear(BTN_SELECT); break;
default: goto unknown;
}
break;
case HID_UP_LED:
// 定义了键盘指示灯,那么对其进行code赋值
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);
}
map_abs_clear(ABS_PRESSURE);
break;
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;
}
break;
case 0x3c: /* Invert */
map_key_clear(BTN_TOOL_RUBBER);
break;
case 0x33: /* Touch */
case 0x42: /* TipSwitch */
case 0x43: /* TipSwitch2 */
device->quirks &= ~HID_QUIRK_NOTOUCH;
map_key_clear(BTN_TOUCH);
break;
case 0x44: /* BarrelSwitch */
map_key_clear(BTN_STYLUS);
break;
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 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;
case HID_UP_MSVENDOR:
goto ignore;
case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
set_bit(EV_REP, input->evbit);
switch(usage->hid & HID_USAGE) {
case 0x003:
/* The fn key on Apple USB keyboards */
map_key_clear(KEY_FN);
hidinput_apple_setup(input);
break;
default: goto ignore;
}
break;
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;
default:
unknown: // 未知usage->hid对应的USAGE_PAGE
if (field->report_size == 1) {
if (field->report->type == HID_OUTPUT_REPORT) {
map_led(LED_MISC);
break;
}
map_key(BTN_MISC);
break;
}
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
map_rel(REL_MISC);
break;
}
map_abs(ABS_MISC);
break;
}
mapped: // 这里我们已经给该usage指定上了usage->code国际值[luther.gliethttp]
if (device->quirks & HID_QUIRK_MIGHTYMOUSE) { // 并且bit已经指向了input结构体对应的单元,比如input->keybit
if (usage->hid == HID_GD_Z) // 下面对个别情况进行数值微调[luther.gliethttp]
map_rel(REL_HWHEEL);
else if (usage->code == BTN_1)
map_key(BTN_2);
else if (usage->code == BTN_2)
map_key(BTN_1);
}
if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 |
HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) &&
(usage->code == REL_WHEEL))
set_bit(REL_HWHEEL, bit);
if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
|| ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
goto ignore;
if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) &&
usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE))
field->flags &= ~HID_MAIN_ITEM_RELATIVE;
set_bit(usage->type, input->evbit);
if (device->quirks & HID_QUIRK_DUPLICATE_USAGES &&
(usage->type == EV_KEY ||
usage->type == EV_REL ||
usage->type == EV_ABS))
clear_bit(usage->code, bit);
// test_and_set_bit()该函数设置bit中第usage->code位,同时返回第usage->code位原有数据
// 如果原始usage->code位为1,那么说明第usage->code位已经被占用,所以需要向后查找
// 一个非0位,不过上面在该usage->code赋值的时候,就已经调用map_key_clear
// 将第usage->code位清了0,所以这里基本不会去执行while循环.[luther.gliethttp]
while (usage->code <= max && test_and_set_bit(usage->code, bit))// ____atomic_test_and_set_bit置位bit的第usage->code个bit
usage->code = find_next_zero_bit(bit, max + 1, usage->code);// lib/find_next_bit.c,像上面基本都调用map_key_clear
// 已经将bit的usage->code位置0,所以这里find_next_zero_bit
if (usage->code > max) // 返回的数值就是usage->code.[luther.gliethttp]
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;
}
/* 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);
}
if (usage->type == EV_KEY) { // 只要是按键usage,那么就要同时启用EV_MSC和MSC_SCAN
set_bit(EV_MSC, input->evbit);
set_bit(MSC_SCAN, input->mscbit);
}
hid_resolv_event(usage->type, usage->code); // 比如:打印Key.A和Key.ScrollLock等调试信息[luther.gliethttp]
dbg_hid_line("\n");
return;
ignore:
dbg_hid_line("IGNORED\n");
return;
}