日期:2014-05-16  浏览次数:20545 次

块设备驱动1--自编ramdisk(在linux-3.2.36上的新接口)

这是块设备驱动的第一期,我们就从ldd3的sbull开始吧,但是ldd3用的linux版本太老了,你直接用它的例子在linux-3.2.x上是很麻烦的。

我主要做的就是在高版本上的移植。

里面有个NOQUEUE宏,可以选择不用一个请求队列 。

自己对着ldd3中的讲解看看吧

/***********************************
 Copyright(C), 2013 LDP
 FileName:  bdev.c
 Author:    
 Date:          
 Description:  
 History:       
 Author       Date            Desc
************************************/
#include <linux/module.h>//MODULE_*
#include <linux/fs.h>//fops
#include <linux/init.h>//printk
#include <linux/slab.h>//kzalloc() kfree()
#include <linux/blkdev.h>//register_blkdev
#include <linux/bio.h>//strucut bio
#include <linux/spinlock.h>//spinlock
#include <linux/hdreg.h>    //HDIO_GETGEO

#include <asm/uaccess.h>//copy_to_user

#define DEV_NAME "vDisk"

#define NOQUEUE//不用一个请求队列 

#define vDisk_MINORS 1 //磁盘分区数

#define HARDSECT_SIZE       512 //硬件的扇区大小
#define KERNEL_SECTOR_SIZE    512 //内核与快设备驱动交互的扇区单位
#define SECTOR_CNT         1024 //扇区数
    
/*****************************************************
              module description 
*****************************************************/

MODULE_LICENSE("GPL");//GPL, GPL v2, GPL and additional rights, Dual BSD/GPL, Dual MPL/GPL, Proprietary.
MODULE_AUTHOR("...");
MODULE_DESCRIPTION("...");
MODULE_VERSION("...");
MODULE_ALIAS("...");

/****************************************************/

static int vDisk_major = 0;
module_param(vDisk_major, int, 0);

/***************************************************/
struct LDP_vDisk
{
    int size;
    u8 *data;

    struct gendisk *gd;
    struct request_queue *queue; 

    bool media_change;
    spinlock_t lock;
};

/****************************************************
               request operation
****************************************************/

static void vDisk_transfer(struct LDP_vDisk *dev, unsigned long sector,
        unsigned long nsect, char *buffer, int write)
{
    unsigned long offset = sector * KERNEL_SECTOR_SIZE;
    unsigned long nbytes = nsect * KERNEL_SECTOR_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 int vDisk_xfer_bio(struct LDP_vDisk *dev, struct bio *bio)
{
    int i;
    struct bio_vec *bvec;
    sector_t sector = bio->bi_sector;
    char *buffer;

    /* Do each segment independently. */
    bio_for_each_segment(bvec, bio, i) 
    {
        buffer = __bio_kmap_atomic(bio, i, KM_USER0);
        vDisk_transfer(dev, sector, bio_cur_bytes(bio) >> 9, buffer, bio_data_dir(bio) == WRITE);
        sector += bio_cur_bytes(bio) >> 9;
        __bio_kunmap_atomic(bio, KM_USER0);
    }

    return 0; /* Always "succeed" */
}

#ifdef NOQUEUE

static void vDisk_make_request(struct request_queue *q, struct bio *bio)
{
    struct LDP_vDisk *dev = q->queuedata;
    int status;

    status = vDisk_xfer_bio(dev, bio);
    bio_endio(bio, status);  
}

#else

static void vDisk_request(struct request_queue *q)
{
    struct request *req;
    //int sectors_xferred = 0;
    struct bio *bio;

    struct LDP_vDisk *dev = q->queuedata;

    req = blk_fetch_request(q);

    while (req != NULL)
    {
        if (req->cmd_type != REQ_TYPE_FS)//文件系统请求
        {
             __blk_end_request_all(req, 1);
             continue;
        }
        
        __rq_for_each_bio(bio, req)
        {
            vDisk_xfer_bio(dev, bio);
        }

        if (!__blk_end_request_cur(req, 0)) //通知设备层,
        {
            //add_disk_randomness(req->rq_disk);//for random
            req = blk_fetch_request(q);
        }
    }
}
#endif