字符设备驱动框架
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");