『壹』 求助,usb hid鼠标上位机程序的编写问题
USBHID是HumanInterfaceDevice的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏杆等。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。下列是HID类别设备的主要能力:1、交换的数据存储在称为报表(report)的结构内,设备的固件必须支持HID报表的格式。主机在控制与中断传输中传送与要求报表,来传送与接收数据。报表的格式非常有弹性,可以处理任何类别的数据。2、每一笔事务可以携带小量或中量的数据。低速设备每一笔事务最大是8个字节,全速设备每一笔事务最大是64个字节,高速设备每一笔事务最大是1024个字节。一个报表可以使用多笔事务。3、设备可以在未预期的时间传送信息给主机,例如键盘的按键或是鼠标的移动。所以主机会定时轮询设备,来取得最新的数据。下列是HID类别设备的主要限制:1、最大的传输速度有限,特别是低速与全速的时候。主机可以保证低速的中断端点,每10ms内不会有超过1笔事务,每一秒最多800个字节。主机可以保证全速端点,每1ms1笔事务,每一秒最多是6400个字节。主机可以保证高速端点,每125us3笔事务,每一秒最多24.576MB。2、没有保证的传输速率。如果设备是设置在10ms的时距,事务之间的时间可能等于或小于10ms。除非设备是设置在全速时在每个帧传输数据,或是在高速时在每个微帧传输数据。这是最快的轮询速率,所以断点可以保证有正确的带宽可供使用。3、Windows98Gold系统不支持中断输出传输,所有主机与设备的数据都必须使用控制传输。HID设备除了传送数据给主机外,它也会从主机接收数据。只要能够符合HID类别规范的设备都可以是HID设备,在规范内提到了条型码笔、温度计以及电压计等例子。这些设备虽然没有传统的人机接口,但是它们都可以传送数据给主机,也可以从主机接收配置的要求。设备除了HID接口之外,它可能同时还包含有其他的USB接口。例如影像显示设备可能使用HID接口来做亮度,对比,与更新率的软件控制,而使用传统的影像接口来传送要显示的数据。USB扩音器可以使用实时传输来播放语音,同时使用HID接口来控制音量,震荡,与低音等。HID接口通常比传统的控制接口来得便宜。HID类别设备的规范是。另外还有一份文件HIDUsabeTables,用来定义让主机了解以及使用HID数据的数值。这两份文件是由USBDeviceWorkingGroup所制定,Group的成员都是USB实施者论坛的会员。您可以在USB实施者论坛网站上下载这两份文件。地址:http://www.usb.org
『贰』 浅析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
schele_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,
.proct_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,
/*
* scheling 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]
『叁』 usbhid 端点0如何发送数据
usbhid端点0发送数据是分两次发送的。检查一个端点的发送是否结束有二种方法:
1、当发送结束(设备收到ACK)时,有一个发送结束中断,这个中断由USB库处理,并通过EP1_IN_Callback这个回调函数交由用户程序确认。
2、查询这个端点的状态,如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。使用下述调用可以得到端点1的发送状态:GetEPTxStatus(ENDP1)。
usbhid是Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏杆等。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。
交换的数据存储在称为报表(report)的结构内,设备的固件必须支持HID报表的格式。主机在控制与中断传输中传送与要求报表,来传送与接收数据。报表的格式非常有弹性,可以处理任何类别的数据。
设备除了HID接口之外,它可能同时还包含有其他的USB接口。例如影像显示设备可能使用HID接口来做亮度,对比,与更新率的软件控制,而使用传统的影 像接口来传送要显示的数据。USB扩音器可以使用实时传输来播放语音,同时使用HID接口来控制音量,震荡,与低音等。HID接口通常比传统的控制接口来得便宜。