NEXT UP previous
Next: Interrupts

Registering Block Devices

Registering a block device driver is rather more complicated than the character device driver equivalent operation, as it requires some effort at kernel boot time, like characters drivers, but it also requires some kernel compile time additions.

Boot Time Registration

The boot time registration is performed by the driver's init() function which, for kernels from 1.1.33 on, has the prototype:

	void my_init (void);

In order for the kernel to call your init() function at boot time, a line of code needs to be added to the kernel's blk_dev_init() function. This function is contained at the end of the file:

	/usr/src/linux/drivers/block/ll_rw_blk.c

Your extra line of code should be inserted just before the return(); line at the end of the blk_dev_init() function, and it should have the general format:

	my_init();

When the kernel executes your init a function at boot time, it has to check that any required hardware exists and can be initialized and it also has to register the major device number that the driver will use, and at the same time, to pass to the kernel a pointer to the driver's file_operations structure.

The file_operations structure used for block device drivers is the same as the one for character devices, but some of the fields have fixed addresses inserted so that standard kernel routines get called, instead of calling your device driver functions. Typically, the structure might be filled in as follows:

	struct file_operations my_fops =
	{
		0,
		block_read,	       /* kernel function */
		block_write	       /* kernel function */
		0,
		0,
		my_ioctl,	/* device driver function */
		0,
		my_open,	/* device driver function */
		my_release,	/* device driver function */
		block_fsync,	       /* kernel function */
		0,
		0,
		0
	}

The block_read(), block_write() and block_fsync() kernel functions should generally be called from all block device drivers. This means that you need not (and, indeed, should not) include these functions in your driver code. The example also shows that ioctl(), open() and release() functions can be called and need to be included in your driver.

Once you have decided which major device number your driver will use and set up its file_operations structure, the device driver can be registered with register_blkdev() function:

	if (register_blkdev(MY_MAJOR, "my_bdev", &my_fops))
	{
		printk("MY_BDEV: Unable to register device\n"); 
		return;                  /* return mem_start; for pre 1.1.33 kernels */
	}

The first parameter to register_blkdev() is the major device number to be used for this driver, the second parameter is a name string for the device and the third parameter is a pointer to the file_operations structure. When used in this form, the function returns zero if all is well, or a negative value on error. If you specify a zero value for the major device number then register_blkdev() will search for a free device number and supply this as its return value instead.

You have already seen that when the kernel needs to perform physical I/O it will call your driver's request() function. However, the address of this function is not contained in the file~operations structure. So, another task of your driver's init() function is to inform the kernel of the address of the request() function. This is done by including the standard line of code:

	blk_dev[MY_MAJOR].request_fn = DEVICE_REQUEST;

This assumes that you are using the major device number MY_MAJOR. The symbollic name DEVICE_REQUEST is defined to be the name (and hence address) of the driver's request() function (we'll look at where the definition for it is given, in a moment).

The final thing that the init() function needs to set up is information to tell the buffer cache the size of a data block (in bytes) on the devices controlled by your new driver:

	my_block_size = 512;
	blksize_size[MY_MAJOR] = &my_block_size;

Again, this stores the value in an array which is indexed on the major device number.

Compile Time Registration

Up to now, you still have not told the kernel where to find your request() function, except in terms of the symbol DEVICE_REQUEST. In order to complete the registration procedure you need to add this macro along with several others to the file "blk.h" in the block driver's source directory:

	/usr/src/linux/drivers/block

To do this, you need to find a line in that file which appears like:

	#endif   /* MAJOR_NR == whatever */

and then, immediately before this line, you need to add the following set of macro definitions:

	#elif (MAJOR_NR==MY_MAJOR)

	static void do_my_request(void);

	#define DEVICE_NAME "My Block Driver"
	#define DEVICE_REQUEST do_my_request
	#define DEVICE_NR(device) (MINOR(device))
	#define DEVICE_ON(device)
	#define DEVICE_OFF(device)

It should be obvious by inspection that you are entering your block of code into the correct place in the file because it will end up immediately after similar blocks of macro definitions for other drivers.

The various macros are defined as follows:

DEVICE_NAMEsdhort driver description string;
DEVICE_REQUESTpointer to driver's request function;
DEVICE_NR (d)calculates physical device number from minor number;
DEVICE_ON (d)used for devices that need to be turned on;
DEVICE_OFF (d)used for devices that need to be turned off (floppy)

NEXT UP previous
Next: Interrupts