- 此文会根据misc测试驱动的代码架构来讲解简单的驱动如何编写。
##1.驱动框架与Linux一切皆文件思想
###1.1 驱动思想理解
核心就是实现设备结构体的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
下列就是一个简单的驱动核心部分
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
| 内核驱动函数 说明 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驱动,当用户加载或者释放驱动,都会有相应的提示,用户写会实现从用户空间拷贝数据,用户读会实现将拷贝来的数据拷贝到用户空间
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| 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, };
struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "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");
|