700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux设备驱动之HID驱动 源码分析

Linux设备驱动之HID驱动 源码分析

时间:2019-02-24 02:55:26

相关推荐

Linux设备驱动之HID驱动 源码分析

转载地址:/kernel-style/p/3208428.html HID是Human Interface Devices的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID设备.常见的HID设备有鼠标键盘,游戏操纵杆等等.在接下来的代码分析中,可以参考HID的spec.这份spec可以在上找到.分析的代码主要集中在linux-2.6.25/drivers/hid目录下. 对此设备结点的处理有两种接口,一种是read(),另一种是ioctl(); read(): This is the event interface. When the HID device performs an interrupt transfer, indicating a change of state, data will be made available at the associated hiddev device with the content of a struct hiddev_event:struct hiddev_event { unsigned hid; signed int value; };containing the HID usage identifier for the status that changed, and the value that it was changed to.ioctl(): This is the control interface. There are a number of controls: //利用libusb 实现的hid 读写 /topics/370027825 写入用usb_interrupt_write 读取用usb_interrupt_read /acf/article/details/5431488 在Linux 2.6环境下读写HID设备(USB Key)

Linux 2.6内核中包含了HID驱动,能够自动把USB Key等HID外设识别成“/dev/hiddev0”之类的设备。但是该驱动没有实现write接口,因此无法象Windows平台那样使用 ReadFile和WriteFile来读写HID设备,而只能使用ioctl接口。

网上有各种各样读写HID设备的源代码例子,有的是通过HIDIOCSUSAGE和HIDIOCGUSAGE来每次收发4个字节,适合鼠标、键盘之类数据传输量小的设备;有的是通过HIDIOCSUSAGES和HIDIOCGUSAGES来连续接收和发送多个字节,适合USB Key一类的设备。

在上一篇日志(已删除)中,介绍了如何利用《USB and PIC: quick guide to an USB HID framework》一文提供的方法与USB Key进行通信(先发送HIDIOCSUSAGES和HIDIOCSREPORT进行写操作,再发送HIDIOCGREPORT和HIDIOCGUSAGES进行读操作,从而完成一次通信过程)。但是经过好友测试,发现该方法不论是在PC机上,还是在Cavium Octeon 52XX开发板上均存在问题,读出的数据始终是第一次通信的结果,除非在每次通信之前都发送HIDIOCINITREPORT控制码,但这又会造成相当长时间的阻塞。

进一步的测试表明,如果按照HIDIOCGUCODE、HIDIOCSUSAGES、HIDIOCSREPORT、HIDIOCGUCODE、HIDIOCGUSAGES的顺序发送控制码,那么可以每次都读出正确数据。不过该方法虽然在PC机上只需400毫秒延时,但是在Octeon开发板上仍会长时间阻塞在usbhid_wait_io函数那里。

无奈之下,我只好根据Cavium SDK自带的Linux内核源码中的usb_skeleton.c写了一个USB设备驱动程序,试图通过直接读写USB端点来完成通信过程。以下是在开发和调试过程中需要注意的几个问题:

首先,必须卸载Linux内核自带的HID驱动,否则它会自动“接管”新插入的USB Key,导致我们自己编写的驱动程序找不到设备。对于开发板,可以在编译内核时去掉HID相关的选项;对于PC机上已经安装好的Linux,我也不知道该怎么卸载其中的HID驱动。

其次,端点类型。在usb_skeleton.c中是通过bulk端点来访问USB设备的,而USB Key作为HID设备,一般只有0号控制端点和一个中断输入端点(例如3号)。对于中断端点,可以用usb_interrupt_msg(其实就是usb_bulk_msg)函数进行访问;对于控制端点,则稍微麻烦一些,因为除了数据,还需要构造一个8字节的setup包。有关setup包的详细结构,可以参考USB和HID规范。获取setup包具体数值最简单的方法,就是在Windows环境下用BusHound观察USB Key的通信过程。

最后,关于Report ID。在Windows环境下通过ReadFile和WriteFile访问HID设备时,必须在数据开头附加1字节的Report ID(一般为0)。在Linux环境下,如果使用HID驱动的ioctl接口,那么需要在hiddev_usage_ref结构中指定Report ID;如果使用自己编写的USB驱动程序,则不需要考虑Report ID,直接发送数据就得了。

经过测试,利用自己编写的驱动程序,可以在Octeon开发板上正确读写HID类型的USB Key,而且读写之间的时间间隔也可以缩短至50毫秒。

从内核2.6.34 的代码来看,/dev/hidraw0只能操作 endpoint 0,即通用的控制通道,强行向其写入数据会提示

write: Broken pipe

在内核 2.6.35以后进行了修正, 参考如下:

/patch/99990/

点击(此处)折叠或打开

diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.cindex 56d06cd..6fd833d 100644--- a/drivers/hid/usbhid/hid-core.c+++ b/drivers/hid/usbhid/hid-core.cstatic int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,unsigned char report_type){struct usbhid_device *usbhid = hid->driver_data;struct usb_device *dev = hid_to_usb_dev(hid);struct usb_interface *intf = usbhid->intf;struct usb_host_interface *interface = intf->cur_altsetting;int ret;- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),- HID_REQ_SET_REPORT,- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,- ((report_type + 1) << 8) | *buf,- interface->desc.bInterfaceNumber, buf + 1, count - 1,- USB_CTRL_SET_TIMEOUT);-- /* count also the report id */- if (ret > 0)- ret++;+ if (usbhid->urbout) {+ int actual_length;+ int skipped_report_id = 0;+ if (buf[0] == 0x0) {+ /* Don't send the Report ID */+ buf++;+ count--;+ skipped_report_id = 1;+ }+ ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,+ buf, count, &actual_length,+ USB_CTRL_SET_TIMEOUT);+ /* return the number of bytes transferred */+ if (ret == 0) {+ ret = actual_length;+ /* count also the report id */+ if (skipped_report_id)+ ret++;+ }+ } else {+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),+ HID_REQ_SET_REPORT,+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,+ ((report_type + 1) << 8) | *buf,+ interface->desc.bInterfaceNumber, buf + 1, count - 1,+ USB_CTRL_SET_TIMEOUT);+ /* count also the report id */+ if (ret > 0)+ ret++;+ }return ret;} hid-example.c,来演示如何操作 /~zumbi/kernel/linux-2.6-3.0.0~rc1/samples/hidraw/hid-example.c

点击(此处)折叠或打开

/**Hidraw Userspace Example**Copyright(c) Alan Ott<alan@signal11.us>*Copyright(c) Signal 11 Software**The code may be used by anyoneforany purpose,*andcan serve as a starting pointfordeveloping*applications using hidraw.*//*Linux*/#include<linux/types.h>#include<linux/input.h>#include<linux/hidraw.h>/**Ugly hacktowork around failing compilationonsystems that don't*yet populate new version of hidraw.htouserspace.**Ifyou need this,please have your distro update the kernel headers.*/#ifndef HIDIOCSFEATURE#define HIDIOCSFEATURE(len)_IOC(_IOC_WRITE|_IOC_READ,'H',0x06,len)#define HIDIOCGFEATURE(len)_IOC(_IOC_WRITE|_IOC_READ,'H',0x07,len)#endif/*Unix*/#include<sys/ioctl.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>/*C*/#include<stdio.h>#include<string.h>#include<stdlib.h>#include<errno.h>constchar*bus_str(intbus);intmain(intargc,char**argv){intfd;inti,res,desc_size=0;char buf[256];struct hidraw_report_descriptor rpt_desc;struct hidraw_devinfo info;/*Open the Device with non-blocking reads.Inreal life, don't use a hard coded path;use libudev instead.*/fd=open("/dev/hidraw0",O_RDWR|O_NONBLOCK);if(fd<0){perror("Unable to open device");return 1;}memset(&rpt_desc,0x0,sizeof(rpt_desc));memset(&info,0x0,sizeof(info));memset(buf,0x0,sizeof(buf));/*GetReport Descriptor Size*/res=ioctl(fd,HIDIOCGRDESCSIZE,&desc_size);if(res<0)perror("HIDIOCGRDESCSIZE");elseprintf("Report Descriptor Size: %d\n",desc_size);/*GetReport Descriptor*/rpt_desc.size=desc_size;res=ioctl(fd,HIDIOCGRDESC,&rpt_desc);if(res<0){perror("HIDIOCGRDESC");}else{printf("Report Descriptor:\n");for(i=0;i<rpt_desc.size;i++)printf("%hhx ",rpt_desc.value[i]);puts("\n");}/*GetRaw Name*/res=ioctl(fd,HIDIOCGRAWNAME(256),buf);if(res<0)perror("HIDIOCGRAWNAME");elseprintf("Raw Name: %s\n",buf);/*GetPhysicalLocation*/res=ioctl(fd,HIDIOCGRAWPHYS(256),buf);if(res<0)perror("HIDIOCGRAWPHYS");elseprintf("Raw Phys: %s\n",buf);/*GetRaw Info*/res=ioctl(fd,HIDIOCGRAWINFO,&info);if(res<0){perror("HIDIOCGRAWINFO");}else{printf("Raw Info:\n");printf("\tbustype: %d (%s)\n",info.bustype,bus_str(info.bustype));printf("\tvendor: 0x%04hx\n",info.vendor);printf("\tproduct: 0x%04hx\n",info.product);}/*SetFeature*/buf[0]=0x9;/*Report Number*/buf[1]=0xff;buf[2]=0xff;buf[3]=0xff;res=ioctl(fd,HIDIOCSFEATURE(4),buf);if(res<0)perror("HIDIOCSFEATURE");elseprintf("ioctl HIDIOCGFEATURE returned: %d\n",res);/*GetFeature*/buf[0]=0x9;/*Report Number*/res=ioctl(fd,HIDIOCGFEATURE(256),buf);if(res<0){perror("HIDIOCGFEATURE");}else{printf("ioctl HIDIOCGFEATURE returned: %d\n",res);printf("Report data (not containing the report number):\n\t");for(i=0;i<res;i++)printf("%hhx ",buf[i]);puts("\n");}/*Send a Reporttothe Device*/buf[0]=0x1;/*Report Number*/buf[1]=0x77;res=write(fd,buf,2);if(res<0){printf("Error: %d\n",errno);perror("write");}else{printf("write() wrote %d bytes\n",res);}/*Geta report from the device*/res=read(fd,buf,16);if(res<0){perror("read");}else{printf("read() read %d bytes:\n\t",res);for(i=0;i<res;i++)printf("%hhx ",buf[i]);puts("\n");}close(fd);return 0;}constchar*bus_str(intbus){switch(bus){caseBUS_USB:return"USB";break;caseBUS_HIL:return"HIL";break;caseBUS_BLUETOOTH:return"Bluetooth";break;caseBUS_VIRTUAL:return"Virtual";break;default:return"Other";break;}}//----- openwrt 下安装和调试 root@OpenWrt:~# opkg install kmod-usb-hid --force-depends Installing kmod-usb-hid (3.3.8-1) to root... Downloading /snapshots/trunk/ar71xx/packages/kmod-usb-hid_3.3.8-1_ar71xx.ipk. Installing kmod-hid (3.3.8-1) to root... Downloading /snapshots/trunk/ar71xx/packages/kmod-hid_3.3.8-1_ar71xx.ipk. Installing kmod-input-evdev (3.3.8-1) to root... Downloading /snapshots/trunk/ar71xx/packages/kmod-input-evdev_3.3.8-1_ar71xx.ipk. Configuring kmod-input-evdev. Configuring kmod-hid. Configuring kmod-usb-hid. Collected errors: * satisfy_dependencies_for: Cannot satisfy the following dependencies for kmod-usb-hid: * kernel (= 3.3.8-1-231112e548aba152810eababd16134bc) * kernel (= 3.3.8-1-231112e548aba152810eababd16134bc) * kernel (= 3.3.8-1-231112e548aba152810eababd16134bc) * root@OpenWrt:~# lsmod | grep hid usbhid 20688 0 [permanent] hid64032 1 usbhid,[permanent] usbcore 99168 19 usbhid,uvcvideo,gspca_zc3xx,gspca_main,ums_usbat,ums_sddr55,ums_sddr09,ums_karma,ums_jumpshot,ums_isd200,ums_freecom,ums_datafab,ums_cypress,ums_alauda,usb_storage,uhci_hcd,ohci_hcd,ehci_hcd input_core 20016 7 usbhid,hid,evdev,uvcvideo,gspca_zc3xx,gspca_main # 插上usb hub, 无法发现hid设备(原因: hub的红线为电源线, 白线为数据线) root@OpenWrt:~# dmesg | grep hid [ 12.000000] usbcore: registered new interface driver usbhid [ 12.010000] usbhid: USB HID core driver # 无hub, 直接接上hid设备, 可以找到设备 root@OpenWrt:~# dmesg | grep hid [ 12.000000] usbcore: registered new interface driver usbhid [ 12.010000] usbhid: USB HID core driver [ 185.290000] generic-usb 0003:0483:D0D0.0001: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1 # hid设备标识符, 不是 标准的 /dev/hiddev0 root@OpenWrt:~# ls /dev/hid* ls: /dev/hid*: No such file or directory root@OpenWrt:~# ls -al /dev/usb/hid* crw-r--r-- 1 root root 180, 96 Jan 1 00:03 /dev/usb/hiddev0 # 去除设备, 无标识符. root@OpenWrt:~# ls -al /dev/usb/hid* ls: /dev/usb/hid*: No such file or directory # 插拔3次, root@OpenWrt:~# dmesg | grep hid [ 12.000000] usbcore: registered new interface driver usbhid [ 12.010000] usbhid: USB HID core driver [ 185.290000] generic-usb 0003:0483:D0D0.0001: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1 [ 1034.170000] generic-usb 0003:0483:D0D0.0002: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1 [ 1085.970000] generic-usb 0003:0483:D0D0.0003: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1root@OpenWrt:~# find / -name hid* /dev/usb/hiddev0 /lib/modules/3.3.8/hid.ko /overlay/lib/modules/3.3.8/hid.ko /sys/devices/platform/ehci-platform/usb1/1-1/1-1:1.1/usb/hiddev0 /sys/bus/hid /sys/class/usb/hiddev0 /sys/kernel/debug/hid /sys/module/input_core/holders/hid /sys/module/hid /sys/module/usbhid/drivers/hid:generic-usb ----------------------------------------------------------------- root@OpenWrt:/xutest# opkg install hid_xu1_ar71xx.ipk Installing hid (xu1) to root... Configuring hid. root@OpenWrt:/xutest# hidtest /dev/hiddev0 information: HIDIOCGVERSION: 1.4 HIDIOCGDEVINFO: bustype=3 busnum=1 devnum=4 ifnum=1 vendor=0x0483 product=0xd0d0 version=0x0200 num_applications=1 HIDIOCGNAME: STMicroelectronics CR95HF Reports of type Input (1): Report id: 7 (1 fields) Field: 0: app: 8c0001 phys 0000 flags 2 (63 usages) unit 0 exp 0 Reports of type Output (2): Report id: 1 (1 fields) Field: 0: app: 8c0001 phys 0000 flags 2 (63 usages) unit 0 exp 0 Report id: 2 (1 fields) Field: 0: app: 8c0001 phys 0000 flags 2 (63 usages) unit 0 exp 0 Reports of type Feature (3): Waiting for events ... (interrupt to exit) ^C root@OpenWrt:/xutest#

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。