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");
}
posted @ 2020-08-30 23:03  barfoot  阅读(2136)  评论(1编辑  收藏  举报