原文出自:http://blog.csdn.net/ghostyu/article/details/6908805
一个现实的linux设备和驱动通常要挂接在一种总线上,像pci,usb,iic,spi等都是总线结构,这当然不是问题,但是嵌入式系统中,Soc系统集成的独立外设控制器,挂接在soc内存空间的外设等却不依附于此类总线。
基于这个背景,linux发明了一种虚拟总线:platform总线,相应的设备称为platform_device,而驱动成为platform_driver。
注意,platform_device并不是与自负设备,块设备等平行的概念,而是linux提供的一种附加手段,例如s3c2440处理器中,把内部集成的iic,rtc,spi,lcd,watchdog,等控制器归纳为platform_device,但是他们本身就是字符设备。
platform_device
[cpp] view plain copy print ?
- struct platform_device {
- const char * name;
- int id;
- struct device dev;
- u32 num_resources;
- struct resource * resource;
- const struct platform_device_id *id_entry;
-
- struct pdev_archdata archdata;
- };
struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; const struct platform_device_id *id_entry; /* arch specific additions */ struct pdev_archdata archdata; };
platform_device成员变量
1、struct device(部分),include<linux/device.h>
[cpp] view plain copy print ?
- struct device {
- struct device *parent;
- struct device_private *p;
- struct kobject kobj;
- const char *init_name;
- struct device_type *type;
- struct mutex mutex;
-
-
- struct bus_type *bus;
- struct device_driver *driver;
-
- void *platform_data;
-
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */
2、struct resource
[cpp] view plain copy print ?
- struct resource {
- resource_size_t start;
- resource_size_t end;
- const char *name;
- unsigned long flags;
- struct resource *parent, *sibling, *child;
- };
struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; };
platform_device对应的platform_driver
[cpp] view plain copy print ?
- 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;
- };
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; };
支持电源管理时,需要实现 shutdown,suspend,resume这三个函数,若不支持,将他们设为null。
platform_driver结构体中的重要成员变量 device_driver
[cpp] view plain copy print ?
- struct device_driver {
- const char *name;
- struct bus_type *bus;
- struct module *owner;
- const char *mod_name;
- bool suppress_bind_attrs;
- #if defined(CONFIG_OF)
- const struct of_device_id *of_match_table;
- #endif
- int (*probe) (struct device *dev);
- int (*remove) (struct device *dev);
- void (*shutdown) (struct device *dev);
- int (*suspend) (struct device *dev, pm_message_t state);
- int (*resume) (struct device *dev);
- const struct attribute_group **groups;
- const struct dev_pm_ops *pm;
- struct driver_private *p;
- };
struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ #if defined(CONFIG_OF) const struct of_device_id *of_match_table; #endif int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; };
用于向内核注册platform_driver的函数platform_driver_register(platform_driver *)
反之,注销platform_driver的函数platform_drvier_unregister(platform*)
一般实现platform_driver时,除了实现file_operations中的read、write等函数外,还要实现platform_driver中的probe与remove等函数,其余均按正常的linux设备驱动的编写方法编写驱动程序。
例如将mychar移植成platform_driver,简略的如下形式
[cpp] view plain copy print ?
- static int __devinit mychar_probe(struct platform_device *pdev)
- {
-
-
-
-
- }
- static int __devexit mychar_remove(struct platform_device *pdev)
- {
-
- }
- static struct platform_driver mychar_device_driver = {
- .probe = mychar_probe,
- .remove = __devexit_p(mychar_remove),
- .driver = {
- .name = "mychar",
- .owner = THIS_MODULE,
- }
- };
-
- static int __init mychar_init(void)
- {
- return platform_driver_register(&mychar_device_driver);
- }
-
- static void __exit mychar_exit(void)
- {
- platform_driver_unregister(mychar_device_driver);
- }
-
- module_init(mychar_init);
- module_exit(mychar_exit);
static int __devinit mychar_probe(struct platform_device *pdev) { //申请设备号 //申请设备结构体的内存 //注册cdev //其实probe 函数里就是实现之前在mychar_init中实现的功能 } static int __devexit mychar_remove(struct platform_device *pdev) { //实现之前在mychar_exit()中的释放内存的功能 } static struct platform_driver mychar_device_driver = { .probe = mychar_probe, .remove = __devexit_p(mychar_remove), .driver = { .name = "mychar", .owner = THIS_MODULE, } }; //在mychar_init中注册platform_driver static int __init mychar_init(void) { return platform_driver_register(&mychar_device_driver); } //在mychar_exit 中注销platform static void __exit mychar_exit(void) { platform_driver_unregister(mychar_device_driver); } //驱动余下部分与之前实现的mychar相同 module_init(mychar_init); module_exit(mychar_exit);注意,如果要让这个驱动在开发板上能工作,需要在板文件中添加相应的代码,在板文件例如 arch/arm/mach-s3c2440/mach-mini2440.c,代码如下
[cpp] view plain copy print ?
- static struct platform_device mychar_device = {
- .name = "mychar",
- .id = -1,
- };
static struct platform_device mychar_device = { .name = "mychar", .id = -1, };这样就表示,开发板上有一个devie,名字叫mychar,因为mychar是内存中虚拟出来的,所以这里并不需要设置别的,只要设置一下与driver相匹配的name:mychar就可以了
通常开发板不会只有这一个设备,所以在platform_device数组中,将上面的mychar_device添加进来,如下:
[cpp] view plain copy print ?
- static struct platform_device *mini2440_devices[] __initdata = {
- &mychar_device,
- &s3c_rtc,
- &s3c_device_fb,
- ...
- }
static struct platform_device *mini2440_devices[] __initdata = { &mychar_device, &s3c_rtc, &s3c_device_fb, ... }
platform_devece的资源与数据(resource 与platform_data)
还记的在platform_device 中的struct resource *resource吗,
[cpp] view plain copy print ?
- <pre name="code" class="cpp"> resource_size_t start;
- resource_size_t end;
- const char *name;
- unsigned long flags;
<pre name="code" class="cpp"> resource_size_t start; resource_size_t end; const char *name; unsigned long flags;
通常只关心struct resource中的以上四个成员变量
start 与end两个字段的值随着flags的改变而改变,当flags 为 IORESOURCE_MEM 时,start,end分别表示该platform_device 占据的内存的开始地址和结束地址,若flags为IORESOURCE_IRQ 时,start end 则表示该platform_device 使用的中断号的开始值和结束值,假如只使用了1个中断号,那么start与end相同。
例如dm9000的resource部分:
[cpp] view plain copy print ?
-
- static struct resource mini2440_dm9k_resource[] = {
- [0] = {
- .start = MACH_MINI2440_DM9K_BASE,
- .end = MACH_MINI2440_DM9K_BASE + 3,
- .flags = IORESOURCE_MEM
- },
- [1] = {
- .start = MACH_MINI2440_DM9K_BASE + 4,
- .end = MACH_MINI2440_DM9K_BASE + 7,
- .flags = IORESOURCE_MEM
- },
- [2] = {
- .start = IRQ_EINT7,
- .end = IRQ_EINT7,
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
- }
- };
/* DM9000AEP 10/100 ethernet controller */ static struct resource mini2440_dm9k_resource[] = { [0] = { .start = MACH_MINI2440_DM9K_BASE, .end = MACH_MINI2440_DM9K_BASE + 3, .flags = IORESOURCE_MEM }, [1] = { .start = MACH_MINI2440_DM9K_BASE + 4, .end = MACH_MINI2440_DM9K_BASE + 7, .flags = IORESOURCE_MEM }, [2] = { .start = IRQ_EINT7, .end = IRQ_EINT7, .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, } };所谓的resource,具体来时是与板级硬件密切相关的,比如控制器映射到soc内存的地址范围,外部中断引脚等,
当然,要把定义的这个resources[]赋值给platform_device的.resource 字段,同时要设置.num_resources资源个数。
设备除了可以再bsp中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存,DMA通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不宜直接放置在设备驱动本身,因此platform也提供了platform_data的支持,platform_data的形式是自定义的,比如对于dm9000网卡来说,platform_data中可以存放mac地址,总线宽度,板上有误eeprom等信息。
[cpp] view plain copy print ?
- * The DM9000 has no eeprom, and it's MAC address is set by
- * the bootloader before starting the kernel.
- */
- tatic struct dm9000_plat_data mini2440_dm9k_pdata = {
- .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
- ;
* The DM9000 has no eeprom, and it's MAC address is set by * the bootloader before starting the kernel. */ static struct dm9000_plat_data mini2440_dm9k_pdata = { .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), }; 然后将这个data赋值给platform_device中.dev的.platform_data数据项,如下:
[cpp] view plain copy print ?
- static struct platform_device mini2440_device_eth = {
- .name = "dm9000",
- .id = -1,
- .num_resources = ARRAY_SIZE(mini2440_dm9k_resource),
- .resource = mini2440_dm9k_resource,
- .dev = {
- .platform_data = &mini2440_dm9k_pdata,
- },
- };
static struct platform_device mini2440_device_eth = { .name = "dm9000", .id = -1, .num_resources = ARRAY_SIZE(mini2440_dm9k_resource), .resource = mini2440_dm9k_resource, .dev = { .platform_data = &mini2440_dm9k_pdata, }, };
所以在抑制linux到具体的开发板时,基本都是这么移植的是不是?回答是肯定的,这里注意了,以上与板级硬件密切相关的代码部分,均在bsp板级支持文件中,例如mach-s3c2440.c中,但是你看到了真正的驱动了吗比如字符设备的read write等函数的实现了吗。
真正的驱动代码在内核的driver文件夹下,比如dm9000的驱动在 drviver/net/文件夹下的dm9000.c中,而且这部分的代码是与具体的板级硬件无关的,再比如nandflash的驱动,配置也是在mach-s3c2440.c中,但关键的驱动源码在 drvier/mtd/nand/文件夹下
这样的结构就是linux驱动的分层思想,设备驱动的核心层与例化。