Linux设备驱动(二)-字符设备驱

字符设备驱动是学习Linux驱动开发的基础入门,所以应从此开始入手。这一节主要记录字符设备驱动的注册与卸载函数。
在内核Linux-2.6中的字符设备是用cdev结构体来描述的,注册和卸载驱动也是由cdev来完成的。
这次我们来把分解注释和讲解其中的用法。

cdev结构体:

1
2
3
4
5
6
7
8
struct cdev {
	struct kobject kobj;//定义kobject结构体
	struct module *owner;//所属模块,新注册的设备必须由"THIS_MODULE"这个值来指定。
	const struct file_operations *ops;//设备对应的操作函数集
	struct list_head list;//定义list结构体
	dev_t dev;//设备号
	unsigned int count;//需要分配的设备数目
};

cdev开发的过程应包括:注册设备号、定义cdev设备结构体、动态申请cdev设备内存(此步可选)、初始化cdev设备、注册cdev设备、卸载cdev设备和释放设备号。

注册设备号的方法有两种:动态和静态。动态适用与不了解系统设备号的情况下使用,将注册设备号的任务交给内核来完成,缺点却是一旦注册的设备跟之前有所不同可能导致应用与此设备的程序做出修改。而静态则是自己完成设备号的注册,缺点可能和已经注册的设备号冲突。无论是动态还是静态注册设备号,最后都使用同样的函数来释放已注册的设备号。

静态分配设备号:

1
2
3
4
int register_chrdev_region(dev_t form, unsigned int count, const char *name)
//from:申请使用的设备号
//count:申请使用设备号数目
//name:设备名(体现在/proc/devices)

动态分配设备号:

1
2
3
4
5
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned int count, const char *name)
//dev:分配到的设备号
//baseminor:起始次设备号
//count:需要分配的设备号数目
//name:设备名(体现在/proc/devices)

释放设备号:

1
2
3
void unregister_chrdev_region(dev_t from, unsigned int count)
//from:注销的起始设备号
//count:需要注销的设备号数目

设备号的注册一般在模块开始的时候”module_init()”中就应该完成,如果已经成功注册设备号而没有完成字符设备cdev函数的注册,则应该释放已经注册的设备号。而设备号的释放一般在”module_exit()”的中完成,并且应该卸载字符cdev后再释放设备号。

定义cdev结构体:

1
2
static struct cdev *p;
//p:定义cdev结构体的指针

动态申请cdev设备内存:

1
struct cdev *cdev_alloc(void)

初始化cdev设备:

1
2
3
void cdev_init(struct cdev *p,const struct file_operations *fops)
//p:待初始化的cdev结构
//fops:设备对应的操作函数集,也就是宣告的file_operations结构

注册cdev设备:

1
2
3
4
int cdev_add(struct cdev *p, dev_t dev, unsigned int count)
//p:待添加到内核的cdev设备结构
//dev:设备号
//count:添加的设备个数

注销cdev设备:

1
2
int cdev_del(struct cdev *p)
//p:要注销的字符设备结构

下面是s3c2440的led设备驱动例子,结合上面函数了解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <mach/regs-gpio.h>
 
#define DEVICE_NAME "leds"
 
static struct cdev *leds_cdev;
static dev_t devno;
static unsigned int leds_table[]=
{
	S3C2410_GPE(14),
	S3C2410_GPE(15),
	S3C2410_GPH(9),
	S3C2410_GPH(10),
};
 
static unsigned int leds_cfg_table[]=
{
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
	S3C2410_GPIO_OUTPUT,
};
 
static	long leds_ioctl(
		struct file *file,
		unsigned int cmd,
		unsigned long arg)
{
	switch(cmd){
		case 0:
			if(arg > 4){
				return -EINVAL;}
			s3c2410_gpio_cfgpin(leds_table[arg],leds_cfg_table[arg]);
			s3c2410_gpio_setpin(leds_table[arg],!cmd);
		case 1:
			if(arg > 4){
				return -EINVAL;}
			s3c2410_gpio_cfgpin(leds_table[arg],leds_cfg_table[arg]);
			s3c2410_gpio_setpin(leds_table[arg],!cmd);
		return 0;
		default:
			return -EINVAL;
		}
}
 
static struct file_operations leds_fops=
{
	.owner = THIS_MODULE,
	.unlocked_ioctl = leds_ioctl,
};
 
static int __init leds_init(void)
{
	int ret;
	int err;
 
	ret= alloc_chrdev_region(&devno,0,1,"leds");
	if (ret<0)
	return ret;
 
	leds_cdev=cdev_alloc();
	cdev_init(leds_cdev,&leds_fops);
	err=cdev_add(leds_cdev,devno,1);
	if(err)
	{
		unregister_chrdev_region(devno,1);
		return -EFAULT;
	}
 
	printk(DEVICE_NAME "\tinitialized!\n");
	return 0;
}
 
static void __exit leds_exit(void)
{
	cdev_del(leds_cdev);
	unregister_chrdev_region(devno,1);
	printk(DEVICE_NAME "\tunloaded\n");
}
 
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yayi");

发表评论

电子邮件地址不会被公开。 必填项已用*标注