LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
来源:互联网 发布:手机查看路由器mac地址 编辑:程序博客网 时间:2024/06/05 20:00
好,到下一组为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;
这是一个局域项目,用途为设定添加项目的最大值
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;
这是全局项目,用于设置逻辑最小值
case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
parser->global.logical_minimum= item_sdata(item);
return 0;
这是一个全局项目,用于设置逻辑最大值
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;
这是一个全局项目,用于设置项目的个数
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;
这是一个全局项目,用于设置单个项目所需要的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;
这是一个主项目,用于将设置好的项目信息添加到域中
case HID_MAIN_ITEM_TAG_INPUT:
ret = hid_add_field(parser, HID_INPUT_REPORT, data);
break;
hid_add_field在/drivers/hid/hid-core.c中
staticint 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中
staticstruct 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_register_field在/drivers/hid/hid-core.c中
staticstruct 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;
}
注册完后的数据结构如下
好~ 到下一组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;
这是一个全局项目,用于修改项的大小为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;
这是一个主项目, 用于将设置好的项目信息添加到域中
case HID_MAIN_ITEM_TAG_INPUT:
ret = hid_add_field(parser, HID_INPUT_REPORT, data);
break;
添加完成后的数据结构图如下
也就是说我们所需要的占位效果出来了,之后的域数会从第9位开始读取
local继续被清0~
好` 下一组0x05, 0x01
这是一个全局项目,用于设置全局用途
case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
parser->global.usage_page= item_udata(item);
return 0;
这是一个局域项目,用于设置用途
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);
这是一个局域项目,用于设置用途
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);
这是一个局域项目,用于设置用途
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);
这是一个全局项目,用于设置逻辑最小值
case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
parser->global.logical_minimum= item_sdata(item);
return 0;
这是一个全局项目,用于设置逻辑最大值
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;
这是一个全局项目,用于设置单个项的所需要的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;
这是一个全局项目,用于设置项的数目
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;
这是一个主项目, 用于将设置好的项目信息添加到域中
case HID_MAIN_ITEM_TAG_INPUT:
ret = hid_add_field(parser, HID_INPUT_REPORT, data);
break;
添加好后的数据结构如下图
这是一个主项目,用于结束物理收集
case HID_MAIN_ITEM_TAG_END_COLLECTION:
ret = close_collection(parser);
break;
这是一个主项目,用于结束物理收集
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负责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;
}
分析工作由hidinput_configure_usage完成
hidinput_configure_usage在drivers/hid/hid-input.c中
staticvoid 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中
staticint 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;
}
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;
}
mousedev_create在/drivers/input/mousedev.c中
staticstruct 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);
}
然后到mousedev_install_chrdev
mousedev_install_chrdev在/drivers/input/mousedev.c中
staticint mousedev_install_chrdev(struct mousedev*mousedev)
{
mousedev_table[mousedev->minor]= mousedev;
return 0;
}
这样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;
}
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(1)
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(1)
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(3)
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(3)
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(3)
- LINUX下USB1.1设备学习小记
- LINUX下USB1.1设备学习小记
- LINUX下USB1.1设备学习小记(4)_uhci(2)
- LINUX下USB1.1设备学习小记(2)_协议
- LINUX下USB1.1设备学习小记(2)_协议
- LINUX下USB1.1设备学习小记(2)_协议
- LINUX下USB1.1设备学习小记(4)_uhci(2)
- LINUX下USB1.1设备学习小记(2)_协议
- LINUX下USB1.1设备学习小记(5)_uhci与设备(2)
- LINUX下USB1.1设备学习小记(5)_uhci与设备(1)
- LINUX下USB1.1设备学习小记(1)
- Learning MVC 3.0 Step by Step
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(1)
- chrome developer tool 调试技巧
- 将一个小图片平铺到大范围内
- HTTP Header 详解
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)
- Dos常用命令
- JAVA NIO 实例
- C#对XML操作
- LINUX下USB1.1设备学习小记(6)_hid与input子系统(3)
- 【学习点滴-数据结构-字符串】 字符串的堆分配方式实现和基本函数
- USCAO section 1.3 Calf Flac
- B2G系统简介(部分资料来源于网上)
- java基本语法汇总