PROGRAM FOR BLOCK DEVICE DRIVER OF DEVFS TYPE 對Linux的devfs類型的驅動程序的編寫可以從以下幾大內容理解和入手:
通過分析驅動程序源代碼可以發現驅動程序一般可分三部分:
核心數據結構;核心數據和資源的初始化,注冊以及注消,釋放;底層設備操作函數;
A.核心數據結構
strUCt file_operations fops 設備驅動程序接口
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char *, 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 (*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 (*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);
};
block_device_operations 塊設備驅動程序接口
{ int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t);
struct module *owner;
};塊設備的READ().WR99vE()不在這裡注冊,而是在設備的讀寫請求隊列裡注冊,內核在這裡將調用通用的blk_read(),blk_write().向讀寫隊列
發出讀寫請求.
Linux 利用這些數據結構向內核注冊open(),release(),ioctl(),check_media_change(),rvalidate()等函數的入口句柄.
我們將要編寫的open(),release(),ioctl(),check_media_change(),revalidate()等函數,將在驅動初始化的時候,
通過一個此結構類型的變量向內核提供函數的 入口.
struct request_queue_t 設備請求隊列的數據結構
struct request_list {
unsigned int count;
unsigned int pending[2];
struct list_head free;
};
struct request {
struct list_head queue;
int elevator_sequence;
kdev_t rq_dev;
int cmd; /* READ or WR99vE */
int errors;
unsigned long start_time;
unsigned long sector;
unsigned long nr_sectors;
unsigned long hard_sector, hard_nr_sectors;
unsigned int nr_segments;
unsigned int nr_hw_segments;
unsigned long current_nr_sectors, hard_cur_sectors;
void * special;
char * buffer;
struct completion * waiting;
struct buffer_head * bh;
struct buffer_head * bhtail;
request_queue_t *q;
};
struct request_queue
{
/*
* the queue request freelist, one for reads and one for writes
*/
struct request_list rq;
/*
* The total number of requests on each queue
*/
int nr_requests;
/*
* Batching threshold for sleep/wakeup decisions
*/
int batch_requests;
/*
* The total number of 512byte blocks on each queue
*/
atomic_t nr_sectors;
/*
* Batching threshold for sleep/wakeup decisions
*/
int batch_sectors;
/*
* The max number of 512byte blocks on each queue
*/
int max_queue_sectors;
/*
* Together with queue_head for cacheline sharing
*/
struct list_head queue_head;
elevator_t elevator;
request_fn_proc * request_fn;
merge_request_fn * back_merge_fn;
merge_request_fn * front_merge_fn;
merge_requests_fn * merge_requests_fn;
make_request_fn * make_request_fn;
plug_device_fn * plug_device_fn;
/*
* The queue owner gets to use this for whatever they like.
* ll_rw_blk doesn't touch it.
*/
void * queuedata;
/*
* This is used to remove the plug when tq_disk runs.
*/
struct tq_struct plug_tq;
/*
* Boolean that indicates whether this queue is plugged or not.
*/
int plugged:1;
/*
* Boolean that indicates whether current_request is active or
* not.
*/
int head_active:1;
/*
* Boolean that indicates you will use blk_started_sectors
* and blk_finished_sectors in addition to blk_started_io
* and blk_finished_io. It enables the throttling code to
* help keep the sectors in flight to a reasonable value
*/
int can_throttle:1;
unsigned long bounce_pfn;
/*
* Is meant to protect the queue in the future instead of
* io_request_lock
*/
spinlock_t queue_lock;
/*
* Tasks wait here for free read and write requests
*/
wait_queue_head_t wait_for_requests;
struct request *last_request;
};
緩沖區和對緩沖區相應的I/O操作在此任務隊列中相關聯,等待內核的調度.如果是字符設備就不需要此數據結構.而
塊設備的read(),write()函數則在buffer_queue的initize和設備請求隊列進行處理請求時候傳遞給request_fn().
struct request_queue_t{}設備請求隊列的變量類型,驅動程序在初始化的時候需要填寫request_fn().
其他的數據結構還有 I/O port,Irq,DMA 資源分配,符合POSIX標准的ioctl的cmd的構造和定義,以及描述設備自身的
相關數據結構定義-如設備的控制寄存器的相關數據結構定義,BIOS裡的參數定義,設備類型定義等.
B.初始化和注冊和注消,模塊方式驅動程序的加載和卸載.
設備驅動程序在定義了數據結構後 ,首先開始初始化:
如I/O 端口的檢查和登記,內核對 I/O PORT的檢查和登記提供了兩個 函數check_region(int io_port, int off_set)
和request_region(int io_port, int off_set,char *devname).I/O Port登記後,就可以用inb()和outb()進行操作了 .
還有DMA和Irq的初始化檢查和 登記,
int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),unsigned int long flags,
const char *device);
irq: 是要申請的中斷。
handle:中斷處理函數指針。
flags:SA_INTERRUPT 請求一個快速中斷,0 正常中斷。
device:設備名。
如果登記成功,返回0,這時在/proc/interrupts文件中可以看你請求的中斷。
DMA主要是在內存中分配交換內存空間.還有緩沖區,設備請求隊列的初始化.
還有設備控制寄存器的檢查和初始化,還有對設備自身相關的數據結構的初始化,填寫一些設備特定的數據等.
然後,開始注冊
devfs_register()向VFS注冊統一的設備操作函數.
static struct file_operations XXX_fops = {
owner: THIS_MODULE, XXX_fops所屬的設備模塊
read: XXX_read, 讀設備操作
write: XXX_write, 寫設備操作
ioctl: XXX_ioctl, 控制設備操作
mmap: XXX_mmap, 內存重映射操作
open: XXX_open, 打開設備操作
release: XXX_release 釋放設備操作
/* ... */
};
blk_init_queue()隊列初始化函數.
request_irq()中斷注冊函數
相應的注消函數:
devfs_unregister (devfs_handle_t de){};
free_irq()釋放中斷,I/O資源,釋放緩沖區,釋放設備,請求隊列,VFS節點等.
模塊方式驅動程序的加載和卸載.
owner: THIS_MODULE, XXX_fops所屬的設備模塊
read: XXX_read, 讀設備操作
write: XXX_write, 寫設備操作
ioctl: XXX_ioctl, 控制設備操作
mmap: XXX_mmap, 內存重映射操作
open: XXX_open, 打開設備操作
release: XXX_release 釋放設備操作
/* ... */
};
blk_init_queue()隊列初始化函數.
request_irq()中斷注冊函數
相應的注消函數:
devfs_unregister (devfs_handle_t de){};
free_irq()釋放中斷,I/O資源,釋放緩沖區,釋放設備,請求隊列,VFS節點等.
模塊方式驅動程序的加載和卸載.