浅析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]