http://docs.blackfin.uclinux.org/doku.php?id=basic_block_driversKernel 2.6 Block Device DriversThe basic block device driver has undergone major changes. The 2.6 kernel has a much simpler and cleaner interface Here is a brief list of the new features
generic disk subsystem.
system.
The steps to create a 2.6 Block Device Driver are :
An example of each step is shown Define a device StructureDefine the structure and initialize it. int num_sects = 1024; int sect_size = 512; static struct simple_device { unsigned long size; //device size in bytes spinlock_t lock; //mutex lock char * data; //device data area struct gendisk *gendisk; //gendisk structure } sbd_device; sbd_device.size = num_sects * sect_size; spin_lock_init(&sbd_device.lock); sbd_device.data = vmalloc(num_sects * sect_size); // Add check for NULL here return -ENOMEM if failed Get a Major Number#define DEVICE_NAME "sbd" int maj = register_blkdev(0,DEVICE_NAME); // error if <=0 Define the sbd_ops table// prototype for the ioctl function int sbd_ioctl ( struct inode * inode, struct file * file, unsigned int cmd , unsigned long arg); static struct block_device_operations sbd_ops = { .owner = THIS_MODULE, .ioctl = snd_ioctl }; Set up a Gen DiskGet a Gen Disk system and set it up using the sbd_ops just defined int num_parts = 17; // we will get 16 partitions here sbd_device.gendisk = alloc_disk(num_parts); // Check for NULL and terminate if required sbd_device.gendisk->major = maj; sbd_device.gendisk->first_minor = 0; sbd_device.gendisk->fops = &sbd_ops; sbd_device.gendisk->private_data = &sbd_device; strcpy(sbd_device.gendisk->disk_name,DEVICE_NAME "0"); set_capacity(sbd_device.gendisk,num_sects*(sect_size/KERNEL_SECTOR_SIZE)); Request QueueGet a Request Queue and add it to the gen disk static struct request_queue * queue; queue = blk_init_queue(sbd_request, \\ the service function &sbd_device.lock ); \\ the lock // check for NULL and handle error blk_queue_hardsect_size(queue,sect_size); sbd_device.gendisk->queue = queue; add_disk(sbd_device.gendisk); Data TransferSpecify the data transfer function static void sbd_transfer ( struct simple_device * dev, unsigned long sector, unsigned long num_sects, char * buffer, int write) { unsigned long offset = sector * sect_size; unsigned long num_bytes = num_sects * sect_size; // check size if ( (offset + num_bytes) > dev->size ) { printk(KERN_NOTICE DEVICE_NAME ":access error\n"); return; } if ( write ) { memcpy(dev->data+offset, buffer, num_bytes); } else { memcpy(buffer, dev->data+offset, num_bytes); } } Request FunctionSpecify the request function to handle entries in the request queue. static void sbd_request(request_queue_t *q) { struct request * req; while((req = elv_next_request(q)) != NULL) {\ if ( !blk_fs_request(req)) { end_request(req,0); // reject request continue; } sbd_transfer(&sbd_device, //our read / write routine req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req)); end_request(req,1); // consume request } } Basic IOCTL functionDefine a basic IOCTL function. The Kernel now handles the default IOCTL calls and only passes the ones to the driver that it cannot handle. This IOCTL is used to get the (Imaginary) geometry of the system int sbd_ioctl ( struct inode * inode, struct file * file, unsigned int cmd , unsigned long arg) { long size; struct hd_geometry geom; switch (cmd) { // HDIO_ETGEO is all we need to supply case HDIO_GETGEO: size = sbd_device.size*(sect_size/KERNEL_SECTOR_SIZE); geom.cylinders = ( size & ~0x3f ) >> 6; geom.heads = 4; geom.sectors = 16; geom.start = 4; if( copy_to_user((void *) arg, &geom, sizeof(geom))) return -EFAULT; return 0; } return -ENOTTY; } Add the new DeviceStart the system by adding the disk. add_disk(sim_device.gendisk); Following this you can define a block device using mknod. Create the device nodes. get maj from cat /proc/devices mknod /dev/myblock b maj 0 mknod /dev/myblock0 b maj 0 mknod /dev/myblock1 b maj 1 mknod /dev/myblock2 b maj 2 Then make a file system on one of the nodes mke2fs /dev/myblock1 Now mount it and add files ( they will dissapear when you reboot or unmount the partition ) mkdir -p /mnt/myblock mount /dev/myblock1 /mnt/yblock Clean UpFinally the clean up code follows. This is used to remove the driver if it was inserted as a module static void __ exit sbd_exit(void) { del_gendisk(sbd_device.gendisk); put_disk(sbd_device.gendisk); unregister_blkdev(maj,DEVICE_NAME); blk_cleanup_queue(queue); vfree(sbd_device.data); } |
