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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析usbhid驱动如何源源不断的获取usb鼠标相关的知识,希望对你有一定的参考价值。

参考技术A 浅析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]
参考技术B 当然是不断的读取了。

请问Linux内核里,USB键盘和鼠标的驱动都是哪些文件?

我是想测试我的嵌入式设备是否能正常识别USB键盘鼠标,并正常识别键盘鼠标的按键,也就是说,我想在设备设别和按键识别的地方加上打印,使得检测结果能够直接在终端打印出来

usbhid和usbmouse.c都在/usr/src/linux/drivers/hid/usbhid目录下

USB 总线引出两个重要的链表!

一个 USB 总线引出两个重要的链表,一个为
USB 设备链表,一个为 USB 驱动链表。设备链表包含各种系统中的USB 设备以及这些设备的所有接口,驱动链表包含 USB 设备驱动程序(usb
device driver)和 USB 驱动程序(usb driver)。

USB 设备驱动程序(usb device driver)和 USB 驱动程序(usb driver)的区别是什么?

USB 设备驱动程序包含 USB 设备的一些通用特性,将与所有 USB 设备相匹配。在 USB core 定义了:struct usb_device_driver usb_generic_driver。usb_generic_driver 是 USB 子系统中唯一的一个设备驱动程序对象。而 USB 驱动程序则是与接口相匹配,接口是一个完成特定功能的端点的集合。
设备是如何添加到设备链表上去的?
在设备插入 USB 控制器之后,USB core 即会将设备在系统中注册,添加到 USB 设备链表上去。

USB 设备驱动程序(usb device driver)是如何添加到驱动链表上去的?
在系统启动注册 USB core 时,USB 设备驱动程序即将被注册,也就添加到驱动链表上去了。

接口是如何添加到设备链表上去的?
在 USB 设备驱动程序和 USB 设备的匹配之后,USB core 会对设备进行配置,分析设备的结构之后会将设备所有接口都添加到设备链表上去。比如鼠标设备中有一个接口,USB core 对鼠标设备配置后,会将这个接口添加到设备链表上去。

USB 驱动程序(usb driver)是如何添加到驱动链表上去的?
在每个 USB 驱动程序的被注册时,USB 驱动程序即会添加到驱动链表上去。
比如鼠标驱动程序,usb_mouse_init 函数将通过 usb_register(&usb_mouse_driver)
将鼠标驱动程序注册到 USB core 中,然后就添加到驱动链表中去了。其中 usb_mouse_driver 是描述鼠标驱动程序的结构体。

已配置状态(configured status)之后话
当鼠标的设备、接口都添加到设备链表,并且鼠标驱动程序也添加到驱动链表上去了,
系统就进入一种叫做已配置(configured)的状态。
要达到已配置状态,将经历复杂的过程,USB core 为 USB
设备奉献着无怨无悔。在这个过程中,系统将会建立起该设备的的设备、配置、接口、设置、端点的描述信息,它们分别被
usb_device、usb_configuration、usb_interface、usb_host_interface、
usb_host_endpoint 结构体描述。
设备达到已配置状态后,首先当然就要进行 USB 驱动程序和相应接口的配对,对于鼠标设备来说则是鼠标驱动程序和鼠标中的接口的配对。USB core 会调用usb_bus 总线的usb_device_match
函数,通过比较设备中的接口信息和 USB 驱动程序中的 id_table,来初步决定该 USB 驱动程序是不是跟相应接口相匹配。通过这一道关卡后,USB core 会认为这个设备应该由这个驱动程序负责。
然而,仅仅这一步是不够的,接着,将会调用 USB 驱动程序中的 probe 函数对相应接口进行进一步检查。如果该驱动程序确实适合设备接口,对设备做一些初始化工作,分配 urb 准备数据传输。
当 鼠标设备在用户空间打开时,
将提交 probe 函数构建的 urb 请求块,urb 将开始为传送数据而忙碌了。urb 请求块就像一个装东西的“袋子”,USB
驱动程序把“空袋子”提交给 USB core,然后再交给主控制器,主控制器把数据放入这个“袋子”后再将装满数据的“袋子”通过 USB core
交还给
USB 驱动程序,这样一次数据传输就完成了。
参考技术A lspci可以看你的usb情况,lsmod 查查加载的模块,应该是usb什么什么的,
驱动应该是在/lib/modules/内核版本/kernel/drives/usb下,
当然另外还要mousedev模块吧。
我只是提供个思路,也没有试过安装本回答被提问者和网友采纳
参考技术B USB鼠标驱动在内核位置:kernel2.6.35.11/driver/hid/usbhid/usbmouse.c
USB键盘驱动在内核位置:kernel2.6.35.11/driver/hid/usbhid/usbkbd.c

以上是关于浅析usbhid驱动如何源源不断的获取usb鼠标的主要内容,如果未能解决你的问题,请参考以下文章

USB驱动之常规usb鼠标驱动

usb键鼠驱动分析钻

Linux 下wifi 驱动开发—— USB接口WiFi驱动浅析

USB HID描述符

USB HID 设备检测

驱动TP电容屏驱动—3.TP报点协议A/B