1. 程式人生 > >一個led-platfrom裝置驅動的例子

一個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開發板)

圖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



在上面的程式中,裝置和驅動都是通過手動載入的,但有些情況下,核心已經為設備註冊到platform總線上,只需要我們註冊驅動就可以了。

接下來 下一篇中 將介紹一下裝置的靜態註冊。


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