糯米文學吧

位置:首頁 > 計算機 > 操作系統

Linux系統字符設備驅動框架筆記

不積跬步,何以至千里。掌握知識都是從很小的點開始的。下面是小編整理的Linux系統字符設備驅動框架筆記,歡迎閲讀!

Linux系統字符設備驅動框架筆記

字符設備是Linux三大設備之一(另外兩種是塊設備,網絡設備),字符設備就是字節流形式通訊的I/O設備,絕大部分設備都是字符設備,常見的字符設備包括鼠標、鍵盤、顯示器、串口等等,當我們執行 ls -l /dev 的時候,就能看到大量的設備文件, c 就是字符設備, b 就是塊設備,網絡設備沒有對應的設備文件。編寫一個外部模塊的字符設備驅動,除了要實現編寫一個模塊所需要的代碼之外,還需要編寫作為一個字符設備的代碼。

  驅動模型

Linux一切皆文件,那麼作為一個設備文件,它的操作方法接口封裝在 struct file_operations ,當我們寫一個驅動的時候,一定要實現相應的接口,這樣才能使這個驅動可用,Linux的內核中大量使用"註冊+回調"機制進行驅動程序的編寫,所謂註冊回調,簡單的理解,就是當我們open一個設備文件的時候,其實是通過VFS找到相應的inode,並執行此前創建這個設備文件時註冊在inode中的'open函數,其他函數也是如此,所以,為了讓我們寫的驅動能夠正常的被應用程序操作,首先要做的就是實現相應的方法,然後再創建相應的設備文件。

#include //for struct cdev

#include //for struct file

#include //for copy_to_user

#include //for error number

/* 準備操作方法集 */

/*

struct file_operations {

struct module *owner; //THIS_MODULE

//讀設備

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

//寫設備

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

//映射內核空間到用户空間

int (*mmap) (struct file *, struct vm_area_struct *);

//讀寫設備參數、讀設備狀態、控制設備

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

//打開設備

int (*open) (struct inode *, struct file *);

//關閉設備

int (*release) (struct inode *, struct file *);

//刷新設備

int (*flush) (struct file *, fl_owner_t id);

//文件定位

loff_t (*llseek) (struct file *, loff_t, int);

//異步通知

int (*fasync) (int, struct file *, int);

//POLL機制

unsigned int (*poll) (struct file *, struct poll_table_struct *);

。。。

};

*/

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)

{

return 0;

}

struct file fops = {

r = THIS_MODULE,

= myread,

...

};

/* 字符設備對象類型 */

struct cdev {

//public

struct module *owner; //模塊所有者(THIS_MODULE),用於模塊計數

const struct file_operations *ops; //操作方法集(分工:打開、關閉、讀/寫、...)

dev_t dev; //設備號(第一個)

unsigned int count; //設備數量

//private

...

};

static int __init chrdev_init(void)

{

...

/* 構造cdev設備對象 */

struct cdev *cdev_alloc(void);

/* 初始化cdev設備對象 */

void cdev_init(struct cdev*, const struct file_opeartions*);

/* 為字符設備靜態申請設備號 */

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

/* 為字符設備動態申請主設備號 */

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

MKDEV(ma,mi) //將主設備號和次設備號組合成設備號

MAJOR(dev) //從dev_t數據中得到主設備號

MINOR(dev) //從dev_t數據中得到次設備號

/* 註冊字符設備對象cdev到內核 */

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

...

}

static void __exit chrdev_exit(void)

{

...

/* 從內核註銷cdev設備對象 */

void cdev_del(struct cdev* );

/* 從內核註銷cdev設備對象 */

void cdev_put(stuct cdev *);

/* 回收設備號 */

void unregister_chrdev_region(dev_t from, unsigned count);

...

}

實現read,write

Linux下各個進程都有自己獨立的進程空間,即使是將內核的數據映射到用户進程,該數據的PID也會自動轉變為該用户進程的PID,由於這種機制的存在,我們不能直接將數據從內核空間和用户空間進行拷貝,而需要專門的拷貝數據函數/宏:

long copy_from_user(void *to, const void __user * from, unsigned long n)

long copy_to_user(void __user *to, const void *from, unsigned long n)

這兩個函數可以將內核空間的數據拷貝到回調該函數的用户進程的用户進程空間,有了這兩個函數,內核中的read,write就可以實現內核空間和用户空間的數據拷貝。

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)

{

long ret = 0;

size = size > MAX_KBUF?MAX_KBUF:size;

if(copy_to_user(user_buf, kbuf,size)

return -EAGAIN;

}

return 0;

}