您的位置:首页 > 其它

focaltech(敦泰)触摸屏驱动Ft5306.c学习记录—未完,待续

2014-06-13 15:53 316 查看
最近正在做安卓系统的驱动开发工作,学习了focaltech(敦泰)触摸屏驱动Ft5306.c,简单总结如下(未完,待续)。因为刚接触驱动开发,许多知识没有彻底理解,如有错误请指正。

1 概述

linux触摸屏驱动基于linux input 子系统,层次上位于input系统的第三层,主要是完成具体输入设备的驱动功能程序,Ft5306.c源文件位于kernel下drivers\input\touchscreen目录下,属于字符型驱动,ft5306使用的多点触控协议是A协议。

Ft5306驱动主要涉及的知识架构有:

linux驱动开始相关知识;

linux input 子系统机制;

linux I2C总线结构及I2C协议;

中断下半部处理机制-工作队列;

linux内核多点触控协议;

触摸屏实现原理;

其他:如platform机制、dts设备结构树,安卓下的唤醒机制、virtualkeys触摸按键发送方法等;

2 具体分析

2.1 驱动的注册及退出:ft5306.c

module_init(focaltech_ft5306_init);

module_exit(focaltech_ft5306_exit);

注意:驱动入口函数focaltech_ft5306_init有固定格式要求:函数入参必须为void,返回值类型必须为int。

2.2 I2C板级信息初始化

: 注意:0x70 >> 1为I2C通信中的从地址,通常从芯片手册中获取的地址需要右移1位;ps:board-granet.c(不同平台源文件可能不同)

staticstruct i2c_board_info __initdata ft5306_info[] = {

{ /* New touch screeni2c slave address. */

I2C_BOARD_INFO("FocalTech-Ft5306",(0x70 >> 1)),

.platform_data= &ft5306_plat_data,

.irq= gpio_to_irq(TSC_GPIO_IRQ_PIN),

},};

i2c_board_info结构体用来保存I2C设备的相关信息,Linux根据这些信息创建I2C设备相关的设备模型树。对于mainboards,通过调 用i2c_register_board_info()静态完成。对于add-on boards,通过调用i2c_new_device()动态完成。i2c_board_info结构体有两个成员必须初始化,一个是type,用来初始化i2c_client.name;另一个是addr,用来初始化i2c_client.addr。其它i2c_board_info结构体成员根据需要赋值或保持为空。

2.3 I2c_client (I2C 适配器)注册(并没有创建)

:使用i2c_register_board_info(3, ft5306_info,ARRAY_SIZE(ft5306_info));board-granet.c

2.4 I2Cdriver的注册:ft5306.c

首先定义结构体:i2c_driver focaltech_ft5306_driver,注意.name 成员必须与ft5306_info中I2C_BOARD_INFO第一个参数相同,否则I2c_client 与I2C driver不会匹配成功

staticstruct i2c_driver focaltech_ft5306_driver = {

.probe = focaltech_ft5306_probe,

.remove = focaltech_ft5306_remove,

#ifndef CONFIG_HAS_EARLYSUSPEND

.suspend = focaltech_ft5306_suspend,

.resume = focaltech_ft5306_resume,

#endif

.id_table = focaltech_ft5306_id,

.driver= {

.name = "FocalTech-Ft5306",

}, } ;

其次注册:在focaltech_ft5306_init 调用i2c_add_driver(&focaltech_ft5306_driver);

最后注销:i2c_driver注销 是在驱动文件中exit函数,例如 focaltech_ft5306_exit 调用i2c_del_driver;

2.5 I2Cclient 与driver的匹配:ft5306.c

foc focaltech_ft5306_init -> i2c_add_driver-> i2c_register_driver-driver_register->调用i2c_for_each_dev遍历所有已存在的i2c_adapter->…-> i2c_do_add_adapter(该函数主要完成i2c_driver与i2c_adapter上的i2c设备的匹配工作,根据i2c_client.name与 driver.name 是否相同来匹配。如果匹配成功,初始化并创建对应的i2c_client。)
在driver_register执行过程中,如果I2C总线上找到了与该驱动匹配的I2C设备,则i2c_driver.probe函数会被调用执行。

当进入i2c_driver.probe后,依次执行:

2.6 初始化input_dev 结构体

2.7.设置设备支持的事件类型

input_set_abs_params(ts->input_dev,ABS_MT_POSITION_X, 0, ts_x_max_value, 0, 0);

input_set_abs_params(ts->input_dev,ABS_MT_POSITION_Y, 0, ts_y_max_value, 0, 0);

input_set_abs_params(ts->input_dev,ABS_MT_TOUCH_MAJOR, 0, 8, 0, 0);

ABS_MT_POSITION_X等中表示多点触控协议的事件类型

2.8 注册input系统

在 Probe方式下(input系统外添加字符驱动的位置在probe使用input_register_device函数中,注销字符驱动的位置的remove中). Linux input 字符设备的注册是在evdev_connect 函数中实现的,使用的是cdev_add 函数,字符设备的删除是在evdev_cleanup函数中实现的。xxx_fops在cdev_init(&xxx_dev.cdev, &xxx_fops)的语句中被建立与cdev的连接。

2.9 申请中断request_irq

if(request_irq(GPIO_TO_IRQ(ts_gpio_irq_pin), focaltech_ft5306_irq_handler,

IRQF_TRIGGER_FALLING,client->name, ts) >= 0) {

printk("RequestedIRQ\n");

ts->use_irq= 1;

printk(KERN_INFO"GPIO_%d INT: %d", ts_gpio_irq_pin,

GPIO_TO_IRQ(ts_gpio_irq_pin));

/*if((ret = set_irq_wake(client->irq, 1)) < 0) {

printk(KERN_ERR"failed to set IRQ wake: %d\n", ret);

}*/

}

2.10 实现睡眠唤醒机制

#ifdefCONFIG_HAS_EARLYSUSPEND

ts->early_suspend.level= EARLY_SUSPEND_LEVEL_BLANK_SCREEN;

ts->early_suspend.suspend= focaltech_ft5306_early_suspend;

ts->early_suspend.resume= focaltech_ft5306_late_resume;

register_early_suspend(&ts->early_suspend);

3 重点分析

3.1中断下半部工作队列软件实现:

focaltech_ft5306_irq_handler中断处理函数

focaltech_ft5306_work_func 中断处理后半部函数

l 声明工作队列结构体:static struct workqueue_struct *synaptics_wq;

l 初始化创建工作队列:

synaptics_wq =create_singlethread_workqueue("synaptics_wq");

if (!synaptics_wq) {

printk(KERN_ERR"Could not create work queue synaptics_wq: no memory");

return -ENOMEM;

l 在工作队列中添加一个工作:

INIT_WORK(&ts->work, focaltech_ft5306_work_func);

// focaltech_ft5306_work_func为处理函数,ts->work(ts为触摸屏结构体synaptics_rmi4)

focaltech_ft5306_work_func为使用container_of实现data指针的传递。

l probe函数中使用request_irq申请中断,绑定中断处理函数focaltech_ft5306_irq_handler

l 安排任务进工作队列:中断处理函数focaltech_ft5306_irq_handler中,queue_work(synaptics_wq,&ts->work);

l focaltech_ft5306_work_func函数中实现数据的上报等

l 工作队列的销毁focaltech_ft5306_exit中,

if (synaptics_wq)

destroy_workqueue(synaptics_wq);

3.2通过I2C数据读取

协议要求

l 从I2C从设备读取数据时,应先调用i2c_master_recv()发送I2C从设备的寄存器地址(I2C从设备的寄存器地址独立于 CPU 内存地址空间,因此不需要做 ioremap()映射),再调用i2c_master_send()读取数据。

l I2C 总线控制器从 I2C 从设备读取数据,只需要调用i2c_master_send(),buf[0]是 I2C 从设备的寄存器地址。

Ft306中I2C
接口使用:ft520x_i2c_rxdataft520x_i2c_txdata

l 调用关系:focaltech_ft5306_work_func->
fts_ctpm_get_touch_info –>ft520x_i2c_rxdata->
i2c_transfer (相当于调用i2c_master_send,因为ft520x_i2c_rxdata做了部分初始化)

l 数据封装:

i2c发送或者接收一次数据都以数据包 structi2c_msg
封装

struct i2c_msg {

__u16 addr; // 从机地址

__u16 flags; // 标志

#define I2C_M_TEN 0x0010 // 十位地址标志

#define I2C_M_RD 0x0001 // 接收数据标志

__u16 len; // 数据长度

__u8 *buf; // 数据指针

};

其中addr为从机地址;flags则是这次通信的标志,发送数据为0,接收数据则为 I2C_M_RD;len为此次通信的数据字节数;buf 为发送或接收数据的指针。在设备驱动中我们通常调用 i2c-core 定义的接口 i2c_master_send 和 i2c_master_recv 来发送或接收一次数据。

i2c_transfer(client->adapter,msgs, 2); // 这里 num = 2,通信成功 ret = 2

使用i2c_transfer接口读取数据时,返回的data中,data[0]为寄存器的地址,data[1]以后才是读取的有效数据。

3.3 linux内核多点触控协议

3.4 触摸数据解析上报分析

3.5 dts设备树
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: