字元裝置驅動:
1.一般我們都採用動態分配裝置號的API:
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
dev是經用於輸出的函數,在成功完成調用以後將儲存分配範圍的第一個編號。
void unregister_chrdev_region(dev_t first,unsigned int count);
上面的函數為驅動程式的使用分配裝置編號,但是他們並沒有告訴核心拿這些編號做什麼。在使用者空間可訪問上述裝置之前,驅動程式需要將裝置編號和內建函式聯絡起來。
為了能夠動態產生裝置號,並且能夠建立裝置節點,對insmod的調用可以替換為一個簡單的指令碼。動態分配的缺點就是不能自動產生裝置節點。
#!bin/sh
module="scull"
device="scull"
mod="664"
/sbin/insmod ./$module.ko $*||exit 1 調用insmod並使用路徑名來指定模組位置。
rm -f /dev/${device}[0-3] 刪除裝置節點
major=$(awk "/$2= =/"$module/" {print /$1}" /proc/devices)
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
group="stuff"
chgrp $group /dev/${device}[0-3]
chmod $mod /dev/${device}[0-3]
2.獲得主裝置號的代碼:
scull採用這種現實方式,他使用了sucll_major,用來儲存所選擇的裝置號(也有一個用於次裝置號的scull_minor)該變數的初始化值是SCUL_MAJOR在,這個宏定義在scull.h中,預設為0,使用者可以使用預設值或選擇特定裝置號。
if(scull_major){
dev=MKDEV(scull_major,scull_minor);
result=register_chrdev_region(dev,scull_nr_devs,"scull");
}else{
result=alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
scull_major=MAJOR(dev);
}
if(result<0){
printk(KERN_INFO "can't get major %d/n",scull_major);
return result;
}
前邊說過alloc_chrdev_region中dev是僅用作輸出的參數。然後通過MAJOR(dev)獲得主裝置號。
在這部分中,比較重要的是在用函數擷取裝置編號後,其中的參數name是和該編號範圍關聯的裝置名稱,它將出現在/proc/devices和sysfs中。
看到這裡,就可以理解為什麼mdev和udev可以動態、自動地產生當前系統需要的裝置檔案。udev就是通過讀取sysfs下的資訊來識別硬體裝置的.
(請看《理解和認識udev》
URL:http://blog.chinaunix.net/u/6541/showart_396425.html)
3.file_operations中幾個選項:
unsigned int (*poll)(struct file *,struct poll_table_struct *)
poll方法是poll,epoll,select這三個系統調用的後台實現,這三個系統調用可以用來查詢某個或多個檔案描述符(多個檔案描述符通過構建一個fd_set來實現)上的讀取或寫入是否被阻塞,poll方法應該返回一個為掩碼,用來指出非阻塞的讀取或寫入是否可能,並且也會向核心提供將調用進程置於休眠狀態直到I/O變為可能時的資訊。
4. file結構體中有private_data結構體,通常這個變數通過強制類型轉換,變為裝置所需的結構體,儲存有用的資源。
5.核心用inode結構在內部表示檔案,因此它和file結構不同,後者表示開啟的檔案描述符。對於單個檔案,可能會有許多多個表示開啟檔案描述符的file結構,但他們都指向單個inode結構。
在inode結構體中有一個struct cdev *i_cdev欄位, struct cdev是表示字元裝置的核心內部結構,當inode指向一個字元裝置時,該欄位包含了指向struct cdev結構的指標。
6.前邊知道,核心內部使用struct cdev結構表示字元裝置。在核心調用裝置操作之前,必須分配並註冊一個或多個以上的結構:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;
分配一個cdev結構。
void cdev_init(struct cdev *cdev,struct file_operations *fops);
進行裝置初始化。
void cdev_add(struct cdev *dev,dev_t num,int count);
將裝置添加到核心中。 dev為cdev裝置,num為裝置號,count一般取1.
struct cdev提供了核心與裝置之間的介面。
static void scull_setup_cdev(struct scull_cdev *dev,int index)
{
int err,devno = MKDEV(scull_major,scull_minor + index);
cdev_init(&dev->cdev,&scull_fops);
dev->cdev.ower = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev,devno,1);
if(err){
....
}
return 0;
}
7.open函數
int (*open)(struct inode *inode,struct file *filp)
其中inode的在其i_cdev欄位中包含了我們所需要的資訊,即我們設定的cdev結構。唯一的問題是我們不需要cdev結構本身,而是要知道包含cdev結構的scull_dev結構。這裡運用container_of宏來完成,該宏可以通過結構體成員得到包含該成員的結構體本身:
container_of(pointer,container_type,container_field);
該宏需要一個container_field欄位的指標,該欄位包含在container_type類型的結構體中,然後返回包含該欄位的結構體指標,在scull_open中,這個宏用來找到合適的裝置結構。
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev,cdev);
filp->private_data = dev;
這段代碼首先定義了一個scull_dev裝置其中包含了i_cdev欄位(包含cdev),然後通過container_of找到包含cdev欄位的scull_dev結構體,然後將該結構體賦值給filp的私人成員變數。
其中open要完成的一個任務就是分配並填寫置於filp->private_data裡的資料結構。
int scull_open(struct inode *inode,struct file *filp)
{
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev,cdev);
filp->private_data = dev;
if((filp->f_flags & O_ACCMODE) == O_WRONLY){
scull_trim(dev); /*負責釋放整個記憶體地區*/
}
return 0;
}
8.release/close的作用
釋放由open分配的,儲存在filp->private_data中的資料結構。
在最後一次關閉操作時關閉裝置。
這裡有個問題就是就是關閉裝置的次數,可能會大於open裝置的次數,比如dup/fork系統調用都會在不調用open的情況下建立已開啟檔案的副本,但每個副本都在程式終止前被關閉。
原因在於:並不是每個close系統調用都會引起對release方法的調用,只有真正釋放裝置資料結構的close才會調用release方法的調用。核心對每個file結構維護其被使用多少次的計數器。對於dup/fork等系統調用,並不會建立新的資料結構(僅由open建立,在open函數中通過申請並初始化的cdev結構,運用container_of求出包含cdev的scull_dev結構,並賦值給filp->private_data,從而分配一個新的資料結構),他們只是增加對資料結構的計數。當file結構的計數減為零時,才會調用close,執行release.