android系统开发(四)-触摸屏tslib移植(内核)和原理分析

    技术2022-05-17  38

    首先了解一下tslib的运行原理,tslib的运行分成两部分(1)校验在LCD固定坐标位置依次显示出5个坐标让用户触摸,把LCD坐标和用户触摸时驱动屏驱动底层的坐标总共5组值保存起来运行tslib库的算法对其进行运算,得出校准用7个值(2)校准每次触摸屏驱动读取到硬件坐标时应用校准用的7个值对该坐标进行一次运算,然后将运算后的坐标作为正常坐标即可。按照上面的原理,(1)我们先修改内核部分,我的平台用的触摸屏幕驱动是tsc2007,驱动文件为内核/drivers/input/touchscreen目录下的tsc2007.c和ts_linear.c其中,ts_linear.c中定义的是校准模块,该模块在proc文件系统中建立了7个文件,用来存放校准用的7个点,7的点的默认值为1,0,0,0,1,0,1,对应的目标平台文件系统的位置为/proc/sys/dev/ts_device目录下a0,a1,a2,a3,a4,a5,a6等7个文件此模块中还定义了一个校准函数ts_linear_scale,此函数的主要内容是读取a0,a1,a2,a3,a4,a5,a6等7个文件中的值作为7个校准值与传入的触摸平坐标值进行运算,返回运算结果。ts_linear_scale函数定义如下:int ts_linear_scale(int *x, int *y, int swap_xy){    int xtemp, ytemp;    xtemp = *x;    ytemp = *y;    if (cal.a[6] == 0)        return -EINVAL;    *x = (cal.a[2] + cal.a[0] * xtemp + cal.a[1] * ytemp) / cal.a[6];    *y = (cal.a[5] + cal.a[3] * xtemp + cal.a[4] * ytemp) / cal.a[6];    if (swap_xy) {        int tmp = *x;        *x = *y;        *y = tmp;    }    return 0;}

    ts2007.c为触摸屏驱,与其他驱动不同的地方是在取得硬件坐标值发送之前先调用了ts_linear_scale函数对坐标值进行了校准            if (x > 0 && y > 0)            {                ts_linear_scale(&x, &y, invert);                input_report_abs(input, ABS_X, x);                input_report_abs(input, ABS_Y, y);                input_report_abs(input, ABS_PRESSURE, 255);                input_report_abs(input, ABS_TOOL_WIDTH, 1);                input_report_key(input, BTN_TOUCH, 1);                input_sync(input);            }(2)在android源代码/system/core/rootdir/init.rc文件中添加tslib相关的宏定义如下:# touchscreen parameters    export TSLIB_FBDEVICE /dev/graphics/fb0    export TSLIB_CALIBFILE /data/etc/pointercal    export TSLIB_CONFFILE  /system/etc/ts.conf    export TSLIB_TRIGGERDEV /dev/input/event0    export TSLIB_TSDEVICE /dev/input/event1(2)移植tslib库到android系统,比较麻烦,看下一节的内容。(3)校验程序完成后会将生成的7个校准值写入到环境变量TSLIB_CALIBFILE对应的路径/data/etc/pointercal文件中(4)校验完后将pointercal文件中的7个值分别写入到/proc/sys/dev/ts_device目录下a0,a1,a2,a3,a4,a5,a6文件即可。(5)开机启动的时候我们编写一个应用程序,首先判断环境变量TSLIB_CALIBFILE对应的路径/data/etc/pointercal文件是否存在,如果文件存在而且非空,则将该文件中的7个值取出来分别写入到/proc/sys/dev/ts_device目录下a0,a1,a2,a3,a4,a5,a6文件(6)为了确保未校验前触摸屏可用,我们将一次校验后得出的7个坐标值作为初始值,修改到内核ts_linear.c文件中。

    下面是源代码:ts_linear.c文件/* *  Touchscreen Linear Scale Adaptor * *  Copyright (C) 2009 Marvell Corporation *  *  Author: Mark F. Brown <markb@marvell.com> *  Based on tslib 1.0 plugin linear.c by Russel King * * This library is licensed under GPL. * */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/wait.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/proc_fs.h>#include <linux/sysctl.h>#include <asm/system.h>/* * sysctl-tuning infrastructure. */static struct ts_calibration {/* Linear scaling and offset parameters for x,y (can include rotation) */    int a[7];} cal;static ctl_table ts_proc_calibration_table[] = {    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a0",     .data = &cal.a[0],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a1",     .data = &cal.a[1],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a2",     .data = &cal.a[2],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a3",     .data = &cal.a[3],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a4",     .data = &cal.a[4],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a5",     .data = &cal.a[5],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {     .ctl_name = CTL_UNNUMBERED,     .procname = "a6",     .data = &cal.a[6],     .maxlen = sizeof(int),     .mode = 0666,     .proc_handler = &proc_dointvec,     },    {.ctl_name = 0}};static ctl_table ts_proc_root[] = {    {     .ctl_name = CTL_UNNUMBERED,     .procname = "ts_device",     .mode = 0555,     .child = ts_proc_calibration_table,     },    {.ctl_name = 0}};static ctl_table ts_dev_root[] = {    {     .ctl_name = CTL_DEV,     .procname = "dev",     .mode = 0555,     .child = ts_proc_root,     },    {.ctl_name = 0}};static struct ctl_table_header *ts_sysctl_header;int ts_linear_scale(int *x, int *y, int swap_xy){    int xtemp, ytemp;    xtemp = *x;    ytemp = *y;    if (cal.a[6] == 0)        return -EINVAL;    *x = (cal.a[2] + cal.a[0] * xtemp + cal.a[1] * ytemp) / cal.a[6];    *y = (cal.a[5] + cal.a[3] * xtemp + cal.a[4] * ytemp) / cal.a[6];    if (swap_xy) {        int tmp = *x;        *x = *y;        *y = tmp;    }    return 0;}EXPORT_SYMBOL(ts_linear_scale);static int __init ts_linear_init(void){    ts_sysctl_header = register_sysctl_table(ts_dev_root);    /* Use default values that leave ts numbers unchanged after transform */    cal.a[0] = 1;    cal.a[1] = 0;    cal.a[2] = 0;    cal.a[3] = 0;    cal.a[4] = 1;    cal.a[5] = 0;    cal.a[6] = 1;    return 0;}static void __exit ts_linear_cleanup(void){    unregister_sysctl_table(ts_sysctl_header);}module_init(ts_linear_init);module_exit(ts_linear_cleanup);MODULE_DESCRIPTION("touch screen linear scaling driver");MODULE_LICENSE("GPL");ts2007.c文件/* *  linux/drivers/input/touchscreen/tsc2007.c * *  touch screen driver for tsc2007 * *  Copyright (C) 2006, Marvell Corporation * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/wait.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/freezer.h>#include <linux/proc_fs.h>#include <linux/clk.h>#include <linux/i2c.h>#include <mach/gpio.h>#include <linux/sysctl.h>#include <asm/system.h>extern int ts_linear_scale(int *x, int *y, int swap_xy);/* Use MAV filter */#define TSC_CMD_SETUP 0xb0/* Use 12-bit */#define TSC_CMD_X 0xc0#define TSC_CMD_PLATEX 0x80#define TSC_CMD_Y 0xd0#define TSC_CMD_PLATEY 0x90#define TSC_X_MAX 4096#define TSC_Y_MAX 4096#define TSC_X_MIN 0#define TSC_Y_MIN 0/* delay time for compute x, y, computed as us */#define DEBUG#ifdef DEBUG#define TS_DEBUG(fmt,args...) printk(KERN_DEBUG fmt, ##args )#else#define TS_DEBUG(fmt,args...)#endifstatic int x_min=TSC_X_MIN;static int y_min=TSC_Y_MIN;static int x_max=TSC_X_MAX;static int y_max=TSC_Y_MAX;static int invert = 0;static int debounce_time  = 150;static int init_debounce = true;static int delay_time = 1;enum tsc2007_status {    PEN_UP,    PEN_DOWN,};struct _tsc2007 {    struct input_dev *dev;    int x;        /* X sample values */    int y;        /* Y sample values */    int status;    struct work_struct irq_work;    struct i2c_client *client;    unsigned long last_touch;};struct _tsc2007 *g_tsc2007;/* update abs params when min and max coordinate values are set */int tsc2007_proc_minmax(struct ctl_table *table, int write, struct file *filp,                     void __user *buffer, size_t *lenp, loff_t *ppos){    struct _tsc2007 *tsc2007= g_tsc2007;    struct input_dev *input = tsc2007->dev;    /* update value */    int ret = proc_dointvec(table, write, filp, buffer, lenp, ppos);    /* updated abs params */    if (input) {        TS_DEBUG(KERN_DEBUG "update x_min %d x_max %d"            " y_min %d y_max %d/n", x_min, x_max,            y_min, y_max);         input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);        input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);    }    return ret;}static ctl_table tsc2007_proc_table[] = {    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "x-max",        .data        = &x_max,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &tsc2007_proc_minmax,    },    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "y-max",        .data        = &y_max,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &tsc2007_proc_minmax,    },    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "x-min",        .data        = &x_min,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &tsc2007_proc_minmax,    },    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "y-min",        .data        = &y_min,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &tsc2007_proc_minmax,    },    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "invert_xy",        .data        = &invert,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &proc_dointvec,    },    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "debounce_time",        .data        = &debounce_time,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &proc_dointvec,    },    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "delay_time",        .data        = &delay_time,        .maxlen        = sizeof(int),        .mode        = 0666,        .proc_handler    = &proc_dointvec,    },    { .ctl_name = 0 }};static ctl_table tsc2007_proc_root[] = {    {        .ctl_name    = CTL_UNNUMBERED,        .procname    = "ts_device",        .mode        = 0555,        .child        = tsc2007_proc_table,    },    { .ctl_name = 0 }};static ctl_table tsc2007_proc_dev_root[] = {    {        .ctl_name    = CTL_DEV,        .procname    = "dev",        .mode        = 0555,        .child        = tsc2007_proc_root,    },    { .ctl_name = 0 }};static struct ctl_table_header *sysctl_header;static int __init init_sysctl(void){    sysctl_header = register_sysctl_table(tsc2007_proc_dev_root);    return 0;}static void __exit cleanup_sysctl(void){    unregister_sysctl_table(sysctl_header);}static int tsc2007_measure(struct i2c_client *client, int *x, int * y){    u8 x_buf[2] = {0, 0};    u8 y_buf[2] = {0, 0};    i2c_smbus_write_byte(client, TSC_CMD_PLATEX);    msleep_interruptible(delay_time);    i2c_smbus_write_byte(client, TSC_CMD_X);    i2c_master_recv(client, x_buf, 2);    *x = (x_buf[0]<<4) | (x_buf[1] >>4);    i2c_smbus_write_byte(client, TSC_CMD_PLATEY);    msleep_interruptible(delay_time);    i2c_smbus_write_byte(client, TSC_CMD_Y);    i2c_master_recv(client, y_buf, 2);    *y = (y_buf[0]<<4) | (y_buf[1] >>4);    *y = 4096 - *y; //added by allen    printk("/ntouchscreen x = 0x%x, y = 0x%x/n",*x,*y);    return 0;}static void tsc2007_irq_work(struct work_struct *work){    struct _tsc2007 *tsc2007= g_tsc2007;    struct i2c_client *client = tsc2007-> client;    struct input_dev *input = tsc2007->dev;    int x = -1, y = -1, is_valid = 0;    int tmp_x = 0, tmp_y = 0;    int gpio = irq_to_gpio(client->irq);    /* Ignore if PEN_DOWN */    if(PEN_UP == tsc2007->status){        if (gpio_request(gpio, "tsc2007 touch detect")) {            printk(KERN_ERR "Request GPIO failed, gpio: %X/n", gpio);            return;        }        gpio_direction_input(gpio);                    while(0 == gpio_get_value(gpio)){                        if ((jiffies_to_msecs(                                ((long)jiffies - (long)tsc2007->last_touch)) <                 debounce_time &&                tsc2007->status == PEN_DOWN) ||                init_debounce)                        {                init_debounce = false;                                tsc2007_measure(client, &tmp_x, &tmp_y);                                TS_DEBUG(KERN_DEBUG                "dropping pen touch %lu %lu (%u)/n",                                jiffies, tsc2007->last_touch,                                jiffies_to_msecs(                (long)jiffies - (long)tsc2007->last_touch));                                schedule();                continue;                        }            /* continue report x, y */            if (x > 0 && y > 0)            {                ts_linear_scale(&x, &y, invert);                input_report_abs(input, ABS_X, x);                input_report_abs(input, ABS_Y, y);                input_report_abs(input, ABS_PRESSURE, 255);                input_report_abs(input, ABS_TOOL_WIDTH, 1);                input_report_key(input, BTN_TOUCH, 1);                input_sync(input);            }            tsc2007->status = PEN_DOWN;            tsc2007_measure(client, &x, &y);            TS_DEBUG(KERN_DEBUG "pen down x=%d y=%d!/n", x, y);            is_valid = 1;            schedule();        }        if (is_valid)        {            /*consider PEN_UP */            tsc2007->status = PEN_UP;            input_report_abs(input, ABS_PRESSURE, 0);            input_report_abs(input, ABS_TOOL_WIDTH, 1);            input_report_key(input, BTN_TOUCH, 0);            input_sync(input);            tsc2007->last_touch = jiffies;            TS_DEBUG(KERN_DEBUG "pen up!/n");         }        gpio_free(gpio);        }}static irqreturn_t tsc2007_interrupt(int irq, void *dev_id){        schedule_work(&g_tsc2007->irq_work);        return IRQ_HANDLED;}static int __devinit tsc2007_probe(struct i2c_client *client,                 const struct i2c_device_id *id){    struct _tsc2007 *tsc2007;    struct input_dev *input_dev;    int ret;    tsc2007 = kzalloc(sizeof(struct _tsc2007), GFP_KERNEL);    input_dev = input_allocate_device();    g_tsc2007 = tsc2007;    if (!tsc2007 || !input_dev) {        ret = -ENOMEM;        goto fail1;    }    i2c_set_clientdata(client, tsc2007);    tsc2007->dev = input_dev;    input_dev->name = "tsc2007";    input_dev->phys = "tsc2007/input0";    //input_dev->id.bustype = BUS_HOST;    input_dev->dev.parent = &client->dev;    __set_bit(EV_KEY, input_dev->evbit);    __set_bit(BTN_TOUCH, input_dev->keybit);    __set_bit(EV_ABS, input_dev->evbit);    __set_bit(ABS_PRESSURE, input_dev->evbit);    __set_bit(ABS_X, input_dev->evbit);    __set_bit(ABS_Y, input_dev->evbit);    input_set_abs_params(input_dev, ABS_X, x_min, x_max, 0, 0);    input_set_abs_params(input_dev, ABS_Y, y_min, y_max, 0, 0);    input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);    ret = request_irq(client->irq, tsc2007_interrupt,         IRQF_DISABLED | IRQF_TRIGGER_FALLING,         "tsc2007 irq", NULL);    if (ret){        printk(KERN_ERR "tsc2007 request irq failed/n");        goto fail2;    }    ret = input_register_device(tsc2007->dev);    if (ret){        printk(KERN_ERR "tsc2007 register device fail/n");        goto fail2;    }    /*init */    tsc2007->status = PEN_UP;    tsc2007->client = client;    tsc2007->last_touch = jiffies;    INIT_WORK(&tsc2007->irq_work, tsc2007_irq_work);    /* init tsc2007 */    i2c_smbus_write_byte(client, TSC_CMD_SETUP);    return 0; fail2:    free_irq(client->irq, client); fail1:    i2c_set_clientdata(client, NULL);    input_free_device(input_dev);    kfree(tsc2007);    return ret;}static int __devexit tsc2007_remove(struct i2c_client *client){    struct _tsc2007 *tsc2007 = i2c_get_clientdata(client);    if(client->irq)        free_irq(client->irq, client);        i2c_set_clientdata(client, NULL);    input_unregister_device(tsc2007->dev);    kfree(tsc2007);    return 0;}static struct i2c_device_id tsc2007_idtable[] = {     { "tsc2007", 0 },     { } }; MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);static struct i2c_driver tsc2007_driver = {    .driver = {        .name     = "tsc2007",    },    .id_table       = tsc2007_idtable,    .probe        = tsc2007_probe,    .remove        = __devexit_p(tsc2007_remove),};static int __init tsc2007_ts_init(void){    init_sysctl();    return i2c_add_driver(&tsc2007_driver);     }static void __exit tsc2007_ts_exit(void){    cleanup_sysctl();    i2c_del_driver(&tsc2007_driver);}module_init(tsc2007_ts_init);module_exit(tsc2007_ts_exit);MODULE_DESCRIPTION("tsc2007 touch screen driver");MODULE_LICENSE("GPL");


    最新回复(0)