[Linux Driver] - Linux Device Driver--Program Design of Block Device (3)

Block device driver registration and deregistration

The first job in block device drivers is usually to register themselves to the kernel. The function to accomplish this task is register_blkdev(), and its prototype is:

int register_blkdev(unsigned int major, const char *name);

The major parameter is the major device number to be used by the block device, and name is the device name, which will be displayed in /proc/devices. If major is 0, the kernel will automatically assign a new major device number. The return value of the register_blkdev() function is this major device number. If it returns 1 as a negative value, it indicates that an error has occurred.

The unregister function corresponding to register_blkdev() is unregister_blkdev(), and its prototype is:

int unregister_blkdev(unsigned int major, const char *name);

Here, the parameters passed to unregister_blkdev() must match the parameters passed to register_blkdev(), otherwise the function returns -EINVAL.

Block device request queue operation

The standard request handler can sort requests and merge adjacent requests. If a block device wants to use the standard request handler, it must call the function blk_init_queue to initialize the request queue. When processing requests on the queue, the queue spin lock must be held. Initialize the request queue

request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);

The first parameter of the function is a pointer to the request processing function, and the second parameter is a spin lock that controls access to the queue. This function will cause memory allocation, so it may fail. When the function call succeeds, it returns Pointer to the initialization request queue, otherwise NULL is returned. This function is generally called in the module loading function of the block device. Clear the request queue

void blk_cleanup_queue(request_queue_t *q);

This function completes the task of returning the request queue to the system, and is generally called in the block device driver module uninstall function.

 

Extract request:

struct request *elv_next_request(request_queue_t *queue);

The above function is used to return the next request to be processed (determined by the I/O scheduler), or NULL if there is no request.

Removal request:

void blkdev_dequeue_request(struct request *req);

The above function removes a request from the queue. If the driver operates multiple requests from the same queue at the same time, it must remove them from the queue in this way.

Assign "request queue":

request_queue_t *blk_alloc_queue(int gfp_mask);

For non-mechanical devices with completely random access such as FLASH and RAM disks, complex I/O scheduling is not required. At this time, you should use the above function to allocate a "request queue", and use the following function to bind the "request queue" And the "manufacturing request" function .

void blk_queue_mask_request(request_queue_t *q, mask_request_fn *mfn);

void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max);

This function is used to inform the kernel of the size of the hardware sector of the block device. All requests generated by the kernel are multiples of this size and are correctly bounded. However, the communication between the kernel block device layer and the driver is still carried out in units of 512-byte sectors.

 

step:

In the module loading function of the block device driver, the following tasks are usually completed:

  1. Allocate and initialize the request queue, bind the request queue and request function.
  2. Allocate and initialize gendisk, assign values ​​to the major, fops, queue and other members of gendisk, and finally add gendisk.
  3. Register the block device driver.

In the module unloading function of the block device driver, the opposite work is usually required with the module loading function:

  1. Clear the request queue.
  2. Delete gendisk and references to gendisk.
  3. Delete the reference to the block device and log off the block device driver.

to sum up:

The I/O operation mode of the block device is quite different from that of the character device, so a series of data structures such as request_queue, request, and bio are introduced. In the I/O operation of the entire block device, the "request" runs through all the time, and the I/O operation of the character device is not detoured this time, and the I/O operation of the block device will be queued and integrated .

The task of the driver is to process requests, and the queuing and integration of requests is solved by the I/O scheduling algorithm. Therefore, the core of the block device driver is the request processing Han Shuo or "manufacturing request" function.

Although the block_device_operations structure and its member functions still exist in the block device driver China, it no longer contains member functions such as reading and writing, but only functions that have nothing to do with specific reading and writing, such as opening, releasing, and I/O control. The structure of the block device driver is quite complex, but fortunately, the block device is not as all-encompassing as the character device. It is usually a storage device, and the main body of the driver has been provided by the Linux kernel. For a specific hardware system, the driver engineer involves Work is often just writing a small amount of code that directly interacts with the hardware.

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
 
#define MAXBUF 1024	
 
 
#define BLK_MAJOR 253
 
char blk_dev_name[]="blk_dev";
static char flash[1024*16];
 
 
int major;
spinlock_t lock;
struct gendisk *gd;
 
 
 
/*块设备数据传输*/
static void blk_transfer(unsigned long sector, unsigned long nsect, char *buffer, int write)
{
	int read = !write;
	if(read)
	{
		memcpy(buffer, flash+sector*512, nsect*512);
	}
	else
	{
		memcpy(flash+sector*512, buffer, nsect*512);
	}
}
 
/*块设备请求处理函数*/
static void blk_request_func(struct request_queue *q)
{
	struct request *req;
	while((req = elv_next_request(q)) != NULL)  
	{
		if(!blk_fs_request(req))
		{
			end_request(req, 0);
			continue;
		}
		
		blk_transfer(req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req));
		/*rq_data_dir从request获得数据传送的方向*/
		/*req->current_nr_sectors 在当前段中将完成的扇区数*/
		/*req->sector 将提交的下一个扇区*/
		end_request(req, 1);
	}
}
 
/*strcut block_device_operations*/
static  int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
{
       return -ENOTTY;
}
 
static int blk_open (struct block_device *dev , fmode_t no)
{
	printk("blk mount succeed\n");
	return 0;
}
static int blk_release(struct gendisk *gd , fmode_t no)
{
	printk("blk umount succeed\n");
	return 0;
}
struct block_device_operations blk_ops=
{
	.owner = THIS_MODULE,
	.open = blk_open,
	.release = blk_release,
    .ioctl = blk_ioctl,
};
 
//-----------------------------------------------
 
static int __init block_module_init(void)
{
	
	
	if(!register_blkdev(BLK_MAJOR, blk_dev_name)) //注册一个块设备
	{
		major = BLK_MAJOR;	
		printk("regiser blk dev succeed\n");
	}
	else
	{
		return -EBUSY;
	}
	gd = alloc_disk(1);  //分配一个gendisk,分区是一个
	spin_lock_init(&lock); //初始化一个自旋锁
	gd->major = major;
	gd->first_minor = 0;   //第一个次设备号
	gd->fops = &blk_ops;   //关联操作函数
 
	gd->queue = blk_init_queue(blk_request_func, &lock); //初始化请求队列并关联到gendisk
 
	snprintf(gd->disk_name, 32, "blk%c", 'a');  
	blk_queue_hardsect_size(gd->queue, 512);  //设置扇区大小512字节
	set_capacity(gd, 32);  //设置块设备大小 512*32=16K
	add_disk(gd);
	printk("gendisk init success!\n");
	return 0;
}
static void __exit block_module_exit(void)
{
	blk_cleanup_queue(gd->queue);
	del_gendisk(gd); 
	unregister_blkdev(BLK_MAJOR, blk_dev_name);
	printk("block module exit succeed!\n");
}
 
module_init(block_module_init);
module_exit(block_module_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gec");
//------------------------------------------------------------------------------

 

 

 

 

Guess you like

Origin blog.csdn.net/u014674293/article/details/104456149