字元裝置驅動
在Linux字元裝置驅動程式設計中,有4種非常重要的資料結構:
Struct file
Struct inode
Struct cdev
Struct file_operations
Struct File(sbh_p92)
代表一個打開的檔案,系統中每個打開的檔案在核心空間都有一個關聯的 struct file。它由核心在打開檔案時建立, 在檔案關閉後,核心釋放這個資料結構。
重要成員:
1 loff_t f_pos /檔案讀寫位置/
2 struct file_operations *f_op //和檔案關聯的操作
3 unsigned int f_flags;//檔案辨別,如O_RDONLY,O_NOBLOCK,O_SYNC,驅動中通常判斷O_NOBLOCK來确定使用者的請求是否阻塞。而讀寫權限通常通過f_mode判斷。
4 mode_t f_mode;//檔案是否可讀或可寫,如FMODE_READ,FMODE_WRITE。核心在調用驅動程式的read write 之前已經檢查了通路權限,沒有對應的權限會被拒絕,是以驅動程式無需對該字段做額外的判斷。
5 void *private_data;
you can use the field to point to allocated data, private_data is a useful resource for preserving state information across system calls
struct inode(sbh_p93)
用來表示實體上存在的檔案。是以, 它和代表打開檔案描述符的file是不同的。一個實體上存在的檔案可以有多個打開描述符file, 隻但有一個inode 結構。
重要成員:
1 dev_t i_rdev ;裝置号(高12位為主裝置号,低20位為次裝置号)
2 struct cdev *icdev;對應着字元裝置的cdev結構體指針
由inode獲得主次裝置号的方法
Unsigned int iminor(struct inode *inode);
Unsigned int imajor(struct inode *inode);
檢視/proc/devices可檢視系統的注冊的裝置的主裝置号
ls /dev –l 所列出的内容中,日期的前2列是裝置的主、次裝置号。
Documents/devices.txt 描述了裝置号的配置設定情況。
struct cdev(sbh_p115)
重要成員:
1 struct module *owner ;
2 struct file_operations *ops;
3 dev_t dev; //裝置号
操作cdev的函數:
1 void cdev_init(struct cdev*,struct file_operation*); //裝置注冊
2 int cdev_add(struct cdev*,dev_t dev,unsigned int no);//裝置添加
3 void cdev_del(struct cdev*);//裝置移除
裝置注冊
在linux 2.6核心中,字元裝置使用 struct cdev 來描述。
字元裝置的注冊可分為如下3個步驟:
1、配置設定cdev
2、初始化cdev
3、添加cdev
裝置注冊(配置設定)
方法1、Struct cdev的配置設定可使用cdev_alloc函數來完成。
方法2、可以直接定義一個struct cdev 類型的變量。—通常使用
裝置注冊(初始化)
Struct cdev的初始化使用cdev_init函數來完成。
參數:
cdev: 待初始化的cdev結構
fops: 裝置對應的操作函數集
裝置注冊(添加)
struct cdev的注冊使用cdev_add函數來完成。
參數:
p: 待添加到核心的字元裝置結構
dev: 裝置号
count: 添加的裝置個數
struct file_operations(sbh_p116)
一個函數指針的集合,定義能在裝置上進行的操作。結構中的成員指向驅動中的函數, 這些函數實作一個特别的操作, 對于不支援的操作保留為 NULL。
例如:
static const struct file_operations globalmem_fops ={
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
裝置操作
int (*open)(struct inode *, struct file *)
這是操作在裝置檔案上的第一個操作,然而并不要求驅動程式一定要聲明這個方法。如果該項為NULL,裝置的打開操作永遠成功。
Open方法是驅動程式用來為以後的操作完成初始化準備工作的。此外,open還會增加裝置計數,以便防止檔案在關閉前子產品被解除安裝出核心。
在大部分驅動程式中,open完成如下工作:
1、初始化裝置。
2、标明次裝置号。
3、增加使用計數。
int (*release)(struct inode *, struct file *)
當裝置檔案被關閉時調用這個操作。與open相反,release也可以沒有。
Release方法的作用正好與open相反。這個裝置方法有時也稱為close。它應該:
1、使用計數減1。
2、關閉裝置。
int (*read)(struct file *filp, char __user *buf, size_t size, loff_t *ppos);
用來從裝置中讀取資料。當其為NULL指針時将引起read系統調用傳回
-EINVAL。函數傳回一個非負值表示成功的讀取了多少位元組
int (*write)(struct file *filp, const char __user *buf, size_t size, loff_t *ppos);
向裝置發送資料。如果沒有這個函數,write系統調用向調用程式傳回一個-EINVAL。
int (*ioctl)(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg);
系統調用ioctl提供調用裝置相關指令的方法,對于任何核心沒有定義的請求,ioctl系統調用将傳回-EINVAL。當調用成功時,傳回給調用程式一個非負傳回值。
最後一個參數unsigned long是指令所帶的參數。
注意: cmd為2時傳不過去。
int (*lseek)(struct file *filp, loff_t offset, int orig);
用來修改一個檔案的目前讀寫位置,并将新位置做為傳回值。
Int (mmap)(struct file,struct vm_area_struct*);
與使用者空間的mmap()對應,将核心位址空間映射到使用者空間,使使用者空間能直接通路裝置的實體位址。
(等講到11.4.4節再講)
讀和寫
int (*read)(struct file *filp, char __user *buf, size_t size, loff_t *ppos);
int (*write)(struct file *filp, const char __user *buf,
size_t size, loff_t *ppos);
對于 2 個方法, filp是檔案指針, count是請求的傳輸資料大小。buff 參數指向資料緩存。最後, offp 它指出使用者正在存取的檔案位置。
Read 和 Write 方法的 buff 參數是使用者空間指針。是以, 它不能被核心代碼直接引用。
驅動必須能夠存取使用者空間緩存以完成它的工作。但是, 為安全起見這個存取必須使用特殊的,核心提供的函數,例如:
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);
字元裝置的登出使用cdev_del函數來完成。
參數:
p: 要登出的字元裝置結構