linux驱动学习笔记2---新字符设备驱动开发

分配和释放设备号

如果没有指定设备号的话就使用如下函数来 申请设备号:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
函数 alloc_chrdev_region 用于申请设备号,此函数有 4 个参数:
dev: 保存申请到的设备号。
baseminor: 次设备号起始地址, alloc_chrdev_region 可以申请一段连续的多个设备号,这些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递增。一般 baseminor 为 0,也就是说次设备号从 0 开始。
count: 要申请的设备号数量。
name:设备名字

如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
from: 是要申请的起始设备号,也就是给定的设备号;
count :是要申请的数量,一般都是一个;
name: 是设备名字

注销字符设备之后要释放掉设备号
void unregister_chrdev_region(dev_t from, unsigned count)

新的字符设备注册方法

1.字符设备结构

在linux中使用cdev结构体表示一个字符设备,cdev结构体在include/linux/cdev.h文件中的定义如下:

12	struct cdev {
13     	struct kobject kobj;
14     	struct module *owner;
15     	const struct file_operations *ops;
16     	struct list_head list;
17     	dev_t dev;
18    	unsigned int count;
19 	};

在cdev中有两个重要的成员变量:ops和dev,这两个就是字符设备文件操作函数集合file_operations以及设备号dev_t
编写字符设备驱动之前需要定义一个 cdev 结构体变量,这个变量就表示一个字符设备,如下所示:
struct cdev test_cdev;

2.cdev_init函数

定义好cdev变量以后就要使用cdev_init函数对其进行初始化,cdev_inti函数原型如下:
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
举个栗子

struct cdev cdev;

/*设备操作函数*/
static struct file_operation test_fops = {
	.owner = THIS_MODULE,
	....
};

cdev.owner = THIS_MODULE;
cdev_init(&cdev,&test_fops);	//初始化cdev结构体变量

3.cdev_add 函数

cdev_add函数用于向linux系统添加字符设备(cdev结构体变量),首先使用cdev_init函数完成对cdev结构体变量的初始化,然后使用cdev_add函数向linux系统添加这个字符设备。cdev_add函数原型如下:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
p: 指向要添加的字符设备(cdev结构体变量)
dev: 就是设备所使用的设备号
count:要添加的设备数量

struct cdev cdev;

/*设备操作函数*/
static struct file_operations test_fops = {
	.owner = THIS_MODULE;
	/*其他具体的初始项*/
};

testcdev.owner = THIS_MODULE;
cdev_init(&cdev, &test_fops); /* 初始化 cdev 结构体变量 */
cdev_add(&cdev, devid, 1); /* 添加字符设备 */

4.cdev_del函数

卸载驱动的时候一定要使用 cdev_del 函数从 Linux 内核中删除相应的字符设备, cdev_del函数原型如下:
void cdev_del(struct cdev *p)
p:就是要删除的字符设备

cdev_del(&testcdev); /* 删除 cdev */

自动创建设备节点

在”老“的字符设备驱动中,当使用modprobe加载驱动程序以后还要使用命令“mknod”手动创建设备节点,但是在xx版本以后的内核可以使用mdev机制,当使用modprobe加载驱动模块成功之后就会自动在/dev目录下创建对应的设备文件。

1. mdev机制

udev 是一个用户程序,在 Linux 下通过 udev 来实现设备文件的创建与删除, udev 可以检测系统中硬件设备状态,可以根据系统中硬件设备状态来创建或者删除设备文件。比如使用modprobe 命令成功加载驱动模块以后就自动在/dev 目录下创建对应的设备节点文件,使用rmmod 命令卸载驱动模块以后就删除掉/dev 目录下的设备节点文件。 使用 busybox 构建根文件系统的时候, busybox 会创建一个 udev 的简化版本—mdev,所以在嵌入式Linux 中我们使用mdev 来实现设备节点文件的自动创建与删除(复制粘贴过来的

2.创建和删除类

自动创建设备节点的工作是在驱动入口函数完成的,一般在cdev_add函数后面添加自动创建设备节点。
首先要创建一个class类,class是个结构体,定义在文件include/linux/device.h里面

 353 struct class {
 354     const char      *name;
 355     struct module       *owner;
 356 
 357     struct class_attribute      *class_attrs;
 358     const struct attribute_group    **dev_groups;
 359     struct kobject          *dev_kobj;
 360 
 361     int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
 362     char *(*devnode)(struct device *dev, umode_t *mode);
 363 
 364     void (*class_release)(struct class *class);
 365     void (*dev_release)(struct device *dev);
 366 
 367     int (*suspend)(struct device *dev, pm_message_t state);
 368     int (*resume)(struct device *dev);
 369 
 370     const struct kobj_ns_type_operations *ns_type;
 371     const void *(*namespace)(struct device *dev);
 372 
 373     const struct dev_pm_ops *pm;
 374 
 375     struct subsys_private *p;
 376 };

class_create是类创建函数,class_create是宏定义,内容如下:

480 extern struct class * __must_check __class_create(struct module *owner,
481                           const char *name,
482                           struct lock_class_key *key);

487 #define class_create(owner, name)       \
488 ({                      \
489     static struct lock_class_key __key; \
490     __class_create(owner, name, &__key);    \
491 })

将宏class_create展开以后内容如下:
struct class * class_create(struct module *owner,const char *name)
owne r: 一般为THIS_MODULE
name:类名字
返回值:指向结构体 class 的指针,也就是创建的类。

卸载驱动程序的时候需要删除掉类,类删除函数为 class_destroy,函数原型如下:
void class_destroy(struct class *cls);
参数 cls 就是要删除的类。

3.创建设备

创建好类以后还不能实现自动创建设备节点,还要在这个类下创建一个设备。使用device_create函数在类下面创建设备,这个函数定义在include/linux/device.h中,device_create函数原型如下:
struct device *device_create(struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...)
device_create是一个可变参数函数。
class: 设备要创建哪个类下面;
parent:父设备,一般为NULL
devt:设备号
drvdata:设备可能会使用的一些数据,一般为NULL
fmt:设备名字,如果设置fmt=xxx的话,就会生成/dev/xxx这个设备文件
返回值:创建好的设备

卸载驱动的时候需要删除创建的设备,设备删除函数为device_destroy,函数原型如下:
void device_destroy(struct class *class, dev_t devt)
参数 classs 是要删除的设备所处的类,参数 devt 是要删除的设备号
举个栗子

//说明:xxx指的是设备名字
struct class *class;		//类
struct device *device;		//设备
dev_t devid;				//设备号

//驱动入口函数
static int __init xxx_init(void)
{
	//创建类
	class = class_create(THIS_MODULE,"xxx");
	//创建设备
	device = device_create(class,NULL,devid,NULL,"xxx");
	return 0;
}
//驱动出口函数
static void __exit led_exit(void)
{
	//删除设备
	device_destroy(class,devid);
	//删除类
	class_destroy(class);
}

module_init(xxx_init);
module_exit(xxx_exit);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章