Linux-Flash驱动(3)-简单块设备驱动设计

来源:互联网 发布:虚拟机linux网不可用 编辑:程序博客网 时间:2024/06/07 09:49

这节课的内容主要根据上一节课分析出来的块设备的初始化和操作流程,编写出自己的块设备驱动函数。因此这篇博文主要讲一下所用到函数的用法及其他框架。

1、编写一个模块框架

2、注册一个块设备major = register_blkdev(major, "blk");它有2个参数,主设备号和设备名字,一般采用内核自动分配,所以这里参数为0,自动分配的设备号会以返回值的形式得到。如果返回值小于等于0,说明主设备号分配失败,应提示错误信息,并返回-EBUSY(这个宏在<linux/errno.h> 中)。第二个参数是设备的名字。

3、完成其他初始化工作,定义一个setup_device函数来实现。

3.1计算块设备的大小

//计算设备大小dev->size = nsectors*sect_size;dev->data = vmalloc(dev->size);

3.2初始化一个请求队列,使用dev->queue = blk_init_queue(blk_request, NULL);,第一个参数是对请求的响应函数,这个定义为blk_request,第二个参数是自旋锁一般为空。返回值是这个队列的地址,这里我们自己定义一个结构来保存这个队列。

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 */};
定义好之后应该在blk_init函数中为这个结构申请一个空间

3.3使用blk_queue_logical_block_size指明设备的扇区大小,参数分别为请求队列和块的大小

3.4分配分配gendisk结构,使用dev->gd = alloc_disk(1);,参数表示这个设备可以支持多少种块设备,这里为了简单可以填1。把返回的gendisk指针保存起来。

3.5对gendisk结构完成初始化:

/*初始化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最后注册我们的块设备add_disk(dev->gd);


4、编写块设备请求函数static void blk_request(struct request_queue *q),参数是请求队列

4.1从请求队列中拿出一个请求,req = blk_fetch_request(q);如果不为空说明拿到了,处理这个请求,再判断队列是否为空,如果不为空接着处理第二个请求。这里的处理函数通过blk_transfer来实现


5、编写块设备处理函数blk_transfer,它的参数包括dev接口,操作起始扇区,操作扇区数,存放数据缓存的结构,然后是读写状态

blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

6、编写块设备退出程序

6.1注销gendiskdel_gendisk(dev->gd);

6.2清除队列blk_cleanup_queue(dev->queue);

6.3释放分配的空间vfree(dev->data);

6.4注销块设备unregister_blkdev(major, "blk");

6.5释放dev结构kfree(dev);


最终代码如下:

#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);elsememcpy(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);}}}/** 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);unregister_blkdev(major, "blk");kfree(dev);}module_init(blk_init);module_exit(blk_exit);





原创粉丝点击