您的位置:首页 > 运维架构 > Linux

LINUX下USB1.1设备学习小记(6)_hid与input子系统(2)

2012-07-11 15:38 525 查看
[align=center] [/align]
走完之前的程序后的数据结构如下



[align=left] [/align]
注意,之后会把local中的数据清空

好,到下一组为0x09, 0x01

这是一个局域项目,重新向局域结构中添加项目

下一个组为0xa1, 0x00

这是一个主项目,用于物理集合收集的开始

添加完成后的数据结构如下



[align=left] [/align]
又把局域结构中的数据清零

到下一组,为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;

}

注册完后的数据结构如下



[align=center] [/align]
[align=center] [/align]
现在又要把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;

添加完成后的数据结构图如下



[align=center] [/align]
咋看之下和之前的没什么不同,但是请注意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;

添加好后的数据结构如下图



[align=center] [/align]
下一组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就注册好了,如下图



[align=center] [/align]
光溜溜的.......

说一下这个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模块就挂载完成了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: