之前是老设备驱动,现在我们要用新字符驱动,据说更好更方便
新字符设备驱动原理
分配和释放设备号
如果没有指定设备号的话就使用如下函数来申请设备号:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
注销字符设备 之 后 要 释 放 掉 设 备 号 , 不 管 是 通 过
alloc_chrdev_region
函数还是
register_chrdev_region
函数申请的设备号,统一使用如下释放函数:
void unregister_chrdev_region(dev_t from, unsigned count)
新的字符设备注册方法
字符设备结构
在
Linux
中使用
cdev
结构体表示一个字符设备,
cdev
结构体在
include/linux/cdev.h
文件中
的定义如下:
示例代码 42.1.2.1 cdev 结构体
1
struct
cdev
{
2
struct
kobject kobj
;
3
struct
module
*
owner
;
4
const struct
file_operations
*
ops
;
5
struct
list_head list
;
6
dev_t
dev
;
7
unsigned int
count
;
8
};
在
cdev
中有两个重要的成员变量:
ops
和
dev
,这两个就是字符设备文件操作函数集合
file_operations
以及设备号
dev_t
。编写字符设备驱动之前需要定义一个
cdev
结构体变量,这个
变量就表示一个字符设备,如下所示:
struct cdev test_cdev;
cdev_init
函数
定义好
cdev
变量以后就要使用
cdev_init
函数对其进行初始化,
cdev_init
函数原型如下:
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
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)
cdev_del
函数
卸载驱动的时候一定要使用
cdev_del
函数从
Linux
内核中删除相应的字符设备,
cdev_del
函数原型如下:
void cdev_del(struct cdev *p)
自动创建设备节点
当我们使用
modprobe
加载驱动程序以后还需要使用命令 “mknod
”手动创建设备节点。实现自动创建节点的功能后我们就可以只用modprode函数,然后系统会自己在/dev自动创建节点了。
mdev
机制
所以在嵌入式
Linux 中我们使用mdev 来实现设备节点文件的自动创建与删除,工作原理我们就不详细探讨了,我们重点来学习一下如何通过
mdev
来实现设备文件节点的自动创建与删除。
创建和删除类
自动创建设备节点的工作是在驱动程序的入口函数中完成的,一般在
cdev_add
函数后面添
加自动创建设备节点相关代码。首先要创建一个
class
类,class 是个结构体,
定义在文件 include/linux/device.h 里面。class_create
是类创建函数
卸载驱动程序的时候需要删除掉类,类删除函数为
class_destroy
创建设备
上一小节创建好类以后还不能实现自动创建设备节点,我们还需要在这个类下创建一个设
备。使用
device_create
函数在类下面创建设备。
device_create
是个可变参数函数,参数
class
就是设备要创建哪个类下面;参数
parent
是父
设备,一般为
NULL
,也就是没有父设备;参数
devt
是设备号;参数
drvdata
是设备可能会使用
的一些数据,一般为
NULL
;参数
fmt
是设备名字,如果设置
fmt=xxx
的话,就会生成
/dev/xxx
这个设备文件。返回值就是创建好的设备。
同样的,卸载驱动的时候需要删除掉创建的设备,设备删除函数为
device_destroy
,函数原
型如下:
void device_destroy(struct class *class, dev_t devt)
参考示例
在驱动入口函数里面创建类和设备,在驱动出口函数里面删除类和设备,参考示例如下:
示例代码 42.2.3.1 创建/删除类/设备参考代码
1
struct
class
*
class
;
/*
类
*/
2
struct
device
*
device
;
/*
设备
*/
3
dev_t devid
;
/*
设备号
*/
4
5
/*
驱动入口函数
*/
6
static int
__init led_init
(
void
)
7
{
8
/*
创建类
*/
9
class
=
class_create
(
THIS_MODULE
,
"xxx"
);
10
/*
创建设备
*/
11
device
=
device_create
(
class
,
NULL
,
devid
,
NULL
,
"xxx"
);
12
return
0
;
13
}
14
15
/*
驱动出口函数
*/
16
static void
__exit led_exit
(
void
)
17
{
18
/*
删除设备
*/
19
device_destroy
(
newchrled
.
class
,
newchrled
.
devid
);
20
/*
删除类
*/
21
class_destroy
(
newchrled
.
class
);
22
}
23
24
module_init
(
led_init
);
25
module_exit
(
led_exit
);
设置文件私有数据
示例代码 42.3.2 设备结构体作为私有数据
/*
设备结构体
*/
1
struct
test_dev
{
2
dev_t devid
;
/*
设备号
*/
3
struct
cdev cdev
;
/* cdev */
4
struct
class
*
class
;
/*
类
*/
5
struct
device
*
device
;
/*
设备
*/
6
int
major
;
/*
主设备号
*/
7
int
minor
;
/*
次设备号
*/
8
};
9
10
struct
test_dev testdev
;
11
12
/* open
函数
*/
13
static int
test_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
14
{
15
filp
->
private_data
= &
testdev
;
/*
设置私有数据
*/
16
return
0
;
17
}
在
open
函数里面设置好私有数据以后,在
write
、
read
、
close
等函数中直接读取
private_data
即可得到设备结构体。