浅析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) <