块设备驱动在内核中有好多的例子,快速写好一个内核驱动最好的方法,我觉得是参考内核中其它代码是怎么写的,以下是我用内存模拟一块储存设备的例程,并且可以对此储存设备进行读写和分区的功能,代码如下
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#define DEBUG(x) x
struct blockdrv_dev {
long size; /* Device size in sectors */
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 hd_geometry geo;
struct timer_list timer; /* For simulated media changes */
};
#define BOLCKDRV_MINORS 16
#define KERNEL_SECTOR_SIZE 512
#define GEO_HEADS 3
#define GEO_CYL 32
static DEFINE_MUTEX(block_mutex);
static struct blockdrv_dev *block_dev;
static int blockdrv_major=0;
static int hardsect_size = 512;
static int nsectors = 1024; /* How big the drive is */
//static struct request_queue *blockdrv_queue;
//static DEFINE_SPINLOCK(blockdrv_lock);
static void blockdrive_exit(void);
static void do_blockdrv_request (struct request_queue * q)
{
struct request *req;
//fetch a request from the request queue
req = blk_fetch_request(q);
while (req) {
int err = 0;
unsigned long start = blk_rq_pos(req)<<9;
unsigned long len = blk_rq_cur_sectors(req)<<9;
struct blockdrv_dev *dev = req->rq_disk->private_data;
if(dev == NULL)
{
printk("req_private_data no data\n");
blockdrive_exit();
}
if ((start + len) > dev->size) {
err = -EIO;
goto done;
}
while (len) {
unsigned long addr = start;
unsigned long length = dev->size - addr;
if (len < length)
length = len;
addr += dev->data[ start >> 16 ];
if (rq_data_dir(req) == READ)
memcpy(req->buffer, (dev->data)+addr, length);
else
memcpy((dev->data)+addr, req->buffer, length);
start += length;
len -= length;
}
/* wrap up, 0 = success, -errno = fail */
done:
if (!__blk_end_request_cur(req, err))
req = blk_fetch_request(q);
}
}
static int blockdrv_open(struct block_device *b_dev, fmode_t fmode)
{
struct blockdrv_dev *bdev = b_dev->bd_disk->private_data;
mutex_lock(&block_mutex);
bdev->users++;
DEBUG(printk("blockdrv_open cnt=%d\n",bdev->users));
mutex_unlock(&block_mutex);
return 0;
}
static int blockdrv_release(struct gendisk *bdisk, fmode_t fmode)
{
struct blockdrv_dev *bdev = bdisk->private_data;
mutex_lock(&block_mutex);
bdev->users--;
DEBUG(printk("blockdrv_release cnt=%d\n",bdev->users));
mutex_unlock(&block_mutex);
return 0;
}
static int blockdrv_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
struct blockdrv_dev *d = bdev->bd_disk->private_data;
geo->cylinders = d->geo.cylinders;
geo->heads = d->geo.heads;
geo->sectors = d->geo.sectors;
return 0;
}
static struct block_device_operations blockdrv_operations = {
.owner = THIS_MODULE,
.open = blockdrv_open,
.release = blockdrv_release,
.getgeo = blockdrv_getgeo,
};
static int add_blockdrv(struct blockdrv_dev *dev)
{
int err = EINVAL;
memset(dev,0,sizeof(struct blockdrv_dev));
dev->size = nsectors*hardsect_size;
dev->data = vmalloc(dev->size);
if (dev->data == NULL) {
printk (KERN_NOTICE "vmalloc failure.\n");
err = ENOMEM;
return err;
}
//before the allocation of the request queue,it's important to allocate and initialize a spinlock
spin_lock_init(&dev->lock);
dev->queue = blk_init_queue(do_blockdrv_request, &dev->lock);//it must be paired with blk_cleanup_queue()
if(!dev->queue)
{
err = -ENOMEM;
printk(KERN_WARNING"blockdriver: block init rueue failed\n");
return err;
}
//after the queue is allocated,we must inform the kernel of the sector size your device supports immediately
blk_queue_max_hw_sectors(dev->queue, hardsect_size);
dev->queue->queuedata = dev; //this step is very import,
dev->gd = alloc_disk(BOLCKDRV_MINORS);
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failure\n");
blk_cleanup_queue(dev->queue);
err = -ENOMEM;
return err;
}
dev->geo.heads = GEO_HEADS;
dev->geo.cylinders = GEO_CYL;
dev->geo.sectors = dev->size/(dev->geo.heads * dev->geo.cylinders *KERNEL_SECTOR_SIZE);
dev->gd->major = blockdrv_major;
dev->gd->first_minor = BOLCKDRV_MINORS;
dev->gd->fops = &blockdrv_operations;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
snprintf (dev->gd->disk_name, 32, "blockdrv");
//the kernel always expresses itself in 512-byte sectors,we should translate all sector numbers accordingly
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
add_disk(dev->gd);
/*
Keep one important thing in mind here: as soon as you call add_disk, the disk is
“live” and its methods can be called at any time. In fact, the first such calls will probably
happen even before add_disk returns; the kernel will read the first few blocks in
an attempt to find a partition table. So you should not call add_disk until your driver
is completely initialized and ready to respond to requests on that disk.
*/
return 0;
}
static int __init blockdrive_init(void)
{
int err = -EINVAL;
blockdrv_major = register_blkdev(0,"blockdrvier");
if(blockdrv_major <= 0)
{
printk(KERN_WARNING "blockdriver: unable to get major number\n");
return -EBUSY;
}
block_dev = kmalloc(sizeof (struct blockdrv_dev), GFP_KERNEL);
if(block_dev == NULL)
{
printk(KERN_WARNING"blockdriver:unalbe to allocate memory to blockdev\n");
goto errout1;
}
err=add_blockdrv(block_dev);
if(err)
{
printk(KERN_WARNING"blockdriver add failed\n");
goto errout2;
}
DEBUG(printk("init ok\n"));
return 0;
errout2:
blk_cleanup_queue(block_dev->queue);
errout1:
unregister_blkdev(blockdrv_major, "blockdrvier");
return -err;
}
static void blockdrive_exit(void)
{
del_gendisk(block_dev->gd);
put_disk(block_dev->gd);
blk_cleanup_queue(block_dev->queue);
unregister_blkdev(blockdrv_major, "blockdrvier");
printk("clean up\n");
}
module_init(blockdrive_init);
module_exit(blockdrive_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Ethyn");
测试方法:
加载驱动
insmod xxx.ko
使用fdisk对/dev/blockdrv进行分区
分区后格式化 mkdosfs /dev/blockdrvxx 其中xx表示第xx个分区
注意最后写w保存,如我的分区后截图如下:
挂载: mount /dev/blockdrvxx /tmp
向/tmp目录中加入一些东西
卸载 mount /tmp
将设备改成普通文件:cat /dev/blockdrvxx > /tmp/xxx.bin
将xxx.bin文件挂载到pc中的/mnt目录中:
mount -o loop ...xxx.bin /mnt
查看/mnt中的文件是否是刚刚加入的文件