linux驱动学习(六) 应用中的linux驱动 platform_device

来源:互联网 发布:唐红的恋歌 知乎 编辑:程序博客网 时间:2024/06/05 03:31


一个现实的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 plaincopy
  1. struct platform_device {  
  2.     const char  * name;  
  3.     int     id;  
  4.     struct device   dev;  
  5.     u32     num_resources;  
  6.     struct resource * resource;  
  7.   
  8.     const struct platform_device_id *id_entry;  
  9.   
  10.     /* arch specific additions */  
  11.     struct pdev_archdata    archdata;  
  12. };  

platform_device成员变量

1、struct device(部分),include<linux/device.h>
[cpp] view plaincopy
  1. struct device {  
  2.     struct device       *parent;  
  3.   
  4.     struct device_private   *p;  
  5.   
  6.     struct kobject kobj;  
  7.     const char      *init_name; /* initial name of the device */  
  8.     struct device_type  *type;  
  9.   
  10.     struct mutex        mutex;  /* mutex to synchronize calls to 
  11.                      * its driver. 
  12.                      */  
  13.   
  14.     struct bus_type *bus;       /* type of bus device is on */  
  15.     struct device_driver *driver;   /* which driver has allocated this 
  16.                        device */  
  17.     void        *platform_data; /* Platform specific data, device 
  18.                        core doesn't touch it */  



2、struct resource

[cpp] view plaincopy
  1. struct resource {  
  2.     resource_size_t start;  
  3.     resource_size_t end;  
  4.     const char *name;  
  5.     unsigned long flags;  
  6.     struct resource *parent, *sibling, *child;  
  7. };  

platform_device对应的platform_driver

[cpp] view plaincopy
  1. struct platform_driver {  
  2.     int (*probe)(struct platform_device *);  
  3.     int (*remove)(struct platform_device *);  
  4.     void (*shutdown)(struct platform_device *);  
  5.     int (*suspend)(struct platform_device *, pm_message_t state);  
  6.     int (*resume)(struct platform_device *);  
  7.     struct device_driver driver;  
  8.     const struct platform_device_id *id_table;  
  9. };  

支持电源管理时,需要实现 shutdown,suspend,resume这三个函数,若不支持,将他们设为null。

platform_driver结构体中的重要成员变量 device_driver

[cpp] view plaincopy
  1. struct device_driver {  
  2.     const char      *name;  
  3.     struct bus_type     *bus;  
  4.   
  5.     struct module       *owner;  
  6.     const char      *mod_name;  /* used for built-in modules */  
  7.   
  8.     bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */  
  9.   
  10. #if defined(CONFIG_OF)  
  11.     const struct of_device_id   *of_match_table;  
  12. #endif  
  13.   
  14.     int (*probe) (struct device *dev);  
  15.     int (*remove) (struct device *dev);  
  16.     void (*shutdown) (struct device *dev);  
  17.     int (*suspend) (struct device *dev, pm_message_t state);  
  18.     int (*resume) (struct device *dev);  
  19.     const struct attribute_group **groups;  
  20.   
  21.     const struct dev_pm_ops *pm;  
  22.   
  23.     struct driver_private *p;  
  24. };  


用于向内核注册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 plaincopy
  1. static int __devinit mychar_probe(struct platform_device *pdev)  
  2. {  
  3.     //申请设备号  
  4.     //申请设备结构体的内存  
  5.     //注册cdev  
  6.     //其实probe    函数里就是实现之前在mychar_init中实现的功能  
  7. }  
  8. static int __devexit mychar_remove(struct platform_device *pdev)  
  9. {  
  10.     //实现之前在mychar_exit()中的释放内存的功能  
  11. }  
  12. static struct platform_driver mychar_device_driver = {  
  13.     .probe = mychar_probe,  
  14.     .remove = __devexit_p(mychar_remove),  
  15.     .driver = {  
  16.         .name = "mychar",  
  17.         .owner = THIS_MODULE,  
  18.         }  
  19. };  
  20. //在mychar_init中注册platform_driver  
  21. static int __init mychar_init(void)  
  22. {  
  23.     return platform_driver_register(&mychar_device_driver);  
  24. }  
  25. //在mychar_exit 中注销platform  
  26. static void __exit mychar_exit(void)  
  27. {  
  28.     platform_driver_unregister(mychar_device_driver);  
  29. }  
  30. //驱动余下部分与之前实现的mychar相同  
  31. module_init(mychar_init);  
  32. module_exit(mychar_exit);  
注意,如果要让这个驱动在开发板上能工作,需要在板文件中添加相应的代码,在板文件例如 arch/arm/mach-s3c2440/mach-mini2440.c,代码如下

[cpp] view plaincopy
  1. static struct platform_device mychar_device = {   
  2.     .name = "mychar",  
  3.     .id = -1,  
  4. };  
这样就表示,开发板上有一个devie,名字叫mychar,因为mychar是内存中虚拟出来的,所以这里并不需要设置别的,只要设置一下与driver相匹配的name:mychar就可以了

通常开发板不会只有这一个设备,所以在platform_device数组中,将上面的mychar_device添加进来,如下:

[cpp] view plaincopy
  1. static struct platform_device *mini2440_devices[] __initdata = {  
  2.     &mychar_device,  
  3.     &s3c_rtc,  
  4.     &s3c_device_fb,  
  5.     ...  
  6. }  


platform_devece的资源与数据(resource 与platform_data)

还记的在platform_device 中的struct resource *resource吗,

[cpp] view plaincopy
  1.     resource_size_t start;  
  2.     resource_size_t end;  
  3.     const char *name;  
  4.     unsigned long flags;  
  5. 通常只关心struct resource中的以上四个成员变量  
  6. start 与end两个字段的值随着flags的改变而改变,当flags 为 IORESOURCE_MEM 时,start,end分别表示该platform_device 占据的内存的开始地址和结束地址,若flags为IORESOURCE_IRQ 时,start end 则表示该platform_device 使用的中断号的开始值和结束值,假如只使用了1个中断号,那么start与end相同。  
  7. 例如dm9000的resource部分:  
  8. /* DM9000AEP 10/100 ethernet controller */  
  9.   
  10. static struct resource mini2440_dm9k_resource[] = {  
  11.     [0] = {  
  12.         .start = MACH_MINI2440_DM9K_BASE,  
  13.         .end   = MACH_MINI2440_DM9K_BASE + 3,  
  14.         .flags = IORESOURCE_MEM  
  15.     },  
  16.     [1] = {  
  17.         .start = MACH_MINI2440_DM9K_BASE + 4,  
  18.         .end   = MACH_MINI2440_DM9K_BASE + 7,  
  19.         .flags = IORESOURCE_MEM  
  20.     },  
  21.     [2] = {  
  22.         .start = IRQ_EINT7,  
  23.         .end   = IRQ_EINT7,  
  24.         .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,  
  25.     }  
  26. };  
  27. 所谓的resource,具体来时是与板级硬件密切相关的,比如控制器映射到soc内存的地址范围,外部中断引脚等,  
  28. 当然,要把定义的这个resources[]赋值给platform_device的.resource 字段,同时要设置.num_resources资源个数。  
  29. 设备除了可以再bsp中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存,DMA通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不宜直接放置在设备驱动本身,因此platform也提供了platform_data的支持,platform_data的形式是自定义的,比如对于dm9000网卡来说,platform_data中可以存放mac地址,总线宽度,板上有误eeprom等信息。  
  30.  * The DM9000 has no eeprom, and it's MAC address is set by  
  31.  * the bootloader before starting the kernel.  
  32.  */  
  33. static struct dm9000_plat_data mini2440_dm9k_pdata = {  
  34.     .flags      = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),  
  35. };  
  36. 然后将这个data赋值给platform_device中.dev的.platform_data数据项,如下:  
  37. static struct platform_device mini2440_device_eth = {  
  38.     .name       = "dm9000",  
  39.     .id     = -1,  
  40.     .num_resources  = ARRAY_SIZE(mini2440_dm9k_resource),  
  41.     .resource   = mini2440_dm9k_resource,  
  42.     .dev        = {  
  43.         .platform_data  = &mini2440_dm9k_pdata,  
  44.     },  
  45. };  
  46.   
  47. 所以在抑制linux到具体的开发板时,基本都是这么移植的是不是?回答是肯定的,这里注意了,以上与板级硬件密切相关的代码部分,均在bsp板级支持文件中,例如mach-s3c2440.c中,但是你看到了真正的驱动了吗比如字符设备的read write等函数的实现了吗。  
  48. 真正的驱动代码在内核的driver文件夹下,比如dm9000的驱动在 drviver/net/文件夹下的dm9000.c中,而且这部分的代码是与具体的板级硬件无关的,再比如nandflash的驱动,配置也是在mach-s3c2440.c中,但关键的驱动源码在 drvier/mtd/nand/文件夹下  
  49. 这样的结构就是linux驱动的分层思想,设备驱动的核心层与例化。  


[cpp] view plaincopy
  1. <pre></pre>  
  2. <pre></pre>  
  3. <pre></pre>  
0 0
原创粉丝点击