[國嵌學習日誌][113][字符設備驅動模型]

設備驅動模型
1.驅動初始化(驅動安裝)
1.1.分配設備描述符
1.2.初始化設備描述符
1.3.註冊設備描述符
1.4.硬件初始化
2.實現設備操作(驅動操作)
3.驅動註銷(驅動卸載)

設備描述結構
在任何一種驅動模型中,設備都會用內核中的一種結構來描述。我們的字符設備在內核中使用struct cdev來描述。
struct cdev{
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;   //設備操作集
    struct list_head list;
    dev_t dev;            //設備號
    unsigned int count;   //設備數
};
 
設備號的作用
通過ls –l命令可以查看/dev目錄下的設備號。字符設備文件與字符設備驅動通過主設備號建立關係,同一類的設備通過不同的次設備號來使用同一個設備驅動。
示例:
ls –l /dev
crw-r—–. 1 root root     10, 223 3月   6 18:33 uinput
c表示字符設備,10表示主設備號,223表示次設備號
 
設備號的操作
Linux內核中使用dev_t類型來定義設備號,dev_t這種類型實質是32爲unsigned int,其中高12位爲主設備號,低20位爲次設備號。
通過主設備號和次設備號合成dev_t類型
dev_t dev = MKDEV(主設備號,次設備號)
通過dev_t中分解主設備號
主設備號=MAJOR(dev_t dev)
通過dev_t中分解次設備號
次設備號=MINOR(dev_t dev)
 
設備號的分配
1.靜態申請
開發者自己選擇一個數字作爲主設備號,通過函數register_chrdev_region向內核申請使用。缺點是如果申請使用的設備號已經被內核中的其他驅動使用,那麼申請失敗。
2.動態申請
使用alloc_chrdev_region由內核分配一個可以使用的主設備號。優點是因爲內核知道哪些設備號已經被使用,所以不會導致分配到已經被使用的設備號。
函數原型:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
參數說明:
dev:設備描述結構
baseminor:次設備號起始編號
count:次設備號數目
name:驅動名稱,對應於分配設備號後添加到/proc/devices裏面的名稱
 
設備號的註銷
不論使用何種方法分配設備號,都應該在驅動程序退出時,使用unregister_chrdev_region函數釋放這些設備號。
 
 
設備操作集
應用程序通過系統調用訪問字符設備文件,字符設備文件通過字符操作集映射到字符設備驅動。
設備操作集相當於一張映射表,能把來自應用程序的系統調用映射成驅動程序中的函數去執行。

struct file_operations {
struct module *owner;
loff_t (llseek) (struct file , loff_t, int);
ssize_t (read) (struct file , char __user , size_t, loff_t );
ssize_t (write) (struct file , const char __user , size_t, loff_t );
ssize_t (aio_read) (struct kiocb , const struct iovec *, unsigned long, loff_t);
ssize_t (aio_write) (struct kiocb , const struct iovec *, unsigned long, loff_t);
int (readdir) (struct file , void *, filldir_t);
unsigned int (poll) (struct file , struct poll_table_struct *);
long (unlocked_ioctl) (struct file , unsigned int, unsigned long);
long (compat_ioctl) (struct file , unsigned int, unsigned long);
int (mmap) (struct file , struct vm_area_struct *);
int (open) (struct inode , struct file *);
int (flush) (struct file , fl_owner_t id);
int (release) (struct inode , struct file *);
int (fsync) (struct file , int datasync);
int (aio_fsync) (struct kiocb , int datasync);
int (fasync) (int, struct file , int);
int (lock) (struct file , int, struct file_lock *);
ssize_t (sendpage) (struct file , struct page , int, size_t, loff_t , int);
unsigned long (get_unmapped_area)(struct file , unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (flock) (struct file , int, struct file_lock *);
ssize_t (splice_write)(struct pipe_inode_info , struct file , loff_t , size_t, unsigned int);
ssize_t (splice_read)(struct file , loff_t , struct pipe_inode_info , size_t, unsigned int);
int (setlease)(struct file , long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};

 
 
struct file_operations是一個函數指針的集合,定義能在設備上進行的操作。結構中的函數指針指向驅動中的函數,這些函數實現一個針對設備的操作,對於不支持的操作設置函數指針爲NULL。
示例:
struct file_operations dev_fops={
.llseek = NULL,
.read = dev_read,
.write = dev_write,
.ioctl = dev_ioctl,
.open = dev_open,
.release = dev_release
};
 
描述結構的分配
cdev變量的定義可以採用靜態和動態分配兩種辦法
1.靜態分配
struct cdev mdev;
2.動態分配
struct cdev *pdev = cdev_alloc();
 
描述結構的初始化
struct cdev的初始化使用cdev_init函數來完成
cdev_init(struct cdev *cdev, const struct file_operations *fops)
參數:
cdev:待初始化的cdev結構
fops:設備對應的操作函數集
 
描述結構的註冊
字符設備的註冊使用cdev_add函數來完成。
cdev_add(struct cdev *p, dev_t dev, unsigned count)
參數:
p:待添加到內核的字符設備結構
dev:設備號
count:該類設備的設備個數
 
硬件初始化
根據相應的硬件芯片手冊,完成初始化。
 
設備操作函數(又稱爲設備操作方法)
設備操作原型的參數都是固定,但是名字可以順便取。
int (open)(struct inode , struct file *)
打開設備,響應open系統調用
 
int (release)(struct inode , struct file *)
關閉設備,響應close系統調用
 
loff_t (llseek)(struct file , loff_t, int)
重定位讀寫指針,響應lseek系統調用
 
ssize_t (read)(struct file , char __user , size_t, loff_t )
從設備讀取數據,響應read系統調用
 
ssize_t (write)(struct file , const char __user , sizet_t, loff_t )
向設備中寫入數據,響應write系統調用
 
struct file
在Linux系統中,每一個打開的文件,在內核中都會關聯一個struct file,它由內核在打開文件時創建,在關閉文件後釋放。
重要成員:
loff_t f_pos                   //文件讀寫指針
struct file_operations *f_op   //該文件所對應的操作
 
struct inode
每一個存在於文件系統裏面的文件都會關聯一個inode結構,該結構主要用來記錄文件物理上的信息。因此它和代表打開文件的file結構是不同的。一個文件沒有被打開時不會關聯file結構,但是卻會關聯一個inode結構。
重要成員:
dev_t i_rder:設備號
 
設備操作open
open設備方法是驅動程序用來爲以後的操作完成初始化準備工作的。在大部分驅動程序中,open完成如下工作:
標明次設備號
啓動設備
 
設備操作release
release方式的作用正好與open相反。這個設備方法有時也稱爲close,它完成如下工作:
關閉設備
 
設備操作read
read設備方法通常完成兩件事情:
從設備中讀取數據(屬於硬件訪問類操作)
將讀取到的數據返回給應用程序
 
ssize_t (*read)(struct file *filp, char __user *buff, size_t count, loff_t *offp)
參數分析:
filp:與字符設備文件關聯的file結構指針,由內核創建。
buff:從設備讀取到的數據,需要保存到的位置。由read系統調用提供該參數。
count:請求傳輸的數據量,由read系統調用提供該參數。
offp:文件的讀寫位置,由內核從file結構中取出後,傳遞進來。
 
buff參數是來源於用戶空間的指針,這類指針都不能被內核代碼直接引用,必須使用專門的函數:
int copy_to_user(void __user *to, const void *from, int n)   讀取數據
int copy_from_user(void __user *to, const void *from, int n)   寫入數據
 
設備操作write
write設備方法通常完成兩件事:
從應用程序提供的地址中取出數據
將數據寫入設備(屬於硬件訪問類操作)
 
struct (write)(struct file , const char __user , size_t, loff_t )
參數類似於read
 
驅動註銷
當我們從內核中卸載驅動程序的時候,需要使用cdev_del函數來完成字符設備的註銷。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章