HID设备的研究总结
HID设备的研究总结
闭关的这段时间里主要完成了公司的一个项目的收尾与交接,是涉及C++、C#的一些知识点。同时最近刚结束了HID设备的研发,结果是该项目最终放弃了。。。还是蛮遗憾的。作为这方面的研发小白,整理一下其中的心血历程是十分重要的,帮自己也是帮别人。
设备的定义以及上位机与下位机的开发、相关参数的定义这里就不说了,百度一堆的东西(看都看不过来)。这里主要介绍我在开发过程中遇到的问题(我觉得对于新手应该是重点,哈哈哈),涉及到上下位之间的通信,这里用的是Windows系统和HID设备(鼠标、键盘、触摸屏等)具体设备这里就不明说了。
-
Linux系统上的开发
作为这方面的小白,拿到设备只能按照给的配套软件进行使用,由于这样是不符合我们当前的开发需求,热血青年也是有事不求于他人,就自己捣鼓起来了。基于我之前利用Linux系统开发过usb相机,所以抱着同样的心理去尝试了一下,结果证明我是对的,设备接入之后会在 /proc/bus/input/devices 目录下会有相应的设备出现。
找到相应的 Handlers 下的eventX(X代表数字,具体看情况),使用代码成功获取了设备返回的数据。
int fdss=-1;
fd_set rds;
int ret;
struct input_event event;
struct timeval time;
fdss = open("/dev/input/eventX", O_RDONLY);
if(fdss<0){
LOGE("Cannot open eventX: %d, %s", errno, strerror (errno));
return -1;
}
while ( 1 ){
FD_ZERO( &rds );
FD_SET( fdss, &rds );
/*调用select检查是否能够从/dev/input/eventX设备读取数据*/
ret = select( fdss + 1, &rds, &rds, NULL, NULL );
if ( ret < 0 ){
LOGE( "select ret error" );
return -1;
}
/*能够读取到数据*/
else if ( FD_ISSET( fdss, &rds ) )
{
LOGE( "START read" );
ret = read( fdss, &event, sizeof(struct input_event) );
time = event.time;
LOGE( "type=%d code=%d value=%d\n", event.type, event.code, event.value );
}
}
close(fdss);
这里的代码与我之前的开发USB相机的代码相似,具体可以去哪里借鉴。于是就更加大胆的猜想出Windows应该也是可以的。
- Windows系统的开发初期
使用的是hidsdi相关的库,说是要安装DDK,实验证明不需要安装,引入相应的头文件的lib文件就可以了。具体的过程是根据vendorID和productID找的设备(推荐一个工具USBDeview.exe 可以直接参看设备的这两个参数,当然不止这两个参数可以看。)
之后调用 _hidDevice.HidDevice =CreateFile(detailData->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
得到类似一个设备句柄,后期所有的读写就是根据这个句柄。
读设备的返回信息 result=ReadFile(_hidDevice.HidDevice, &_inputReportBuffer,_hidDevice.Caps.InputReportByteLength,&numberOfBytesRead,(LPOVERLAPPED)&_hIDOverlapped);
读的方式比较简单,主要关心一下参数的设置要和上一步的获取句柄的参数保持一致,否则会报87的错误,另外还有几个错误信息,我没有遇到(上天眷顾吧,做到这里其中已经是焦头烂额了)。其实后来才发现读出来的数据不是我们需要的,或者说这些数据是设备输出的,我们只能默默接受却不能做些改变,而且数据是以数组的形式返回的,一堆数据抛给你也不清楚数据代表的具体意思(关键我同事看数据竟然猜的八九不离十)。所以打算写数据来弥补不足(现在想想真的天真)。
虽然写操作有相应的函数直接调用,可惜的一直返回-1,GetLasterror返回的错误码对应的是参数有问题,网上找资料也是说数组的第一个需要时reportID(根本不知道是啥,也不知道接下来如何走了)。
- 中期 Bus Hound工具的使用
这里不得不介绍一下这个工具,简直不要太好用。山穷水尽疑无路,柳暗花明又一村。通过这个软件理清楚了许多东西,主要是设备的所有操作都是通过命令来的,这些命令是hid设备通用的,只是不同的设备会有点不一样,传输的数据是上下位机开发者自己协商好的。简单说一下怎么使用吧。第一次打开软件需要点Setting设置一下,主要设置左上角的Length 和勾选右侧栏需要输出显示那些信息。长度是为了保证输出的信息是完整的,如果小了输出的信息会不完整,一般设置64,设置大了也没事,会自动处理的。接下来就是把自己的设备挂载到上面,类似你要监听哪一个设备。点击Devices进入,列举了所有的设备,把你当前的USB输入设备勾上即可。配合上文提到的两个ID找到,你会发现可能不止一个设备,这是因为这个设备具有多个子项。实在没发区分的话,就一个个单独尝试,勾选之后在Capture下点击RUN,然后看设备的输出的信息是否符合你的要求来确定到底是哪一个设备。选好设备后回到Devices下,勾上找到的设备,点击Send Commands给设备发送命令,这也证明了上下位机之间的交互是通过命令。会进入一个小窗口,这里具体的细节不多介绍可以查询百度,主要说一下重点吧。上面的菜单一般是不要选择的默认就是USB,在中间第一个框内Endpoint(端口),Type,比较重要。端口是后续代码进入的一个连接点。type主要是设备支持的输入输出方式,一般有Control(控制支持输入和输出)和Interrupt(中断仅支持输入或者输出);在往下就是命令输入框了,一般八个字节,根据输入的不同信息后面会显示不同的提示语,这里主要用到SET REPORT和GET REPORT。最下面就是输入框了,比如需要写入数据就在下面写上具体的数据即可。
- 中期 设备的命令详情获取
命令这些都是固定的,不像数据是由开发人员定义的。为了后期代码可以操作设备(写数据到设备)必须先理清命令有哪些(重要的是命令里面就有相应的REPORTID,一般你会发现执行命令之后输出的数组第一个数据一致都是不变的)。Bus Hound的监听开启,操作一些设备,会有数据输出
Device 代表是设备的哪一个端口输出的,这个端口也是后期代码实现的接口;
Phase 上文介绍的是control还是Interrupt的方式输出或者输入
Data 输入或者输出的具体数据
后面的就不怎么重要了。
在Data里面的第一句就是命令:
a1 01 55 03 03 00 40 00(是我这边设备的GET REPORT命令,用上文的Send Commands填上这个命令点击RUN在Capture界面会出现数据哦,具体就不用多说了,看了就明白。如果没有效果可能是你的设备的命令需要按照顺序来,不能从中间截取任意一个来执行),得到这些命令之后就可以使用代码来实现了(代码模拟命令的输入输出,实现控制设备)。可是使用的上面的代码函数根本没有所谓的参数可以设置,把命令写入数组中也无济于事,无奈之好找寻新的方法。
- 后期 libusb库的使用
最终还是找到了libusb,和hidsid一样引入头文件和库文件即可。这个使用比较的简单,这里按照函数的调用顺序介绍: handle = libusb_open_device_with_vid_pid(ctx, VID, PID);
还是通过两个ID来获取句柄
libusb_kernel_driver_active(handle, 3)
参数3对应的是上文提到的端口,一定要设置对,不然会有问题(我这里很奇怪,端口在Bus Hound里是0,我这里代码需要设置3,我是0到4都设置一遍才找到这个3可以)这里是看当前内核驱动程序是否已连接,如果是的话就调用 libusb_detach_kernel_driver(handle, 3)
来关闭,不然不用调用
libusb_claim_interface(handle, 3);//0 - 4
连接设备
libusb_control_transfer(handle, 0x21, 0x09, 0x0355, 0x0003, setReport01, 0x40, 0x00);
对应的还有其他的输入输出方式(可选中该函数按F12进入libusb.h查看),我的设备只支持control方式的输入(写数据到设备,看上文提到的type),需要注意以下几点:
Bus Hound的写入命令是21 09 55 03 03 00 40 00,代码里面 55 03 和 03 00 需要调位置输入;
命令都是16进制;
中间的数据大小必须是0x40(根据实际情况)的大小;
如果是输入(写数据)数组需要写上相应的数据(一般来说前八个数据或者更多都是固定的,这是开发人员自己的设置的);
如果是输出(读数据)数组大小合适即可。
libusb_release_interface(handle, 3);
关闭端口
libusb_close(handle);
关闭设备句柄
综上代码的对设备的控制就结束了。
-
总结
开发总结:人体输入学设备的研发(hid设备)经历的时间是漫长的,中途遇到了许许多多的问题,现在发现这些问题都不是什么大事,都是可以解决的,关键在于坚持和对方法的探索;虽然项目最终还是以失败告终了,但学到的知识,技能是无价的,整理一下方便以后可以快速拿起来以及帮助一下开发hid的新手,希望不要走弯路。
人生总结:距上一次写博客已经过去很长一段时间了,也不打算找借口,借此机会好好反省一下。明天开始新的项目。
这里的介绍全是自己的血泪史,现在看也不怎么轰轰烈烈,找寻方法的时候真的是焦头烂额。具体的代码、库、工具没有放上来,有兴趣的小伙伴可以私信,我会给你想要的全部哦。哈哈哈,经验之谈,不喜勿喷!我是Mr.小艾。