linux裝置驅動程式學習(1)
-字元裝置驅動程式
一、分配裝置號
1、 對字元裝置的訪問是通過檔案系統內的裝置名稱進行的(/dev/ttys0)。在核心中,
#include dev_t 用來儲存裝置編號——包括主裝置號和次裝置號。
由dev_t獲得主次裝置號:
major(dev_t dev);
minor(dev_t dev);
由主次裝置號獲得dev_t
mkdev(int major, int minor);
2、分配和釋放裝置編號:
靜態分配裝置號: int register_chrdev_region(dev_t first, unsigned int count, char *name);
動態分配裝置號: int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
釋放裝置編號 : void unregister_chrdev_region(dev_t first, unsigned int count);
3、動態分配裝置編號
以《ldd3》作者的觀點,分配主裝置號的最佳方式是:預設採用動態分配,同時保留在載入甚至是編譯時指定主裝置號的餘地。示例驅動scull採用了如下方式:
二、與裝置驅動相關的三個重要資料結構
1、struct file_operation
它用來建立裝置編號與裝置操作之間的連線,scull裝置驅動程式的此結構初始化為如下形式:
2、struct file 檔案描述符
file 結構與使用者空間的file沒有任何關聯,file在c庫中定義且不會出現在核心**中,而struct file是乙個核心結構,它不會出現在使用者程式中。
3、struct inode
核心用inode結構在內部表示檔案,因此它和file結構不同,後者表示開啟的檔案描述符。對單個檔案,可能會有許多個表示開啟的檔案描述符的file結構,但它們都指向單個inode結構。
dev_t i_rdev; 包含真正的裝置編號,unsigned int imajor(struct inode *inode); unsigned int iminor(struct inode *inode);
struct cdev *i_cdev;
三、註冊字元裝置
核心內部使用struct cdev結構來表示字元裝置。在核心呼叫裝置的操作之前,必須分配並註冊乙個或者多個上述結構。在 中定義了這個結構及其相關操作。
1、分配cdev結構
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;
2、初始化分配到了cdev結構
void cdev_init(struct cdev *cdev, struct file_operation *fops);
my_cedv->owner = this_module;
3、告訴核心該結構的資訊
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
4、從系統中刪除乙個字元裝置
voi d cdev_del(struct cdev *dev);
scull的裝置註冊如下:
四、裝置的相關操作實現 ——open() release() read() write()
open方法提供給驅動程式以初始化的能力,為以後的操作作準備。應完成的工作如下:
(1)檢查裝置特定的錯誤(如裝置未就緒或硬體問題);
(2)如果裝置是首次開啟,則對其進行初始化;
(3)如有必要,更新f_op指標;
(4)分配並填寫置於filp->private_data裡的資料結構。
而根據scull的實際情況,他的open函式只要完成第四步(將初始化過的struct scull_dev dev的指標傳遞到filp->private_data裡,以備後用)就好了,所以open函式很簡單。但是其中用到了定義在中的container_of巨集,原始碼如下:
#define container_of(ptr, type, member) ()
其實從原始碼可以看出,其作用就是:通過指標ptr,獲得包含ptr所指向資料(是member結構體)的type結構體的指標。即是用指標得到另外乙個指標。
release方法提供釋放記憶體,關閉裝置的功能。應完成的工作如下:
(1)釋放由open分配的、儲存在file->private_data中的所有內容;
(2)在最後一次關閉操作時關閉裝置。
由於前面定義了scull是乙個全域性且持久的記憶體區,所以他的release什麼都不做。
read和write
read和write方法的主要作用就是實現核心與使用者空間之間的資料拷貝。因為linux的核心空間和使用者空間隔離的,所以要實現資料拷貝就必須使用在中定義的:
unsigned long copy_to_user(void __user *to,
const void *from,
unsigned long count);
unsigned long copy_from_user(void *to,
const void __user *from,
unsigned long count);
而值得一提的是以上兩個函式和
#define __copy_from_user(to,from,n) (memcpy(to, (void __force *)from, n), 0)
#define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0)
之間的關係:通過原始碼可知,前者呼叫後者,但前者在呼叫前對使用者空間指標進行了檢查。
五、scull的記憶體使用模型
Linux裝置驅動程式學習筆記1
系統linux2.6.32.2 arm linux gcc 4.3.2 hello world模組 include include static int hello init void static void hello exit void module init hello init module...
linux裝置驅動程式 字元裝置驅動程式
先留個 有一起學習驅動程式的加qq295699450 字元裝置驅動 這篇比較惱火。載入成功,但是讀不出來資料,有知道怎麼回事的,留個言,一起討論下 資料結構 struct scull mem struct scull dev dev 整個驅動程式 如下 include include include...
Linux裝置驅動程式 字元裝置驅動程式
1.檢視主裝置號,次裝置號 進入 dev目錄執行ls l,第四,五列分別為主次裝置號,10,180,1,5,這些是主裝置號,而60,63這些就是次裝置號 130 shell android dev ls l crw rw r system radio 10,60 1969 12 31 21 00 a...