对hidraw进行多点触摸分析
发现如果系统支持多点触摸的话,直接像内核文档这样上报是没问题的
但如果是拿了一个不支持多点的系统,还需要模拟出多点的效果
比如说上面这样,手动画一条,机器模拟一条的话,逻辑什么的都需要改改
官方文档
分析input event的数据
多点按下
多点发送是 开始先单点发送的指令 +另一个点
0003 D047 00000001 //好像代表手指1
0003 D057 00000001 //应该是对1手指的追踪
0003 D053 000020a1
0003 D054 00005a44
多点移动
/dev/input/event0: 0003 D047 00000000
/dev/input/event0: 0003 D053 0000425b
/dev/input/event0: 0003 D054 000033fa
/dev/input/event0: 0003 0000 0000425b
/dev/input/event0: 0003 0001 000033fa
/dev/input/event0: 0000 0000 00000000
多点结束
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0003 D057 ffffffff
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0003 D047 00000000
/dev/input/event0: 0003 D053 00003f3b
/dev/input/event0: 0003 D054 000026d2
/dev/input/event0: 0003 0000 00003f3b
/dev/input/event0: 0003 0001 000026d2
/dev/input/event0: 0000 0000 00000000
这些就不做什么解释了,很多地方都有,或者对应着内核文档里,命令进行查看就能知道里面的意思
不支持多点触摸的设备模拟多点信息
问题发现
如果我们用hidraw构造出数据,再用getevnt进行上报,那么会有大量重复的数据,比如下面这样
没想到hidraw的原生数据会是 hexdump的 16倍
原因1: 当x,y不变的时候,hidraw的伪造数据会持续上报,就会有这么多的重复数据
原因2: 其实如果吧上面下面三组数据一点点挑出来分析,会发现并不是像官方的文档描述的这样,啊,你用我的命令就好了啊
抓了三天头发后
如果是两根手指的触摸,应该会有下面这样的流程
我们只要把上报数据根据这样的格式进行上报
现在模仿出来的数据就很正常,接下来放一下代码
为什么点1拿了点0的数据 1有四个数据的时候,就会拿两个0的数据
代码
/********************************************************************************
* @file analyze_data.c
* @author duck
* @version V1.6.0
* @date 13-08-2021
* @brief 该文件用于分析hidraw触摸点的down/up数据,以及能统计丢up率
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,能进行5点及五点以下的统计,测试中功能
* 优异,随便舞动手指也不会出错
******************************************************************************
*/
#include "analyze_data.h"
/*
* @fun_name : -my_split
* @description : 把原始数据存入数组里
* @param - filp : 获取的hidraw原生数据(字符串指针)
* @return : 返回分割好的数据放入一个数组里
*/
unsigned int *analyze_split(char *str_data ,unsigned int * split_analyze_data) //把原始数据存入数组里
{
char *split = " ";
char *p;
p = strtok(str_data, split);
split_analyze_data[0] = (unsigned int)(atoi(p));
int split_i = 1;
for (split_i = 1; split_i < 40; split_i++)
{
p = strtok(NULL, split);
split_analyze_data[split_i] = (unsigned int)(atoi(p));
}
return split_analyze_data;
}
/*
* @fun_name : create_now_analyze_point
* @description : 构造出5个触摸点,并且进行赋值
* @param - filp : 获取的hidraw原生数据(hidraw分割好的数组)
* @return : NULL
*/
void create_now_analyze_point(unsigned int *split ,struct analyze_all_point* p_now_analyze_point)
{
int count;
for (count = 0; count < 5; count++)
{
p_now_analyze_point->touch[split[4 + 6 * count]].x_location = split[5 + 6 * count] + split[6 + 6 * count] * 256;
p_now_analyze_point->touch[split[4 + 6 * count]].y_location = split[7 + 6 * count] + split[8 + 6 * count] * 256;
p_now_analyze_point->touch[split[4 + 6 * count]].point_id = split[4 + 6 * count];
p_now_analyze_point->touch[split[4 + 6 * count]].touch_info = split[3 + 6 * count];
}
p_now_analyze_point->sec = split[0];
p_now_analyze_point->usec = split[1];
p_now_analyze_point->point_count = split[39];
}
/*
* @fun_name : creat_report
* @description : 根据触摸点构造出触摸事件
* @param - all_point now_analyze_point : 当前触摸点数据
* @param - all_point pre_analyze_point : 之前触摸点数据(进行对比)
* @param - report_event all_event[] : 构造的5个触摸事件
* @return : NULL
*/
void creat_report(struct analyze_all_point now_analyze_point, struct analyze_all_point pre_analyze_point)
{
int i;
for (i = 0; i < 5; i++)
{
//判断point_state
if (pre_analyze_point.touch[i].touch_info == 0 && now_analyze_point.touch[i].touch_info == 4)
record_data[i * 2] += 1; //表示按下
if (pre_analyze_point.touch[i].touch_info == 7 && now_analyze_point.touch[i].touch_info == 4)
record_data[i * 2 + 1] += 1; //表示抬起
if (pre_analyze_point.touch[i].touch_info == 4 && now_analyze_point.touch[i].touch_info == 4)
record_data[i * 2] += 1; //表示按下
}
}
/*
* @fun_name : analyze_report
* @description : 分析上报数据,对已知的down/up进行统计
* @param : NULL
* @return : NULL
*/
void analyze_report()
{
int the_num_of_up = 0;
int the_num_of_down = 0;
int i;
for (i = 0; i < 5; i++)
{
printf("the point %d down %d up %d\n", i, record_data[i * 2], record_data[i * 2 + 1]);
the_num_of_down += record_data[i * 2];
the_num_of_up += record_data[i * 2 + 1];
}
float loss_rate = (float)(the_num_of_up * 100 / the_num_of_down) / 100;
int count = loss_rate * 100;
printf("the sucess rate is %d%\n", count);
}
/*
* @fun_name : init_all_point
* @description : 对原始点触摸数据进行初始化赋值,好产生对照组
* @param - pre_analyze_point : 当前触摸点数据
* @return : NULL
*/
void init_all_point(struct analyze_all_point* p_pre_analyze_point)
{
p_pre_analyze_point->sec = 0;
p_pre_analyze_point->usec = 0;
int i;
for (i = 0; i < 5; i++)
{
p_pre_analyze_point->touch[i].touch_info = 0;
p_pre_analyze_point->touch[i].x_location = 0;
p_pre_analyze_point->touch[i].y_location = 0;
p_pre_analyze_point->touch[i].point_id = 0;
}
}
/*
* @fun_name : analyze_data
* @description : 分析上报数据,对原生hidraw数据进行清洗,重新分析统计
* @param : NULL
* @return : NULL
*/
int analyze_data()
{
FILE *fp;
char str[N + 1];
unsigned int *split;
unsigned int split_analyze_data[50]={0};
struct analyze_all_point now_analyze_point;
struct analyze_all_point pre_analyze_point;
printf("i am running\n");
if ((fp = fopen("./1.txt", "rt")) == NULL)
{
puts("Fail to open file!");
exit(0);
}
// 开始拿数据
init_all_point(&pre_analyze_point);
while (fgets(str, N, fp) != NULL)
{
split = analyze_split(str,split_analyze_data);
create_now_analyze_point(split ,&now_analyze_point);
creat_report(now_analyze_point, pre_analyze_point);
pre_analyze_point = now_analyze_point;
}
analyze_report();
fclose(fp);
sleep(5);
return 0;
}
/*******************************************************************************
* @file cartoon.c
* @author duck
* @version V1.7.0
* @date 16-08-2021
* @brief 由于在树莓派的系统实现不了多点触摸,但为了证明作者逻辑还算正确,所以作者用
* 控制台的形式,来进行多点触摸的划线
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
****
*/
#include "cartoon.h"
/*
* @fun_name : -change_buffer
* @description : 更新我们的buff,构造最先的点阵
* @param -x_location : 更新点的x坐标
* @param -y_location : 更新点多y坐标
* @return : 返回0成功
*/
void change_buffer(int x_location, int y_location)
{
cartoon_buffer[y_location][x_location] = 1;
}
/*
* @fun_name : -cartoon_report
* @description : 判断这次上报的方式,决定如何打点
* @param -all_event : 全部的触发事件
* @param -point_count : 全部的手指头
* @return : 返回0成功
*/
void cartoon_report(struct report_event all_event[5], int point_count)
{
int count;
int count_effective = 5;
for (count = 0; count < 5; count++)
{
if (all_event[count].effective == -1)
{
count_effective--;
if (all_event[count].point_state == 1 || all_event[count].point_state == 0)
count_effective++;
}
}
if (count_effective == 1)
{
for (count = 0; count < 5; count++)
{
if (all_event[count].point_state == -1)
continue;
if (all_event[count].effective == -1 && all_event[count].point_state != 1 && all_event[count].point_state != 0)
continue;
//如果是抬起
if (all_event[count].point_state == 1)
{
if (point_count == 1) //唯一的手指抬起
{ //通过真实存在判断只有一个点
continue;
}
else //一个手指不动,另一个抬起
{
continue;
}
}
//刚刚按下
if (all_event[count].point_state == 0)
{
if (point_count == 1) //唯一的手指按下
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24);
}
else //一个手指不动,另一个按下
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24); //同步一下事件
}
}
//保持按下
if (all_event[count].point_state == 2)
{
if (point_count == 1) //唯一的手指滑动
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24);
}
else //一个手指不动,另一个滑动
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24);
}
}
}
}
if (count_effective > 1)
{ //这时候就是多点按下了
for (count = 0; count < 5; count++)
{
if (all_event[count].point_state == -1)
continue;
if (all_event[count].effective == -1 && all_event[count].point_state != 1 && all_event[count].point_state != 0)
continue;
//如果是抬起
if (all_event[count].point_state == 1)
{
continue;
}
//多点按下
if (all_event[count].point_state == 0)
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24);
}
//多点滑动
if (all_event[count].point_state == 2)
{
if (count == 0) //构造出touch1的多点触摸上报事件
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24);
}
else
{
change_buffer(all_event[count].x_location / 24, all_event[count].y_location / 24); //同步一下事件
}
}
}
}
}
/*
* @fun_name : -frame_buffer
* @description : 根据buff进行画点
* @return : 返回0成功
*/
int frame_buffer()
{
int x, y;
for (y = 0; y < LINE; y++)
{
for (x = 0; x < ROW; x++)
{
if (cartoon_buffer[y][x] == 0)
{
printf("**");
}
else
{
printf(" ");
}
}
putchar('\n');
}
return 0;
}
/*
* @fun_name : -show_cartoon
* @description : 根据buff进行画点
* @return : 返回0成功
*/
int show_cartoon()
{
int ts;
int us;
FILE *fp_data;
int start = 0; //开始运行定义
char str[N + 1];
unsigned int *split;
unsigned int split_data[50] = {0}; //单次获取数据数组
struct report_event all_event[5];
struct all_point now_all_point; //本次数据的整理
struct all_point pre_all_point; //上次数据的整理
if ((fp_data = fopen("./1.txt", "rt")) == NULL)
{
puts("Fail to open file!");
exit(0);
}
// 构建虚拟设备
int ret = 0;
ret = creat_user_uinput();
if (ret < 0)
{
printf("%s:%d\n", __func__, __LINE__);
return -1; //error process.
}
sleep(5); // help you to 'hexdump -C /dev/input/event[X]' for test.
//开始复现
pre_all_point.sec = 0;
pre_all_point.touch[0].touch_info = 0;
while (fgets(str, N, fp_data) != NULL)
{
split = my_split(str, split_data);
//取出split的数据,构造出now_all_point
create_now_all_point(&now_all_point, split);
//用now_all_point 和 pre_all_point对比,构造出event
creat_report_event(now_all_point, pre_all_point, all_event);
//获取延时时间
ts = now_all_point.sec - pre_all_point.sec;
us = now_all_point.usec - pre_all_point.usec;
if (us < 0)
{
us = (1000000 - pre_all_point.usec) + now_all_point.usec;
ts -= 1;
}
if (ts < 0)
ts = (1000000 - pre_all_point.sec) + now_all_point.sec;
if (start == 0)
{
ts = 0;
us = 0;
}
start = 1;
usleep(us);
sleep(ts);
//上报event1,上报event2
cartoon_report(all_event, now_all_point.point_count);
pre_all_point = now_all_point;
frame_buffer();
}
//关闭文件
fclose(fp_data);
sleep(5);
close(uinput_fd);
return 0;
}
/********************************************************************************
* @file hidraw.c
* @author duck
* @version V1.5.0
* @date 12-08-2021
* @brief 文件用来保存hidraw原生数据,在此基础上增加了时间戳,对数据保存在1.txt上
* 为将来的复现数据基础做好准备
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#include "hid_raw.h"
#include "search_touch.h"
/*
* @fun_name : -hidraw_write
* @description : 把原始数据存1.txt里
* @param - the_str : 获取的hidraw原生数据(字符串指针)
* @return : 返回0成功
*/
int hidraw_write(char * the_str)
{
FILE* p = (fopen("./1.txt","a"));
if(p ==NULL){
printf("open error!\n");
return -1;
}
fputs(the_str,p);
free(the_str);
fclose(p);
return 0;
}
/*
* @fun_name : -hidraw_start
* @description : 把原始数据存1.txt里,并且在此基础上增加了时间戳
* @param : NULL
* @return : 返回0成功
*/
int hidraw_start()
{
struct hidraw_devinfo info;
char *path_name = scan_devices();
int fd = open(path_name, O_RDONLY);
if (fd == -1)
{
printf("touch device open fail\n");
return -1;
}
printf("open the device is %s \n",path_name);
free(path_name);
int rc = ioctl(fd, HIDIOCGRAWINFO, &info);
if (rc < 0)
{
printf("readerror!\n");
return 1;
}
printf("HID device info %04x:%04x:%04x is\n", info.bustype,info.vendor, info.product);
unsigned char buff[96];
printf("open successful\n");
while (1)
{
int i =0;
int size = read(fd, buffer, 64);
gettimeofday(&now_time, NULL); //获取当前时间
char * save_time = (char *)malloc(200); //保存时间数据
sprintf(save_time,"%d %d ",now_time.tv_sec,now_time.tv_usec);
hidraw_write(save_time);
for (i = 0; i < size; ++i)
{
char * save_data = (char *)malloc(200); //保存触摸数据
sprintf(save_data,"%d ",buffer[i]);
hidraw_write(save_data);
}
for (i = 0; i < size; ++i)
{
printf("%d ", buffer[i]);
}
printf("\n");
char * save_data = (char *)malloc(200); //保存触摸数据
sprintf(save_data,"\n");
hidraw_write(save_data);
}
return 0;
}
/*******************************************************************************
* @file search_touch.h
* @author duck
* @version V1.8.0
* @date 18-08-2021
* @brief 增加了自动识别触摸设备的功能,无论在哪个hidraw都能识别出来
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#include "search_touch.h"
static int is_event_device(const struct dirent *dir)
{
return strncmp(EVENT_DEV_NAME, dir->d_name, 5) == 0;
}
char *scan_devices(void)
{
struct dirent **namelist;
int i, ndev;
char *filename = NULL;
long propbit[BITS_TO_LONGS(INPUT_PROP_MAX)] = {0};
ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, alphasort);
if (ndev <= 0)
return NULL;
for (i = 0; i < ndev; i++) {
char fname[512];
int fd = -1;
snprintf(fname, sizeof(fname),
"%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name);
fd = open(fname, O_RDONLY);
if (fd < 0)
continue;
if ((ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit) < 0) ||
!(propbit[BIT_WORD(INPUT_PROP_DIRECT)] &
BIT_MASK(INPUT_PROP_DIRECT))) {
close(fd);
continue;
} else {
close(fd);
filename = malloc(strlen(DEV_INPUT_EVENT) +
strlen(EVENT_DEV_NAME) +
12);
if (!filename)
break;
sprintf(filename, "%s%d",
"/dev/hidraw", i);
break;
}
}
for (i = 0; i < ndev; ++i)
free(namelist[i]);
free(namelist);
return filename;
}
/********************************************************************************
* @file readraw.c
* @author duck
* @version V1.5.0
* @date 12-08-2021
* @brief 文件提供了构造虚拟设备和复现数据的所有函数
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#include "readraw.h"
/*
* @fun_name : -my_split
* @description : 把原始数据存入数组里
* @param - filp : 获取的hidraw原生数据(字符串指针)
* @return : 返回分割好的数据放入一个数组里
*/
unsigned int *my_split(char *str_data ,unsigned int * split_data )
{
char *split = " ";
char *p;
p = strtok(str_data, split);
split_data[0] = (unsigned int)(atoi(p));
int split_i = 1;
for (split_i = 1; split_i < 40; split_i++)
{
p = strtok(NULL, split);
split_data[split_i] = (unsigned int)(atoi(p));
}
return split_data;
}
/*
* @fun_name : create_now_all_point
* @description : 构造出5个触摸点,并且进行赋值
* @param - filp : 获取的hidraw原生数据(hidraw分割好的数组)
* @return : NULL
*/
void create_now_all_point(struct all_point *p_now_all_point, unsigned int *split)
{
int count;
for (count = 0; count < 5; count++)
{
p_now_all_point->touch[split[4 + 6 * count]].x_location = split[5 + 6 * count] + split[6 + 6 * count] * 256;
p_now_all_point->touch[split[4 + 6 * count]].y_location = split[7 + 6 * count] + split[8 + 6 * count] * 256;
p_now_all_point->touch[split[4 + 6 * count]].point_id = split[4 + 6 * count];
p_now_all_point->touch[split[4 + 6 * count]].touch_info = split[3 + 6 * count];
}
p_now_all_point->sec = split[0];
p_now_all_point->usec = split[1];
p_now_all_point->point_count = split[39];
}
/*
* @fun_name : creat_report_event
* @description : 根据触摸点构造出触摸事件
* @param - all_point now_all_point : 当前触摸点数据
* @param - all_point pre_all_point : 之前触摸点数据(进行对比)
* @param - report_event all_event[] : 构造的5个触摸事件
* @return : NULL
*/
void creat_report_event(struct all_point now_all_point, struct all_point pre_all_point, struct report_event all_event[5])
{
//只有按下的时候才需要上报id,所以touch分配到哪一个event都没问题
int i;
for (i = 0; i < 5; i++)
{
//判断point_state
if (pre_all_point.touch[i].touch_info == 0 && now_all_point.touch[i].touch_info == 4)
all_event[i].point_state = 0; //表示按下
if (pre_all_point.touch[i].touch_info == 4 && now_all_point.touch[i].touch_info == 4)
all_event[i].point_state = 0; //表示按下
if (pre_all_point.touch[i].touch_info == 7 && now_all_point.touch[i].touch_info == 4)
all_event[i].point_state = 1; //表示抬起
if (pre_all_point.touch[i].touch_info == 4 && now_all_point.touch[i].touch_info == 7)
all_event[i].point_state = 2; //接着滑动
if (pre_all_point.touch[i].touch_info == 7 && now_all_point.touch[i].touch_info == 7)
all_event[i].point_state = 2; //按着滑动
if (pre_all_point.touch[i].touch_info == 0 && now_all_point.touch[i].touch_info == 0)
all_event[i].point_state = -1; //该触摸点没有启用
if (pre_all_point.touch[i].touch_info == 4 && now_all_point.touch[i].touch_info == 0)
all_event[i].point_state = -1; //该触摸点没有启用
//分析x,y的位置
all_event[i].x_location = now_all_point.touch[i].x_location * 1824 / 32767;
all_event[i].y_location = now_all_point.touch[i].y_location * 984 / 32767;
//判断这个点是否有效
if (pre_all_point.touch[i].x_location == now_all_point.touch[i].x_location && pre_all_point.touch[i].y_location == now_all_point.touch[i].y_location)
all_event[i].effective = -1;
else
all_event[i].effective = 1;
all_event[i].point_id = now_all_point.touch[i].point_id;
}
}
/*
* @fun_name : creat_user_uinput
* @description : 对虚拟设备uinput创建成为触摸屏
* @return : 成功返回0
*/
int creat_user_uinput(void)
{
int i;
int ret = 0;
uinput_fd = open("/dev/uinput", O_RDWR | O_NDELAY);
if (uinput_fd < 0)
{
printf("%s:%d\n", __func__, __LINE__);
return -1; //error process.
}
//to set uinput dev
memset(&uinput_dev, 0, sizeof(struct uinput_user_dev));
snprintf(uinput_dev.name, UINPUT_MAX_NAME_SIZE, "uinput-custom-dev");
uinput_dev.id.version = 1;
uinput_dev.id.bustype = BUS_VIRTUAL;
uinput_dev.id.bustype = BUS_USB;
uinput_dev.absmin[ABS_MT_SLOT] = 0;
uinput_dev.absmax[ABS_MT_SLOT] = 5; // MT代表multi touch 多指触摸 最大手指的数量我们设置5
uinput_dev.absmin[ABS_MT_TOUCH_MAJOR] = 0;
uinput_dev.absmax[ABS_MT_TOUCH_MAJOR] = 15;
uinput_dev.absmin[ABS_MT_POSITION_X] = 0; // 屏幕最小的X尺寸
uinput_dev.absmax[ABS_MT_POSITION_X] = 32767; // 屏幕最大的X尺寸
uinput_dev.absmin[ABS_MT_POSITION_Y] = 0; // 屏幕最小的Y尺寸
uinput_dev.absmax[ABS_MT_POSITION_Y] = 32767; //屏幕最大的Y尺寸
uinput_dev.absmin[ABS_MT_TRACKING_ID] = 0;
uinput_dev.absmin[ABS_MT_PRESSURE] = 0;
uinput_dev.absmax[ABS_MT_PRESSURE] = 255; //屏幕按下的压力值
// Touch 让ev =b
ioctl(uinput_fd, UI_SET_EVBIT, EV_ABS); //支持触摸
ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN);
ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY);
//调整ABS 为2608000 3
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_X);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_Y);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_SLOT);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_WIDTH_MAJOR);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ioctl(uinput_fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
ioctl(uinput_fd, UI_SET_KEYBIT, BTN_TOUCH);
for (i = 0; i < 256; i++)
{
ioctl(uinput_fd, UI_SET_KEYBIT, i);
}
ioctl(uinput_fd, UI_SET_MSCBIT, KEY_CUSTOM_UP);
ioctl(uinput_fd, UI_SET_MSCBIT, KEY_CUSTOM_DOWN);
ret = write(uinput_fd, &uinput_dev, sizeof(struct uinput_user_dev));
if (ret < 0)
{
printf("%s:%d\n", __func__, __LINE__);
return ret; //error process.
}
ret = ioctl(uinput_fd, UI_DEV_CREATE);
if (ret < 0)
{
printf("%s:%d\n", __func__, __LINE__);
close(uinput_fd);
return ret; //error process.
}
return ret;
}
/*
* @fun_name : report_key
* @description : 上报一个触摸事件
* @param - type : 事件种类
* @param - keycode : 该种的key
* @param - value : 对应上面key的value
* @return : 成功返回0
*/
int report_key(unsigned int type, unsigned int keycode, unsigned int value)
{
struct input_event key_event;
int ret;
memset(&key_event, 0, sizeof(struct input_event));
gettimeofday(&key_event.time, NULL);
key_event.type = type;
key_event.code = keycode;
key_event.value = value;
ret = write(uinput_fd, &key_event, sizeof(struct input_event));
if (ret < 0)
{
printf("%s:%d\n", __func__, __LINE__);
return ret; //error process.
}
return 0;
}
/*
* @fun_name : device_writeEvent
* @description : 上报一个触摸事件
* @param - type : 事件种类
* @param - keycode : 该种的key
* @param - value : 对应上面key的value
* @return : 成功返回0
*/
static int device_writeEvent(unsigned int type, unsigned int keycode, unsigned int value)
{
struct input_event ev;
// 保存log数据
char *save_log_data = (char *)malloc(200);
sprintf(save_log_data, "%04x %04x %08x \n", type, keycode, value);
free(save_log_data);
memset(&ev, 0, sizeof(struct input_event));
ev.type = type;
ev.code = keycode;
ev.value = value;
if (write(uinput_fd, &ev, sizeof(struct input_event)) < 0)
{
char *mesg = strerror(0);
printf("nibiru uinput errormag info :%s\n", mesg);
return 0;
}
return 1;
}
/*
* @fun_name : start_report
* @description : 对全部的event进行上报
* @param - all_event : 全部的事件结构体数组
* @param - point_count : 此时有多少事件是能够被input识别的
* @return : NULL
*/
void start_report(struct report_event all_event[5], int point_count)
{
int count;
int count_effective = 5;
for (count = 0; count < 5; count++)
{
if (all_event[count].effective == -1)
{
count_effective--;
if (all_event[count].point_state == 1 || all_event[count].point_state == 0)
count_effective++;
}
}
if (count_effective == 1)
{
for (count = 0; count < 5; count++)
{
if (all_event[count].point_state == -1)
continue;
if (all_event[count].effective == -1 && all_event[count].point_state != 1 && all_event[count].point_state != 0)
continue;
//如果是抬起
if (all_event[count].point_state == 1)
{
if (point_count == 1) //唯一的手指抬起
{ //通过真实存在判断只有一个点
//device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id); //其实不用加的,但是这个不太听话,乱搞,以至于不上报
device_writeEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); //代表手指消失
device_writeEvent(1, 330, 0); // 表示抬起指令
device_writeEvent(0, 0, 0); //同步一下事件
}
else //一个手指不动,另一个抬起
{
device_writeEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); //代表手指消失
device_writeEvent(0, 0, 0); //同步一下事件
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[0].point_id);
}
}
//刚刚按下
if (all_event[count].point_state == 0)
{
if (point_count == 1) //唯一的手指按下
{
device_writeEvent(EV_ABS, ABS_MT_TRACKING_ID, all_event[count].point_id + 40); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标
device_writeEvent(1, 330, 1); //表示按下指令
device_writeEvent(EV_ABS, ABS_X, all_event[count].x_location); //单点下 x坐标
device_writeEvent(EV_ABS, ABS_Y, all_event[count].y_location); //单点下 y坐标
device_writeEvent(0, 0, 0); //同步一下事件
}
else //一个手指不动,另一个按下
{
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_TRACKING_ID, all_event[count].point_id + 40); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标
device_writeEvent(0, 0, 0); //同步一下事件
}
}
//保持按下
if (all_event[count].point_state == 2)
{
if (point_count == 1) //唯一的手指滑动
{
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标
device_writeEvent(EV_ABS, ABS_X, all_event[count].x_location); //单点下 x坐标
device_writeEvent(EV_ABS, ABS_Y, all_event[count].y_location); //单点下 y坐标
device_writeEvent(0, 0, 0); //同步一下事件
}
else //一个手指不动,另一个滑动
{
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标
device_writeEvent(EV_ABS, ABS_X, all_event[0].x_location); //单点下 x坐标
device_writeEvent(EV_ABS, ABS_Y, all_event[0].y_location); //单点下 y坐标
device_writeEvent(0, 0, 0); //同步一下事件
}
}
}
}
if (count_effective > 1)
{ //这时候就是多点按下了
for (count = 0; count < 5; count++)
{
if (all_event[count].point_state == -1)
continue;
if (all_event[count].effective == -1 && all_event[count].point_state != 1 && all_event[count].point_state != 0)
continue;
//如果是抬起
if (all_event[count].point_state == 1)
{
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id);
device_writeEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); //代表手指消失
device_writeEvent(EV_ABS, ABS_X, all_event[0].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_Y, all_event[0].y_location); //多点下y的坐标
device_writeEvent(0, 0, 0); //同步一下事件
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[0].point_id);
}
//多点按下
if (all_event[count].point_state == 0)
{
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_TRACKING_ID, all_event[count].point_id + 40); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标
device_writeEvent(0, 0, 0); //同步一下事件
}
//多点滑动
if (all_event[count].point_state == 2)
{
if (count == 0) //构造出touch1的多点触摸上报事件
{
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标;
}
else
{
device_writeEvent(EV_ABS, ABS_MT_SLOT, all_event[count].point_id); //代表手指id
device_writeEvent(EV_ABS, ABS_MT_POSITION_X, all_event[count].x_location); //多点下x坐标
device_writeEvent(EV_ABS, ABS_MT_POSITION_Y, all_event[count].y_location); //多点下y的坐标
device_writeEvent(EV_ABS, ABS_X, all_event[0].x_location); //单点下 x坐标
device_writeEvent(EV_ABS, ABS_Y, all_event[0].y_location); //单点下 y坐标
device_writeEvent(0, 0, 0); //同步一下事件
}
}
}
}
}
/*
* @fun_name : read_raw
* @description : 读取原生数据,调用虚拟设备,并且进行复现
* @return : 返回0表示成功
*/
int read_raw()
{
int ts;
int us;
FILE *fp_data;
int start = 0; //开始运行定义
char str[N + 1];
unsigned int *split;
unsigned int split_data[50] = {0}; //单次获取数据数组
struct report_event all_event[5];
struct all_point now_all_point; //本次数据的整理
struct all_point pre_all_point; //上次数据的整理
if ((fp_data = fopen("./1.txt", "rt")) == NULL)
{
puts("Fail to open file!");
exit(0);
}
// 构建虚拟设备
int ret = 0;
ret = creat_user_uinput();
if (ret < 0)
{
printf("%s:%d\n", __func__, __LINE__);
return -1; //error process.
}
sleep(5); // help you to 'hexdump -C /dev/input/event[X]' for test.
//开始复现
pre_all_point.sec = 0;
pre_all_point.touch[0].touch_info = 0;
while (fgets(str, N, fp_data) != NULL)
{
printf("starting %s\n", str);
split = my_split(str,split_data);
//取出split的数据,构造出now_all_point
create_now_all_point(&now_all_point, split);
//用now_all_point 和 pre_all_point对比,构造出event
creat_report_event(now_all_point, pre_all_point, all_event);
//获取延时时间
ts = now_all_point.sec - pre_all_point.sec;
us = now_all_point.usec - pre_all_point.usec;
if (us < 0)
{
us = (1000000 - pre_all_point.usec) + now_all_point.usec;
ts -= 1;
}
if (ts < 0)
ts = (1000000 - pre_all_point.sec) + now_all_point.sec;
if (start == 0)
{
ts = 0;
us = 0;
}
start = 1;
usleep(us);
sleep(ts);
//上报event1,上报event2
start_report(all_event, now_all_point.point_count);
pre_all_point = now_all_point;
}
//关闭文件
fclose(fp_data);
sleep(5);
close(uinput_fd);
return 0;
}
/********************************************************************************
* @file analyze_data.h
* @author duck
* @version V1.6.0
* @date 13-08-2021
* @brief 该文件用于分析hidraw触摸点的down/up数据,以及能统计丢up率
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,能进行5点及五点以下的统计,测试中功能
* 优异,随便舞动手指也不会出错
******************************************************************************
*/
#ifndef _ANALYZE_DATA_H
#define _ANALYZE_DATA_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <linux/uinput.h>
#include <linux/input.h>
//我的变量
#define N 150
static int record_data[16] = {0};
/**** 单触摸点定义 ****/
struct analyze_touch_point{
int point_id;
int x_location;
int y_location;
int touch_info;
};
/**** 所有触摸点数据定义 ****/
struct analyze_all_point{
int sec;
int usec;
int point_count;
struct analyze_touch_point touch[5];
};
unsigned int *analyze_split(char *str_data ,unsigned int * split_analyze_data);
void create_now_analyze_point(unsigned int *split ,struct analyze_all_point* p_now_analyze_point);
void creat_report(struct analyze_all_point now_all_point ,struct analyze_all_point pre_all_point );
void analyze_report();
#endif
/*******************************************************************************
* @file cartoon.h
* @author duck
* @version V1.7.0
* @date 16-08-2021
* @brief 由于在树莓派的系统实现不了多点触摸,但为了证明作者逻辑还算正确,所以作者用
* 控制台的形式,来进行多点触摸的划线
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#ifndef _CARTOON_H
#define _CARTOON_H
#include "readraw.h"
#define LINE 41 //给控制台行
#define ROW 76 //给控制台列
static int cartoon_buffer[LINE][ROW] = {0};
int show_cartoon();
#endif
/********************************************************************************
* @file hidraw.h
* @author duck
* @version V1.5.0
* @date 12-08-2021
* @brief 文件用来保存hidraw原生数据,在此基础上增加了时间戳,对数据保存在1.txt上
* 为将来的复现数据基础做好准备
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#ifndef _HID_RAW_H
#define _HID_RAW_H
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#define MOUSE_DEV_PATH "/dev/hidraw0"
unsigned char buffer[1000];//存放一次的hidraw数据
struct timeval now_time; //增加时间节点
int hidraw_start();
int hidraw_write(char * the_str);
#endif
/********************************************************************************
* @file read_raw.h
* @author duck
* @version V1.5.0
* @date 12-08-2021
* @brief 文件构造了虚拟设备并且对多指触摸进行复现
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#ifndef _READRAW_H
#define _READRAW_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <linux/uinput.h>
#include <linux/input.h>
#define N 150 //单次获取数据大小
#define KEY_CUSTOM_UP 0x20
#define KEY_CUSTOM_DOWN 0x30
static int uinput_fd; //虚拟文件指针
static struct uinput_user_dev uinput_dev; //虚拟设备节点
/**** 单触摸点定义 ****/
struct touch_point
{
int point_id;
int x_location;
int y_location;
int touch_info;
};
/**** 所有触摸点数据定义 ****/
struct all_point
{
int sec;
int usec;
int point_count;
struct touch_point touch[5];
};
/**** 所有上报事件定义 ****/
struct report_event
{
int point_state; //判断抬起和按下或者保持下按(0,1,2)
int point_id;
int x_location;
int y_location;
int effective;
};
unsigned int *my_split(char *str_data ,unsigned int * split_data );
void create_now_all_point(struct all_point *p_now_all_point, unsigned int *split);
void creat_report_event(struct all_point now_all_point, struct all_point pre_all_point, struct report_event all_event[5]);
int creat_user_uinput(void);
int report_key(unsigned int type, unsigned int keycode, unsigned int value);
static int device_writeEvent(unsigned int type, unsigned int keycode, unsigned int value);
void start_report(struct report_event all_event[5], int point_count);
int read_raw();
#endif
/*******************************************************************************
* @file search_touch.h
* @author duck
* @version V1.8.0
* @date 18-08-2021
* @brief 增加了自动识别触摸设备的功能,无论在哪个hidraw都能识别出来
******************************************************************************
* @attention
* 目前的固件仅供指导,目的是为完成demo,现在能进行两点触摸的复现,对于两点以上
* 还有一丝逻辑没有理清, 复现的不准确来自于和input子系统判断的时间戳不同
******************************************************************************
*/
#ifndef _SEARCH_TOUCH_H
#define _SEARCH_TOUCH_H
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include<stdlib.h>
#include<string.h>
#ifndef INPUT_PROP_MAX
# define INPUT_PROP_MAX 0x1f
#endif
#ifndef INPUT_PROP_DIRECT
# define INPUT_PROP_DIRECT 0x01
#endif
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define BIT(nr) (1UL << (nr))
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define BITS_PER_BYTE 8
#define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE)
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DEV_INPUT_EVENT "/dev/input"
#define EVENT_DEV_NAME "event"
char *scan_devices(void);
#endif