触摸屏驱动程序框架分析

耗尽温柔 提交于 2019-12-02 15:42:41

触摸屏驱动程序,用于人机交互lcd上的独立的一个屏,这里指的是电阻屏。下面来分析一下内核自带的触摸屏驱动框架,便于我们自已编写触摸屏驱动程序

触摸屏驱动使用的是Input_subsys系统。我们打开内核的s3c2410_ts.c触摸屏驱动来分析:下面来看一下流程是怎么样 从入口函数开始分析

static struct platform_driver s3c_ts_driver = {
    .driver         = {
        .name   = "samsung-ts",
        .owner  = THIS_MODULE,
#ifdef CONFIG_PM
        .pm    = &s3c_ts_pmops,
#endif
    },
    .id_table    = s3cts_driver_ids,
    .probe        = s3c2410ts_probe,
    .remove        = __devexit_p(s3c2410ts_remove),
};
module_platform_driver(s3c_ts_driver);
View Code

在这里没有发现所谓的入口函数 init 但我们看到了一条这样的定义 module_platform_driver(s3c_ts_driver); 我们搜索一个这个东西看看是什么,

#define module_platform_driver(__platform_driver) \
    module_driver(__platform_driver, platform_driver_register, \
            platform_driver_unregister)

展开看一下

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
    return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
    __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

##__VA_ARGS__ 宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错 把参数代进去结果就是 入口函数和出口函数

static int __init s3c_ts_driver_init(void) 
{ 
    return platform_driver_register(&(s3c_ts_driver));
} 
module_init(s3c_ts_driver_init); 
static void __exit s3c_ts_driver_exit(void) 
{ 
    platform_driver_unregister(&(s3c_ts_driver)); 
} 
module_exit(s3c_ts_driver_exit);

到这里又到了关平平台总线的概念了,之前的驱动有讲过的,如果内核里有相同名了的device就会调用 s3c_ts_driver 里的s3c2410ts_probeb函数,现在我们分析probeb函数的内容就可以得到基本的触摸屏框架了 进去看一下这个函数做了些什么事情:

 

static int __devinit s3c2410ts_probe(struct platform_device *pdev)
{
    struct s3c2410_ts_mach_info *info;
    struct device *dev = &pdev->dev;
    struct input_dev *input_dev;
    struct resource *res;
    int ret = -EINVAL;

    /* Initialise input stuff */
    memset(&ts, 0, sizeof(struct s3c2410ts));

    ts.dev = dev;

    info = pdev->dev.platform_data;
    if (!info) {
        dev_err(dev, "no platform data, cannot attach\n");
        return -EINVAL;
    }

    dev_dbg(dev, "initialising touchscreen\n");

    ts.clock = clk_get(dev, "adc");
    if (IS_ERR(ts.clock)) {
        dev_err(dev, "cannot get adc clock source\n");
        return -ENOENT;
    }

    clk_enable(ts.clock);
    dev_dbg(dev, "got and enabled clocks\n");

    ts.irq_tc = ret = platform_get_irq(pdev, 0);
    if (ret < 0) {
        dev_err(dev, "no resource for interrupt\n");
        goto err_clk;
    }

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(dev, "no resource for registers\n");
        ret = -ENOENT;
        goto err_clk;
    }

    ts.io = ioremap(res->start, resource_size(res));
    if (ts.io == NULL) {
        dev_err(dev, "cannot map registers\n");
        ret = -ENOMEM;
        goto err_clk;
    }

    /* inititalise the gpio */
    if (info->cfg_gpio)
        info->cfg_gpio(to_platform_device(ts.dev));

    ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
                     s3c24xx_ts_conversion, 1);
    if (IS_ERR(ts.client)) {
        dev_err(dev, "failed to register adc client\n");
        ret = PTR_ERR(ts.client);
        goto err_iomap;
    }

    /* Initialise registers */
    if ((info->delay & 0xffff) > 0)
        writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);

    writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);

    input_dev = input_allocate_device();
    if (!input_dev) {
        dev_err(dev, "Unable to allocate the input device !!\n");
        ret = -ENOMEM;
        goto err_iomap;
    }

    ts.input = input_dev;
    ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
    ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
    input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);

    ts.input->name = "S3C24XX TouchScreen";
    ts.input->id.bustype = BUS_HOST;
    ts.input->id.vendor = 0xDEAD;
    ts.input->id.product = 0xBEEF;
    ts.input->id.version = 0x0102;

    ts.shift = info->oversampling_shift;
    ts.features = platform_get_device_id(pdev)->driver_data;

    ret = request_irq(ts.irq_tc, stylus_irq, 0,
              "s3c2410_ts_pen", ts.input);
    if (ret) {
        dev_err(dev, "cannot get TC interrupt\n");
        goto err_inputdev;
    }

    dev_info(dev, "driver attached, registering input device\n");

    /* All went ok, so register to the input system */
    ret = input_register_device(ts.input);
    if (ret < 0) {
        dev_err(dev, "failed to register input device\n");
        ret = -EIO;
        goto err_tcirq;
    }

    return 0;

 err_tcirq:
    free_irq(ts.irq_tc, ts.input);
 err_inputdev:
    input_free_device(ts.input);
 err_iomap:
    iounmap(ts.io);
 err_clk:
    del_timer_sync(&touch_timer);
    clk_put(ts.clock);
    return ret;
}
View Code

 

初始化一块内存 memset(&ts, 0, sizeof(struct s3c2410ts));

从device里得到 数据 info = pdev->dev.platform_data;

获得adc时钟 ts.clock = clk_get(dev, "adc");

使能(打开) adc时钟 clk_enable(ts.clock);

 

获得资源 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

等等 一些数据 还有 gpio 映射,这些

input_dev = input_allocate_device();  分配一个input_dev 结构体,上面说过会用到输入子系统,这里就体现出来了

一堆设置过后

ret = request_irq(ts.irq_tc, stylus_irq, 0, 注册触摸屏中断

ret = input_register_device(ts.input);  注册input_dev结构体。

看到这些和我们前面说过的输入子系统那功就一模一样了,如果不了解,就去看前面的输入子系统分析

 

当触摸屏有触摸动作时就会进入中断处理函数 stylus_irq 注册中断时这个就是参数了 然后 s3c_adc_start(ts.client, 0, 1 << ts.shift); 启动adc转换 当adc转换完成后,会产生adc中断,在adc中断里启动一个定时器,在定时器中断里上报事件 input_report_key(ts.input, BTN_TOUCH, 0); 这个函数上报

 

static void touch_timer_fire(unsigned long data)
{
    unsigned long data0;
    unsigned long data1;
    bool down;

    data0 = readl(ts.io + S3C2410_ADCDAT0);
    data1 = readl(ts.io + S3C2410_ADCDAT1);

    down = get_down(data0, data1);

    if (down) {
        if (ts.count == (1 << ts.shift)) {
            ts.xp >>= ts.shift;
            ts.yp >>= ts.shift;

            dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
                __func__, ts.xp, ts.yp, ts.count);

            input_report_abs(ts.input, ABS_X, ts.xp);
            input_report_abs(ts.input, ABS_Y, ts.yp);

            input_report_key(ts.input, BTN_TOUCH, 1);
            input_sync(ts.input);

            ts.xp = 0;
            ts.yp = 0;
            ts.count = 0;
        }

        s3c_adc_start(ts.client, 0, 1 << ts.shift);
    } else {
        ts.xp = 0;
        ts.yp = 0;
        ts.count = 0;

        input_report_key(ts.input, BTN_TOUCH, 0);
        input_sync(ts.input);

        writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
    }
}

static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
View Code

 

到基本框架就出来了,还有没分析的就是输入子系统框架了,前有随笔有分析过,总结我们自已写触摸屏驱动有以下步骤

1:分配一个input_dev结构体

2:设置

3:注册

4:硬件相关操作,比如,ADC转换的值的优化,对触摸屏的值解析,上报成我们需要的数据即可

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!