字符设备驱动框架
linux/cdev.h
cdev结构体: 操作方法集 设备号(主设备号+次设备号) 围绕cdev结构体进行的一系列操作: 0.申请/注册设备号 1.分配cdev结构体 2.初始化结构体 3.添加cdev到内核
编写驱动程序的三种方法
一:传统方法,优点简单缺点就是不易宽展,硬件更换了板子都要重新写代码重新比编译,硬件的每次改动都要重新修改软件版本增加工作量。
二: 总线设备驱动模型,将驱动分为两部分,device和driver两部分挂在platform总线上,device负责指定资源,如管脚等。对于相同的硬件操作driver部分都是相同的,硬件只需要编写相应的device代码来指定硬件资源即可。这种方法稍微复杂,但是易扩展,冗余代码多且以代码的形式出现。
三: 设备树。仍然是分为两部分,一是xxx_driver.c跟总线设备驱动模型一样,二是设备树文件xxx.dts文件来指定文件,内核根据这个文件来构造xxx_device文件,运行时去读取解析xxx.dts文件。优点是比传统方法稍复杂,容易扩展,没有冗余代码,不需要重新编译内核或者驱动只需要提供不一样的设备树文件。
字符设备驱动代码的编写*
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #define BASEMINOR 0 #define COUNT 3 #define NAME "cdev_demo" dev_t devno = 0; struct cdev *cdevp = NULL; int demo_open(struct inode *inode, struct file *filp){ printk(KERN_DEBUG "---%s---%s---%d---\n",__FILE__,__func__,__LINE__); return 0; } int demo_release(struct inode *inode, struct file *filp){ printk(KERN_DEBUG "---%s---%s---%d---\n",__FILE__,__func__,__LINE__); return 0; } struct file_operations fops = { .owner = THIS_MODULE, .open = demo_open, .release = demo_release,}; int __init demo_init(void){ int ret = 0; //0.申请设备号-内核分配主设备号,次设备好从0开始,共三个(0,1,2) ret = alloc_chrdev_region(&devno,BASEMINOR,COUNT, NAME); if(ret < 0) { printk(KERN_ERR "alloc_chrdev_region failed...\n"); goto err0; } printk(KERN_INFO "---major:%d---\n",MAJOR(devno)); //1.分配cdev结构体 cdevp = cdev_alloc(); if(cdevp == NULL) { printk(KERN_ERR "cdev_alloc failed...\n"); ret = -ENOMEM; goto err1; } //2.初始化cdev结构体 cdev_init(cdevp, &fops); //3.将cdev结构体添加到内核中,由内核对驱动进行统一的管理 ret = cdev_add(cdevp, devno, COUNT); if(ret < 0){ printk(KERN_ERR "cdev_add failed...\n"); goto err1; } printk(KERN_DEBUG "---%s---%s---%d---\n",__FILE__,__func__,__LINE__); return 0;err1: unregister_chrdev_region(devno, COUNT);err0: return ret;}void __exit demo_exit(void){ cdev_del(cdevp); unregister_chrdev_region(devno, COUNT); printk(KERN_DEBUG "---%s---%s---%d---\n",__FILE__,__func__,__LINE__); } module_init(demo_init);module_exit(demo_exit);MODULE_LICENSE("GPL");