1、系统整体工作原理
(1)
应用层->API->设备驱动->硬件
(2)API:open、read、write、close等
(3)
驱动源码中提供真正的open、read、write、close等函数实体
2、file_operations结构体(
用来
挂接实体函数地址)
(1)元素主要是
函数指针
,用来
挂接实体函数地址
(2)
每个设备驱动都需要一个该结构体类型的变量
(3)设备
驱动向内核注册
时提供该结构体类型的变量
3、注册字符设备驱动
驱动向内核注册函数
register_chrdev()在(#include <linux/fs.h>)中
4.从零开始编写一个驱动程序
首先应该有一个最简单的驱动程序原型,详细可见博客中-》linux驱动开发(最简单的驱动模块,内核printk函数)这篇。
(1)
先定义file_operations结构体变量,
在工程中随便搜索一个file_operations结构体变量看别人怎样定义的,再修改成自己想要的。
(2)open和close函数原型确定、内容填充。
找到file_operations结构体中的open函数,跳到open函数定义的地方,复制别人的open函数,修改成自己想要的,其他函数一样的道理。
(3)模块安装,注册驱动。
往模块安装函数里面添加注册驱动函数,以及返回值的检测。
主设备号的选择(cat /proc/devices 查看当前可用的主设备号,确定没用过的先随便定一个)
(4)模块卸载,
(5)最后编写好的驱动模块
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#define MYMAJOR 200
#define MYNAME "testchar"
int mymajor;
static int test_chrdev_open(struct inode *inode, struct file *file)
{
// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
printk(KERN_INFO "test_chrdev_open\n");
return 0;
}
static int test_chrdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_release\n");
return 0;
}
// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {
.owner = THIS_MODULE, // 惯例,直接写即可
.open = test_chrdev_open, // 将来应用open打开这个设备时实际调用的
.release = test_chrdev_release, // 就是这个.open对应的函数
};
// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev_init helloworld init\n");
// 在module_init宏调用的函数中去注册字符设备驱动
// major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号
// 内核如果成功分配就会返回分配的主设备好;如果分配失败会返回负数
mymajor = register_chrdev(0, MYNAME, &test_fops);
if (mymajor < 0)
{
printk(KERN_ERR "register_chrdev fail\n");
return -EINVAL;
}
printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);
return 0;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit helloworld exit\n");
// 在module_exit宏调用的函数中去注销字符设备驱动
unregister_chrdev(mymajor, MYNAME);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息