Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15531158
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: 嵌入式

2009-07-21 22:20:55

《浅析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;
}
阅读(6955) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~