1. 程式人生 > >Linux-Flash驅動(2)-塊裝置驅動例項分析

Linux-Flash驅動(2)-塊裝置驅動例項分析

在上一節課中,我們在記憶體中劃分出512kB作為一個塊裝置,並對它實現讀寫的操作。現在我們來具體分析這段程式碼。



#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>


#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>   /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/timer.h>
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>


MODULE_LICENSE("Dual BSD/GPL");


static int major = 0;


static int sect_size = 512;


static int nsectors = 1024; 


/*
* The internal representation of our device.
*/
struct blk_dev{
         int size;                        /* Device size in sectors */
         u8 *data;                        /* The data array */
         struct request_queue *queue;     /* The device request queue */
         struct gendisk *gd;              /* The gendisk structure */
};


struct blk_dev *dev;




/*
* Handle an I/O request, in sectors.
*/
static void blk_transfer(struct blk_dev *dev, unsigned long sector,
   unsigned long nsect, char *buffer, int write)
{
unsigned long offset = sector*sect_size;
unsigned long nbytes = nsect*sect_size;


if ((offset + nbytes) > dev->size) {
printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
return;
}
if (write)
memcpy(dev->data + offset, buffer, nbytes);
else
memcpy(buffer, dev->data + offset, nbytes);
}


/*
* 讀寫請求處理函式
*/
static void blk_request(struct request_queue *q)
{
struct request *req;


//從佇列中取出要處理的一個請求
req = blk_fetch_request(q);
while (req != NULL) {
struct blk_dev *dev = req->rq_disk->private_data;


blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));
   
if(!__blk_end_request_cur(req, 0)) 
{
req = blk_fetch_request(q);
}
}
}




/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;


/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
blk_transfer(dev, sector, bio_cur_bytes(bio)>>9 /* in sectors */, 
buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_bytes(bio)>>9; /* in sectors */
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}


/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{
struct bio *bio;
int nsect = 0;
     
__rq_for_each_bio(bio, req) {
blk_xfer_bio(dev, bio);
nsect += bio->bi_size/sect_size;
}
return nsect;
}






/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = {
.owner            = THIS_MODULE,
};




/*
* Set up our internal device.
*/
static void setup_device()
{
//計算裝置大小
dev->size = nsectors*sect_size;
dev->data = vmalloc(dev->size);
if (dev->data == NULL) {
printk (KERN_NOTICE "vmalloc failure.\n");
return;
}


//把塊裝置放入請求佇列中,blk_request用於指明處理這個請求的函式
dev->queue = blk_init_queue(blk_request, NULL);
if (dev->queue == NULL)
goto out_vfree;


//指明裝置的扇區大小
blk_queue_logical_block_size(dev->queue, sect_size);
dev->queue->queuedata = dev;


//分配gendisk結構
dev->gd = alloc_disk(1);
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failure\n");
goto out_vfree;
}


/*初始化alloc_disk*/
dev->gd->major = major;//主裝置號
dev->gd->first_minor = 0;//次裝置號
dev->gd->fops = &blk_ops;//操作函式集
dev->gd->queue = dev->queue;//請求佇列
dev->gd->private_data = dev;//私有資料
sprintf (dev->gd->disk_name, "simp_blk%d", 0);//磁碟名字
set_capacity(dev->gd, nsectors*(sect_size/sect_size));//扇區數


//註冊塊裝置
add_disk(dev->gd);
return;


out_vfree:
if (dev->data)
vfree(dev->data);
}


static int __init blk_init(void)
{
/*
* 註冊塊裝置,申請主裝置號
*/
major = register_blkdev(major, "blk");
if (major <= 0) {
printk(KERN_WARNING "blk: unable to get major number\n");
return -EBUSY;
}
//申請一個描述結構(不是每個塊裝置都有)
dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
if (dev == NULL)
goto out_unregister;
//安裝這個裝置
setup_device();
     
return 0;


out_unregister:
unregister_blkdev(major, "sbd");
return -ENOMEM;
}


static void blk_exit(void)
{


if (dev->gd) {
del_gendisk(dev->gd);
put_disk(dev->gd);
}
if (dev->queue)
blk_cleanup_queue(dev->queue);
if (dev->data)
vfree(dev->data);


nregister_blkdev(major, "blk");
kfree(dev);
}


module_init(blk_init);
module_exit(blk_exit);
這是一個模組的程式,先看看模組初始化裡面的blk_init函式,裡面做了這幾件事:

1、註冊一個快裝置register_blkdev,如果沒有分配到主裝置號則列印錯誤資訊

2、然後是申請一個結構,這個結構是用來儲存這個塊裝置資訊的,不是每個塊裝置都有

3、再安裝這個裝置setup_device,這個函式是自定義的

    3.1完成塊裝置大小的計算

    3.2把快裝置放入請求佇列中(IO排程層把請求排序後放入請求佇列中,裡面的引數blk_request是一個函式,用於指明使用哪個函式對這個請求進行處理)

    3.3指明裝置的扇區大小

    3.4然後用alloc_disk函式分配一個gendisk結構(一個驅動可能對於幾個塊裝置,用gendisk來區分)

    3.5緊接著需要對這個結構進行初始化,如下:

	/*初始化alloc_disk*/
	dev->gd->major = major;//主裝置號
	dev->gd->first_minor = 0;//次裝置號
	dev->gd->fops = &blk_ops;//操作函式集
	dev->gd->queue = dev->queue;//請求佇列
	dev->gd->private_data = dev;//私有資料
	sprintf (dev->gd->disk_name, "simp_blk%d", 0);//磁碟名字
	set_capacity(dev->gd, nsectors*(sect_size/sect_size));//扇區數

3.6註冊這個塊裝置

第二個重要的函式是實現讀寫請求處理,讀寫請求通過blk_request函式來實現:

1、使用blk_fetch_request從佇列中取出要處理的一個請求

2、使用blk_transfer實現對對應扇區的硬體操作,比如讀和寫,應該這裡的塊裝置是記憶體模擬的,所以使用的是memcpy函式

3、使用__blk_end_request_cur判斷請求佇列是否還有請求要處理,如果有則繼續處理,沒有退出。