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