M平台 按键板/触摸框 虚拟uinput 设备

基于Android平台的TV 产品,输入设备中除了上篇提到的遥控器,空鼠。还有一些模拟
出来的uinput 设备“Mstar Smart TV Keypad” ,“libxTouchScreen” 。

1. 虚拟按键板
上篇我们通过getevent -l 可以看到device 2(/dev/input/event3)是属于按键板的keypad
设备,在Mstar 平台作为一个服务在后台运行
在这里插入图片描述
在运行时,按键板可以正常操作,kill 之后就无法响应。所以当我们按键无响应时,要看
服务是否正常,按键板ADC 值是否配置正确,按键板连接的sar 通道软件中是否开启。
接着我们看源码部分device\mstar\common\executables\virtualkeypad\main.c

  • 处理流程
    在这里插入图片描述
    驱动初始化,主要是一些系统全局模块初识化,gpio,sar 初识化
int main()
{
    MDrv_SYS_GlobalInit();
    mdrv_gpio_init();
    int ret = 0;

    MDrv_MIU_SetIOMapBase();
    SAR_KpdRegCfg SARCfg;
    MDrv_SAR_Kpd_Init();
    . . . . . .
}
  • keypad 值映射
	typedef struct {
		MS_BOOL bEnable;
		unsigned char u8SARChID;
		SARKpdBndCfg tSARChBnd;
		unsigned char u8KeyLevelNum;  // 0-8 levels
		unsigned char u8KeyThreshold[8];  // each threshold match to one keycode
		unsigned char u8KeyCode[8];
	} SARKpdRegCfg;

	 static SARKpdRegCfg BOARD_KPD[MAX_KEYPAD_CH] = {
		KPD_TABLE
	};  

    int i, j;
    //  遍历MAX_KEYPAD_CH 个通道,将每个按键对应的sar值 保存在配置中,
    //  根据sar 值,给每个按键定义一个keycode值,注册到sar 模块中,当按键触发时,
    //  sar模块返回keycode 值,该值也用于kl 文件中的keycode 和 应用层的映射
    for (i = 0; i < MAX_KEYPAD_CH; i++) {
        if (BOARD_KPD[i].bEnable == false) {
            continue;

        }
        SARCfg.u8SARChID = BOARD_KPD[i].u8SARChID;
        SARCfg.tSARChBnd.u8UpBnd = BOARD_KPD[i].tSARChBnd.u8UpBnd;
        SARCfg.tSARChBnd.u8LoBnd = BOARD_KPD[i].tSARChBnd.u8LoBnd;
        SARCfg.u8KeyLevelNum = BOARD_KPD[i].u8KeyLevelNum;
        for (j = 0; j < 8; j++) {
            SARCfg.u8KeyThreshold[j] = BOARD_KPD[i].u8KeyThreshold[j]; //  sar 物理值
            SARCfg.u8KeyCode[j] = BOARD_KPD[i].u8KeyCode[j];  //  keycode 值
        }
        //  将sar 物理值 和 keycode 映射值保存到sar 配置模块中
        if (MDrv_SAR_Kpd_SetChInfo(&SARCfg) == E_SAR_KPD_FAIL) {
            ALOGV("DFBInfo MDrv_SAR_Config: CH_%d fails\n", i);
            return FALSE;
        }
    }

其中KEYPAD映射如下:

//bEnable, u8SARChID, u8UpBnd, u8LoBnd, u8KeyLevelNum,             u8KeyThreshold[8],                                   u8KeyCode[8]
{  0x01,     0x00,    {0xFF  ,  0x70},     0x08,       {0x10, 0x2F, 0x4D, 0x71, 0x92, 0xAB, 0xC3, 0xE7}, {0x1E, 0x30, 0x23, 0x24, 0x25, 0x26, 0x32, 0x31}}, \
{  0x00,     0x01,    {0xFF  ,  0x70},     0x00,       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, \
{  0x00,     0x02,    {0xFF  ,  0x70},     0x00,       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, \
{  0x00,     0x03,    {0xFF  ,  0x70},     0x00,       {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},

Vendor_3697_Product_0002.kl 映射又如下:

#
# MStar Smart TV Keypad.
#

key 30   POWER
key 35   HOME
key 48   TV_INPUT
key 50   VOLUME_DOWN
key 49   VOLUME_UP
key 37   ENTER

keypad 里面keycode 为十六进制,kl 中keycode 是10进制,右侧枚举是字符串加上
"KEYCODE_"前缀,映射到上层,如POWER 对应的就是KEYCODE_POWER。

  • 虚拟uinput设备
    主要是打开uinput设备,配置一些参数,包括名称,vendor id,product id,bustype
    keycode 的区间
int setup_uinput_device(stKeypadDeviceData* pKpdDev) {
    struct uinput_user_dev uinp; // uInput device structure
    int i;
    // Open the input device
    uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
    if (uinp_fd == 0) {
        printf("Unable to open /dev/uinput\n");
        return -1;
    }

    // Intialize the uInput device to NULL
    memset(&uinp, 0x00, sizeof(uinp));
    strncpy(uinp.name, KEYPAD_NAME, sizeof(uinp.name)-1);
    uinp.id.vendor = KEYPAD_VENDOR_ID;
    uinp.id.product = 0x0002;
    uinp.id.bustype = BUS_VIRTUAL;

    // Keyboard value of area
    ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
    for (i = pKpdDev->min_keycode; i < pKpdDev->max_keycode; i++) {
        ioctl(uinp_fd, UI_SET_KEYBIT, i);
    }

    // Create input device into input sub-system
    if (write(uinp_fd, &uinp, sizeof(uinp)) != sizeof(uinp)) {
        printf("First write returned fail.\n");
        return -1;
    }
    //  创建
    if (ioctl(uinp_fd, UI_DEV_CREATE)) {
        printf("ioctl UI_DEV_CREATE returned fail.\n");
        return -1;
    }

    return 1;
}
  • sar 口获取键值
    在前面将sar 物理值和keycode 映射值 设置到sar模块以后,连接在sar 口的按键板,有
    按键触发时,就可以获取到keycode值
static void* keypad_EventThread(void *driver_data) {
	. . . . . . 

    while (1) {
        ret = E_SAR_KPD_FAIL;
        u8Keycode = NULL_KEYVALUE;
        u8Repeat = 0;
        /* sleep an interval time */
        usleep(keypad_input_interval);
        ret = MDrv_SAR_Kpd_GetKeyCode(&u8Keycode, &u8Repeat);
        
        MDrv_SAR_Adc_Config(1, TRUE);
        adc1 = MDrv_SAR_Adc_GetValue(1);

        /* check the get keycode successfully */
        if (ret != E_SAR_KPD_OK) {
            loop_count += 1;
            if (loop_count == SLOW_POLLING_BOUNDARY)
                keypad_input_interval = KEYPAD_INPUT_SLOW_POLLING;
            if (repeatKey != NULL_KEYVALUE) {
                write_event_to_device(repeatKey, 0);
                repeatKey = NULL_KEYVALUE;
            }
            continue;
        }

        /* check the keypad is vaild? */
        if (u8Keycode == NULL_KEYVALUE) {
            loop_count += 1;
            if (loop_count == SLOW_POLLING_BOUNDARY)
                keypad_input_interval = KEYPAD_INPUT_SLOW_POLLING;
            continue;
        }

        /* fill event to uinput device. */
        if (u8Repeat == 1) {
            repeatKey = u8Keycode;
        }
        write_event_to_device(u8Keycode, u8Repeat);
        loop_count = 0;
        keypad_input_interval = KEYPAD_INPUT_NORMAL_POLLING;
    }

    printf ("keypad keypad thread died\n");
    pthread_exit(NULL);
    return NULL;
}

线程通过MDrv_SAR_Kpd_GetKeyCode 接口不停地遍历SAR,获取keycode值,成功
之后通过write_event_to_device 将键值注入uinput 设备中,也即模拟出来的input设备。

  • 模拟设备上抛事件
    上一步步骤完成keyevent 事件的获取,接着通过模拟设备上抛到上层了。
void write_event_to_device(MS_U8 u8KeyCode, MS_U8 u8Repeat) {
    struct input_event event; // Input device structure
    struct timespec s;
    s.tv_nsec = 5000000L;
    s.tv_sec = 0;

    memset(&event, 0x00, sizeof(event));
    gettimeofday(&event.time, NULL);
    event.type = EV_KEY;                 //  事件类型      
    event.code = u8KeyCode;           // 键值
    event.value = 1;                          // down: 按下
    write(uinp_fd, &event, sizeof(event));

    if (u8Repeat == 0) {
        memset(&event, 0x00, sizeof(event));
        gettimeofday(&event.time, NULL);
        event.type = EV_KEY;                 // 事件类型
        event.code = u8KeyCode;          //   键值
        event.value = 0;                          //  up:按键弹起 . 非长按马上弹起
        write(uinp_fd, &event, sizeof(event));  // 写入uinput 设备节点
    }
    //  发送sync 事件
    memset(&event, 0x00, sizeof(event));
    gettimeofday(&event.time, NULL);
    event.type = EV_SYN;
    event.code = SYN_REPORT;
    event.value = 0;
    write(uinp_fd, &event, sizeof(event));
}

android 虚拟uinput 设备参考链接:
https://www.xuebuyuan.com/1867494.html
https://blog.csdn.net/ZHONGkunjia/article/details/75142699?utm_source=blogxgwz3
uinput 设备驱动参考:
https://blog.csdn.net/mcgrady_tracy/article/details/22961339

2. 虚拟触摸框
主要是介绍触摸框串口数据通过模拟的uinput 输入设备,向上层发送触摸事件的场景。
分为两大部分从串口获取原始数据,向uinput 设备发送event事件,至于协议解析参看
https://blog.csdn.net/kehyuanyu/article/details/95996289 即可。

  • 串口获取数据
    触摸框的串口发出来的是通过红外框采集的手指触摸的是坐标,宽度等等信息,总共
    67 bytes 都是通过/dev/ttyS* 获取,串口初始化
int setupSerial()
{
#if (NON_BLOCK_MODE == 1)
	// O_NDELAY/O_NONBLOCK  非阻塞模式也即轮训模式, read 时候没有数据会马上返回
    input_fd = open(SERIAL1_IN_DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
#else
	// 阻塞/同步模式,read 在读取数据时,有数据或读取到文件结尾时才会返回
    input_fd = open(SERIAL1_IN_DEVICE, O_RDWR | O_NOCTTY);// remmove the O_NDELAY mode
#endif
    if (input_fd < 0) {
        fprintf(stderr, "inputattach: '%s' - %s\n",SERIAL1_IN_DEVICE, strerror(errno));
        return -1;
    }
    //start
    int ret = -1;
    struct serial_struct serial;
    ret = ioctl(input_fd, TIOCGSERIAL, &serial);
    if (ret != 0) {
        close(input_fd);
        return -1;
    }
    serial.xmit_fifo_size = 100 * 1024*1024; //1M
    ret = ioctl(input_fd, TIOCSSERIAL, &serial);
    if (ret != 0) {
        close(input_fd);
        return -1;
    }
    //end
#if (NON_BLOCK_MODE == 1)
	// 非阻塞模式
    fcntl(input_fd, F_SETFL, FNDELAY);
#else
	// 阻塞模式
    fcntl(input_fd, F_SETFL, 0);// set nonblock mode (FNDELAY to zero)
#endif
	
    tcflush(input_fd,TCIOFLUSH);
    if (tcgetattr(input_fd,&old_options) != 0) {
        return -1;
    }
	// 获取串口属性
    tcgetattr(input_fd, &new_options);
	// 设置新的串口属性
    new_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | ISIG);
    new_options.c_iflag  &= ~(INPCK | INLCR | ICRNL | IUCLC | IXON | IXOFF);
    new_options.c_oflag  &= ~OPOST;   // raw output
    // c_CC[VTIME] , c_CC[VMIN] 影响read 的返回值。
	new_options.c_cc[VTIME] = 100;
    new_options.c_cc[VMIN] = 60;
	// 设置波特率
    cfsetispeed(&new_options, B115200);
    cfsetospeed(&new_options, B115200);

    if((tcsetattr(input_fd,TCSANOW,&new_options))!=0)
    {
        return -1;
    }
    return 0;

}

初识化串口,注册阻塞/非阻塞模式的区别,另外c_cc[VTIME],c_cc[VMIN]属性的设置影响read的返回值。完成之后线程开始不停读取触摸数据。

#define PACKAGE_SIZE (67)
static MAPI_U8 _cmdLine[CMD_LINE_SIZE];

int  serialDataLoop(void*  arg)
{
	. . . . . . 
	MAPI_U8 *dataTmp = _cmdLine;
	
    while(!_bExitUartDebug)
    {
        FD_ZERO(&r_fds);
        FD_SET(input_fd, &r_fds);
    #if (NON_BLOCK_MODE == 1)
        //set Time Out
        //sys.boot_completed
        memset(&timeout,0x00,sizeof(timeout));
        timeout.tv_sec = 0;
        timeout.tv_usec = 3*1000; // 7 ms
        ret = ::select(input_fd + 1, &r_fds, NULL, NULL, &timeout); // 非阻塞
    #else
        ret = ::select(input_fd + 1, &r_fds, NULL, NULL, NULL); // 阻塞模式
    #endif

        if(ret == 0)
        {
            continue;
        }
        else if(ret > 0 && FD_ISSET(input_fd, &r_fds))//else if(ret > 0)
        {
            /*Here is demo code for read/write serial data,please Add Customer
            code here for special use case*/
            // data recieved 
        #if (NON_BLOCK_MODE == 1)
            readRawDataRet = readRAWData(input_fd, tmpReadBuff);
        if(readRawDataRet){
            doProcessCommand(tmpReadBuff);
            memset(tmpReadBuff,0,sizeof(tmpReadBuff));
            ReadBuff = tmpReadBuff;
        }else{
            continue;
        }
        #else
            readRawDataRet = readFullTouchData(input_fd, tmpReadBuff, DATA_LEN_SIX);
			if(readRawDataRet == 1){
                doProcessCommand(tmpReadBuff);
                memset(tmpReadBuff,0,sizeof(tmpReadBuff));
                ReadBuff = tmpReadBuff;
            } else {
            	continue;
            } 
        #endif
        }
    }
	. . . . . . 
}

readRAWData(非阻塞)/readFullTouchData(阻塞) 不停从/dev/ttyS* 读取数据,读取完成之后,doProcessCommand 再校验,解析数据。读取数据可以参考另一篇博客https://blog.csdn.net/kehyuanyu/article/details/101756151
更新应用可以参考MSrv_UartDebug.cpp文件。

  • 虚拟uinput设备
int setup_uinput_device() {
    struct uinput_user_dev uinp; // uInput device structure
    // Open the input device
    uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
    if (uinp_fd == 0) {
        init_uinput = 0;
        return -1;
    }
    // Intialize the uInput device to NULL
    //  设置名称,版本,bustype
    memset(&uinp, 0, sizeof(uinp));
    strncpy(uinp.name, "libxTouchScreen", 16);
    uinp.id.version = 4;
    uinp.id.bustype = BUS_USB;

    uinp.absmin[ABS_MT_SLOT] = 0;
    uinp.absmax[ABS_MT_SLOT] = 20; // MT代表multi touch 多指触摸 最大手指的数量我们设置9,//电容屏ID有0x0B,0x11,暂时改成20.
    uinp.absmin[ABS_MT_POSITION_X] = 0; // 屏幕最小的X尺寸
    uinp.absmax[ABS_MT_POSITION_X] = 32767; // 屏幕最大的X尺寸
    uinp.absmin[ABS_MT_POSITION_Y] = 0; // 屏幕最小的Y尺寸
    uinp.absmax[ABS_MT_POSITION_Y] = 32767; //屏幕最大的Y尺寸
    uinp.absmin[ABS_MT_TRACKING_ID] = 0;
    uinp.absmax[ABS_MT_TRACKING_ID] = 65535;//按键码ID累计叠加最大值
     //  根据需要设置uinput属性
    //uinp.absmin[ABS_MT_PRESSURE] = 0;   
    //uinp.absmax[ABS_MT_PRESSURE] = 255;     //屏幕按下的压力值
    //uinp.absmin[ABS_MT_DISTANCE] = 0;
    //uinp.absmax[ABS_MT_DISTANCE] = 0;   //离表面距离
    //uinp.absmin[ABS_MT_TOUCH_MAJOR] = 0;
    //uinp.absmax[ABS_MT_TOUCH_MAJOR] = 15;
    //ABS_MT_WIDTH_MAJOR  宽度
    #if 0//(TOUCH_HAS_WIDTH == 1)
        uinp.absmin[ABS_MT_WIDTH_MAJOR] = 0;
        uinp.absmax[ABS_MT_WIDTH_MAJOR] = 1500;
        //uinp.absmax[ABS_MT_DISTANCE] = 1500;   //离表面距离
        //uinp.absmin[ABS_MT_DISTANCE] = 0;
    #endif
    if(!isNoWidth){
        uinp.absmin[ABS_MT_WIDTH_MAJOR] = 0;
        uinp.absmax[ABS_MT_WIDTH_MAJOR] = 1500;
        ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_WIDTH_MAJOR);
    }
    // Setup the uinput device
    ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);   //该设备支持按键
    ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);   //支持鼠标
    
    // Touch
    ioctl (uinp_fd, UI_SET_EVBIT,  EV_ABS); //支持触摸
    ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_SLOT);
    ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
    ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
    #if 0//(TOUCH_HAS_WIDTH == 1)
        ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_WIDTH_MAJOR);//weight
        //ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_DISTANCE);//height
    #endif
    ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);    
    ioctl (uinp_fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
    ioctl (uinp_fd, UI_SET_KEYBIT, BTN_TOUCH);
    
    //ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
    //ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
    //ioctl (uinp_fd, UI_SET_ABSBIT, ABS_MT_DISTANCE);

    // Create input device into input sub-system
    if (write(uinp_fd, &uinp, sizeof(uinp)) != sizeof(uinp)) {
        return -1;
    }

    if (ioctl(uinp_fd, UI_DEV_CREATE)) {
        return -1;
    }
    return 1;
}

打开设备,配置好属性,就虚拟成功了。接着就是将解析好的数据封装成event 发送即可。

  • 发送模拟事件
void doProcessCommand(unsigned char tmpBuff[])
{
    int width = 0;
    unsigned char nPosStartIndex = 0;
    unsigned int  unUsefulDotCnt = tmpBuff[VALID_DOT_NUMBER_IN_EACH_PACKGE];
    touchCount   = unUsefulDotCnt;
    for(int i = 0; i < DOTCNT_OF_PACKAGE; i++){
        struct timeval tv;
        gettimeofday(&tv,NULL);

        nPosStartIndex = i * CNT_OF_DOT + 5;
        action = tmpBuff[nPosStartIndex];
        id = tmpBuff[nPosStartIndex + 1];
        
        // record the useful dot count
        if(unUsefulDotCnt != 0x00){
            touchCount = unUsefulDotCnt;
        }

        if(id > TOUCH_COUNT){  // ID < 12
            printf( "\033[1;31;40m  Touch data error,id:%d. \n\033[0m", id);
            continue;//break;
        }

        //  pos (x,y)
        x = tmpBuff[nPosStartIndex+2] + (tmpBuff[nPosStartIndex+3] << 8);
        y = tmpBuff[nPosStartIndex+4] + (tmpBuff[nPosStartIndex+5] << 8);

        if(!isNoWidth){
            // Dot width
            width = tmpBuff[nPosStartIndex + 6] + (tmpBuff[nPosStartIndex + 7] << 8);
        }

        if(action == 0x02){
            if(preTouchAct[id] == 0x03 || preTouchAct[id] == 0x02){
                touchCount--;
                write_event_to_device(EV_ABS, ABS_MT_SLOT, id);
                write_event_to_device(EV_ABS, ABS_MT_TRACKING_ID, -1);
                if(touchCount == 0){
                    write_event_to_device(EV_KEY, BTN_TOUCH, 0);   // UP to Android
                    printf("[%s][%d]...... id = [%d] ALL UP .\n",__FUNCTION__,__LINE__,id);
                }else{
                    printf("[%s][%d]..... id = [%d]. ONE UP .\n",__FUNCTION__,__LINE__,id);  
                }
                write_event_to_device(EV_SYN, SYN_REPORT, 0);
                preTouchAct[id] = 0x00;  // set default when dot up
            }else if(preTouchAct[id] == 0x00){
                printf("[%s][%d]...... id = [%d] DOWN....\n",__FUNCTION__,__LINE__,id);
                count++;
                trackingId[id] = count;
                write_event_to_device(EV_ABS, ABS_MT_SLOT, id);
                write_event_to_device(EV_ABS, ABS_MT_TRACKING_ID, count);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_X, x);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_Y, y);

                if(!isNoWidth){
                    write_event_to_device(EV_ABS, ABS_MT_WIDTH_MAJOR, width);
                }
                write_event_to_device(EV_KEY, BTN_TOUCH, 1);  // DOWN to Android
                write_event_to_device(EV_SYN, SYN_REPORT, 0);
                preTouchAct[id] = action;
            }
        }else if(action == 0x03){
            if(preTouchAct[id] == 0x00){
                count++;
                trackingId[id] = count;
                write_event_to_device(EV_ABS, ABS_MT_SLOT, id);
                write_event_to_device(EV_ABS, ABS_MT_TRACKING_ID, count);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_X, x);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_Y, y);

                if(!isNoWidth){
                    write_event_to_device(EV_ABS, ABS_MT_WIDTH_MAJOR, width);
                }
                write_event_to_device(EV_KEY, BTN_TOUCH, 1);
                write_event_to_device(EV_SYN, SYN_REPORT, 0);
                preTouchAct[id] = action;
                //move
                write_event_to_device(EV_ABS, ABS_MT_SLOT, id);
                write_event_to_device(EV_ABS, ABS_MT_TRACKING_ID, trackingId[id]);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_X, x);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_Y, y);
                #if (TOUCH_HAS_WIDTH == 1)
                    write_event_to_device(EV_ABS, ABS_MT_WIDTH_MAJOR, width);
                #endif
                write_event_to_device(EV_SYN, SYN_REPORT, 0);
                tmpX[id] = x;
                tmpY[id] = y;
            }else if((preTouchAct[id] == 0x02)||(abs(tmpX[id] - x) > 17 || abs(tmpY[id] - y) > 30)){//最大坐标除以分辨率,拿到比例
                printf("[%s][%d]...... id = [%d] MOVE....\n",__FUNCTION__,__LINE__,id);
                write_event_to_device(EV_ABS, ABS_MT_SLOT, id);
                write_event_to_device(EV_ABS, ABS_MT_TRACKING_ID, trackingId[id]);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_X, x);
                write_event_to_device(EV_ABS, ABS_MT_POSITION_Y, y);

                if(!isNoWidth){
                    write_event_to_device(EV_ABS, ABS_MT_WIDTH_MAJOR, width);
                }
                write_event_to_device(EV_SYN, SYN_REPORT, 0);
                tmpX[id] = x;
                tmpY[id] = y;
                preTouchAct[id] = action;
            }
        }else if(action == 0x00){
            //
        }
        printf("\n\n\n");
    }
}

将解析后的数据(坐标,宽度,id,tracking_id,sync,report,up,down事件)通过
write_event_to_device 接口发送。

int write_event_to_device(int type, int code, int value) {
    if(init_uinput == 0) {
        if(setup_uinput_device() < 0) {
            return -1;
        }
    }
    struct input_event event; // Input device structure
    memset(&event, 0, sizeof(event));
    gettimeofday(&event.time, NULL);
    event.type = type;
    event.code = code;
    event.value = value;
    write(uinp_fd, &event, sizeof(event));//int ret = write(uinp_fd, &event, sizeof(event));
    return 1;
}

至此,完成了触摸框的触摸点数据,从串口到uinput 模拟设备的发送流程。后续android
的eventhub 模块监控 /dev/input/event* 节点,获取event,然后封装给上层使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值