2008年1月6日 星期日

create block device driver

example: sbd( simple block driver)
ps: referenc http://lwn.net/Articles/58719/

(1) define a sturcture to represent the disk
static struct sbd_device {
unsigned long size;
spinlock_t lock;
u8 *data;
struct gendisk *gd;
} Device;

(2) disk initializtion
Device.size = nsectors*hardsect_size;
spin_lock_init(&Device.lock);
Device.data = vmalloc(Device.size);
if (Device.data == NULL)
return -ENOMEM;

(3) register with kernel( assignment of a dynamic major number (if requested), and causing the block
driver to show up in /proc/devices.)
major_num = register_blkdev(major_num, "sbd");

(4) gendisk( general disk) initializtion
Device.gd = alloc_disk(16);
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*(hardsect_size/KERNEL_SECTOR_SIZE));

//The fops field is a pointer to the block_device_operations structure
//The private_data field can be used by the driver, so we stick a pointer to our sbd_device structure

(5) set up the request queue
static struct request_queue *Queue;
/* ... */
Queue = blk_init_queue(sbd_request, &Device.lock);
if (Queue == NULL)
goto out;
blk_queue_hardsect_size(Queue, hardsect_size);
Device.gd->queue = Queue;

// sbd_request is the request function
// the lock used by the driver to serialize access to internal resources is the best choice for controlling
access to the request queue as well

(6) add the disk to the system
add_disk(Device.gd);

(7) open and release( ( kept in the block_device_operations structure)
Drivers for real hardware may need to lock and unlock doors, check for media

(8) the request method
static void sbd_request(request_queue_t *q)
{
struct request *req;

while ((req = elv_next_request(q)) != NULL) {
if (! blk_fs_request(req)) {
end_request(req, 0);
continue;
}
sbd_transfer(&Device, req->sector, req->current_nr_sectors,
req->buffer, rq_data_dir(req));
end_request(req, 1);
}
}

// elv_next_request(): getting the first request in the queue. this function does not actually remove the
// request from the queue
// there can be multiple types of requests
// A nonzero return value from the blk_fs_request() macro says "this is a normal filesystem request."
// sbd_transfer(): just a memcpy() with some checking
// rq_data_dir(): A nonzero value (WRITE) indicates that this is a write request
// end_request(): finish processing of this request

(9) ioctl
int sbd_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
long size;
struct hd_geometry geo;

switch(cmd) {
/*
* The only command we need to interpret is HDIO_GETGEO, since
* we can't partition the drive otherwise. We have no real
* geometry, of course, so make something up.
*/
case HDIO_GETGEO:
size = Device.size*(hardsect_size/KERNEL_SECTOR_SIZE);
geo.cylinders = (size & ~0x3f) >> 6;
geo.heads = 4;
geo.sectors = 16;
geo.start = 4;
if (copy_to_user((void *) arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY; /* unknown command */
}

沒有留言: