天天看點

字元裝置驅動(2)字元裝置驅動裝置操作讀和寫字元裝置的登出使用cdev_del函數來完成。

字元裝置驅動

在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

字元裝置驅動(2)字元裝置驅動裝置操作讀和寫字元裝置的登出使用cdev_del函數來完成。

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);
           
字元裝置驅動(2)字元裝置驅動裝置操作讀和寫字元裝置的登出使用cdev_del函數來完成。

字元裝置的登出使用cdev_del函數來完成。

參數:

p: 要登出的字元裝置結構

繼續閱讀