Linux 应用程序对驱动程序的调用如图所示
open函数调用过程:
驱动的加载与卸载
Linux
驱动有两种运行方式,第一种就是将驱动编译进
Linux
内核中,这样当
Linux
内核启
动的时候就会自动运行驱动程序。第二种就是将驱动编译成模块
(Linux
下模块扩展名为
.ko)
,在
Linux
内核启动以后使用“
insmod
”命令加载驱动模块。在调试驱动的时候一般都选择将其编译
为模块,这样我们修改驱动以后只需要编译一下驱动代码即可,不需要编译整个
Linux
代码。
而且在调试的时候只需要加载或者卸载驱动模块即可,不需要重启整个系统。
模块的加载与卸载函数:
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
module_init
函数用来向
Linux
内核注册一个模块加载函数,参数
xxx_init
就是需要注册的
具体函数,当使用“
insmod
”命令加载驱动的时候,
xxx_init
这个函数就会被调用。
module_exit()
函数用来向
Linux
内核注册一个模块卸载函数,参数
xxx_exit
就是需要注册的具体函数,当使
用“
rmmod
”命令卸载具体驱动的时候
xxx_exit
函数就会被调用。字符设备驱动模块加载和卸
载模板如下所示:
驱动编译完成以后扩展名为
.ko
,有两种命令可以加载驱动模块:
insmod
和
modprobe。
insmod 命令不能解决模块的依赖关系,使用insmod加载多个ko要注意依赖关系先后顺序,modprobe 会分析模块的依赖关系,然后会将所有的依赖模块都加载到内核中。
驱动模块的卸载使用命令“
rmmod
”即可,比如要卸载
drv.ko
,使用如下命令即可:
rmmod drv.ko 也可以使用“modprobe -r
”命令卸载驱动,比如要卸载
drv.ko
,命令如下:
modprobe -r drv.ko。但是modprobe -r会解除多个ko之间的依赖关系,所以推荐使用rmmod。
字符设备注册与注销
字符设备的注册和注销函数原型如下所示
:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)
函数样板
1
/*
打开设备
*/
2
static int
chrtest_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
3
{
4
/*
用户实现具体功能
*/
5
return
0
;
6
}
7
8
/*
从设备读取
*/
9
static
ssize_t chrtest_read
(
struct
file
*
filp
,
char
__user
*
buf
,
size_t
cnt
,
loff_t
*
offt
)
10
{
11
/*
用户实现具体功能
*/
12
return
0
;
13
}
14
15
/*
向设备写数据
*/
16
static
ssize_t chrtest_write
(
struct
file
*
filp
,
const char
__user
*
buf
,
size_t
cnt
,
loff_t
*
offt
)
17
{
18
/*
用户实现具体功能
*/
19
return
0
;
20
}
22
/*
关闭
/
释放设备
*/
23
static int
chrtest_release
(
struct
inode
*
inode
,
struct
file
*
filp
)
24
{
25
/*
用户实现具体功能
*/
26
return
0
;
27
}
28
29
static struct
file_operations test_fops
= {
30
.
owner
=
THIS_MODULE
,
31
.
open
=
chrtest_open
,
32
.
read
=
chrtest_read
,
33
.
write
=
chrtest_write
,
34
.
release
=
chrtest_release
,
35
};
36
37
/*
驱动入口函数
*/
38
static int
__init xxx_init
(
void
)
39
{
40
/* 入口函数具体内容
*/
41
int
retvalue
=
0
;
42
43
/* 注册字符设备驱动
*/
44
retvalue
=
register_chrdev
(
200
,
"chrtest"
, &
test_fops
);
45
if
(
retvalue
<
0
){
46
/* 字符设备注册失败
,
自行处理
*/
47
}
48
return
0
;
49
}
50
51
/*
驱动出口函数
*/
52
static void
__exit xxx_exit
(
void
)
53
{
54
/* 注销字符设备驱动
*/
55
unregister_chrdev
(
200
,
"chrtest"
);
56
}
57
58
/*
将上面两个函数指定为驱动的入口和出口函数
*/
59
module_init
(
xxx_init
);//注册模块加载函数
60
module_exit
(
xxx_exit
);//注册模块卸载函数
62
MODULE_LICENSE
(
"GPL"
); //添加证书
63
MODULE_AUTHOR
(
"zipeng"
); //添加作者
编译设备驱动模块
使用如下Makefile文件
1
KERNELDIR
:=
/home/zipeng/linux/myKernel/linux-imx_rel_imx_4.1.15_2.1.0_ga
2
CURRENT_PATH
:=
$(shell pwd)
3
obj-m
:=
chrdevbase.o
4
5
build
:
kernel_modules
6
7
kernel_modules
:
8
$(MAKE)
-C
$(KERNELDIR) M
=
$(CURRENT_PATH)
modules
9
clean
:
10
$(MAKE)
-C
$(KERNELDIR) M
=
$(CURRENT_PATH)
clean
命令:make
编写编译测试App
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = {"usr data!"};
/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
char readbuf[100], writebuf[100];
if(argc != 3){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("Can't open file %s\r\n", filename);
return -1;
}
if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */
retvalue = read(fd, readbuf, 50);
if(retvalue < 0){
printf("read file %s failed!\r\n", filename);
}else{
/* 读取成功,打印出读取成功的数据 */
printf("read data:%s\r\n",readbuf);
}
}
if(atoi(argv[2]) == 2){
/* 向设备驱动写数据 */
memcpy(writebuf, usrdata, sizeof(usrdata));
retvalue = write(fd, writebuf, 50);
if(retvalue < 0){
printf("write file %s failed!\r\n", filename);
}
}
/* 关闭设备 */
retvalue = close(fd);
if(retvalue < 0){
printf("Can't close file %s\r\n", filename);
return -1;
}
return 0;
}
编译:
arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
生成在ARM运行的程序chrdevbaseApp
将编译生成的.ko文件和APP可执行文件拷贝到nfs/rootfs指定目录下
sudo cp chrdevbase.ko chrdevbaseApp /home/zuozhongkai/linux/nfs/rootfs/lib/modules/4.1.15/ -f
挂载根文件系统测试
拷贝完成之后重新启动ARM开发板,可以在对应目录下看到新添加的ko模块文件和APP可执行文件。
加载驱动模块
输入如下命令加
载
chrdevbase.ko:
insmod chrdevbase.ko 或 modprobe chrdevbase.ko
输入lsmod查看当前系统存在的模块
输入cat /proc/devices查看系统所有设备
创建设备节点文件
mknod /dev/chrdevbase c 200 0
chrdevbase设备操作测试
./chrdevbaseApp /dev/chrdevbase 1 //通过执行chrdevbaseApp对/dev/chrdevbase设备写1
./chrdevbaseApp /dev/chrdevbase 2
卸载驱动模块
rmmod chrdevbase.ko
lsmod命令查看是否还有该驱动