- 论坛徽章:
- 0
|
走完之前的程序后的数据结构如下
![]()
注意,之后会把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.hid = parser->local.usage[j];
//拷贝所处的收集
field->usage.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.type == HID_COLLECTION_APPLICATION ||
hid->collection.type == HID_COLLECTION_PHYSICAL)
if (IS_INPUT_APPLICATION(hid->collection.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->maxusage; j++)
hidinput_configure_usage(hidinput, report->field,
report->field->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模块就挂载完成了
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/57901/showart_1870953.html |
|