Driver block design apparatus (2) - Analysis block device driver Example

  • The example code
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>   /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/timer.h>
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>

MODULE_LICENSE("Dual BSD/GPL");

static int major = 0;
static int sect_size = 512;
static int nsectors = 1024;

/*
* The internal representation of our device.
*/
struct blk_dev 
{
	int size;                        /* Device size in sectors */
	u8 *data;                        /* The data array */
	struct request_queue *queue;     /* The device request queue */
	struct gendisk *gd;              /* The gendisk structure */
};

struct blk_dev *dev;

/*
* Handle an I/O request, in sectors.
*/
static void blk_transfer(struct blk_dev *dev, unsigned long sector,
	unsigned long nsect, char *buffer, int write)
{
	unsigned long offset = sector * sect_size;
	unsigned long nbytes = nsect * sect_size;


	if ((offset + nbytes) > dev->size) 
	{
		printk(KERN_NOTICE "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 blk_request(struct request_queue *q)
{
	struct request *req;

	//从队列中取出要处理的一个请求
	req = blk_fetch_request(q);
	while (req != NULL) 
	{
		struct blk_dev *dev = req->rq_disk->private_data;

		blk_transfer(dev, 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);
		}
	}
}

/*
* Transfer a single BIO.
*/
static int blk_xfer_bio(struct blk_dev *dev, struct bio *bio)
{
	int i;
	struct bio_vec *bvec;
	sector_t sector = bio->bi_sector;

	/* Do each segment independently. */
	bio_for_each_segment(bvec, bio, i) 
	{
		char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
		blk_transfer(dev, sector, bio_cur_bytes(bio) >> 9 /* in sectors */,
			buffer, bio_data_dir(bio) == WRITE);
		sector += bio_cur_bytes(bio) >> 9; /* in sectors */
		__bio_kunmap_atomic(bio, KM_USER0);
	}
	
	return 0; /* Always "succeed" */
}

/*
* Transfer a full request.
*/
static int blk_xfer_request(struct blk_dev *dev, struct request *req)
{
	struct bio *bio;
	int nsect = 0;

	__rq_for_each_bio(bio, req) 
	{
		blk_xfer_bio(dev, bio);
		nsect += bio->bi_size / sect_size;
	}
	
	return nsect;
}

/*
* The device operations structure.
*/
static struct block_device_operations blk_ops = 
{
	.owner = THIS_MODULE,
};

/*
* Set up our internal device.
*/
static void setup_device()
{
	//计算设备大小
	dev->size = nsectors * sect_size;
	dev->data = vmalloc(dev->size);
	if (dev->data == NULL) 
	{
		printk(KERN_NOTICE "vmalloc failure.\n");
		
		return;
	}

	//把块设备放入请求队列中,blk_request用于指明处理这个请求的函数
	dev->queue = blk_init_queue(blk_request, NULL);
	if (dev->queue == NULL)
		goto out_vfree;

	//指明设备的扇区大小
	blk_queue_logical_block_size(dev->queue, sect_size);
	dev->queue->queuedata = dev;

	//分配gendisk结构
	dev->gd = alloc_disk(1);
	if (!dev->gd) 
	{
		printk(KERN_NOTICE "alloc_disk failure\n");
		goto out_vfree;
	}

	/*初始化alloc_disk*/
	dev->gd->major = major;//主设备号
	dev->gd->first_minor = 0;//次设备号
	dev->gd->fops = &blk_ops;//操作函数集
	dev->gd->queue = dev->queue;//请求队列
	dev->gd->private_data = dev;//私有数据
	sprintf(dev->gd->disk_name, "simp_blk%d", 0);//磁盘名字
	set_capacity(dev->gd, nsectors*(sect_size / sect_size));//扇区数

	//注册块设备
	add_disk(dev->gd);
	return;

out_vfree:
	if (dev->data)
		vfree(dev->data);
}

static int __init blk_init(void)
{
	/*
	* 注册块设备,申请主设备号
	*/
	major = register_blkdev(major, "blk");
	if (major <= 0) 
	{
		printk(KERN_WARNING "blk: unable to get major number\n");
		return -EBUSY;
	}
	//申请一个描述结构(不是每个块设备都有)
	dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
	if (dev == NULL)
		goto out_unregister;
	//安装这个设备
	setup_device();

	return 0;

out_unregister:
	unregister_blkdev(major, "sbd");
		return -ENOMEM;
}


static void blk_exit(void)
{
	if (dev->gd)
	{
		del_gendisk(dev->gd);
		put_disk(dev->gd);
	}
	if (dev->queue)
		blk_cleanup_queue(dev->queue);
	if (dev->data)
		vfree(dev->data);

	nregister_blkdev(major, "blk");
	
	kfree(dev);
}

module_init(blk_init);
module_exit(blk_exit);
  • This is a module of the program, take a look inside the blk_init module initialization function, which it did a few things:
    • 1, a block device registration register_blkdev , if no number is assigned to the master print error message
    • 2, and then apply a structure that is used to hold the device information blocks, each block device has not
    • 3, and then install the device setup_device , this function is customizable
      •  3.1 complete computing device sized blocks
      •  3.2 Fast device into the request queue (IO scheduling level ordering to the request into the request queue, which parameter blk_request is a function for indicating which function to use to process the request)
      • 3.3 sector size specified device
      • 3.4 then alloc_disk assign a gendisk structure function (for several blocks may drive a device to distinguish with gendisk)
      • 3.5 followed by the need to initialize this structure, as follows:
      • ​/*初始化alloc_disk*/
        dev->gd->major = major;//主设备号
        dev->gd->first_minor = 0;//次设备号
        dev->gd->fops = &blk_ops;//操作函数集
        dev->gd->queue = dev->queue;//请求队列
        dev->gd->private_data = dev;//私有数据
        sprintf(dev->gd->disk_name, "simp_blk%d", 0);//磁盘名字
        set_capacity(dev->gd, nsectors*(sect_size / sect_size));//扇区数
      • 3.6 register the block device
  • The second important function is to process read and write requests, read and write requests blk_request function is achieved by:
    • 1, using blk_fetch_request taken to process a request from the queue
    • 2, using blk_transfer hardware operation of the corresponding sector, such as read and write, there should be an analog memory device is a block, it is used memcpy function
    • 3, using __blk_end_request_cur determine whether the request queue also request to be processed, then continue processing if there is no exit.

Guess you like

Origin blog.csdn.net/qq_22847457/article/details/93970477