红联Linux门户
Linux帮助

Linux环境下字符设备驱动开发入门

发布时间:2016-06-10 15:06:25来源:blog.csdn.net/u011481047/作者:计算机的感性面

前言

这篇文章主要总结了我学习嵌入式系统中,一个字符设备驱动的构建和运行过程。这篇总结中我会尽量简洁地告诉你在Ubuntu(Linux)中字符设备驱动的编程方式,一个简单的模块用C如何进行构建,并且如何将它作为内核模块动态地加载和卸载,你能够看到这个字符驱动设备在系统日志中的记录。
在这篇当中,我们会将重点放在程序结构和编译运行加载过程,关于字符设备和环境等等将不做详细介绍。


主要内容

1.字符设备驱动的主要结构和编写
2.字符设备驱动的Makefile编写
3.将驱动作为动态模块进行加载
为了简洁明了,我们以一个加载时和卸载时候打印hello的驱动为例进行说明。


字符驱动的结构

1.首先需要声明该设备的主设备号TEST_MAJOR和设备名称drv_name。
2.其次实现关于设备的函数,一般包括 open(), release(), read(), write(),后面用户操作的时候,kernel实际会调用 这些函数。
3.声明一个 file_operation结构体,然后使用标记化结构初始化语法进行初始化(关于这个可以参考标记化结构初始化,C Primer Plus中也有较为详细的介绍,请参考结构相关章节)。这一步的操作将结构体中的内容和我们的实现函数进行一次“绑定”。
4.实现 init()函数,并且在函数内使用
register_chrdev(TEST_MAJOR,drv_name,&chardev_fops)函数进行设备号的注册;
实现exit()函数,并在函数内部使用
unregister_chrdev(TEST_MAJOR,drv_name,&chardev_fops)函数对设备进行注销操作。
5.最后记得使用module_init(test_init);module_exit(test_exit);将设备看作模块进行注册注销关联。


下面我们将给出这个hello.c程序:

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/fs.h>

#define TEST_MAJOR  233 //定义主设备号
static char drv_name[] = "test";   //定义主设备名  

//自己实现的打开函数
static int test_chardev_open(struct inode *inode,struct file *file)
{
printk("open major=%d, minor=%d\n", imajor(inode), iminor(inode));
return 0;
}

//自己实现的释放函数
static int test_chardev_release(struct inode *inode,struct file *file )
{
   printk("close major=%d, minor=%d\n", imajor(inode), iminor(inode));
return 0;
}

//标记化结构体初始化
static struct file_operations chardev_fops={
.owner = THIS_MODULE,
.open = test_chardev_open,
.release = test_chardev_release,
};

//初始化注册设备
static int  __init  test_init(void)
{
printk("Hello Mr.Bubbles\n");
if(register_chrdev(TEST_MAJOR,drv_name,&chardev_fops))
{
printk("fail to register device \n");
return -1;
 }
return 0;
}

//注销设备
static void  __exit  test_exit(void)
{
printk("Goodbye,Mr.Bubbles!\n");
unregister_chrdev(TEST_MAJOR,drv_name);

}

//进行关联
module_init(test_init);
module_exit(test_exit);

//声明许可证
MODULE_LICENSE("GPL"); 


字符驱动的Makefile文件

我们直接放上Makefile文件的代码:

obj-m := hello.o
KERNELDIR := /lib/modules/4.4.0-22-generic/build
PWD := $(shell pwd)

modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm –rf  *.o  *~ core  .depend .  *.cmd  *.ko  *.mod.c  .tmp_versions


在这里我们需要声明以下内核所在的位置,因为我们的驱动将作为一个模块单独编译进内核中去。-C进入内核目录,M返回当前模块的目录


编译、加载和卸载模块

我们的驱动编写工作可以说是进入了收尾阶段,下面就需要进行编译和加载。首先这是未编译的状态:

Linux环境下字符设备驱动开发入门

然后我们执行make指令:

Linux环境下字符设备驱动开发入门

这里我们可以清楚的看到shell进入了内核执行了makefile然后回到当前目录生成ko文件。这个就是我们的模块文件。在当前目录下我们执行insmod指令就可以将我们的模块动态进行加载。

Linux环境下字符设备驱动开发入门

这里的效果我们需要通过系统日志才能看到,打开/var/log/syslog文件就可以看到(中间的“Hello,Mr.Bubbles”):

Linux环境下字符设备驱动开发入门

然后我们尝试进行卸载,使用rmmod就可以卸载当前的模块 :

Linux环境下字符设备驱动开发入门

最终模块被成功卸载,显示“Goodbye,Mr.Bubbles!”字样。


小结

虽然是考试前的复习,但是如果不自己动手亲自写一下的话是体会不到其中的乐趣的,当然这个介绍只是一点简单的总结,很多细节都被略过了,要对其有更好地了解还需要自行补更多的知识,当然,Embedded System还有很多好玩的东西,希望这篇可以帮助我回顾,同时与大家共勉。


本文永久更新地址:http://www.linuxdiyf.com/linux/21402.html