博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【驱动】linux设备驱动·字符设备驱动开发
阅读量:5862 次
发布时间:2019-06-19

本文共 3370 字,大约阅读时间需要 11 分钟。

Preface

   前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段!

   《linux设备驱动入门篇》:

   《linux设备驱动扫盲篇》:

   《fedora下的字符设备驱动开发


开发一个基本的字符设备驱动

   在Linux内核驱动中,字符设备是最基本的设备驱动。

字符设备包括了设备最基本的操作,如打开设备、关闭设备、I/O控制等。

功能建立一个名为GlobalChar的虚拟设备,设备内部只有一个全局变量供用户操作。设备提供了读函数读取全局变量的值并且返回给用户写函数把用户设定的值写入全局变量。

代码如下:

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
//GlobalCharDev.c
#include <linux/fs.h>               
#include <linux/module.h>
#include <asm/uaccess.h>            
#include <linux/init.h>
MODULE_LICENSE(
"GPL"
);
MODULE_AUTHOR(
"mystery"
);
#define DEV_NAME "GlobalChar"
static 
ssize_t GlobalRead(
struct 
file *, 
char 
*, 
size_t
, loff_t *);
static 
ssize_t GlobalWrite(
struct 
file *, 
const 
char 
*, 
size_t
, loff_t *);
                                                                                                                                                                                                                                                                                            
static 
int 
char_major = 0;                  
static 
int 
GlobalData = 0;  
// "GlobalData" 设备的全局变量
//初始化字符设备驱动的file_operations结构体
static 
const 
struct 
file_operations globalchar_fops =
{
    
.read = GlobalRead,
    
.write = GlobalWrite
}; 
//注意分号啊!!!
//模块初始化函数
static 
int 
__init GlobalChar_init(
void
)
{
    
int 
ret;
    
ret = register_chrdev(char_major, DEV_NAME, &globalchar_fops);  
//注册设备驱动
    
if 
(ret < 0)
    
{
        
printk(KERN_ALERT 
"GlobalChar Reg Fail ! \n"
);
    
}
    
else
    
{
        
printk(KERN_ALERT 
"GlobalChar Reg Success ! \n"
);
        
char_major = ret;
        
printk(KERN_ALTER 
"Major = %d \n"
,char_major);
    
}
    
return 
ret;
}
//模块卸载函数
static 
void 
__exit GlobalChar_exit(
void
)
{
    
unregister_chrdev(char_major, DEV_NAME);    
//注销设备驱动
    
return
;
}
//设备驱动读函数
static 
ssize_t GlobalRead(
struct 
file *filp, 
char 
*buf, 
size_t 
len, loff_t *off)
{
    
if
(copy_to_user(buf, &GlobalData, 
sizeof
(
int
))) 
//从内核空间复制GlobalData到用户空间
    
{
        
return 
-EFAULT;
    
}
    
return 
sizeof
(
int
);
}
//设备驱动写函数
static 
ssize_t GlobalWrite(
struct 
file *filp, 
const 
char 
*buf, 
size_t 
len, loff_t *off)
{
    
if
(copy_from_user(&GlobalData, buf, 
sizeof
(
int
)))   
//从用户空间复制GlobalData到内核空间
    
{
        
return 
-EFAULT;
    
}
    
return 
sizeof
(
int
);
}
module_init(GlobalChar_init);
module_exit(GlobalChar_exit);

在内核中操作数据要区分数据的来源,对于用户空间的数据要使用copy_from_user()函数复制,使用copy_to_user()函数回写,不能直接操作用户空间的数据,否则会产生内存访问错误。

②编写Makefile

1
2
3
4
5
obj-m := GlobalCharDev.o
KDIR := 
/lib/modules/
$(shell 
uname 
-r)
/build
SRCPWD := $(shell 
pwd
)
all:
    
make 
-C $(KDIR) M=$(SRCPWD) modules

③编译并加载内核模块

④查看内核分配的主设备号

⑤使用mknod命令建立一个设备文件

mknod命令使用-m参数指定GlobalChar设备可以被所有用户访问。

249即上面查询的主设备号。

   到这里,我们就已经正确地添加了一个字符设备到内核,下面需要测试一下驱动程序能否正常工作。


测试字符设备驱动

   为了测试编写的字符设备是否能正常工作,我们编写一个应用程序测试一下能否正常读写字符设备。

测试代码:

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
//GlobalCharTest.c
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#define DEV_NAME "/dev/GlobalChar"
int 
main()
{
        
int 
fd,num;
                                                                                                            
        
fd = open(DEV_NAME, O_RDWR, S_IRUSR | S_IWUSR); 
//打开设备文件
        
if 
(fd < 0)
        
{
            
printf
(
"Open Deivece Fail ! \n"
);
            
return 
-1;
        
}
        
read(fd, &num, 
sizeof
(
int
));
        
printf
(
"The GlobalChar is %d \n"
, num); 
//获取当前设备数值
        
printf
(
"Please input a number written to GlobalChar: "
);
        
scanf
(
"%d"
, &num);
        
write(fd, &num, 
sizeof
(
int
));   
//写入新的数值
        
read(fd, &num, 
sizeof
(
int
));
        
printf
(
"The GlobalChar is %d \n"
, num); 
//重新读取设备数值
        
close(fd);
        
return 
0;
}

程序首先使用open函数打开设备文件,然后使用read()函数读取字符设备的值,open()系统调用最终会被解释为字符设备注册的read调用。

   测试结果:

   从程序输出结果来看,最初从设备得到的数值是0,输入520后写入到字符设备,重新读出的数值也是520,与设置相同,表示设备驱动程序功能正确。


总结

   linux字符设备驱动也不过如此嘛,嘿嘿,虽然只实现了read和write两个函数,不过其它函数也大同小异。

   重点:实践再实践!!! 

本文转自infohacker 51CTO博客,原文链接:http://blog.51cto.com/liucw/1219217

转载地址:http://apgjx.baihongyu.com/

你可能感兴趣的文章
iOS--The request was denied by service delegate (SBMainWorkspace) for reason:
查看>>
Android 打开WIFI并快速获取WIFI的信息
查看>>
Spring boot 入门篇
查看>>
linux设备驱动第三篇:写一个简单的字符设备驱动
查看>>
linux网络配置命令总结
查看>>
颜色值表
查看>>
PHP面向对象设计和设计模式
查看>>
【IOS开发】GDataXML解析XML
查看>>
我的友情链接
查看>>
JavaScript 错误处理
查看>>
我的友情链接
查看>>
mysql学习资料
查看>>
性能调优概述,你不能不知的优化策略
查看>>
AlpalyOneCell.xib Frame for "中老年抗癌计划" will be different at run time.
查看>>
我的友情链接
查看>>
Excel表格模板:费用支出报销单下载
查看>>
我的友情链接
查看>>
微软云计算介绍与实践(实践之七)
查看>>
解决OSPF不连续区域的3种方法
查看>>
多形性
查看>>