Linux-Flash驱动(2)-块设备驱动实例分析
2017-07-08 09:12
801 查看
在上一节课中,我们在内存中划分出512kB作为一个块设备,并对它实现读写的操作。现在我们来具体分析这段代码。
这是一个模块的程序,先看看模块初始化里面的blk_init函数,里面做了这几件事:
1、注册一个快设备register_blkdev,如果没有分配到主设备号则打印错误信息
2、然后是申请一个结构,这个结构是用来保存这个块设备信息的,不是每个块设备都有
3、再安装这个设备setup_device,这个函数是自定义的
3.1完成块设备大小的计算
3.2把快设备放入请求队列中(IO调度层把请求排序后放入请求队列中,里面的参数blk_request是一个函数,用于指明使用哪个函数对这个请求进行处理)
3.3指明设备的扇区大小
3.4然后用alloc_disk函数分配一个gendisk结构(一个驱动可能对于几个块设备,用gendisk来区分)
3.5紧接着需要对这个结构进行初始化,如下:
3.6注册这个块设备
第二个重要的函数是实现读写请求处理,读写请求通过blk_request函数来实现:
1、使用blk_fetch_request从队列中取出要处理的一个请求
2、使用blk_transfer实现对对应扇区的硬件操作,比如读和写,应该这里的块设备是内存模拟的,所以使用的是memcpy函数
3、使用__blk_end_request_cur判断请求队列是否还有请求要处理,如果有则继续处理,没有退出。
#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);
这是一个模块的程序,先看看模块初始化里面的blk_init函数,里面做了这几件事:
1、注册一个快设备register_blkdev,如果没有分配到主设备号则打印错误信息
2、然后是申请一个结构,这个结构是用来保存这个块设备信息的,不是每个块设备都有
3、再安装这个设备setup_device,这个函数是自定义的
3.1完成块设备大小的计算
3.2把快设备放入请求队列中(IO调度层把请求排序后放入请求队列中,里面的参数blk_request是一个函数,用于指明使用哪个函数对这个请求进行处理)
3.3指明设备的扇区大小
3.4然后用alloc_disk函数分配一个gendisk结构(一个驱动可能对于几个块设备,用gendisk来区分)
3.5紧接着需要对这个结构进行初始化,如下:
/*初始化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注册这个块设备
第二个重要的函数是实现读写请求处理,读写请求通过blk_request函数来实现:
1、使用blk_fetch_request从队列中取出要处理的一个请求
2、使用blk_transfer实现对对应扇区的硬件操作,比如读和写,应该这里的块设备是内存模拟的,所以使用的是memcpy函数
3、使用__blk_end_request_cur判断请求队列是否还有请求要处理,如果有则继续处理,没有退出。
相关文章推荐
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux下读写FLASH驱动——MTD设备分析
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux下读写FLASH驱动——MTD设备分析
- Linux下读写FLASH驱动——MTD设备分析
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux I2C设备驱动编写(三)-实例分析AM3359
- 【转】Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux设备驱动:kobject原理与实例分析
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux下读写FLASH驱动——MTD设备分析
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux设备驱动程序架构分析之一个I2C驱动实例
- 基于mini6410的linux驱动学习总结(五 字符设备驱动程序实例分析(虚拟设备驱动))
- Linux下读写FLASH驱动——MTD设备分析
- linux网络设备应用与驱动编程学习4——模板与实例(A)
- Linux设备模型之tty驱动架构分析
- Linux设备驱动之I2C架构分析
- s3c2410 linux IIS音频设备驱动分析