5.9.1.触摸屏驱动概览
5.9.1.1、常用的2种触摸屏:
(1)电阻触摸屏。
驱动一般分2种:
a.一种是SoC内置触摸屏控制器;//成本低,可扩展性差,精度需要跟随内部AD.
b.一种是外置的专门触摸屏控制芯片,通过I2C接口和SoC通信。//灵活,多加芯片故成本高。
图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总线汇总概览
图162-----------------------------
(1)三根通信线:SCL、SDA、GND
(2)同步、串行、电平、低速、近距离
(3)总线式结构,支持多个设备挂接在同一条总线上
(4)主从式结构,通信双方必须一个为主(master)一个为从(slave),主设备掌握每次通信的主动权,从设备按照主设备的节奏被动响应。每个从设备在总线中有唯一的地址(slave address),主设备通过从地址找到自己要通信的从设备(本质是广播)。
(5)I2C主要用途就是主SoC和外围设备之间的通信,最大优势是可以在总线上扩展多个外围设备的支持。常见的各种物联网传感器芯片(如gsensor、温度、湿度、光强度、酸碱度、烟雾浓度、压力等)均使用I2C接口和主SoC进行连接。
(6)电容触摸屏芯片的多个引脚构成2个接口:
一个接口是I2C的,负责和主SoC连接(本身作为从设备),主SoC通过该接口初始化及控制电容触摸屏芯片、芯片通过该接口向SoC汇报触摸事件的信息(触摸坐标等),我们使用电容触摸屏时重点关注的是这个接口;
另一个接口是电容触摸板的管理接口,电容触摸屏芯片通过该接口来控制触摸板硬件。该接口是电容触摸屏公司关心的,他们的触摸屏芯片内部固件编程要处理这部分,我们使用电容触摸屏的人并不关心这里。
图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设备驱动!分离、分层思想!
//主机的驱动是手段、策略;我们最终目的是驱动从机来工作实现我们自身需求则较重要。
图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驱动源码分析