五.linux设备驱动模型
5.5.1.linux设备驱动模型简介
5.5.1.1、什么是设备驱动模型
(1)类class、总线bus、设备device、驱动driver
//上述四个可用内核抽象出来的4个结构体表示,可具体实例化它
//热插拔(插上后驱动自动安装,拔掉后驱动自动卸载)
(2)kobject和对象生命周期
//kobject是高度抽象的结构体,表示内核里面的一个对象,所有对象抽象出来的一个总类/基类
//对象的存活时间,何时诞生何时消亡!有自我管理的能力,即合适的时间自生自灭。
(3)sysfs
//虚拟文件系统,在用户空间和内核空间建立一种映射关系。内核一些结构体变量的值可用sysfs以文件的形式展现出来,可在用户空间通过sysfs文件系统操作,读取和修改。
(4)udev
//实现用户空间和内核空间信息同步和转换,可让用户空间及时知晓内核空间发生的变化(譬如哪个设备驱动被加载了、被卸载了等)
5.5.1.2、为什么需要设备驱动模型
(1)早期内核(2.4之前)没有统一的设备驱动模型,但照样可以用
(2)2.6版本中正式引入设备驱动模型,目的是在设备越来越多,功耗要求等新特性要求的情况下让驱动体系更易用、更优秀。
(3)设备驱动模型负责统一实现和维护一些特性,诸如:电源管理、热插拔、对象生命周期、用户空间和驱动空间的交互等基础设施
(4)设备驱动模型目的是简化驱动程序编写,但是客观上设备驱动模型本身设计和实现很复杂。
//例如:开车、造车,我们目的在于开好 车。
5.5.1.3、驱动开发的2个点
(1)驱动源码本身编写、调试。重点在于对硬件的了解。
//比如:我在工作手机制造中维护的是触摸屏这块:触摸屏(需要研究触摸屏需要哪些寄存器,怎么去调节灵敏度,防止雨水啊啥的!) //原厂驱动工程师写好的
(2)驱动什么时候被安装、驱动中的函数什么时候被调用。跟硬件无关,完全和设备驱动模型有关。//内核的人已写好
//重点:我们的工作量是把这两块有机的结合起来,将需要的驱动移植添加到内核里面来,需要懂很多。移植驱动犹如开飞机,主要学习框架,移植后有错误去如何判断。(1)(2)还是应用层代码出现问题!然后花时间去解决问题!
5.5.2.设备驱动模型的底层架构
5.5.2.1、kobject
//驱动模型里面的总纲,该结构体是最原始的、最基本的,其他都是基于其构建的。犹如面向对象里面的基类。作为其他结构体的成员存在。
(1)定义在linux/kobject.h中
//[root@localhost linux-3.5]# vim include/linux/kobject.h
60 struct kobject {
61 const char *name;
62 struct list_head entry;//维护对象链表
63 struct kobject *parent;//进行众多kobject上下层挂接;双向链表
64 struct kset *kset;//里面有加锁功能;操作时不能别人使用。//绑定
65 struct kobj_type *ktype;//对用户空间的表示
66 struct sysfs_dirent *sd;
67 struct kref kref;//对象引用计数
68 unsigned int state_initialized:1;
69 unsigned int state_in_sysfs:1;
70 unsigned int state_add_uevent_sent:1;
71 unsigned int state_remove_uevent_sent:1;
72 unsigned int uevent_suppress:1;
73 };
(2)各种对象最基本单元,提供一些公用型服务如:对象引用计数、维护对象链表、对象上锁、对用户空间的表示
(3)设备驱动模型中的各种对象其内部都会包含一个kobject
(4)地位相当于面向对象体系架构中的总基类
5.5.2.2、kobj_type
//
108 struct kobj_type {
109 void (*release)(struct kobject *kobj);//检测引用计数,若为0则释放空间;若不为0则引用计数减1;close可反复打开
110 const struct sysfs_ops *sysfs_ops;//sysfs文件下操作方法;包含执行函数; echo/cat :show/store函数指针
111 struct attribute **default_attrs;//sysfs文件下的属性,例如LED亮度等
112 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
113 const void *(*namespace)(struct kobject *kobj);
114 };
(1)很多书中简称为ktype,每一个kobject都需要绑定一个ktype来提供相应功能
//不一定包含;可通过指针
(2)关键点1:sysfs_ops,提供该对象在sysfs中的操作方法(show和store)
(2)关键点2:attribute,提供在sysfs中以文件形式存在的属性,其实就是应用接口
5.5.2.3、kset
//
159 struct kset {
160 struct list_head list;
161 spinlock_t list_lock;
162 struct kobject kobj;//注意包含的绑定的含义!
163 const struct kset_uevent_ops *uevent_ops;
164 };
//kset是kobject直接子类
(1)kset的主要作用是做顶层kobject的容器类
(2)kset的主要目的是将各个kobject(代表着各个对象)组织出目录层次架构
(3)可以认为kset就是为了在sysfs中弄出目录,从而让设备驱动模型中的多个对象能够有层次有逻辑性的组织在一起
//总结:以上3个底层架构都是为了实现sysfs目录里面的各种玩意。层次架构!知道怎么回事就好,不必深究其原理。
5.5.3.总线式设备驱动组织方式
5.5.3.1、总线
(1)物理上的真实总线及其作用(英文bus)//总线式结构

图81----------------------------------
(2)驱动框架中的总线式设计
(3)bus_type结构体,关键是match函数和uevent函数
//该结构体是总线模板;eg:一类总线struct ac97_bus_type
//[root@localhost linux-3.5]# vim include/linux/device.h
90 struct bus_type {
91 const char *name;//总线名字;在sysfs文件中可以看见的映像口
92 const char *dev_name;
93 struct device *dev_root;
94 struct bus_attribute *bus_attrs;//总线自己本身的属性drivers_autoprobe drivers_probe uevent
95 struct device_attribute *dev_attrs;
96 struct driver_attribute *drv_attrs;
97 //总线所具备的功能:
98 int (*match)(struct device *dev, struct device_driver *drv);
//做总线下设备和驱动的匹配devices drivers
99 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
100 int (*probe)(struct device *dev);
101 int (*remove)(struct device *dev);
102 void (*shutdown)(struct device *dev);
103
104 int (*suspend)(struct device *dev, pm_message_t state);
105 int (*resume)(struct device *dev);
106
107 const struct dev_pm_ops *pm;//电源管理函数集合
108
109 struct iommu_ops *iommu_ops;
110
111 struct subsys_private *p;
112 };
[root@localhost linux-3.5]# ls /sys/
block bus class dev devices firmware fs hypervisor kernel module power
[root@localhost linux-3.5]# ls /sys/bus/
ac97 event_source i2c mdio_bus pci pcmcia pnp serio
acpi hid isa mipi-dsi pci_express platform scsi usb
[root@localhost linux-3.5]# ls /sys/bus/ac97/
devices drivers drivers_autoprobe drivers_probe uevent
[root@localhost linux-3.5]# ls /sys/bus/ac97/devices/
0-0:CS4297A
[root@localhost linux-3.5]# ls /sys/bus/ac97/drivers
5.5.3.2、设备
(1)struct device是硬件设备在内核驱动框架中的抽象
//像基类一样,有共用的属性
struct device {
634 struct device *parent;
635
636 struct device_private *p;
637
638 struct kobject kobj;
639 const char *init_name; /* initial name of the device */
640 const struct device_type *type;
641
642 struct mutex mutex; /* mutex to synchronize calls to
643 * its driver.
644 */
645
646 struct bus_type *bus; /* type of bus device is on */
647 struct device_driver *driver; /* which driver has allocated this
648 device */
649 void *platform_data; /* Platform specific data, device
650 core doesn't touch it */
651 struct dev_pm_info power;
652 struct dev_pm_domain *pm_domain;
......
664 struct device_dma_parameters *dma_parms;
665
666 struct list_head dma_pools; /* dma pools (if dma'ble) */
667
668 struct dma_coherent_mem *dma_mem; /* internal for coherent mem
669 override */
670 #ifdef CONFIG_CMA
671 struct cma *cma_area; /* contiguous memory area for dma
672 allocations */
673 #endif
674 /* arch specific additions */
675 struct dev_archdata archdata;
676
677 struct device_node *of_node; /* associated device tree node */
678
679 dev_t devt; /* dev_t, creates the sysfs "dev" */
680 u32 id; /* device instance */
681
682 spinlock_t devres_lock;
683 struct list_head devres_head;
684
685 struct klist_node knode_class;
686 struct class *class;
687 const struct attribute_group **groups; /* optional groups */
688
689 void (*release)(struct device *dev);
690 };
(2)device_register用于向内核驱动框架注册一个设备
(3)通常device不会单独使用,而是像自子类一样,被包含在一个具体设备结构体中,如struct usb_device //包含USB设备特有的属性
//[root@localhost linux-3.5]# vim include/linux/usb.h
488 struct usb_device {
489 int devnum;
490 char devpath[16];
491 u32 route;
492 enum usb_device_state state;
493 enum usb_device_speed speed;
494
495 struct usb_tt *tt;
496 int ttport;
497
498 unsigned int toggle[2];
499
500 struct usb_device *parent;
501 struct usb_bus *bus;
502 struct usb_host_endpoint ep0;
503
504 struct device dev;//包含这个共用的“基类”
505
506 struct usb_device_descriptor descriptor;
507 struct usb_host_bos *bos;
508 struct usb_host_config *config;
5.5.3.3、驱动
(1)struct device_driver是驱动程序在内核驱动框架中的抽象
(2)关键元素1:name,驱动程序的名字,很重要,经常被用来作为驱动和设备的匹配依据
(3)关键元素2:probe,驱动程序的探测函数,用来检测一个设备是否可以被该驱动所管理
//[root@localhost linux-3.5]# vim include/linux/device.h
215 struct device_driver {
216 const char *name;//驱动名字;一般和设备的名字进行匹配
217 struct bus_type *bus;
218
219 struct module *owner;
220 const char *mod_name; /* used for built-in modules */
221
222 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
223
224 const struct of_device_id *of_match_table;
225
226 int (*probe) (struct device *dev);//函数指针,探测函数;
227 int (*remove) (struct device *dev);
228 void (*shutdown) (struct device *dev);
229 int (*suspend) (struct device *dev, pm_message_t state);
230 int (*resume) (struct device *dev);
231 const struct attribute_group **groups;
232
233 const struct dev_pm_ops *pm;
234
235 struct driver_private *p;
236 };
5.5.3.4、类
(1)相关结构体:struct class 和 struct class_device
(2)udev的使用离不开class
(3)class的真正意义在于作为同属于一个class的多个设备的容器。也就是说,class是一种人造概念,目的就是为了对各种设备进行分类管理。当然,class在分类的同时还对每个类贴上了一些“标签”,这也是设备驱动模型为我们写驱动提供的基础设施。
//类和总线一样都是管理设备的,不同的管理思路和方法:设备受总线管理、受类管理。双重管理!
//[root@localhost linux-3.5]# ls /sys/devices/ //其他地方通过符号链接过来的!
cpu isa LNXSYSTM:00 pci0000:00 platform pnp0 pnp1 software system tracepoint virtual
5.5.3.5、总结
(1)模型思想很重要,其实就是面向对象的思想
(2)全是结构体套结构体,对基本功(语言功底和大脑复杂度)要求很高
//////////////////////////////////////////////////////////////
5.5.4.platform平台总线工作原理1
5.5.4.1、何为平台总线
(1)相对于usb、pci、i2c等物理总线来说,platform总线是虚拟的、抽象出来的。
(2)回顾裸机中讲的,CPU与外部通信的2种方式:
地址总线式连接和专用接口式连接。平台总线对应地址总线式连接设备,也就是SoC内部集成的各种内部外设。

图82----------------------------------------
(3)思考:为什么要有平台总线?进一步思考:为什么要有总线的概念?
//因为CPU有大部分外设在内部直接连接,而且他的工作方式是直接扩展到CPU32位地址总线空间,没有总线概念。本来不需要总线连接但为了都有总线管理机制和便于管理才引入的。借用这种管理体系。
//为了把总线下面的设备和驱动方便管理起来。eg:大公司保洁部一个人担任自己为经理
5.5.4.2、平台总线下管理的2员大将
(1)platform工作体系都定义在drivers/base/platform.c中
(2)两个结构体:platform_device和platform_driver
(3)两个接口函数:platform_device_register和platform_driver_register分别注册上面两个
//[root@localhost linux-3.5]# vim drivers/base/platform.c
struct platform_device {//平台总线的设备结构体
const char * name; // 平台总线下设备的名字,不能重合
int id;//索引、标记;
struct device dev; //// 所有设备通用的属性部分
u32 num_resources; // 设备使用到的resource的个数;资源如有限的中断号、IO内存地址
struct resource * resource; // 设备使用到的资源数组的首地址;kmalloc自己申请,把地址放给这个
const struct platform_device_id *id_entry; // 设备ID表;数组
//比如一款驱动可以操作的五款相似的NAND flash设备(兼容),还想知道这五款设备的详情;该数组放了一系列描述他们的ID号,分别对应这五款设备。
/* arch specific additions */
struct pdev_archdata archdata; // 自留地,用来提供扩展性的
};
struct platform_driver {//平台总线的设备驱动结构体
int (*probe)(struct platform_device *);
//安装、驱动探测函数;驱动找到设备后会调用该函数试图去操作该设备,看是否为可控设备。
int (*remove)(struct platform_device *); // 去掉一个设备
void (*shutdown)(struct platform_device *); // 关闭一个设备
int (*suspend)(struct platform_device *, pm_message_t state);//挂起设备
int (*resume)(struct platform_device *); //恢复设备
struct device_driver driver; //// 所有设备共用的一些属性
const struct platform_device_id *id_table; // 设备ID表;数组
//比如一款驱动可以操作的五款相似的NAND flash设备(兼容),还想知道这五款设备的详情;该数组放了一系列描述他们的ID号,分别对应这五款设备。
};
**********************************************************************
5.5.5.platform平台总线工作原理2
5.5.5.1、平台总线体系的工作流程
(1)第一步(自动工作执行):系统启动时在bus系统中注册platform
(2)第二步:内核移植的人负责提供platform_device
//[root@localhost linux-3.5]# vim arch/arm/mach-exynos/mach-tiny4412.c
//找,里面如果没有定义,你需要移植的则需要添加
(3)第三步:写驱动的人负责提供platform_device
(4)第四步(自动运行):platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了
//给设备找驱动思路:以上两个各自去管理员内核注册自己,注册的platform_device去到platform_driver的链表去找,找到了则匹配成功。
5.5.5.2、代码分析:platform本身注册
(1)每种总线(不光是platform,usb、i2c那些也是)都会带一个match方法,match方法用来对总线下的device和driver进行匹配。理论上每种总线的匹配算法是不同的,但是实际上一般都是看name的。
(2)platform_match函数就是平台总线的匹配方法。
该函数的工作方法是:
如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了,如果找完id_table都还没找到就说明没匹配上;
如果没有id_table或者没匹配上,那就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上那就匹配失败。
//平台总线匹配方法:
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 }
---------------------------------------------------------------
//该函数一执行,该套体系就建立起来了!完成这四步中的第一步;接下来等待用户提供platform_device和platform_driver
837 int __init platform_bus_init(void)
838 {
839 int error;
840
841 early_platform_cleanup();
842
843 error = device_register(&platform_bus);
844 if (error)
845 return error;
846 error = bus_register(&platform_bus_type);//
847 if (error)
848 device_unregister(&platform_bus);
849 return error;
850 }
828 struct bus_type platform_bus_type = {//
829 .name = "platform",
830 .dev_attrs = platform_dev_attrs,
831 .match = platform_match,//两者做匹配的
832 .uevent = platform_uevent,
833 .pm = &platform_dev_pm_ops,
834 };
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 }
633 static const struct platform_device_id *platform_match_id( //
634 const struct platform_device_id *id,
635 struct platform_device *pdev)
636 {
637 while (id->name[0]) {
638 if (strcmp(pdev->name, id->name) == 0) {
639 pdev->id_entry = id;
640 return id;
641 }
642 id++;
643 }
644 return NULL;
645 }
5.5.6.platform平台总线工作原理3
5.5.6.1、以leds-s3c24xx.c为例来分析platform设备和驱动的注册过程
(1)platform_driver_register
(2)platform_device_register
----------------------------------------------
//驱动部分:
//[root@localhost linux-3.5]# vim drivers/leds/leds-s3c24xx.c //三星驱动工程师写的
115 static struct platform_driver s3c24xx_led_driver = {//
116 .probe = s3c24xx_led_probe,//
117 .remove = s3c24xx_led_remove,
118 .driver = { //内置的struct device_driver driver结构体的属性如下:
119 .name = "s3c24xx_led",//必须要和s3c24xx_led_device 里的struct device device结构体里的name匹配!
120 .owner = THIS_MODULE,
121 },
122 };
71 static int s3c24xx_led_probe(struct platform_device *dev)//重点查看!
72 {
73 struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;//设备方给驱动方转交、交接数据!
74 struct s3c24xx_gpio_led *led;
75 int ret;
76
77 led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
78 if (led == NULL) {
79 dev_err(&dev->dev, "No memory for device\n");
80 return -ENOMEM;
81 }
82
83 platform_set_drvdata(dev, led);
84
85 led->cdev.brightness_set = s3c24xx_led_set;
86 led->cdev.default_trigger = pdata->def_trigger;
87 led->cdev.name = pdata->name;
88 led->cdev.flags |= LED_CORE_SUSPENDRESUME;
89
90 led->pdata = pdata;
91
92 /* no point in having a pull-up if we are always driving */
93
94 if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
95 s3c2410_gpio_setpin(pdata->gpio, 0);
96 s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
97 } else {
98 s3c2410_gpio_pullup(pdata->gpio, 0);
99 s3c2410_gpio_setpin(pdata->gpio, 0);
100 s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
101 }
102
103 /* register our new led device */
104
105 ret = led_classdev_register(&dev->dev, &led->cdev);//注册接口
106 if (ret < 0) {
107 dev_err(&dev->dev, "led_classdev_register failed\n");
108 kfree(led);
109 return ret;
110 }
111
112 return 0;
113 }
114
//设备部分:
[root@localhost linux-3.5]# vim arch/arm/mach-s3c24xx/mach-mini2440.c
//这里是mini2440架构,属于不同的架构吧!
朱老师用的深圳九鼎X210开发板(CPU:S5PV210,ARM Cortex-A8)不是我们需要的那个架构;
同理我的tiny4412也没有的话可能是友善之臂没有用这一套,自己另辟蹊径搞了一套。
所以我们这里需要自己参考别人的写。
428 static struct platform_device mini2440_led1 = {
429 .name = "s3c24xx_led",//
430 .id = 1, //我们自己指定ID;若为-1的话,不指定ID,让内核帮我们指定。
431 .dev = {
432 .platform_data = &mini2440_led1_pdata, //标记596: //追
433 },
434 };
435
436 static struct platform_device mini2440_led2 = {
437 .name = "s3c24xx_led",
438 .id = 2,
439 .dev = {
440 .platform_data = &mini2440_led2_pdata,
441 },
442 };
443
444 static struct platform_device mini2440_led3 = {
445 .name = "s3c24xx_led",
446 .id = 3,
447 .dev = {
448 .platform_data = &mini2440_led3_pdata,
449 },
450 };
451
452 static struct platform_device mini2440_led4 = {
453 .name = "s3c24xx_led",
454 .id = 4,
455 .dev = {
456 .platform_data = &mini2440_led4_pdata,
457 },
458 };
505 static struct platform_device *mini2440_devices[] __initdata = { //追不到,估计这里过时了!
506 &s3c_device_ohci,
507 &s3c_device_wdt,
508 &s3c_device_i2c0,
509 &s3c_device_rtc,
510 &s3c_device_usbgadget,
511 &mini2440_device_eth,
512 &mini2440_led1,//
513 &mini2440_led2,
514 &mini2440_led3,
515 &mini2440_led4,
516 &mini2440_button_device,
517 &s3c_device_nand,
518 &s3c_device_sdi,
519 &s3c_device_iis,
520 &uda1340_codec,
521 &mini2440_audio,
522 &samsung_asoc_dma,
523 };
622 static void __init mini2440_init(void)//
623 {
624 struct mini2440_features_t features = { 0 };
625 int i;
626
627 printk(KERN_INFO "MINI2440: Option string mini2440=%s\n",
628 mini2440_features_str);
629
630 /* Parse the feature string */
631 mini2440_parse_features(&features, mini2440_features_str);
632
633 /* turn LCD on */
634 s3c_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
635
636 /* Turn the backlight early on */
637 WARN_ON(gpio_request_one(S3C2410_GPG(4), GPIOF_OUT_INIT_HIGH, NULL));
638 gpio_free(S3C2410_GPG(4));
639
640 /* remove pullup on optional PWM backlight -- unused on 3.5 and 7"s */
641 s3c_gpio_setpull(S3C2410_GPB(1), S3C_GPIO_PULL_UP);
642 s3c2410_gpio_setpin(S3C2410_GPB(1), 0);
643 s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT);
644
645 /* mark the key as input, without pullups (there is one on the board) */
646 for (i = 0; i < ARRAY_SIZE(mini2440_buttons); i++) {
647 s3c_gpio_setpull(mini2440_buttons[i].gpio, S3C_GPIO_PULL_UP);
648 s3c_gpio_cfgpin(mini2440_buttons[i].gpio, S3C2410_GPIO_INPUT);
649 }
650 if (features.lcd_index != -1) {
651 int li;
652
653 mini2440_fb_info.displays =
654 &mini2440_lcd_cfg[features.lcd_index];
655
656 printk(KERN_INFO "MINI2440: LCD");
657 for (li = 0; li < ARRAY_SIZE(mini2440_lcd_cfg); li++)
658 if (li == features.lcd_index)
659 printk(" [%d:%dx%d]", li,
660 mini2440_lcd_cfg[li].width,
661 mini2440_lcd_cfg[li].height);
662 else
663 printk(" %d:%dx%d", li,
664 mini2440_lcd_cfg[li].width,
665 mini2440_lcd_cfg[li].height);
666 printk("\n");
667 s3c24xx_fb_set_platdata(&mini2440_fb_info);
668 }
669
670 s3c24xx_udc_set_platdata(&mini2440_udc_cfg);
671 s3c24xx_mci_set_platdata(&mini2440_mmc_cfg);
672 s3c_nand_set_platdata(&mini2440_nand_info);
673 s3c_i2c0_set_platdata(NULL);
674
675 i2c_register_board_info(0, mini2440_i2c_devs,
676 ARRAY_SIZE(mini2440_i2c_devs));
677
678 platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));//
679
680 if (features.count) /* the optional features */
681 platform_add_devices(features.optional, features.count);
682
683 }
//123 /**
124 * platform_add_devices - add a numbers of platform devices
125 * @devs: array of platform devices to add
126 * @num: number of platform devices in array
127 */
128 int platform_add_devices(struct platform_device **devs, int num)//
129 {
130 int i, ret = 0;
131
132 for (i = 0; i < num; i++) {
133 ret = platform_device_register(devs[i]);
134 if (ret) {
135 while (--i >= 0)
136 platform_device_unregister(devs[i]);
137 break;
138 }
139 }
140
141 return ret;
142 }
[root@localhost linux-3.5]# vim include/linux/device.h
633 struct device {
634 struct device *parent;
635
636 struct device_private *p;
637
638 struct kobject kobj;
639 const char *init_name; /* initial name of the device */
640 const struct device_type *type;
641
642 struct mutex mutex; /* mutex to synchronize calls to
643 * its driver.
644 */
645
646 struct bus_type *bus; /* type of bus device is on */
647 struct device_driver *driver; /* which driver has allocated this
648 device */
649 void *platform_data; /* Platform specific data, device
650 core doesn't touch it */ //留白
//标记447:这里void *前期不知道指向哪里,但可以指向任意类型的变量,而不是指向空。
392 /* LEDS */ //arch/arm/mach-s3c24xx/mach-mini2440.c
393
394 static struct s3c24xx_led_platdata mini2440_led1_pdata = { //
395 .name = "led1",
396 .gpio = S3C2410_GPB(5),
397 .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
398 .def_trigger = "heartbeat", //心跳的
399 };
400
401 static struct s3c24xx_led_platdata mini2440_led2_pdata = {
402 .name = "led2",
403 .gpio = S3C2410_GPB(6),
404 .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
405 .def_trigger = "nand-disk",
406 };
407
408 static struct s3c24xx_led_platdata mini2440_led3_pdata = {
409 .name = "led3",
410 .gpio = S3C2410_GPB(7),
411 .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
412 .def_trigger = "mmc0",
413 };
414
415 static struct s3c24xx_led_platdata mini2440_led4_pdata = {
416 .name = "led4",
417 .gpio = S3C2410_GPB(8),
418 .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
419 .def_trigger = "",
420 };
///linuxtags/arch/arm/mach-s3c24xx/include/mach/leds-gpio.h
20 struct s3c24xx_led_platdata { //LED特有的平台数据
21 unsigned int gpio;
22 unsigned int flags;
23
24 char *name;
25 char *def_trigger;
26 };
5.5.6.2、platdata怎么玩
(1)platdata其实就是设备注册时提供的设备有关的一些数据(譬如设备对应的gpio、使用到的中断号、设备名称····)
(2)这些数据在设备和驱动match之后,会由设备方转给驱动方。驱动拿到这些数据后,通过这些数据得知设备的具体信息,然后来操作设备。
(3)这样做的好处是:驱动源码中不携带数据,只负责算法(对硬件的操作方法)。现代驱动设计理念就是算法和数据分离,这样最大程度保持驱动的独立性和适应性。
//同一类设备驱动应具有普遍性、通用性。
5.5.6.3、match函数的调用轨迹
5.5.6.4、probe函数的功能和意义
///////////////////////////实践////////////////////////////
5.5.7.平台总线实践环节1
5.5.7.1、回顾
5.5.7.2、先初步改造添加platform_driver
(1)第1步:先修改原来的代码到只有led1
(2)第2步:
//[root@localhost ~]# vim /linux-3.5/drivers/leds/leds-s3c24xx.c //模仿编写
5.5.7.3、实验现象分析
//名义上安装了,但是driver里是空的
5.5.8.平台总线实践环节2
5.5.8.1、检查mach-x210.c中是否有led相关的platform_device
//[root@localhost ~]# vim /linux-3.5/arch/arm/mach-s5pv210/mach-smdkv210.c //检查
//[root@localhost ~]# vim /linux-3.5/arch/arm/mach-s3c24xx/mach-mini2440.c //
5.5.8.2、参考mach-mini2440.c中添加led的platform_device定义
5.5.8.3、测试只有platform_device没有platform_driver时是怎样的
5.5.9.平台总线实践环节3
5.5.9.1、测试platform_device和platform_driver相遇时会怎样
5.5.9.2、probe函数
(1)probe函数应该做什么
(2)probe函数的数据从哪里来
(3)编程实践
5.5.10.平台总线实践环节4
浙公网安备 33010602011771号