LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)

时间:2022-07-28 16:11:38


 
走完之前的程序后的数据结构如下
LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
 
注意,之后会把local中的数据清空
好,到下一组为0x09, 0x01
这是一个局域项目,重新向局域结构中添加项目
下一个组为0xa1, 0x00
这是一个主项目,用于物理集合收集的开始
添加完成后的数据结构如下
LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
 
又把局域结构中的数据清零
到下一组,为0x05, 0x09
这是一个全局项目,重设用途
继续下一组, 0x19, 0x01
这是一个局域项目,用途为设定添加项目的最小值
//设置开始设置的最小项

        case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            //检测数据的大小是否小于或者等于2字节
            if (item->size <= 2)
                //加上作用标记
                data = (parser->global.usage_page << 16) + data;
            parser->local.usage_minimum = data;
            return 0;

到下一组, 0x29, 0x03
这是一个局域项目,用途为设定添加项目的最大值

        case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            //检测数据的大小是否小于或者等于2字节
            if (item->size <= 2)
                //加上作用标记
                data = (parser->global.usage_page << 16) + data;
            //添加要求的项
            for (n = parser->local.usage_minimum; n <= data; n++)
                if (hid_add_usage(parser, n)) {
                    dbg_hid("hid_add_usage failed\n");
                    return -1;
                }
            return 0;

下一组为0x15, 0x00
这是全局项目,用于设置逻辑最小值

case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
            parser->global.logical_minimum = item_sdata(item);
            return 0;

下一组为0x25, 0x01
这是一个全局项目,用于设置逻辑最大值

case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
            //检测是否需要符号来表示负数
            if (parser->global.logical_minimum < 0)
                parser->global.logical_maximum = item_sdata(item);
            else
                parser->global.logical_maximum = item_udata(item);
            return 0;

下一组为0x95, 0x03
这是一个全局项目,用于设置项目的个数

        case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
            //检测是否超越最大个数
            if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
                dbg_hid("invalid report_count %d\n", parser->global.report_count);
                return -1;
            }
            return 0;

下一组为0x75, 0x01
这是一个全局项目,用于设置单个项目所需要的bit数目

        case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
            //检测是否超过32个bit,也就是4个字节
            if ((parser->global.report_size = item_udata(item)) > 32) {
                dbg_hid("invalid report_size %d\n", parser->global.report_size);
                return -1;
            }
            return 0;

下一组为0x81, 0x02
这是一个主项目,用于将设置好的项目信息添加到域中

        case HID_MAIN_ITEM_TAG_INPUT:
            ret = hid_add_field(parser, HID_INPUT_REPORT, data);
            break;

hid_add_field在/drivers/hid/hid-core.c中

static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags)
{
    struct hid_report *report;
    struct hid_field *field;
    int usages;
    unsigned offset;
    int i;
    //注册一个报告
    if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
        dbg_hid("hid_register_report failed\n");
        return -1;
    }
    if (parser->global.logical_maximum < parser->global.logical_minimum) {
        dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum);
        return -1;
    }
    //计算偏移
    offset = report->size;
    //计算所有域数据的大小
    report->size += parser->global.report_size * parser->global.report_count;
    //检测是否有项,无则为占位域,不处理
    if (!parser->local.usage_index) /* Ignore padding fields */
        return 0;
    //检测是否需要重复最后一个项
    usages = max_t(int, parser->local.usage_index, parser->global.report_count);
    //注册一个域
    if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
        return 0;
    //检测域的物理属性
    field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
    //检测域的逻辑属性
    field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
    //检测域的应用属性
    field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
    //历遍项目
    for (i = 0; i < usages; i++) {
        int j = i;
        /* Duplicate the last usage we parsed if we have excess values */
        //超过项的最大数目则重复最后一个项
        if (i >= parser->local.usage_index)
            j = parser->local.usage_index - 1;
        //拷贝用途
        field->usage[i].hid = parser->local.usage[j];
        //拷贝所处的收集
        field->usage[i].collection_index =
            parser->local.collection_index[j];
    }
    field->maxusage = usages;
    field->flags = flags;
    field->report_offset = offset;
    field->report_type = report_type;
    field->report_size = parser->global.report_size;
    field->report_count = parser->global.report_count;
    field->logical_minimum = parser->global.logical_minimum;
    field->logical_maximum = parser->global.logical_maximum;
    field->physical_minimum = parser->global.physical_minimum;
    field->physical_maximum = parser->global.physical_maximum;
    field->unit_exponent = parser->global.unit_exponent;
    field->unit = parser->global.unit;
    return 0;
}

hid_register_report在/drivers/hid/hid-core.c中

 

static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
{
    struct hid_report_enum *report_enum = device->report_enum + type;
    struct hid_report *report;
    //检测是否已经注册
    if (report_enum->report_id_hash[id])
        return report_enum->report_id_hash[id];
    if (!(report = kzalloc(sizeof(struct hid_report), GFP_KERNEL)))
        return NULL;
    if (id != 0)
        report_enum->numbered = 1;
    report->id = id;
    report->type = type;
    report->size = 0;
    report->device = device;
    report_enum->report_id_hash[id] = report;
    list_add_tail(&report->list, &report_enum->report_list);
    return report;
}

回到hid_add_field中,现在到hid_register_field
hid_register_field在/drivers/hid/hid-core.c中

static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
{
    struct hid_field *field;
    //每个报告最大只支持64个域
    if (report->maxfield == HID_MAX_FIELDS) {
        dbg_hid("too many fields in report\n");
        return NULL;
    }
    //申请空间,hid_usage用于存放项,values用于存放项所需要的usb数据
    if (!(field = kzalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
        + values * sizeof(unsigned), GFP_KERNEL))) return NULL;
    field->index = report->maxfield++;
    report->field[field->index] = field;
    //令usage指针指向第一个项
    field->usage = (struct hid_usage *)(field + 1);
    //令value指针指向第一个数据缓冲的首地址
    field->value = (s32 *)(field->usage + usages);
    field->report = report;
    return field;
}

注册完后的数据结构如下

LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
 
 
现在又要把local中的内容清零了
好~ 到下一组0x95, 0x01
这里为全局项目,用途为修改项的数目

case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
            //检测是否超越最大个数
            if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
                dbg_hid("invalid report_count %d\n", parser->global.report_count);
                return -1;
            }
            return 0;

下一组为0x75, 0x05
这是一个全局项目,用于修改项的大小为5bit

case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
            //检测是否超过32个bit,也就是4个字节
            if ((parser->global.report_size = item_udata(item)) > 32) {
                dbg_hid("invalid report_size %d\n", parser->global.report_size);
                return -1;
            }
            return 0;

接着下一组为0x81, 0x03
这是一个主项目, 用于将设置好的项目信息添加到域中

case HID_MAIN_ITEM_TAG_INPUT:
            ret = hid_add_field(parser, HID_INPUT_REPORT, data);
            break;

添加完成后的数据结构图如下

LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
 
咋看之下和之前的没什么不同,但是请注意report中的size,这里就变成8了~
也就是说我们所需要的占位效果出来了,之后的域数会从第9位开始读取
local继续被清0~
好` 下一组0x05, 0x01
这是一个全局项目,用于设置全局用途

        case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
            parser->global.usage_page = item_udata(item);
            return 0;

接着下一组0x09, 0x30
这是一个局域项目,用于设置用途

case HID_LOCAL_ITEM_TAG_USAGE:
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            //检测数据的大小是否小于或者等于2字节
            if (item->size <= 2)
                //加上作用标记
                data = (parser->global.usage_page << 16) + data;
            return hid_add_usage(parser, data);

下一组0x09, 0x31
这是一个局域项目,用于设置用途

case HID_LOCAL_ITEM_TAG_USAGE:
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            //检测数据的大小是否小于或者等于2字节
            if (item->size <= 2)
                //加上作用标记
                data = (parser->global.usage_page << 16) + data;
            return hid_add_usage(parser, data);

下一组0x09, 0x38
这是一个局域项目,用于设置用途

case HID_LOCAL_ITEM_TAG_USAGE:
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            //检测数据的大小是否小于或者等于2字节
            if (item->size <= 2)
                //加上作用标记
                data = (parser->global.usage_page << 16) + data;
            return hid_add_usage(parser, data);

下一组0x15, 0x81
这是一个全局项目,用于设置逻辑最小值

        case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
            parser->global.logical_minimum = item_sdata(item);
            return 0;

下一组0x25, 0x7f
这是一个全局项目,用于设置逻辑最大值

        case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
            //检测是否需要符号来表示负数
            if (parser->global.logical_minimum < 0)
                parser->global.logical_maximum = item_sdata(item);
            else
                parser->global.logical_maximum = item_udata(item);
            return 0;

下一组0x75, 0x08
这是一个全局项目,用于设置单个项的所需要的bit数目

case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
            //检测是否超过32个bit,也就是4个字节
            if ((parser->global.report_size = item_udata(item)) > 32) {
                dbg_hid("invalid report_size %d\n", parser->global.report_size);
                return -1;
            }
            return 0;

下一组0x95, 0x03
这是一个全局项目,用于设置项的数目

case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
            //检测是否超越最大个数
            if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
                dbg_hid("invalid report_count %d\n", parser->global.report_count);
                return -1;
            }
            return 0;

下一组0x81, 0x06
这是一个主项目, 用于将设置好的项目信息添加到域中

case HID_MAIN_ITEM_TAG_INPUT:
            ret = hid_add_field(parser, HID_INPUT_REPORT, data);
            break;

添加好后的数据结构如下图

LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
 
下一组0xc0
这是一个主项目,用于结束物理收集

case HID_MAIN_ITEM_TAG_END_COLLECTION:
            ret = close_collection(parser);
            break;

下一组0xc0
这是一个主项目,用于结束物理收集

case HID_MAIN_ITEM_TAG_END_COLLECTION:
            ret = close_collection(parser);
            break;

到这里报告描述符就分析完了,然后过河拆桥,卸磨杀驴,把parser结构给释放掉
回到usb_hid_configure中,之后根据端点描述符申请相应的in类型urb或者out类型urb,最后是控制类型的urb,配置完成的数据结构图如下

LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)

 

连向usb设备的结构和urb我就不画出来了
usb_hid_configure完成后来到usbhid_init_reports
但是usbhid_init_reports对于鼠标是没有作用的
因为他所调用的usbhid_submit_report函数中会判断怪癖是否有HID_QUIRK_NOGET
而所有的鼠标都会有HID_QUIRK_NOGET这个设置,所以直接返回
现在来到梦寐以求的hidinput_connect
hidinput_connect负责input子系统和hid设备的连接
hidinput_connect在/drivers/hid/hid-input.c中

int hidinput_connect(struct hid_device *hid)
{
    struct hid_report *report;
    struct hid_input *hidinput = NULL;
    struct input_dev *input_dev;
    int i, j, k;
    int max_report_type = HID_OUTPUT_REPORT;
    if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT)
        return -1;
    INIT_LIST_HEAD(&hid->inputs);
    //寻找应用收集
    for (i = 0; i < hid->maxcollection; i++)
        if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
         hid->collection[i].type == HID_COLLECTION_PHYSICAL)
            if (IS_INPUT_APPLICATION(hid->collection[i].usage))
                break;
    if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0)
        return -1;
    if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
        max_report_type = HID_INPUT_REPORT;
    //历遍应用收集对应的报告
    for (k = HID_INPUT_REPORT; k <= max_report_type; k++)
        list_for_each_entry(report, &hid->report_enum[k].report_list, list)
        {
            if (!report->maxfield)
                continue;
            //检测是否已经分配
            if (!hidinput)
            {
                //分配hid_input结构所需要的空间
                hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
                //分配一个input_dev结构
                input_dev = input_allocate_device();
                if (!hidinput || !input_dev)
                {
                    kfree(hidinput);
                    input_free_device(input_dev);
                    err_hid("Out of memory during hid input probe");
                    goto out_unwind;
                }
                input_set_drvdata(input_dev, hid);
                input_dev->event = hid->hidinput_input_event;
                input_dev->open = hidinput_open;
                input_dev->close = hidinput_close;
                input_dev->setkeycode = hidinput_setkeycode;
                input_dev->getkeycode = hidinput_getkeycode;
                input_dev->name = hid->name;
                input_dev->phys = hid->phys;
                input_dev->uniq = hid->uniq;
                input_dev->id.bustype = hid->bus;
                input_dev->id.vendor = hid->vendor;
                input_dev->id.product = hid->product;
                input_dev->id.version = hid->version;
                input_dev->dev.parent = hid->dev;
                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;

                if (input_register_device(hidinput->input))
                    goto out_cleanup;
                hidinput = NULL;
            }
        }
    //匹配input驱动
    if (hidinput && input_register_device(hidinput->input))
        goto out_cleanup;
    return 0;
out_cleanup:
    input_free_device(hidinput->input);
    kfree(hidinput);
out_unwind:
    /* unwind the ones we already registered */
    hidinput_disconnect(hid);
    return -1;
}

首先申请了一个input_dev结构并对其进行初始化,然后分析hid协议
分析工作由hidinput_configure_usage完成
hidinput_configure_usage在drivers/hid/hid-input.c中

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_BUTTON:
            code = ((usage->hid - 1) & 0xf);
            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;
                    }
            }
            /* 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);
            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;
                }
                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;
        default:
        unknown:
            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:
    if (device->quirks & HID_QUIRK_MIGHTYMOUSE)
    {
        if (usage->hid == HID_GD_Z)
            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);
    //检测是否大于最大值
    //检测位图中的位是否已经占用,无占用则置这个位为1
    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;
    }
    /* 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)
    {
        set_bit(EV_MSC, input->evbit);
        set_bit(MSC_SCAN, input->mscbit);
    }
    hid_resolv_event(usage->type, usage->code);
    dbg_hid_line("\n");
    return;
ignore:
    dbg_hid_line("IGNORED\n");
    return;
}

我们以第一个域中的第一个项为例进行分析
首先判断其用途,这里usage->hid为0x90001,呢么就会到case HID_UP_BUTTON 中
然后hid-1,取其最低4位,90001-1为90000,最低4位为0,呢么code就是0了
然后检测域中 的application属性,这里为0x10002,呢么就是case HID_GD_MOUSE,
code += 0x110,呢么现在code现在为0x110,然后检测是否为罗技的产品,我们显然不是,然后到map_key(code),map_key是一个宏
#define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
这就是他的宏定义
我们这里需要注意的是bit = input->keybit和max = KEY_MAX这两个
然后到set_bit(usage->type, input->evbit),这里就要注意了,先看看evbit是什么
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
EV_CNT为0x20,0x20转换成10进制就是32,也就是说需要32个位,1个位表示1种事件
呢么这里unsigned long的类型根据x86来说就是32位,呢么这个数组其实只有1个成员,来看一下keybit
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
KEY_CNT为0x200,10进制就是512,也就说需要512个位, unsigned long是32位,呢么就是512/32 = 16,需要16个unsigned long来描述,而使用的时候会把这个数组初始化成一个bit序列来看,所以像test_and_set_bit(usage->code, bit),如果code等于0x110,转换为10进制就是273,也就是置512中的第273位为1,并返回第273位原本的数值
回到set_bit(usage->type, input->evbit),这里也就是置evbit中的第usage->type为1,usage->type为0x01,也就是置第一位为1
最后到
if (usage->type == EV_KEY)
 {
  set_bit(EV_MSC, input->evbit);
  set_bit(MSC_SCAN, input->mscbit);
 }
不用多说了吧,设置evbit的第EV_MSC位为1,设置mscbit的第MSC_SCAN位为1
这样一个项就分析完成了
全部分析完后的数据结构如下图
LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)

 

分析完协议之后就开始匹配input子系统中的处理模块了
这个入口在input_register_device
input_register_device在/drivers/input/input.c

int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT(0);
    struct input_handler *handler;
    const char *path;
    int error;
    __set_bit(EV_SYN, dev->evbit);
    /*
     * If delay and period are pre-set by the driver, then autorepeating
     * is handled by the driver itself and we don't do it in input.c.
     */

    //初始化定时器结构
    init_timer(&dev->timer);
    if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
        dev->timer.data = (long) dev;
        dev->timer.function = input_repeat_key;
        dev->rep[REP_DELAY] = 250;
        dev->rep[REP_PERIOD] = 33;
    }
    if (!dev->getkeycode)
        dev->getkeycode = input_default_getkeycode;
    if (!dev->setkeycode)
        dev->setkeycode = input_default_setkeycode;
    //建立一个对应input设备
    snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
         "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
    error = device_add(&dev->dev);
    if (error)
        return error;
    path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
    printk(KERN_INFO "input: %s as %s\n",
        dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    kfree(path);
    error = mutex_lock_interruptible(&input_mutex);
    if (error) {
        device_del(&dev->dev);
        return error;
    }
    //将设备挂载到设备链表下
    list_add_tail(&dev->node, &input_dev_list);
    //历遍处理模块
    list_for_each_entry(handler, &input_handler_list, node)
        //进行匹配
        input_attach_handler(dev, handler);
    input_wakeup_procfs_readers();
    mutex_unlock(&input_mutex);
    return 0;
}

一路来到input_attach_handler
input_attach_handler在drivers/input/input.c中

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int eror;
    //检测处理模块是否有黑名单并进行黑名单的匹配
    if (handler->blacklist && input_match_device(handler->blacklist, dev))
        return -ENODEV;
    //匹配模块特性
    id = input_match_device(handler->id_table, dev);
    if (!id)
        return -ENODEV;
    //匹配成功则连接设备与模块
    error = handler->connect(handler, dev, id);
    if (error && error != -ENODEV)
        printk(KERN_ERR
            "input: failed to attach handler %s to device %s, "
            "error: %d\n",
            handler->name, kobject_name(&dev->dev.kobj), error);
    return error;
}

到这里就要开始看看input系统下mouse模块的挂载了
mousedev_init完成mouse模块的挂载
mousedev_init在/drivers/input/mousedev.c中


static int __init mousedev_init(void)
{
    int error;
    //注册一个misc的空设备
    mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX);
    if (IS_ERR(mousedev_mix))
        return PTR_ERR(mousedev_mix);
    //挂载到input模块下
    error = input_register_handler(&mousedev_handler);
    if (error) {
        mousedev_destroy(mousedev_mix);
        return error;
    }
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
    error = misc_register(&psaux_mouse);
    if (error)
        printk(KERN_WARNING "mice: could not register psaux device, "
            "error: %d\n", error);
    else
        psaux_registered = 1;
#endif
    printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
    return 0;
}

首先是mice的注册
mousedev_create在/drivers/input/mousedev.c中

static struct mousedev *mousedev_create(struct input_dev *dev,
                    struct input_handler *handler,
                    int minor)
{
    struct mousedev *mousedev;
    int error;
    mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);
    if (!mousedev) {
        error = -ENOMEM;
        goto err_out;
    }
    INIT_LIST_HEAD(&mousedev->client_list);
    INIT_LIST_HEAD(&mousedev->mixdev_node);
    spin_lock_init(&mousedev->client_lock);
    mutex_init(&mousedev->mutex);
    lockdep_set_subclass(&mousedev->mutex,
             minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
    init_waitqueue_head(&mousedev->wait);
    //判断是否为mice设备
    if (minor == MOUSEDEV_MIX)
        strlcpy(mousedev->name, "mice", sizeof(mousedev->name));
    else
        snprintf(mousedev->name, sizeof(mousedev->name),
             "mouse%d", minor);
    mousedev->minor = minor;
    mousedev->exist = 1;
    mousedev->handle.dev = input_get_device(dev);
    mousedev->handle.name = mousedev->name;
    mousedev->handle.handler = handler;
    mousedev->handle.private = mousedev;
    strlcpy(mousedev->dev.bus_id, mousedev->name,
        sizeof(mousedev->dev.bus_id));
    mousedev->dev.class = &input_class;
    if (dev)
        mousedev->dev.parent = &dev->dev;
    mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor);
    mousedev->dev.release = mousedev_free;
    device_initialize(&mousedev->dev);
    if (minor != MOUSEDEV_MIX) {
        //注册一个连接器
        error = input_register_handle(&mousedev->handle);
        if (error)
            goto err_free_mousedev;
    }
    //注册进mouse设备组中
    error = mousedev_install_chrdev(mousedev);
    if (error)
        goto err_unregister_handle;
    error = device_add(&mousedev->dev);
    if (error)
        goto err_cleanup_mousedev;
    return mousedev;
 err_cleanup_mousedev:
    mousedev_cleanup(mousedev);
 err_unregister_handle:
    if (minor != MOUSEDEV_MIX)
        input_unregister_handle(&mousedev->handle);
 err_free_mousedev:
    put_device(&mousedev->dev);
 err_out:
    return ERR_PTR(error);
}

由于这里minor为MOUSEDEV_MIX,所以是不会进行连接器的注册的
然后到mousedev_install_chrdev
mousedev_install_chrdev在/drivers/input/mousedev.c中

static int mousedev_install_chrdev(struct mousedev *mousedev)
{
    mousedev_table[mousedev->minor] = mousedev;
    return 0;
}

很简单,就是根据minor在mousedev_table设备数组中占一个位置
这样mice就注册好了,如下图
LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
 
光溜溜的.......
说一下这个mice设备的作用,这个mice用于打开或者关闭所有的鼠标设备,做为一个统一管理
回到mousedev_init中,现在进入input_register_handler
input_register_handler在/drivers/input/input.c中

int input_register_handler(struct input_handler *handler)
{
    struct input_dev *dev;
    int retval;
    retval = mutex_lock_interruptible(&input_mutex);
    if (retval)
        return retval;
    INIT_LIST_HEAD(&handler->h_list);
    //检测处理模块的操作集是否为空
    if (handler->fops != NULL) {
        //检测处理模块数组中的对应位置是否为空
        if (input_table[handler->minor >> 5]) {
            retval = -EBUSY;
            goto out;
        }
        //占用相应位置
        input_table[handler->minor >> 5] = handler;
    }
    //添加到处理模块队列中
    list_add_tail(&handler->node, &input_handler_list);
    //历遍设备队列
    list_for_each_entry(dev, &input_dev_list, node)
        //匹配设备
        input_attach_handler(dev, handler);
    input_wakeup_procfs_readers();
 out:
    mutex_unlock(&input_mutex);
    return retval;
}

这样mouse模块就挂载完成了