日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux下触摸屏驱动程序分析

發(fā)布時(shí)間:2025/4/16 linux 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下触摸屏驱动程序分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
[摘要: 本文以 linux 3.5--Exynos4412仄臺(tái),剖析 觸摸屏 驅(qū)動(dòng)焦點(diǎn)內(nèi)容。Linux下觸摸屏驅(qū)動(dòng)(以ft5x06_ts為例)須要懂得以下學(xué)問(wèn): 1. I2C協(xié)定 2. Exynos4412處置懲罰器的I2C接心 3. I2]?

?????本文以Linux3.5--Exynos4412平臺(tái),分析觸摸屏驅(qū)動(dòng)核心內(nèi)容。Linux下觸摸屏驅(qū)動(dòng)(以ft5x06_ts為例)需要了解如下知識(shí):

1.? I2C協(xié)議

2.? Exynos4412處理器的I2C接口

3.? I2C接口觸摸屏基本知識(shí)

4.? bus-dev-drv模型

5.? Linux下I2C總線驅(qū)動(dòng)框架

6.? Linux下輸入子系統(tǒng)

7.? Linux下中斷處理

8.? Linux下工作隊(duì)列機(jī)制

9.? Linux下驅(qū)動(dòng)程序設(shè)計(jì)基本知識(shí)

????? 由此可見(jiàn),Linux下觸摸屏驅(qū)動(dòng)涉及的知識(shí)點(diǎn)是非常多的,基本上每一個(gè)知識(shí)點(diǎn)都是Linux下的一個(gè)子系統(tǒng),綜合性很強(qiáng)。


一、背景


???? 觸摸屏偶爾會(huì)卡死,不能操作。通過(guò)分析原因,初步認(rèn)定為I2C死鎖問(wèn)題,關(guān)于I2C死鎖可以了解文章《I2C死鎖原因及解決方法》。為了解決這個(gè)問(wèn)題,決定在發(fā)生I2C死鎖時(shí),斷掉I2C觸摸屏的電源,然后給它上電,使處理器和觸摸屏之間的I2C通信恢復(fù)正常。

??? 這樣做的前提是I2C觸摸屏內(nèi)部單片機(jī)自帶flash,并且一上電,它會(huì)自動(dòng)運(yùn)行flash里面的程序初始化觸摸屏本身,不用外界主控制器通過(guò)I2C的SDA信號(hào)初始化觸摸屏單片機(jī)。這樣,I2C觸摸屏上電后,它自動(dòng)運(yùn)行嵌入在內(nèi)部flash里面的程序,用戶有操作時(shí),拉低跟主控制器相連的中斷引腳,告訴主控制器有手指按下,這個(gè)時(shí)候主控制器會(huì)讀I2C觸摸屏,然后觸摸屏通過(guò)SDA線把觸摸數(shù)據(jù)傳輸給主控制器。

??? 上述解決思路是參考我們?nèi)粘S檬謾C(jī)時(shí),也會(huì)偶而出現(xiàn)手機(jī)不能觸摸的問(wèn)題。但是按一下手機(jī)關(guān)機(jī)按鈕,然后再打開(kāi)觸摸屏就能用了(并沒(méi)有重啟手機(jī))。這樣的操作過(guò)程就是把手機(jī)觸摸屏給斷了個(gè)電,然后再重新上了下電。

???? 使用這種方法還需找到觸摸屏卡死的地方,換句話說(shuō)得知道在哪種情況下發(fā)生后給觸摸屏上電和斷電。因此,得分析Linux下觸摸屏驅(qū)動(dòng)程序,每個(gè)細(xì)節(jié)都不放過(guò)。


二、驅(qū)動(dòng)程序的總體框架


1.? bus-dev-drv模型


? ?? I2C觸摸屏屬于I2C設(shè)備,它是掛接在I2C總線上的,所以它在Linux下的驅(qū)動(dòng)肯定會(huì)涉及到bus-dev-drv模型。在 bus-dev-drv模型中,肯定要注冊(cè)dev,及把dev結(jié)構(gòu)掛接到bus下的dev鏈表中;另外還要注冊(cè)drv,把drv結(jié)構(gòu)掛接到bus結(jié)構(gòu)下的drv鏈表中。

???? 它們注冊(cè)的先后順序任意。但是不管誰(shuí)注冊(cè),(除開(kāi)要dev或drv掛接到bus相應(yīng)鏈表中外)都會(huì)觸發(fā)bus結(jié)構(gòu)的match函數(shù)比較"name"。如果匹配,將調(diào)用drv的probe函數(shù)。在probe函數(shù)中可以做任何事情,由用戶決定(驅(qū)動(dòng)開(kāi)發(fā)者),一般是注冊(cè)設(shè)備驅(qū)動(dòng)。


2.? 設(shè)置和注冊(cè)i2c_client結(jié)構(gòu)


????? 根據(jù)文章《Linux3.5下I2C設(shè)備驅(qū)動(dòng)程序》,可以知道注冊(cè)此結(jié)構(gòu)體有四種方法,比較常用的是第二種方法。然后通過(guò)分析源碼發(fā)現(xiàn)本系統(tǒng)中使用了第一種方法。代碼如下:

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

????? 上述代碼將被smdk4x12_machine_init函數(shù)調(diào)用(/arch/arm/mach-exynos/Mach-Tiny4412.c),關(guān)于smdk4x12_machine_init函數(shù),它在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
????? ?關(guān)于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?? ??? ??? ??? ?\
};

????? 這里需要展開(kāi)介紹下,我們?cè)谝浦瞝inux內(nèi)核時(shí),有個(gè)結(jié)構(gòu)體變量需要被定義和初始化,這個(gè)變量就是__mach_desc_##_type,類(lèi)型為machine_desc結(jié)構(gòu)體類(lèi)型。這個(gè)類(lèi)型的變量放在內(nèi)核代碼段.arch.info.init中,在內(nèi)核運(yùn)行初期,被函數(shù)lookup_machine_type(匯編)取出,讀取流程為:

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

????? __mach_desc_##_type結(jié)構(gòu)體變量的init_machine(函數(shù)指針)指向上述smdk4x12_machine_init函數(shù),在smdk4x12_machine_init函數(shù)中,主要是些資源注冊(cè)的初始化代碼,這些代碼告訴linux內(nèi)核一些開(kāi)發(fā)板相關(guān)的硬件設(shè)備信息,即注冊(cè)開(kāi)發(fā)板所用到的所有設(shè)備的相關(guān)硬件信息。另外,?__mach_desc_##_type結(jié)構(gòu)體變量其他成員也需要我們實(shí)現(xiàn),如果要定制Linux內(nèi)核,這里就不展開(kāi)了。

?????? 在上述i2c_register_board_info函數(shù)中會(huì)把i2c_board_info結(jié)構(gòu)放入__i2c_board_lis鏈表,然后在i2c_scan_static_board_info函數(shù)中使用到__i2c_board_list鏈表,即調(diào)用i2c_new_device函數(shù)把鏈表中的每個(gè)成員構(gòu)造成一個(gè)i2c_client,并放入bus-dev-drv模型中總線中設(shè)備鏈表中去。這樣,就完成了i2c_client結(jié)構(gòu)體的設(shè)置和注冊(cè)。


???? 另外,在上述smdk4x12_i2c_devs1結(jié)構(gòu)數(shù)組中的每項(xiàng)元素的platform_data成員(即i2c_board_info類(lèi)型結(jié)構(gòu)的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,
};

????? 成功調(diào)用probe函數(shù)后,probe函數(shù)的第一個(gè)參數(shù)struct i2c_client *client即client就可以通過(guò)client->dev.platform_data的方式訪問(wèn)到上述構(gòu)建的ft5x0x_pdata。

????? 因此,在設(shè)置和注冊(cè)i2c_client結(jié)構(gòu)時(shí),需要填充一個(gè)i2c_board_info結(jié)構(gòu),此結(jié)構(gòu)的platform_data成員被初始化的值是保存在了 ? i2c_client.dev.platform_data中的。


3.? 設(shè)置和注冊(cè)i2c_driver結(jié)構(gòu)


????? 在driver/input/touchscreen中的ft5x05_ts.c中,入口函數(shù)ft5x0x_ts_init將調(diào)用i2c_add_driver注冊(cè)i2c_driver。i2c_add_driver函數(shù)的參數(shù)指向如下的結(jié)構(gòu)體:

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,}, };

???????注冊(cè)過(guò)程就是向bus總線的drv鏈表中增加一個(gè)i2c_driver結(jié)構(gòu),并調(diào)用bus的match函數(shù)匹配上述i2c_board_info結(jié)構(gòu)中的name(用i2c_driver結(jié)構(gòu)的id_table成員去匹配)。如果匹配,將調(diào)用探測(cè)函數(shù)probe,即上述ft5x0x_ts_probe。

????????

4.? 關(guān)于設(shè)備驅(qū)動(dòng)層、核心層、適配器層


???? 當(dāng)探測(cè)函數(shù)probe被調(diào)用后,設(shè)備驅(qū)動(dòng)層即在里面實(shí)現(xiàn)(跟上層應(yīng)用相關(guān))。在這里,ft5x05_ts.c中的ft5x0x_ts_probe函數(shù)注冊(cè)中斷(設(shè)備驅(qū)動(dòng)相關(guān)內(nèi)容),在中斷處理函數(shù)中將調(diào)用I2C核心層相關(guān)函數(shù),核心層相關(guān)函數(shù)最終是調(diào)用適配器層相關(guān)函數(shù)實(shí)現(xiàn)I2C總線上的具體的底層硬件操作(這里是操作4412處理器的I2C控制器的寄存器即可實(shí)現(xiàn)I2C操作)。也就是說(shuō),我們一般不用實(shí)現(xiàn)核心層、適配器層,這些都是內(nèi)核中做好了的,只是在實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)層時(shí)要調(diào)用核心層、適配器層的某些現(xiàn)成的函數(shù)。


5. 關(guān)于平臺(tái)設(shè)備的注冊(cè)(I2C控制器)


(1)構(gòu)建platform_device型設(shè)備tiny4412_i2c1_data


???? ?在smdk4x12_machine_init函數(shù)中,調(diào)用s3c_i2c1_set_platdata(&tiny4412_i2c1_data)構(gòu)建platform_device型設(shè)備tiny4412_i2c1_data,相關(guān)代碼如下:

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結(jié)構(gòu)如下:

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總線注冊(cè)到平臺(tái)總線


????? 我們都知道,Linux中所有總線上的設(shè)備通過(guò)bus-dev-drv模型實(shí)現(xiàn)驅(qū)動(dòng),然后所有的總線也會(huì)掛接在平臺(tái)總線上,只是平臺(tái)總線是Linux內(nèi)核中虛擬出來(lái)的一條總線而已。

???? 首先將之前創(chuàng)建的platform_device型設(shè)備tiny4412_i2c1_data添加到platform_bus總線,一般調(diào)用platform_add_device函數(shù)實(shí)現(xiàn),此函數(shù)最終調(diào)用platform_device_register函數(shù),這就就實(shí)現(xiàn)了平臺(tái)設(shè)備的注冊(cè)。


三、設(shè)備驅(qū)動(dòng)層實(shí)現(xiàn)之中斷


???? 我們都知道,一般的I2C觸摸屏有中斷引腳。當(dāng)用戶觸摸屏幕后,觸摸屏里的MCU將把中斷引腳拉低告知主控制器有觸摸動(dòng)作。所以,主控制器必須要初始化中斷,在這里就體現(xiàn)為L(zhǎng)inux下中斷的注冊(cè)(關(guān)于Linux下中斷體系結(jié)構(gòu)、中斷的初始化、中斷的注冊(cè)、中斷實(shí)現(xiàn)、卸載詳見(jiàn)其他資料,比如《嵌入式Linux應(yīng)用開(kāi)發(fā)完全手冊(cè)》中講得很透徹)。

??? 上述ft5x0x_ts_probe函數(shù)中,注冊(cè)中斷的代碼如下:

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


1.? 第一個(gè)參數(shù)client->irq


??????在介紹client指向的結(jié)構(gòu)體的irq成員之前,先分析client指向的內(nèi)容的初始化,初始化函數(shù)為i2c_set_clientdata(client, ts)。由此可見(jiàn),client指向的內(nèi)容被ts指向的內(nèi)容初始化。在ft5x0x_ts_probe前面部分中可以看到ts指向的內(nèi)容被pdata指向的內(nèi)容初始化。而pdata指向client->dev.platform_dat,即代碼pdata = http://www.ithao123.cn/client->dev.platform_data,

????? request_irq函數(shù)的第一個(gè)參數(shù)為注冊(cè)中斷的中斷號(hào),此中斷號(hào)的值為client->irq,client->irq的設(shè)置過(guò)程為client->irq = gpio_to_irq(ts->gpio_irq)。由此可見(jiàn)此中斷號(hào)跟ts->gpio_irq有關(guān),看代碼可知ts->gpio_irq = pdata->gpio_irq,因此最終中斷號(hào)跟pdata->gpio_irq的值有關(guān)。pdata指向ft5x0x_i2c_platform_data類(lèi)型的結(jié)構(gòu)

體,在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),//對(duì)應(yīng)原理圖中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 ??????? 從個(gè)代碼中可以知道g pio_irq的值被設(shè)置為 E XYNOS4_GPX1(6) ,即原理圖上的EINT14,也就是觸摸屏的中斷引腳。

2.? 第二個(gè)參數(shù)ft5x0x_ts_interrupt


???? 此參數(shù)就是中斷處理函數(shù),也就是中斷發(fā)生后將自動(dòng)調(diào)用的函數(shù)。此函數(shù)代碼如下:

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函數(shù)中剩下部分代碼為linux下workqueue相關(guān),workqueue相關(guān)知識(shí)查看文章《linux? workqueue工作原理》。這里意思是比較耗時(shí)的操作不能放在中斷處理函數(shù)中,應(yīng)該使用workqueue的知識(shí)在內(nèi)核中另外創(chuàng)建一個(gè)線程,專(zhuān)門(mén)用來(lái)做比較耗時(shí)的操作,對(duì)應(yīng)函數(shù)為ft5x0x_ts_pen_irq_work。

????? 注意,INIT_WORK(&ts->work, ft5x0x_ts_pen_irq_work);和ts->queue = create_singlethread_workqueue(dev_name(&client->dev));都是為使用workqueue機(jī)制做準(zhǔn)備。

????? 總之,中斷處理函數(shù)被調(diào)用后,接下來(lái)就會(huì)調(diào)用ft5x0x_ts_pen_irq_work函數(shù),此函數(shù)代碼如下:

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)函數(shù)和?ft5x0x_ts_report(ts)函數(shù)就是比較耗時(shí)的操作,ft5x0x_read_data(ts)函數(shù)為主控制器向I2C觸摸屏那讀數(shù)據(jù),最終會(huì)調(diào)用I2C核心層的i2c_transfer函數(shù),此函數(shù)最終會(huì)調(diào)用I2C適配器層的相關(guān)函數(shù)實(shí)現(xiàn)I2C接口硬件相關(guān)操作。

????? 如果讀到數(shù)據(jù),將調(diào)用ft5x0x_ts_report(ts)函數(shù)上報(bào)數(shù)據(jù),這是輸入子系統(tǒng)相關(guān)內(nèi)容,下面會(huì)介紹到。

??? ? 最后,中斷處理完成,可以打開(kāi)中斷了,即調(diào)用enable_irq(this_client->irq);使能中斷。


3.? 剩下的參數(shù)


????? 第三個(gè)參數(shù)是中斷觸發(fā)方式,即上升延觸發(fā)還是電平觸發(fā)等;第4個(gè)參數(shù)是名字;第5個(gè)參數(shù)是中斷發(fā)生的次數(shù)的記錄。


????? 最后,再說(shuō)下ft5x0x_ts_probe函數(shù)中,ft5x0x_i2c_platform_data

????? 因此,lInux內(nèi)核源碼也不是那么神秘。另外吐槽一下國(guó)產(chǎn)手機(jī),為什么屏幕不能觸摸了后非要黑屏一下,然后在上電就能用了,那樣非要用戶按一下,如果能在程序中自動(dòng)做多好,那樣給用戶的感覺(jué)就是觸摸屏不會(huì)出現(xiàn)不能使用的情況,ip手機(jī)貌似觸摸屏就沒(méi)死過(guò),里面肯定做了恢復(fù)處理的。


4.? 屏卡死問(wèn)題解決思路

????? 不管觸摸屏與主控制器的I2C連接是否死鎖,當(dāng)有手指按下后,觸摸屏MCU里面的程序還是會(huì)響應(yīng)一個(gè)中斷,即會(huì)把中斷引腳拉低,這里對(duì)應(yīng)的中斷引腳為EINT14。主控制器端是注冊(cè)過(guò)中斷的,當(dāng)此中斷引腳拉低就會(huì)觸發(fā)主控制器那邊注冊(cè)的中斷處理函數(shù)。

????? 所以,在觸摸屏斷電、上電過(guò)程肯定在probe函數(shù)中的中斷處理函數(shù)中進(jìn)行。而且是發(fā)生中斷,但是讀寫(xiě)I2C觸摸屏數(shù)據(jù)失敗的情況下關(guān)閉觸摸屏電源,中斷返回時(shí)打開(kāi)觸摸屏電源。

四、設(shè)備驅(qū)動(dòng)層實(shí)現(xiàn)之輸入子系統(tǒng)


????? 關(guān)于Linux下輸入子系統(tǒng)查看文章《Linux輸入子系統(tǒng)(input? subsystem)》。

???? ?在probe函數(shù)中,也實(shí)現(xiàn)了輸入子系統(tǒng)設(shè)備驅(qū)動(dòng)程序,部分代碼如下:

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_MULTITOUCHset_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); #elseset_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); #endifinput_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函數(shù)即上報(bào)事件,在ft5x0x_ts_pen_irq_work函數(shù)中被調(diào)用。


五、probe函數(shù)里面所有源碼


static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { /*ft5x0x_i2c_platform_data結(jié)構(gòu)已經(jīng)在內(nèi)核初始化時(shí)設(shè)置好了,具體查看mach-tiny4412.c中smdk4x12_i2c_devs1結(jié)構(gòu)體數(shù)組中,第一個(gè)i2c_board_info型結(jié)構(gòu)的platform_data成員然后,ft5x0x_ts_data結(jié)構(gòu)成員的初始化就會(huì)根據(jù)ft5x0x_i2c_platform_data結(jié)構(gòu)的的值來(lái)設(shè)置。 */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; //在運(yùn)行時(shí)通過(guò)一個(gè)指針創(chuàng)建一個(gè)工作INIT_WORK(&ts->work, ft5x0x_ts_pen_irq_work);this_client = client;i2c_set_clientdata(client, ts);//創(chuàng)建工作queuets->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_MULTITOUCHset_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); #elseset_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); #endifinput_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;} /*注冊(cè)一個(gè)中斷,當(dāng)用戶操作觸摸屏后,觸摸屏與ARM連接的EINT14就會(huì)被觸發(fā)(對(duì)應(yīng)引腳為GPX1-6),這里就會(huì)調(diào)用ft5x0x_ts_interrupt中斷處理函數(shù),這里的中斷處理函數(shù)相當(dāng)于最初的處理,即頂半部中斷。在此中斷處理函數(shù)中還會(huì)根據(jù)linux內(nèi)核的工作隊(duì)列機(jī)制運(yùn)行另外一個(gè)函數(shù)即上面的ft5x0x_ts_pen_irq_work函數(shù)。這樣做的目的是中斷處理函數(shù)ft5x0x_ts_interrupt里面不能做太多費(fèi)時(shí)操作,就把那些費(fèi)時(shí)操作放在ft5x0x_ts_pen_irq_work函數(shù) 中執(zhí)行。在ft5x0x_ts_pen_irq_work函數(shù)中先調(diào)用ft5x0x_read_data函數(shù)讀數(shù)據(jù),此函數(shù)根據(jù)I2C總線驅(qū)動(dòng)模型,此函數(shù)里面肯定會(huì)調(diào)用核心層里的函數(shù),核心層里的函數(shù)最終會(huì)調(diào)用適配器 層里的函數(shù)操作處理器的I2C控制器實(shí)現(xiàn)I2C觸摸屏數(shù)據(jù)的讀寫(xiě)。在ft5x0x_ts_pen_irq_work函數(shù)中最后會(huì)調(diào)用ft5x0x_ts_report函數(shù)上報(bào)上報(bào)事件,具體上報(bào)過(guò)程就是輸入子系統(tǒng)的事了。*/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_EARLYSUSPENDts->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); #endifenable_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函數(shù)返回一個(gè)類(lèi)型為struct workqueue_struct的指針變量,該指針變量所指向的內(nèi)存地址在函數(shù)內(nèi)部調(diào)用kzalloc動(dòng)態(tài)生成。所以driver在不再使用該work queue的情況下調(diào)用此函數(shù)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; }

六、關(guān)于校準(zhǔn)

????? 可以參見(jiàn)資料:http://blog.csdn.net/liukun321/article/details/24102307

總結(jié)

以上是生活随笔為你收集整理的Linux下触摸屏驱动程序分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。