一个led-platfrom设备驱动的例子

什么是platform总线

一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USBPCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线platform机制将设备本身的资源注册进内核,有内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。


下面就介绍一下 platform总线、设备和驱动

1platform总线:

这里用的是linux2.6.36 内核的源代码

linux在系统启动时就注册了platform总线,看内核代码:

/*drivers/base/platform.c*/

 626 static int platform_match(struct device *dev, struct device_driver *drv)
 627 {
 628     struct platform_device *pdev = to_platform_device(dev);
 629     struct platform_driver *pdrv = to_platform_driver(drv);
 630 
 631     /* Attempt an OF style match first */
 632     if (of_driver_match_device(dev, drv))
 633         return 1;
 634 
 635     /* Then try to match against the id table */
 636     if (pdrv->id_table)
 637         return platform_match_id(pdrv->id_table, pdev) != NULL;
 638 
 639     /* fall-back to driver name match */
 640     return (strcmp(pdev->name, drv->name) == 0);  //配对函数检验名字是否一致
 641 }

。。。。。

 970 struct bus_type platform_bus_type = {
 971     .name       = "platform",          /*定义了总线名字为platform 总线注册后新建目录sys/bus/platform */
 972     .dev_attrs  = platform_dev_attrs,
 973     .match      = platform_match,       //指定配对函数
 974     .uevent     = platform_uevent, 
 975     .pm     = &platform_dev_pm_ops,
 976 };
 977 EXPORT_SYMBOL_GPL(platform_bus_type);

可以看到,总线中定义了成员名字和match函数,当有总线或者设备注册到platform总线时,内核自动调用match函数,

判断设备和驱动的name是否一致


2platform设备:

同样的,先看一下platform设备对应的结构体paltform_device

 /home/linux-2.6.36/include/linux
 17 struct platform_device {
 18     const char  * name;                      
 19     int     id;               //设备id 用于给插入总线并且具有相同name的设备编号 如果只有一个设备的话填 -1
 20     struct device   dev;      // 结构体中内嵌的device结构体                                  
 21     u32     num_resources;                   // 资源数
 22     struct resource * resource;              // 用于存放资源的数据
 23 
 24     const struct platform_device_id *id_entry;
 25 
 26     /* arch specific additions */
 27     struct pdev_archdata    archdata;
 28 };



上面的结构体中先不介绍idnum_resourcesresource。可以看到,platform_device的封装就是指定了一个目录的名字name,并且内嵌device

platform_device的注册和注销使用以下函数:

 /home/linux-2.6.36/drivers/base
 325 int platform_device_register(struct platform_device *pdev)
 326 {
 327     device_initialize(&pdev->dev);
 328     return platform_device_add(pdev);
 329 }
 330 EXPORT_SYMBOL_GPL(platform_device_register);

 340 void platform_device_unregister(struct platform_device *pdev)
 341 {
 342     platform_device_del(pdev);
 343     platform_device_put(pdev);
 344 }
 345 EXPORT_SYMBOL_GPL(platform_device_unregister);

注册后,同样会在 /sys/device/platform 目录下创建一个以 name 命名的目录,并且创建软连接到 /sys/bus/platform/device 下。

3platform驱动:

先看一下platform驱动对应的结构体paltform_driver

114 struct platform_driver {
115     int (*probe)(struct platform_device *);
116     int (*remove)(struct platform_device *);
117     void (*shutdown)(struct platform_device *);
118     int (*suspend)(struct platform_device *, pm_message_t state);
119     int (*resume)(struct platform_device *);
120     struct device_driver driver;
121     const struct platform_device_id *id_table;
122 };
可以看到, platform_driver结构体内嵌了 device_driver,并且实现了 probremove等操作。其实,当内核需要调用 probe函数时,它会调用 driver->probe,在 driver->probe中再调用 platform_driver->probe


platform_driver的注册和注销使用以下函数:

 432 int platform_driver_register(struct platform_driver *drv)
 433 {
 434     drv->driver.bus = &platform_bus_type;
 435     if (drv->probe)
 436         drv->driver.probe = platform_drv_probe;
 437     if (drv->remove)
 438         drv->driver.remove = platform_drv_remove;
 439     if (drv->shutdown)
 440         drv->driver.shutdown = platform_drv_shutdown;
 441 
 442     return driver_register(&drv->driver);
 443 }
 444 EXPORT_SYMBOL_GPL(platform_driver_register);
 。。。
 
 450 void platform_driver_unregister(struct platform_driver *drv)
 451 {
 452     driver_unregister(&drv->driver);
 453 }
 454 EXPORT_SYMBOL_GPL(platform_driver_unregister);


注册成功后内核会在 /sys/bus/platform/driver/ 目录下创建一个名字为 driver->name 的目录。


以下是一个led 的简单例子:

(用的是友善之臂的mini6410开发板)

一个led-platfrom设备驱动的例子_第1张图片一个led-platfrom设备驱动的例子_第2张图片

图1                                                                                                                                                         图2


只用了GPK8(图1)作为led的控制引脚 外接一个led(图2)    

  当引脚为电平时led灯亮      当引脚为高电平时灯灭

  //device.c
  1 #include <linux/module.h>
  2 #include <linux/init.h>
  3 
  4 #include <linux/platform_device.h>
  5 
  6 void led_dev_release(struct device *dev)
  7 {
  8         printk("<kernel> release\n");
  9 }
 10 struct resource s3c_led_res[1] = {
 11         [0] = {
 12                 .start = 0x7F008800,
 13                 .end   = 0x7F00880C,
 14                 .flags = IORESOURCE_MEM,
 15         },
 16 };
 17 
 18 struct platform_device s3c_led_dev = {
 19         .name = "plat_led",
 20         .id = -1,
 21         .dev = {
 22                     .release = led_dev_release,
 23         },
 24                     .num_resources = ARRAY_SIZE(s3c_led_res),   //platform资源的数量,为1
 25                     .resource = s3c_led_res,
 26 };
 27 
 28 static int __init led_device_init(void)
 29 {
 30         int ret;
 31             ret = platform_device_register(&s3c_led_dev);
 32                 if(ret){
 33                     printk("device register failed!\n");
 34                     return ret;
 35                 }
 36 
 37                 printk("led device init\n");
 38                 return 0;
 39 }
 40 
 41 static void __exit led_device_exit(void)
 42 {
 43         platform_device_unregister(&s3c_led_dev);
 44         printk("led device bye!\n");
 45 }
 46 
 47 module_init(led_device_init);
 48 module_exit(led_device_exit);
 49 
 50 MODULE_LICENSE("GPL");
 51 MODULE_AUTHOR("wenhui");

 //driver.c
  1 #include<linux/module.h>
  2 #include<linux/init.h>
  3 
  4 #include<linux/platform_device.h>
  5 #include<asm/io.h>
  6 #include<asm/sizes.h>
  7 
  8 struct plat_led{
  9     unsigned long phys, virt;
 10     unsigned long gpkcon1, gpkdat, gpkup;
 11     unsigned long reg;
 12 };
 13 
 14 struct plat_led pled;
 15 
 16 int led_driver_probe(struct platform_device *pdev)
 17 {
 18     pled.phys = pdev->resource[0].start; /*0x7F008800*/
 19 
 20 /*不加强制性转换 会报 warning: assignment makes integer from pointer without a cast*/
 21     pled.virt = (unsigned long)ioremap(pled.phys, SZ_4K);
 22 
 23     pled.gpkcon1 = pled.virt + 0x4;
 24     pled.gpkdat = pled.virt + 0x8;
 25     pled.gpkup  = pled.virt + 0xc;
 26 
 27     //config
 28     pled.reg = ioread32(pled.gpkcon1); /*GPK4 LED1*/
 29     pled.reg &= ~(0xe<<0);           /*0001 output*/
 30     pled.reg |= (0x1<<0);
 31     iowrite32(pled.reg, pled.gpkcon1);
 32 
 33     //up
 34     pled.reg = ioread32(pled.gpkup);
 35     pled.reg &= ~(0x3<<8);           /*disable pull-up/down*/
 36     iowrite32(pled.reg, pled.gpkup);
 37 
 38     //dat
 39     pled.reg = ioread32(pled.gpkdat);
 40     pled.reg &= ~(0x1<<8);            /*low */
 41     iowrite32(pled.reg, pled.gpkdat);
 42 
 43     printk("led on\n");
 44     return 0;
 45 }
 46 
 47 int led_driver_remove(struct platform_device *pdev)
 48 {
 49     pled.reg = ioread32(pled.gpkdat);
 50     pled.reg |= (0x1<<8);
 51     iowrite32(pled.reg, pled.gpkdat);
 52 
 53     printk("led off\n");
 54     return 0;
 55 }
 56 
 57 struct platform_driver s3c_led_drv = {
 58     .probe = led_driver_probe,
 59     .remove = led_driver_remove,
 60     .driver = {
 61         .name = "plat_led", /*在/sys/ 中的驱动目录名字*/
 62     }
 63 };
 64 
 65 static int __init plat_led_init(void)
 66 {
 67     int ret;
 68     ret = platform_driver_register(&s3c_led_drv);
 69     if(ret){
 70         printk("led register failed!\n");
 71         return ret;
 72     }
 73     printk("led driver init\n");
 74 
 75     return 0;
 76 }
 77 
 78 static void __exit plat_led_exit(void)
 79 {
 80     platform_driver_unregister(&s3c_led_drv);
 81     printk("led driver exit");
 82 }
 83 
 84 module_init(plat_led_init);
 85 module_exit(plat_led_exit);
 86 
 87 MODULE_LICENSE("GPL");
 88 MODULE_AUTHOR("wenhui");

Makefile:

 
  1 KERDIR = /home/linux-2.6.36
  2 obj-m += device.o driver.o
  3 build: kernel_modules
  4 
  5 kernel_modules:
  6     make -C $(KERDIR) M=$(CURDIR) modules
  7 clean:
  8     make -C $(KERDIR) M=$(CURDIR) clean

一个led-platfrom设备驱动的例子_第3张图片

在上面的程序中,设备和驱动都是通过手动加载的,但有些情况下,内核已经为设备注册到platform总线上,只需要我们注册驱动就可以了。

接下来 下一篇中 将介绍一下设备的静态注册。



参考:http://blog.chinaunix.net/space.php?do=blog&uid=25014876&id=111745











你可能感兴趣的:(linux,struct,Module,table,makefile,resources)