LWN.net Logo

sbd driver source

/*
 * A sample, extra-simple block driver.
 * Copyright 2003 Eklektix, Inc.
 */

#ifndef __KERNEL__
#  define __KERNEL__
#endif

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

#include <linux/kernel.h> /* printk() */
#include <linux/fs.h>     /* everything... */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/blk.h>
#include <linux/buffer_head.h>  /* invalidate_bdev */

MODULE_LICENSE("Dual BSD/GPL");

static int major_num = 0;
module_param(major_num, int, 0);
static int hardsect_size = 512;
module_param(hardsect_size, int, 0);
static int nsectors = 1024;  /* How big the drive is */
module_param(nsectors, int, 0);

/*
 * Our request queue.
 */
static struct request_queue Queue;

/*
 * The internal representation of our device.
 */
static struct sbd_device {
    unsigned long size;
    spinlock_t lock;
    u8 *data;
    struct gendisk *gd;
    struct block_device *bdev;
} Device;


/*
 * Handle an I/O request.
 */
static void sbd_transfer(struct sbd_device *dev, unsigned long sector,
		unsigned long nsect, char *buffer, int write)
{
    unsigned long offset = sector*hardsect_size;
    unsigned long nbytes = nsect*hardsect_size;
    
    if ((offset + nbytes) > dev->size) {
	printk (KERN_NOTICE "sbd: 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 sbd_request(request_queue_t *q)
{
    struct request *req;

    while ((req = elv_next_request(q)) != NULL) {
	if (! blk_fs_request(req)) {
	    printk (KERN_NOTICE "Skip non-CMD request\n");
	    end_request(req, 0);
	    continue;
	}
	sbd_transfer(&Device, req->sector, req->current_nr_sectors,
			req->buffer, rq_data_dir(req));
	end_request(req, 1);
    }
}

/*
 * Open and close.
 */

static int sbd_open(struct inode *inode, struct file *filp)
{
/*
 * Remember the block_device pointer - but only once.
 */
    if (! Device.bdev) {
	Device.bdev = inode->i_bdev;
	atomic_inc(&Device.bdev->bd_count);
    }
    return 0;
}

static int sbd_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/*
 * Look for a media change.
 */
int sbd_media_changed(struct gendisk *gd)
{
/* Look in gd->private_data for our disk structure */
    return 0;
}

/*
 * Revalidate.
 */
int sbd_revalidate(struct gendisk *gd)
{
    return 0;
}

/*
 * The device operations structure.
 */
static struct block_device_operations sbd_ops = {
    .owner           = THIS_MODULE,
    .open 	     = sbd_open,
    .release 	     = sbd_release,
    .media_changed   = sbd_media_changed,
    .revalidate_disk = sbd_revalidate
};

static int __init sbd_init(void)
{
/*
 * Set up our internal device.
 */
    Device.size = nsectors*hardsect_size;
    spin_lock_init(&Device.lock);
    Device.data = vmalloc(Device.size);
    if (Device.data == NULL)
	return -ENOMEM;
    Device.bdev = NULL; /* we'll get it at open time */
/*
 * Get registered.
 */
    major_num = register_blkdev(major_num, "sbd", &sbd_ops);
    if (major_num <= 0) {
	printk(KERN_WARNING "sbd: unable to get major number\n");
	goto out;
    }
/*
 * And the gendisk structure.
 */
    Device.gd = alloc_disk(1);
    if (! Device.gd)
	goto out_unregister;
    Device.gd->major = major_num;
    Device.gd->first_minor = 0;
    Device.gd->fops = &sbd_ops;
    Device.gd->private_data = &Device;
    strcpy (Device.gd->disk_name, "sbd0");
    set_capacity(Device.gd, nsectors);
    blk_init_queue(&Queue, sbd_request, &Device.lock);
    Device.gd->queue = &Queue;
    add_disk(Device.gd);

    return 0;

  out_unregister:
    unregister_blkdev(major_num, "sbd");
  out:
    vfree(Device.data);
    return -ENOMEM;
}

static void sbd_exit(void)
{
    if (Device.bdev) {
	invalidate_bdev(Device.bdev, 1);
	bdput(Device.bdev);
    }
    del_gendisk(Device.gd);
    put_disk(Device.gd);
    unregister_blkdev(major_num, "sbd");
    vfree(Device.data);
}
	
module_init(sbd_init);
module_exit(sbd_exit);


(Log in to post comments)

Copyright © 2003, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds