块设备驱动实例

摘自:http://my.oschina.net/heiden/blog/194505
将ldd3(linxu device driver 3)中块设备驱动部分的代码在redhat6.3下编译时,会出现很多问题,主要是由于ldd3示例代码使用的内核版本较低(2.6.10版本),

对于块设备子系统,很多接口都已经发生了改变,

主要有如下变化:

1,elv_next_request已删除,使用如下方式从请求队列中获取请求

struct request *req;

req =  blk_fetch_request (q);

while(req != NULL){

    struct sbull_dev *dev = req->rq_disk->private_data;

    if(req->cmd_type != REQ_TYPE_FS){

        printk(KERN_NOTICE "Skip non-fs request\n");

        blk_end_request_all(req,-EIO);

        continue;

    }

    sbull_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 = NULL;

    }

}

其中blk_peek_request从队列中获取请求,但是不会从队列中删除该请求。

从队列中删除请求需要调用blk_start_request(),blk_fetch_request 相当于调用blk_peek_request,和blk_start_request()

具体参考:http://lwn.net/Articles/333620/,

2,end_request,改成了blk_end_request_all

3,现在不能直接获取request结构的sector和current_nr_sector;而是通过函数blk_rq_pos和blk_rq_cur_sectors获得,所以将对用两处改为:

sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

4,bio_endio参数过多

5,blk_queue_hardsect_size定义已删除,使用blk_queue_logical_block_size(dev->queue, hardsect_size);

6,block_device_operation接口已经发生了非常大的变化

新接口如下:(该接口包含getgeo函数,从而不再需要在ioctl函数实现HDIO_GETGEO命令)

struct block_device_operations {

 int (*open) (struct block_device *, fmode_t);

 int (*release) (struct gendisk *, fmode_t);

 int (*locked_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

 int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

 int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

 int (*direct_access) (struct block_device *, sector_t,

 void **, unsigned long *);

 int (*media_changed) (struct gendisk *);

 unsigned long long (*set_capacity) (struct gendisk *,

 unsigned long long);

 int (*revalidate_disk) (struct gendisk *);

 int (*getgeo)(struct block_device *, struct hd_geometry *);

 /* this callback is with swap_lock and sometimes page table lock held */

 void (*swap_slot_free_notify) (struct block_device *, unsigned long);

 struct module *owner;

};

编译通过后就可以使用该设备,例如

创建分区:

fdisk /dev/sbulla

创建文件系统

mkfs.ext3 /dev/sbulla1

修改后的代码如下:

#include 

#include 

#include 



#include 

#include 

#include 

#include 

#include 

#include 

#include  /* size_t */

#include  /* O_ACCMODE */

#include  /* HDIO_GETGEO */

#include 

#include 

#include 

#include 

#include  /* invalidate_bdev */

#include 



MODULE_LICENSE("Dual BSD/GPL");



static int sbull_major = 0;

module_param(sbull_major,int,0);



static int hardsect_size =512;

module_param(hardsect_size,int,0);



static int nsectors = 1024;

module_param(nsectors,int,0);



static int ndevices = 4;

module_param(ndevices,int,0);



enum {

 RM_SIMPLE = 0,

 RM_FULL = 1,

 RM_NOQUEUE = 2,

};



static int request_mode = RM_SIMPLE;

module_param(request_mode, int, 0);



#define SBULL_MINORS 16







#define KERNEL_SECTOR_SIZE 512





#define INVALIDATE_DELAY 30*HZ



struct sbull_dev{

   int size;

   u8 *data;

   short users;

   short media_change;

   spinlock_t lock;

   struct request_queue *queue;

   struct gendisk *gd;

   struct timer_list timer;

};



static struct sbull_dev *Devices = NULL;



static void sbull_transfer(struct sbull_dev *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 void sbull_request(struct request_queue *q)

{

    struct request *req;

    req = blk_fetch_request(q);

    while(req != NULL){

        struct sbull_dev *dev = req->rq_disk->private_data;

        if(req->cmd_type != REQ_TYPE_FS){

            printk(KERN_NOTICE "Skip non-fs request\n");

            blk_end_request_all(req,-EIO);

            continue;

        }

        sbull_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 = NULL;

        }



    }

}



static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)

{

   int i;

   struct bio_vec *bvec;

   sector_t sector = bio->bi_sector;



   bio_for_each_segment(bvec,bio,i){

      char *buffer = __bio_kmap_atomic(bio,i,KM_USER0);

      sbull_transfer(dev,sector,bio_cur_bytes(bio)/KERNEL_SECTOR_SIZE,buffer,bio_data_dir(bio) == WRITE);

      sector += bio_cur_bytes(bio)/KERNEL_SECTOR_SIZE;

      __bio_kunmap_atomic(bio,KM_USER0);

   }

   return 0;

}



static int sbull_xfer_request(struct sbull_dev *dev,struct request *req)

{

    struct bio *bio;

    int nsect = 0;

    __rq_for_each_bio(bio,req){

       sbull_xfer_bio(dev,bio);

       nsect += bio->bi_size / KERNEL_SECTOR_SIZE;

    }

    return nsect;

}



static void sbull_full_request(struct request_queue *q)

{

   struct request *req;

   struct sbull_dev *dev = q->queuedata;

   req = blk_fetch_request(q);

   while(req  != NULL){

       if(req->cmd_type != REQ_TYPE_FS){

            printk(KERN_NOTICE "Skip non-fs request\n");

             blk_end_request_all(req,-EIO);

            continue;

       }

       sbull_xfer_request(dev,req);

       if(!blk_end_request_cur(req,0)){

          req = NULL;

       }

   }

}



static int sbull_make_request(struct request_queue *q,struct bio *bio)

{

   struct sbull_dev *dev = q->queuedata;

   int status;

   status = sbull_xfer_bio(dev,bio);

   bio_endio(bio,status);

   return 0;

}



static int sbull_open(struct block_device *bd, fmode_t mode)

{

    struct sbull_dev *dev = bd->bd_disk->private_data;

    del_timer_sync(&dev->timer);

    spin_lock(&dev->lock);

    if(!dev->users)

        check_disk_change(bd);

    dev->users++;

    spin_unlock(&dev->lock);

    return 0;

}



static int sbull_release(struct gendisk *gd, fmode_t mode)

{

    struct sbull_dev *dev = gd->private_data;

    spin_lock(&dev->lock);

    dev->users--;

    if(!dev->users){

        dev->timer.expires = jiffies + INVALIDATE_DELAY;

        add_timer(&dev->timer);

    }

    spin_unlock(&dev->lock);

    return 0;

}



int sbull_media_change(struct gendisk *gd)

{

    struct sbull_dev *dev = gd->private_data;

    return dev->media_change;

}



int sbull_revalidate(struct gendisk *gd)

{

    struct sbull_dev *dev = gd->private_data;

    if(dev->media_change){

        dev->media_change = 0;

        memset(dev->data,0,dev->size);

    }

    return 0;

}



void sbull_invalidate(unsigned long ldev)

{

    struct sbull_dev *dev = (struct sbull_dev *)ldev;

    spin_lock(&dev->lock);

    if(dev->users || !dev->data)

        printk (KERN_WARNING "sbull: timer sanity check failed\n");

    else

        dev->media_change = 1;

    spin_unlock(&dev->lock);

}



int sbull_ioctl(struct block_device *bd, fmode_t mode, unsigned cmd, unsigned long arg)

{

    return 0;

}

static int sbull_getgeo(struct block_device *bd, struct hd_geometry *geo)

{

    long size;

    struct sbull_dev *dev = bd->bd_disk->private_data;

    size = dev->size *(hardsect_size / KERNEL_SECTOR_SIZE);

    geo->cylinders = (size & ~0x3f) >> 6;

    geo->heads = 4;

    geo->sectors = 16;

    geo->start = 4;

    return 0;



}

static struct block_device_operations sbull_ops = {

    .owner = THIS_MODULE,

    .open = sbull_open,

    .release = sbull_release,

    .media_changed = sbull_media_change,

    .revalidate_disk = sbull_revalidate,

    .ioctl = sbull_ioctl,

    .getgeo = sbull_getgeo

};



static void setup_device(struct sbull_dev *dev,int which)

{

    printk(KERN_WARNING "sbull: step into into setup_device\n");

    memset(dev,0,sizeof(struct sbull_dev));

    dev->size = nsectors * hardsect_size;

    dev->data = vmalloc(dev->size);

    if(dev->data == NULL){

        printk(KERN_NOTICE "vmalloc failure\n");

        return;

    }

    spin_lock_init(&dev->lock);

    init_timer(&dev->timer);

    dev->timer.data = (unsigned long)dev;

    dev->timer.function = sbull_invalidate;

    switch(request_mode){

    case RM_NOQUEUE:

        dev->queue = blk_alloc_queue(GFP_KERNEL);

        if(dev->queue == NULL)

            goto out_vfree;

        blk_queue_make_request(dev->queue,sbull_make_request);

        break;

    case RM_FULL:

        dev->queue = blk_init_queue(sbull_full_request,&dev->lock);

        if(dev->queue == NULL)

            goto out_vfree;

        break;

    case RM_SIMPLE:

        dev->queue = blk_init_queue(sbull_request,&dev->lock);

        if(dev->queue == NULL)

            goto out_vfree;

        break;

    default:

        printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);

    }

    blk_queue_logical_block_size(dev->queue,hardsect_size);

    dev->queue->queuedata = dev;



    dev->gd = alloc_disk(SBULL_MINORS);

    if(!dev->gd){

        printk(KERN_NOTICE "alloc_disk failure\n");

        goto out_vfree;

    }



    dev->gd->major = sbull_major;

    dev->gd->first_minor = which * SBULL_MINORS;

    dev->gd->fops = &sbull_ops;

    dev->gd->queue = dev->queue;

    dev->gd->private_data = dev;

    snprintf(dev->gd->disk_name,32,"sbull%c",which + 'a');

    set_capacity(dev->gd,nsectors * (hardsect_size / KERNEL_SECTOR_SIZE));

    add_disk(dev->gd);

    return;

out_vfree:

    if(dev->data)

        vfree(dev->data);

}



static int __init sbull_init(void)

{

    int i;

    printk(KERN_WARNING "sbull: start init\n");

    sbull_major = register_blkdev(sbull_major,"sbull");

    if (sbull_major <= 0) {

 printk(KERN_WARNING "sbull: unable to get major number\n");

 return -EBUSY;

 }

 printk(KERN_WARNING "sbull: start kmalloc\n");

 Devices = kmalloc(ndevices * sizeof(struct sbull_dev),GFP_KERNEL);

 if(Devices == NULL)

        goto out_unregister;

    printk(KERN_WARNING "sbull: start setup_device\n");

    for(i = 0; i < ndevices; i++)

        setup_device(Devices + i,i);

    return 0;

out_unregister:

    unregister_blkdev(sbull_major,"sbd");

    return -ENOMEM;

}

static void sbull_exit(void)

{

 int i;



 for (i = 0; i < ndevices; i++) {

 struct sbull_dev *dev = Devices + i;



 del_timer_sync(&dev->timer);

 if (dev->gd) {

 del_gendisk(dev->gd);

 put_disk(dev->gd);

 }

 if (dev->queue) {

 if (request_mode == RM_NOQUEUE)

 kobject_put(&(dev->queue)->kobj);

 else

 blk_cleanup_queue(dev->queue);

 }

 if (dev->data)

 vfree(dev->data);

 }

 unregister_blkdev(sbull_major, "sbull");

 kfree(Devices);

}



module_init(sbull_init);

module_exit(sbull_exit);

其原始版本
摘自:http://www.linuxidc.com/Linux/2010-12/30792.htm

/*
 * Sample disk driver, from the beginning.
 */

#include 
#include 
#include 
#include 
#include 
#include     /* printk() */
#include         /* kmalloc() */
#include         /* everything... */
#include     /* error codes */
#include 
#include     /* size_t */
#include     /* O_ACCMODE */
#include     /* HDIO_GETGEO */
#include 
#include 
#include 
#include 
#include     /* invalidate_bdev */
#include 

MODULE_LICENSE("Dual BSD/GPL");

static int sbull_major = 0;
module_param(sbull_major, int, 0);
static int hardsect_size = 512;
module_param(hardsect_size, int, 0);
static int nsectors = 25600;    /* How big the drive is */
module_param(nsectors, int, 0);
static int ndevices = 1;
module_param(ndevices, int, 0);

/*
 * The different "request modes" we can use.
 */
enum {
    RM_SIMPLE  = 0,    /* The extra-simple request function */
    RM_FULL    = 1,    /* The full-blown version */
    RM_NOQUEUE = 2,    /* Use make_request */
};
//static int request_mode = RM_FULL;
static int request_mode = RM_SIMPLE;
//static int request_mode = RM_SIMPLE;
module_param(request_mode, int, 0);

/*
 * Minor number and partition management.
 */
#define SBULL_MINORS    16
#define MINOR_SHIFT    4
#define DEVNUM(kdevnum)    (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT

/*
 * We can tweak our hardware sector size, but the kernel talks to us
 * in terms of small sectors, always.
 */
#define KERNEL_SECTOR_SIZE    512

/*
 * After this much idle time, the driver will simulate a media change.
 */
#define INVALIDATE_DELAY    60*HZ

/*
 * The internal representation of our device.
 */
struct sbull_dev {
        int size;                       /* Device size in sectors */
        // data 是本程序模拟的块设备,是一片连续的虚拟空间
        // 在初始化函数里分配的虚拟地址连续的内存空间
        u8 *data;                       /* The data array */
        short users;                    /* How many users */
        short media_change;             /* Flag a media change? */
        spinlock_t lock;                /* For mutual exclusion */
        struct request_queue *queue;    /* The device request queue */
        struct gendisk *gd;             /* The gendisk structure */
        struct timer_list timer;        /* For simulated media changes */
};

static struct sbull_dev *Devices = NULL;

/*
 * Handle an I/O request.
 */
static void sbull_transfer(struct sbull_dev *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;
    }
    // 实际的读写操作
    // 由于本程序是用一片连续的内存空间模拟块设备
    // 所以这里对硬件(内存空间)的读写操作,就是复制内存
    // 在具体点,就是下面的memcpy
    // 具体的项目,需修改为具体的接口函数
    if (write)
        // 写
        memcpy(dev->data + offset, buffer, nbytes);
    else
        // 读
        memcpy(buffer, dev->data + offset, nbytes);
}

/*The simple form of the request function.*/

static void sbull_request(struct request_queue *q)
{
    struct request *req;

    // 服务完队列上的所有请求
    while ((req = elv_next_request(q)) != NULL) {  // elv_next_request :从队列上去一个下来
        struct sbull_dev *dev = req->rq_disk->private_data;
        if (! blk_fs_request(req)) {
            printk (KERN_NOTICE "Skip non-fs request\n");
            end_request(req, 0);
            continue;
        }
        sbull_transfer(dev, req->sector, req->current_nr_sectors,
                req->buffer, rq_data_dir(req));
        end_request(req, 1);
    }
}


/*
 * Transfer a single BIO.
 */
static int sbull_xfer_bio(struct sbull_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);
        sbull_transfer(dev, sector, bio_cur_sectors(bio),
                buffer, bio_data_dir(bio) == WRITE);
        sector += bio_cur_sectors(bio);
        __bio_kunmap_atomic(bio, KM_USER0);
    }
    return 0; /* Always "succeed" */
}

/*
 * Transfer a full request.
 */
static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
{
    struct bio *bio;
    int nsect = 0;

    // steps through each bio that makes up a request.
    // 遍历
    __rq_for_each_bio(bio, req) {
        sbull_xfer_bio(dev, bio);
        nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
    }
    return nsect;
}


/*
 * Smarter request function that "handles clustering".
 */
static void sbull_full_request(struct request_queue *q)
{
    struct request *req;
    int sectors_xferred;
    struct sbull_dev *dev = q->queuedata;

    printk("<0>""in %s\n",__FUNCTION__);
    while ((req = elv_next_request(q)) != NULL) {
        if (! blk_fs_request(req)) {
            printk (KERN_NOTICE "Skip non-fs request\n");
            end_request(req, 0);
            continue;
        }
        sectors_xferred = sbull_xfer_request(dev, req);
        __blk_end_request(req,0,sectors_xferred<<9);//add by lht for 2.6.27
    }
}



//The direct make request version
static int sbull_make_request(struct request_queue *q, struct bio *bio)
{
    struct sbull_dev *dev = q->queuedata;
    int status;

    status = sbull_xfer_bio(dev, bio);
    //bio_endio(bio, bio->bi_size, status);
    bio_endio(bio, status);
    return 0;
}


/*
 * Open and close.
 */

static int sbull_open(struct inode *inode, struct file *filp)
{
    struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
    //printk("<0>" "fdfjdlksjfdlkj\n");    
    del_timer_sync(&dev->timer);
    filp->private_data = dev;
    spin_lock(&dev->lock);
    if (! dev->users) 
        check_disk_change(inode->i_bdev);
    dev->users++;
    spin_unlock(&dev->lock);
    return 0;
}

static int sbull_release(struct inode *inode, struct file *filp)
{
    struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;

    spin_lock(&dev->lock);
    dev->users--;

    if (!dev->users) {
        dev->timer.expires = jiffies + INVALIDATE_DELAY;
        add_timer(&dev->timer);
    }
    spin_unlock(&dev->lock);

    return 0;
}

/*
 * Look for a (simulated) media change.
 */
int sbull_media_changed(struct gendisk *gd)
{
    struct sbull_dev *dev = gd->private_data;

    return dev->media_change;
}

/*
 * Revalidate.  WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
 * with open.  That needs to be reevaluated.
 */
int sbull_revalidate(struct gendisk *gd)
{
    struct sbull_dev *dev = gd->private_data;

    if (dev->media_change) {
        dev->media_change = 0;
        memset (dev->data, 0, dev->size);
    }
    return 0;
}

/*
 * The "invalidate" function runs out of the device timer; it sets
 * a flag to simulate the removal of the media.
 */
void sbull_invalidate(unsigned long ldev)
{
    struct sbull_dev *dev = (struct sbull_dev *) ldev;

    spin_lock(&dev->lock);
    if (dev->users || !dev->data) 
        printk (KERN_WARNING "sbull: timer sanity check failed\n");
    else
        dev->media_change = 1;
    spin_unlock(&dev->lock);
}

/*
 * The ioctl() implementation
 */

int sbull_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
    long size;
    struct hd_geometry geo;
    struct sbull_dev *dev = filp->private_data;

    switch(cmd) {
        case HDIO_GETGEO:
            /*
         * Get geometry: since we are a virtual device, we have to make
         * up something plausible.  So we claim 16 sectors, four heads,
         * and calculate the corresponding number of cylinders.  We set the
         * start of data at sector four.
         */
        //printk("<0>""-------------size=%d\n",size);
        /****************for early version************/
        //size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
        //printk("<0>""-------------size=%d\n",size);
        //geo.cylinders = (size & ~0x3f) >> 6;
        //geo.cylinders=2000;
        //geo.heads = 4;
        //geo.sectors = 16;
        //geo.sectors=2560;
        //geo.start = 0;
        //if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
        //    return -EFAULT;
        return 0;
    }

    return -ENOTTY; /* unknown command */
}

static int sbull_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
    unsigned long size;
    struct sbull_dev *pdev = bdev->bd_disk->private_data;

    size = pdev->size;
    geo->cylinders = (size & ~0x3f) >> 6;
    geo->heads    = 4;
    geo->sectors = 16;
    geo->start = 0;
    return 0;
}


/*
 * The device operations structure.
 */
static struct block_device_operations sbull_ops = {
    .owner           = THIS_MODULE,
    .open              = sbull_open,
    .release      = sbull_release,
    .media_changed   = sbull_media_changed,
    .revalidate_disk = sbull_revalidate,
    .ioctl             = sbull_ioctl,
    .getgeo            = sbull_getgeo,
};


/*
 * Set up our internal device.
 */
// 初始化设备结构体 static struct sbull_dev *Devices中的成员
static void setup_device(struct sbull_dev *dev, int which)
{
    /*
     * Get some memory.
     */
    memset (dev, 0, sizeof (struct sbull_dev));
    dev->size = nsectors*hardsect_size;
    // 分配一片虚拟地址连续的内存空间,作为块设备。
    dev->data = vmalloc(dev->size);   
    if (dev->data == NULL) {
        printk (KERN_NOTICE "vmalloc failure.\n");
        return;
    }
    spin_lock_init(&dev->lock);

    /*
     * The timer which "invalidates" the device.
     */
    init_timer(&dev->timer);
    dev->timer.data = (unsigned long) dev;
    dev->timer.function = sbull_invalidate;

    /*
     * The I/O queue, depending on whether we are using our own
     * make_request function or not.
     */
    switch (request_mode) {
        case RM_NOQUEUE:
        dev->queue = blk_alloc_queue(GFP_KERNEL);
        if (dev->queue == NULL)
            goto out_vfree;
        blk_queue_make_request(dev->queue, sbull_make_request);
        break;

        case RM_FULL:
        dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
        if (dev->queue == NULL)
            goto out_vfree;
        break;

        default:
        printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
            /* fall into.. */

        case RM_SIMPLE:
        dev->queue = blk_init_queue(sbull_request, &dev->lock);
        if (dev->queue == NULL)
            goto out_vfree;
        break;
    }
    blk_queue_hardsect_size(dev->queue, hardsect_size);
    dev->queue->queuedata = dev;
    /*
     * And the gendisk structure.
     */
    dev->gd = alloc_disk(SBULL_MINORS);
    if (! dev->gd) {
        printk (KERN_NOTICE "alloc_disk failure\n");
        goto out_vfree;
    }
    dev->gd->major = sbull_major;
    dev->gd->first_minor = which*SBULL_MINORS;
    dev->gd->fops = &sbull_ops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
    set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
    add_disk(dev->gd);
    return;

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



static int __init sbull_init(void)
{
    int i;
    /*
     * Get registered.
     */
    //    printk("<0>" "add by lht\n");
    sbull_major = register_blkdev(sbull_major, "sbull");
    if (sbull_major <= 0) {
        printk(KERN_WARNING "sbull: unable to get major number\n");
        return -EBUSY;
    }
    /*
     * Allocate the device array, and initialize each one.
     */
    Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
    if (Devices == NULL)
        goto out_unregister;
    for (i = 0; i < ndevices; i++) 
        setup_device(Devices + i, i);

    return 0;

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

static void sbull_exit(void)
{
    int i;

    for (i = 0; i < ndevices; i++) {
        struct sbull_dev *dev = Devices + i;

        del_timer_sync(&dev->timer);
        if (dev->gd) {
            del_gendisk(dev->gd);
            put_disk(dev->gd);
        }
        if (dev->queue) {
            if (request_mode == RM_NOQUEUE)
            //    blk_put_queue(dev->queue);
            kobject_put(&(dev->queue)->kobj);
            else
                blk_cleanup_queue(dev->queue);
        }
        if (dev->data)
            vfree(dev->data);
    }
    unregister_blkdev(sbull_major, "sbull");
    kfree(Devices);
}

module_init(sbull_init);
module_exit(sbull_exit);



测试方法:

# Makefile
ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /home/lht/kernel2.6/linux-2.6.14

KERNELDIR ?= /lib/modules/$(shell uname -r)/build M=$(PWD) modules
PWD := $(shell pwd)

modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    obj-m := sbull.o
endif

将模块插入内核(2.6.27root@linuxidc:/source/workplace/test/sbull_linuxidc# insmod sbull.ko 
用lsmod查看模块是否成功插入内核
root@linuxidc:/source/workplace/test/sbull_linuxidc# lsmod | grep sbu
sbull                  13452  0 
出现上面结果,说明成功了
用ls查看/dev下是否有sbull设备
root@linuxidc:/source/workplace/test/sbull_linuxidc# ls /dev | grep sbu
sbulla
出现上面结果,说明有了,如果没有,用命令
mknod /dev/sbulla b 254 0
手动创建
至此,已经有一个块设备了
下面用fdisk对虚拟块设备分区
root@linuxidc:/source/workplace/test/sbull_linuxidc# fdisk /dev/sbulla 
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x14d0973f.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

Command (m for help): n        这里选择n,新建
Command action
   e   extended
   p   primary partition (1-4)  这里选p,主分区
p
Partition number (1-4): 1   这里选1,第一个分区
First cylinder (1-400, default 1): 1
Last cylinder, +cylinders or +size{K,M,G} (1-400, default 400): 
Using default value 400

Command (m for help): w  这里选w,保存并推出
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
接着将其格式化为ext2

root@linuxidc:/source/workplace/test/sbull_linuxidc# mkfs.ext2 /dev/sbulla1 
mke2fs 1.41.3 (12-Oct-2008)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
3200 inodes, 12792 blocks
639 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=13107200
2 block groups
8192 blocks per group, 8192 fragments per group
1600 inodes per group
Superblock backups stored on blocks: 
        8193

Writing inode tables: done                            
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 29 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
新建一个文件夹,作为此处模拟的块设备的挂载点
root@linuxidc:/source/workplace/test/sbull_linuxidc# ls /mnt/
hgfs  initrd
root@linuxidc:/source/workplace/test/sbull_linuxidc# mkdir /mnt/sbulla1
挂载
root@linuxidc:/source/workplace/test/sbull_linuxidc# mount /dev/sbulla1 /mnt/sbulla1
进入目录,新建一个文件,测试一下
root@linuxidc:/source/workplace/test/sbull_linuxidc# cd /mnt/sbulla1/
root@linuxidc:/mnt/sbulla1# ls
lost+found
root@linuxidc:/mnt/sbulla1# echo hi > hello.c
root@linuxidc:/mnt/sbulla1# ls
hello.c  lost+found
root@linuxidc:/mnt/sbulla1# cat hello.c 
hi
root@linuxidc:/mnt/sbulla1#

bio 代表一个IO 请求
request 是bio 提交给IO调度器产生的数据,一个request 中放着顺序排列的bio
当设备提交bio 给IO调度器时,IO调度器可能会插入bio,或者生成新的request
request_queue代表着一个物理设备,顺序的放着request

你可能感兴趣的:(Linux驱动)