RK3399 模拟成鼠标触摸屏
目标
利用3399 OTG口,连接电脑时能作为鼠标和触摸屏使用
应用场景
3399用在大屏显示设备上,支持HDMI输入,当外接笔记本电脑时,同时用USB数据线连接电脑,这时可以用大屏幕的触摸屏模拟成电脑的触摸屏,在大屏幕上直接操作电脑。本质上就是3399模拟成一个hid设备,网上模拟成鼠标的参考资料较多,第一步先模拟成鼠标,第二步实现单点触摸,第三步实现多点复合(其实是系统会报告是否支持单点,多点,如果都不支持,就去模拟成鼠标,例如windows XP,这部分可以参考圈圈教你学usb第二版,这一步暂时未实现)
系统架构
除了在内核模拟hid设备以外,在应用层3399收到触摸事件后,需要把数据写入到hid设备,这部分比较简单,可以参考android getevent的实现。
系统平台
Android7.1
参考资料
android-keyboard-gadget开源项目
圈圈教你学usb
调试工具
圈圈书上推荐的Bus Hound
USB Trace?
模拟成鼠标设备
这部分主要参考3288开源项目,手上刚好有一块firefly3288的板子,在3288上验证了没问题。原理方面圈圈已经讲得很清楚,这里贴一下鼠标的设备描述符。
static struct hidg_func_descriptor fdesc_hid= {
---.subclass = 1, /* No subclass /
---.protocol = 2, / Keyboard */
---.report_length = 4,
---.report_desc_length = 52,
---.report_desc = {
0x05, 0x01, //Usage Page(Generic Desktop Controls)
0x09, 0x02, //Usage (Mouse)
0xa1, 0x01, //Collection (Application)
0x09, 0x01, //Usage (pointer)
0xa1, 0x00, //Collection (Physical)
0x05, 0x09, //Usage Page (Button)
0x19, 0x01, //Usage Minimum(1)
0x29, 0x05, //Usage Maximum(5)
0x15, 0x00, //Logical Minimum(1)
0x25, 0x01, //Logical Maximum(1)
0x95, 0x05, //Report Count(5)
0x75, 0x01, //Report Size(1)
0x81, 0x02, //Input(Data,Variable,Absolute,BitField)
0x95, 0x01, //Report Count(1)
0x75, 0x03, //Report Size(3)
0x81, 0x01, //Input(Constant,Array,Absolute,BitField)
0x05, 0x01, //Usage Page(Generic Desktop Controls)
0x09, 0x30, //Usage(x)
0x09, 0x31, //Usage(y)
0x09, 0x38, //Usage(Wheel)
0x15, 0x81, //Logical Minimum(-127)
0x25, 0x7F, //Logical Maximum(127)
0x75, 0x08, //Report Size(8)
0x95, 0x03, //Report Count(3)
0x81, 0x06, //Input(Data,Variable,Relative,BitField)
0xc0, //End Collection
0xc0 //End Collection
---}
};
鼠标测试程序
核心代码
char track[] = {
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
50,50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
-50,-50,
};
while (count) {
for(i = 0; i< sizeof(track); i = i+2 ){
memset(report, 0x0, sizeof(report));
report[1] = track[i];
report[2] = track[i+1];
if (write(fd, report, 4) != 4) {
perror(filename);
return 1;
}
usleep(500000);
}
count--;
}
注意3288和3399都适用
移植到3399
需要注意的是3288的内核是3.10的,3399的内核是4.x,启用了configfs,设备描述符的写法与原先有些不同。
模拟成触摸屏
触摸屏与鼠标的不同点是鼠标的上报值是相对坐标,触摸屏是绝对坐标,鼠标xy轴分别需要一个字节,而触摸屏一般为12bit即两个字节。这里遇到一个坑是我把上报总长度搞错了,3288依然可以运行,但3399就不行。
描述符如下
static struct hidg_func_descriptor fdesc_hid= {
---.subclass = 0, /* No subclass /
---.protocol = 0, / Mouse */
---.report_length = 5,
---.report_desc_length = 56,
---.report_desc = {
0x05, 0x01, //Usage Page(Generic Desktop Controls)
0x09, 0x02, //Usage (Mouse)
0xa1, 0x01, //Collection (Application)
0x09, 0x01, //Usage (pointer)
0xa1, 0x00, //Collection (Physical)
0x05, 0x09, //Usage Page (Button)
0x19, 0x01, //Usage Minimum(1)
0x29, 0x05, //Usage Maximum(5)
0x15, 0x00, //Logical Minimum(1)
0x25, 0x01, //Logical Maximum(1)
0x95, 0x05, //Report Count(5)
0x75, 0x01, //Report Size(1)
0x81, 0x02, //Input(Data,Variable,Absolute,BitField)
0x95, 0x01, //Report Count(1)
0x75, 0x03, //Report Size(3)
0x81, 0x01, //Input(Constant,Array,Absolute,BitField)
0x05, 0x01, //Usage Page(Generic Desktop Controls)
0x09, 0x30, //Usage(x)
0x09, 0x31, //Usage(y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x35, 0x00, //Physical Minimum (0)
0x46, 0xff, 0x7f, //Physical Maximum(32767)
0x75, 0x10, //Report Size(16)
0x95, 0x02, //Report Count(2)
0x81, 0x02, //Input(Data,Variable,ABS)
0xc0, //End Collection
0xc0 //End Collection
---}
};
测试程序
这块代码主要参考圈圈教你学USB第二版,同样适合3288和3399
核心代码
void MoveTo(int x, int y)
{
//需要返回的5字节报告的缓冲
//Buf[0]的D0就是左键,D1就是右键,D2就是中键
//Buf[1]为X轴低字节,Buf[2]为X轴高字节,
//Buf[3]为Y轴低字节,Buf[4]为Y轴高字节,
char Buf[5]={0,0,0,0,0};
Buf[0] = 0x00;
Buf[1] = x & 0xFF;
Buf[2] = (x >> 8) & 0xFF;
Buf[3] = y & 0xFF;
Buf[4] = (y >> 8) & 0xFF;
if (write(mFd, Buf, 5) != 5) {
return;
}
usleep(50000);
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函数功能:画线段的函数。
入口参数:x:x轴坐标;y:y轴坐标
返 回:无。
备 注:无。
********************************************************************/
void LineTo(int x, int y)
{
//需要返回的5字节报告的缓冲
//Buf[0]的D0就是左键,D1就是右键,D2就是中键
//Buf[1]为X轴低字节,Buf[2]为X轴高字节,
//Buf[3]为Y轴低字节,Buf[4]为Y轴高字节,
char Buf[5]={0,0,0,0,0};
Buf[0] = 0x01; //左键按下
Buf[1] = x & 0xFF;
Buf[2] = (x >> 8) & 0xFF;
Buf[3] = y & 0xFF;
Buf[4] = (y >> 8) & 0xFF;
if (write(mFd, Buf, 5) != 5) {
return;
}
usleep(50000);
}
if(strstr(cmd, "a") != NULL)
{
printf("Draw Tri-angle begin\n");
MoveTo(2000, 1000); //移动到(2000,1000)
LineTo(2000, 1000); //开始画线
LineTo(1000, 3000); //画线到(1000,3000)
LineTo(3000, 3000); //画线到(3000,3000)
LineTo(2000, 1000); //画线到(2000,1000)
MoveTo(2000, 1000); //松开鼠标左键
printf("Draw Tri-angle end\n");
}
else if(strstr(cmd, "l") != NULL)
{
printf("Draw line begin\n");
MoveTo(1000, 1000); //移动到(1000,1000)
LineTo(1000, 1000); //开始画线
LineTo(3000, 3000); //画线到(3000,3000)
MoveTo(3000, 3000); //松开鼠标左键
printf("Draw line end\n");
}