浅析linux下usb鼠标和usb键盘usbhid驱动hid_parse_report报告描述符解析

浅析linux下usb鼠标和usb键盘usbhid驱动hid_parse_report报告描述符解析

hid_probe
==>usb_hid_configure
==*>hid = hid_parse_report(rdesc, n)

/*
 * Parse a report description into a hid_device structure. Reports are
 * enumerated, fields are attached to these reports.
 */
struct hid_device *hid_parse_report(__u8 *start, unsigned size)
{
    struct hid_device *device;
    struct hid_parser *parser;
    struct hid_item item;
    __u8 *end;
    unsigned i;
    static int (*dispatch_type[])(struct hid_parser *parser,
                      struct hid_item *item) = {
        hid_parser_main,
        hid_parser_global,
        hid_parser_local,
        hid_parser_reserved
    };

    if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
        return NULL;

    if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
                   HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
// #define HID_DEFAULT_NUM_COLLECTIONS    16
// 默认申请16个项内存
        kfree(device);
        return NULL;
    }
    device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; // 默认16项

    for (i = 0; i < HID_REPORT_TYPES; i++)
        INIT_LIST_HEAD(&device->report_enum[i].report_list);
// 报告描述符不会很大,所以kmalloc申请(kmalloc申请上限为2M)
    if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {
        kfree(device->collection);
        kfree(device);
        return NULL;
    }
    memcpy(device->rdesc, start, size); // 将从设备读取上来报告描述符永久拷贝到device->rdesc中[luther.gliethttp]
    device->rsize = size; // 记录报告描述符实际大小.

// struct hid_parser该结构体可是一个耗费内存大户,只能使用vmalloc申请只需虚拟地址连续的内核内存[luther.gliethttp]
// 当parser完报告描述符之后,会vfree释放掉,所以也只是生命期较短暂的占用一会内存[luther.gliehttp]
    if (!(parser = vmalloc(sizeof(struct hid_parser)))) {
        kfree(device->rdesc);
        kfree(device->collection);
        kfree(device);
        return NULL;
    }
    memset(parser, 0, sizeof(struct hid_parser));
    parser->device = device; // 该parser需要对该device的报告描述符作解析

    end = start + size;
    // 从设备报告描述符中读取一个item项
    while ((start = fetch_item(start, end, &item)) != NULL) {

        if (item.format != HID_ITEM_FORMAT_SHORT) {
            // 现在Long item项还没有使用,所以这里不支持
            dbg_hid("unexpected long global item\n");
            hid_free_device(device);
            vfree(parser);
            return NULL;
        }
        // 是我们所能支持的Short Item项,数据有0字节,1字节,2字节或者4字节等4中情况[luther.gliethttp]
        // 比如0x05, 0x01, // USAGE_PAGE (Generic Desktop)
        // 从fetch_item中我们知道,item.type = (0x05 >> 2) & 0x03 = 1;[luther.gliehttp]
        // 所以将调用hid_parser_global处理函数.
        // hid_parser_global    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
        // hid_parser_local     0x09, 0x02, // USAGE (Mouse)
        // hid_parser_main      0xa1, 0x01, // COLLECTION (Application)
        // hid_parser_local     0x09, 0x01, //   USAGE (Pointer)
        // hid_parser_main      0xa1, 0x00, //   COLLECTION (Physical)
        // hid_parser_global    0x05, 0x09, //     USAGE_PAGE (Button)
        // hid_parser_local     0x19, 0x01, //     USAGE_MINIMUM (Button 1)
        // ......
        if (dispatch_type[item.type](parser, &item)) {
            dbg_hid("item %u %u %u %u parsing failed\n",
                item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
            hid_free_device(device);
            vfree(parser);
            return NULL;
        }

        if (start == end) {
        // 解析完了
            if (parser->collection_stack_ptr) {
                // 入栈操作多于出栈操作,比如
                // COLLECTION (Application)就是入栈
                // END_COLLECTION对应出栈
                // 目前定义堆栈大小为4个
                // #define HID_COLLECTION_STACK_SIZE 4
                // 所以报告描述符脚本书写有误,返回NULL,失败[luther.gliethttp]
                dbg_hid("unbalanced collection at end of report description\n");
                hid_free_device(device);
                vfree(parser);
                return NULL;
            }
            if (parser->local.delimiter_depth) {
                // 该变量也是通过入栈,出栈收集的,所以也必须配对[luther.giehtttp]
                dbg_hid("unbalanced delimiter at end of report description\n");
                hid_free_device(device);
                vfree(parser);
                return NULL;
            }
            vfree(parser); // 正常解析,释放vmalloc到的parser解释器结构体内存.
            return device;
        }
    }
// 报告描述脚本有误[luther.gliehttp]
    dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
    hid_free_device(device);
    vfree(parser);
    return NULL;
}

/*
 * Fetch a report description item from the data stream. We support long
 * items, though they are not used yet.
 */
static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
{
    u8 b;

    if ((end - start) <= 0)
        return NULL;

    b = *start++; // 取出第1个开始字节,比如0x05, 0x01, --- USAGE_PAGE (Generic Desktop)

    item->type = (b >> 2) & 3; // 取出类型[luther.gliethttp]
    item->tag  = (b >> 4) & 15;// 取出tag信息

    if (item->tag == HID_ITEM_TAG_LONG) {
    // Long items: 3 – 258 bytes in length; used for items that require larger data
    //             structures for parts.
        item->format = HID_ITEM_FORMAT_LONG;

        if ((end - start) < 2) // 空间不足,b算1个字节,所以至少保证还要2字节,才能到3个字节以上[luther.gliethttp]
                               // 0字节 -- b
                               // 1字节 -- 该Long itme的大小
                               // 2字节 -- tag信息[luther.gliethttp]
                               // 3字节 -- 有效数据开始
            return NULL;

        item->size = *start++;
        item->tag  = *start++;

        if ((end - start) < item->size) // 保证该Long item拥有所需的足够数据[luther.gliehttp]
            return NULL;

        item->data.longdata = start; // 从第4个字节开始就是数据区
        start += item->size;    // start指向下一个item开始处[luther.gliethttp]
        return start;
    }
    // Short items: 1 – 5 bytes total length; used for the most commonly occurring
    //              items. A short item typically contains 1 or 0 bytes of optional data.

    item->format = HID_ITEM_FORMAT_SHORT;
    item->size = b & 3; // b算一个字节+0-3字节

    switch (item->size) {

        case 0:
            return start; // 没有数据区,start指向下一个item开始处

        case 1:
            if ((end - start) < 1)
                return NULL;
            item->data.u8 = *start++; // 取出1字节数据
            return start;

        case 2:
            if ((end - start) < 2)
                return NULL;
            // 取出小端模式存储的2字节数据,数据是packed的[luther.gliethttp]
            item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start));
            start = (__u8 *)((__le16 *)start + 1); // start加2
            return start;

        case 3:
            item->size++; // 强制调整到4字节
            if ((end - start) < 4)
                return NULL;
            // 取出小端模式存储的4字节数据,数据是packed的[luther.gliethttp]
            item->data.u32 = le32_to_cpu(get_unaligned((__le32*)start));
            start = (__u8 *)((__le32 *)start + 1); // start加4
            return start;
    }

    return NULL;
}

/*
 * Process a main item.
 */
static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
{
    __u32 data;
    int ret;
    // item->tag  = (b >> 4) & 15;
    data = item_udata(item); // 返回该Short Item数据,数据有4种,0,1,2,4字节[luther.gliehttp]
    switch (item->tag) {
        case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
            ret = open_collection(parser, data & 0xff); // Open a collection. 入栈push操作The type/usage is pushed on the stack.
            break;
        case HID_MAIN_ITEM_TAG_END_COLLECTION:
            ret = close_collection(parser);             // Close a collection.出栈pop操作parser->collection_stack_ptr--;
            break;
        case HID_MAIN_ITEM_TAG_INPUT:                   // 0x81, 0x02, //     INPUT (Data,Var,Abs)
            ret = hid_add_field(parser, HID_INPUT_REPORT, data); // Register a new field for this report.
            break;
        case HID_MAIN_ITEM_TAG_OUTPUT:
            ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
            break;
        case HID_MAIN_ITEM_TAG_FEATURE:
            ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
            break;
        default:
            dbg_hid("unknown main item tag 0x%x\n", item->tag);
            ret = 0;
    }

// 清0所有parser->local内存数据,local包括:
// struct hid_local {
//    unsigned usage[HID_MAX_USAGES]; /* usage array */
//    unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
//    unsigned usage_index;
//    unsigned usage_minimum;
//    unsigned delimiter_depth;
//    unsigned delimiter_branch;
//};
    // 所以parser->local.usage_index在遇到main item时全部清0.[luther.gliethttp]
    memset(&parser->local, 0, sizeof(parser->local));    /* Reset the local parser environment */

    return ret;
}

/*
 * Process a global item.
 */
static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
{
    switch (item->tag) {

        case HID_GLOBAL_ITEM_TAG_PUSH:

            if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
                dbg_hid("global enviroment stack overflow\n");
                return -1;
            }

            memcpy(parser->global_stack + parser->global_stack_ptr++,
                &parser->global, sizeof(struct hid_global));            // 将parser->global压入堆栈parser->global_stack[global_stack_ptr]
            return 0;

        case HID_GLOBAL_ITEM_TAG_POP:

            if (!parser->global_stack_ptr) {
                dbg_hid("global enviroment stack underflow\n");
                return -1;
            }

            memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
                sizeof(struct hid_global));                             // 将parser->global_stack[--global_stack_ptr]出栈[luther.gliethttp]
            return 0;

        case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
        // 比如
        // 0x05, 0x01, --- USAGE_PAGE (Generic Desktop)
        // 0x05, 0x09, --- USAGE_PAGE (Button)
        // 根据fetch_item()
        // item->tag  = (0x05 >> 4) & 15 = 0;
        // #define HID_GLOBAL_ITEM_TAG_USAGE_PAGE        0
            parser->global.usage_page = item_udata(item);              // 全局量global.usage_page
            return 0;

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

        case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
            if (parser->global.logical_minimum < 0)
                parser->global.logical_maximum = item_sdata(item);      // 全局量global.logical_maximum
            else
                parser->global.logical_maximum = item_udata(item);
            return 0;

        case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
            parser->global.physical_minimum = item_sdata(item);         // 全局量global.physical_minimum
            return 0;

        case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
            if (parser->global.physical_minimum < 0)
                parser->global.physical_maximum = item_sdata(item);     // 全局量global.physical_maximum
            else
                parser->global.physical_maximum = item_udata(item);
            return 0;

        case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
            parser->global.unit_exponent = item_sdata(item);            // 全局量global.unit_exponent
            return 0;

        case HID_GLOBAL_ITEM_TAG_UNIT:
            parser->global.unit = item_udata(item);                     // 全局量global.unit
            return 0;

        case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:                           // 全局量global.report_size
        // 比如:0x75, 0x01, //     REPORT_SIZE (1)
        // 表示1个bit
            if ((parser->global.report_size = item_udata(item)) > 32) {
                dbg_hid("invalid report_size %d\n", parser->global.report_size);
                return -1;
            }
            return 0;

        case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:                          // 全局量global.report_count
        // 比如:0x95, 0x03, //     REPORT_COUNT (3)
            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;

        case HID_GLOBAL_ITEM_TAG_REPORT_ID: // 全局量global.report_id
                                            // Report ID items are used to indicate which data fields are represented in each report
                                            // structure. A Report ID item tag assigns a 1-byte identification prefix to each
                                            // report transfer. If no Report ID item tags are present in the Report descriptor, it
                                            // can be assumed that only one Input, Output, and Feature report structure exists
                                            // and together they represent all of the device’s data.
                                            // Set Report ID to 0 (zero) if Report IDs are not used.
                                            // Value    Report Type
                                            // 01       Input
                                            // 02       Output
                                            // 03       Feature
                                            // 04-FF    Reserved
                                            // 现在有上面3种类型,每种类型都有一个report_id_hash[256]数组,表示hid设备最多可以产生256种report上报数据
                                            // report_id是一个8位-1字节的域,所以一个类型下最多有255个report_id来标识
                                            // 他们,也就是一个hid设备最多可以有255种input,255种ouput,255种feature类型数据
                                            // (0保留,表示没有report_id)[luther.gliethttp]
                                            // 如果没有report id在报告描述符中,那么report_id等于0 [luther.gliethttp]
                                            // 在hid_parse_report ==> memset(parser, 0, sizeof(struct hid_parser));
            if ((parser->global.report_id = item_udata(item)) == 0) {
                dbg_hid("report_id 0 is invalid\n");
                return -1;
            }
            return 0;

        default:
            dbg_hid("unknown global tag 0x%x\n", item->tag);
            return -1;
    }
}

/*
 * Process a local item.
 */
static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
{
    __u32 data;
    unsigned n;

    if (item->size == 0) {
        dbg_hid("item data expected for local item\n");
        return -1;
    }

    data = item_udata(item);

    switch (item->tag) {

        case HID_LOCAL_ITEM_TAG_DELIMITER:

            if (data) {
                /*
                 * We treat items before the first delimiter
                 * as global to all usage sets (branch 0).
                 * In the moment we process only these global
                 * items and the first delimiter set.
                 */
                if (parser->local.delimiter_depth != 0) {
                    dbg_hid("nested delimiters\n");
                    return -1;
                }
                parser->local.delimiter_depth++;        // 左分割符,比如{
                parser->local.delimiter_branch++;
            } else {
                if (parser->local.delimiter_depth < 1) {
                    dbg_hid("bogus close delimiter\n");
                    return -1;
                }
                parser->local.delimiter_depth--;        // 右分割符,比如}
            }
            return 1;

        case HID_LOCAL_ITEM_TAG_USAGE:
            // 0x09, 0x02, // USAGE (Mouse)
            // 0x09, 0x01, //   USAGE (Pointer)
            // 0x19, 0x01, //     USAGE_MINIMUM (Button 1)
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }

            if (item->size <= 2)
                data = (parser->global.usage_page << 16) + data;
            // 比如
            // global.usage_page = 0x01;   0x05, 0x01, --- USAGE_PAGE (Generic Desktop)
            // global.usage_page = 0x09;   0x05, 0x09, --- USAGE_PAGE (Button)
            // 所以这里data高16位表示global.usage_page值,data低16位为local本地usage数值,
            // 这样2个组合之后唯一确定出input,output,feature数据所属类型,进而对数据做所属类型对应的处理[luther.gliehttp]
            // 将data添加到parser->local.usage[parser->local.usage_index++] = data;操作堆栈[luther.gliehttp]
            // parser->global.usage_page = 0x01;                        0x05, 0x01, // USAGE_PAGE (Generic Desktop)
            // parser->local.usage[0] = (0x01 << 16) | 0x02;            0x09, 0x02, // USAGE (Mouse)
            // parser->device->collection[0]->usage = local.usage[0];   0xa1, 0x01, // COLLECTION (Application)
            // hid_parser_main==>memset(&parser->local, 0, sizeof(parser->local));
            // 清0所有parser->local数据,所以遇到main item,是parser->local的终结,
            // main item当前一共包括5种,
            // 1.BEGIN_COLLECTION
            // 2.END_COLLECTION
            // 3.TAG_INPUT
            // 4.TAG_OUTPUT
            // 5.TAG_FEATURE
            // 是下一个parser->local数据收集的开始[luther.gliethttp]
            // parser->local.usage[0] = (0x01 << 16) | 0x01;            0x09, 0x01, //   USAGE (Pointer)
            // parser->device->collection[1]->usage = local.usage[0];   0xa1, 0x00, //   COLLECTION (Physical)
            // 经过该COLLECTION之后再次执行memset(&parser->local, 0, sizeof(parser->local));复位parser->local
            // parser->global.usage_page = 0x09;                        0x05, 0x09, //     USAGE_PAGE (Button)
            return hid_add_usage(parser, data);         // 将data压入local.usage[]堆栈

        case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:

            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            // 比如:
            // parser->global.usage_page = 0x09;        0x05, 0x09, USAGE_PAGE (Button)
            // data=0x01                                0x19, 0x01, USAGE_MINIMUM (Button 1)
            if (item->size <= 2)
                data = (parser->global.usage_page << 16) + data; // data = (0x09 << 16) | 0x01 = 0x90001

            parser->local.usage_minimum = data; // 等于parser->local.usage_minimum = 0x90001;
            return 0;

        case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
            // parser->global.usage_page = 0x09;                    0x05, 0x09, USAGE_PAGE (Button)
            // parser->local.usage_minimum = (0x09 << 16) | 0x01;   0x19, 0x01, USAGE_MINIMUM (Button 1)
            // data = (0x09 << 16) | 0x03;                          0x29, 0x03, //     USAGE_MAXIMUM (Button 3)

            if (item->size <= 2)
                data = (parser->global.usage_page << 16) + data;
            // 所以等效于for (n = 0x90001; n < 0x90003; n++);
            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;

        default:

            dbg_hid("unknown local item tag 0x%x\n", item->tag);
            return 0;
    }
    return 0;
}

/*
 * Process a reserved item.
 */
static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item)
{
    dbg_hid("reserved item type, tag 0x%x\n", item->tag);
    return 0;
}


/*
 * Open a collection. The type/usage is pushed on the stack.
 */
static int open_collection(struct hid_parser *parser, unsigned type)
{
    struct hid_collection *collection;
    unsigned usage;
    // 从取出最先进入local的usage
    // 在处理local usage的函数hid_parser_local==>HID_LOCAL_ITEM_TAG_USAGE中
    // data = (parser->global.usage_page << 16) + data;
    // 比如
    // global.usage_page = 0x01;   0x05, 0x01, --- USAGE_PAGE (Generic Desktop)
    // global.usage_page = 0x09;   0x05, 0x09, --- USAGE_PAGE (Button)
    // 所以这里data高16位表示全局usage类型值,data低16位为local本地usage数值,
    // 这样2个组合之后唯一确定出input,output,feature数据所属类型,进而对数据做所属类型对应的处理[luther.gliehttp]
    // 所以这里读取的parser->local.usage[0];
    // 高16位对应global.usage_page,低16位才对应local的usage
    // 比如报告描述符的前6个字节数据如下:
    // parser->global.usage_page = 0x01;                    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
    // parser->local.usage[0] = (0x01 << 16) | 0x02;        0x09, 0x02, // USAGE (Mouse)
    //                                                      0xa1, 0x01, // COLLECTION (Application)
    // 那么0xa1将进入open_collection处理函数,
    // parser->local.usage[0] = (0x01 << 16) | 0x02; = 0x10002
    // usage = 0x10002;所以可以看到,之后做得所有报告描述符解析都是对USAGE (Mouse)进行的进一步细致分类,
    // 这也就决定了,这是一段mouse的报告描述符.
    usage = parser->local.usage[0];

    if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
        // 当前定义栈大小为4
        dbg_hid("collection stack overflow\n");
        return -1;
    }

    if (parser->device->maxcollection == parser->device->collection_size) {
    // 开始默认device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; // 默认16项
    // 现在已经满了,那么2倍大小,
    // 16,32,64,128,256,....这样动态生长下去[luther.gliethttp]
        collection = kmalloc(sizeof(struct hid_collection) *
                parser->device->collection_size * 2, GFP_KERNEL);
        if (collection == NULL) {
            dbg_hid("failed to reallocate collection array\n");
            return -1;
        }
        memcpy(collection, parser->device->collection,              // 复制原有数据
            sizeof(struct hid_collection) *
            parser->device->collection_size);
        memset(collection + parser->device->collection_size, 0,     // 将未使用空间添0
            sizeof(struct hid_collection) *
            parser->device->collection_size);
        kfree(parser->device->collection);                          // 释放原有数据空间
        parser->device->collection = collection;                    // 将2倍大小空间作为collection空间
        parser->device->collection_size *= 2;                       // 调整其大小为2倍[luther.glithttp]
    }

    parser->collection_stack[parser->collection_stack_ptr++] =      // 压栈,将当前collection的存储数组索引maxcollection
        parser->device->maxcollection;                              // 压入collection_stack_ptr堆栈[luther.gliethttp]

    collection = parser->device->collection +
        parser->device->maxcollection++;                            // 该collection存储地址为&parser->device->collection[maxcollection++]
    collection->type = type;                                        // 将它存进去
    collection->usage = usage;
    collection->level = parser->collection_stack_ptr - 1;           // 所处堆栈层值[luther.gliethttp]

    if (type == HID_COLLECTION_APPLICATION)                         // type = (b >> 2) & 3; 所以0x04有效的都执行此++
        parser->device->maxapplication++;

    return 0;
}
/*
 * Close a collection.
 */
static int close_collection(struct hid_parser *parser)
{
    if (!parser->collection_stack_ptr) {
        dbg_hid("collection stack underflow\n");
        return -1;
    }
    parser->collection_stack_ptr--;
    return 0;
}

/*
 * Register a new field for this report.
 */
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;
    // 返回report_type类型下report_id数值(范围0-255)对应的数组空间
    // 一个report_id对应hid物理设备上传的一个特定report数据块,驱动需要根据这里返回
    // 的随后填充具体限制信息的report来具体解析它,
    // 即device->report_enum[report_type]->report_id_hash[parser->global.report_id]存储空间
    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;
    // 比如:0x75, 0x01, //     REPORT_SIZE (1)
    // 表示1个bit
    // parser->global.report_size = 1;
    // 在HID_GLOBAL_ITEM_TAG_REPORT_SIZE中设置[luther.gliethttp]
    report->size += parser->global.report_size * parser->global.report_count; // 由0x95, 0x01, //     REPORT_COUNT (1)定义[luther.gliethttp]
    // 这样表示3个连续的bit位,组成了该filed数据区的有效数据.
    if (!parser->local.usage_index) /* Ignore padding fields */
        return 0;

    usages = max_t(int, parser->local.usage_index, parser->global.report_count);
    // 这个parser->global.report_count在,
    // HID_GLOBAL_ITEM_TAG_REPORT_COUNT中设置
    // 比如:0x95, 0x03, //     REPORT_COUNT (3)
    // 那么parser->global.report_count = 3; [luther.gliethttp]
    if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
    // 创建一个data filed数据区,存放数据,比如:0x81, 0x02, // INPUT (Data,Var,Abs)
        return 0;

    field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); // 弹出最后入栈的HID_COLLECTION_PHYSICAL报告描述符脚本usage数值
    field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); // 弹出最后入栈的HID_COLLECTION_LOGICAL报告描述符脚本usage数值
    field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); // 弹出最后入栈的HID_COLLECTION_APPLICATION报告描述符脚本usage数值

    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]; // 将local中的所有usage和collection_index都应用到该filed数据域中.
        field->usage[i].collection_index =            // 也就是该field数据域,将含有所有local和之前global信息,被染成他们的色[luther.gliethttp]
            parser->local.collection_index[j];
    }

    field->maxusage = usages;
    field->flags = flags;       // 该filed的数据内容,比如:0x81, 0x02, // INPUT (Data,Var,Abs),那么flags将等于0x02.[luther.gliethttp]
    field->report_offset = offset;  // 默认为0
    field->report_type = report_type;   // input,output或者feature.
    field->report_size = parser->global.report_size; // 每个数据所占bit数,比如0x75, 0x01, // REPORT_SIZE (1)就表示没有数据占1个bit.
    field->report_count = parser->global.report_count; // 一共有report_count个数据[luther.gliethttp],比如:0x95, 0x03, // REPORT_COUNT (3)
    field->logical_minimum = parser->global.logical_minimum;
    // HID设备上报的每个数据(比如上面的1个bit就对应1个数据,一共连续3个bit,3个数据)逻辑最小值[luther.gliethttp]
    field->logical_maximum = parser->global.logical_maximum;
    field->physical_minimum = parser->global.physical_minimum;  // 物理最大,物理最小,他们都是全局属性[luther.gliethttp]
    field->physical_maximum = parser->global.physical_maximum;  // 并不是所有HID设备都需要这些定义.
    field->unit_exponent = parser->global.unit_exponent;
    field->unit = parser->global.unit;
    // 至此,一个hid设备上报的符合上述诸多属性限制的filed数据域生成完毕,所以这里看,
    // 在报告描述符中,应该先生成上述诸多限制属性描述,最后使用比如:
    // 0x81, 0x02, //     INPUT (Data,Var,Abs)
    // 来生成hid设备正常工作之后上传的数据属性格式对应的filed数据域属性限制规范[luther.gliethttp]
    return 0;
}

/*
 * Register a new report for a device.
 */
static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
{
    // #define HID_INPUT_REPORT        0
    // #define HID_OUTPUT_REPORT    1
    // #define HID_FEATURE_REPORT    2
    // 目前type只有上面的3种类型
    // 所有的INPUT报告信息都将添加到device->report_enum[0]->report_id_hash[0-255]数组
    // 所有的OUTPUT报告信息都将添加到device->report_enum[1]->report_id_hash[0-255]数组
    // 所有的FEATURE报告信息都将添加到device->report_enum[2]->report_id_hash[0-255]数组[luther.gliethttp]
    struct hid_report_enum *report_enum = device->report_enum + type;
    struct hid_report *report;
    // 这里的id就是report_id(默认值0)
    // 该device->report_enum[type]最多允许255种不同的report_id,
    // 也就是该hid设备最多有255种input类型数据,255种output类型数据和255种feature类型数据[luther.gliethtt]
    // 对report id的详细解释见hid_parser_global ==> HID_GLOBAL_ITEM_TAG_REPORT_ID
    if (report_enum->report_id_hash[id])                            // 该类型的id对应的report已经创建了,直接返回.
        return report_enum->report_id_hash[id];

    if (!(report = kzalloc(sizeof(struct hid_report), GFP_KERNEL))) // 为该report-id对应的类型申请report空间
        return NULL;                                                //
                                                                    // 登记到report_id_hash[id]中[luther.gliethttp]
    if (id != 0)
        report_enum->numbered = 1;                      // id非0表示,真的存在report_id,0表示没有report id

    report->id = id;
    report->type = type;
    report->size = 0;
    report->device = device;
    report_enum->report_id_hash[id] = report;           // 根据id可取出该report,比如鼠标上传了一个INPUT数据,那么driver将首先
                                                        // 取出1字节的report id,比如等于3,然后直接
                                                        // device->report_enum[0]->report_id_hash[3]映射出report报告描述中对该id对应的
                                                        // input数据流解析方法report,进行特定数据解析[luther.gliethttp]
    list_add_tail(&report->list, &report_enum->report_list); // report挂到所属类型input,output或feature对应的report_enum->report_list链表上
                      // 这样直接遍历device->report_enum[0]->report_list就可以获得
    return report;    // 位于device->report_enum[0]->report_id_hash[]中所有登记的id对应的report[luther.gliethttp]
}

/*
 * Climb up the stack, search for the specified collection type
 * and return the usage.
 */
static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
{
    int n;
    for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
        if (parser->device->collection[parser->collection_stack[n]].type == type)
            return parser->device->collection[parser->collection_stack[n]].usage;
    return 0; /* we know nothing about this usage type */
}

/*
 * Add a usage to the temporary parser table.
 */
static int hid_add_usage(struct hid_parser *parser, unsigned usage)
{
    if (parser->local.usage_index >= HID_MAX_USAGES) {
        dbg_hid("usage index exceeded\n");
        return -1;
    }
    parser->local.usage[parser->local.usage_index] = usage;
    parser->local.collection_index[parser->local.usage_index] =
        parser->collection_stack_ptr ? // 找到对应的操作堆栈[luther.gliehttp]
        parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
    parser->local.usage_index++; // 局部usage堆栈索引指针加1
    return 0;
}

========================================
附上一个参考鼠标报告描述符
unsigned char mouse_report[] = {
    0x05, 0x01, // USAGE_PAGE (Generic Desktop) // parser->global.usage_page = 0x01;进入Generic Desktop
    0x09, 0x02, // USAGE (Mouse)
    0xa1, 0x01, // COLLECTION (Application)     // 对基于Generic Desktop的Mouse信息进行收集
    0x09, 0x01, //   USAGE (Pointer)
    0xa1, 0x00, //   COLLECTION (Physical)      // 对基于Generic Desktop的Porinter信息进行收集[luther.gliethttp]
    0x05, 0x09, //     USAGE_PAGE (Button)      // parser->global.usage_page = 0x09;进入Button
    0x19, 0x01, //     USAGE_MINIMUM (Button 1)
    0x29, 0x03, //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00, //     LOGICAL_MINIMUM (0)
    0x25, 0x01, //     LOGICAL_MAXIMUM (1)
    0x95, 0x03, //     REPORT_COUNT (3)
    0x75, 0x01, //     REPORT_SIZE (1)
    0x81, 0x02, //     INPUT (Data,Var,Abs)     // 生成具有上面4个local属性和2个global属性的Button的Input报告report数据格式,同时复位local[luther.gliethttp]
    0x95, 0x01, //     REPORT_COUNT (1)
    0x75, 0x05, //     REPORT_SIZE (5)
    0x81, 0x03, //     INPUT (Cnst,Var,Abs)     // 生成只有上面2个local.usage[0-1]属性的Button的Input报告report数据格式[luther.gliethttp]
    0x05, 0x01, //     USAGE_PAGE (Generic Desktop)// parser->global.usage_page = 0x01;进入Generic Desktop
    0x09, 0x30, //     USAGE (X)
    0x09, 0x31, //     USAGE (Y)
    0x09, 0x38, //     USAGE (Wheel)
    0x15, 0x81, //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f, //     LOGICAL_MAXIMUM (127)
    0x75, 0x08, //     REPORT_SIZE (8)
    0x95, 0x03, //     REPORT_COUNT (3)
    0x81, 0x06, //     INPUT (Data,Var,Rel)     // 生成hid设备report上报数据格式[luther.gliethttp]
    0xc0,       //   END_COLLECTION
    0xc0        // END_COLLECTION
};

转载自: http://blog.chinaunix.net/space.php?uid=23159239

http://blog.chinaunix.net/space.php?uid=23159239

http://blog.chinaunix.net/space.php?uid=23159239

http://blog.chinaunix.net/space.php?uid=23159239

http://blog.chinaunix.net/space.php?uid=23159239

你可能感兴趣的:(linux)