Linux 字符設備驅動框架
字符設備是Linux三大設備之一(另外兩種是塊設備,網絡設備),字符設備就是字節流形式通訊的I/O設備,絕大部分設備都是字符設備,常見的字符設備包括鼠標、鍵盤、顯示器、串口等等,當我們執行ls -l /dev的時候,就能看到大量的設備文件,c就是字符設備,b就是塊設備,網絡設備沒有對應的設備文件。編寫一個外部模塊的字符設備驅動,除了要實現編寫一個模塊所需要的代碼之外,還需要編寫作為一個字符設備的代碼。
驅動模型
Linux一切皆文件,那么作為一個設備文件,它的操作方法接口封裝在struct file_operations,當我們寫一個驅動的時候,一定要實現相應的接口,這樣才能使這個驅動可用,Linux的內核中大量使用"注冊+回調"機制進行驅動程序的編寫,所謂注冊回調,簡單的理解,就是當我們open一個設備文件的時候,其實是通過VFS找到相應的inode,并執行此前創建這個設備文件時注冊在inode中的open函數,其他函數也是如此,所以,為了讓我們寫的驅動能夠正常的被應用程序操作,首先要做的就是實現相應的方法,然后再創建相應的設備文件。
#include <linux/cdev.h> //for struct cdev#include <linux/fs.h> //for struct file#include <asm-generic/uaccess.h> //for copy_to_user#include <linux/errno.h> //for error number/* 準備操作方法集 *//* struct file_operations { struct module *owner; //THIS_MODULE //讀設備 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //寫設備 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //映射內核空間到用戶空間 int (*mmap) (struct file *, struct vm_area_struct *); //讀寫設備參數、讀設備狀態、控制設備 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //打開設備 int (*open) (struct inode *, struct file *); //關閉設備 int (*release) (struct inode *, struct file *); //刷新設備 int (*flush) (struct file *, fl_owner_t id); //文件定位 loff_t (*llseek) (struct file *, loff_t, int); //異步通知 int (*fasync) (int, struct file *, int); //POLL機制 unsigned int (*poll) (struct file *, struct poll_table_struct *); 。。。};*/ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset){ return 0;}struct file fops = { .owner = THIS_MODULE, .read = myread, ...};/* 字符設備對象類型 */struct cdev { //public struct module *owner; //模塊所有者(THIS_MODULE),用于模塊計數 const struct file_operations *ops; //操作方法集(分工:打開、關閉、讀/寫、...) dev_t dev; //設備號(第一個) unsigned int count; //設備數量 //private ...};static int __init chrdev_init(void){ ... /* 構造cdev設備對象 */ struct cdev *cdev_alloc(void); /* 初始化cdev設備對象 */ void cdev_init(struct cdev*, const struct file_opeartions*); /* 為字符設備靜態申請設備號 */ int register_chedev_region(dev_t from, unsigned count, const char* name); /* 為字符設備動態申請主設備號 */ int alloc_chedev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name); MKDEV(ma,mi) //將主設備號和次設備號組合成設備號 MAJOR(dev) //從dev_t數據中得到主設備號 MINOR(dev) //從dev_t數據中得到次設備號 /* 注冊字符設備對象cdev到內核 */ int cdev_add(struct cdev* , dev_t, unsigned); ...}static void __exit chrdev_exit(void){ ... /* 從內核注銷cdev設備對象 */ void cdev_del(struct cdev* ); /* 從內核注銷cdev設備對象 */ void cdev_put(stuct cdev *); /* 回收設備號 */ void unregister_chrdev_region(dev_t from, unsigned count); ...}
新聞熱點
疑難解答