Chinaunix首页 | 论坛 | 博客
  • 博客访问: 292492
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

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_deviceplatform_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)

{

}

只是将原来注册和销毁字符设备的工作移交到proberemove函数中,同时模块加载和卸载函数仅仅通过在出入口函数中调用platform_driver_registerplatform_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_registerplatform_device_unregister函数来完成。


4.3 一定要注意,platform_driver里面driver结构体里面的.nameplatform_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;

};


我们通常关心startendflags字段,分别表示资源的开始值,结束值和类型。flags可以为IORESOURCE_IOIORESOURCE_MEMIORESOURCE_IRQIORESOURCE_DMA等。Startend所表达的意思根据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 \n", argv[0]);

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 }


阅读(1498) | 评论(0) | 转发(0) |
0

上一篇:输入子系统

下一篇:LCD驱动程序

给主人留下些什么吧!~~