一、HIDRAW overview
HIDRAW -原生的USB 和Bluetooth 人类交互设备
==================================================================
hidraw驱动提供了原生的接口为USB 和Bluetooth 人类交互设备。它与hiddev的不同之处在于,发送和接收的报表没有被hiddev解析器解析,而是从未修改的设备中发送到和接收到的。
如果用户空间应用程序知道怎么恰当和硬件设备通信,和能够手动构建HID 报表,那么hidraw应该被使用。这通常是在用户控件驱动自定义HID 设备的时候。
Hidraw与不符合规范的HID 设备通信也是有利 的,这些设备以一种不符合报表描述符不一致的方式发送和接收数据。因为Hiddev解析器通过他发送和接收报表,检测设备的报表描述符,这样的通信是不可能使用hiddev。Hidraw是唯一的选择,为这些不兼容的设备编写一个定制的内核驱动程序。
Hidraw一个好处是用户空间应用程序使用独立的底层硬件类型。当前,hidraw是通过bluetooth 和 usb实现。在将来,随着硬件总线的发展,hidraw将支持更多的类型。
二、The HIDRAW API
read()
========
read() 将读取从HID 设备接收的报表队列。在USB 设备,使用 read() 读取从设备在 INTERRUPT IN 终端发送的报表。默认的,read()是堵塞的,直到有报表数据可读。read()也可以是非阻塞的,通过在open()参数中设置O_NONBLOCK 标识,或者使用fcntl()设置ononblock标志来进行非阻塞
在使用报表编号的设备上,返回的数据的第一个字节将是报表 ID;报告数据如下,报表数据从第二个字节开始。对于不使用报表编号的设备,报表数据将从第一个byte开始
write()
========
write()函数写入报表到设备。例如USB设备,如果这个设备有一个INTERRUPT OUT 终端,报表将发送到该终端,如果没有,报表将被发送到控制端点,使用SET_REPORT传递
这个传入write()函数的buffer的第一个字节应该是报表id,如果这个设备不使用报表号,那么第一个字节应该设置成0,报表自身应该从第二个字节开始。
ioctl()
=======
Hidraw支持以下ioctls:
HIDIOCGRDESCSIZE:得到报表描述符大小
这个ioctl将得到设备的报表描述符大小
HIDIOCGRDESC:得到报表描述符
这个ioctl返回设备的报表描述符使用一个hidraw_report_descriptor结构体。确保hidraw_report_descriptor 结构体size字段是从HIDIOCGRDESCSIZE返回的size
HIDIOCGRAWINFO:得到原生的信息
这个ioctl将返回一个hidraw_devinfo结构体,包含设备的总线类型、VID和PID。总线类型可以是以下之一:
BUS_USB
BUS_HIL
BUS_BLUETOOTH
BUS_VIRTUAL
定义在uapi/linux/input.h.
HIDIOCGRAWNAME(len):得到原生的名字
这个ioctl返回一个包含设备vendor和produce的字符串。这个字符串是Unicode ,UTF-8编码
HIDIOCGRAWPHYS(len):得到物理地址
这个ioctl返回一个字符串代表设备的物理地址。对于USB设备,字符串包含设备的物理路径(USB控制器、集线器、端口等)。对于蓝牙设备,这个字符串包含硬件(MAC)地址。
HIDIOCSFEATURE(len):发送一个特性报表
这个ioctl发送一个特性报表到设备。根据HID报表说明,特性报表总是使用控制端点发送。将提供的buffer的第一个字节设置为报表号。不使用报表号的报表,将第一个字节设置为0。报表数据从第二个字节开始。确保将len设置为一个比报表更大的长度(要考虑报表的数量)。
HIDIOCGFEATURE(len):
这个ioctl通过从设备的控制端点请求特性报表。应该将提供的buffer的第一个字节设置为请求报表的报表号。对于设备没有报表号,应该设置为0。该报告将从buffer的第一个字节开始返回(即:报告号没有返回)。
三、Example
HidrawManager.h
#ifndef HIDRAW_MANAGER_H
#define HIDRAW_MANAGER_H
#include <linux/types.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#define HIDRAW_DIR "/dev/"
#define HIDRAW_PATH_SIZE 20
class HidrawManager{
public:
HidrawManager();
virtual ~HidrawManager();
int openHidraw();
int writeHidraw(char* buf,unsigned int size);
int readHidraw(char* buf,unsigned int size);
int closeHidraw();
bool isSelfHidraw();
private:
int fd;
};
#endif
HidrawMananger.cpp
#include"HidrawManager.h"
HidrawManager::HidrawManager():fd(-1){
}
HidrawManager::~HidrawManager(){
closeHidraw();
}
bool HidrawManager::isSelfHidraw(){
struct hidraw_devinfo raw_info;
struct hidraw_report_descriptor rep_des;
int desc_size = 0;
if(-1 == ioctl(fd, HIDIOCGRAWINFO, &raw_info)){
return false;
}
if(ioctl(fd, HIDIOCGRDESCSIZE, &desc_size) < 0) {
return false;
}
rep_des.size = desc_size;
if(4096 == raw_info.product && 1546 == raw_info.vendor)//
{
return true;
}
return false;
}
int HidrawManager::openHidraw(){
int result=-1;
DIR *dir = NULL;
struct dirent *hidraw = NULL;
char path[HIDRAW_PATH_SIZE];
dir = opendir(HIDRAW_DIR);
if(dir == NULL)
{
return -1;
}
memset(path,0x00,sizeof(path));
strcpy(path,HIDRAW_DIR);
do{
if(!(hidraw = readdir(dir))){
goto EXIT;
}
if(hidraw!=NULL){
if(hidraw->d_name == NULL){
continue;
}
}
if(!strstr(hidraw->d_name,"hidraw"))
{
continue;
}
memset(&path[sizeof(HIDRAW_DIR)-1],0x00,sizeof(path)-sizeof(HIDRAW_DIR));
strcpy(&path[sizeof(HIDRAW_DIR)-1],hidraw->d_name);
fd=open(path,O_RDWR);
if(fd<0){
continue;
}
if(isSelfHidraw()){
result=fd;
}
}while(hidraw != NULL);
EXIT:
closedir(dir);
return result;
}
int HidrawManager::writeHidraw(char* buf,unsigned int size){
int ret = -1;
ret = write(fd,buf,size);
return ret;
}
int HidrawManager::readHidraw(char* buf,unsigned int size){
int ret = -1;
ret = read(fd,buf,size);
return ret;
}
int HidrawManager::closeHidraw(){
int ret = -1;
ret = close(fd);
return ret;
}