天天看點

一個BIO塊裝置驅動例子

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/errno.h>  
#include <linux/types.h>  
#include <linux/fcntl.h>  
#include <linux/vmalloc.h>  
#include <linux/hdreg.h>  
#include <linux/blkdev.h>  
#include <linux/blkpg.h>  
#include <asm/uaccess.h>  

#define BLK_NAME "ram_blk"  
#define BLK_MAJOR 222  
#define DISK_SECTOR_SIZE 512 //每扇區大小  
#define DISK_SECTOR 1024  //總扇區數,  
#define DISK_SIZE (DISK_SECTOR_SIZE*DISK_SECTOR)//總大小,共0.5M  

typedef struct//裝置結構體  
{  
       unsigned char          *data;  
       struct request_queue   *queue;  
       struct gendisk         *gd;  
} disk_dev;  

disk_dev device;//定義裝置結構體  

//--------------------------------------------------------------------------  
//在硬碟等帶柱面扇區等的裝置上使用request,可以整理隊列。但是ramdisk等可以  
//使用make_request  
static int disk_make_request(struct request_queue *q,struct bio *bio)  
{  
       int i;  
       char *mem_pbuf;  
       char *disk_pbuf;  
       disk_dev *pdevice;  
       struct bio_vec *pbvec;  
       /*在周遊段之前先判斷要傳輸資料的總長度大小是否超過範圍*/  
       i=bio->bi_sector*DISK_SECTOR_SIZE+bio->bi_size;  
       if(i>DISK_SIZE)//判斷是否超出範圍  
              goto fail;  

       pdevice=(disk_dev*)bio->bi_bdev->bd_disk->private_data;//得到裝置結構體  
       disk_pbuf=pdevice->data+bio->bi_sector*DISK_SECTOR_SIZE;//得到要讀寫的起始位置  

       /*開始周遊這個bio中的每個bio_vec*/  
       bio_for_each_segment(pbvec,bio,i)//循環分散的記憶體segment  
       {  
              mem_pbuf=kmap(pbvec->bv_page)+pbvec->bv_offset;//獲得實際記憶體位址  
              switch(bio_data_dir(bio))  
              {//讀寫  
                     case READA:  
                     case READ:  
                            memcpy(mem_pbuf,disk_pbuf,pbvec->bv_len);  
                            break;  
                     case WRITE:  
                            memcpy(disk_pbuf,mem_pbuf,pbvec->bv_len);  
                            break;  
                     default:  
                            kunmap(pbvec->bv_page);  
                            goto fail;  
              }  
              kunmap(pbvec->bv_page);//清除映射  
              disk_pbuf+=pbvec->bv_len;  
       }  
       bio_endio(bio,0);//這個函數2.6.25和2.6.4是不一樣的,  
       return 0;  
fail:  
       bio_io_error(bio);//這個函數2.6.25和2.6.4是不一樣的,  
       return 0;  
}  

int blk_open(struct block_device *dev, fmode_t no)   
{  
       return 0;  
}  

int blk_release(struct gendisk *gd, fmode_t no)  
{  
       return 0;  
}  

int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)  
{  
       return -ENOTTY;  
}  

static struct block_device_operations blk_fops=  
{  
       .owner=THIS_MODULE,  
       .open=blk_open,//  
       .release=blk_release,//  
       .ioctl=blk_ioctl,//   
};  

int disk_init(void)  
{  
        if(!register_blkdev(BLK_MAJOR,BLK_NAME));//注冊驅動  
    {  
         printk("register blk_dev succeed\n");  
    }  

       device.data=vmalloc(DISK_SIZE);  
       device.queue=blk_alloc_queue(GFP_KERNEL);//生成隊列  
       blk_queue_make_request(device.queue,disk_make_request);/*注冊make_request  綁定請求制造函數*/  

    printk("make_request succeed\n");  

       device.gd=alloc_disk(1);//生成gendisk  
       device.gd->major=BLK_MAJOR;//主裝置号  
       device.gd->first_minor=0;//此裝置号  
       device.gd->fops=&blk_fops;//塊檔案結構體變量  
       device.gd->queue=device.queue;//請求隊列  
       device.gd->private_data=&device;  
       sprintf(device.gd->disk_name,"disk%c",'a');//名字  
       set_capacity(device.gd,DISK_SECTOR);//設定大小  
       add_disk(device.gd);//注冊塊裝置資訊  
    printk("gendisk succeed\n");      
       return 0;  
}  

void disk_exit(void)  
{  

       del_gendisk(device.gd);  
       put_disk(device.gd);  
       unregister_blkdev(BLK_MAJOR,BLK_NAME);  
       vfree(device.data);  
        printk("free succeed\n");  

}  

module_init(disk_init);  
module_exit(disk_exit);  

MODULE_LICENSE("Dual BSD/GPL");  
           

繼續閱讀