转载请注明出处 @SayidZhong
https://sayid95.github.io/
下面以思立威的tp为例,不足之处还望指正
主要是梳理下逻辑,主要看其中代码的注释,涉及的知识点还是比较多的中断、队列以后再写
先找到驱动入口,module_init(tpd_driver_init);
接着看到 init 函数里面
/* called when loaded into kernel */
static int __init tpd_driver_init(void) {
printk("Sileadinc gslX680 touch panel driver init\n");
//注册一个i2c设备,主要配置 IC 挂载在哪条 i2c 总线
i2c_register_board_info(TPD_I2C_NUMBER, &gslX680_i2c_tpd, 1);
tpd_get_dts_info();
//添加一个驱动到静态数组 tpd_driver_list 中,加载 TP 设备驱动
if(tpd_driver_add(&tpd_device_driver) < 0)
printk("add gslX680 -driver failed\n");
return 0;
}
tpd_device_driver 结构体看下面
//tpd_device_name 被保存到 tpd_driver_list[0] 数组中
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = "GSL_I2C_NAME",
.tpd_local_init = tpd_local_init,
.suspend = tpd_suspend,
.resume = tpd_resume,
#ifdef TPD_HAVE_BUTTON
.tpd_have_button = 1,
#else
.tpd_have_button = 0,
#endif
};
在看上面 tpd_device_driver 结构体 中的 tpd_local_init 函数
int tpd_local_init(void)
{
int retval;
printk("==tpd_local_init==\n");
TPD_DMESG("Focaltech gslx68x I2C Touchscreen Driver...\n");
//设置vtouch=2.8v
tpd->reg = regulator_get(tpd->tpd_dev, "vtouch");
retval = regulator_set_voltage(tpd->reg, 2800000, 2800000);
if (retval != 0) {
TPD_DMESG("Failed to set reg-vgp6 voltage: %d\n", retval);
return -1;
}
if(i2c_add_driver(&tpd_i2c_driver)!=0) {
TPD_DMESG("unable to add i2c driver.\n");
return -1;
}
//设置input设备 tpd->dev 支持的最大手指触摸个数
input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, (MAX_CONTACTS+1), 0, 0);
//MTK虚拟按键的实现在 tpd_button.c 中实现
#ifdef TPD_HAVE_BUTTON
tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);
// initialize tpd button data
#endif
#if (defined(TPD_WARP_START) && defined(TPD_WARP_END))
TPD_DO_WARP = 1;
memcpy(tpd_wb_start, tpd_wb_start_local, TPD_WARP_CNT*4);
memcpy(tpd_wb_end, tpd_wb_start_local, TPD_WARP_CNT*4);
#endif
#if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION))
memcpy(tpd_calmat, tpd_calmat_local, 8*4);
memcpy(tpd_def_calmat, tpd_def_calmat_local, 8*4);
#endif
//设置TP类型为电容屏
tpd_type_cap = 1;
printk("==tpd_local_init end==\n");
return 0;
}
i2c_add_driver(&tpd_i2c_driver)
其中的结构体
//包含设备的名称、以及 TP 初始化的函数接口
struct i2c_driver tpd_i2c_driver = {
.driver = {
.name = GSL_I2C_NAME,
.of_match_table = of_match_ptr(gslx68x_dt_match),
},
.probe = tpd_i2c_probe,
.remove = tpd_i2c_remove,
.id_table = tpd_i2c_id,
.detect = tpd_i2c_detect,
};
分析下 比较重要的 probe 探测函数
static int tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
int err = 0;
//char buffer[5];
//int status=0;
int ret;
printk("==tpd_i2c_probe==i2c %d\n", TPD_I2C_NUMBER);
//malloc memory
ddata = kzalloc(sizeof(struct gsl_ts_data), GFP_KERNEL);
if (!ddata) {
print_info("alloc ddata memory error\n");
return -ENOMEM;
}
ddata->client = client;
if(i2c_client->addr !=0x40)
{
//printk("==gsl---erro---addr=%d\n",ddata->addr);
//i2c_client->addr=0x40;
}
of_get_ft5x0x_platform_data(&client->dev);
tpd_gpio_output(GTP_RST_PORT, 0);
//gpio_direction_output(tpd_rst_gpio_number, 0);
msleep(100);
ret = regulator_enable(tpd->reg);
if (ret != 0)
TPD_DMESG("Failed to enable reg-vgp2: %d\n", ret);
//gpio_direction_output(tpd_rst_gpio_number, 1);
msleep(100);
tpd_gpio_output(GTP_RST_PORT, 1);
msleep(100);
/* set tp INT mode */
tpd_gpio_as_int(GTP_INT_PORT);
//gpio_direction_input(tpd_int_gpio_number);
msleep(100);
i2c_client = client;
if(i2c_client->addr !=0x40)
{
printk("gsl-addr=%d\n",i2c_client->addr);
i2c_client->addr=0x40;
}
printk("gsl-addr=%d\n",i2c_client->addr);
disable_irq(touch_irq);
msleep(100);
init_chip(i2c_client);
msleep(100);
init_chip(ddata->client);
check_mem_data(i2c_client);
check_mem_data(ddata->client);
tpd_irq_registration();
//msleep(100);
tpd_load_status = 1;
//创建名为 TPD_DEVICE 线程thread是为了处理中断来临之后读取坐标、上报坐标、手势识别、按键等等信息
thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);
if (IS_ERR(thread)) {
err = PTR_ERR(thread);
TPD_DMESG(TPD_DEVICE " failed to create kernel thread: %d\n", err);
}
//静电检测
#ifdef GSL_MONITOR
printk( "tpd_i2c_probe () : queue gsl_monitor_workqueue\n");
INIT_DELAYED_WORK(&gsl_monitor_work, gsl_monitor_worker);
gsl_monitor_workqueue = create_singlethread_workqueue("gsl_monitor_workqueue");
queue_delayed_work(gsl_monitor_workqueue, &gsl_monitor_work, 1000);
#endif
#ifdef TPD_PROC_DEBUG
#if 0
gsl_config_proc = create_proc_entry(GSL_CONFIG_PROC_FILE, 0666, NULL);
printk("[tp-gsl] [%s] gsl_config_proc = %x \n",__func__,gsl_config_proc);
if (gsl_config_proc == NULL)
{
print_info("create_proc_entry %s failed\n", GSL_CONFIG_PROC_FILE);
}
else
{
gsl_config_proc->read_proc = gsl_config_read_proc;
gsl_config_proc->write_proc = gsl_config_write_proc;
}
#else
//通过创建 proc 文件节点来进行adb读写(现在大都用/sys/下面的设备节点)
//安卓7.0 可能需要 0777 权限才能创建文件节点
proc_create(GSL_CONFIG_PROC_FILE,0666,NULL,&gsl_seq_fops);
#endif
gsl_proc_flag = 0;
#endif
enable_irq(touch_irq);
printk("==tpd_i2c_probe end==\n");
return 0;
}
看下 touch_event_handler
线程
static int touch_event_handler(void *unused)
{
struct sched_param param = { .sched_priority = 4 };
// 调度策略和调度参数分别设置为 param 指向的 sched_param
sched_setscheduler(current, SCHED_RR, ¶m);
printk("gsl-touch_event_handler, task running===\n");
do{
set_current_state(TASK_INTERRUPTIBLE);// 设置当前线程可被打断
wait_event_interruptible(waiter, tpd_flag != 0);//收到唤醒信号,结束等侍,执行下一条代码
tpd_flag = 0;// 判断是否有中断标记
TPD_DEBUG_SET_TIME;
set_current_state(TASK_RUNNING);// 标记当前线程在执行
eint_flag = 0;//清标志位
report_data_handle();//上报数据
} while (!kthread_should_stop());
return 0;
}
接着看下 report_data_handle(); 怎么上报数据的
static void report_data_handle(void)
{
u8 touch_data[MAX_FINGERS * 4 + 4] = {0};
u8 buf[4] = {0};
unsigned char id, point_num = 0;
unsigned int x, y, temp_a, temp_b, i;
#ifdef GSL_NOID_VERSION
struct gsl_touch_info cinfo={{0},{0},{0},0};
int tmp1 = 0;
#endif
#ifdef GSL_MONITOR
if(i2c_lock_flag != 0)
return;
else
i2c_lock_flag = 1;
#endif
#ifdef TPD_PROC_DEBUG
if(gsl_proc_flag == 1)
return;
#endif
//触摸点数寄存器0x80
//中断函数的作用就是报点,就是将从地址 0x80 开始的 44byte 的数据(存放触摸点的信息)通过系统的 API 上报给上层
//对读出的 44 个字节进行分析,得到初步触摸点坐标
i2c_smbus_read_i2c_block_data(i2c_client, 0x80, 4, &touch_data[0]);
point_num = touch_data[0];
if(point_num > 0)
i2c_smbus_read_i2c_block_data(i2c_client, 0x84, 8, &touch_data[4]);
if(point_num > 2)
i2c_smbus_read_i2c_block_data(i2c_client, 0x8c, 8, &touch_data[12]);
if(point_num > 4)
i2c_smbus_read_i2c_block_data(i2c_client, 0x94, 8, &touch_data[20]);
if(point_num > 6)
i2c_smbus_read_i2c_block_data(i2c_client, 0x9c, 8, &touch_data[28]);
if(point_num > 8)
i2c_smbus_read_i2c_block_data(i2c_client, 0xa4, 8, &touch_data[36]);
#ifdef GSL_NOID_VERSION
cinfo.finger_num = point_num;
//printk("tp-gsl------------- finger_num = %d\n",cinfo.finger_num);
for(i = 0; i < (point_num < MAX_CONTACTS ? point_num : MAX_CONTACTS); i ++)
{
temp_a = touch_data[(i + 1) * 4 + 3] & 0x0f;
temp_b = touch_data[(i + 1) * 4 + 2];
cinfo.x[i] = temp_a << 8 |temp_b;
temp_a = touch_data[(i + 1) * 4 + 1];
temp_b = touch_data[(i + 1) * 4 + 0];
cinfo.y[i] = temp_a << 8 |temp_b;
cinfo.id[i] = ((touch_data[(i + 1) * 4 + 3] & 0xf0)>>4);
//printk("tp-gsl------------- before: x[%d] = %d, y[%d] = %d, id[%d] = %d \n",i,cinfo.x[i],i,cinfo.y[i],i,cinfo.id[i]);
}
cinfo.finger_num = (touch_data[3]<<24)|(touch_data[2]<<16)|
(touch_data[1]<<8)|touch_data[0];
gsl_alg_id_main(&cinfo);
tmp1=gsl_mask_tiaoping();
print_info("[tp-gsl] tmp1=%x\n",tmp1);
if(tmp1>0&&tmp1<0xffffffff)
{
buf[0]=0xa;buf[1]=0;buf[2]=0;buf[3]=0;
i2c_smbus_write_i2c_block_data(i2c_client,0xf0,4,buf);
buf[0]=(u8)(tmp1 & 0xff);
buf[1]=(u8)((tmp1>>8) & 0xff);
buf[2]=(u8)((tmp1>>16) & 0xff);
buf[3]=(u8)((tmp1>>24) & 0xff);
print_info("tmp1=%08x,buf[0]=%02x,buf[1]=%02x,buf[2]=%02x,buf[3]=%02x\n",
tmp1,buf[0],buf[1],buf[2],buf[3]);
i2c_smbus_write_i2c_block_data(i2c_client,0x8,4,buf);
}
point_num = cinfo.finger_num;
#endif
for(i = 1 ;i <= MAX_CONTACTS; i ++)
{
if(point_num == 0)
id_sign[i] = 0;
id_state_flag[i] = 0;
}
for(i = 0; i < (point_num < MAX_FINGERS ? point_num : MAX_FINGERS); i ++)
{
#ifdef GSL_NOID_VERSION
id = cinfo.id[i];
x = cinfo.x[i];
y = cinfo.y[i];
#else
id = touch_data[(i + 1) * 4 + 3] >> 4;
temp_a = touch_data[(i + 1) * 4 + 3] & 0x0f;
temp_b = touch_data[(i + 1) * 4 + 2];
x = temp_a << 8 |temp_b;
temp_a = touch_data[(i + 1) * 4 + 1];
temp_b = touch_data[(i + 1) * 4 + 0];
y = temp_a << 8 |temp_b;
#endif
if(1 <= id && id <= MAX_CONTACTS)
{
#ifdef FILTER_POINT
filter_point(x, y ,id);
#else
record_point(x, y , id);
#endif
tpd_down(id, x_new, y_new, 10);
id_state_flag[id] = 1;
}
}
for(i = 1; i <= MAX_CONTACTS; i ++)
{
if( (0 == point_num) || ((0 != id_state_old_flag[i]) && (0 == id_state_flag[i])) )
{
id_sign[i]=0;
}
id_state_old_flag[i] = id_state_flag[i];
}
if(0 == point_num)
{
tpd_up();
}
input_sync(tpd->dev);
#ifdef GSL_MONITOR
i2c_lock_flag = 0;
#endif
}
还有写按下手指和抬起函数
void tpd_down( int id, int x, int y, int p)
{
//printk("gsl----------------tpd_down id: %d, x:%d, y:%d------ \n", id, x, y);
//通过系统的 API 上报给上层,上报给上 层主要是通过下面几个 API 来实现的:
input_report_key(tpd->dev, BTN_TOUCH, 1);
input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 1);
input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, id);
input_mt_sync(tpd->dev);
}
void tpd_up(void)
{
input_report_key(tpd->dev, BTN_TOUCH, 0);
input_mt_sync(tpd->dev);
}
最后还有休眠和唤醒以及 check 函数
static void tpd_suspend(struct device *h)
{
printk("==tpd_suspend==\n");
tpd_halt = 1;
disable_irq(touch_irq);//屏蔽中断
tpd_gpio_output(GTP_RST_PORT, 0);//RST拉低
}
/* Function to manage power-on resume */
//void tpd_resume(struct early_suspend *h)
static void tpd_resume(struct device *h)
{
printk("==tpd_resume==\n");
tpd_gpio_output(GTP_RST_PORT,1);//RST拉高
msleep(20);
reset_chip(i2c_client);//ic复位
startup_chip(i2c_client);//启动IC
check_mem_data(i2c_client);//检测吧b0
enable_irq(touch_irq);//开启中断
tpd_halt = 0;
}
static void check_mem_data(struct i2c_client *client){
//如果 b0 的值不是 0x5a5a5a5a,说明 TP 的配置(即.h)没 有下载正确,需要重新下载
u8 read_buf[4] = {0};
msleep(30);
i2c_smbus_read_i2c_block_data(client,0xb0, sizeof(read_buf), read_buf);
if (read_buf[3] != 0x5a || read_buf[2] != 0x5a || read_buf[1] != 0x5a || read_buf[0] != 0x5a)
{
printk("gsl----check mem read 0xb0 = %x %x %x %x \n", read_buf[3], read_buf[2], read_buf[1], read_buf[0]);
}
}
下面有些寄存器简单介绍下:
- 0xe0 :芯片复位寄存器
- 0xe4 :芯片时钟寄存器
- 0xbc :掉电检测寄存器
- 0xf0 :页码寄存器
- 0x80 :触摸点数寄存器
- 0x84~0xa8 :触摸点坐标寄存器
- 0xb4 :芯片内部中断计数寄存器
- 0xb0 :芯片检验码寄存器
- 0xac :靠近关屏寄存器