[摘要: 本文以linux3.5--Exynos4412仄台,剖析触摸屏驱动焦点内容。Linux下触摸屏驱动(以ft5x06_ts为例)须要懂得以下学问: 1. I2C协定 2. Exynos4412处置惩罚器的I2C接心 3. I2]

本文以Linux3.5--Exynos4412平台,分析触摸屏驱动核心内容。Linux下触摸屏驱动(以ft5x06_ts为例)需要了解如下知识

1.  I2C协议

2.  Exynos4412处理器的I2C接口

3.  I2C接口触摸屏基本知识

4.  bus-dev-drv模型

5.  Linux下I2C总线驱动框架

6.  Linux下输入子系统

7.  Linux下中断处理

8.  Linux下工作队列机制

9.  Linux下驱动程序设计基本知识

由此可见,Linux下触摸屏驱动涉及的知识点是非常多的,基本上每一个知识点都是Linux下的一个子系统,综合性很强。

一、背景

触摸屏偶尔会卡死,不能操作。通过分析原因,初步认定为I2C死锁问题,关于I2C死锁可以了解文章《I2C死锁原因及解决方法》。为了解决这个问题,决定在发生I2C死锁时,断掉I2C触摸屏的电源,然后给它上电,使处理器和触摸屏之间的I2C通信恢复正常。

这样做的前提是I2C触摸屏内部单片机自带flash,并且一上电,它会自动运行flash里面的程序初始化触摸屏本身,不用外界主控制器通过I2C的SDA信号初始化触摸屏单片机。这样,I2C触摸屏上电后,它自动运行嵌入在内部flash里面的程序,用户有操作时,拉低跟主控制器相连的中断引脚,告诉主控制器有手指按下,这个时候主控制器会读I2C触摸屏,然后触摸屏通过SDA线把触摸数据传输给主控制器。

上述解决思路是参考我们日常用手机时,也会偶而出现手机不能触摸的问题。但是按一下手机关机按钮,然后再打开触摸屏就能用了(并没有重启手机)。这样的操作过程就是把手机触摸屏给断了个电,然后再重新上了下电。

使用这种方法还需找到触摸屏卡死的地方,换句话说得知道在哪种情况下发生后给触摸屏上电和断电。因此,得分析Linux下触摸屏驱动程序,每个细节都不放过。

二、驱动程序的总体框架

1.  bus-dev-drv模型

I2C触摸屏属于I2C设备,它是挂接在I2C总线上的,所以它在Linux下的驱动肯定会涉及到bus-dev-drv模型。在 bus-dev-drv模型中,肯定要注册dev,及把dev结构挂接到bus下的dev链表中;另外还要注册drv,把drv结构挂接到bus结构下的drv链表中。

它们注册的先后顺序任意。但是不管谁注册,(除开要dev或drv挂接到bus相应链表中外)都会触发bus结构的match函数比较"name"。如果匹配,将调用drv的probe函数。在probe函数中可以做任何事情,由用户决定(驱动开发者),一般是注册设备驱动。

2.  设置和注册i2c_client结构

根据文章《Linux3.5下I2C设备驱动程序》,可以知道注册此结构体有四种方法,比较常用的是第二种方法。然后通过分析源码发现本系统中使用了第一种方法。代码如下:

i2c_register_board_info(1, smdk4x12_i2c_devs1,ARRAY_SIZE(smdk4x12_i2c_devs1));

上述代码将被smdk4x12_machine_init函数调用(/arch/arm/mach-exynos/Mach-Tiny4412.c),关于smdk4x12_machine_init函数,它在MACHINE_START-MACHINE_END中被赋给了init_machine,代码如下:

MACHINE_START(TINY4412, "TINY4412")
/* Maintainer: FriendlyARM (www.arm9.net) */
/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
/* Maintainer: Changhwan Youn <chaos.youn@samsung.com> */
.atag_offset = 0x100,
.init_irq = exynos4_init_irq,
.map_io = smdk4x12_map_io,
.handle_irq = gic_handle_irq,
.init_machine = smdk4x12_machine_init,
.init_late = exynos_init_late,
.timer = &exynos4_timer,
.restart = exynos4_restart,
.reserve = &smdk4x12_reserve,
MACHINE_END

关于MACHINE_START(TINY4412, "TINY4412")和MACHINE_END,都是在arch.h文件中定义的宏,具体定义代码如下:

/*
 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
 */
#define MACHINE_START(_type,_name)            \
static const struct machine_desc __mach_desc_##_type    \
 __used                            \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr        = MACH_TYPE_##_type,        \
    .name        = _name,

#define MACHINE_END                \
};

这里需要展开介绍下,我们在移植linux内核时,有个结构体变量需要被定义和初始化,这个变量就是__mach_desc_##_type,类型为machine_desc结构体类型。这个类型的变量放在内核代码段.arch.info.init中,在内核运行初期,被函数lookup_machine_type(汇编)取出,读取流程为:

Start_kernel() -> setup_arch() -> setup_processor() -> lookup_machine_type()

__mach_desc_##_type结构体变量的init_machine(函数指针)指向上述smdk4x12_machine_init函数,在smdk4x12_machine_init函数中,主要是些资源注册的初始化代码,这些代码告诉linux内核一些开发板相关的硬件设备信息,即注册开发板所用到的所有设备的相关硬件信息。另外, __mach_desc_##_type结构体变量其他成员也需要我们实现,如果要定制Linux内核,这里就不展开了。

在上述i2c_register_board_info函数中会把i2c_board_info结构放入__i2c_board_lis链表,然后在i2c_scan_static_board_info函数中使用到__i2c_board_list链表,即调用i2c_new_device函数把链表中的每个成员构造成一个i2c_client,并放入bus-dev-drv模型中总线中设备链表中去。这样,就完成了i2c_client结构体的设置和注册。

另外,在上述smdk4x12_i2c_devs1结构数组中的每项元素的platform_data成员(即i2c_board_info类型结构的platform_data成员)被初始化为了ft5x0x_pdata,如下:

static struct ft5x0x_i2c_platform_data ft5x0x_pdata = http://www.ithao123.cn/{
    .gpio_irq        = EXYNOS4_GPX1(6),
    .irq_cfg        = S3C_GPIO_SFN(0xf),
    .screen_max_x    = 800,
    .screen_max_y    = 1280,
    .pressure_max    = 255,
};

成功调用probe函数后,probe函数的第一个参数struct i2c_client *client即client就可以通过client->dev.platform_data的方式访问到上述构建的ft5x0x_pdata。

因此,在设置和注册i2c_client结构时,需要填充一个i2c_board_info结构,此结构的platform_data成员被初始化的值是保存在了   i2c_client.dev.platform_data中的。

3.  设置和注册i2c_driver结构

在driver/input/touchscreen中的ft5x05_ts.c中,入口函数ft5x0x_ts_init将调用i2c_add_driver注册i2c_driver。i2c_add_driver函数的参数指向如下的结构体:

static struct i2c_driver ft5x0x_ts_driver = {
.probe = ft5x0x_ts_probe,
.remove = __devexit_p(ft5x0x_ts_remove),
.id_table = ft5x0x_ts_id,
.driver = {
.name = FT5X0X_NAME,
.owner = THIS_MODULE,
},
};

注册过程就是向bus总线的drv链表中增加一个i2c_driver结构,并调用bus的match函数匹配上述i2c_board_info结构中的name(用i2c_driver结构的id_table成员去匹配)。如果匹配,将调用探测函数probe,即上述ft5x0x_ts_probe。

4.  关于设备驱动层、核心层、适配器层

当探测函数probe被调用后,设备驱动层即在里面实现(跟上层应用相关)。在这里,ft5x05_ts.c中的ft5x0x_ts_probe函数注册中断(设备驱动相关内容),在中断处理函数中将调用I2C核心层相关函数,核心层相关函数最终是调用适配器层相关函数实现I2C总线上的具体的底层硬件操作(这里是操作4412处理器的I2C控制器的寄存器即可实现I2C操作)。也就是说,我们一般不用实现核心层、适配器层,这些都是内核中做好了的,只是在实现设备驱动层时要调用核心层、适配器层的某些现成的函数。

5. 关于平台设备的注册(I2C控制器)

(1)构建platform_device型设备tiny4412_i2c1_data

在smdk4x12_machine_init函数中,调用s3c_i2c1_set_platdata(&tiny4412_i2c1_data)构建platform_device型设备tiny4412_i2c1_data,相关代码如下:

→s3c_i2c1_set_platdata(&tiny4412_i2c1_data);

→s3c_set_platdata(&tiny4412_i2c1_data, sizeof(struct s3c2410_platform_i2c),&s3c_device_i2c1);

→struct platform_device s3c_device_i2c1 = {
                                                                             .name        = "s3c2410-i2c",
                                                                             .id        = 1,
                                                                             .num_resources    = ARRAY_SIZE(s3c_i2c1_resource),
                                                                             .resource    = s3c_i2c1_resource,
                                                                        };

→static struct resource s3c_i2c1_resource[] = {
                                                                                                                       [0] = DEFINE_RES_MEM(S3C_PA_IIC1, SZ_4K),
                                                                                                                       [1] = DEFINE_RES_IRQ(IRQ_IIC1),
                                                                                                                   };

其中,tiny4412_i2c1_data结构如下:

static struct s3c2410_platform_i2c tiny4412_i2c1_data __initdata = http://www.ithao123.cn/{
    .flags            = 0,
    .bus_num        = 1,
    .slave_addr        = 0x10,
    .frequency        = 200*1000,
    .sda_delay        = 100,
};

(2)I2C总线注册到平台总线

我们都知道,Linux中所有总线上的设备通过bus-dev-drv模型实现驱动,然后所有的总线也会挂接在平台总线上,只是平台总线是Linux内核中虚拟出来的一条总线而已。

首先将之前创建的platform_device型设备tiny4412_i2c1_data添加到platform_bus总线,一般调用platform_add_device函数实现,此函数最终调用platform_device_register函数,这就就实现了平台设备的注册。

三、设备驱动层实现之中断

我们都知道,一般的I2C触摸屏有中断引脚。当用户触摸屏幕后,触摸屏里的MCU将把中断引脚拉低告知主控制器有触摸动作。所以,主控制器必须要初始化中断,在这里就体现为Linux下中断的注册(关于Linux下中断体系结构、中断的初始化、中断的注册、中断实现、卸载详见其他资料,比如《嵌入式Linux应用开发完全手册》中讲得很透彻)。

上述ft5x0x_ts_probe函数中,注册中断的代码如下:

err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_FALLING*/, "ft5x0x_ts", ts);

1.  第一个参数client->irq

在介绍client指向的结构体的irq成员之前,先分析client指向的内容的初始化,初始化函数为i2c_set_clientdata(client, ts)。由此可见,client指向的内容被ts指向的内容初始化。在ft5x0x_ts_probe前面部分中可以看到ts指向的内容被pdata指向的内容初始化。而pdata指向client->dev.platform_dat,即代码pdata = http://www.ithao123.cn/client->dev.platform_data,

request_irq函数的第一个参数为注册中断的中断号,此中断号的值为client->irq,client->irq的设置过程为client->irq = gpio_to_irq(ts->gpio_irq)。由此可见此中断号跟ts->gpio_irq有关,看代码可知ts->gpio_irq = pdata->gpio_irq,因此最终中断号跟pdata->gpio_irq的值有关。pdata指向ft5x0x_i2c_platform_data类型的结构

体,在Mach-tiny4412.c中,有如下部分代码:

#ifdef CONFIG_TOUCHSCREEN_FT5X0X
#include <plat/ft5x0x_touch.h>
static struct ft5x0x_i2c_platform_data ft5x0x_pdata = http://www.ithao123.cn/{
.gpio_irq = EXYNOS4_GPX1(6),//对应原理图中EINT14
.irq_cfg = S3C_GPIO_SFN(0xf),
.screen_max_x = 800,
.screen_max_y = 1280,
.pressure_max = 255,
};
#endif

static struct i2c_board_info smdk4x12_i2c_devs1[] __initdata = http://www.ithao123.cn/{
#ifdef CONFIG_TOUCHSCREEN_FT5X0X
{
I2C_BOARD_INFO("ft5x0x_ts", (0x70 >> 1)),
.platform_data = &ft5x0x_pdata,
},
#endif

从个代码中可以知道gpio_irq的值被设置为EXYNOS4_GPX1(6),即原理图上的EINT14,也就是触摸屏的中断引脚。

2.  第二个参数ft5x0x_ts_interrupt

此参数就是中断处理函数,也就是中断发生后将自动调用的函数。此函数代码如下:

static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id) {
    struct ft5x0x_ts_data *ts = dev_id;

disable_irq_nosync(this_client->irq);   //禁用中断

if (!work_pending(&ts->work)) {
        queue_work(ts->queue, &ts->work);
    }

return IRQ_HANDLED;
}

ft5x0x_ts_interrupt函数中剩下部分代码为linux下workqueue相关,workqueue相关知识查看文章《linux  workqueue工作原理》。这里意思是比较耗时的操作不能放在中断处理函数中,应该使用workqueue的知识在内核中另外创建一个线程,专门用来做比较耗时的操作,对应函数为ft5x0x_ts_pen_irq_work。

注意,INIT_WORK(&ts->work, ft5x0x_ts_pen_irq_work);和ts->queue = create_singlethread_workqueue(dev_name(&client->dev));都是为使用workqueue机制做准备。

总之,中断处理函数被调用后,接下来就会调用ft5x0x_ts_pen_irq_work函数,此函数代码如下:

static void ft5x0x_ts_pen_irq_work(struct work_struct *work) {
    struct ft5x0x_ts_data *ts = container_of(work, struct ft5x0x_ts_data, work);

if (!ft5x0x_read_data(ts)) {
        ft5x0x_ts_report(ts);
    }

enable_irq(this_client->irq);
}

上述ft5x0x_read_data(ts)函数和 ft5x0x_ts_report(ts)函数就是比较耗时的操作,ft5x0x_read_data(ts)函数为主控制器向I2C触摸屏那读数据,最终会调用I2C核心层的i2c_transfer函数,此函数最终会调用I2C适配器层的相关函数实现I2C接口硬件相关操作。

如果读到数据,将调用ft5x0x_ts_report(ts)函数上报数据,这是输入子系统相关内容,下面会介绍到。

最后,中断处理完成,可以打开中断了,即调用enable_irq(this_client->irq);使能中断。

3.  剩下的参数

第三个参数是中断触发方式,即上升延触发还是电平触发等;第4个参数是名字;第5个参数是中断发生的次数的记录。

最后,再说下ft5x0x_ts_probe函数中,ft5x0x_i2c_platform_data

因此,lInux内核源码也不是那么神秘。另外吐槽一下国产手机,为什么屏幕不能触摸了后非要黑屏一下,然后在上电就能用了,那样非要用户按一下,如果能在程序中自动做多好,那样给用户的感觉就是触摸屏不会出现不能使用的情况,ip手机貌似触摸屏就没死过,里面肯定做了恢复处理的。

4.  屏卡死问题解决思路

不管触摸屏与主控制器的I2C连接是否死锁,当有手指按下后,触摸屏MCU里面的程序还是会响应一个中断,即会把中断引脚拉低,这里对应的中断引脚为EINT14。主控制器端是注册过中断的,当此中断引脚拉低就会触发主控制器那边注册的中断处理函数。

所以,在触摸屏断电、上电过程肯定在probe函数中的中断处理函数中进行。而且是发生中断,但是读写I2C触摸屏数据失败的情况下关闭触摸屏电源,中断返回时打开触摸屏电源。

四、设备驱动层实现之输入子系统

关于Linux下输入子系统查看文章《Linux输入子系统(input  subsystem)》。

在probe函数中,也实现了输入子系统设备驱动程序,部分代码如下:

	input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
dev_err(&client->dev, "failed to allocate input device\n");
goto exit_input_dev_alloc_failed;
} ts->input_dev = input_dev; set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit); #ifdef CONFIG_FT5X0X_MULTITOUCH
set_bit(ABS_MT_TRACKING_ID, input_dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
set_bit(ABS_MT_POSITION_X, input_dev->absbit);
set_bit(ABS_MT_POSITION_Y, input_dev->absbit); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ts->screen_max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ts->screen_max_y, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ts->pressure_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, FT5X0X_PT_MAX, 0, 0);
#else
set_bit(ABS_X, input_dev->absbit);
set_bit(ABS_Y, input_dev->absbit);
set_bit(ABS_PRESSURE, input_dev->absbit);
set_bit(BTN_TOUCH, input_dev->keybit); input_set_abs_params(input_dev, ABS_X, 0, ts->screen_max_x, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, ts->screen_max_y, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ts->pressure_max, 0 , 0);
#endif input_dev->name = FT5X0X_NAME;
input_dev->phys = "input(mt)";
input_dev->id.bustype = BUS_I2C;
input_dev->id.vendor = 0x12FA;
input_dev->id.product = 0x2143;
input_dev->id.version = 0x0100; err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
dev_err(&client->dev, "failed to register input device %s, %d\n",
dev_name(&client->dev), err);
goto exit_input_dev_alloc_failed;
} msleep(3);

ft5x0x_ts_report函数即上报事件,在ft5x0x_ts_pen_irq_work函数中被调用。

五、probe函数里面所有源码

static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/*
ft5x0x_i2c_platform_data结构已经在内核初始化时设置好了,具体查看mach-tiny4412.c中smdk4x12_i2c_devs1结构体数组中,第一个i2c_board_info型结构的platform_data成员
然后,ft5x0x_ts_data结构成员的初始化就会根据ft5x0x_i2c_platform_data结构的的值来设置。 */
struct ft5x0x_i2c_platform_data *pdata;
struct ft5x0x_ts_data *ts;
struct input_dev *input_dev;
unsigned char val;
unsigned int ctp_id;
int err = -EINVAL; ctp_id = tiny4412_get_ctp();
if (ctp_id != CTP_FT5X06 && ctp_id != CTP_AUTO) {
return -ENODEV;
} if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit_check_functionality_failed;
} ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (!ts) {
err = -ENOMEM;
goto exit_alloc_data_failed;
} pdata = http://www.ithao123.cn/client->dev.platform_data;
if (!pdata) {
dev_err(&client->dev, "failed to get platform data/n");
goto exit_no_pdata;
} ts->screen_max_x = pdata->screen_max_x;
ts->screen_max_y = pdata->screen_max_y;
ts->pressure_max = pdata->pressure_max; ts->gpio_irq = pdata->gpio_irq;
if (ts->gpio_irq != -EINVAL) {
client->irq = gpio_to_irq(ts->gpio_irq);
} else {
goto exit_no_pdata;
}
if (pdata->irq_cfg) {
s3c_gpio_cfgpin(ts->gpio_irq, pdata->irq_cfg);
s3c_gpio_setpull(ts->gpio_irq, S3C_GPIO_PULL_NONE);
} ts->gpio_wakeup = pdata->gpio_wakeup;
ts->gpio_reset = pdata->gpio_reset;
//在运行时通过一个指针创建一个工作
INIT_WORK(&ts->work, ft5x0x_ts_pen_irq_work);
this_client = client;
i2c_set_clientdata(client, ts); //创建工作queue
ts->queue = create_singlethread_workqueue(dev_name(&client->dev));
if (!ts->queue) {
err = -ESRCH;
goto exit_create_singlethread;
} input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
dev_err(&client->dev, "failed to allocate input device/n");
goto exit_input_dev_alloc_failed;
} ts->input_dev = input_dev; set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit); #ifdef CONFIG_FT5X0X_MULTITOUCH
set_bit(ABS_MT_TRACKING_ID, input_dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
set_bit(ABS_MT_POSITION_X, input_dev->absbit);
set_bit(ABS_MT_POSITION_Y, input_dev->absbit); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ts->screen_max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ts->screen_max_y, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ts->pressure_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, FT5X0X_PT_MAX, 0, 0);
#else
set_bit(ABS_X, input_dev->absbit);
set_bit(ABS_Y, input_dev->absbit);
set_bit(ABS_PRESSURE, input_dev->absbit);
set_bit(BTN_TOUCH, input_dev->keybit); input_set_abs_params(input_dev, ABS_X, 0, ts->screen_max_x, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, ts->screen_max_y, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ts->pressure_max, 0 , 0);
#endif input_dev->name = FT5X0X_NAME;
input_dev->phys = "input(mt)";
input_dev->id.bustype = BUS_I2C;
input_dev->id.vendor = 0x12FA;
input_dev->id.product = 0x2143;
input_dev->id.version = 0x0100; err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
dev_err(&client->dev, "failed to register input device %s, %d/n",
dev_name(&client->dev), err);
goto exit_input_dev_alloc_failed;
} msleep(3);
err = ft5x0x_read_fw_ver(&val);
if (err < 0) {
dev_err(&client->dev, "chip not found/n");
goto exit_irq_request_failed;
}
/*
注册一个中断,当用户操作触摸屏后,触摸屏与ARM连接的EINT14就会被触发(对应引脚为GPX1-6),这里就会调用ft5x0x_ts_interrupt中断处理函数,这里的中断处理函数相当于最初的处理,即顶半部中断。
在此中断处理函数中还会根据linux内核的工作队列机制运行另外一个函数即上面的ft5x0x_ts_pen_irq_work函数。这样做的目的是中断处理函数ft5x0x_ts_interrupt里面不能做太多费时操作,就把那些费时操作放在ft5x0x_ts_pen_irq_work函数
中执行。
在ft5x0x_ts_pen_irq_work函数中先调用ft5x0x_read_data函数读数据,此函数根据I2C总线驱动模型,此函数里面肯定会调用核心层里的函数,核心层里的函数最终会调用适配器
层里的函数操作处理器的I2C控制器实现I2C触摸屏数据的读写。
在ft5x0x_ts_pen_irq_work函数中最后会调用ft5x0x_ts_report函数上报上报事件,具体上报过程就是输入子系统的事了。 */
err = request_irq(client->irq, ft5x0x_ts_interrupt,
IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_FALLING*/, "ft5x0x_ts", ts);
if (err < 0) {
dev_err(&client->dev, "Request IRQ %d failed, %d/n", client->irq, err);
goto exit_irq_request_failed;
} disable_irq(client->irq); dev_info(&client->dev, "Firmware version 0x%02x/n", val); #ifdef CONFIG_HAS_EARLYSUSPEND
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
ts->early_suspend.suspend = ft5x0x_ts_suspend;
ts->early_suspend.resume = ft5x0x_ts_resume;
register_early_suspend(&ts->early_suspend);
#endif enable_irq(client->irq); tiny4412_set_ctp(CTP_FT5X06);
dev_info(&client->dev, "FocalTech ft5x0x TouchScreen initialized/n");
return 0; exit_irq_request_failed:
input_unregister_device(input_dev); exit_input_dev_alloc_failed:
cancel_work_sync(&ts->work);
destroy_workqueue(ts->queue);//create_singlethread_workqueue函数返回一个类型为struct workqueue_struct的指针变量,该指针变量所指向的内存地址在函数内部调用kzalloc动态生成。所以driver在不再使用该work queue的情况下调用此函数 exit_create_singlethread:
i2c_set_clientdata(client, NULL); exit_no_pdata:
kfree(ts); exit_alloc_data_failed:
exit_check_functionality_failed:
dev_err(&client->dev, "probe ft5x0x TouchScreen failed, %d/n", err); return err;
}

六、关于校准

可以参见资料:http://blog.csdn.net/liukun321/article/details/24102307

Linux下触摸屏驱动程序分析的更多相关文章

  1. 在Linux下,如何分析一个程序达到性能瓶颈的原因

    0.在Linux下,如何分析一个程序达到性能瓶颈的原因,请分别从CPU.内存.IO.网络的角度判断是谁导致的瓶颈?注意现在的机器CPU是多核 1.用sar -n DEV 1 10 2.用iotop命令 ...

  2. 几款抓包工具在windows,mac,linux下的支持分析

    抓包工具的使用 几款抓包工具在windows,mac,linux下的支持分析 抓包工具简介 Chrome/Firefox 开发者工具: 浏览器内置,方便易用 Fiddler/Charles: 基于代理 ...

  3. 以C语言为例完成简单的网络聊天程序以及关于socket在Linux下系统调用的分析

    套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程. 端 ...

  4. (转)Linux 下栈溢出问题分析解决 *** stack smashing detected *** XXXX terminated

    Linux 下栈溢出问题分析解决 *** stack smashing detected *** XXXX terminated 1.利用gdb 或者valgrind 定位到具体的代码 最近在Linu ...

  5. Android/Linux下CGroup框架分析及其使用

    1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...

  6. linux下进程权限分析

    转自http://blog.chinaunix.net/uid-27105712-id-3349522.html 在linux下,关于文件权限,大部分人接触比较多,也比较熟悉了解.但是对进程权限一般知 ...

  7. linux下编译原理分析

    linux下编译hello.c 程序,使用gcc hello.c,然后./a.out就能够执行:在这个简单的命令后面隐藏了很多复杂的过程,这个过程包含了以下的步骤: ================= ...

  8. Linux 下蓝牙bluez分析及使用 (1)

    蓝牙耳机的使用 由于Linux下蓝牙使用不及Windows下直观,致使使用时会出现一些小问题.虽然是小问题,但是由于普通的操作都具有顺序性,前面的中断了,后面的哪怕再简单,也无法继续了.正好近期工作与 ...

  9. Linux下UPnP sample分析

        一.UPnP简介   UPnP(Universal Plug and Play)技术是一种屏蔽各种数字设备的硬件和操作系统的通信协议.它是一种数字网络中间件技术,建立在TCP/IP.HTTP协 ...

随机推荐

  1. Ubuntu安装与配置KVM

    事前检查 查看一下linux是32位还是64位 file /bin/ls 确认一下 CPU支持硬件虚拟化(不支持也没关系,可以继续) egrep -o '(vmx|svm)' /proc/cpuinf ...

  2. 学习笔记:Zookeeper 应用案例(上下线动态感知)

    1.Zookeeper 应用案例(上下线动态感知) 8.1 案例1--服务器上下线动态感知 8.1.1 需求描述 某分布式系统中,主节点可以有多台,可以动态上下线 任意一台客户端都能实时感知到主节点服 ...

  3. lglob-lua 静态检查脚本

    ./lglob ~/ngx/lualib/mvc/*.lua 2>&1 | grep ' set '

  4. iOS学习笔记--触摸事件

    最近空闲时间在学习iOS相关知识,几周没有更新文章了,今天总结下这些天的学习内容,也整理下iOS的学习笔记,以便以后查阅翻看- iOS中的事件可以分为3大类型: 触摸事件 加速计事件 远程控制事件 响 ...

  5. Compass 更智能的搜索引擎(3)--高亮,排序,过滤以及各种搜索

    要想使得一个搜索系统更加的完美,查询精确度和页面显示算是其中比较重要的两个方面.今天,我们就来谈谈怎么使得我们的搜索系统更加的完美. 关于分词 下载地址 配置 关于高亮 关于排序 原理 冗余字段 使用 ...

  6. [mysql]创建数据库并指定编码

    现在用orm比较多,很多mysql表啊都不用自己创建,但是数据库还是要自己创建,记录下sql, 备忘. CREATE DATABASE `mydb` CHARACTER SET utf8 COLLAT ...

  7. JVM基础知识GC

    在网上看到一篇很不错的讲解JVM GC的文章,看完之后觉得可以留着以后多看几遍便转载了下来.但是找了半天也没有找到原作者地址.抱歉不能标明原文地址了.以下是文章内容. 几年前写过一篇关于JVM调优的文 ...

  8. 保存图片到SD卡

    添加SD卡写权限 方法 public void saveMyBitmap(String bitName, Bitmap mBitmap) { File f = new File("/sdca ...

  9. 带你深入理解STL之迭代器和Traits技法

    在开始讲迭代器之前,先列举几个例子,由浅入深的来理解一下为什么要设计迭代器. //对于int类的求和函数 int sum(int *a , int n) { int sum = 0 ; for (in ...

  10. 关闭Win10自动更新

    使用过Windows10系统的小伙伴们都清楚,在Windows10中强制开启了自动更新功能,我们无法通过常规的办法关闭自动更新功能,那么我们该怎么去关闭自动更新呢?欢迎速来围观我的经验啦. 工具/原料 ...