linux设备驱动之platform平台总线工作原理(二)

5.5.5.platform平台总线工作原理2

5.5.5.1、平台总线体系的工作流程

(1)第一步:linux内核系统启动时在bus系统中注册platform。

1、什么叫做bus系统,操作系统中有一套管理总线的体系,内核里有一个子系统,就叫做总线子系统。就是内核来管理总线的。bus系统在内核启动时建立起来,比platform建立的时间还要早,bus系统的是由内核编写的人提供的,我们将来分析代码的时候不需要去分析他。在bus系统起来以后,就需要在bus系统中注册这个platform平台总线的bus,将其注册到bus系统中,来让内核的bus系统来进行管理,因为bus系统是来管理总线的嘛,所以你想要使用一种总线,自然要将这个总线注册到bus系统中,让其来进行管理,这个注册的过程将来可以分析一下,来知道platform平台总线是怎么注册的。

第一步做的就是系统起来时,先把这套体系构建起来,什么体系,当然是bus系统管理platform等总线的这个体系构建起来,以及platform总线注册到bus系统中,platform这个平台总线体系可以工作起来。bus系统能工作管理其他总线,platform总线能工作。这是第一步

(2)第二步:内核移植的人负责提供platform_device

何为内核移植的人,就是uboot移植、Linux内核移植、构建rootfs,做这些工作的人,这些人首先要提供platform_device,比如你的板子上有4个led灯,那么你在移植这个板子的时候,系统中就应该提供led对应的platform_device(因为led灯是扩展到cpu地址总线上去的,根据前面讲的,这类设备是属于platform平台下的,所以既然有了led这个设备,那么移植的人就要提供platform总线下的platform_device这个led设备),要提供platform_device对应的那个结构体变量,一个platform_device结构体变量对应的就是一个led设备,这个结构体就是对设备的一种抽象。这个变量的定义是由内核移植的人来负责的,内核移植说白了,就是将硬件的相关信息写到软件里面去,比如你用到了几个led,你的led对应的gpio是什么,需不要用到中断什么的,这些都是系统移植的人做的,所以系统移植的人,需要先将platform_device个注册好,理论上platform_device是由系统移植的人提供的,但是如果没有提供,那些驱动的人需要自己给他填上。

提供的意思就是你要编写相关的代码,一般platform_device你提供这个代码的时候,一般是在mach-什么什么的文件中进行提供,对应你的Soc的名字,系统移植的人提供板子上的硬件的platform_device对应的结构体变量,要放在mach-xxx的文件中,xxx一般为soc的名字。

像我在mach-smdkv210.c文件中找到了许多platform_device类型结构体定义变量,这些变量都是对应于Soc中属于平台总线下的一些硬件设备。

如:DM9000设备对应的平台总线platform下的设备platform_device定义的结构体变量smdkv210_dm9000

static struct platform_device smdkv210_dm9000 = {

.name = "dm9000",

.id = -1,

.num_resources = ARRAY_SIZE(smdkv210_dm9000_resources),

.resource = smdkv210_dm9000_resources,

.dev = {

.platform_data = &smdkv210_dm9000_platdata,

},

};

其中name成员表示设备的名字,很显然是dm9000,num_resources成员是表示该设备使用的资源个数,使用了几个资源,资源包括内存,io等,resource表示所使用的具体资源,指向的是一个含有资源的一个数组,数组中的元素个数,也就是资源个数是用的ARRAY_SIZE(smdkv210_dm9000_resources),方法来计算出使用的资源个数,其实就是资源数组的总大小/数组一个元素的大小,只是这里是用的宏的方式来实现的。

如:

static struct platform_device smdkv210_lcd_lte480wv = {

.name = "platform-lcd",

.dev.parent = &s3c_device_fb.dev,

.dev.platform_data = &smdkv210_lcd_lte480wv_data,

};

lcd的platform_device。

最后用的一个总的表示smdk设备的platform_device的结构体数组,这个数组表示该Soc所有的现在使用的platform总线下的设备。

如:

static struct platform_device *smdkv210_devices[] __initdata = {

&s3c_device_adc,

&s3c_device_cfcon,

&s3c_device_fb,

&s3c_device_hsmmc0,

&s3c_device_hsmmc1,

&s3c_device_hsmmc2,

&s3c_device_hsmmc3,

&s3c_device_i2c0,

&s3c_device_i2c1,

&s3c_device_i2c2,

&s3c_device_rtc,

&s3c_device_ts,

&s3c_device_usb_hsotg,

&s3c_device_wdt,

&s5p_device_fimc0,

&s5p_device_fimc1,

&s5p_device_fimc2,

&s5p_device_fimc_md,

&s5p_device_jpeg,

&s5p_device_mfc,

&s5p_device_mfc_l,

&s5p_device_mfc_r,

&s5pv210_device_ac97,

&s5pv210_device_iis0,

&s5pv210_device_spdif,

&samsung_asoc_idma,

&samsung_device_keypad,

&smdkv210_dm9000,

&smdkv210_lcd_lte480wv,

};

定义了一个platform_device类型的结构体指针数组,数组里面每一个元素都是一个指针,都指向一个platform_device类型的抽象出来的设备。

最后使用

platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));

函数将当前smdkv210_devices中的所有platform_device设备进行添加,这个函数里面的内容实际上就是注册设备的操作。

如下:

int platform_add_devices(struct platform_device **devs, int num)

{

int i, ret = 0;

for (i = 0; i < num; i++) {

ret = platform_device_register(devs[i]);

if (ret) {

while (--i >= 0)

platform_device_unregister(devs[i]);

break;

}

}

return ret;

}

此时可以说,该Soc的属于platform平台总线下的platform_device所有设备才注册到了platform总线中。之后就等待写驱动的人将平台总线下的platform_driver写好,所有的平台设备驱动写好,然后进行注册到platform总线中。

(3)第三步:写驱动的人负责提供platform_driver

提供的意思就是你要编写相关的代码,写驱动的人需要将对应的platform_device的驱动给写好,比如dm9000设备,驱动的人就需要写一个关于dm9000这个platform_device设备的驱动platform_driver。

如:

static struct platform_driver dm9000_driver = {

.driver = {

.name    = "dm9000",

.owner = THIS_MODULE,

.pm = &dm9000_drv_pm_ops,

},

.probe   = dm9000_probe,

.remove  = dm9000_drv_remove,

};

这就是需要的人要实现的,.name是驱动的名字,因为设备和驱动是通过名字来进行匹配的,所以名字一定不能写错,.owner应该是说明这是个驱动模块。.pm是电源管理对应的电源管理函数。.probe是dm9000的探测函数,.remove是dm9000的卸载函数,这些函数都是要写驱动的人去实现的,实现好后,绑定到这个dm9000_driver的函数指针成员中。然后调用platform提供的注册函数,注册这个驱动到platform总线中。platform总线将来就会利用match函数来发现driver和device从而来进行匹配。找到匹配上后,就会执行驱动的probe函数来完成探测初始化和安装,然后就工作起来了。

(4)第四步:platform的match函数发现driver和device匹配后,调用driver和probe函数来完成驱动的初始化和安装,然后设备就工作起来了

platform总线,他的地位有这系统的管理员的地位,来管理该总线下的驱动和设备,platform_device和platform_driver各自像管理员platform总线去注册自己,从而让platform总线这个管理员来进行管理,来用match进行设备和驱动的匹配。通过platform总线下的platform_device这个分支,是一个链表来管理的,找这些设备的name,然后在通过platform总线下的platform_driver这个分支,也是一个链表来进行找设备的name名字,如果在驱动这个分支中找到了这个name,说明这个设备有这个驱动,然后驱动对应的函数等就开始工作了。

5.5.5.2、代码分析

1、platform.c中入口函数,就是platform_bus_init函数,这个函数就是platform这个总线本身自己初始化,本身自己安装注册到bus系统中的时候。

int __init platform_bus_init(void)

{

int error;

early_platform_cleanup();

error = device_register(&platform_bus);//这句话的作用就是在目录下弄出来一个platform

//目录,在device目录下

if (error)

return error;

error =  bus_register(&platform_bus_type); //注册platform到bus系统中,在bus目录下会

//看到platform

if (error)

device_unregister(&platform_bus);

return error;

}

其中

struct bus_type platform_bus_type = {

.name = "platform",

.dev_attrs = platform_dev_attrs,

.match = platform_match,

.uevent = platform_uevent,

.pm = &platform_dev_pm_ops,

};

中的.match方法就是给platform总线下的驱动和设备来提供匹配的方法。

2、每种总线,都会带一个match方法,用来对总线下的device和driver进行匹配,理论上每种总线匹配device和driver的方法是不一样的,比如看name,看id,但实际上匹配都是看name进行匹配的。match函数怎么写的,就是怎么匹配设备和驱动的。platform_match函数就是平台总线的一个match匹配方法。

如下:

static int platform_match(struct device *dev, struct device_driver *drv)

{

struct platform_device *pdev = to_platform_device(dev);//这个宏是里面有一个continer

//宏,用来给一个结构体成员的指针,来得到整个结构体的首地址的。因为struct device是所有设备

//都用的部分,每一个设备中都会有这么一个struct device,所以可以通过platform_device中的

//struct device来找到struct platform_device这个结构体的首地址。

//同时因为这个总线是由struct bus_type来定义出来的platform_bus_type,但是bus_type不知道

//自己这个这个总线类型结构体将来会定义出来什么结构体,所以bus_type里面的match函数指针的

//参数只能是struct device和struct driver而不能是struct platform_device和struct //platform_driver,所以只能是struct device,从而因为所有设备都有struct device,所以就能找

//到每个总线下的设备,比如struct platform_device,是这么玩的

struct platform_driver *pdrv = to_platform_driver(drv);

/* Attempt an OF style match first */

if (of_driver_match_device(dev, drv))

return 1;

/* Then try ACPI style match */

if (acpi_driver_match_device(dev, drv))

return 1;

/* Then try to match against the id table */

if (pdrv->id_table) //如果pdrv中的id表是非空的,则条用下面的这个函数进行匹配

return platform_match_id(pdrv->id_table, pdev) != NULL;

/* fall-back to driver name match */

return (strcmp(pdev->name, drv->name) == 0);    //如果pdrv的id是空的,则直接比较

//名字name是否相同来进行匹配

}

时间: 2024-08-01 10:46:06

linux设备驱动之platform平台总线工作原理(二)的相关文章

linux设备驱动之platform平台总线工作原理(三)

设备为数据,驱动为加工着 1.以led-s3c24xx.c为例来分析platform设备和驱动的注册过程 其中关于led的驱动数据结构为: static struct platform_driver s3c24xx_led_driver = { .probe = s3c24xx_led_probe, .remove = s3c24xx_led_remove, .driver = { .name = "s3c24xx_led", .owner = THIS_MODULE, }, }; s

linux驱动之platform平台总线工作原理(一)

5.5.4.platform平台总线工作原理 5.5.4.1.何为平台总线 (1)platform总线相对于i2c.usb.spi.pci等总线是不同的,他们属于物理总线,platform总线是属于虚拟总线.抽象出来的,platform总线下的设备并不对应于真实存在的一种设备,这种总线在真实的物理是是没有的.比如i2c在物理上有i2c总线,但是platform总线在物理上并没有这种总线. (2)CPU和外部通信时,有两种连接方式,一种叫做地址总线式连接,一种叫做专用接口式连接,有一些设备是通过地

linux设备驱动模型之平台总线实践环节(一)

1.首先回顾下之前写的驱动和数据在一起的led驱动代码,代码如下: #include <linux/module.h> #include <linux/init.h> #include <linux/leds.h> #include <asm/io.h> //ioremap和iounmap的头文件 writel等 /**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表************

linux设备驱动模型之平台总线实践环节(四)

//通过led_classdev类型的指针得到s5pv210_led_pladata类型的指针,这个s5pv210_led_pladata类型结构体是我们自己定义的设备数据部分. static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev) { return container_of(led_cdev, struct s5pv210_gpio_led, cdev); } #define X210_LED

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

Linux设备驱动核心理论(一)

4.Linux内核模块 4.1 Linux内核模块简介 如果把所有需要的功能都编译到Linux内核.这回导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核. 现在我们需要的是一种机制使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码被动态地加载到内核中. Linux提供了这样的一种机制,这种机制被称为模块(Module).模块具有这样的特点: 模块本身不被编译如内核映像,从而控制内核的大小. 模块一旦被加载,它就

linux设备驱动之平台总线实践环节(三)

1.在平台总线的实践环节的一和二中,分别将led的platform_driver和platform_device初步完成,接下来看下platform_device和platform_driver同时存在时的效果,就是led的平台设备已经注册到内核中,并且在根文件系统中ismod加载上led的平台总线驱动.看一下platform_device和platform_driver两者相遇会怎么样,根据platform平台总线的逻辑,因为我们已经将led的platform_device注册到了内核中,这个

linux设备驱动之平台总线实践环节(二)

1.上一节中,我们将初步的驱动代码写完后编译后,放入到rootfs中进行insmod时,在/sys/bus/platform/drvier/目录中能够看到why_led这个目录,但是进入后只有一些基本的东西,却没有能使用这个led驱动的关键性东西,那是因为我们没有提供platform_device,并且驱动代码中的probe函数remove函数的代码内容也不对.这一节课中,做另一半,就是platform_device这一部分. 2.做platform_device这一半 参考mach-mini2

linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这一节可以理解是第八章的延伸,从这节开始介绍platform设备驱动. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx