一個led-platfrom裝置驅動的例子
什麼是platform匯流排
一個現實的linux裝置和驅動通常都需要掛接在一種總線上,比較常見的匯流排有USB、PCI匯流排等。但是,在嵌入式系統裡面,SoC系統中整合的獨立的外設控制器、掛接在SoC記憶體空間的外設卻不依附與此類匯流排。基於這樣的背景下,2.6核心加入了platform虛擬匯流排。platform機制將裝置本身的資源註冊進核心,有核心統一管理,在驅動程式使用這些資源時使用統一的介面,這樣提高了程式的可移植性。
下面就介紹一下platform匯流排、裝置和驅動1、platform匯流排:
這裡用的是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是否一致。
2、platform裝置:
同樣的,先看一下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 };
上面的結構體中先不介紹id、num_resources和resource。可以看到,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下。3、platform驅動:
先看一下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,並且實現了prob、remove等操作。其實,當核心需要呼叫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