浅析usbhid驱动如何源源不断的获取usb鼠标数据


浅析usbhid驱动如何源源不断的获取usb鼠标数据

hid_probe
==>usb_hid_configure
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, // 首先申请interrupt urb内存,并填充下面的有效数据
                                                              // 后面的hid_start_in()函数会usb_submit_urb提交该urb,到
                                                              // usb host控制器,进而发送interrupt in事物到hid设备[鼠标或键盘]
                    hid_irq_in, hid, interval);               // hid_irq_in为interrupt中断管道数据处理回调函数
                                                              // urb->complete = hid_irq_in;
                                                              // interval为usbhid driver需要每隔interval毫秒
                                                              // 产生一次in读取动作,这只是一个理论上的东西[luther.gliethttp]
                                                              // 实际上该interval数值,仅仅用来usb host管理interrupt类型总线带宽
                                                              // 时,作为调整系数之一而已,[luther.gliethttp]
                                                              // 真正通信是这样的,对该urb执行一次usb_submit_urb()操作,
                                                              // 那么usb host将等待interrupt数据返回,如果hid物理设备没有
                                                              // 向它的interrupt端点填入指定大小的数据,那么
                                                              // usb host将一直等待,直到hid物理设备将指定个数的数据填入
                                                              // 它的interrupt端点为止,于是usb host将触发中断,
                                                              // 通知usb_submit_urb提交的interrupt类型的urb有数据回来了,
                                                              // 同时该urb生命终结,如果不再执行usb_submit_urb提交动作,再次等待
                                                              // 下一次interrupt数据到来的话,那么usbhid.ko将只得到
                                                              // 一次数据,[luther.gliethttp]
                                                              // 于是hid_irq_in函数将被执行,幸运的是,
                                                              // hid_irq_in函数中确实又调用了usb_submit_urb,再次将
                                                              // 该urb添加usb host事件中,等待下一次hid设备产生数据上传,然后再次调用到这里hid_irq_in处理数据,
                                                              // 如果强行将hid_irq_in函数中的usb_submit_urb屏蔽掉,
                                                              // 我们可以通过kernel klog看到,鼠标数据只会产生一个[luther.gliethttp]
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->transfer_buffer,                      // 下面是截获的urb->transfer_buffer数据内容,对于我的mouse,每次都是4个字节:
                     urb->actual_length, 1);                    // [13602.612302] 00 fe 00 00
            break;                                              // [13602.868282] 01 00 00 00
        case -EPIPE:        /* stall */                         // [13602.964277] 00 00 00 00
            clear_bit(HID_IN_RUNNING, &usbhid->iofl);           // [13603.860290] 04 00 00 00
            set_bit(HID_CLEAR_HALT, &usbhid->iofl);             // [13604.052288] 00 00 00 00
            schedule_work(&usbhid->reset_work);                 // [13605.332295] 02 00 00 00
            return;                                             // [13605.460297] 00 00 00 00
        case -ECONNRESET:    /* unlink */                        // [13605.812292] 00 f9 01 00
        case -ENOENT:                                           // [13605.876280] 00 ff 00 00
        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);                   // 再次将该urb提交到usb host上,
    if (status) {                                               // 这样才能继续读取下一次鼠标数据[luther.gliethttp]
        clear_bit(HID_IN_RUNNING, &usbhid->iofl);               // 如果将status = usb_submit_urb(urb, GFP_ATOMIC);注释掉
        if (status != -EPERM) {                                 // 那么表示urb生命就真的终结在这次了,不会再读到mouse数据了.
            err_hid("can't resubmit intr, %s-%s/input%d, status %d", // 因为没有任何urb让usb host做读取mouse的interrupt管道[luther.gliethttp].
                    hid_to_usb_dev(hid)->bus->bus_name,
                    hid_to_usb_dev(hid)->devpath,
                    usbhid->ifnum, status);
            hid_io_error(hid);
        }
    }
}

那hid_irq_in什么时候被调用呢,来看看,对hid_irq_in的调用直接来自物理irq中断[luther.gliethttp]

drivers/usb/host/ohci-s3c2410.c|455| .urb_enqueue = ohci_urb_enqueue,
drivers/usb/host/ohci-ep93xx.c|132| .urb_enqueue = ohci_urb_enqueue
drivers/usb/host/ohci-at91.c|250| .urb_enqueue = ohci_urb_enqueue,
static const struct hc_driver ohci_at91_hc_driver = {
    .description =        hcd_name,
    .product_desc =        "AT91 OHCI",
    .hcd_priv_size =    sizeof(struct ohci_hcd),

    /*
     * generic hardware linkage
     */
    .irq =            ohci_irq,
    .flags =        HCD_USB11 | HCD_MEMORY,

    /*
     * basic lifecycle operations
     */
    .start =        ohci_at91_start,
    .stop =            ohci_stop,
    .shutdown =        ohci_shutdown,

    /*
     * managing i/o requests and associated device resources
     */
    .urb_enqueue =        ohci_urb_enqueue,
    .urb_dequeue =        ohci_urb_dequeue,
    .endpoint_disable =    ohci_endpoint_disable,

    /*
     * scheduling support
     */
    .get_frame_number =    ohci_get_frame,

    /*
     * root hub support
     */
    .hub_status_data =    ohci_hub_status_data,
    .hub_control =        ohci_hub_control,
    .hub_irq_enable =    ohci_rhsc_enable,
#ifdef CONFIG_PM
    .bus_suspend =        ohci_bus_suspend,
    .bus_resume =        ohci_bus_resume,
#endif
    .start_port_reset =    ohci_start_port_reset,
};

static struct platform_driver ohci_hcd_at91_driver = {
    .probe        = ohci_hcd_at91_drv_probe,
    .remove        = ohci_hcd_at91_drv_remove,
    .shutdown    = usb_hcd_platform_shutdown,
    .suspend    = ohci_hcd_at91_drv_suspend,
    .resume        = ohci_hcd_at91_drv_resume,
    .driver        = {
        .name    = "at91_ohci",
        .owner    = THIS_MODULE,
    },
};
ohci_hcd_at91_drv_probe
==> usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev);
==*> usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);          // pdev->resource[1].start等于irqnum中断号[luther.gliethttp]
==**> request_irq(irqnum, &usb_hcd_irq, irqflags, hcd->irq_descr, hcd)  // 注册物理中断处理函数usb_hcd_irq

所以当usb host有数据或者异常时就会产生物理irq中断,随后kernel调用到usb_hcd_irq中断处理函数
usb_hcd_irq
==> hcd->driver->irq (hcd);即ohci_irq
==> ohci_irq
==*> dl_done_list (ohci);
==**> takeback_td(ohci, td);
==***> finish_urb(ohci, urb, status); // 如果ed->td_list.next链表上没有任何控制管道,bulk等数据发送时,调用该函数[luther.gliethtt]
==****> usb_hcd_giveback_urb(ohci_to_hcd(ohci), urb, status);
==*****> urb->complete (urb);即hid_irq_in   // 调用回调函数, hid_irq_in会调用usb_submit_urb(urb, GFP_ATOMIC);
                                            // 再次让usb host等待hid硬件设备的interrupt数据到来.[luther.gliethttp]
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值