Linux-Flash驅動(2)-塊裝置驅動例項分析
阿新 • • 發佈:2019-01-06
在上一節課中,我們在記憶體中劃分出512kB作為一個塊裝置,並對它實現讀寫的操作。現在我們來具體分析這段程式碼。
這是一個模組的程式,先看看模組初始化裡面的blk_init函式,裡面做了這幾件事:#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);
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判斷請求佇列是否還有請求要處理,如果有則繼續處理,沒有退出。