【驱动】linux设备驱动·字符设备驱动开发

简介:

Preface

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

   《linux设备驱动入门篇》:http://infohacker.blog.51cto.com/6751239/1218461

   《linux设备驱动扫盲篇》:http://infohacker.blog.51cto.com/6751239/1218747

   《fedora下的字符设备驱动开发http://infohacker.blog.51cto.com/6751239/1155153



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

   在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

相关文章
|
14天前
|
Linux 开发工具 Perl
在Linux中,有一个文件,如何删除包含“www“字样的字符?
在Linux中,如果你想删除一个文件中包含特定字样(如“www”)的所有字符或行,你可以使用多种文本处理工具来实现。以下是一些常见的方法:
38 5
|
30天前
|
Linux 开发工具 Perl
Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
【10月更文挑战第20天】Linux命令替换目录下所有文件里有"\n"的字符为""如何操作?
41 4
|
1月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
90 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
存储 Linux 开发工具
如何进行Linux内核开发【ChatGPT】
如何进行Linux内核开发【ChatGPT】
|
3月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
44 6
|
3月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
50 5
|
2月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
2月前
|
Linux 程序员 编译器
Linux内核驱动程序接口 【ChatGPT】
Linux内核驱动程序接口 【ChatGPT】
下一篇
无影云桌面