最近博主我接到要求,要对研发的一个产品的触摸屏进行测试,大概内容就是要不断地随机点击触摸屏,来测试软件会不会意外退出(鲁棒性验证)。这里我把开发过程记录下来,方便参考。
我们的触摸屏使用的是ads7846这一款触摸屏控制器,找到内核下对应的驱动文件:/drivers/input/touchscreen/ads7846.c。
接下来就要开始修改这个驱动了。我的修改思路是这样的:在这个驱动的基础上增加一个外节点,比如叫做input_emu。然后应用层通过ioctl来操作这个设备节点,从而触发定时器。在这个定时器里面来上报坐标、键值、压力这些关键信息,从而模拟点击这个过程:
1.修改ads7846_probe函数,增加如下几句代码,从而建立input_emu这个节点:
major = register_chrdev(0, "touch_emulate", &touch_emulate_fops);
cls = class_create(THIS_MODULE, "touch_emulate");
device_create(cls, NULL, MKDEV(major, 0), NULL, "input_emu"); /* /dev/input_emu */
emulate_dev = ts;
init_timer(&touch_emulate_timer);
touch_emulate_timer.function = touch_timer_function;
2.构建touch_emulate_fops结构体,touch_emulate_ioctl函数以及touch_timer_function定时器服务函数:
long touch_emulate_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case EMULATE_START:
{
touch_emulate_timer.expires = jiffies + 1; //10ms后执行,1HZ定义是100
add_timer(&touch_emulate_timer); //将定时器加入定时器等待队列中
printk("touch_emulate_ioctl add_timer\n");
break;
}
case EMULATE_STOP:
{
del_timer(&touch_emulate_timer);
printk("touch_emulate_ioctl del_timer\n");
break;
}
default:
break;
}
return 0;
}
struct file_operations touch_emulate_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = touch_emulate_ioctl,
};
void touch_timer_function(unsigned long data)
{
static unsigned int x = 0;
static unsigned int y = 0;
/* 随机生成坐标,范围: 2bytes, 0-65535 */
get_random_bytes(&x, 2);
get_random_bytes(&y, 2);
x = (x % 4096); //ads7846的采样精度为12位,范围是0-4096,把随机生成的值限制在该范围。
y = (y % 4096);
/* 模拟触屏事件,上报坐标值 */
input_report_abs(emulate_dev->input, ABS_X, x);
input_report_abs(emulate_dev->input, ABS_Y, y);
input_report_abs(emulate_dev->input, ABS_PRESSURE, 1);
/* BTN_TOUCH为点击事件,单点触摸中需要上报点击事件*/
input_report_key(emulate_dev->input, BTN_TOUCH, 1); //上报BTN_TOUCH按键值按下
input_sync(emulate_dev->input); //上报同步事件,通知系统有事件上报
/* 松开*/
input_report_abs(emulate_dev->input, ABS_PRESSURE, 0); //上报压力值为0
input_report_key(emulate_dev->input, BTN_TOUCH, 0); //上报BTN_TOUCH按键值松开
input_sync(emulate_dev->input);
mod_timer(&touch_emulate_timer, jiffies + 1 * HZ);
}
3.重新编译内核(或者重新编译模块,这个得看内核配置中该驱动的配置模式)。
4.编写控制程序来开启或者关闭这个模拟的功能:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define EMULATE_START 1
#define EMULATE_STOP 0
/* Usage:
* ./touch_emulate start
* ./touch_emulate stop
*/
void print_usage(char *file)
{
printf("Usage:\n");
printf("%s start\n", file);
printf("%s stop\n", file);
}
int main(int argc, char **argv)
{
int fd;
if(argc != 2)
{
print_usage(argv[0]);
return -1;
}
fd = open("/dev/input_emu", O_RDWR);
if(fd < 0)
{
printf("Can't open /dev/input_emu!\n");
return -1;
}
if (strcmp(argv[1], "start") == 0)
{
ioctl(fd, EMULATE_START);
}
else if(strcmp(argv[1], "stop") == 0)
{
ioctl(fd, EMULATE_STOP);
}
else
{
print_usage(argv[0]);
return -1;
}
return 0;
}
5.测试:
重新加载这个驱动,然后运行编译好的控制程序:
./touch_emulate start
通过hexdump来观察输出:
cat /dev/input/touchscreen0 | hexdump
输出以下信息:
0000f00 2d42 5780 22b5 0000 0003 0000 00a8 0000
0000f10 2d42 5780 22b5 0000 0003 0001 07eb 0000
0000f20 2d42 5780 22b5 0000 0003 0018 0001 0000
0000f30 2d42 5780 22b5 0000 0001 014a 0001 0000
0000f40 2d42 5780 22b5 0000 0000 0000 0000 0000
0000f50 2d42 5780 22dc 0000 0003 0018 0000 0000
0000f60 2d42 5780 22dc 0000 0001 014a 0000 0000
0000f70 2d42 5780 22dc 0000 0000 0000 0000 0000
分析输出信息:
hexdump序列号 秒 微秒 type code value
绝对坐标事件 code=ABS_X X坐标值
0000f00 2d42 5780 22b5 0000 0003 0000 00a8 0000
绝对坐标事件 code=ABS_Y Y坐标值
0000f10 2d42 5780 22b5 0000 0003 0001 07eb 0000
code=ABS_PRESSURE 压力值
0000f20 2d42 5780 22b5 0000 0003 0018 0001 0000
键盘事件 code=触摸按键 value=1(按下)
0000f30 2d42 5780 22b5 0000 0001 014a 0001 0000
同步事件
0000f40 2d42 5780 22b5 0000 0000 0000 0000 0000
0000f50 2d42 5780 22dc 0000 0003 0018 0000 0000
0000f60 2d42 5780 22dc 0000 0001 014a 0000 0000
0000f70 2d42 5780 22dc 0000 0000 0000 0000 0000
可以看到,输出的顺序和我们定时器里面的上报顺序是一样的。