1. 程式人生 > >6.1Linux字元裝置驅動結構

6.1Linux字元裝置驅動結構

cdev結構體

用於描述字元裝置

1 struct cdev
2 {
3 struct kobject kobj; /* 內嵌的kobject物件*/
4 struct module *owner; /*所屬模組*/
5 struct file_operations *ops; /*檔案操作結構體,定義了字元裝置驅動提供給虛擬檔案系統的介面函式*/
6 struct list_head list;
7 dev_t dev; /*32 位裝置號,其中高12 位為主裝置號,低20 位為次裝置號*/
8 unsigned int count;
9 };

從dev_t獲得主裝置號和次裝置號

MAJOR(dev_t dev)
MINOR(dev_t dev)

通過主裝置號和次裝置號生成dev_t

MKDEV(int major, int minor)

操作cdev結構體的函式

void cdev_init(struct cdev *, struct file_operations *);
//初始化cdev 的成員,並建立cdev 和file_operations 之間的連線

struct cdev *cdev_alloc(void);
動態申請一個cdev記憶體

void cdev_put(struct cdev *p);

int cdev_add(struct cdev *, dev_t, unsigned);
//向系統新增一個cdev,完成字元裝置的註冊。

void cdev_del(struct cdev *);
//向系統刪除一個cdev,完成字元裝置的登出。

呼叫cdev_add() 函式向系統註冊字元裝置之前,首先應該分配裝置號,

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);
//動態分配裝置號,裝置號未知,向系統動態申請未被佔用的裝置號

呼叫cdev_del() 函式從系統登出字元裝置之後,應釋放原先申請的裝置號

void unregister_chrdev_region(dev_t from, unsigned count);

file_operations結構體

在應用程式進行Linux 的open()、write()、read()、close()等系統呼叫時會呼叫file_operations結構體的成員函式。

1 struct file_operations
2 {
3 struct module *owner;
4 // 擁有該結構的模組的指標,一般為THIS_MODULES

5 loff_t(*llseek)(struct file *, loff_t, int);
6 // 用來修改檔案當前的讀寫位置,並將新位置返回,在出錯時,返回負值

7 ssize_t(*read)(struct file *, char _ _user *, size_t, loff_t*);
8 // 從裝置中同步讀取資料,成功時返回讀取的位元組數,出錯時返回負值

9 ssize_t(*write)(struct file *, const char _ _user *, size_t,loff_t*);
10 // 向裝置傳送資料,成功時返回寫入的位元組數。如果此函式未被實現,呼叫時返回-EINVAL值

11 ssize_t(*aio_read)(struct kiocb *, char _ _user *, size_t, loff_t);
13 ssize_t(*aio_write)(struct kiocb *, const char _ _user *, size_t,loff_t);
14 // 初始化一個非同步的讀取/寫入操作後,使用者空間可呼叫aio_read()、aio_write()進行讀寫。

15 int(*readdir)(struct file *, void *, filldir_t);
16 // 僅用於讀取目錄,對於裝置檔案,裝置節點不需要實現它,該欄位為NULL,

17 unsigned int(*poll)(struct file *, struct poll_table_struct*);
18 // 輪詢函式,判斷目前是否可以進行非阻塞的讀取或寫入

19 int(*ioctl)(struct inode *, struct file *, unsigned int, unsignedlong);
20 // 執行裝置I/O控制命令,呼叫成功時,返回非負值,防止裝置中有核心不能識別的命令

21 long(*unlocked_ioctl)(struct file *, unsigned int, unsigned long);
22 // 不使用BLK檔案系統,將使用此種函式指標代替ioctl

23 long(*compat_ioctl)(struct file *, unsigned int, unsigned long);
24 // 在64位系統上,32位的ioctl呼叫將使用此函式指標代替

25 int(*mmap)(struct file *, struct vm_area_struct*);
26 // 用於請求將裝置記憶體對映到程序地址空間,針對於幀緩衝等裝置

27 int(*open)(struct inode *, struct file*);
28 // 開啟

29 int(*flush)(struct file*);

30 int(*release)(struct inode *, struct file*);
31 // 關閉

32 int(*synch)(struct file *, struct dentry *, int datasync);
33 // 重新整理待處理的資料

34 int(*aio_fsync)(struct kiocb *, int datasync);
35 // 非同步fsync

36 int(*fasync)(int, struct file *, int);
37 // 通知裝置FASYNC標誌發生變化

38 int(*lock)(struct file *, int, struct file_lock*);

39 ssize_t(*readv)(struct file *, const struct iovec *, unsigned long,loff_t*);
40 ssize_t(*writev)(struct file *, const struct iovec *, unsigned long,loff_t*);
41 // readv和writev:分散/聚集型的讀寫操作

42 ssize_t(*sendfile)(struct file *, loff_t *, size_t, read_actor_t,void*);
43 // 通常為NULL

44 ssize_t(*sendpage)(struct file *, struct page *, int, size_t,loff_t *, int);
45 // 通常為NULL

46 unsigned long(*get_unmapped_area)(struct file *,unsigned long,unsigned long,
                                     unsigned long, unsigned long);
48 // 在程序地址空間找到一個將底層裝置中的記憶體段對映的位置

49 int(*check_flags)(int);
50 // 允許模組檢查傳遞給fcntl(F_SETEL...)呼叫的標誌

51 int(*dir_notify)(struct file *filp, unsigned long arg);
52 // 僅對檔案系統有效,驅動程式不必實現

53 int(*flock)(struct file *, int, struct file_lock*);
54 };

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

1 //裝置結構體
2 struct xxx_dev_t
3 {
4 struct cdev cdev;
5 ...
6 } xxx_dev;
7 //裝置驅動模組載入函式
8 static int _ _init xxx_init(void)
9 {
10 ...
11 cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev
12 xxx_dev.cdev.owner = THIS_MODULE;

13 //獲取字元裝置號
14 if (xxx_major)
15 {
16 register_chrdev_region(xxx_dev_no, 1, DEV_NAME);//靜態分配裝置號
17 }
18 else
19 {
20 alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);//動態分配裝置號
21 }
22
23 ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //註冊裝置
24 ...
25 }
26 /*裝置驅動模組解除安裝函式*/
27 static void _ _exit xxx_exit(void)
28 {
29 unregister_chrdev_region(xxx_dev_no, 1); //釋放佔用的裝置號
30 cdev_del(&xxx_dev.cdev); //登出裝置
31 ...
32 }

字元裝置驅動的file_operations 結構體中成員函式

file_operations 結構體中成員函式是字元裝置驅動與核心的介面,是使用者空間對Linux 進行系統呼叫最終的落實者。大多數字符裝置驅動會實現read()、write()和ioctl()函式

 /* 讀裝置*/
 ssize_t xxx_read(struct file *filp, char _ _user *buf, size_t count,loff_t*f_pos)
 {
 ...
copy_to_user(buf, .., ..);//核心空間與使用者空間的記憶體不能直接互訪,完成核心空間到使用者空間的複製
 ...
 }
/* 寫裝置*/
 ssize_t xxx_write(struct file *filp, const char _ _user *buf, size_tcount, loff_t *f_pos)
{
 ...
copy_from_user(.., buf, ..);//核心空間與使用者空間的記憶體不能直接互訪,完成使用者空間到核心空間的複製
 ...
 }
//在讀寫函式中,filp是檔案結構體指標,buf是使用者空間記憶體的地址,該地址在核心空間不能直接讀寫,
//count 是要讀寫的位元組數,f_pos 是讀寫的位置相對於檔案開頭的偏移。


 /* ioctl函式*/
 int xxx_ioctl(struct inode *inode, struct file *filp, unsigned int
cmd,
 unsigned long arg)
 {
 ...
switch (cmd)
 {
 case XXX_CMD1:
 ...
 break;
 case XXX_CMD2:
 ...
 break;
 default:
 /* 不能支援的命令*/
 return - ENOTTY;
 }
 return 0;
 }