- 此文会根据misc测试驱动的代码架构来讲解简单的驱动如何编写。
1.驱动框架与Linux一切皆文件思想
1.1 驱动思想理解
- Linux系统通过文件抽象实现设备管理,所有硬件设备都通过文件接口进行访问。这种设计体现在
设备节点以文件形式存在于/dev目录使用标准文件操作接口(open/close/read/write)驱动开发者通过实现文件操作函数与硬件交互 - 比如说我们下面的misc测试驱动,就是一个典型的最小驱动,包含Linux最常用的读、写、打开、关闭函数
核心就是实现设备结构体的file_operations的文件读、写、打开、关闭函数,给出模块入口出口函数这个框架就类似JAVA的CRUD,属于是同一类思想,只不过驱动操作的是硬件设备数据,JAVA操作的是软件数据库管理理解这个核心,写SPI、I2C,或者其他简单的设备驱动都是大致相同的,首先实现设备结构体,再实现file_operations的函数赋给设备结构体,最后是模块入口出口函数 - misc(杂项)设备是Linux内核提供的一种简化字符设备驱动开发的机制
主设备号固定为10,次设备号由内核动态分配(MISC_DYNAMIC_MINOR)自动创建设备节点(需配合udev机制)相比传统字符设备注册(register_chrdev),简化了开发流程适用于功能简单的设备驱动开发
1.2 驱动使用过程生命周期
- 设备结构体->实现file_operations的文件读、写、打开、关闭函数->给出模块入口出口函数
- 内核或者用户手动加载驱动->进入模块入口,注册设备结构体,在/dev下新增设备接口
用户的APP启动
-> 打开设备接口 -> 通过设备结构体的file_operations调用misc_open -> 向设备写入数据 -> 通过设备结构体的file_operations调用misc_write -> 读设备的数据 -> 通过设备结构体的file_operations调用misc_read -> 关闭设备 -> 进入模块出口,释放设备结构体,调用misc_release- 下列就是一个简单的驱动核心部分
内核驱动函数 说明
misc_open 设备初始化/资源分配
misc_read 从设备获取数据
misc_write 向设备发送数据
misc_release 设备资源释放
// 文件操作结构体
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.read = misc_read,
.write = misc_write,
};
// misc设备结构体
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号
.name = "hello_misc", // 设备节点名(/dev/hello_misc)
.fops = &misc_fops, // 关联文件操作
};
module_init(misc_test_init); 模块加载入口函数
module_exit(misc_test_exit); 模块出口函数
1.3 驱动代码
- 下面的程序我们用这种框架,实现一个简单的misc驱动,当用户加载或者释放驱动,都会有相应的提示,用户写会实现从用户空间拷贝数据,用户读会实现将拷贝来的数据拷贝到用户空间
misc_test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
//设备打开
int misc_open(struct inode *inode, struct file *file){
printk("misc open\n");
return 0;
}
//设备关闭
int misc_release(struct inode *inode, struct file *file){
printk("misc release\n");
return 0;
}
//读设备的数据
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){
char kbuf[64] = "qerwq";
if(copy_to_user(ubuf,kbuf,strlen(kbuf)) != 0){
printk("copy to user error\n");
return -1;
}
return 0;
}
//向设备写数据
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){
char kbuf[64] = {0};
if(copy_from_user(kbuf,ubuf,size) != 0){
printk("copy from user error\n");
return -1;
}
printk("write: %s\n",kbuf);
return 0;
}
// 文件操作结构体
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.read = misc_read,
.write = misc_write,
};
// misc设备结构体
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号
.name = "hello_misc", // 设备节点名(/dev/hello_misc)
.fops = &misc_fops, // 关联文件操作
};
static int misc_test_init(void){
int ret;
ret = misc_register(&misc_dev);
if(ret<0){
printk("misc init faild\n");
return -1;
}
printk(KERN_WARNING "misc hello!\n");
return 0;
}
static void misc_test_exit(void){
misc_deregister(&misc_dev);
printk(KERN_WARNING "bye!\n");
}
module_init(misc_test_init);
module_exit(misc_test_exit);
MODULE_LICENSE("GPL");
评论