块设备驱动的写法

块设备驱动在内核中有好多的例子,快速写好一个内核驱动最好的方法,我觉得是参考内核中其它代码是怎么写的,以下是我用内存模拟一块储存设备的例程,并且可以对此储存设备进行读写和分区的功能,代码如下

#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中的文件是否是刚刚加入的文件









猜你喜欢

转载自blog.csdn.net/humanspider1/article/details/41654671
今日推荐