一、 實現思路:
Linux將硬件設備看做一類特殊的文件(/dev/*),設備驅動程序被組織爲一組完成不同任務的函數的集合,通過這些函數使得linux的設備操作猶如文件一般。在應用程序看來,硬件設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬件設備進行操作,如open()、close()、read()、write() 等。
所以,一個字符( char ) 設備也就是一種可以當作一個字節流來存取的設備( 如同一個文件 ),可以通過一系列的文件操作實現字符設備驅動程序。
二、 實驗步驟:
(1) 設置內核支持模塊(make menuconfig),如圖2.1所示:
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
圖2.1 模塊支持的設置
不過,這一步可以省略,因爲所使用的linux系統――linux-up內核,默認支持模塊的。
(2) 所需頭文件及宏定義
#include <linux/kernel.h> // for kernel programming
#include <linux/module.h> // for kernel module struct.
#include <linux/fs.h> // struct file_operations
#define RWBUF_NAME “rwbuf” // 設備文件 /dev/rwbuf
#define RWBUF_DEV “/dev/rwbuf” // device path
#define RWBUF_MAJOR 60 // 主設備號
#define RWBUF_CLEAR 0x909090 // IO Ctrl Command
(3) 重要的數據結構:file_operations,file,inode
<linux/fs.h>中定義了這三個結構. struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); } file_operations 結構定義了一組函數指針,每一個打開的文件(用struct file表示)和它自己的一組函數(包含在一個叫f_op的域中,它指向一個 struct file_operations結構)相聯繫.這些操作都是來實現系統調用的,所以才被命名爲open,read,等等.對於那些不需要的 功能(比如你的設備不需要write功能,即不需要向設備寫數據),可以給write指針付NULL.
struct file
{ struct list_head f_list; struct dentry *f_dentry; struct vfsmount *f_vfsmnt; struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; int f_error; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; struct file_ra_state f_ra;
unsigned long f_version; void *f_security;
/* needed for tty driver, and maybe others */ void *private_data;
#ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct list_head f_ep_links; spinlock_t f_ep_lock; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; }; 每個打開的文件對應一個struct file.它在被打開時由內核創建,並傳給它所有可以操作該文件的函數,到文件被關閉時才被刪除.
The inode Structure. Inode結構是用來在內核內部表示文件的.同一個文件可以被打開好多次,所以可以對應很多struct file,但是隻對應一個struct inode.該結構裏面包含了很多信息,但是,驅動開發者只關心裏面兩個重要的域: dev_t i_rdev;//含有真正的設備號 struct cdev *i_cdev;//struct cdev是內核內部表示字符設備的結構.
(4) 註冊設備號
定義好major和minor number 後就可以在內核中註冊一個設備了.註冊一個字符設備需要用到下面幾個函數: int register_chrdev_region(dev_t first,unsigned int count,char *name); first是要註冊的設備號範圍的開始(其中minor號一般設置爲0),count是所申請的連續設備號的總數.name是設備的名稱.它會在/proc/devices中出現. int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name); 這個函數是用來動態分配設備號的.有餘開發者不知道所要用的major號是多少,便讓內核動態分配一個.參數dev是個output-only參數. void unregister_chrdev_region(dev_t first,unsigned int count); 一般在模塊清除函數中調用.
(5) 用戶接口實現
定義操作集合:
static struct file_operations rwbuf_fops =
{
open: rwbuf_open,
release: rwbuf_close,
read: rwbuf_read,
write: rwbuf_write,
ioctl: rwbuf_ioctl,
};
然後分別實現各個操作,詳見代碼。
(6) 編譯及加載設備模塊
編譯:
gcc -c rwbuf.c -D__KERNEL__ -DMODULE -DMODVERSIONS -Wall -include /usr/src/linux/include/linux/modversions.h -I/usr/src/linux/include
安裝與卸載:
mknod /dev/rwbuf c 60 0 創建設備文件
/sbin/insmod rwbuf.o 安裝設備驅動
/sbin/rmmod rwbuf 卸載設備驅動
(7) 測試驅動程序
void writer()
{
char yourmsg[1000]="Hello OYZ!";
char c;
int i = 0, h = 0, n = 0 ;
h=open(RWBUF_DEV,O_WRONLY);
n = write(h, yourmsg, sizeof(yourmsg)+1);
close(h);
printf("write OK!/n");
}
void reader()
{
char yourmsg[1000];
int h=open(RWBUF_DEV,O_RDONLY);
int n=read(h,yourmsg,sizeof(yourmsg));
close(h);
printf("read OK!/n");
}
void cleaner()
{
char yourmsg[1000];
int h=open(RWBUF_DEV,O_RDWR);
int n=ioctl(h,RWBUF_CLEAR,0);
close(h);
printf("rwbuf successfully removed!/n");
}
三、 實驗結果:
圖2.2 字符設備測試結果
測試結果如上圖2所示,可以說明字符驅動設備添加成功。
如果將rwbuf模塊卸載,則測試程序將不能輸入寫入的字符串,如下圖所示:
圖2.3 卸載先前已裝入的字符設備
|