3.4. 字符設(shè)備注冊

2018-02-24 15:49 更新

3.4.?字符設(shè)備注冊

如我們提過的, 內(nèi)核在內(nèi)部使用類型 struct cdev 的結(jié)構(gòu)來代表字符設(shè)備. 在內(nèi)核調(diào)用你的設(shè)備操作前, 你編寫分配并注冊一個或幾個這些結(jié)構(gòu). [11]為此, 你的代碼應(yīng)當(dāng)包含 <linux/cdev.h>, 這個結(jié)構(gòu)和它的關(guān)聯(lián)幫助函數(shù)定義在這里.

有 2 種方法來分配和初始化一個這些結(jié)構(gòu). 如果你想在運行時獲得一個獨立的 cdev 結(jié)構(gòu), 你可以為此使用這樣的代碼:


struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;

但是, 偶爾你會想將 cdev 結(jié)構(gòu)嵌入一個你自己的設(shè)備特定的結(jié)構(gòu); scull 這樣做了. 在這種情況下, 你應(yīng)當(dāng)初始化你已經(jīng)分配的結(jié)構(gòu), 使用:


void cdev_init(struct cdev *cdev, struct file_operations *fops);

任一方法, 有一個其他的 struct cdev 成員你需要初始化. 象 file_operations 結(jié)構(gòu), struct cdev 有一個擁有者成員, 應(yīng)當(dāng)設(shè)置為 THIS_MODULE. 一旦 cdev 結(jié)構(gòu)建立, 最后的步驟是把它告訴內(nèi)核, 調(diào)用:


int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

這里, dev 是 cdev 結(jié)構(gòu), num 是這個設(shè)備響應(yīng)的第一個設(shè)備號, count 是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號的數(shù)目. 常常 count 是 1, 但是有多個設(shè)備號對應(yīng)于一個特定的設(shè)備的情形. 例如, 設(shè)想 SCSI 磁帶驅(qū)動, 它允許用戶空間來選擇操作模式(例如密度), 通過安排多個次編號給每一個物理設(shè)備.

在使用 cdev_add 是有幾個重要事情要記住. 第一個是這個調(diào)用可能失敗. 如果它返回一個負(fù)的錯誤碼, 你的設(shè)備沒有增加到系統(tǒng)中. 它幾乎會一直成功, 但是, 并且?guī)鹆似渌狞c: cdev_add 一返回, 你的設(shè)備就是"活的"并且內(nèi)核可以調(diào)用它的操作. 除非你的驅(qū)動完全準(zhǔn)備好處理設(shè)備上的操作, 你不應(yīng)當(dāng)調(diào)用 cdev_add.

為從系統(tǒng)去除一個字符設(shè)備, 調(diào)用:


void cdev_del(struct cdev *dev);

顯然, 你不應(yīng)當(dāng)在傳遞給 cdev_del 后存取 cdev 結(jié)構(gòu).

3.4.1.?scull 中的設(shè)備注冊

在內(nèi)部, scull 使用一個 struct scull_dev 類型的結(jié)構(gòu)表示每個設(shè)備. 這個結(jié)構(gòu)定義為:


struct scull_dev { 
 struct scull_qset *data;  /* Pointer to first quantum set */ 
 int quantum;  /* the current quantum size */ 
 int qset;  /* the current array size */ 
 unsigned long size;  /* amount of data stored here */ 
 unsigned int access_key;  /* used by sculluid and scullpriv */ 
 struct semaphore sem;  /* mutual exclusion semaphore  */ 

 struct cdev cdev; /* Char device structure */
};

我們在遇到它們時討論結(jié)構(gòu)中的各個成員, 但是現(xiàn)在, 我們關(guān)注于 cdev, 我們的設(shè)備與內(nèi)核接口的 struct cdev. 這個結(jié)構(gòu)必須初始化并且如上所述添加到系統(tǒng)中; 處理這個任務(wù)的 scull 代碼是:


static void scull_setup_cdev(struct scull_dev *dev, int index)
{
 int err, devno = MKDEV(scull_major, scull_minor + index);

 cdev_init(&dev->cdev, &scull_fops);
 dev->cdev.owner = THIS_MODULE;
 dev->cdev.ops = &scull_fops;
 err = cdev_add (&dev->cdev, devno, 1);
 /* Fail gracefully if need be */
 if (err)
 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
} 

因為 cdev 結(jié)構(gòu)嵌在 struct scull_dev 里面, cdev_init 必須調(diào)用來進(jìn)行那個結(jié)構(gòu)的初始化.

3.4.2.?老方法

如果你深入瀏覽 2.6 內(nèi)核的大量驅(qū)動代碼, 你可能注意到有許多字符驅(qū)動不使用我們剛剛描述過的 cdev 接口. 你見到的是還沒有更新到 2.6 內(nèi)核接口的老代碼. 因為那個代碼實際上能用, 這個更新可能很長時間不會發(fā)生. 為完整, 我們描述老的字符設(shè)備注冊接口, 但是新代碼不應(yīng)當(dāng)使用它; 這個機(jī)制在將來內(nèi)核中可能會消失.

注冊一個字符設(shè)備的經(jīng)典方法是使用:


int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

這里, major 是感興趣的主編號, name 是驅(qū)動的名子(出現(xiàn)在 /proc/devices), fops 是缺省的 file_operations 結(jié)構(gòu). 一個對 register_chrdev 的調(diào)用為給定的主編號注冊 0 - 255 的次編號, 并且為每一個建立一個缺省的 cdev 結(jié)構(gòu). 使用這個接口的驅(qū)動必須準(zhǔn)備好處理對所有 256 個次編號的 open 調(diào)用( 不管它們是否對應(yīng)真實設(shè)備 ), 它們不能使用大于 255 的主或次編號.

如果你使用 register_chrdev, 從系統(tǒng)中去除你的設(shè)備的正確的函數(shù)是:


int unregister_chrdev(unsigned int major, const char *name);

major 和 name 必須和傳遞給 register_chrdev 的相同, 否則調(diào)用會失敗.

[11] 有一個早些的機(jī)制以避免使用 cdev 結(jié)構(gòu)(我們在"老方法"一節(jié)中討論).但是, 新代碼應(yīng)當(dāng)使用新技術(shù).

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號