Linux設備驅動之HID驅動---非常全面而且深刻


本文系本站原創,歡迎轉載!
轉載請注明出處:http://ericxiao.cublog.cn/
------------------------------------------
一:前言
繼前面分析過UHCI和HUB驅動之后,接下來以HID設備驅動為例來做一個具體的USB設備驅動分析的例子.HID是Human Interface Devices的縮寫.翻譯成中文即為人機交互設備.這里的人機交互設備是一個宏觀上面的概念,任何設備,只要符合HID spec,都可以稱之為HID設備.常見的HID設備有鼠標鍵盤,游戲操縱桿等等.在接下來的代碼分析中,可以參考HID的spec.這份spec可以在www.usb.org上找到.分析的代碼主要集中在linux-2.6.25/drivers/hid目錄下.
二:HID驅動入口分析
USB HID設備驅動入口位於linux-2.6.25/drivers/hid/usbhid/hid-core.c中.該module的入口為hid_init().代碼如下:
static int __init hid_init(void)
{
    int retval;
    retval = usbhid_quirks_init(quirks_param);
    if (retval)
        goto usbhid_quirks_init_fail;
    retval = hiddev_init();
    if (retval)
        goto hiddev_init_fail;
    retval = usb_register(&hid_driver);
    if (retval)
        goto usb_register_fail;
    info(DRIVER_VERSION ":" DRIVER_DESC);
 
    return 0;
usb_register_fail:
    hiddev_exit();
hiddev_init_fail:
    usbhid_quirks_exit();
usbhid_quirks_init_fail:
    return retval;
}
 
首先來看usbhid_quirks_init()函數.quirks我們在分析UHCI和HUB的時候也接觸過,表示需要做某種修正的設備.該函數調用的參數是quirks_param.定義如下:
static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
從此可以看出, quirks_param是MAX_USBHID_BOOT_QUIRKS元素的字符串數組.並且在加載module的時候,可以動態的指定這些值.
分析到這里.有人可以反應過來了,usbhid_quirks_init()是一種動態進行HID設備修正的方式.具體要修正哪些設備,要修正設備的那些方面,都可以由加載模塊是所帶參數來決定.
usbhid_quirks_init()的代碼如下:
int usbhid_quirks_init(char **quirks_param)
{
    u16 idVendor, idProduct;
    u32 quirks;
    int n = 0, m;
 
    for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
 
        m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
                &idVendor, &idProduct, &quirks);
 
        if (m != 3 ||
                usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
            printk(KERN_WARNING
                    "Could not parse HID quirk module param %s\n",
                    quirks_param[n]);
        }
    }
 
    return 0;
}
由此可以看出, quirks_param數組中的每一項可以分為三個部份,分別是要修正設備的VendorID,ProductID和要修正的功能.比如0x1000 0x0001 0x0004就表示:要忽略掉VendorID為0x1000,ProductID為0x0004的設備.(在代碼中,有 #define HID_QUIRK_IGNORE    0x00000004的定義)
 
跟進usbhid_modify_dquirk()函數,代碼如下:
int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
        const u32 quirks)
{
    struct quirks_list_struct *q_new, *q;
    int list_edited = 0;
 
    if (!idVendor) {
        dbg_hid("Cannot add a quirk with idVendor = 0\n");
        return -EINVAL;
    }
 
    q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
    if (!q_new) {
        dbg_hid("Could not allocate quirks_list_struct\n");
        return -ENOMEM;
    }
 
    q_new->hid_bl_item.idVendor = idVendor;
    q_new->hid_bl_item.idProduct = idProduct;
    q_new->hid_bl_item.quirks = quirks;
 
    down_write(&dquirks_rwsem);
 
    list_for_each_entry(q, &dquirks_list, node) {
 
        if (q->hid_bl_item.idVendor == idVendor &&
                q->hid_bl_item.idProduct == idProduct) {
 
            list_replace(&q->node, &q_new->node);
            kfree(q);
            list_edited = 1;
            break;
 
        }
 
    }
 
    if (!list_edited)
        list_add_tail(&q_new->node, &dquirks_list);
 
    up_write(&dquirks_rwsem);
 
    return 0;
}
這個函數比較簡單,就把quirks_param數組項中的三個部份存入一個封裝結構.然后將其結構掛載到dquirks_list表.如果dquirks_list有重復的VendorId和ProductID就更新其quirks信息.
 
經過usbhid_quirks_init()之后,所有要修正的設備的相關操作都會存放在dquirks_list中.
返回到hid_init(),繼續往下面分析.
hiddev_init()是一個無關的操作,不會影響到后面的操作.忽略
后面就是我們今天要分析的重點了,如下:
retval = usb_register(&hid_driver);
通過前面對HUB的驅動分析,相信對usb_redister()應該很熟悉了.hid_driver定義如下:
static struct usb_driver hid_driver = {
    .name =     "usbhid",
    .probe =    hid_probe,
    .disconnect =   hid_disconnect,
    .suspend =  hid_suspend,
    .resume =   hid_resume,
    .reset_resume = hid_post_reset,
    .pre_reset =    hid_pre_reset,
    .post_reset =   hid_post_reset,
    .id_table = hid_usb_ids,
    .supports_autosuspend = 1,
};
其中,id_table的結構為hid_usb_ids.定義如下:
static struct usb_device_id hid_usb_ids [] = {
    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
        .bInterfaceClass = USB_INTERFACE_CLASS_HID },
    { }                     /* Terminating entry */
};
也就是說,該驅動會匹配interface的ClassID,所有ClassID為USB_INTERFACE_CLASS_HID的設備都會被這個驅動所匹配.所以,所有USB HID設備都會由這個module來驅動.
 
三:HID驅動的probe過程
從上面的分析可看到,probe接口為hid_probe().定義如下:
static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct hid_device *hid;
    char path[64];
    int i;
    char *c;
 
    dbg_hid("HID probe called for ifnum %d\n",
            intf->altsetting->desc.bInterfaceNumber);
    //config the hid device
    if (!(hid = usb_hid_configure(intf)))
        return -ENODEV;
 
    usbhid_init_reports(hid);
    hid_dump_device(hid);
    if (hid->quirks & HID_QUIRK_RESET_LEDS)
        usbhid_set_leds(hid);
 
    if (!hidinput_connect(hid))
        hid->claimed |= HID_CLAIMED_INPUT;
    if (!hiddev_connect(hid))
        hid->claimed |= HID_CLAIMED_HIDDEV;
    if (!hidraw_connect(hid))
        hid->claimed |= HID_CLAIMED_HIDRAW;
 
    usb_set_intfdata(intf, hid);
 
    if (!hid->claimed) {
        printk ("HID device claimed by neither input, hiddev nor hidraw\n");
        hid_disconnect(intf);
        return -ENODEV;
    }
 
    if ((hid->claimed & HID_CLAIMED_INPUT))
        hid_ff_init(hid);
 
    if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
        hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
            intf->cur_altsetting->desc.bInterfaceNumber);
 
    printk(KERN_INFO);
 
    if (hid->claimed & HID_CLAIMED_INPUT)
        printk("input");
    if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
                hid->claimed & HID_CLAIMED_HIDRAW))
        printk(",");
    if (hid->claimed & HID_CLAIMED_HIDDEV)
        printk("hiddev%d", hid->minor);
    if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
            (hid->claimed & HID_CLAIMED_HIDRAW))
        printk(",");
    if (hid->claimed & HID_CLAIMED_HIDRAW)
        printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
 
    c = "Device";
    for (i = 0; i < hid->maxcollection; i++) {
        if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
            (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
            (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
            c = hid_types[hid->collection[i].usage & 0xffff];
            break;
        }
    }
 
    usb_make_path(interface_to_usbdev(intf), path, 63);
 
    printk(": USB HID v%x.%02x %s [%s] on %s\n",
        hid->version >> 8, hid->version & 0xff, c, hid->name, path);
 
    return 0;
}
這個函數看起來是不是讓人心慌慌?其實這個函數的最后一部份就是打印出一個Debug信息,我們根本就不需要去看. hiddev_connect()和hidraw_connect()是一個選擇編譯的操作,也不可以不要去理會.然后,剩下的就沒多少了.
 
3.1:usb_hid_configure()函數分析
先來看usb_hid_configure().顧名思義,該接口用來配置hid設備.怎么配置呢?還是深入到代碼來分析,該函數有一點長,分段分析如下:
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
{
    struct usb_host_interface *interface = intf->cur_altsetting;
    struct usb_device *dev = interface_to_usbdev (intf);
    struct hid_descriptor *hdesc;
    struct hid_device *hid;
    u32 quirks = 0;
    unsigned rsize = 0;
    char *rdesc;
    int n, len, insize = 0;
    struct usbhid_device *usbhid;
 
    quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
            le16_to_cpu(dev->descriptor.idProduct));
 
    /* Many keyboards and mice don't like to be polled for reports,
     * so we will always set the HID_QUIRK_NOGET flag for them. */
     //如果是boot設備,跳出.不由此驅動處理
    if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
        if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
            interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
                quirks |= HID_QUIRK_NOGET;
    }
 
    //如果是要忽略的
    if (quirks & HID_QUIRK_IGNORE)
        return NULL;
 
    if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
        (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
            return NULL;
首先找到該接口需要修正的操作,也就是上面代碼中的quirks值,如果沒有修正操作,則quirks為0.另外,根據usb hid spec中的定義,subclass如果為1,則說明該設備是一個boot階段使用的hid設備,然后Protocol Code為1和2時分別代表Keyboard和Mouse. 如果是boot階段的Keyboard和Mouse是不會由這個驅動進行處理的.另外,quirks為HID_QUIRK_IGNORE表示忽略這個設備,為HID_QUIRK_IGNORE_MOUSE,表示,如果該設備是一個鼠標設備,則忽略.
 
    //get hid descriptors
    if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
        (!interface->desc.bNumEndpoints ||
         usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
        dbg_hid("class descriptor not present\n");
        return NULL;
    }
 
    //bNumDescriptors:支持的附屬描述符數目
    for (n = 0; n < hdesc->bNumDescriptors; n++)
        if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
            rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
    //如果Report_Descriptors長度不合法
    if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
        dbg_hid("weird size of report descriptor (%u)\n", rsize);
        return NULL;
    }
 
    if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
        dbg_hid("couldn't allocate rdesc memory\n");
        return NULL;
    }
 
    //Set idle_time = 0
    hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
    //Get Report_Descriptors
    if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
        dbg_hid("reading report descriptor failed\n");
        kfree(rdesc);
        return NULL;
    }
 
    //是否屬於fixup?
    usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
            le16_to_cpu(dev->descriptor.idProduct), rdesc,
            rsize, rdesc_quirks_param);
 
    dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
    for (n = 0; n < rsize; n++)
        dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
    dbg_hid_line("\n");
對於HID設備來說,在interface description之后會附加一個hid description, hid description中的最后部份包含有Report description或者Physical Descriptors的長度.
在上面的代碼中,首先取得附加在interface description之后的hid description,然后,再從hid description中取得report description的長度.最后,取得report description的詳細信息.
在這里,還會將idle時間設備為0,表示無限時,即,從上一次報表傳輸后,只有在報表發生改變時,才會傳送此報表內容,否則,傳送NAK.
這段代碼的最后一部份是相關的fixup操作,不做詳細分析.
 
    //pasrse the report_descriptor
    if (!(hid = hid_parse_report(rdesc, n))) {
        dbg_hid("parsing report descriptor failed\n");
        kfree(rdesc);
        return NULL;
    }
 
    kfree(rdesc);
    hid->quirks = quirks;
 
    if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
        goto fail_no_usbhid;
 
    hid->driver_data = usbhid;
    usbhid->hid = hid;
解析獲得的report description,解析之后的信息,存放在hid_device->collection和hid_device->report_enum[ ]中,這個解析過程之后會做詳細分析.然后,初始化一個usbhid_device結構,使usbhid_device->hid指向剛解析report description獲得的hid_device.同樣,hid_device->driver_data關聯到usbhid_device.
 
    usbhid->bufsize = HID_MIN_BUFFER_SIZE;
    //計算各傳輸方向的最大buffer
    hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
    hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
    hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);
 
    if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
        usbhid->bufsize = HID_MAX_BUFFER_SIZE;
    //in方向的傳輸最大值
    hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
 
    if (insize > HID_MAX_BUFFER_SIZE)
        insize = HID_MAX_BUFFER_SIZE;
 
    if (hid_alloc_buffers(dev, hid)) {
        hid_free_buffers(dev, hid);
        goto fail;
    }
計算傳輸數據的最大緩存區,並以這個大小為了hid設備的urb傳輸分配空間.另外,這里有一個最小值限制即代碼中所看到的HID_MIN_BUFFER_SIZE,為64, 即一個高速設備的一個端點一次傳輸的數據量.在這里定義最小值為64是為了照顧低速/全速/高速三種類型的端點傳輸數據量.
然后,調用hid_alloc_buffers()為hid的urb傳輸初始化傳輸緩沖區.
另外,需要注意的是,insize為INPUT方向的最大數據傳輸量.
 
    // 初始化usbhid->urbin和usbhid->usbout
    for (n = 0; n < interface->desc.bNumEndpoints; n++) {
 
        struct usb_endpoint_descriptor *endpoint;
        int pipe;
        int interval;
 
        endpoint = &interface->endpoint[n].desc;
        //不是中斷傳輸 退出
        if ((endpoint->bmAttributes & 3) != 3)      /* Not an interrupt endpoint */
            continue;
 
        interval = endpoint->bInterval;
 
        /* Change the polling interval of mice. */
        //修正鼠標的雙擊時間
        if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
            interval = hid_mousepoll_interval;
 
        if (usb_endpoint_dir_in(endpoint)) {
            if (usbhid->urbin)
                continue;
            if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
                goto fail;
            pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
            usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
                     hid_irq_in, hid, interval);
            usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
            usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        } else {
            if (usbhid->urbout)
                continue;
            if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
                goto fail;
            pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
            usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
                     hid_irq_out, hid, interval);
            usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
            usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        }
    }
 
    if (!usbhid->urbin) {
        err_hid("couldn't find an input interrupt endpoint");
        goto fail;
    }
遍歷接口中的所有endpoint,並初始化in中斷傳輸方向和out中斷方向的urb.如果一個hid設備沒有in方向的中斷傳輸,非法.
另外,在這里要值得注意的是, 在為OUT方向urb初始化的時候,它的傳輸緩存區大小被設為了0.IN方向的中斷傳輸緩存區大小被設為了insize,傳輸緩存區大小在submit的時候會修正的.
 
    init_waitqueue_head(&hid->wait);
 
    INIT_WORK(&usbhid->reset_work, hid_reset);
    setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
 
    spin_lock_init(&usbhid->inlock);
    spin_lock_init(&usbhid->outlock);
    spin_lock_init(&usbhid->ctrllock);
 
    hid->version = le16_to_cpu(hdesc->bcdHID);
    hid->country = hdesc->bCountryCode;
    hid->dev = &intf->dev;
    usbhid->intf = intf;
    usbhid->ifnum = interface->desc.bInterfaceNumber;
 
    hid->name[0] = 0;
 
    if (dev->manufacturer)
        strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
 
    if (dev->product) {
        if (dev->manufacturer)
            strlcat(hid->name, " ", sizeof(hid->name));
        strlcat(hid->name, dev->product, sizeof(hid->name));
    }
 
    if (!strlen(hid->name))
        snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
             le16_to_cpu(dev->descriptor.idVendor),
             le16_to_cpu(dev->descriptor.idProduct));
 
    hid->bus = BUS_USB;
    hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
    hid->product = le16_to_cpu(dev->descriptor.idProduct);
 
    usb_make_path(dev, hid->phys, sizeof(hid->phys));
    strlcat(hid->phys, "/input", sizeof(hid->phys));
    len = strlen(hid->phys);
    if (len < sizeof(hid->phys) - 1)
        snprintf(hid->phys + len, sizeof(hid->phys) - len,
             "%d", intf->altsetting[0].desc.bInterfaceNumber);
 
    if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
        hid->uniq[0] = 0;
初始化hid的相關信息.
 
    //初始化hid 的ctrl傳輸
    usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
    if (!usbhid->urbctrl)
        goto fail;
 
    usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
                 usbhid->ctrlbuf, 1, hid_ctrl, hid);
    usbhid->urbctrl->setup_dma = usbhid->cr_dma;
    usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
    usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
    hid->hidinput_input_event = usb_hidinput_input_event;
    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;
初始化usbhid的控制傳輸urb,之后又初始化了usbhid的幾個操作函數.這個操作有什么用途,等用到的時候再來進行分析.
fail:
    usb_free_urb(usbhid->urbin);
    usb_free_urb(usbhid->urbout);
    usb_free_urb(usbhid->urbctrl);
    hid_free_buffers(dev, hid);
    kfree(usbhid);
fail_no_usbhid:
    hid_free_device(hid);
 
    return NULL;
}
經過上面的分析之后,我們對這個函數的大概操作有了一定的了解.現在分析里面調用的一些重要的子調函數.等這些子函數全部分析完了之后,不妨回過頭看下這個函數.
 
3.1.1:hid_parse_report()分析
第一個要分析的函數是hid_parse_report().該函數用來解析report description.
解析report description是一個繁雜的過程,對這個描述符不太清楚的,仔細看一下spec.在這里我們只會做代碼上的分析.
代碼如下:
struct hid_device *hid_parse_report(__u8 *start, unsigned size)
{
    struct hid_device *device;
    struct hid_parser *parser;
    struct hid_item item;
    __u8 *end;
    unsigned i;
    static int (*dispatch_type[])(struct hid_parser *parser,
                      struct hid_item *item) = {
        hid_parser_main,
        hid_parser_global,
        hid_parser_local,
        hid_parser_reserved
    };
 
    if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
        return NULL;
 
    //默認HID_DEFAULT_NUM_COLLECTIONS 項
    if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
                   HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
        kfree(device);
        return NULL;
    }
    //hid_device->collection_size: collection的項數
    device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
 
    for (i = 0; i < HID_REPORT_TYPES; i++)
        INIT_LIST_HEAD(&device->report_enum[i].report_list);
 
    if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {
        kfree(device->collection);
        kfree(device);
        return NULL;
    }
    //hid_device->rdesc存放report_descriptor,hid_device->size存放這個描述符的大小
    memcpy(device->rdesc, start, size);
    device->rsize = size;
 
    if (!(parser = vmalloc(sizeof(struct hid_parser)))) {
        kfree(device->rdesc);
        kfree(device->collection);
        kfree(device);
        return NULL;
    }
    memset(parser, 0, sizeof(struct hid_parser));
    parser->device = device;
 
    end = start + size;
    while ((start = fetch_item(start, end, &item)) != NULL) {
 
        //long item在這里暫不做parse
        if (item.format != HID_ITEM_FORMAT_SHORT) {
            dbg_hid("unexpected long global item\n");
            hid_free_device(device);
            vfree(parser);
            return NULL;
        }
 
        //parse the short item
        if (dispatch_type[item.type](parser, &item)) {
            dbg_hid("item %u %u %u %u parsing failed\n",
                item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
            hid_free_device(device);
            vfree(parser);
            return NULL;
        }
 
        //如果全部解析完了
        if (start == end) {
            if (parser->collection_stack_ptr) {
                dbg_hid("unbalanced collection at end of report description\n");
                hid_free_device(device);
                vfree(parser);
                return NULL;
            }
            if (parser->local.delimiter_depth) {
                dbg_hid("unbalanced delimiter at end of report description\n");
                hid_free_device(device);
                vfree(parser);
                return NULL;
            }
            vfree(parser);
            return device;
        }
    }
 
    dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
    hid_free_device(device);
    vfree(parser);
    return NULL;
}
進入到這個函數,我們首先看到的是Main,Globa,Local標簽的解析函數.然后,分配並初始化了hid_device結構和hid_ parser.在代碼中我們看到,hid_ parser-> device指向了hid_device.后hid_device沒有任何域指向hid_parser. 實際上hid_parser只是一個輔助結構.report description解析之后的信息全部都存放在hid_device結構中.
另外,hid_device-> rdesc保存了一份report description副本.
然后,就開始對report description的解析.函數fetch_item()用來取出report description的一項數據.代碼如下:
static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
{
    u8 b;
 
    //合法性檢測
    if ((end - start) <= 0)
        return NULL;
 
    //取前面一個字節.對於短項.它的首個字節定義了bsize,bType,bTag.而對於長項,它的值為0xFE
    b = *start++;
 
    item->type = (b >> 2) & 3;
    item->tag  = (b >> 4) & 15;
 
    //如果為長項.它的Type和Tag在其后的二個字節中.item->data.longdata指向數據的起始位置
    if (item->tag == HID_ITEM_TAG_LONG) {
 
        item->format = HID_ITEM_FORMAT_LONG;
 
        if ((end - start) < 2)
            return NULL;
 
        item->size = *start++;
        item->tag  = *start++;
 
        if ((end - start) < item->size)
            return NULL;
 
        item->data.longdata = start;
        start += item->size;
        return start;
    }
    //對於短項的情況.取得size值.並根據size值取得它的data域
    item->format = HID_ITEM_FORMAT_SHORT;
    item->size = b & 3;
 
    switch (item->size) {
 
        case 0:
            return start;
 
        case 1:
            if ((end - start) < 1)
                return NULL;
            item->data.u8 = *start++;
            return start;
 
        case 2:
            if ((end - start) < 2)
                return NULL;
            item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start));
            start = (__u8 *)((__le16 *)start + 1);
            return start;
 
        case 3:
            item->size++;
            if ((end - start) < 4)
                return NULL;
            item->data.u32 = le32_to_cpu(get_unaligned((__le32*)start));
            start = (__u8 *)((__le32 *)start + 1);
            return start;
    }
 
    return NULL;
}
對照代碼中的注釋,應該很容易看懂這個函數,不再詳細分析.
返回到hid_parse_report()中,取得相應項之后,如果是長項,這里不會做處理.對於短項.為不同的type調用不同的解析函數.
3.1.1.1:Global項解析
Global的解析入口是hid_parser_global().代碼如下:
static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
{
    switch (item->tag) {
        //PUSH項
        case HID_GLOBAL_ITEM_TAG_PUSH:
 
            if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
                dbg_hid("global enviroment stack overflow\n");
                return -1;
            }
 
            memcpy(parser->global_stack + parser->global_stack_ptr++,
                &parser->global, sizeof(struct hid_global));
            return 0;
        //POP項
        case HID_GLOBAL_ITEM_TAG_POP:
 
            if (!parser->global_stack_ptr) {
                dbg_hid("global enviroment stack underflow\n");
                return -1;
            }
 
            memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
                sizeof(struct hid_global));
            return 0;
 
        case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
            parser->global.usage_page = item_udata(item);
            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_PHYSICAL_MINIMUM:
            parser->global.physical_minimum = item_sdata(item);
            return 0;
 
        case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
            if (parser->global.physical_minimum < 0)
                parser->global.physical_maximum = item_sdata(item);
            else
                parser->global.physical_maximum = item_udata(item);
            return 0;
 
        case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
            parser->global.unit_exponent = item_sdata(item);
            return 0;
 
        case HID_GLOBAL_ITEM_TAG_UNIT:
            parser->global.unit = item_udata(item);
            return 0;
 
        case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
            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_GLOBAL_ITEM_TAG_REPORT_ID:
            if ((parser->global.report_id = item_udata(item)) == 0) {
                dbg_hid("report_id 0 is invalid\n");
                return -1;
            }
            return 0;
 
        default:
            dbg_hid("unknown global tag 0x%x\n", item->tag);
            return -1;
    }
}
這個函數雖然長,但是邏輯很簡單,對於global信息,存放在hid_parse->global中.
如果遇到了PUSH項,將當前的global項入棧,棧即為hid_parse-> global_stack[ ].當前的棧頂位置由hid_parse-> global_stack_ptr指定.
如果遇到了POP項,就將棧中的global信息出棧.
 
3.1.1.2:Local項解析
Local項解析的相應接口為hid_parser_local().代碼如下:
static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
{
    __u32 data;
    unsigned n;
 
    if (item->size == 0) {
        dbg_hid("item data expected for local item\n");
        return -1;
    }
 
    data = item_udata(item);
 
    switch (item->tag) {
 
        //DELIMITER項,定義一個Local項的開始
        case HID_LOCAL_ITEM_TAG_DELIMITER:
            //data>1:一個local項開始,0:一個local項結束
            //parse->local.delimiter_branch:表示local項計數.
            //進入一個local項時,local.delimiter_depth為1,退出一個local項時local.delimiter_depth為0
            // TODO: Local項不能嵌套
            if (data) {
                /*
                 * We treat items before the first delimiter
                 * as global to all usage sets (branch 0).
                 * In the moment we process only these global
                 * items and the first delimiter set.
                 */
                if (parser->local.delimiter_depth != 0) {
                    dbg_hid("nested delimiters\n");
                    return -1;
                }
                parser->local.delimiter_depth++;
                parser->local.delimiter_branch++;
            } else {
                if (parser->local.delimiter_depth < 1) {
                    dbg_hid("bogus close delimiter\n");
                    return -1;
                }
                parser->local.delimiter_depth--;
            }
            return 1;
        //以下各項不能出現在有DELIMITER標簽的地方
        case HID_LOCAL_ITEM_TAG_USAGE:
 
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
 
//local的usage項有擴展用法,它的高16可以定義usage_page.如果高16為空,它的//usage_page則定義在global中的usage_page
            if (item->size <= 2)
                data = (parser->global.usage_page << 16) + data;
            //然后添加到parse->local的usage列表
            return hid_add_usage(parser, data);
 
//對於有usage_min和usage_max的情況,將usage_min和usage_max之間的usage添加到//parse=>local的usage列表
        case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
 
            if (parser->local.delimiter_branch > 1) {
                dbg_hid("alternative usage ignored\n");
                return 0;
            }
 
            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;
            }
 
            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;
 
        default:
 
            dbg_hid("unknown local item tag 0x%x\n", item->tag);
            return 0;
    }
    return 0;
}
詳細分析一下hid_add_usage().代碼如下:
static int hid_add_usage(struct hid_parser *parser, unsigned usage)
{
    if (parser->local.usage_index >= HID_MAX_USAGES) {
        dbg_hid("usage index exceeded\n");
        return -1;
    }
    parser->local.usage[parser->local.usage_index] = usage;
    parser->local.collection_index[parser->local.usage_index] =
        parser->collection_stack_ptr ?
        parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
    parser->local.usage_index++;
    return 0;
}
如果usage項超過了HID_MAX_USAGES,為非法.最大為8192項.
Parse->local.usage_index表示local的項數,當然也表示了parse->local.usage[ ]數組中的下一個可用項.
parser->local.collection_index表示該usage所在的collection項序號.具體的collection信息存放在hid_deivce->collection[ ]中.
關於collection我們在分析Main項解析的時候會詳細分析.
 
3.1.1.3:Main項解析
Main項解析的入口為hid_parser_main().代碼如下:
static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
{
    __u32 data;
    int ret;
 
    //data域
    data = item_udata(item);
 
    switch (item->tag) {
        //Collection
        case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
            ret = open_collection(parser, data & 0xff);
            break;
        //End Collection   
        case HID_MAIN_ITEM_TAG_END_COLLECTION:
            ret = close_collection(parser);
            break;
        //Input
        case HID_MAIN_ITEM_TAG_INPUT:
            ret = hid_add_field(parser, HID_INPUT_REPORT, data);
            break;
        //Outpput  
        case HID_MAIN_ITEM_TAG_OUTPUT:
            ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
            break;
        //Feature  
        case HID_MAIN_ITEM_TAG_FEATURE:
            ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
            break;
        default:
            dbg_hid("unknown main item tag 0x%x\n", item->tag);
            ret = 0;
    }
 
    memset(&parser->local, 0, sizeof(parser->local));   /* Reset the local parser environment */
 
    return ret;
}
對Main項的解析要稍微復雜一點,Main項主要有兩個部份,一個是Collection,一個是Input/Output/Feature項.
先來看Collection項的解析.
所有的collection信息都存放在hid_device->collection[ ]中.而Collection項又有嵌套的情況,每遇到一個Collection項就將collection的序號入棧,棧為parser_device->collection_stack[ ].棧頂指針為parser_device->collection_stack_ptr .遇到了一個end collection之后,就parser_device->collection_stack_ptr減1,表示出棧.
 
熟悉這個大概的情況之后,就可以跟進open_collection()了.代碼如下:
//所有的collection都存放在hid_dev->collection 中, 而hid_dev->maxcollection 表示collection[]中的下一個空閑位置
//paser->collection_stack[ ]存放的是當前解析的collection在hid_dev->collection[ ]中的序號
static int open_collection(struct hid_parser *parser, unsigned type)
{
    struct hid_collection *collection;
    unsigned usage;
 
    usage = parser->local.usage[0];
 
    //colletcion嵌套過多
    if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
        dbg_hid("collection stack overflow\n");
        return -1;
    }
 
    //device->maxcollection:存放的collection個數
   
    //device->collection[ ]太小,必須擴大存放空間
    if (parser->device->maxcollection == parser->device->collection_size) {
        collection = kmalloc(sizeof(struct hid_collection) *
                parser->device->collection_size * 2, GFP_KERNEL);
        if (collection == NULL) {
            dbg_hid("failed to reallocate collection array\n");
            return -1;
        }
        memcpy(collection, parser->device->collection,
            sizeof(struct hid_collection) *
            parser->device->collection_size);
        memset(collection + parser->device->collection_size, 0,
            sizeof(struct hid_collection) *
            parser->device->collection_size);
        kfree(parser->device->collection);
        parser->device->collection = collection;
        parser->device->collection_size *= 2;
    }
 
    //將collection序號入棧
    parser->collection_stack[parser->collection_stack_ptr++] =
        parser->device->maxcollection;
 
    //存入hid_device->collection[]
    collection = parser->device->collection +
        parser->device->maxcollection++;
    collection->type = type;
    collection->usage = usage;
    //collection的深度
    collection->level = parser->collection_stack_ptr - 1;
 
    if (type == HID_COLLECTION_APPLICATION)
        parser->device->maxapplication++;
 
    return 0;
}
對照上面的分析和函數中的注釋,理解這個函數應該很簡單,不做詳細分析.
 
對於Input/Output/Feature項的解析:
先來看一下hid_device結構的定義片段:
struct hid_device
{  
    ……
    ……
struct hid_report_enum report_enum[HID_REPORT_TYPES];
……
}  
對於INPUT/OUTPUT/FEATURE,每種類型都對應report_enum[ ]中的一項.
Struct hid_report_enum定義如下:
struct hid_report_enum {
    unsigned numbered;
    struct list_head report_list;
    struct hid_report *report_id_hash[256];
};
對於每一個report_id,對應report_id_hash[ ]中的一項,同時,將所對應的hid_report添加到report_list鏈表中.如果有多個report_id 的情況,numbered被賦為1.
Struct hid_report定義如下:
struct hid_report {
    struct list_head list;
    unsigned id;                    /* id of this report */
    unsigned type;                  /* report type */
    struct hid_field *field[HID_MAX_FIELDS];    /* fields of the report */
    unsigned maxfield;              /* maximum valid field index */
    unsigned size;                  /* size of the report (bits) */
    struct hid_device *device;          /* associated device */
}
List:用來形成鏈表
Id:表示report_id
Type: INPUT/OUTPUT/FEATURE
Field[ ]:成員列表,對應一個report_id有多個INPUT(OUTPUT/FEATURE)項
Maxfield: field[ ]中的有效項數
Size: 該report的大小
Device:所屬的hid_device
 
了解了這些之后,就可以來看一下代碼了:
如下:
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;
 
    //找到類型和對應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設備去深入研究.
 
 

非常感謝: http://blog.chinaunix.net/uid-20742320-id-3971218.html

原文地址:http://blog.chinaunix.net/uid/20543183/cid-29897-list-1.html(非常完全)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM