Linux设备驱动之HID驱动---非常全面而且深刻

    struct hid_report *report;
    struct hid_field *field;
    int usages;
    unsigned offset;
    int i;
 
    //找到类型和对应report_id所在的report.如果不存在,则新建之
    if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
        dbg_hid("hid_register_report failed\n");
        return -1;
    }
 
    //对当前global数据的有效性判断
    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;
    }
   
    //当前项在整个report中的数据偏移位置
    offset = report->size;
    //更新report->size
    report->size += parser->global.report_size * parser->global.report_count;
 
    //在local中没有定义usage项.该项是一个padding项
    if (!parser->local.usage_index) /* Ignore padding fields */
        return 0;
    //计算parser->local.usage_index与parser->global.report_count的最大值
    //1: parser->global.report_count >parser->local.usage_index :则后续的report项共用最后一个usage
    //2: parser->global.report_count <parser->local.usage_index:在report项为Arrary类型的时候最为常
    //见.
    //3:相等的情况.每一个report项对应一个usage
    usages = max_t(int, parser->local.usage_index, parser->global.report_count);
    //注册这个report项
    if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
        return 0;
    //初始化field的相关成员
    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);
    //保存usage值
    for (i = 0; i < usages; i++) {
        int j = i;
        /* Duplicate the last usage we parsed if we have excess values */
        if (i >= parser->local.usage_index)
            j = parser->local.usage_index - 1;
        field->usage[i].hid = parser->local.usage[j];
        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_parser_main()处理的最后,有这样的一段代码:
memset(&parser->local, 0, sizeof(parser->local));   /* Reset the local parser environment */
即把local项清0.因为一个local项目只对它下面的第一个Main有效.
 
到这里,hid_parse_report()就分析完了.由于这个过程涉及到的数据结构有一点,用图的方式列出如下:
 

 

 
3.1.2:hid_find_max_report()函数分析
第二个要分析的函数是hid_find_max_report().代码如下:
static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max)
{
    struct hid_report *report;
    int size;
 
    list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
        size = ((report->size - 1) >> 3) + 1;
        if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
            size++;
        if (*max < size)
            *max = size;
    }
}
经过前面的分析,我们可以得到,对于同种类型,不同report_id的report都会链接在对应类型的hid_device->report_enum[ ] ->report_list.
该函数就是遍历这个链表,取得最大的report size.
在这里之所以将这个函数单独列出.是因为在这里需要注意以下两点:
1: report->size这里存放的大小并不是以字节计数,而是位计算的
2:在INPUT类型,并有多个report_id的情,size会加1的原因:
  在有多个report_id的情况下,input的数据最前面有一个字节会表示它的report_id
 
3.2: usbhid_init_reports()函数分析
返回到hid_probe()中,继续来分析probe过程.分析完usb_hid_configure()之后,紧接着就是usbhid_init_reports().代码如下:
void usbhid_init_reports(struct hid_device *hid)
{
    struct hid_report *report;
    struct usbhid_device *usbhid = hid->driver_data;
    int err, ret;
 
    //提交INPUT类型的,in方向的urb
    list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
        usbhid_submit_report(hid, report, USB_DIR_IN);
    //提交Feature类型的,in方向的urb
    list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
        usbhid_submit_report(hid, report, USB_DIR_IN);
 
    err = 0;
    //等待提交的信息传输完成.如果在定义时间内传输完成,返回0.否则-1
    ret = usbhid_wait_io(hid);
 
    //如果传输超时.清除传输的相关urb
    while (ret) {
        err |= ret;
        if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
            usb_kill_urb(usbhid->urbctrl);
        if (test_bit(HID_OUT_RUNNING, &usbhid->iofl))
            usb_kill_urb(usbhid->urbout);
        ret = usbhid_wait_io(hid);
    }
 
    if (err)
        warn("timeout initializing reports");
}
在这里会遇到两个标志,分别是HID_CTRL_RUNNING, HID_OUT_RUNNING,表示正在提交usbhid->urbctrl和usbhid->urbout.
跟进去看一下usbhid_submit_report()的代码.如下:
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
{
    int head;
    unsigned long flags;
    struct usbhid_device *usbhid = hid->driver_data;
 
    if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
        return;
 
    if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
 
        spin_lock_irqsave(&usbhid->outlock, flags);
 
        if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
            spin_unlock_irqrestore(&usbhid->outlock, flags);
            warn("output queue full");
            return;
        }
 
        usbhid->out[usbhid->outhead] = report;
        usbhid->outhead = head;
 
        if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
            if (hid_submit_out(hid))
                clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
 
        spin_unlock_irqrestore(&usbhid->outlock, flags);
        return;
    }
 
    spin_lock_irqsave(&usbhid->ctrllock, flags);
 
    //Control Queue Full   
    if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
        spin_unlock_irqrestore(&usbhid->ctrllock, flags);
        warn("control queue full");
        return;
    }
 
    usbhid->ctrl[usbhid->ctrlhead].report = report;
    usbhid->ctrl[usbhid->ctrlhead].dir = dir;
    usbhid->ctrlhead = head;
 
    if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
        if (hid_submit_ctrl(hid))
            clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
 
    spin_unlock_irqrestore(&usbhid->ctrllock, flags);
}
这个函数有三个参数,第一个为hid,表示操作的hid_deivce.第二个参数为report,表示要操作的report,dir表示提交URB的方向.有USB_DIR_IN / USB_DIR_OUT可选.
虽然我们在上面看到是以USB_DIR_IN调用此函数.不过在分析代码的时候,顺带把USB_DIR_OUT的情况也给分析一下.
这个函数其实很简单,如果要提交的是OUT方向的,就将相关信息存入usbhid->out[ ]这个环形缓存区.然后调用hid_submit_out()提交hid->urbout. 如果要提交的是IN方向的,就将相关信息存放usbhid->in[ ]这个环形缓冲,然后调用hid_submit_ctrl()提交hid->urbctrl.
 
分别来看一下hid_submit_out()和hid_submit_ctrl().
static int hid_submit_out(struct hid_device *hid)
{
    struct hid_report *report;
    struct usbhid_device *usbhid = hid->driver_data;
 
    report = usbhid->out[usbhid->outtail];
 
    hid_output_report(report, usbhid->outbuf);
    usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
    usbhid->urbout->dev = hid_to_usb_dev(hid);
 
    dbg_hid("submitting out urb\n");
 
    if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
        err_hid("usb_submit_urb(out) failed");
        return -1;
    }
 
    return 0;
}
首先从hid_device->out[ ]环形缓冲区中取得要操作的信息,然后调用hid_output_report( )将该report项的所有值存放到usbhid->outbuf中,然后将hid->urbout提交.
不要忘记了,在初始化hid->urbout的时候,它的传输缓存区是usbhid->outbuf.另外在这里重新定义了urbout传输缓存区的大小.(在初始化的时候,它的传输长度被置为了1)
 
static int hid_submit_ctrl(struct hid_device *hid)
{
    struct hid_report *report;
    unsigned char dir;
    int len;
    struct usbhid_device *usbhid = hid->driver_data;
 
    report = usbhid->ctrl[usbhid->ctrltail].report;
    dir = usbhid->ctrl[usbhid->ctrltail].dir;
 
    len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
    if (dir == USB_DIR_OUT) {
        hid_output_report(report, usbhid->ctrlbuf);
        usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
        usbhid->urbctrl->transfer_buffer_length = len;
    } else {
        int maxpacket, padlen;
 
        usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
        maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
        if (maxpacket > 0) {
            padlen = DIV_ROUND_UP(len, maxpacket);
            padlen *= maxpacket;
            if (padlen > usbhid->bufsize)
                padlen = usbhid->bufsize;
        } else
            padlen = 0;
        usbhid->urbctrl->transfer_buffer_length = padlen;
    }
    usbhid->urbctrl->dev = hid_to_usb_dev(hid);
 
    usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
    usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
    usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
    usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
    usbhid->cr->wLength = cpu_to_le16(len);
 
    dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
        usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
        usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
 
    if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
        err_hid("usb_submit_urb(ctrl) failed");
        return -1;
    }
 
    return 0;
}
不要被这里的USB_DIR_OUT和上面的hid_submit_out()情况的USB_DIR_OUT相混淆.在这里是指Feature类型的,而在上面,是指OUTPUT类型.
在这里,是以Get_Report/Set_Report的方式接收或者向设备发送信息.
对于OUT方向的,传输的缓存区长度即为report的大小,而对于IN方向,.每次传一个endport最大支持长度.因此,对于IN方向.可能有些填充位.
之后.将hid->urbctrl提交.
 
提交了hid->urbout和hid->urbctrl之后会做什么呢?我们来看下它们的传输完成处理函数.
 
3.2.1: hid_submit_out()/hid_submit_ctrl()的后续处理
注意下面的几个代码片段:
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
{
    ……
usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
                     hid_irq_out, hid, interval);
    ……
    usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
                 usbhid->ctrlbuf, 1, hid_ctrl, hid);
    ……
}
也就是说,如果hid->urbout和hid->urbctrl传输完成之后,分别会调用hid_irq_out()和usbhid->ctr()
下面对这两个操作进行分析.
Hid_irq_out()代码如下:
static void hid_irq_out(struct urb *urb)
{
    struct hid_device *hid = urb->context;
    struct usbhid_device *usbhid = hid->driver_data;
    unsigned long flags;
    int unplug = 0;
 
    switch (urb->status) {
        case 0:         /* success */
            break;
        case -ESHUTDOWN:    /* unplug */
            unplug = 1;
        case -EILSEQ:       /* protocol error or unplug */
        case -EPROTO:       /* protocol error or unplug */
        case -ECONNRESET:   /* unlink */
        case -ENOENT:
            break;
        default:        /* error */
            warn("output irq status %d received", urb->status);
    }
 
    spin_lock_irqsave(&usbhid->outlock, flags);
 
    if (unplug)
        usbhid->outtail = usbhid->outhead;
    else
        usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
 
    if (usbhid->outhead != usbhid->outtail) {
        if (hid_submit_out(hid)) {
            clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
            wake_up(&hid->wait);
        }
        spin_unlock_irqrestore(&usbhid->outlock, flags);
        return;
    }
 
    clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
    spin_unlock_irqrestore(&usbhid->outlock, flags);
    wake_up(&hid->wait);
}
从该代码看出,在hid->urbout传输完全之后,会取usbhid->out[ ]环形缓冲区中的数据取出.调用hid_submit_out( )再次将对应report的相关信息通过hid->urbout提交.如果缓存区中report全部处理完全或者是传输出现了错误,清除掉HID_OUT_RUNNING标志.
 
hid_ctrl()代码如下:
static void hid_ctrl(struct urb *urb)
{
    struct hid_device *hid = urb->context;
    struct usbhid_device *usbhid = hid->driver_data;
    unsigned long flags;
    int unplug = 0;
 
    spin_lock_irqsave(&usbhid->ctrllock, flags);
 
    switch (urb->status) {
        case 0:         /* success */
            if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
                hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type,
                        urb->transfer_buffer, urb->actual_length, 0);
            break;
        case -ESHUTDOWN:    /* unplug */
            unplug = 1;
        case -EILSEQ:       /* protocol error or unplug */
        case -EPROTO:       /* protocol error or unplug */
        case -ECONNRESET:   /* unlink */
        case -ENOENT:
        case -EPIPE:        /* report not available */
            break;
        default:        /* error */
            warn("ctrl urb status %d received", urb->status);
    }
 
    if (unplug)
        usbhid->ctrltail = usbhid->ctrlhead;
    else
        usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
    if (usbhid->ctrlhead != usbhid->ctrltail) {
        if (hid_submit_ctrl(hid)) {
            clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
            wake_up(&hid->wait);
        }
        spin_unlock_irqrestore(&usbhid->ctrllock, flags);
        return;
    }
 
    clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
    spin_unlock_irqrestore(&usbhid->ctrllock, flags);
    wake_up(&hid->wait);
}
该函数的处理流程跟上面分析的hid_irq_out()差不多,不同的是,如果是IN方向的数据,则必须要调用hid_input_report()进行处理了.
 
3.2.2: hid_input_report()函数分析
hid_input_report()函数是一个很重要的函数.代码如下:
int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
{
    struct hid_report_enum *report_enum = hid->report_enum + type;
    struct hid_report *report;
    int n, rsize, i;
 
    if (!hid)
        return -ENODEV;
 
    if (!size) {
        dbg_hid("empty report\n");
        return -1;
    }
 
    dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
 
    n = 0;                          /* Normally report number is 0 */
    if (report_enum->numbered) {    /* Device uses numbered reports, data[0] is report number */
        n = *data++;
        size--;
    }
 
    /* dump the report */
    dbg_hid("report %d (size %u) = ", n, size);
    for (i = 0; i < size; i++)
        dbg_hid_line(" %02x", data[i]);
    dbg_hid_line("\n");
 
    if (!(report = report_enum->report_id_hash[n])) {
        dbg_hid("undefined report_id %d received\n", n);
        return -1;
    }
 
    rsize = ((report->size - 1) >> 3) + 1;
 
    if (size < rsize) {
        dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize);
        memset(data + size, 0, rsize - size);
    }
 
    if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
        hid->hiddev_report_event(hid, report);
    if (hid->claimed & HID_CLAIMED_HIDRAW)
        hidraw_report_event(hid, data, size);
 
    for (n = 0; n < report->maxfield; n++)
        hid_input_field(hid, report->field[n], data, interrupt);
 
    if (hid->claimed & HID_CLAIMED_INPUT)
        hidinput_report_event(hid, report);
 
    return 0;
}
首先判断report_enum->numbered是否为1,如果为1,则说明该report类型有多个report_id.那INPUT传回来的数据的第一个字节是report_id值.
根据report的类型和report_id就可以在hid_device中找到相应的report了.
如果传回来的数据比report size要小,就把后面的无效数据全部置为0.
然后,对于HID_CLAIMED_HIDDEV和HID_CLAIMED_HIDRAW是选择编译部份,忽略这一部份.
如果一个设备是INPUT设备,我们会在后面看到,会在hid->claimed设置HID_CLAIMED_INPUT标志.
对于hidinput_report_event()函数十分简单,就是将hid关联的input_deivce全部发送EV_SYN.表示上报的信息已经结束了.
 
最后,我们要分析的重点就是下面的这段代码:
    for (n = 0; n < report->maxfield; n++)
        hid_input_field(hid, report->field[n], data, interrupt);
在这里会涉及到hid_deivce和input_deivce的关联,所以我们先留个尾巴.等分析完后面的流程再来分析.
 
3.3:hidinput_connect()函数分析
返回hid_probe().继续下面的流程,调用usbhid_init_reports()之后,接着的一个重要的操作就是hidinput_connect().这是我们对porbe过程最后要分析的函数了.
代码如下:
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;
    //初始化hid->inputs链表
    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;
 
            //如果不存在hidinput,分配并初始化它,并将其链入hid-<inputs链表
            if (!hidinput) {
                hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
                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);
            }
 
            //遍历report的filed项
            for (i = 0; i < report->maxfield; i++)
                //遍历filed中的usage
                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_device
    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;
}
很容易看出,这个函数的重点是在中间的那个for循环上,
首先.如果hidinput为空.分配空间并初始化它,同时,分配并初始化hidinput->input域.然后将该hidinput链接到hid_deivce->inputs链表.
另外,从代码中看出.如果hid->quirks中没有定义HID_QUIRK_MULTI_INPUT.那hidinput只会初始化一次,对应的,hid_deivce->inputs链表上只有一个hidinput.
 
跟踪hidinput_configure_usage().代码如下:
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
    field->hidinput = hidinput;
 
    //Debug,忽略
    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 */
    //如果是否个输出设备但却不是LED,忽略
    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 */
    //关于quirks的东东,忽略
    ret = hidinput_mapping_quirks(usage, input, &bit, &max);
    if (ret)
        goto mapped;
 
    //取usage的高16位,即usage_page
    switch (usage->hid & HID_USAGE_PAGE) {
 
        case HID_UP_UNDEFINED:
            goto ignore;
        //键盘类型的设备
        case HID_UP_KEYBOARD:
 
            //使input device支持重复按键
            set_bit(EV_REP, input->evbit);
 
            if ((usage->hid & HID_USAGE) < 256) {
                if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
                map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
            } else
                map_key(KEY_UNKNOWN);
 
            break;
 
        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_SIMULATION:
 
            switch (usage->hid & 0xffff) {
                case 0xba: map_abs(ABS_RUDDER);   break;
                case 0xbb: map_abs(ABS_THROTTLE); break;
                case 0xc4: map_abs(ABS_GAS);      break;
                case 0xc5: map_abs(ABS_BRAKE);    break;
                case 0xc8: map_abs(ABS_WHEEL);    break;
                default:   goto ignore;
            }
            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;
 
        case HID_UP_LED:
 
            switch (usage->hid & 0xffff) {                        /* HID-Value:                   */
                case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
                case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
                case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
                case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
                case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
                case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
                case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
                case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
                case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
                case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
                case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
 
                default: goto ignore;
            }
            break;
 
        case HID_UP_DIGITIZER:
 
            switch (usage->hid & 0xff) {
 
                case 0x30: /* TipPressure */
                    if (!test_bit(BTN_TOUCH, input->keybit)) {
                        device->quirks |= HID_QUIRK_NOTOUCH;
                        set_bit(EV_KEY, input->evbit);
                        set_bit(BTN_TOUCH, input->keybit);
                    }
 
                    map_abs_clear(ABS_PRESSURE);
                    break;
 
                case 0x32: /* InRange */
                    switch (field->physical & 0xff) {
                        case 0x21: map_key(BTN_TOOL_MOUSE); break;
                        case 0x22: map_key(BTN_TOOL_FINGER); break;
                        default: map_key(BTN_TOOL_PEN); break;
                    }
                    break;
 
                case 0x3c: /* Invert */
                    map_key_clear(BTN_TOOL_RUBBER);
                    break;
 
                case 0x33: /* Touch */
                case 0x42: /* TipSwitch */
                case 0x43: /* TipSwitch2 */
                    device->quirks &= ~HID_QUIRK_NOTOUCH;
                    map_key_clear(BTN_TOUCH);
                    break;
 
                case 0x44: /* BarrelSwitch */
                    map_key_clear(BTN_STYLUS);
                    break;
 
                default:  goto unknown;
            }
            break;
 
        case HID_UP_CONSUMER:   /* USB HUT v1.1, pages 56-62 */
 
            switch (usage->hid & HID_USAGE) {
                case 0x000: goto ignore;
                case 0x034: map_key_clear(KEY_SLEEP);       break;
                case 0x036: map_key_clear(BTN_MISC);        break;
 
                case 0x040: map_key_clear(KEY_MENU);        break;
                case 0x045: map_key_clear(KEY_RADIO);       break;
 
                case 0x083: map_key_clear(KEY_LAST);        break;
                case 0x088: map_key_clear(KEY_PC);      break;
                case 0x089: map_key_clear(KEY_TV);      break;
                case 0x08a: map_key_clear(KEY_WWW);     break;
                case 0x08b: map_key_clear(KEY_DVD);     break;
                case 0x08c: map_key_clear(KEY_PHONE);       break;
                case 0x08d: map_key_clear(KEY_PROGRAM);     break;
                case 0x08e: map_key_clear(KEY_VIDEOPHONE);  break;
                case 0x08f: map_key_clear(KEY_GAMES);       break;
                case 0x090: map_key_clear(KEY_MEMO);        break;
                case 0x091: map_key_clear(KEY_CD);      break;
                case 0x092: map_key_clear(KEY_VCR);     break;
                case 0x093: map_key_clear(KEY_TUNER);       break;
                case 0x094: map_key_clear(KEY_EXIT);        break;
                case 0x095: map_key_clear(KEY_HELP);        break;
                case 0x096: map_key_clear(KEY_TAPE);        break;
                case 0x097: map_key_clear(KEY_TV2);     break;
                case 0x098: map_key_clear(KEY_SAT);     break;
                case 0x09a: map_key_clear(KEY_PVR);     break;
 
                case 0x09c: map_key_clear(KEY_CHANNELUP);   break;
                case 0x09d: map_key_clear(KEY_CHANNELDOWN); break;
                case 0x0a0: map_key_clear(KEY_VCR2);        break;
 
                case 0x0b0: map_key_clear(KEY_PLAY);        break;
                case 0x0b1: map_key_clear(KEY_PAUSE);       break;
                case 0x0b2: map_key_clear(KEY_RECORD);      break;
                case 0x0b3: map_key_clear(KEY_FASTFORWARD); break;
                case 0x0b4: map_key_clear(KEY_REWIND);      break;
                case 0x0b5: map_key_clear(KEY_NEXTSONG);    break;
                case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);    break;
                case 0x0b7: map_key_clear(KEY_STOPCD);      break;
                case 0x0b8: map_key_clear(KEY_EJECTCD);     break;
 
                case 0x0cd: map_key_clear(KEY_PLAYPAUSE);   break;
                    case 0x0e0: map_abs_clear(ABS_VOLUME);       break;
                case 0x0e2: map_key_clear(KEY_MUTE);        break;
                case 0x0e5: map_key_clear(KEY_BASSBOOST);   break;
                case 0x0e9: map_key_clear(KEY_VOLUMEUP);    break;
                case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);  break;
 
                case 0x182: map_key_clear(KEY_BOOKMARKS);   break;
                case 0x183: map_key_clear(KEY_CONFIG);      break;
                case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
                case 0x185: map_key_clear(KEY_EDITOR);      break;
                case 0x186: map_key_clear(KEY_SPREADSHEET); break;
                case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);  break;
                case 0x188: map_key_clear(KEY_PRESENTATION);    break;
                case 0x189: map_key_clear(KEY_DATABASE);    break;
                case 0x18a: map_key_clear(KEY_MAIL);        break;
                case 0x18b: map_key_clear(KEY_NEWS);        break;
                case 0x18c: map_key_clear(KEY_VOICEMAIL);   break;
                case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break;
                case 0x18e: map_key_clear(KEY_CALENDAR);    break;
                case 0x191: map_key_clear(KEY_FINANCE);     break;
                case 0x192: map_key_clear(KEY_CALC);        break;
                case 0x194: map_key_clear(KEY_FILE);        break;
                case 0x196: map_key_clear(KEY_WWW);     break;
                case 0x19c: map_key_clear(KEY_LOGOFF);      break;
                case 0x19e: map_key_clear(KEY_COFFEE);      break;
                case 0x1a6: map_key_clear(KEY_HELP);        break;
                case 0x1a7: map_key_clear(KEY_DOCUMENTS);   break;
                case 0x1ab: map_key_clear(KEY_SPELLCHECK);  break;
                case 0x1b6: map_key_clear(KEY_MEDIA);       break;
                case 0x1b7: map_key_clear(KEY_SOUND);       break;
                case 0x1bc: map_key_clear(KEY_MESSENGER);   break;
                case 0x1bd: map_key_clear(KEY_INFO);        break;
                case 0x201: map_key_clear(KEY_NEW);     break;
                case 0x202: map_key_clear(KEY_OPEN);        break;
                case 0x203: map_key_clear(KEY_CLOSE);       break;
                case 0x204: map_key_clear(KEY_EXIT);        break;
                case 0x207: map_key_clear(KEY_SAVE);        break;
                case 0x208: map_key_clear(KEY_PRINT);       break;
                case 0x209: map_key_clear(KEY_PROPS);       break;
                case 0x21a: map_key_clear(KEY_UNDO);        break;
                case 0x21b: map_key_clear(KEY_COPY);        break;
                case 0x21c: map_key_clear(KEY_CUT);     break;
                case 0x21d: map_key_clear(KEY_PASTE);       break;
                case 0x21f: map_key_clear(KEY_FIND);        break;
                case 0x221: map_key_clear(KEY_SEARCH);      break;
                case 0x222: map_key_clear(KEY_GOTO);        break;
                case 0x223: map_key_clear(KEY_HOMEPAGE);    break;
                case 0x224: map_key_clear(KEY_BACK);        break;
                case 0x225: map_key_clear(KEY_FORWARD);     break;
                case 0x226: map_key_clear(KEY_STOP);        break;
                case 0x227: map_key_clear(KEY_REFRESH);     break;
                case 0x22a: map_key_clear(KEY_BOOKMARKS);   break;
                case 0x22d: map_key_clear(KEY_ZOOMIN);      break;
                case 0x22e: map_key_clear(KEY_ZOOMOUT);     break;
                case 0x22f: map_key_clear(KEY_ZOOMRESET);   break;
                case 0x233: map_key_clear(KEY_SCROLLUP);    break;
                case 0x234: map_key_clear(KEY_SCROLLDOWN);  break;
                case 0x238: map_rel(REL_HWHEEL);        break;
                case 0x25f: map_key_clear(KEY_CANCEL);      break;
                case 0x279: map_key_clear(KEY_REDO);        break;
 
                case 0x289: map_key_clear(KEY_REPLY);       break;
                case 0x28b: map_key_clear(KEY_FORWARDMAIL); break;
                case 0x28c: map_key_clear(KEY_SEND);        break;
 
                default:    goto ignore;
            }
            break;
 
        case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
 
            set_bit(EV_REP, input->evbit);
            switch (usage->hid & HID_USAGE) {
                    case 0x021: map_key_clear(KEY_PRINT);           break;
                case 0x070: map_key_clear(KEY_HP);      break;
                case 0x071: map_key_clear(KEY_CAMERA);      break;
                case 0x072: map_key_clear(KEY_SOUND);       break;
                case 0x073: map_key_clear(KEY_QUESTION);    break;
                case 0x080: map_key_clear(KEY_EMAIL);       break;
                case 0x081: map_key_clear(KEY_CHAT);        break;
                case 0x082: map_key_clear(KEY_SEARCH);      break;
                case 0x083: map_key_clear(KEY_CONNECT);         break;
                case 0x084: map_key_clear(KEY_FINANCE);     break;
                case 0x085: map_key_clear(KEY_SPORT);       break;
                case 0x086: map_key_clear(KEY_SHOP);            break;
                default:    goto ignore;
            }
            break;
 
        case HID_UP_MSVENDOR:
 
            goto ignore;
 
        case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
 
            set_bit(EV_REP, input->evbit);
            switch(usage->hid & HID_USAGE) {
                case 0x003:
                    /* The fn key on Apple USB keyboards */
                    map_key_clear(KEY_FN);
                    hidinput_apple_setup(input);
                    break;
 
                default:    goto ignore;
            }
            break;
 
        case HID_UP_LOGIVENDOR:
 
            goto ignore;
       
        case HID_UP_PID:
 
            switch(usage->hid & HID_USAGE) {
                case 0xa4: map_key_clear(BTN_DEAD); break;
                default: goto ignore;
            }
            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);
 
    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;
}
乍看之下,这个函数超长,为们以keyboad为例,对它进行分析,同时忽略掉quirks和调试信息以及一些无关的操作.代码就缩减成下面这样了:
……
……
switch (usage->hid & HID_USAGE_PAGE) {
 
        case HID_UP_UNDEFINED:
            goto ignore;
        //键盘类型的设备
        case HID_UP_KEYBOARD:
            //使input device支持重复按键
            set_bit(EV_REP, input->evbit);
 
            if ((usage->hid & HID_USAGE) < 256) {
                if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
                map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
            } else
                map_key(KEY_UNKNOWN);
 
            break;
        ……
        ……
}
mapped:
   
    set_bit(usage->type, input->evbit);
    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_KEY) {
        set_bit(EV_MSC, input->evbit);
        set_bit(MSC_SCAN, input->mscbit);
    }
    ……
    ……
    return;
 
ignore:
    dbg_hid_line("IGNORED\n");
    return;
}
关于键盘这部份的usage 定义请自行参考 USB HID Usage Tables sepc.对照hid_keyboard[ ]和键盘的扫描码可以得知,其实hid_keyboard[ ]就是定义了按键的扫描码.
如果filed的usage在hid_keyboard[ ]中有定义,则表示该设备支持这个类型的按键.在代码中,也就是会调用map_key_clear().跟踪看一下它的定义:
#define map_key_clear(c)        do { map_key(c); clear_bit(c, bit); } while (0)
#define map_key(c)  do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
假设该设备支持的按键为C.则经过map_key_clear()后会变成:
Usage->code = C
Usage->type=EV_KEY
Bit 为input->keybit所支持的按键类型,不过已经将C位清除了.
 
接下来,在hidinput_configure_usage()函数中调用
set_bit(usage->type, input->evbit)
即让input device 支持EV_KEY事件
 
然后经过下列语句:
while (usage->code <= max && test_and_set_bit(usage->code, bit))
        usage->code = find_next_zero_bit(bit, max + 1, usage->code);
会在bit中设置usage->code.即上面例子中的按键C.因为在前面已经在bit中usage->code清除.因此test_and_set_bit(usage->code, bit)是不会满足的.
 
最后会调用以下语句:
    if (usage->type == EV_KEY) {
        set_bit(EV_MSC, input->evbit);
        set_bit(MSC_SCAN, input->mscbit);
    }
即设置input_deivce的evbit和mscbit位.
到这里,这个函数已经分析完了.至于keyboard以外的设备,对照usage table spec,也很容易弄得,为了节省篇幅,这里就不将各种设备一一列出.
 
3.4:关于HID中的input_device操作
在前面分析hidinput_connect看到了hid的input_device初始化过程.为了描述方便,将相关的代码列出如下:
                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子系统的分析。所有的input device都会被终端控制台的input_handler匹配。在匹配过程中,会调用input_device->open。对这个过程不太清楚的,请参阅本站关于input子系统分析的文档。
 
对应的,open的接口如下示:
static int hidinput_open(struct input_dev *dev)
{
    struct hid_device *hid = input_get_drvdata(dev);
 
    return hid->hid_open(hid);
}
由此可见,它会转换到hid_device->open()。
在usb_hid_configure()中,hid_device的信息初始化如下:
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
{
    ……
    hid->hid_open = usbhid_open;
    hid->hid_close = usbhid_close;
#ifdef CONFIG_USB_HIDDEV
    hid->hiddev_hid_event = hiddev_hid_event;
    hid->hiddev_report_event = hiddev_report_event;
#endif
    hid->hid_output_raw_report = usbhid_output_raw_report;
    return hid;
    ……
}
相应的接口如下示:
int usbhid_open(struct hid_device *hid)
{
    struct usbhid_device *usbhid = hid->driver_data;
    int res;
 
    if (!hid->open++) {
        res = usb_autopm_get_interface(usbhid->intf);
        if (res < 0) {
            hid->open--;
            return -EIO;
        }
    }
    if (hid_start_in(hid))
        hid_io_error(hid);
    return 0;
}
这个函数里会调用hid_start_in().代码如下:
static int hid_start_in(struct hid_device *hid)
{
    unsigned long flags;
    int rc = 0;
    struct usbhid_device *usbhid = hid->driver_data;
 
    spin_lock_irqsave(&usbhid->inlock, flags);
    if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
            !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
        rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
        if (rc != 0)
            clear_bit(HID_IN_RUNNING, &usbhid->iofl);
    }
    spin_unlock_irqrestore(&usbhid->inlock, flags);
    return rc;
}
由此看到,它会提交usbhid->urbin.
相对于整个过程来说,如果open了input_device.就要开始从设备读取数据了。
 
3.3.1: hid_irq_in()函数分析
Usbhid->urbin传输完成之后,会调用hid_irq_in()。该函数代码如下:
static void hid_irq_in(struct urb *urb)
{
    struct hid_device   *hid = urb->context;
    struct usbhid_device    *usbhid = hid->driver_data;
    int         status;
 
    switch (urb->status) {
        case 0:         /* success */
            usbhid->retry_delay = 0;
            hid_input_report(urb->context, HID_INPUT_REPORT,
                     urb->transfer_buffer,
                     urb->actual_length, 1);
            break;
        case -EPIPE:        /* stall */
            clear_bit(HID_IN_RUNNING, &usbhid->iofl);
            set_bit(HID_CLEAR_HALT, &usbhid->iofl);
            schedule_work(&usbhid->reset_work);
            return;
        case -ECONNRESET:   /* unlink */
        case -ENOENT:
        case -ESHUTDOWN:    /* unplug */
            clear_bit(HID_IN_RUNNING, &usbhid->iofl);
            return;
        case -EILSEQ:       /* protocol error or unplug */
        case -EPROTO:       /* protocol error or unplug */
        case -ETIME:        /* protocol error or unplug */
        case -ETIMEDOUT:    /* Should never happen, but... */
            clear_bit(HID_IN_RUNNING, &usbhid->iofl);
            hid_io_error(hid);
            return;
        default:        /* error */
            warn("input irq status %d received", urb->status);
    }
 
    status = usb_submit_urb(urb, GFP_ATOMIC);
    if (status) {
        clear_bit(HID_IN_RUNNING, &usbhid->iofl);
        if (status != -EPERM) {
            err_hid("can't resubmit intr, %s-%s/input%d, status %d",
                    hid_to_usb_dev(hid)->bus->bus_name,
                    hid_to_usb_dev(hid)->devpath,
                    usbhid->ifnum, status);
            hid_io_error(hid);
        }
    }
}
从上面的代码可以看出,它会一直提交usbhid->urbin.以这样的方式轮询HID设备.直到发生错误,清除HID_IN_RUNNING标志退出。
另外,对于接收到的数据会调用hid_input_report().
这样函数我们在上面已经分析过,不过那时候还留下了一个尾巴,现在就把它补上
 
3.4:遗留的尾巴:hid_input_field()函数
代码如下:
void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt)
{
    unsigned n;
    unsigned count = field->report_count;
    unsigned offset = field->report_offset;
    unsigned size = field->report_size;
    __s32 min = field->logical_minimum;
    __s32 max = field->logical_maximum;
    __s32 *value;
 
    //每一项report的值都存放在一个32位的的buff中
    if (!(value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC)))
        return;
 
    for (n = 0; n < count; n++) {
 
            value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) :
                            extract(data, offset + n * size, size);
            //Array类型的.且为ErrorRollOver .忽略
            if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */
                && value[n] >= min && value[n] <= max
                && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
                goto exit;
    }
 
    for (n = 0; n < count; n++) {
        //如果field为variable 类型, 如果是var型的话,传递过来的数量应该为了0,1表示按键的状态
        if (HID_MAIN_ITEM_VARIABLE & field->flags) {
            hid_process_event(hid, field, &field->usage[n], value[n], interrupt);
            continue;
        }
 
        //如果是Array类型,那传递过来的应该就是按键码的usage值(与min相减)
 
        //如果field里原本有,但传递过来的按键却没有这个键了,表示上次的按键已经松开了.
        if (field->value[n] >= min && field->value[n] <= max
            && field->usage[field->value[n] - min].hid
            &&
            search(value, field->value[n], count))
                hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);
 
        //filed里没有,vaule里却有,表示这个键是新按下的
        if (value[n] >= min && value[n] <= max
            && field->usage[value[n] - min].hid
            && search(field->value, value[n], count))
                hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
    }
//把这一次的按键值保存到field->value中
    memcpy(field->value, value, count * sizeof(__s32));
exit:
    kfree(value);
}
在这个函数里,首先要注意的是field的value的部份.结合之前对report description的解析过程好好理解一下.再次给出field的结构.如下图:

 
上图中的value是附加部份,是在分配field空间的时候留出来的部份
每一个report项,对于value中的一项,用来存放上一次从设备读取的值或者是要传送给设备的值.
另外,还需要注意的是,对于array和variable类型的不同.以keyboard类型为例.对于variable,上面的usage数组分别表示了每一个按键的扫描码.因此从设备读取的信息,也就是value中的值表示的是按键的状态,0是松开,1是按下. 而对于array类型.usage保存的是可能出现的按键类型.从设备读取的信息就浊按键的扫描码.
对于array类型而言,上一次的按键可以从field->value[ ]中找到,就可以得到,上次的按键有没有被松开.或者对比从设备读取回来的值,就可以得知,哪些键是刚被按下去的.
最后,将读取到的信息更新回filed->value.供下一次按键的时候比较.
 
每次的按键上报都是调用hid_process_event()来完成的,这个是hid封装的一个input device上报消息的接触,最终会调用input_event()将事件上报.这个过程很简单,可以自行查阅.
 
四:总结
总的来说,HID的驱动不算复杂,只是对report description的解析比较晦涩一点.另外这个hid驱动封装了几乎所有类型的HID设备.因此,代码中的分支处理比较繁杂.研究代码的时候,最好是抓住一种类型的HID设备去深入研究.
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值