对usbhid_start函数解析学习

   最近在学习linux下HID设备操作,根据一些牛人的一些解析学习hid驱动,下面是自己学习的一点总结,主要针对hid-core.c

中的usbhid_start函数,自己理解的不是很深,交流学习,多多指教,谢谢!!
下面我们开始usbhid_start函数的分析。
usbhid->bufsize = HID_MIN_BUFFER_SIZE;
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;
 
hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
 
if (insize > HID_MAX_BUFFER_SIZE)
              insize = HID_MAX_BUFFER_SIZE;
    在usbhid_start函数中hid_find_max_report函数会调用list_for_each_entry(for循环的宏)来遍历报告描述,
对于同种类型,不同report_id的report都会链接在对应类型的hid_device->report_enum[ ] ->report_list.该函数
就是遍历这个链表,取得最大的report size.
    在usbhid_start中会对输入(INPUT_REPORT)、输出(OUT_REPORT)、特征(FEATURE_REPORT)这三种报告取最大的
report size。之后会取输入报告大最大的report size,然后判断此report size是否大于最大缓冲区HID_MAX_BUFFER_SIZE(4Kb),
超出缓冲区,将report size设置为最大缓冲区长度。
之后会调用hid_alloc_buffers()为hid的urb传输初始化传输缓冲区
注意:
1、report->size这里存放的大小并不是以字节计数,而是位计算的
2、在INPUT类型,并有多个report_id的情况,size会加1的原因:
    在有多个report_id的情况下,input的数据最前面有一个字节会表示它的report_id
 
endpoint = &interface->endpoint[n].desc;
if (!usb_endpoint_xfer_int(endpoint))
        continue;
//检测是否为中断传输端点
interval = endpoint->bInterval;
    检测完报告长度后开始对每个中断端点进行检测,usb_endpoint_xfer_int函数用来检测端点(endpoint)是否
为中断传输端点。
    如果是中断端点则返回true,否则返回flase。类似的usb_endpoint_xfer_isoc判断端点是否为同时传输类型,
usb_endpoint_xfer_bulk
   判断端点是否为批量传输类型,usb_endpoint_xfer_control判断是否为控制传输端点。
 
/* Some vendors give fullspeed interval on highspeed devides */
if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
               dev->speed == USB_SPEED_HIGH)
{
              interval = fls(endpoint->bInterval*8);
              printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
                            hid->name, endpoint->bInterval, interval);
}
检测到端点是中断端点后检查一些特殊的高速设备用全速间隔时间,然后修正全速间隔为高速间隔。
 
 
/* 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;
       }
   在这调用usb_endpoint_dir_in判断这个中断端点是什么方向,确定了方向后检查urb然后创建并初始化一个新的urb,
创建管道,填充初始化urb,使用usb_fill_int_urb函数对urb填充和初始化,其中参数hid_irq_in或hid_irq_out
为回调函数,interval参数为轮询间隔时间。
 
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
if (!usbhid->urbctrl) {
   ret = -ENOMEM;
   goto fail;
}
usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
                    usbhid->ctrlbuf, 1, hid_ctrl, hid);
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
   初始化完了中断端点后开始初始化控制端点,并且填充初始化,设置控制端点传输的dma地址和标志位。
 
if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
     usbhid_init_reports(hid);
   上面函数是检测hid->quirks & HID_QUIRK_NO_INIT_REPORTS值,如果为0则会执行usbhid_init_reports函数
HID_QUIRK_NO_INIT_REPORTS宏会在usbhid_probe(很重要的)函数中调用usbhid_lookup_quirk函数设置,
hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product);usbhid_lookup_quirk函数的具体实现如下:
u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
{
    u32 quirks = 0;
    const struct hid_blacklist *bl_entry = NULL;
 
    /* NCR devices must not be queried for reports */
      if (idVendor == USB_VENDOR_ID_NCR &&
              idProduct >= USB_DEVICE_ID_NCR_FIRST &&
             idProduct <= USB_DEVICE_ID_NCR_LAST)
                        return HID_QUIRK_NO_INIT_REPORTS;
 
    down_read(&dquirks_rwsem);
    bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
    if (!bl_entry)
        bl_entry = usbhid_exists_squirk(idVendor, idProduct);
    if (bl_entry)
        quirks = bl_entry->quirks;
    up_read(&dquirks_rwsem);
   return quirks;
}
usbhid_lookup_quirk函数中黑体字体是重点,
 
   初始化报告函数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
  
    usbhid_submit_report中调用__usbhid_submit_report函数
在__usbhid_submit_report函数中
if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
              return;
    如果有HID的鼠标键盘,到这就结束,HID_QUIRK_NOGET标志是在usbhid_parse函数判断设置代码如下:
/* Many keyboards and mice don't like to be polled for reports,
* so we will always set the HID_QUIRK_NOGET flag for them. */
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;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值