分类: 嵌入式
2015-06-01 19:13:16
驱动程序分层分离概念及总线驱动设备模型
15年5月18日21:20:15
(1)先讲总线,设备,驱动三者的关系:
在linux2.6中,总线将设备和驱动绑定,系统每注册一个设备的时候,会寻找与之匹配的驱动。相反的,如果在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
在linux设备模型中,有很多总线,并且这些总线都有自己的子系统。用bus_type结构表示总线,下面列出了部分bus_type结构的成员:
struct bus_type {
const char * name;
struct module * owner;
int (*match)(struct device * dev, struct device_driver * drv);
int (*uevent)(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
}
只有非常少的bus_type成员需要初始化(如 .name .match .hotplug等),大多数由设备模型核心所控制。对于新的总线,需要调用bus_register函数进行注册,删除的时候,需要使用bus_unregister函数。
(2)在linux中,发明了一种platform的虚拟总线,相应的设备为platform_device,相应的驱动为platform_driver。注意:platform只是系统提供的一种附加手段,而并不是与字符设备,块设备,网络设备并列的概念。我们通过使用platform总线来学习这一块知识。因为这个总线已经存在于系统中,所以我们不需要注册删除等操作,直接调用即可。
(3)写这个程序的核心就是分别构造platform_device和platform_driver两个结构体。下面列出内核源码中对这两个结构体的定义:
struct platform_device {
const char * name; /* 设备名称 */
u32 id;
struct device dev;
u32 num_resources; /* 设备所使用各类资源数量 */
struct resource *resource; /* 资源 */
};
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
驱动程序中,需要将platform_driver 结构体挂接到platform总线上面,而设备同样需要将 platform_device 结构体挂接到platform总线上面。
(4)下面分别分析驱动程序跟设备程序。
4.1 驱动程序:
为完成将platform_driver 结构体挂接到platform总线的工作,需要在原始的字符设备驱动中套一层platform_driver 的外壳。这个通过platform_driver 结构体中的probe函数来完成。同时,卸载的时候用remove函数。
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
};
static int led_probe(struct platform_device *pdev)
{
}
static int led_remove(struct platform_device *pdev)
{
}
只是将原来注册和销毁字符设备的工作移交到probe和remove函数中,同时模块加载和卸载函数仅仅通过在出入口函数中调用platform_driver_register和platform_driver_unregister函数来完成。
注意在 led_probe函数中通过platform_get_resource(pdev, IORESOURCE_MEM, 0)函数来获取设备的资源。。。。。。。这个在后面再讲。
4.2 设备程序:
static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
};
static struct resource led_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 4,
.end = 4,
.flags = IORESOURCE_IRQ,
}
};
在设备程序中,通过构建 platform_device结构体,就完成了将platform_device 结构体挂接到platform总线上的工作。同时,模块加载和卸载函数仅仅通过在出入口函数中调用platform_device_register和platform_device_unregister函数来完成。
4.3 一定要注意,platform_driver里面driver结构体里面的.name和platform_device里面的.name一定要相同,总线的match函数仅仅是通过驱动程序和设备的名字来比较匹配的。如果这一步错了的话,肯定匹配不上。。。。我当时粗心写错了,检查了一下午才检查出来。。。。。
4.4 设备资源和数据:
在platform_device结构体中,有一项为resources,描述了platform_device的资源,资源本身用resource结构体来描述。Resource结构体定义如下所示:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
我们通常关心start,end,flags字段,分别表示资源的开始值,结束值和类型。flags可以为IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等。Start和end所表达的意思根据flags的不同而改变。
而在驱动程序中,需要得到设备的资源情况的话,就调用platform_get_resource(pdev, IORESOURCE_MEM, 0)这个API函数来获得,我们可以看到,在驱动程序的probe函数中就有这样的用法。下面写出platform_get_resource函数的原型:
platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)
使用分层分离有很多好处,具体的现在也不懂,就不写了~~~
(5)下面附上程序的代码:
设备程序:
1
#include
2
#include
3
4
#include
5
6
#include
7
#include
8
#include
9
#include
10
#include
11
#include
12
#include
13
#include
14
15 /* 分配/设置/注册一个platform_device结构体 */
16
17 static struct resource led_resource[] = {
18 [0] = {
19 .start = 0x56000050,
20 .end = 0x56000050 + 8 - 1,
21 .flags = IORESOURCE_MEM,
22 },
23 [1] = {
24 .start = 4,
25 .end = 4,
26 .flags = IORESOURCE_IRQ,
27 }
28
29 };
30
31 static void led_release(struct device *dev)
32 {
33 }
34
35 static struct platform_device led_dev = {
36 .name = "myled",
37 .id = -1,
38 .num_resources = ARRAY_SIZE(led_resource),
39 .resource = led_resource,
40 .dev = {
41 .release = led_release,
42 },
43 };
44
45 static int led_dev_init(void)
46 {
47 platform_device_register(&led_dev);
48 return 0;
49 }
50
51 static void led_dev_exit(void)
52 {
53 platform_device_unregister(&led_dev);
54 }
55
56
57 module_init(led_dev_init);
58 module_exit(led_dev_exit);
59
60 MODULE_LICENSE("GPL");
驱动程序:
1
#include
2
#include
3
4
#include
5
#include
6
#include
7
#include
8
#include
9
#include
10
#include
11
#include
12
#include
13
#include
14
#include
15
#include
16
#include
17
#include
18
19 static int major;
20
21 static struct class *cls;
22 static volatile unsigned long *gpio_con;
23 static volatile unsigned long *gpio_dat;
24 static int pin;
25
26 static int led_open(struct inode *inode, struct file *file)
27 {
28 //printk("first_drv_open\n");
29
30 *gpio_con &= ~(0x3<<(pin*2));
31 *gpio_con |= (0x1<<(pin*2));
32 return 0;
33 }
34
35 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
36 {
37 int val;
38
39 //printk("first_drv_write\n");
40
41 copy_from_user(&val, buf, count);
42
43 if (val == 1)
44 {
45
*gpio_dat &= ~(1<
46 }
47 else
48 {
49
*gpio_dat |= (1<
50 }
51
52 return 0;
53 }
54
55 static struct file_operations led_fops = {
56 .owner = THIS_MODULE,
57 .open = led_open,
58 .write = led_write,
59 };
60
61 static int led_probe(struct platform_device *pdev)
62 {
63 struct resource *res;
64
65 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
66 gpio_con = ioremap(res->start, res->end - res->start + 1);
67 gpio_dat = gpio_con + 1;
68
69 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
70 pin = res->start;
71
72 printk("led_probe , found led!\n");
73
74 major = register_chrdev(0, "myled", &led_fops);
75
76 cls = class_create(THIS_MODULE, "myled");
77
78 class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led");
79
80 return 0;
81 }
82
83 static int led_remove(struct platform_device *pdev)
84 {
85 printk("led_remove , remove led!\n");
86
87 unregister_chrdev(major, "myled");
88
89 class_destroy(cls);
90 class_device_destroy(cls, MKDEV(major, 0));
91 iounmap(gpio_con);
92
93 return 0;
94 }
95
96 struct platform_driver led_drv = {
97 .probe = led_probe,
98 .remove = led_remove,
99 .driver = {
100 .name = "myled",
101 }
102 };
103
104 static int led_drv_init(void)
105 {
106 platform_driver_register(&led_drv);
107 return 0;
108 }
109
110 static void led_drv_exit(void)
111 {
112 platform_driver_unregister(&led_drv);
113 }
114
115
116 module_init(led_drv_init);
117 module_exit(led_drv_exit);
118
119 MODULE_LICENSE("GPL");
120
测试程序为之前的一个点灯程序:
1
2
#include
3
#include
4
#include
5
#include
6
7 /* led_test on
8 * led_test off
9 */
10 int main(int argc, char **argv)
11 {
12 int fd;
13 int val = 1;
14 fd = open("/dev/led", O_RDWR);
15 if (fd < 0)
16 {
17 printf("can't open!\n");
18 }
19 if (argc != 2)
20 {
21 printf("Usage :\n");
22
printf("%s
23 return 0;
24 }
25
26 if (strcmp(argv[1], "on") == 0)
27 {
28 val = 1;
29 }
30 else
31 {
32 val = 0;
33 }
34
35 write(fd, &val, 4);
36 return 0;
37 }