Linux设备驱动之HID驱动

出处:http://ericxiao.cublog.cn/

一:前言
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 的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设备去深入研究.

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目  录 第1章 引言 1 1.1 演进 1 1.2 gnu copyleft 2 1.3 kernel.org 2 1.4 邮件列表和论坛 3 1.5 linux发行版 3 1.6 查看源代码 4 1.7 编译内核 7 1.8 可加载的模块 8 1.9 整装待发 9 第2章 内核 11 2.1 启动过程 11 2.1.1 bios-provided physical ram map 12 2.1.2 758mb lowmem available 14 2.1.3 kernel command line: ro root=/dev/hda1 14 2.1.4 calibrating delay...1197.46 .bogomips (lpj=2394935) 15 2.1.5 checking hlt instruction 16 2.1.6 net: registered protocol family 2 17 2.1.7 freeing initrd memory: 387k freed 17 2.1.8 io scheduler anticipatory registered (default) 18 2.1.9 setting up standard pci resources 18 2.1.10 ext3-fs: mounted filesystem 19 2.1.11 init: version 2.85 booting 19 2.2 内核模式和用户模式 20 2.3 进程上下文和中断上下文 20 2.4 内核定时器 21 2.4.1 hz和jiffies 21 2.4.2 长延时 22 2.4.3 短延时 24 2.4.4 pentium时间戳计数器 24 2.4.5 实时钟 25 2.5 内核中的并发 26 2.5.1 自旋锁和互斥体 26 2.5.2 原子操作 30 2.5.3 读—写锁 31 2.5.4 调试 32 2.6 proc文件系统 32 2.7 内存分配 33 2.8 查看源代码 34 第3章 内核组件 37 3.1 内核线程 37 3.1.1 创建内核线程 37 3.1.2 进程状态和等待队列 41 3.1.3 用户模式辅助程序 42 3.2 辅助接口 43 3.2.1 链表 44 3.2.2 散列链表 49 3.2.3 工作队列 49 3.2.4 通知链 51 3.2.5 完成接口 54 3.2.6 kthread辅助接口 56 3.2.7 错误处理助手 57 3.3 查看源代码 58 第4章 基本概念 61 4.1 设备驱动程序介绍 61 4.2 中断处理 63 4.2.1 中断上下文 63 4.2.2 分配irq号 64 4.2.3 设备实例:导航杆 65 4.2.4 softirq和tasklet 68 4.3 linux设备模型 71 4.3.1 udev 71 4.3.2 sysfs、kobject和设备类 73 4.3.3 热插拔和冷插拔 76 4.3.4 微码下载 76 4.3.5 模块自动加载 77 4.4 内存屏障 78 4.5 电源管理 79 4.6 查看源代码 79 第5章 字符设备驱动程序 81 5.1 字符设备驱动程序基础 81 5.2 设备实例:系统cmos 82 5.2.1 驱动程序初始化 83 5.2.2 打开与释放 86 5.2.3 数据交换 88 5.2.4 查找 92 5.2.5 控制 94 5.3 检测数据可用性 95 5.3.1 轮询 95 5.3.2 fasync 98 5.4 和并行端口交互 99 5.5 rtc子系统 108 5.6 伪字符驱动程序 109 5.7 混杂驱动程序 110 5.8 字符设备驱动程序注意事项 115 5.9 查看源代码 115 第6章 串行设备驱动程序 118 6.1 层次架构 119 6.2 uart驱动程序 121 6.2.1 设备实例:手机 122 6.2.2 rs-485 132 6.3 tty驱动程序 132 6.4 线路规程 134 6.5 查看源代码 141 第7章 输入设备驱动程序 143 7.1 输入事件驱动程序 144 7.2 输入设备驱动程序 150 7.2.1 serio 150 7.2.2 键盘 150 7.2.3 鼠标 152 7.2.4 触摸控制器 157 7.2.5 加速度传感器 158 7.2.6 输出事件 158 7.3 调试 159 7.4 查看源代码 160 第8章 i2c协议 161 8.1 i2c/smbus是什么 161 8.2 i2c核心 162 8.3 总线事务 164 8.4 设备实例:eeprom 164 8.4.1 初始化 165 8.4.2 探测设备 167 8.4.3 检查适配器的功能 169 8.4.4 访问设备 169 8.4.5 其他函数 170 8.5 设备实例:实时时钟 171 8.6 i2c-dev 174 8.7 使用lm-sensors监控硬件 174 8.8 spi总线 174 8.9 1-wire总线 176 8.10 调试 176 8.11 查看源代码 176 第9章 pcmcia和cf 179 9.1 pcmcia/cf是什么 179 9.2 linux-pcmcia子系统 181 9.3 主机控制器驱动程序 183 9.4 pcmcia核心 183 9.5 驱动程序服务 183 9.6 客户驱动程序 183 9.6.1 数据结构 184 9.6.2 设备实例:pcmcia卡 185 9.7 将零件组装在一起 188 9.8 pcmcia存储 189 9.9 串行pcmcia 189 9.10 调试 191 9.11 查看源代码 191 第10章 pci 193 10.1 pci系列 193 10.2 寻址和识别 195 10.3 访问pci 198 10.3.1 配置区 198 10.3.2 i/o和内存 199 10.4 dma 200 10.5 设备实例:以太网—调制解调器卡 203 10.5.1 初始化和探测 203 10.5.2 数据传输 209 10.6 调试 214 10.7 查看源代码 214 第11章 usb 216 11.1 usb体系架构 216 11.1.1 总线速度 218 11.1.2 主机控制器 218 11.1.3 传输模式 219 11.1.4 寻址 219 11.2 linux-usb子系统 220 11.3 驱动程序的数据结构 221 11.3.1 usb_device结构体 221 11.3.2 urb 222 11.3.3 管道 223 11.3.4 描述符结构 223 11.4 枚举 225 11.5 设备实例:遥测卡 225 11.5.1 初始化和探测过程 226 11.5.2 卡寄存器的访问 230 11.5.3 数据传输 233 11.6 类驱动程序 236 11.6.1 大容量存储设备 236 11.6.2 usb-串行端口转换器 241 11.6.3 人机接口设备 243 11.6.4 蓝牙 243 11.7 gadget驱动程序 243 11.8 调试 244 11.9 查看源代码 245 第12章 视频驱动程序 247 12.1 显示架构 247 12.2 linux视频子系统 249 12.3 显示参数 251 12.4 帧缓冲api 252 12.5 帧缓冲驱动程序 254 12.6 控制台驱动程序 265 12.6.1 设备实例:手机 266 12.6.2 启动logo 270 12.7 调试 270 12.8 查看源代码 271 第13章 音频驱动程序 273 13.1 音频架构 273 13.2 linux声音子系统 275 13.3 设备实例:mp3播放器 277 13.3.1 驱动程序函数和结构体 278 13.3.2 alsa编程 287 13.4 调试 288 13.5 查看源代码 289 第14章 块设备驱动程序 291 14.1 存储技术 291 14.2 linux块i/o层 295 14.3 i/o调度器 295 14.4 块驱动程序数据结构和方法 296 14.5 设备实例:简单存储控制器 298 14.5.1 初始化 299 14.5.2 块设备操作 301 14.5.3 磁盘访问 302 14.6 高级主题 304 14.7 调试 306 14.8 查看源代码 306 第15章 网络接口卡 308 15.1 驱动程序数据结构 308 15.1.1 套接字缓冲区 309 15.1.2 网络设备接口 310 15.1.3 激活 311 15.1.4 数据传输 311 15.1.5 看门狗 311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:以太网nic 316 15.5 isa网络驱动程序 321 15.6 atm 321 15.7 网络吞吐量 322 15.7.1 驱动程序性能 322 15.7.2 协议性能 323 15.8 查看源代码 324 第16章 linux无线设备驱动 326 16.1 蓝牙 327 16.1.1 bluez 328 16.1.2 设备实例:cf卡 329 16.1.3 设备实例:usb适配器 330 16.1.4 rfcomm 331 16.1.5 网络 332 16.1.6 hid 334 16.1.7 音频 334 16.1.8 调试 334 16.1.9 关于源代码 334 16.2 红外 335 16.2.1 linux-irda 335 16.2.2 设备实例:超级i/o芯片 337 16.2.3 设备实例:ir dongle 338 16.2.4 ircomm 340 16.2.5 联网 340 16.2.6 irda套接字 341 16.2.7 lirc 341 16.2.8 查看源代码 342 16.3 wifi 343 16.3.1 配置 343 16.3.2 设备驱动程序 346 16.3.3 查看源代码 347 16.4 蜂窝网络 347 16.4.1 gprs 347 16.4.2 cdma 349 16.5 当前趋势 350 第17章 存储技术设备 352 17.1 什么是闪存 352 17.2 linux-mtd子系统 353 17.3 映射驱动程序 353 17.4 nor芯片驱动程序 358 17.5 nand芯片驱动程序 359 17.6 用户模块 361 17.6.1 块设备模拟 361 17.6.2 字符设备模拟 361 17.6.3 jffs2 362 17.6.4 yaffs2 363 17.7 mtd工具 363 17.8 配置mtd 363 17.9 xip 364 17.10 fwh 364 17.11 调试 367 17.12 查看源代码 367 第18章 嵌入式linux 369 18.1 挑战 369 18.2 元器件选择 370 18.3 工具链 371 18.4 bootloader 372 18.5 内存布局 374 18.6 内核移植 375 18.7 嵌入式驱动程序 376 18.7.1 闪存 377 18.7.2 uart 377 18.7.3 按钮和滚轮 378 18.7.4 pcmcia/cf 378 18.7.5 sd/mmc 378 18.7.6 usb 378 18.7.7 rtc 378 18.7.8 音频 378 18.7.9 触摸屏 379 18.7.10 视频 379 18.7.11 cpld/fpga 379 18.7.12 连接性 379 18.7.13 专用领域电子器件 380 18.7.14 更多驱动程序 380 18.8 根文件系统 380 18.8.1 nfs挂载的根文件系统 381 18.8.2 紧凑型中间件 382 18.9 测试基础设施 383 18.10 调试 383 18.10.1 电路板返工 384 18.10.2 调试器 385 第19章 用户空间的驱动程序 386 19.1 进程调度和响应时间 387 19.1.1 原先的调度器 387 19.1.2 o(1)调度器 387 19.1.3 cfs 388 19.1.4 响应时间 388 19.2 访问i/o区域 390 19.3 访问内存区域 393 19.4 用户模式scsi 395 19.5 用户模式usb 397 19.6 用户模式i2c 400 19.7 uio 401 19.8 查看源代码 402 第20章 其他设备驱动程序 403 20.1 ecc报告 403 20.2 频率调整 407 20.3 嵌入式控制器 408 20.4 acpi 408 20.5 isa与mca 410 20.6 火线 410 20.7 智能输入/输出 411 20.8 业余无线电 411 20.9 voip 411 20.10 高速互联 412 20.10.1 infiniband 413 20.10.2 rapidio 413 20.10.3 光纤通道 413 20.10.4 iscsi 413 第21章 调试设备驱动程序 414 21.1 kdb 414 21.1.1 进入调试器 415 21.1.2 kdb 415 21.1.3 kgdb 417 21.1.4 gdb 420 21.1.5 jtag调试器 421 21.1.6 下载 423 21.2 内核探测器 423 21.2.1 kprobe 423 21.2.2 jprobe 427 21.2.3 返回探针 429 21.2.4 局限性 431 21.2.5 查看源代码 431 21.3 kexec与kdump 431 21.3.1 kexec 432 21.3.2 kdump与kexec协同工作 432 21.3.3 kdump 433 21.3.4 查看源代码 437 21.4 性能剖析 437 21.4.1 利用oprofile剖析内核性能 438 21.4.2 利用gprof剖析应用程序性能 440 21.5 跟踪 441 21.6 ltp 444 21.7 uml 444 21.8 诊断工具 444 21.9 内核修改配置选项 444 21.10 测试设备 445 第22章 维护与发布 446 22.1 代码风格 446 22.2 修改标记 446 22.3 版本控制 447 22.4 一致性检查 447 22.5 构建脚本 448 22.6 可移植代码 450 第23章 结束语 451 23.1 流程一览表 451 23.2 下一步该做什么 452 附录a linux汇编 453 附录b linux与bios 457 附录c seq文件 461
Linux系统中,USB驱动通常由内核模块提供。这些USB驱动程序负责与USB主机控制器和外部设备进行通信,并提供电源、数据传输和其他控制功能。 以下是编写Linux USB驱动程序的一般步骤: 1. 设计USB驱动程序的架构和功能。这包括确定驱动程序的名称、设备ID、功能和特性等。 2. 编写USB驱动程序的核心部分。这包括定义USB设备的数据结构、注册驱动程序、处理USB设备插入和移除事件等。 3. 实现USB设备的读写功能。这包括定义USB设备的读写函数、分配和释放USB缓冲区、处理USB传输等。 4. 测试USB驱动程序。这包括在Linux系统中编译和加载驱动程序、连接USB设备、执行读写操作等。 以下是一个简单的Linux USB驱动程序的代码示例: ```c #include <linux/module.h> #include <linux/usb.h> #include <linux/hid.h> MODULE_LICENSE("Dual BSD/GPL"); static struct usb_device_id my_usb_ids[] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) }, { } }; MODULE_DEVICE_TABLE(usb, my_usb_ids); static int my_probe(struct usb_interface *interface, const struct usb_device_id *id) { int err = 0; struct usb_device *dev = interface_to_usbdev(interface); /* Do something with the USB device */ ... return err; } static void my_disconnect(struct usb_interface *interface) { /* Do something when the USB device is disconnected */ ... return; } static struct usb_driver my_usb_driver = { .name = "my_usb_driver", .id_table = my_usb_ids, .probe = my_probe, .disconnect = my_disconnect, }; static int __init my_init(void) { int err = 0; /* Register the USB driver */ err = usb_register(&my_usb_driver); if (err) goto out; /* Do something else */ out: return err; } static void __exit my_exit(void) { /* Unregister the USB driver */ usb_deregister(&my_usb_driver); /* Do something else */ } module_init(my_init); module_exit(my_exit); ``` 在这个例程中,我们定义了一个USB设备ID,用于匹配要处理的USB设备。在 `my_probe()` 函数中,我们首先获取了USB设备的指针,然后在其中执行了一些操作。在 `my_disconnect()` 函数中,我们定义了设备被拔出时的操作。 在 `my_usb_driver` 结构体中,我们还定义了设备的名称、ID 表、探测函数和卸载函数等。在 `my_init()` 函数中,我们注册了这个驱动程序,而在 `my_exit()` 函数中则注销了它。 希望这个简单的示例能够帮助您了解如何编写Linux USB驱动程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值