块设备框架实例(基于内存模拟)

吐舌头驱动:

//---mydisk.c   实现最简功能  
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/device.h>

#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/vmalloc.h>

MODULE_LICENSE("Dual BSD/GPL");
/*DTS
	mydisk {
		compatible = "xdisk";
		parts = <4>;
		kschedule_enable = <1>;
		gpios = <&gpx2 7 0>;
	};
 */
//#define MYDISK_MINORS		4   //硬盘的分区数
#define MYDISK_HEADS		4
#define MYDISK_SECTORS		16    // 磁盘的扇区数
#define MYDISK_CYLINDERS	256   // 柱面数(多个磁盘的磁道叠加形成柱面)
#define MYDISK_SECTOR_SIZE	512   // 磁盘扇区大小
#define MYDISK_SECTOR_TOTAL	(MYDISK_HEADS * MYDISK_SECTORS * MYDISK_CYLINDERS)
#define MYDISK_SIZE		(MYDISK_SECTOR_TOTAL * MYDISK_SECTOR_SIZE)

struct disk{
	int gpio;			// 磁盘读写指示灯
	int mydisk_major;
	int MYDISK_MINORS;
	spinlock_t mylock;
	int kschedule_enable;
	char mydisk_name[20];
	struct gendisk *mydisk;
	char data[MYDISK_SIZE];
}MYDISK;

static int mydisk_open(struct block_device *bdev, fmode_t mode)
{	
	gpio_request(MYDISK.gpio, "mydisk gpio");
	gpio_direction_output(MYDISK.gpio, 0);

	return 0;
}

static void mydisk_release(struct gendisk* gd, fmode_t mode)
{
	return ;
}

static int mydisk_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long arg)
{
	return 0;
}

//获取磁盘几何信息
static int mydisk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
	geo->cylinders = MYDISK_CYLINDERS;
	geo->heads = MYDISK_HEADS;
	geo->sectors = MYDISK_SECTORS;
	geo->start = 0;
	return 0;
}
// 处理块层发来的磁盘操作请求队列
static void mydisk_request_handler(struct request_queue *q)
{
	int status = 0;
	volatile int i = 0,j = 0,k = 0, t = 0;
	struct request *req;
	struct bio *bio;
	struct bio_vec bvec;
	struct bvec_iter iter;
	sector_t sector;
	unsigned char *buffer;
	unsigned long offset;
	unsigned long nbytes;
	struct mydisk_dev *bdev;

	printk("enter: %s!\n", __func__);
	gpio_set_value(MYDISK.gpio, 1);
	bdev = q->queuedata;
	req = blk_fetch_request(q);
	/* 逻辑结构(多叉树)
	1. request queue: O ----->     O ----->     O ----->     O ----->     O ----->     O ----->     O ----->     O 
	                 / | \        / | \        / | \        / | \        / | \        / | \        / | \        / | \                
			/  |  \      /  |  \      /  |  \      /  |  \      /  |  \      /  |  \      /  |  \      /  |  \  
	2. bio	       O   O   O    O   O   O    O   O   O    O   O   O    O   O   O    O   O   O    O   O   O    O   O   O   
	              /|\ /|\ /|\  /|\ /|\ /|\  /|\ /|\ /|\  /|\ /|\ /|\  /|\ /|\ /|\  /|\ /|\ /|\  /|\ /|\ /|\  /|\ /|\ /|\
	3. bv_page   o o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o o  
	*/
	/* 第一层遍历,遍历每一个request */
	while (req != NULL) {
		i++;
		printk("request_queue count: %d\n", i);
		/* 第二层遍历,遍历每一个bio */
		__rq_for_each_bio(bio, req) {
			j++;
			printk("bio count: %d\n", j);
			sector = bio->bi_iter.bi_sector;
			if (bio_end_sector(bio) > get_capacity(MYDISK.mydisk)) {
				status = -EIO;
				goto out;
			}
			/* 对bvec中的每个页提供服务 */
			bio_for_each_segment(bvec, bio, iter) {
				k++;
				printk("segment(page) count: %d\n", k);
				buffer = kmap_atomic(bvec.bv_page);
				offset = sector * MYDISK_SECTOR_SIZE;
				nbytes = bvec.bv_len;

				// 读写采用内存模拟
				if (bio_data_dir(bio) == WRITE)
					memcpy(MYDISK.data + offset, buffer + bvec.bv_offset, nbytes);
				else
					memcpy(buffer + bvec.bv_offset, MYDISK.data + offset, nbytes);
				for(t = 0; t < nbytes; t++) {
//					printk("buffer: %c", (buffer+ bvec.bv_offset)[t]);
				}
//				printk("\n");
				kunmap_atomic(buffer);
				sector += nbytes >> 9;
			}

			status = 0;
		}
	gpio_set_value(MYDISK.gpio, 0);
out:
		if (!__blk_end_request_cur(req, status))
			req = blk_fetch_request(q);
	}
}

static struct block_device_operations mydisk_fops = {
	.owner = THIS_MODULE,
	.open = mydisk_open,
	.release = mydisk_release,
	.ioctl = mydisk_ioctl,
	.getgeo = mydisk_getgeo,
};
// 自定义的处理块层发来的磁盘请求队列
void my_make_request_func(struct request_queue *q, struct bio *bio)
{

	unsigned char *buffer;
	unsigned long offset;
	unsigned long nbytes;
	struct bio_vec bvec;
	struct bvec_iter iter;
//	printk("bio %s !\n", (bio_data_dir(bio) == WRITE)?"write":"read");
	sector_t sector = bio->bi_iter.bi_sector;
	gpio_set_value(MYDISK.gpio, 1);
	bio_for_each_segment(bvec, bio, iter) {
		buffer = kmap_atomic(bvec.bv_page);
		offset = sector * MYDISK_SECTOR_SIZE;
		nbytes = bvec.bv_len;

		if (bio_data_dir(bio) == WRITE) // 解析读写动作,并执行(这里时内存模拟),实际中应该使用具体的块设备读写操作接口
			// 注意这里使用的数据源地址为上面映射得到的虚拟地址
			memcpy(MYDISK.data + offset, buffer + bvec.bv_offset, nbytes);
		else
			memcpy(buffer + bvec.bv_offset, MYDISK.data + offset, nbytes);
		kunmap_atomic(buffer);
		sector += nbytes >> 9;
	}

	gpio_set_value(MYDISK.gpio, 0);
	return  bio_endio(bio, 0);
}

static int disk_probe(struct platform_device *pdev)
{
	printk("match ok\n");
	of_property_read_u32(pdev->dev.of_node, "parts", &MYDISK.MYDISK_MINORS);
	of_property_read_u32(pdev->dev.of_node, "kschedule_enable", &MYDISK.kschedule_enable);
	MYDISK.gpio = of_get_gpio(pdev->dev.of_node, 0);

	printk("parts: %d\n", MYDISK.MYDISK_MINORS);
	printk("schedule: %d\n", MYDISK.kschedule_enable);
	printk("gpio: %d\n", MYDISK.gpio);
	

	// 1. 申请主设备号,如果参数为0,表示自动分配一个主设备号
	MYDISK.mydisk_major = register_blkdev(MYDISK.mydisk_major, MYDISK.mydisk_name);
	if (MYDISK.mydisk_major <= 0) {
		printk(KERN_WARNING "mydisk: unable to get major number\n");
		return -EBUSY;
	}
	// 2. 申请块设备对象
	MYDISK.mydisk = alloc_disk(MYDISK.MYDISK_MINORS);
	if (!MYDISK.mydisk) {
		printk (KERN_NOTICE "alloc_disk failure\n");
		return -1;
	}

	spin_lock_init(&MYDISK.mylock);
	if(MYDISK.kschedule_enable) {// need schedule help(使用内核提供的块层的块设备IO操作调度算法)
		MYDISK.mydisk->queue = blk_init_queue(mydisk_request_handler, &MYDISK.mylock);
	}
	else { 
	//	blk_queue_logical_block_size(bdev->queue, MYDISK_SECTOR_SIZE);
		MYDISK.mydisk->queue = blk_alloc_queue(GFP_KERNEL);
	// 使用自己的请求调度方法
		blk_queue_make_request(MYDISK.mydisk->queue, my_make_request_func);
	}
	// 对象成员构造
	MYDISK.mydisk->major = MYDISK.mydisk_major;
	MYDISK.mydisk->first_minor = 0;
	MYDISK.mydisk->fops = &mydisk_fops;
	// 磁盘设备名
	snprintf(MYDISK.mydisk->disk_name, 32, "mydisk%c", 'a');
	set_capacity(MYDISK.mydisk, MYDISK_SECTOR_TOTAL);		
	// disk设备注册到kernel
	add_disk(MYDISK.mydisk);

	return 0;
}

static int disk_remove(struct platform_device *pdev)
{
	del_gendisk(MYDISK.mydisk);
//	put_disk(mydisk);
	blk_cleanup_queue(MYDISK.mydisk->queue);
	unregister_blkdev(MYDISK.mydisk_major, "mydisk");

	return 0;
}


#ifdef CONFIG_OF
struct of_device_id disk_table[] = {
	{.compatible = "xdisk"},
	{}
};

#endif 

struct platform_driver disk_driver = {
	.probe = disk_probe,
	.remove = disk_remove,
	.driver = {
		.name = "xdisk",
		.of_match_table = of_match_ptr(disk_table)
	}
};
module_platform_driver(disk_driver);
测试:

1.分区

        fdisk /dev/mydiska

p->n->w

2.格式化

mkfs.ext2 /dev/mydiska1

3.挂载

mount /dev/mydisk1 /mnt/

4.测试

cd /mnt/ 

touch test


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值