字符設備驅動框架
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");