目標
利用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");
}