This is an online example.
Http://blog.superpat.com/2010/05/04/a-simple-block-driver-for-linux-kernel-2-6-31/
/*
* A sample, extra-simple block driver. Updated for kernel 2.6.31.
*
* (C) 2003 Eklektix, Inc.
* (C) 2010 Pat Patterson <pat at superpat dot com>
* Redistributable under the terms of the gnu gpl.
*/
# 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/hdreg. h>
MODULE_LICENSE ("Dual BSD/GPL ");
Static char X Version = "1.4 ";
Static int major_num = 0;
Module_param (major_num, int, 0 );
Static int logical_block_size = 512;
Module_param (logical_block_size, int, 0 );
Static int nsectors = 1024;/* How big the drive is */
Module_param (nsectors, int, 0 );
/*
* We can tweak our hardware sector size, but the kernel talks to us
* In terms of small sectors, always.
*/
# Define KERNEL_SECTOR_SIZE 512
/*
* 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;
} Device;
/*
* Handle an I/O Request.
*/
Static void sbd_transfer (struct sbd_device * Dev, sector_t sector,
Unsigned long nsect, char * buffer, int write ){
Unsigned long offset = sector * logical_block_size;
Unsigned long nbytes = nsect * logical_block_size;
If (Offset + nbytes)> Dev-> size ){
Printk (KERN_NOTICE "sbd: Beyond-end write (% ld)/n", offset, nbytes );
Return;
}
If (write)
Memcpy (dev-> data + offset, buffer, nbytes );
Else
Memcpy (buffer, dev-> data + offset, nbytes );
}
Static void sbd_request (struct request_queue * q ){
Struct request * req;
Req = blk_fetch_request (q );
While (req! = NULL ){
If (req-> performance_type! = REQ_TYPE_FS ){
Printk (KERN_NOTICE "Skip non-CMD request/n ");
_ Blk_end_request_all (req,-EIO );
Continue;
}
Sbd_transfer (& Device, blk_rq_pos (req), blk_rq_cur_sectors (req ),
Req-> buffer, rq_data_dir (req ));
If (! _ Blk_end_request_cur (req, 0 )){
Req = blk_fetch_request (q );
}
}
}
/*
* The HDIO_GETGEO ioctl is handled in blkdev_ioctl (), which
* Callthis. We need to implement getgeo, since we can't
* Use tools such as fdisk to partition the drive otherwise.
*/
Int sbd_getgeo (struct block_device * block_device, struct hd_geometry * geo ){
Long size;
/* We have no real geometry, of course, so make something up .*/
Size = device. Size * (logical_block_size/kernel_sector_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 sbd_ops = {
. Owner = this_module,
. Getgeo = sbd_getgeo
};
Static int _ init sbd_init (void ){
/*
* Set up our internal device.
*/
Device. size = nsectors * logical_block_size;
Spin_lock_init (& device. Lock );
Device. Data = vmalloc (device. size );
If (device. Data = NULL)
Return-enomem;
/*
* Get a request queue.
*/
Queue = blk_init_queue (sbd_request, & device. Lock );
If (Queue = NULL)
Goto out;
Blk_queue_logical_block_size (Queue, logical_block_size );
/*
* Get registered.
*/
Major_num = register_blkdev (major_num, "sbd ");
If (major_num <= 0 ){
Printk (KERN_WARNING "sbd: unable to get major number/n ");
Goto out;
}
/*
* And the gendisk structure.
*/
Device. gd = alloc_disk (16 );
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 );
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 _ exit sbd_exit (void)
{
Del_gendisk (Device. gd );
Put_disk (Device. gd );
Unregister_blkdev (major_num, "sbd ");
Blk_cleanup_queue (Queue );
Vfree (Device. data );
}
Module_init (sbd_init );
Module_exit (sbd_exit );
The last step to use this block device
opensuse:/home/pat/sbd # insmod sbd.ko
opensuse:/home/pat/sbd # fdisk /dev/sbd0
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x5f93978c.
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
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-16, default 1):
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-16, default 16):
Using default value 16
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
opensuse:/home/pat/sbd # mkfs /dev/sbd0p1
mke2fs 1.41.9 (22-Aug-2009)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
64 inodes, 504 blocks
25 blocks (4.96%) reserved for the super user
First data block=1
Maximum filesystem blocks=524288
1 block group
8192 blocks per group, 8192 fragments per group
64 inodes per group
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 24 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
opensuse:/home/pat/sbd # mount /dev/sbd0p1 /mnt
opensuse:/home/pat/sbd # echo Hi > /mnt/file1
opensuse:/home/pat/sbd # cat /mnt/file1
Hi
opensuse:/home/pat/sbd # ls -l /mnt
total 13
-rw-r--r-- 1 root root 3 2010-04-29 07:04 file1
drwx------ 2 root root 12288 2010-04-29 07:04 lost+found
opensuse:/home/pat/sbd # umount /mnt
opensuse:/home/pat/sbd # rmmod sbd