1、触摸屏代码整体剖析
(1)gslx680触摸屏是I2C插口设备,所以驱动代码是借助I2C子系统提供的插口来编撰,用I2C核心层提供的I2C驱动注册插口将建立好的I2C驱动结构体向I2C子系统注册;
(2)注册的触摸屏I2C驱动会在I2C总线上和对应的I2C设备匹配上,因而调用I2C驱动的probe技巧;
(3)I2C子系统在整个触摸屏驱动代码中linux 电容触摸屏驱动,只是负责I2C设备和主控Soc的通讯,整个触摸屏代码还涉及了input子系统用于向下层应用上报触摸时间,涉及中断子系统用于处理触摸屏的中断;
2、触摸屏驱动注册函数
static struct i2c_driver gsl_ts_driver = {
.driver = {
.name = GSLX680_I2C_NAME,
.owner = THIS_MODULE,
},
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = gsl_ts_suspend,
.resume = gsl_ts_resume,
#endif
.probe = gsl_ts_probe,
.remove = __devexit_p(gsl_ts_remove),
.id_table = gsl_ts_id,
};
static int __init gsl_ts_init(void)
{
int ret;
print_info("==gsl_ts_init==n");
ret = i2c_add_driver(&gsl_ts_driver);
print_info("ret=%dn",ret);
return ret;
}
注册函数完成的功能就是向I2C子系统注册了名子是gsl_ts_driver的I2C驱动;
3、触摸屏驱动的probe函数
//在gslx680触摸屏驱动中用来描述I2C驱动的结构体
struct gsl_ts {
struct i2c_client *client;
struct input_dev *input;
struct work_struct work;
struct workqueue_struct *wq;
struct gsl_ts_data *dd;
u8 *touch_data;
u8 device_id;
int irq;
#if defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
};
static int __devinit gsl_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct gsl_ts *ts;
int rc;
······
//检查适配器是否支持标准I2C通信协议
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C functionality not supportedn");
return -ENODEV;
}
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
print_info("==kzalloc success=n");
//将client保存到ts结构体中
ts->client = client;
//把ts保存到client->dev的私有数据
i2c_set_clientdata(client, ts);
ts->device_id = id->driver_data;
//注册input子系统,初始化工作队列,绑定触摸屏中断的下半部
rc = gslX680_ts_init(client, ts);
if (rc < 0) {
dev_err(&client->dev, "GSLX680 init failedn");
goto error_mutex_destroy;
}
gsl_client = client;
//设置触摸屏相关GPIO引脚
gslX680_init();
//通过读写触摸屏芯片相关的寄存器完成初始化
init_chip(ts->client);
check_mem_data(ts->client);
//申请中断号并绑定中断处理程序
rc= request_irq(client->irq, gsl_ts_irq, IRQF_TRIGGER_RISING, client->name, ts);
if (rc < 0) {
print_info( "gsl_probe: request irq failedn");
goto error_req_irq_fail;
}
······
}
(1)向I2C核心层注册的I2C触摸屏驱动最终会在I2C总线上匹配上I2C设备,也就是structi2c_client结构体;
(2)I2C总线会调用I2C驱动的probe方式,把匹配上的structi2c_client结构体传进去;
(3)probe函数中会完成初始化工作linux软件工程师,其中包括input子系统的注册,中断程序的注册;
4、中断处理函数
static irqreturn_t gsl_ts_irq(int irq, void *dev_id)
{
struct gsl_ts *ts = dev_id;
print_info("========gslX680 Interrupt=========n");
//禁止中断
disable_irq_nosync(ts->irq);
//检测中断下半部所属的工作队列是否挂起,如果挂起则将中断的下半部添加到队列中进行调度
//调用中断下半部函数,也就是gslX680_ts_worker()
if (!work_pending(&ts->work))
{
queue_work(ts->wq, &ts->work);
}
return IRQ_HANDLED;
}
(1)gsl_ts_irq()函数只是中断处理函数的上半部linux 电容触摸屏驱动,实际功能就是严禁中断,之后调用中断下半部;
(2)gslX680_ts_worker()是中断的下半部,在gslX680_ts_init()函数手指定;
补充:参考博客:《中断的顶半部和底半部介绍以及实现方法(tasklet和工作队列)》;
5、整个触摸屏驱动代码工作的逻辑
gsl_ts_irq() //中断上半部
gslX680_ts_worker() //中断下半部
gsl_ts_write() //触摸屏驱动的写数据
i2c_master_send() //实际调用I2C核心层的发数据接口
gsl_ts_read() //触摸屏驱动的读数据
i2c_master_recv() //实际调用I2C核心层的收数据接口
report_data() //上报数据
input_report_abs() //实际调用input子系统的上报接口
(1)当触摸屏发生触摸风波时,触发绑定的中断处理程序gsl_ts_irq();
(2)gsl_ts_irq()是中断上半部,严禁中断之后调用中断下半部gslX680_ts_worker();
(3)gslX680_ts_worker()函数会通过I2C子系统提供的I2C总线的收发数据插口函数,读写触摸屏芯片的相关寄存器;
(4)gslX680_ts_worker()函数将从触摸屏芯片读取到的数据进行估算和转换,之后填充成输入风波结构体中标麒麟linux,依照之前注册的input子系统,按input子系统的输入风波往应用层上报;