注意,之后会把local中的数据清空
好,到下一组为0x09, 0x01
这是一个局域项目,重新向局域结构中添加项目
下一个组为0xa1, 0x00
这是一个主项目,用于物理集合收集的开始
添加完成后的数据结构如下
又把局域结构中的数据清零
到下一组,为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; }
|
注册完后的数据结构如下
现在又要把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;
|
添加完成后的数据结构图如下
咋看之下和之前的没什么不同,但是请注意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;
|
添加好后的数据结构如下图
下一组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,配置完成的数据结构图如下
连向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
这样一个项就分析完成了
全部分析完后的数据结构如下图
分析完协议之后就开始匹配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就注册好了,如下图
光溜溜的.......
说一下这个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模块就挂载完成了