手机作为USB单点触摸板实例
此功能是将一台android手机作为一个usb触摸板,将device的触摸数据上报到host,主要是通过linux gadget中的f_hid.c来实现。
修改init.qcom.usb.rc,具体如下,使手机成为一个同时有ADB和HID device功能的复合设备:
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=hid,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "hid_adb"
rm /config/usb_gadget/g1/configs/b.1/f1
rm /config/usb_gadget/g1/configs/b.1/f2
rm /config/usb_gadget/g1/configs/b.1/f3
rm /config/usb_gadget/g1/configs/b.1/f4
rm /config/usb_gadget/g1/configs/b.1/f5
rm /config/usb_gadget/g1/configs/b.1/f6
rm /config/usb_gadget/g1/configs/b.1/f7
rm /config/usb_gadget/g1/configs/b.1/f8
rm /config/usb_gadget/g1/configs/b.1/f9
symlink /config/usb_gadget/g1/configs/b.1 /config/usb_gadget/g1/os_desc/b.1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/MaxPower 120
write /config/usb_gadget/g1/idVendor 0x2717
write /config/usb_gadget/g1/idProduct 0x5001
write /config/usb_gadget/g1/functions/hid.0/protocol 1
write /config/usb_gadget/g1/functions/hid.0/subclass 1
write /config/usb_gadget/g1/functions/hid.0/report_length 8
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=hid2,adb && property:sys.usb.configfs=1
symlink /config/usb_gadget/g1/functions/hid.0 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
重启后,将属性切到hid,adb模式上来,并向手机发送USB touch的报告描述符,然后再切到hid2,adb上来,使手机插入usb host时能被枚举成一个usb touch设备。
setprop sys.usb.config hid,adb
echo -ne \\x05\\x0D\\x09\\x04\\xA1\\x01\\x85\\x01\\x09\\x22\\xA1\\x02\\x09\\x42\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x01\\x81\\x02\\x09\\x32\\x81\\x02\\x95\\x06\\x81\\x03\\x75\\x08\\x09\\x51\\x95\\x01\\x81\\x02\\x05\\x01\\x09\\x30\\x75\\x10\\x95\\x01\\x55\\x0E\\x65\\x33\\x35\\x00\\x46\\xFF\\x7F\\x26\\x30\\x2A\\x81\\x02\\x09\\x31\\x75\\x10\\x95\\x01\\x55\\x0E\\x65\\x33\\x35\\x00\\x46\\xFF\\x7F\\x26\\xC0\\x5D\\x81\\x02\\xC0\\xC0 > /config/usb_gadget/g1/functions/hid.0/report_desc
setprop sys.usb.config hid2,adb
报告描述符解析如下:
0x05, 0x0D, // Usage Page (Digitizer)
0x09, 0x04, // Usage (Touch Screen)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1) 用来表示不同的功能,如鼠标、键盘、touchpad、touchscreen的序号
0x09, 0x22, // Usage (Finger)
0xA1, 0x02, // Collection (Logical)
0x09, 0x42, // Usage (Tip Switch)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x32, // Usage (In Range)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06, // Report Count (6)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x09, 0x51, // Usage (0x51)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x55, 0x0E, // Unit Exponent (-2)
0x65, 0x33, // Unit (Eng Lin: in^3)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
0x26, 0x30, 0x2A, // Logical Maximum (10800)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x31, // Usage (Y)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x55, 0x0E, // Unit Exponent (-2)
0x65, 0x33, // Unit (Eng Lin: in^3)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
0x26, 0xC0, 0x5D, // Logical Maximum (24000)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
与之前的调试相比,在开始准备x与y坐标的上报时,从Usage Page开始,x和y分别作为一个usage,之前的对Logical Maximum的定义是在Usage (X)和 Usage Page之间,这样有个问题就是导致Usage Page下的每个usage都用这个属性,但我们需要对每个usage分别定义。所以把之前的逻辑删除,在Usage (X)和Usage (Y)下面分别规定Logical Maximum、Logical Maximum是触摸时与屏幕边界对应的值,如果比要求的值大,会导致触摸的边界在屏幕中央的某个地方,如果比要求值小,会导致触摸的边界在屏幕外面。
手机通过C-TO-C线插入另一个USB host手机后,在host端可以看到一个触摸的输入设备:
枚举成功后,device向host发送的输入报告,将device的触摸数据读到后,根据上面的报告描述符,重新组合成新的usb输入报告,并将数据写入/dev/hidg0,然后数据会通过USB总线发送到usb host。通过Bus hound抓包如下:
此时usb host可同步响应device的触摸事件。
参考用户应用程序如下:
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <linux/input.h>
#include <stdlib.h>
#include <string.h>
struct input_event ev;
unsigned char* report;
int main()
{
//int oflags;
int fd_hid = -1;
int fd_tp = -1;
int ret = -1;
unsigned char button = 0;
unsigned char track_id = 0;
int x = 0;
int y = 0;
report = malloc(7);
memset(report,0,7);
fd_hid = open("/dev/hidg0", O_RDWR);
fd_tp = open("/dev/input/event4", O_RDWR);
if (fd_tp != - 1)
{
while(1){
ret = read( fd_tp, &ev, sizeof(ev));
if(ret < 0)
{
printf("Failed to read from /dev/input/event13");
}
else if(ret > 0)
{
if (ev.type == EV_ABS) {
if (ev.code == 53) {//x
//x = 2880.0/1581.0*ev.value;
x = ev.value;
} else if (ev.code == 54) {//y
//y = 1880.0/1582.0*ev.value;
y = ev.value;
} else if (ev.code == ABS_MT_TRACKING_ID) {
if (ev.value == -1) {
// 触摸结束
//button = 0x0;
track_id = ev.value;
} else {
// 触摸开始或移动
//button = 0x01;
track_id = ev.value;
}
}
}
if(ev.type == 0x01) {//EV_KEY
if(ev.code == BTN_TOUCH){
if (ev.value == 0) {
// 触摸点离开
button = button & 0xfe;//tip switch
} else if(ev.value == 1) {
// 触摸开始有触摸
button = button | 0x01;
}
}
}
// 发送HID报告
if (ev.type == EV_SYN) {
report[0] = 0x01; // 报告ID
report[1] = button; // 按钮状态button
report[2] = 0x00; // range
report[3] = x & 0xff; // X坐标的LSB
report[4] = (x >> 8) & 0xff; // X坐标的MSB
report[5] = y & 0xff; // Y坐标的LSB
report[6] = (y >> 8) & 0xff; // Y坐标的MSB
}
else
continue;
printf("button:%d,track_id:%d,x_low:0x%x,x_high:0x%x,y_low:0x%x,y_high:0x%x\n",report[1],report[2],report[3],report[4],report[5],report[6]);
// 向/dev/hidg0写入数据
ret = write(fd_hid, report, 7);
if (ret < 0) {
printf("Failed to write to /dev/hidg0\n");
}
}
}
}
else
{
printf("device open failure\n");
}
return 0;
}