5.9.1.触摸屏驱动概览
5.9.1.1、常用的2种触摸屏:
(1)电阻触摸屏。
驱动一般分2种:
a.一种是SoC内置触摸屏控制器;//成本低,可扩展性差,精度需要跟随内部AD.
b.一种是外置的专门触摸屏控制芯片,通过I2C接口和SoC通信。//灵活,多加芯片故成本高。

android 触摸驱动移植 触摸屏驱动移植_ci


图161-----------------------------

(2)电容触摸屏。

驱动只有一种,外接专用的电容式触摸屏控制芯片,I2C接口和SoC通信。


5.9.1.2、X210使用的触摸屏//(属I2C接口的电容式触摸屏)

(1)X210V3使用的触摸屏:ft5x06

(2)X210V3S使用的触摸屏:gslX680

5.9.1.3、学习触摸屏驱动的关键点
(1)input子系统相关知识
(2)中断上下半部
(3)I2C子系统
(4)触摸屏芯片本身知识
5.9.1.4、竞争状态和同步

5.9.2_3.内核中的竞争状态和互斥1_2
5.9.2.1、一些概念
(1)竞争状态(简称竟态)
//并发引起的危险性动作
(2)临界段、互斥锁、死锁
//临界段中间有可能被打断,可能出现并发等问题,前后最好加上互斥锁;一般很短。
(3)同步(多CPU、多任务、中断)
//并行执行
5.9.2.2、解决竞态的方法!
(1)原子操作 automic_t
(2)信号量、互斥锁
//某个进程持有信号量后可以休眠等待,常用于时间不定或者较长时间。(意思是把CPU交出来让别的进程使用,即代码执行着突然条件不满足而挂起自己等待在这里。比如:input设备在读取东西时没有东西可读了就休眠等待在这里),可以提高CPU使用率。eg:三个人打篮球想打3小时结果3分钟后一人脚崴了不得不让出场地让别打。
(3)自旋锁
//某进程自旋等待在这里,占用CPU白白消耗着。常用于较短时间。eg:三个人打篮球想打3小时结果3分钟后一人接电话3、5分钟等待后继续打。

5.9.2.3、自旋锁和信号量的使用要点
(1)自旋锁不能递归
//自身进程已经获取了锁还想继续获取该锁,只有自己释放后才能接着获取,不可能啊!因为自己一直在那自旋着(自己把自己锁住了);否则只能一直苦等在那而不能自拔。
(2)自旋锁可以用在中断上下文(信号量不可以,因为可能睡眠),但是在中断上下文中获取自旋锁之前要先禁用本地中断
//中断上下文是不参与进程调度的,故把CPU交出去后就再也回不来了!所以,若中断上下文中程序没有执行完则不能交出CPU。自旋锁抱着CPU,你不给老子老子就不走!
//禁用本地中断!:担心来个中断嵌套把它打断可能会出问题,持有锁的情况下再有人/进程去获取该锁很危险!
(3)自旋锁的核心要求是:拥有自旋锁的代码必须不能睡眠,要一直持有CPU直到释放自旋锁
(4)信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,
而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。
如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,
如果对共享资源的访问时间非常短,自旋锁也可以。
但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。
自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下(以前的情况),自旋锁的所有操作都是空操作。

5.9.4.中断的上下半部1
5.9.4.1、中断处理的注意点
(1)中断上下文,不能和用户空间数据交互
//内核运行环境下的称为:内核状态下的进程上下文、中断上下文
//进程上下文特点:这段代码可以被内核调度的,若时间到了没运行完则交出CPU(被调度)让其他进程运行,运行完了轮到你了会再次给你CPU时间去运行。
//中断上下文特点:处在中断处理程序中,该程序不能被调度的(意味着可以被打断),不参与内核的调度系统(内核链表中没有该函数)。也不能被打断,不能和用户空间数据交互。
(2)不能交出CPU(不能休眠、不能schedule)
//否则会丢失CPU
(3)ISR(中断处理程序)运行时间尽可能短,越长则系统响应特性越差
//时间片极短(人感觉不到的),所有进程雨露均沾!

5.9.4.2、中断下半部2种解决方案:
(1)为什么要分上半部(top half,又叫顶半部)和下半部(bottom half,又叫底半部)
//上半部特点是时间短且紧急,只负责登记中断、做一些和中断标记有关的事情,然后调度/激活下半部;中断中真正紧急的事可放在上半部。
//下半部特点相反,真正的干活!:处理中断程序。eg:按键中断:读取按键值再上报;网卡中断:从网卡缓冲区把对方发来的数据包读出来
//分成上下半部的好处://例如:2s == 10ms + 1990ms
a.保证中断能立马执行,意味着中断响应速度快;
b.上下半部间的时间段可以让出CPU 让CPU做其他更加紧急的事;
兼顾了系统的响应特性和中断处理程序的任务执行。
////实现下半部的思路和机制:////
(2)下半部处理策略1:tasklet(小任务)
(3)下半部处理策略2:workqueue(工作队列)
5.9.4.3、tasklet使用实战
(1)tasklet相关接口介绍
(2)实战演示tasklet实现下半部

//[root@localhost linux-3.5]# vim include/linux/interrupt.h
505 struct tasklet_struct
506 {
507         struct tasklet_struct *next;
508         unsigned long state;
509         atomic_t count;
510         void (*func)(unsigned long);//下半部处理函数
511         unsigned long data;//需要传递的数据
512 };
513 
514 #define DECLARE_TASKLET(name, func, data) \
515 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
516 
517 #define DECLARE_TASKLET_DISABLED(name, func, data) \
518 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }551 static inline void tasklet_schedule(struct tasklet_struct *t) //启用软件中断
552 {
553         if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
554                 __tasklet_schedule(t);
555 }

5.9.5.中断的上下半部2
5.9.5.1、workqueue实战演示
(1)workqueue的突出特点:
下半部会交给worker thead(利用线程实现),因此下半部处于进程上下文,可以被调度,因此可以睡眠。
(2)include/linux/workqueue.h
//为何这么设计:因为中断处理程序中有时不得不处理一些比较耗时的、甚至不得不 处理可能会睡眠的操作;
中断上下文是不能睡眠的、不能交出CPU的,但是没办法!有时候中断中处理的事情就是有可能睡眠,避不开啊!所以就采用workqueue机制来实现下半部。
workqueue下半部是处于进程上下文的!:workqueue下半部是参与操作系统的调度的,本身类似于一个进程/线程。
所以workqueue下半部是可以被切开很多段来分开运行,这样就可以在workqueue下半部来睡眠、调度。好像中断下半部、中断上下文解放了,这样可以在中断处理程序中做各种事情。

//[root@localhost linux-3.5]# vim include/linux/workqueue.h 
 79 struct work_struct {
 80         atomic_long_t data;
 81         struct list_head entry;
 82         work_func_t func;
 83 #ifdef CONFIG_LOCKDEP
 84         struct lockdep_map lockdep_map;
 85 #endif
 86 };
 87 
 88 #define WORK_DATA_INIT()        ATOMIC_LONG_INIT(WORK_STRUCT_NO_CPU)
 89 #define WORK_DATA_STATIC_INIT() \
 90         ATOMIC_LONG_INIT(WORK_STRUCT_NO_CPU | WORK_STRUCT_STATIC)135 #define DECLARE_WORK(n, f)                                      \
136         struct work_struct n = __WORK_INITIALIZER(n, f)

380 extern int schedule_work(struct work_struct *work);

5.9.5.2、中断上下半部处理原则:
(1)必须立即进行紧急处理的极少量任务放入在中断的顶半部中,此时屏蔽了与自己同类型的中断,由于任务量少,所以可以迅速不受打扰地处理完紧急任务。
(2)需要较少时间的中等数量的急迫任务放在tasklet中。此时不会屏蔽任何中断(包括与自己的顶半部同类型的中断),所以不影响顶半部对紧急事务的处理;同时又不会进行用户进程调度,从而保证了自己急迫任务得以迅速完成。
//下半部何时执行由操作系统决定,其有一套机制会去衡量,衡量的标准就是当前操作系统的负载量,忙与不忙!
当上半部执行schedule后当前CPU没有任务去执行、也没有中断来、也没有更紧急的任务来,即操作系统轻载情况下会立马调用下半部函数;
当有更紧急的事件来了或者同类型的中断来了操作系统就会认为有更加紧急的任务、优先级更高的要去执行,所以下半部的函数将推后被调度执行。这些都是tasklet机制背后实现好的。
tasklet机制属于折中方案。
(3)需要较多时间且并不急迫(允许被操作系统剥夺运行权)的大量任务放在workqueue中。此时操作系统会尽量快速处理完这个任务,但如果任务量太大,期间操作系统也会有机会调度别的用户进程运行,从而保证不会因为这个任务需要运行时间将其它用户进程无法进行。
(4)可能引起睡眠的任务放在workqueue中。因为在workqueue中睡眠是安全的。在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,用workqueue很合适。

 

5.9.6_7.linux内核的I2C子系统详解1_2
5.9.6.1、I2C总线汇总概览

android 触摸驱动移植 触摸屏驱动移植_自旋锁_02


图162-----------------------------

(1)三根通信线:SCL、SDA、GND

(2)同步、串行、电平、低速、近距离

(3)总线式结构,支持多个设备挂接在同一条总线上

(4)主从式结构,通信双方必须一个为主(master)一个为从(slave),主设备掌握每次通信的主动权,从设备按照主设备的节奏被动响应。每个从设备在总线中有唯一的地址(slave address),主设备通过从地址找到自己要通信的从设备(本质是广播)。

(5)I2C主要用途就是主SoC和外围设备之间的通信,最大优势是可以在总线上扩展多个外围设备的支持。常见的各种物联网传感器芯片(如gsensor、温度、湿度、光强度、酸碱度、烟雾浓度、压力等)均使用I2C接口和主SoC进行连接。

(6)电容触摸屏芯片的多个引脚构成2个接口:

一个接口是I2C的,负责和主SoC连接(本身作为从设备),主SoC通过该接口初始化及控制电容触摸屏芯片、芯片通过该接口向SoC汇报触摸事件的信息(触摸坐标等),我们使用电容触摸屏时重点关注的是这个接口;

另一个接口是电容触摸板的管理接口,电容触摸屏芯片通过该接口来控制触摸板硬件。该接口是电容触摸屏公司关心的,他们的触摸屏芯片内部固件编程要处理这部分,我们使用电容触摸屏的人并不关心这里。

android 触摸驱动移植 触摸屏驱动移植_自旋锁_03


图163-----------------------------

5.9.6.2、linux内核的I2C驱动框架总览
(1)I2C驱动框架的主要目标是:
让驱动开发者可以在内核中方便的添加自己的I2C设备的驱动程序,从而可以更容易的在linux下驱动自己的I2C接口硬件
(2)源码中I2C相关的驱动均位于:drivers/i2c目录下。
linux系统提供2种I2C驱动实现方法:
a.第一种叫i2c-dev,对应drivers/i2c/i2c-dev.c,这种方法只是封装了主机(I2C master,一般是SoC中内置的I2C控制器)的I2C基本操作,并且向应用层提供相应的操作接口,应用层代码需要自己去实现对slave的控制和操作,所以这种I2C驱动相当于只是提供给应用层可以访问slave硬件设备的接口,本身并未对硬件做任何操作,应用需要实现对硬件的操作,因此写应用的人必须对硬件非常了解,其实相当于传统的驱动中干的活儿丢给应用去做了,所以这种I2C驱动又叫做“应用层驱动”,这种方式并不主流,它的优势是把差异化都放在应用中,这样在设备比较难缠(尤其是slave是非标准I2C时)时不用动驱动,而只需要修改应用就可以实现对各种设备的驱动。这种驱动在驱动层很简单(就是i2c-dev.c)我们就不分析了。
b.第二种I2C驱动是所有的代码都放在驱动层实现,直接向应用层提供最终结果。应用层甚至不需要知道这里面有I2C存在,譬如电容式触摸屏驱动,直接向应用层提供/dev/input/event1的操作接口,应用层编程的人根本不知道event1中涉及到了I2C。这种是我们后续分析的重点。

//
[root@localhost linux-3.5]# ls drivers/i2c/  !!!
algos       i2c-boardinfo.c!!!  i2c-core.h  i2c-dev.o    Kconfig          modules.order
built-in.o  i2c-boardinfo.o  i2c-core.o  i2c-mux.c    Makefile         muxes
busses      i2c-core.c       i2c-dev.c   i2c-smbus.c  modules.builtin
[root@localhost linux-3.5]# ls drivers/i2c/busses/
built-in.o                i2c-diolan-u2c.c  i2c-nforce2.c        i2c-puv3.c       i2c-stub.c
i2c-acorn.c               i2c-eg20t.c       i2c-nforce2-s4985.c  i2c-pxa.c        i2c-taos-evm.c
i2c-ali1535.c             i2c-elektor.c     i2c-nomadik.c        i2c-pxa-pci.c    i2c-tegra.c
i2c-ali1563.c             i2c-gpio.c        i2c-nuc900.c         i2c-s3c2410.c    i2c-tiny-usb.c
i2c-ali15x3.c             i2c-highlander.c  i2c-ocores.c         i2c-s3c2410.o    i2c-versatile.c
i2c-amd756.c              i2c-hydra.c       i2c-octeon.c         i2c-s6000.c      i2c-via.c
i2c-amd756-s4882.c        i2c-i801.c        i2c-omap.c           i2c-s6000.h      i2c-viapro.c
i2c-amd8111.c             i2c-ibm_iic.c     i2c-parport.c        i2c-scmi.c       i2c-xiic.c
i2c-at91.c                i2c-ibm_iic.h     i2c-parport.h        i2c-sh7760.c     i2c-xlr.c
i2c-au1550.c              i2c-imx.c         i2c-parport-light.c  i2c-sh_mobile.c  Kconfig
i2c-bfin-twi.c            i2c-intel-mid.c   i2c-pasemi.c         i2c-sibyte.c     Makefile
i2c-cpm.c                 i2c-iop3xx.c      i2c-pca-isa.c        i2c-simtec.c     modules.builtin
i2c-davinci.c             i2c-iop3xx.h      i2c-pca-platform.c   i2c-sirf.c       modules.order
i2c-designware-core.c     i2c-isch.c        i2c-piix4.c          i2c-sis5595.c    scx200_acb.c
i2c-designware-core.h     i2c-mpc.c         i2c-pmcmsp.c         i2c-sis630.c     scx200_i2c.c
i2c-designware-pcidrv.c   i2c-mv64xxx.c     i2c-pnx.c            i2c-sis96x.c
i2c-designware-platdrv.c  i2c-mxs.c         i2c-powermac.c       i2c-stu300.c

5.9.8.linux内核的I2C子系统详解3
5.9.8.1、I2C子系统的4个关键结构体:
(1)struct i2c_adapter I2C适配器

//这段驱动代码和i2c控制器有关,和210芯片有关(换芯片则这段代码也要变)
//[root@localhost linux-3.5]# vim include/linux/i2c.h
370 /*
371  * i2c_adapter is the structure used to identify a physical i2c bus along
372  * with the access algorithms necessary to access it.
373  */
374 struct i2c_adapter {
375         struct module *owner;
376         unsigned int class;               /* classes to allow probing for */
377         const struct i2c_algorithm *algo; /* the algorithm to access the bus */ //
378         void *algo_data;
379 
380         /* data fields that are valid for all devices   */
381         struct rt_mutex bus_lock;
382 
383         int timeout;                    /* in jiffies */
384         int retries;
385         struct device dev;              /* the adapter device */
386 
387         int nr;
388         char name[48];
389         struct completion dev_released;
390 
391         struct mutex userspace_clients_lock;
392         struct list_head userspace_clients;
393 };
394 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)

(2)struct i2c_algorithm I2C算法
//描述主机i2c设备和从机i2c设备之间通信的算法(时序),该结构体包含在i2c_adapter中,由适配器来执行;即如何通过i2c总线和i2c设备进行通信.
//主机i2c控制器和所有i2c设备都可通信,标准的i2c设备(例如触摸屏)与非标准(例如烟雾报警器)区别就是时序不同,反映在struct i2c_algorithm,主机控制器这边的寄存器、初始化和设置方法是一样的;
主机控制器如何按照一定的时序来和不同i2c从机设备通信是不一样的。

(3)struct i2c_client I2C(从机)设备信息 //彼此配对
(4)struct i2c_driver I2C(从机)设备驱动 //彼此配对
//实现思路和platform总线一致! 大胆想象USB设备驱动!分离、分层思想!
//主机的驱动是手段、策略;我们最终目的是驱动从机来工作实现我们自身需求则较重要。

android 触摸驱动移植 触摸屏驱动移植_android 触摸驱动移植_04


图181--------------------------

5.9.8.2、关键文件
(1)i2c-core.c !!!
//内核开发者实现提供,纯软件,与具体硬件操作无关,属于i2c子系统的核心。为其他各部分提供接口。
(2)busses目录
//放置了大部分都是i2c_adapter,譬如i2c-at91.c 是at91芯片内置i2c控制器对应的adapter相关操作;
重点看:!!!i2c-s3c2410.c (和210一样,没变)
(3)algos目录

//自己实现的一些算法;定制化的i2c算法
[root@localhost linux-3.5]# ls drivers/i2c/algos/
built-in.o      i2c-algo-pca.c  i2c-algo-pcf.h  Makefile         modules.order
i2c-algo-bit.c  i2c-algo-pcf.c  Kconfig         modules.builtin
//!!!vim arch/arm/mach-s5pv210/Mach-x210.c (九鼎自己实现好的!)

5.9.9.linux内核的I2C子系统详解4
5.9.9.1、i2c-core.c初步分析
(1)smbus代码略过
//基于i2c总线设计的一种机制,与我们现在所涉及的无关。
(2)模块加载和卸载:bus_register(&i2c_bus_type);

//
1302 
1303 /* We must initialize early, because some subsystems register i2c drivers
1304  * in subsys_initcall() code, but are linked (and initialized) before i2c.
1305  */
1306 postcore_initcall(i2c_init);
1307 module_exit(i2c_exit);1266 static int __init i2c_init(void)//
1267 {
1268         int retval;
1269 
1270         retval = bus_register(&i2c_bus_type);//注册i2c总线
1271         if (retval)
1272                 return retval;
1273 #ifdef CONFIG_I2C_COMPAT
1274         i2c_adapter_compat_class = class_compat_register("i2c-adapter");
1275         if (!i2c_adapter_compat_class) {
1276                 retval = -ENOMEM;
1277                 goto bus_err;
1278         }
1279 #endif
1280         retval = i2c_add_driver(&dummy_driver);//添加了一个空驱动
1281         if (retval)
1282                 goto class_err;
1283         return 0;
1284 
1285 class_err:
1286 #ifdef CONFIG_I2C_COMPAT
1287         class_compat_unregister(i2c_adapter_compat_class);
1288 bus_err:
1289 #endif
1290         bus_unregister(&i2c_bus_type);
1291         return retval;
1292 }316 struct bus_type i2c_bus_type = { //i2c的各种细节
 317         .name           = "i2c",
 318         .match          = i2c_device_match,//匹配函数;一般用名字去匹配
 319         .probe          = i2c_device_probe,//
 320         .remove         = i2c_device_remove,
 321         .shutdown       = i2c_device_shutdown,
 322         .pm             = &i2c_device_pm_ops,
 323 };[root@localhost linux-3.5]# ls /sys/bus/i2c/
devices  drivers  drivers_autoprobe  drivers_probe  uevent
[root@localhost linux-3.5]# ls /sys/bus/i2c/drivers
dummy
[root@localhost linux-3.5]# ls /sys/bus/i2c/drivers/dummy/
bind  module  uevent  unbind

//另:每种总线都包含两个串:device和driver;
若注册了新的device则须和设备链的每个driver挨个去匹配,匹配成功则执行probe函数;匹配失败则告无驱动;
同理,若注册了新的driver则须和每个device去匹配。

107 static int i2c_device_probe(struct device *dev) //i2c总线的匹配函数;i2c总线决定执行与否。
 108 {
 109         struct i2c_client       *client = i2c_verify_client(dev);
 110         struct i2c_driver       *driver;
 111         int status;
 112 
 113         if (!client)
 114                 return 0;
 115 
 116         driver = to_i2c_driver(dev->driver);//做一些数据结构的绑定
 117         if (!driver->probe || !driver->id_table)
 118                 return -ENODEV;
 119         client->driver = driver;
 120         if (!device_can_wakeup(&client->dev))//做一些安全信息的校验
 121                 device_init_wakeup(&client->dev,
 122                                         client->flags & I2C_CLIENT_WAKE);
 123         dev_dbg(dev, "probe\n");
 124 
 125         status = driver->probe(client, i2c_match_id(driver->id_table, client));
			//关键;间接调用了driver中的probe函数
 126         if (status) {
 127                 client->driver = NULL;
 128                 i2c_set_clientdata(client, NULL);
 129         }
 130         return status;
 131 }  69 static int i2c_device_match(struct device *dev, struct device_driver *drv)//
  70 {
  71         struct i2c_client       *client = i2c_verify_client(dev);
  72         struct i2c_driver       *driver;
  73 
  74         if (!client)
  75                 return 0;
  76 
  77         /* Attempt an OF style match */
  78         if (of_driver_match_device(dev, drv))
  79                 return 1;
  80 
  81         driver = to_i2c_driver(drv);
  82         /* match on an id table if there is one */
  83         if (driver->id_table)
			 //关键;
  84                 return i2c_match_id(driver->id_table, client) != NULL;//真正匹配!!!
  85 
  86         return 0;
  87 }[root@localhost linux-3.5]# vim include/linux/i2c.h 
161 struct i2c_driver {
162         unsigned int class;
163 
164         /* Notifies the driver that a new bus has appeared or is about to be
165          * removed. You should avoid using this, it will be removed in a
166          * near future.
167          */
168         int (*attach_adapter)(struct i2c_adapter *) __deprecated;
169         int (*detach_adapter)(struct i2c_adapter *) __deprecated;
170 
171         /* Standard driver model interfaces */
172         int (*probe)(struct i2c_client *, const struct i2c_device_id *);
173         int (*remove)(struct i2c_client *);
174 
175         /* driver model interfaces that don't relate to enumeration  */
176         void (*shutdown)(struct i2c_client *);
177         int (*suspend)(struct i2c_client *, pm_message_t mesg);
178         int (*resume)(struct i2c_client *);
179 
180         /* Alert callback, for example for the SMBus alert protocol.
181          * The format and meaning of the data value depends on the protocol.
182          * For the SMBus alert protocol, there is a single bit of data passed
183          * as the alert response's low bit ("event flag").
184          */
185         void (*alert)(struct i2c_client *, unsigned int data);
186 
187         /* a ioctl like command that can be used to perform specific functions
188          * with the device.
189          */
190         int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
191 
192         struct device_driver driver;
193         const struct i2c_device_id *id_table; 
			//用于匹配设备的驱动名字/id号,有好多的id号。
194 
195         /* Device detection callback for automatic device creation */
196         int (*detect)(struct i2c_client *, struct i2c_board_info *);
197         const unsigned short *address_list;
198         struct list_head clients;
199 };
200 #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)428 /* i2c */
429 
430 #define I2C_NAME_SIZE   20
431 #define I2C_MODULE_PREFIX "i2c:"
432 
433 struct i2c_device_id {  //存的是ID,识别码
434         char name[I2C_NAME_SIZE];
435         kernel_ulong_t driver_data      /* Data private to the driver */
436                         __attribute__((aligned(sizeof(kernel_ulong_t))));//对齐操作
437 };//按照名字去匹配:
  58 static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
  59                                                 const struct i2c_client *client)
  60 {
  61         while (id->name[0]) {
  62                 if (strcmp(client->name, id->name) == 0) 
			//eg:ft5206;同系列的芯片公用一个驱动很常见;一个驱动可以用于多个设备。
  63                         return id;
  64                 id++;
  65         }
  66         return NULL;
  67 }      220 struct i2c_client {
221         unsigned short flags;           /* div., see below              */
222         unsigned short addr;            /* chip address - NOTE: 7bit    */
223                                         /* addresses are stored in the  */
224                                         /* _LOWER_ 7 bits               */
225         char name[I2C_NAME_SIZE];
			//用于匹配驱动的设备名字
226         struct i2c_adapter *adapter;    /* the adapter we sit on        */
227         struct i2c_driver *driver;      /* and our access routines      */
228         struct device dev;              /* the device structure         */
229         int irq;                        /* irq issued by device         */
230         struct list_head detected;
231 };
232 #define to_i2c_client(d) container_of(d, struct i2c_client, dev)



5.9.9.2、I2C总线的匹配机制
(1)match函数
(2)probe函数
总结:
I2C总线上有2条分支:i2c_client链和i2c_driver链,当任何一个driver或者client去注册时,I2C总线都会调用match函数去对client.name和driver.id_table.name进行循环匹配。
如果driver.id_table中所有的id都匹配不上则说明client并没有找到一个对应的driver,没了;
如果匹配上了则标明client和driver是适用的,那么I2C总线会调用自身的probe函数,自身的probe函数又会调用driver中提供的probe函数,driver中的probe函数会对设备进行硬件初始化和后续工作。

5.9.9.3、核心层开放给其他部分的注册接口
(1)i2c_add_adapter/i2c_add_numbered_adapter 注册adapter的
(2)i2c_add_driver 注册driver的
(3)i2c_new_device 注册client的

//[root@localhost linux-3.5]# vim drivers/i2c/busses/i2c-s3c2410.c
1025 static int s3c24xx_i2c_probe(struct platform_device *pdev)//eg:
1026 {
	ret = i2c_add_numbered_adapter(&i2c->adap);//
1166         if (ret < 0) {
1167                 dev_err(&pdev->dev, "failed to add bus to i2c core\n");
1168                 goto err_cpufreq; 961 int i2c_add_numbered_adapter(struct i2c_adapter *adap)//i2c-core.c
 962 {
 963         int     id;
 964         int     status;
 965 
 966         if (adap->nr == -1) /* -1 means dynamically assign bus id */
 967                 return i2c_add_adapter(adap);
 968         if (adap->nr & ~MAX_IDR_MASK)
 969                 return -EINVAL;
 970 
 971 retry:
 972         if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
 973                 return -ENOMEM;
 974 
 975         mutex_lock(&core_lock);
 976         /* "above" here means "above or equal to", sigh;
 977          * we need the "equal to" result to force the result
 978          */
 979         status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
 980         if (status == 0 && id != adap->nr) {
 981                 status = -EBUSY;
 982                 idr_remove(&i2c_adapter_idr, id);
 983         }
 984         mutex_unlock(&core_lock);
 985         if (status == -EAGAIN)
 986                 goto retry;
 987 
 988         if (status == 0)
 989                 status = i2c_register_adapter(adap);
 990         return status;
 991 }

5.9.10.linux内核的I2C子系统详解5
5.9.10.1、adapter模块的注册
(1)平台总线方式注册
(2)找到driver和device,并且确认其配对过程
(3)probe函数

//[root@localhost linux-3.5]# vim drivers/i2c/busses/i2c-s3c2410.c
1272 /* device driver for platform bus bits */
1273 
1274 static struct platform_driver s3c24xx_i2c_driver = {
1275         .probe          = s3c24xx_i2c_probe,//
1276         .remove         = s3c24xx_i2c_remove,
1277         .id_table       = s3c24xx_driver_ids,//若定义了id_table就优先匹配它,匹配上了就不管下面了;匹配不上或者没有(NULL)再考虑driver里的name
1278         .driver         = {
1279                 .owner  = THIS_MODULE,
1280                 .name   = "s3c-i2c",//
1281                 .pm     = S3C24XX_DEV_PM_OPS,
1282                 .of_match_table = of_match_ptr(s3c24xx_i2c_match),
1283         },
1284 };


-----------------------------------------------------------------------

660 static int platform_match(struct device *dev, struct device_driver *drv)
 661 {
 662         struct platform_device *pdev = to_platform_device(dev);
 663         struct platform_driver *pdrv = to_platform_driver(drv);
 664 
 665         /* Attempt an OF style match first */
 666         if (of_driver_match_device(dev, drv))//
 667                 return 1;
 668 
 669         /* Then try to match against the id table */
 670         if (pdrv->id_table)
 671                 return platform_match_id(pdrv->id_table, pdev) != NULL;
 672 
 673         /* fall-back to driver name match */
 674         return (strcmp(pdev->name, drv->name) == 0);
 675 }
-----------------------------------------------------------------------------
130 static struct platform_device_id s3c24xx_driver_ids[] = {
 131         {
 132                 .name           = "s3c2410-i2c",//
 133                 .driver_data    = 0,
 134         }, {
 135                 .name           = "s3c2440-i2c", 
 136                 .driver_data    = QUIRK_S3C2440,
 137         }, {
 138                 .name           = "s3c2440-hdmiphy-i2c",
 139                 .driver_data    = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
 140         }, { },
 141 };//[root@localhost linux-3.5]# vim arch/arm/mach-exynos/mach-tiny4412.c
1817 static struct platform_device *smdk4x12_devices[] __initdata = {
1818 #ifdef CONFIG_EXYNOS4_DEV_DWMCI
1819         &exynos_device_dwmci,
1820 #endif
1821         &s3c_device_hsmmc2,
1822         &s3c_device_hsmmc3,
1823         &wm8994_fixed_voltage0,
1824         &wm8994_fixed_voltage1,
1825         &wm8994_fixed_voltage2,
1826         &s3c_device_i2c0,//
1827         &s3c_device_i2c1,
1828         &s3c_device_i2c2,
1829         &s3c_device_i2c3,
1830 #ifdef CONFIG_VIDEO_M5MOLS
1831         &s3c_device_i2c4,
1832 #endif
1833         &s3c_device_i2c7,
1834         &s3c_device_adc,
1835         &s3c_device_rtc,
1836         &s3c_device_wdt,
1837 #ifdef CONFIG_TINY4412_BUZZER
1838         &s3c_device_timer[0],
1839 #endif 577 /* I2C */
 578 
 579 static struct resource s3c_i2c0_resource[] = {
 580         [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),
 581         [1] = DEFINE_RES_IRQ(IRQ_IIC),
 582 };
 583 
 584 struct platform_device s3c_device_i2c0 = { //
 585         .name           = "s3c2410-i2c",//追到名字了!可以匹配!
 586 #ifdef CONFIG_S3C_DEV_I2C1
 587         .id             = 0,
 588 #else
 589         .id             = -1,
 590 #endif
 591         .num_resources  = ARRAY_SIZE(s3c_i2c0_resource),
 592         .resource       = s3c_i2c0_resource,
 593 };5.9.10.2、probe函数分析
(1)填充一个i2c_adapter结构体,并且调用接口去注册之
(2)从platform_device接收硬件信息,做必要的处理(request_mem_region & ioremap、request_irq等)
(3)对硬件做初始化(直接操作210内部I2C控制器的寄存器)
5.9.10.3、i2c_algorithm
(1)i2c->adap.algo    = &s3c24xx_i2c_algorithm;
(2)functionality
(3)s3c24xx_i2c_doxfer5.9.11_12.linux内核的I2C子系统详解6_7
5.9.11.1、i2c_driver的注册
(1)以gslX680的驱动为例
(2)将驱动添加到内核SI项目中
(3)i2c_driver的基本分析:name和probe
5.9.11.2、i2c_client从哪里来
(1)直接来源:i2c_register_board_info
smdkc110_machine_init
	i2c_register_board_info

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];			// 设备名
	unsigned short	flags;						// 属性
	unsigned short	addr;						// 设备从地址
	void		*platform_data;					// 设备私有数据
	struct dev_archdata	*archdata;
#ifdef CONFIG_OF
	struct device_node *of_node;
#endif
	int		irq;								// 设备使用的IRQ号,对应CPU的EINT
};

(2)实现原理分析
内核维护一个链表 __i2c_board_list,这个链表上链接的是I2C总线上挂接的所有硬件设备的信息结构体。也就是说这个链表维护的是一个struct i2c_board_info结构体链表。
真正的需要的struct i2c_client在别的地方由__i2c_board_list链表中的各个节点内容来另外构建生成。

函数调用层次:

i2c_add_adapter/i2c_add_numbered_adapter
	i2c_register_adapter
		i2c_scan_static_board_info
			i2c_new_device
				device_register

总结:I2C总线的i2c_client的提供是内核通过i2c_add_adapter/i2c_add_numbered_adapter接口调用时自动生成的,生成的原料是mach-x210.c中的i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

5.9.13.gslX680驱动的移植实践
5.9.13.1、初步移植实验
(1)源码获取
(2)源码加入内核中
(3)mach文件中添加board_info
(4)编译后内核去启动
5.9.13.2、在内核配置中添加CONFIG项
(1)定义一个宏名,譬如CONFIG_TOUCHSCREEN_GSLX680
(2)在代码中使用宏来条件编译
(3)在Makefile中使用宏来条件配置
(4)在Kconfig项目中添加宏的配置项
(5)make menuconfig并选择Y或者N

5.9.14_15.gslX680驱动源码分析1_2

5.9.16.老版本触摸屏的驱动
5.9.16.1、ft5x06驱动移植实践
5.9.16.2、ft5x06驱动源码分析