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

2016年(20)

2015年(56)

分类: 嵌入式

2015-07-03 21:47:16

字符设备驱动(新)

15年6月6月18月18日18日12:10:17

在之前的字符设备驱动程序中,驱动程序注册用register_chrdev()函数来完成,这个是在2.4内核中使用的方法。这种方法有个缺点,只能使用256个设备,比如对于同一个主设备号,次设备号0255都属于这个主设备号的设备。在2.6内核中,抛弃了这种方法,目的是想把同一个主设备号,但是不同的次设备号分配给不同的设备。

先看代码:

1

2 #include <linux/module.h>

3 #include <linux/kernel.h>

4 #include <linux/fs.h>

5 #include <linux/init.h>

6 #include <linux/delay.h>

7 #include <linux/irq.h>

8 #include <asm/uaccess.h>

9 #include <asm/irq.h>

10 #include <asm/io.h>

11 #include <asm/arch/regs-gpio.h>

12 #include <asm/hardware.h>

13 #include <linux/poll.h>

14 #include <linux/cdev.h>

15

16 static int major;

17 static int hello_open(struct inode *inode, struct file *file)

18 {

19 printk("hello_open\n");

20 return 0;

21 }

22

23 static int hello2_open(struct inode *inode, struct file *file)

24 {

25 printk("hello2_open\n");

26 return 0;

27 }

28

29 static struct file_operations hello_fops = {

30 .owner = THIS_MODULE,

31 .open = hello_open,

32 };

33

34 static struct file_operations hello2_fops = {

35 .owner = THIS_MODULE,

36 .open = hello2_open,

37 };

38

39 #define HELLO_CNT 2

40

41 static struct cdev hello_cdev;

42 static struct cdev hello2_cdev;

43 static struct class *cls;

44

45 static int hello_init(void)

46 {

47 dev_t devid;

48 int i = 0;

49 #if 0

50 major = register_chrdev(0, "hello", hello_fops);

51 #else

52 if (major)

53 {

54 devid = MKDEV(major, 0);

55 register_chrdev_region(devid, HELLO_CNT, "hello");

56 }

57 else

58 {

59 alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");

60 major = MAJOR(devid);

61 }

62

63 cdev_init(&hello_cdev, &hello_fops);

64 cdev_add(&hello_cdev, devid, HELLO_CNT);

65

66 devid = MKDEV(major, 2);

67 register_chrdev_region(devid, 1, "hello2");

68 cdev_init(&hello2_cdev, &hello2_fops);

69 cdev_add(&hello2_cdev, devid, 1);

70 #endif

71

72 cls = class_create(THIS_MODULE, "hello");

73 for(i = 0; i < 4; i++)

74 {

75 class_device_create(cls, NULL, MKDEV(major, i), NULL, "hello%d", i);

76 }

77

78 return 0;

79 }

80

81 static void hello_exit(void)

82 {

83 int i;

84 class_destroy(cls);

85 for(i = 0; i < 4; i++)

86 {

87 class_device_destroy(cls, MKDEV(major, i));

88 }

89

90 cdev_del(&hello_cdev);

91 unregister_chrdev_region(0, HELLO_CNT);

92

93 cdev_del(&hello2_cdev);

94 unregister_chrdev_region(2, 1);

95

96 }

97

98 module_init(hello_init);

99 module_exit(hello_exit);

100

101 MODULE_LICENSE("GPL");

102


先讲解一下基础知识:

在内核中,用dev_t类型来保存设备编号,在2.6内核中,dev_t是一个32位的数,其中12位用来表示主设备号,其余的20位来表示次设备号。要获得dev_t的主设备号或次设备号,应使用:

MAJOR(dev_t dev);

MINOR(dev_t dev);

相反,如果需要将主设备号和次设备号转换成dev_t类型,则使用:

MKDEV(int major, int minor);


如果知道或者指定了设备的主设备号,在注册的时候使用register_chrdev_region()函数,函数原型如下:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

第一个参数是次设备号的起始值,第二个参数是想申请的个数,从起始值开始的个数,第三个参数是设备的名称。




如果提前不知道设备将要使用哪些主设备号,则需要使用动态分配函数:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)


分配主设备号的最佳方式是:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。所以推荐使用下面类似的方法来进行分配:

if (major)

{

devid = MKDEV(major, 0);

register_chrdev_region(devid, HELLO_CNT, "hello");

}

else

{

alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");

major = MAJOR(devid);

}


每个字符设备对应一个cdev结构体,结构体如下:

struct cdev {

struct kobject kobj;

struct module *owner;

const struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count;

};


注册完以后,调用 cdev_init()和 cdev_add()函数将字符设备的操作函数file_operations结构体和cdev结合在一起:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

本例中用下面两条语句:

cdev_init(&hello_cdev, &hello_fops);

cdev_add(&hello_cdev, devid, HELLO_CNT);


下面再来理解这个程序的目的:想要动态分配一个主设备号给两个设备,即两个设备对应同一个主设备号,但是次设备号不同,次设备号01分配给hello,次设备号2分配给hello2


通过一个测试程序来测试是否能够这样分配:

1 #include <stdio.h>

2 #include <stdlib.h>

3 #include <string.h>

4 #include <sys/types.h>

5 #include <sys/stat.h>

6 #include <fcntl.h>

7

8

9 /*

10 * hello_test /dev/hello0

11 */

12

13 void print_usage(char *file)

14 {

15 printf("%s <dev>\n", file);

16 }

17

18 int main(int argc, char **argv)

19 {

20 int fd;

21 if (argc != 2)

22 {

23 print_usage(argv[0]);

24 return 0;

25 }

26

27 fd = open(argv[1], O_RDWR);

28 if (fd < 0)

29 printf("can't open %s\n", argv[1]);

30 else

31 printf("can open %s\n", argv[1]);

32

33 return 0;

34 }

可以看到,hellohello2的主设备号相同。




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

上一篇:ethhdr结构体详解

下一篇:声卡驱动程序

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