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

http://blog.csdn.net/wenhui_/article/details/6829513

是platform总线


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


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


1、platform总线:

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

linux在系统启动时就注册了platform总线,看内核代码:
[html] view plain
copy

/*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:

[html] view plain
copy

/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的注册和注销使用以下函数:

[html] view plain
copy

/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:

[html] view plain
copy

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的注册和注销使用以下函数:

[html] view plain
copy

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灯亮 当引脚为高电平时灯灭

[html] view plain
copy

//device.c
nbsp; 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");


[html] view plain
copy

//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:

[html] view plain
copy


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总线上,只需要我们注册驱动就可以了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值