1. 程式人生 > >USB HID裝置資料的讀取

USB HID裝置資料的讀取

USB裝置(HID)

Android下獲取HID資料的方式:

1.通過讀取/dev/hidrawX裝置節點 看名字就知道意思了(這個需要在編譯的時候開啟)

2.如果是標準HID裝置,還可以通過/dev/input裡的裝置節點讀取

方式一.通過input/event獲得資料

這裡的資料被轉化為統一的結構,結構的定義在input.h中,值也在標頭檔案中定義。幾個結構體定義如下:
我們在event節點讀取裝置,所以格式就是input_event,單幀timeval時間資料結構長度加8位元組資料。

struct input_event {
 struct timeval time;
 __u16 type;
 __u16 code;
 __s32 value;
};

#define EV_VERSION 0x010000

struct input_id {
 __u16 bustype;
 __u16 vendor;
 __u16 product;
 __u16 version;
};

struct input_absinfo {
 __s32 value;
 __s32 minimum;
 __s32 maximum;
 __s32 fuzz;
 __s32 flat;
};

題外話:之前比較好奇USB裝置為什麼能將不同的裝置的協議轉化為相同的結構。得到的結果好像是可以將自己的協議解析告訴USB協議告訴系統,然後讓系統按自己的協議進行解析。[…]

獲取資料

首先獲取資料作為測試test.cpp。這裡資料解析是絕對資料解析。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/input.h>

#define MOUSE_DEV_PATH    "/dev/input/event1"
int x = 0;
int y = 0;

int main()
{
        unsigned char data[sizeof(input_event)];
        input_event dev_data;
        int fd = open(MOUSE_DEV_PATH, O_RDONLY);
        if (fd == -1)
        {
                printf("mouse device open fail\n");
                return -1;
        }

        printf("open successful");
        while (1)
        {
                int size = read (fd, (unsigned char*)data, sizeof(input_event));
                printf("size:%d", size);
                memcpy(&dev_data, data, sizeof(input_event));
                        if (dev_data.type == EV_REL)
                        {
                                if (dev_data.code == REL_X)
                                {
                                        x += dev_data.value;
                                        if (x < 0) x = 0;
                                        printf("rel X:%d", dev_data.value);
                                }
                                else if(dev_data.code == REL_Y)
                                {
                                        printf("rel Y:%d", dev_data.value);
                                        y += dev_data.value;
                                        if (y < 0) y = 0;
                                }
                                printf("\nx=%d,y=%d\n", x, y);
                        }

                printf("\n");
                for (int i = 0; i < size; ++i)
                        printf(" %3d", data[i]);
                printf("\n");

        }
        close(fd);
}

MY_LOCAL_PATH:=$(call my-dir)
LOCAL_PATH:=$(MY_LOCAL_PATH)
MY_LOCAL_ANDSRC:=/home/Android7.1.1
include $(CLEAR_VARS)


LOCAL_MODULE:=mytest
LOCAL_SRC_FILES:=test.cpp


include $(BUILD_EXECUTABLE)

獲得裝置

event的裝置號會因插入順序而不同,所以需要自動獲得裝置。
可以通過檢視:
cat /proc/bus/input/devices
可以得到對應的vid和pid對應的event

I: Bus=0003 Vendor=17ef Product=6019 Version=0111
N: Name=" Mouse"
P: Phys=usb-fe3c0000.usb-1.7/input0
S: Sysfs=/devices/platform/fe3c0000.usb/usb2/2-1/2-1.7/2-1.7:1.0/0003:XXXX:XXXX.0001/input/input2
U: Uniq=
H: Handlers=event2 cpufreq
B: PROP=0
B: EV=17
B: KEY=70000 0 0 0 0
B: REL=103
B: MSC=10

一種解決思路是通過解析這個裝置檔案進行查詢

方式二.通過hidraw節點獲得原生資料

這種方式很普通直接讀寫裝置獲得資料即可,然後按照協議的資料解析即可。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/input.h>
#include <linux/hidraw.h>

#define MOUSE_DEV_PATH    "/dev/hidraw0"

unsigned char buffer[1000];

int main()
{
        struct hidraw_devinfo info;

        int fd = open(MOUSE_DEV_PATH, O_RDONLY);
        if (fd == -1)
        {
                printf("mouse device open fail\n");
                return -1;
        }

        int rc = ioctl(fd, HIDIOCGRAWINFO, &info);
        if (rc < 0)
        {
                printf("readerror!\n");
                return 1;
        }
        printf("HID device info %04x:%04x:%04x is\n", info.bustype,info.vendor, info.product);

        
        unsigned char buff[96];
        printf("open successful\n");
        while (1)
        {
                int size = read(fd, buffer, 64);
                printf("\nsize:%d\t", size);
                for (int i = 0; i < size; ++i)
                        printf("%d ", buffer[i]);
              printf("\n");
        }
        return 0;
}

獲取設別節點的VID和PID可以通過ioctl查詢HIDIOCGRAWINFO獲得。
結構體定義在hidraw.h中

修改hidrawX裝置節點許可權的方法

1.修改uevent.rc檔案

修改編譯後的uevent.rc檔案。檔案位置在 out/target/product/XXXXXXX/root 路徑下
增加:

/dev/hidraw*              0666   root       root

這樣預設是把hidraw*的所有許可權都改成0666了。然後在進行打包成映象。

2.修改原始碼

其實應該還有種方法是在原始碼建立hidraw的節點時的許可權。

3.監聽修改許可權服務

這種方法是。寫一個監聽服務,可以輪詢檢視插上的hid裝置。根據hid的PID和VID修改許可權。

然後把服務設定成開機啟動

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <linux/hidraw.h>

#define  DEV_BASE_PATH    "/dev/hidraw"
#define  MAX_SUPPORT_DEV   20
#define  PRODUCT_VID        
#define  PRODUCT_PID 

int main()
{
	int fd;
	struct hidraw_devinfo info;
	char path[20];
	struct stat buf;
	
	while (1)
	{	
		for (int i = 0; i < MAX_SUPPORT_DEV; ++i)
		{
			sprintf(path, "%s%d", DEV_BASE_PATH, i);
			stat(path, &buf);
			if (!(buf.st_mode & S_IROTH))
			{
				if((fd = open(path, O_RDONLY)) < 0)
					continue;

				int rc = ioctl(fd, HIDIOCGRAWINFO, &info);
				if (rc < 0)			
					continue;
				if ((info.vendor == PRODUCT_VID) && (info.product == PRODUCT_PID))
				{
					fchmod(fd,0666);
					close(fd);
					break;	
				}	
			}
		}
		
		sleep(3);
	}
	return 0;
}