Imx6使用的触摸屏控制芯片是ft5x06系列的,对应的文件为:ft5x06_ts.c。
Ft5x06_ts驱动涉及的内容如下:
1. I2C驱动框架。
2. 中断子系统,中断分层思想。
3. Workqueue机制。
4. Input输入子系统。
如上图所示,是触摸板与cpu的连接图,触摸板没有使用cpu的AD转换装置,ft5x06内部自带AD转换,将x y坐标通过I2C传送出来。当用户手指接触触摸屏时,CAP_TCH_INT0产生中断,在中断处理函数中,将数据读取出来,并通过处理传输到上层。
引脚功能配置
1. I2C2功能初始化:
查看原理图可知道,触摸板与cpu的I2C-2相连。
在board-mx6q_sarbresd.h中找到mx6q_sabresd_pads[]数组,在数组中添加:
/* I2C2, Camera, MIPI */
MX6Q_PAD_KEY_COL3__I2C2_SCL,
MX6Q_PAD_KEY_ROW3__I2C2_SDA,
2.将A16(NANDF_ALE)引脚设置为中断模式
仍然在mx6q_sabresd_pads[]数组中添加:
/* CAP_TCH_INT0*/
MX6Q_PAD_NANDF_ALE__GPIO_6_8,
板级文件添加
board-mx6q_sarbresd.c中找到mxc_i2c1_board_info2[]数组,这里很奇怪,为什么是mxc_i2c1?这是飞思卡尔的定义,在原理图中,I2C以1开头,而在驱动中,I2C又是以I2C0开头。在xc_i2c1_board_info2[]添加:
{
I2C_BOARD_INFO("ft5x0x_ts", 0x38),
.irq = gpio_to_irq(SABRESD_CAP_TCH_INT0),
},
通过查看原理图,和芯片手册,可以知道Ft5x06_ts的地址是0x38。将触摸板的设备名字取为“ft5x06_ts”,I2C通信的地址为0x38。
.irq = gpio_to_irq(SABRESD_CAP_TCH_INT0),设置A16引脚的中断号,gpio_to_irq是飞思卡尔提供的一个接口函数,宏SABRESD_CAP_TCH_INT0的定义为:
#define SABRESD_CAP_TCH_INT0 IMX_GPIO_NR(6, 8)
关于如何使用引脚,参考:
http://blog.csdn.net/sddsighhz/article/details/36653517
板级文件添加好了之后,接下来分析驱动,驱动对应的文件是:ft5x06_ts.c。
老方法,驱动的入口函数是module_init:
module_init(ft5x0x_ts_init);
static int __init ft5x0x_ts_init(void)
{
int ret;
//printk("ft5x0x_ts_init\n");
ret =i2c_add_driver(&ft5x0x_ts_driver);
//printk("ret=%d\n",ret);
return ret;
}
ft5x0x_ts_init函数中注册了一个i2c驱动,找到ft5x0x_ts_driver结构。
static struct i2c_driver ft5x0x_ts_driver = {
.probe = ft5x0x_ts_probe,
.remove = __devexit_p(ft5x0x_ts_remove),
.id_table = ft5x0x_ts_id,
.driver = {
.name = FT5X0X_NAME,
.owner = THIS_MODULE,
},
};
I2C设备和驱动是通过.name匹配的,FT5X0X_NAME宏就是ft5x0x_ts。
#defineFT5X0X_NAME "ft5x0x_ts"
前面板级文件中,有:I2C_BOARD_INFO("ft5x0x_ts",0x38)正好和驱动中的FT5X0X_NAME匹配,配备成功之后,就会运行.probe。
接下来分析ft5x0x_ts_probe函数
代码就不全部贴出来了,分析关键部分。
if (!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {
err =-ENODEV;
goto exit_check_functionality_failed;
}
上面代码判断i2c适配器的所支持的通信方法,如果这个适配器不具有I2C功能,则失败。
ft5x0x_ts = kzalloc(sizeof(struct ft5x0x_ts_data),GFP_KERNEL);
//ft5x0x_ts= kmalloc(sizeof(struct ft5x0x_ts_data), GFP_KERNEL);
if(!ft5x0x_ts) {
err =-ENOMEM;
goto exit_alloc_data_failed;
}
上述代码分配一个structft5x0x_ts_data结构,这个结构是自己定义的,我们看看这个结构,并注释好结构中的内容:
structft5x0x_ts_data {
struct input_dev *input_dev;//input输入设备结构
struct ts_event event;//飞凌定义的触摸结构,包括x坐标,y坐标等。
struct work_struct pen_event_work;//工作结构体,用于中断分层
struct workqueue_struct *ts_workqueue;//工作队列结构体,用于处理中断下部分工作
struct early_suspend early_suspend;//安卓的休眠机制。
struct mutex device_mode_mutex; /* Ensures that only one function canspecify the Device Mode at a time. */
};
reqirq_id = client->irq;
这行代码是获取板级文件中的中断号。 .irq =gpio_to_irq(SABRESD_CAP_TCH_INT0)
i2c_set_clientdata(client, ft5x0x_ts);
将自定义的设备结构ft5x0x_ts赋给设备驱动client的私有指针.
mutex_init(&ft5x0x_ts->device_mode_mutex);
初始化互斥锁。
INIT_WORK(&ft5x0x_ts->pen_event_work,ft5x0x_ts_pen_irq_work);
初始化工作队列。待会儿需要关注ft5x0x_ts_pen_irq_work。
ft5x0x_ts->ts_workqueue =create_singlethread_workqueue(dev_name(&client->dev));
创建workqueue,只创建一个内核线程。它和create_workqueue区别:create_workqueue用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。
err =request_irq(reqirq_id, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING,"ft5x0x_ts", ft5x0x_ts);
申请中断,采用的是下降沿的方式触发中断,当中断产生时,进入到ft5x0x_ts_interrupt函数中执行。
disable_irq(reqirq_id);
使中断失效。
input_dev = input_allocate_device();
分配输入型设备结构
ft5x0x_ts->input_dev = input_dev;
将输入型设备结构复制给struct ft5x0x_ts结构中的input_dev成员变量。
接下来的代码,将重点分析:
#ifdef CONFIG_ANDROID
set_bit(ABS_MT_TOUCH_MAJOR,input_dev->absbit);
set_bit(ABS_MT_POSITION_X,input_dev->absbit);
set_bit(ABS_MT_POSITION_Y,input_dev->absbit);
set_bit(ABS_MT_WIDTH_MAJOR,input_dev->absbit);
input_set_abs_params(input_dev,
ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_TRACKING_ID, 0, 5, 0, 0);
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
#if CFG_SUPPORT_TOUCH_KEY
//setup keycode area
set_bit(EV_SYN, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //new
input_dev->keycode = tsp_keycodes_forlinx;
for(i = 0;i < CFG_NUMOFKEYS; i++)
{
input_set_capability(input_dev, EV_KEY,((int*)input_dev->keycode)[i]);
tsp_keystatus_forlinx[i] = KEY_RELEASE;
}
#endif
#else
set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_ABS,input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
input_set_abs_params(input_dev, ABS_X, TOUCHSCREEN_MINX,TOUCHSCREEN_MAXX, 0, 0);
input_set_abs_params(input_dev, ABS_Y, TOUCHSCREEN_MINY,TOUCHSCREEN_MAXY, 0, 0);
input_set_abs_params(input_dev,ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
#endif
首先,上述这段代码用宏控制用于安卓还是linux,安卓设备中,一般支持多点触控,在上报input event的时候,需要设置多点触控的相关属性,以便于在中断中实现多点触控的协议。Linux+QT设备,一般不需要使用多点触控的功能。
set_bit(ABS_MT_TOUCH_MAJOR,input_dev->absbit);ABS_MT_TOUCH_MAJOR:接触区域的长轴的长度。该长度应该按接触表面的单位提供。如果表面的分辨率是X-Y,则ABS_MT_TOUCH_MAJOR可能的最大值是sqrt(X^2 + Y^2)。
set_bit(ABS_MT_POSITION_X,input_dev->absbit);ABS_MT_POSITION_X:接触中心的X坐标。
set_bit(ABS_MT_POSITION_Y, input_dev->absbit);ABS_MT_POSITION_Y:接触中心的Y坐标。
set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);ABS_MT_WIDTH_MAJOR:工具轮廓区域短轴的表面单位长度,圆形的话可以被忽略。
input_set_abs_params(input_dev,ABS_MT_POSITION_X, 0,SCREEN_MAX_X, 0, 0);设置x坐标取值范围。SCREEN_MAX_X为800
input_set_abs_params(input_dev,ABS_MT_POSITION_Y,0, SCREEN_MAX_Y, 0, 0);设置y坐标取值范围。SCREEN_MAX_X为480
input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR,0, PRESS_MAX, 0, 0);将触摸点看成一个椭圆,这里是设置它的长轴长度。这个是可选项,并不影响正常使用。PRESS_MAX是255.
input_set_abs_params(input_dev,ABS_MT_WIDTH_MAJOR,0, 200, 0, 0); 如果设备支持ABS_MT_WIDTH_MAJOR这个事件,那么此事件可以提供手指触摸接触面积大小。
ABS_MT_TOUCH_MAJOR表示了手指接触TP的直径的近似。ABS_MT_WIDTH_MAJOR是手指的直径的近似。当手指和触摸屏接触的越紧密,则压力越大,手指和屏幕接触的面积(直径)也会越大。而手指的直径通常是一个常量。这样ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR就可以用来表示压力了。而且这个值在[0,1)间。对于某些提供了压力值的TP,可以使用ABS_MT_PRESSURE来替代ABS_MT_TOUCH_MAJOR.
input_set_abs_params(input_dev,ABS_MT_TRACKING_ID,0, 5, 0, 0);设置上报的id数目为5个。ABS_MT_TRACKING_ID,用来支持硬件跟踪多点信息,即该点属于哪一条线等。
对于触摸屏,必须支持的事件类型有以下这么三个
set_bit(EV_SYN, input_dev->evbit); //设备同步,每次触摸完成以后都要发送一个同步事件,来表明这次触摸已经完成
set_bit(EV_ABS, input_dev->evbit); //绝对坐标事件,触摸屏每次发送的坐标都是绝对坐标,不同于鼠标的相对坐标
set_bit(EV_KEY, input_dev->evbit); //按键事件,每次触摸都有一个
#if CFG_SUPPORT_TOUCH_KEY
//setup keycode area
set_bit(EV_SYN, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); //new
input_dev->keycode = tsp_keycodes_forlinx;
for(i = 0;i < CFG_NUMOFKEYS; i++)
{
input_set_capability(input_dev,EV_KEY, ((int*)input_dev->keycode)[i]);
tsp_keystatus_forlinx[i] = KEY_RELEASE;
}
#endif
这部分代码是支持屏幕之外的触摸按键的,就是我们安卓手机home键那一排的三个实体的按键(back home menu),这里要却别于安卓的虚拟按键,有很多厂商在显示屏内部也搞了三个虚拟按键(back home menu),虚拟按键是在framework层实现的,这里我们不管。
代码中的input_set_capability(input_dev, EV_KEY,((int*)input_dev->keycode)[i]); 用于记录本设备对于哪些事件感兴趣,这里把(back home menu)按键记录下来了。我们知道,实体按键是和触摸屏连在一起的,在LCD屏的显示区域外,当触摸屏发现x坐标>800,或者y坐标> 480的时候,我们就需要去处理本设备“感兴趣”事件。
Linux的部分先不分析,比较简单。
继续分析ft5x0x_ts_probe。
input_dev->name= FT5X0X_NAME;
设置input设备的名字为ft5x0x_ts。
err = input_register_device(input_dev);
注册input设备驱动,代码运行到这儿,linux内核中就可以在设备的/dev/input/目录下看到相应的eventX设备了,至于是event几,要看内核的分配。
#ifdef CONFIG_HAS_EARLYSUSPEND
//printk("==register_early_suspend=\n");
ft5x0x_ts->early_suspend.level= EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ft5x0x_ts->early_suspend.suspend= ft5x0x_ts_suspend;
ft5x0x_ts->early_suspend.resume = ft5x0x_ts_resume;
register_early_suspend(&ft5x0x_ts->early_suspend);
#endif
上面的这段代码是与安卓休眠机制有关的,early_suspend是Android休眠流程的第一阶段即浅度休眠,不会受到wake_lock的阻止,一般用于关闭lcd、tp等设备为运行的应用节约电能。Android的PowerManagerService会根据用户的操作情况调整电源状态,如果需要休眠则会调用到HAL层的set_screen_state()接口,在set_screen_state()中会向/sys/power/state节点写入"mem"值让驱动层开始进入休眠流程。
.level代表的是这个休眠的等级。这里设置的是51级
.suspend是休眠挂起的函数,我们进入到ft5x0x_ts_suspend函数中,其实只有一行代码:
disable_irq(this_client->irq);其实就是使中断失能能。当安卓设备休眠之后,我们再按屏幕是没有反应的,因为中断已经被我们关闭了。
. resume是唤醒函数,进入到ft5x0x_ts_resume函数中,其实就是使中断使能而已。enable_irq(this_client->irq);
register_early_suspend(&ft5x0x_ts->early_suspend);把early_suspend注册到内核。这样系统就可以统一调度休眠唤醒了。
关于安卓浅度休眠的详细介绍,参考博客http://blog.csdn.net/g_salamander/article/details/7982170
msleep(150); //make sure CTPalready finish startup process
//get someregister information
uc_reg_value = ft5x0x_read_fw_ver();
//printk("[FTS] Firmware version = 0x%x\n", uc_reg_value);
ft5x0x_read_reg(FT5X0X_REG_PERIODACTIVE, &uc_reg_value);
//printk("[FTS] report rate is %dHz.\n", uc_reg_value * 10);
ft5x0x_read_reg(FT5X0X_REG_THGROUP, &uc_reg_value);
//printk("[FTS] touch threshold is %d.\n", uc_reg_value * 4);
//#ifdef CONFIG_ANDROID
//#else
printk("uc_reg_value=%d \n",uc_reg_value);
//uc_reg_value=128 while read iic error
if(uc_reg_value<128)
{
ts_proc_entry =create_proc_entry("driver/ft5x06_ts", 0, NULL);
//if (ts_proc_entry) {
// ts_proc_entry->write_proc= 0;
//}
}
上面的这些代码就是去读取ft5x0x_ts相关寄存器的内容,其结果就起到调试的作用。
ts_proc_entry = create_proc_entry("driver/ft5x06_ts",0, NULL);会在开发板/proc/driver/目录创建一个ft5x0x_ts文件,这个文件不可用来操作ft5x0x_ts。
#if CFG_SUPPORT_AUTO_UPG
fts_ctpm_auto_upg();
#endif
#if CFG_SUPPORT_UPDATE_PROJECT_SETTING
fts_ctpm_update_project_setting();
#endif
上面的这些代码是不编译的。
enable_irq(reqirq_id);
probe函数退出之前,需要使能中断。
err = sysfs_create_group(&client->dev.kobj,&ft5x0x_attribute_group);
在kobj目录下创建一个属性集合,并显示集合中的属性文件。也就是这个input设备的属性集合。
分析probe函数中提交的工作
在ft5x0x_ts_probe函数中,我们分析到注册了一个中断函数。
err = request_irq(reqirq_id, ft5x0x_ts_interrupt,IRQF_TRIGGER_FALLING, "ft5x0x_ts", ft5x0x_ts);
中断来临时,会执行注册的ft5x0x_ts_interrupt函数,打开该函数:
static irqreturn_t ft5x0x_ts_interrupt(int irq, void*dev_id)
{
structft5x0x_ts_data *ft5x0x_ts = dev_id;
//disable_irq(IRQ_EINT(6));
disable_irq_nosync(reqirq_id);
// printk("-------------------------INT______-\n");
// enable_irq(reqirq_id);
if(!work_pending(&ft5x0x_ts->pen_event_work))
{
queue_work(ft5x0x_ts->ts_workqueue,&ft5x0x_ts->pen_event_work);
}
return IRQ_HANDLED;
}
进入中断后,先disable_irq_nosync(reqirq_id);关闭中断,防止触摸屏再次发送中断过来。为什么使用disable_irq_nosync呢?
因为disable_irq关闭中断并等待中断处理完后返回, 而disable_irq_nosync立即返回.在中断中是不允许使用disable_irq的,否则会导致cpu被synchronize_irq独占而发生系统崩溃.
接着work_pending(&ft5x0x_ts->pen_event_work)检查之前初始化的work队列是否被挂起,如果没有挂起,则提交工作(queue_work(ft5x0x_ts->ts_workqueue,&ft5x0x_ts->pen_event_work);)。
在ft5x0x_ts_probe函数中,我们也分析到初始化了一个工作:
INIT_WORK(&ft5x0x_ts->pen_event_work,ft5x0x_ts_pen_irq_work);
中断中提交了工作之后,中断就结束了,然后下半部分的中断就留给ft5x0x_ts_pen_irq_work函数处理。
static void ft5x0x_ts_pen_irq_work(struct work_struct*work)
{
int ret =-1;
ret =ft5x0x_read_data();
if (ret ==0)
{
ft5x0x_report_value();
}
enable_irq(reqirq_id);
}
下半部分中断中,先读取触摸芯片的数据,然后通过一系列的处理传送给上层,最后重新使能中断。
读取触摸芯片分析
static int ft5x0x_read_data(void)
{
structft5x0x_ts_data *data = i2c_get_clientdata(this_client);
structts_event *event = &data->event;
u8buf[CFG_POINT_READ_BUF] = {0};
int ret =-1;
int i;
//printk("start read_data i2c_rxdata\n");
ret = ft5x0x_i2c_rxdata(buf,CFG_POINT_READ_BUF);
if (ret< 0) {
//printk("%sread_data i2c_rxdata failed: %d\n", __func__, ret);
returnret;
}
memset(event,0, sizeof(struct ts_event));
event->touch_point= buf[2] & 0x07;
if(event->touch_point > CFG_MAX_TOUCH_POINTS)
{
event->touch_point = CFG_MAX_TOUCH_POINTS;
}
for (i = 0;i < event->touch_point; i++)
{
event->au16_x[i] = (s16)(buf[3 + 6*i] & 0x0F)<<8 |(s16)buf[4 + 6*i];
event->au16_y[i] = (s16)(buf[5 + 6*i] & 0x0F)<<8 |(s16)buf[6 + 6*i];
event->au8_touch_event[i] = buf[0x3 + 6*i] >> 6;
event->au8_finger_id[i] = (buf[5 + 6*i])>>4;
printk("\ntouch_point = %d event->au16_x[%d] = %u event->au16_y[%d] = %u \nau8_touch_event[%d] = %u au8_finger_id[%d] =%u\n",event->touch_point,i,event->au16_x[i],i,event->au16_y[i],i,event->au8_touch_event[i],event->au8_finger_id[i]);
}
if(event->touch_point==0)
{
printk("Touch up,it is end !\n");
}
event->pressure = 200;
//printk("start read_data i2c_rxdata OK\n");
return 0;
}
函数首先定义个buf数组,大小为3+6*5。这个数组格式正好是ft5x0x_ts芯片上报的数据格式。buf[0-2],包含的是一些基本信息,比如有多少个手指按下;每个6byte空间存放每个触控点的信息,这些信息包含接触点的x坐标(2byte)、y坐标(2byte)、按下还是弹起(1byte)、touch ID(1byte),加起来正好是6byte。这款触摸屏最多支持5点触控,所以buf设置也只能容下5个接触点的信息。
ret = ft5x0x_i2c_rxdata(buf, CFG_POINT_READ_BUF);
读取3+6*5长度的数据,读取不到这么多时,返回实际的读取长度,这个函数的实现,实际上是使用I2C core层提供的i2c_transfer函数。关于I2C驱动,这里不详细介绍了。
event->pressure = 200;这句说明这款新品不支持触摸压力检测,所以设置了一个常量。
读取之后,就是填充event结构了。下面是打印出来的event填充信息:
填充好event结构之后,就要上报event事件了,进入到ft5x0x_report_value函数中。我们简化一下代码,把宏去掉,把linux部分去掉:
static void ft5x0x_report_value(void)
{
structft5x0x_ts_data *data = i2c_get_clientdata(this_client);
structts_event *event = &data->event;
int i;
for (i = 0; i < event->touch_point; i++)
{
if(event->au16_x[i] < SCREEN_MAX_X && event->au16_y[i]
{
input_report_abs(data->input_dev,ABS_MT_POSITION_X, event->au16_x[i]);
input_report_abs(data->input_dev,ABS_MT_POSITION_Y, event->au16_y[i]);
input_report_abs(data->input_dev,ABS_MT_WIDTH_MAJOR, 1);
input_report_abs(data->input_dev,ABS_MT_TRACKING_ID, event->au8_finger_id[i]);
if (event->au8_touch_event[i]== 0 ||event->au8_touch_event[i] == 2)
{
input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR, event->pressure);
}
else
{
input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR, 0);
}
}
else //maybe the touch key area
{
if(event->au16_x[i] >= SCREEN_MAX_X)
{
ft5x0x_touch_key_process_forlinx(data->input_dev,event->au16_x[i], event->au16_y[i], event->au8_touch_event[i]);
}
}
input_report_key(data->input_dev,BTN_TOUCH, 1); //new
input_mt_sync(data->input_dev);
}
if(event->touch_point==0)//touch up
{
intk=0;
for(k=0;k
{
if(tsp_keystatus_forlinx[k] == KEY_PRESS)
{
input_report_key(data->input_dev, tsp_keycodes_forlinx[k], 0);
tsp_keystatus_forlinx[k] = KEY_RELEASE;
printk( "[FTS] %s key is up. Keycode : %d\n",tsp_keyname_forlinx[k], tsp_keycodes_forlinx[k]);
}
}
input_report_key(data->input_dev, BTN_TOUCH, 0); //new
input_mt_sync(data->input_dev);//new
}
input_sync(data->input_dev);
if(event->touch_point == 0) {
ft5x0x_ts_release();
return;
}
} /*endft5x0x_report_value*/
先简单介绍下多点触控的协议。
多点触摸协议有两种,A协议和B协议。
根具代码看,是使用A协议,协议上说了报点格式是这样的,以两点为例:
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Yy[0]
SYN_MT_REPORT
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
如果第一个触点离开(抬起),这里的意思是说还有一个触点,需要继续上报这个触点。
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
如果两个触点都离开了,那么只需要报告一个同步事件就可以了。
SYN_MT_REPORT
SYN_REPORT
if(event->au16_x[i] >= SCREEN_MAX_X)
{
ft5x0x_touch_key_process_forlinx(data->input_dev,event->au16_x[i], event->au16_y[i], event->au8_touch_event[i]);
}
上述代码判断是否按下的是物理back、home、menu按键,SCREEN_MAX_X值是800,当x坐标值大于800时,ft5x0x_touch_key_process_forlinx函数将执行,然后将本设备记录的感兴趣事件上报给上层。
int ft5x0x_touch_key_process_forlinx(struct input_dev*dev, int x, int y, int touch_event)
{
int i;
int key_id;
if( y <120&&y > 90)
{
key_id= 0;
}
else if (y< 67&&y > 47)
{
key_id= 1;
}
elseif(y<20&&y>=0)
{
key_id =2;
}
else
{
key_id= 0xf;
}
for(i = 0;i
{
if(key_id == i )
{
if(touch_event == 0) // detect
{
input_report_key(dev, tsp_keycodes_forlinx[i], 1);
printk( "[FTS] %s key is pressed. Keycode : %d\n",tsp_keyname_forlinx[i], tsp_keycodes_forlinx[i]);
tsp_keystatus_forlinx[i] = KEY_PRESS;
}
}
}
return 0;
}