字元裝置驅動 1

2021-09-20 02:57:49 字數 4523 閱讀 9242

字元裝置驅動(1)

成於堅持,敗於止步

linux 字元裝置驅動結構

cdev 結構體

在 linux 2.6 核心中使用 cdev 結構體描述字元裝置,cdev 結構體的定義如**所示。

1 struct cdev

2 ;cdev 結構體的 dev_t 成員定義了裝置號,為 32 位,其中高 12 位為主裝置號,低20 位為次裝置號。使用下列巨集可以從 dev_t 獲得主裝置號和次裝置號。 

major(dev_t dev)

minor(dev_t dev)

而使用下列巨集則可以通過主裝置號和裝置號生成 dev_t。

mkdev(int major, int minor)

cdev 結構體的另乙個重要成員 file_operations 定義了字元裝置驅動提供給虛擬檔案系統的介面函式。

linux 2.6 核心提供了一組函式用於操作 cdev 結構體,如下所示:

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

struct cdev *cdev_alloc(void);

void cdev_put(struct cdev *p);

int cdev_add(struct cdev *, dev_t, unsigned);

void cdev_del(struct cdev *);

cdev_init()函式用於初始化 cdev 的成員,並建立 cdev 和 file_operations 之間的連線,其源**如**所示。

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

2 cdev_alloc()函式用於動態申請乙個 cdev 記憶體,其源**如**所示。

1 struct cdev *cdev_alloc(void)

2 10 return p;

11 }

cdev_add()函式和 cdev_del()函式分別向系統新增和刪除乙個 cdev,完成字元裝置的註冊和登出。對 cdev_add()的呼叫通常發生在字元裝置驅動模組載入函式中,而對cdev_del()函式的呼叫則通常發生在字元裝置驅動模組解除安裝函式中。 

分配和釋放裝置號

在 調 用 cdev_add() 函式向系統註冊字元裝置之前,應首先呼叫register_chrdev_region()或 alloc_chrdev_region()函式向系統申請裝置號,這兩個函式的原型如下:

int register_chrdev_region(dev_t from, unsigned count, const char *name);

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

register_chrdev_region() 函 數 用 於 已 知 起 始 設 備 的 設 備 號 的 情 況 ; 而alloc_chrdev_region()用於裝置號未知,向系統動態申請未被占用的裝置號的情況。函式呼叫成功之後,會把得到的裝置號放入第乙個引數 dev 中。alloc_chrdev_region()與register_chrdev_region()對比的優點在於它會自動避開裝置號重複的衝突。

相反地,在呼叫 cdev_del() 函式從系統登出字元裝置之後 ,unregister_chrdev_region()應該被呼叫以釋放原先申請的裝置號,這個函式的原型如下:

void unregister_chrdev_region(dev_t from, unsigned count);

file_operations 結構體

file_operations 結構體中的成員函式是字元裝置驅動程式設計的主體內容,這些函式實際會在應用程式進行 linux 的 open()、write()、read()、close()等系統呼叫時最終被呼叫。file_operations 結構體目前已經比較龐大,它的定義如**所示。

read()函式用來從裝置中讀取資料,成功時函式返回讀取的位元組數,出錯時返回乙個負值。

write()函式向裝置傳送資料,成功時該函式返回寫入的位元組數。如果此函式未被實現,當使用者進行 write()系統呼叫時,將得到-einval 返回值。

readdir()函式僅用於目錄,裝置節點不需要實現它。

ioctl()提供裝置相關控制命令的實現(既不是讀操作也不是寫操作),當呼叫成功時,返回給呼叫程式乙個非負值。核心本身識別部分控制命令,而不必呼叫裝置驅動中的ioctl()。如果裝置不提供 ioctl()函式,對於核心不能識別的命令,使用者進行 ioctl()系統呼叫時將獲得-einval 返回值。

mmap()函式將裝置記憶體對映到程序記憶體中,如果裝置驅動未實現此函式,使用者進行 mmap()系統呼叫時將獲得-enodev 返回值。這個函式對於幀緩衝等裝置特別有意義。

當使用者空間呼叫 linux api 函式 open()開啟裝置檔案時,裝置驅動的 open()函式最終被呼叫。驅動程式可以不實現這個函式,在這種情況下,裝置的開啟操作永遠成功。與 open()函式對應的是 release()函式。

poll()函式一般用於詢問裝置是否可被非阻塞地立即讀寫。當詢問的條件未觸發時,使用者空間進行 select()和 poll()系統呼叫將引起程序的阻塞。

aio_read()和 aio_write()函式分別對與檔案描述符對應的裝置進行非同步讀、寫操作。

裝置實現這兩個函式後,使用者空間可以對該裝置檔案描述符呼叫 aio_read()、aio_write()等系統呼叫進行讀寫。

linux 字元裝置驅動的組成

在 linux 系統中,字元裝置驅動由如下幾個部分組成。

1.字元裝置驅動模組載入與解除安裝函式

在字元裝置驅動模組載入函式中應該實現裝置號的申請和 cdev 的註冊,而在解除安裝函式中應實現裝置號的釋放和 cdev 的登出。

工程師通常習慣將裝置定義為乙個裝置相關的結構體,其包含該裝置所涉及的cdev、私有資料及訊號量等資訊。常見的裝置結構體、模組載入和解除安裝函式形式如**所示。

1 //裝置結構體

2 struct ***_dev_t

3 ***_dev;

7 //裝置驅動模組載入函式

8 static int _ _init ***_init(void)

9 18 else

19 22

23 ret = cdev_add(&***_dev.cdev, ***_dev_no, 1); //註冊裝置

24 …

25 }

26 /裝置驅動模組解除安裝函式/

27 static void _ _exit ***_exit(void)

28 裝置驅動的讀函式中,filp 是檔案結構體指標,buf 是使用者空間記憶體的位址,該位址在核心空間不能直接讀寫,count 是要讀的位元組數,f_pos 是讀的位置相對於檔案開頭的偏移。 

裝置驅動的寫函式中,filp 是檔案結構體指標,buf 是使用者空間記憶體的位址,該位址在核心空間不能直接讀寫,count 是要寫的位元組數,f_pos 是寫的位置相對於檔案開頭的偏移。

由於核心空間與使用者空間的記憶體不能直接互訪,因此借助函式 copy_from_user()完成使用者空間到核心空間的複製,函式 copy_to_user()完成核心空間到使用者空間的複製。

copy_from_user()和 copy_to_user()的原型如下所示:

unsigned long copy_from_user(void *to, const void _ _user *from, unsigned long count);

unsigned long copy_to_user(void _ _user *to, const void *from, unsigned long count);

上述函式均返回不能被複製的位元組數,因此,如果完全複製成功,返回值為 0。

如果要複製的記憶體是簡單型別,如 char、int、long 等,則可以使用簡單的 put_user()和 get_user(),如下所示:

int val; //核心空間整型變數

…get_user(val, (int *) arg); //使用者空間到核心空間,arg 是使用者空間的位址

…put_user(val, (int *) arg); //核心空間到使用者空間,arg 是使用者空間的位址

讀和寫函式中的_ _user 是乙個巨集,表明其後的指標指向使用者空間,這個巨集定義如下:

#ifdef _ checker _

#else

#endif

i/o 控制函式的 cmd 引數為事先定義的 i/o 控制命令,而 arg 為對應於該命令的引數。例如對於序列裝置,如果 set_baudrate 是乙個設定波特率的命令,那後面的arg 就應該是波特率值。

在字元裝置驅動中,需要定義乙個 file_operations 的例項,並將具體裝置驅動的函式賦值給 file_operations 的成員,如**所示。

1 struct file_operations ***_fops =

2 ;圖 6.1 所示為字元裝置驅動的結構、字元裝置驅動與字元裝置以及字元裝置驅動與使用者空間訪問該裝置的程式之間的關係。

就到這裡了,o(∩_∩)o~

字元裝置驅動1

1 cdev 結構體linux2.6 核心使用 cdev 結構體描述字元裝置 struct cdev cdev 的定義在 其中dev t 定義了裝置號為32位 前12位為主裝置號,後 20位為次裝置號 可以使用如下巨集呼叫來獲得主 次裝置號 major dev t dev minor dev t d...

驅動開發 字元裝置1

函式的呼叫 open syscall define3 sys open char filename,inflag do sys open do file open path openat do tmpfile return fd finish open do dentry open open ino...

字元裝置驅動學習(1)

首先,以查詢方式的按鍵驅動開始字元裝置驅動的學習。一.寫出框架 對於驅動的學習,框架思想非常重要 1.1.file operation file operation 結構是乙個字元驅動如何應用程式建立連線.這個結構,定義在 linux fs.h中,是乙個函式指標的集合.每個開啟檔案與它自身的函式集合...