⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rfd_ftl.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * rfd_ftl.c -- resident flash disk (flash translation layer) * * Copyright (C) 2005  Sean Young <sean@mess.org> * * This type of flash translation layer (FTL) is used by the Embedded BIOS * by General Software. It is known as the Resident Flash Disk (RFD), see: * *	http://www.gensw.com/pages/prod/bios/rfd.htm * * based on ftl.c */#include <linux/hdreg.h>#include <linux/init.h>#include <linux/mtd/blktrans.h>#include <linux/mtd/mtd.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <asm/types.h>#define const_cpu_to_le16	__constant_cpu_to_le16static int block_size = 0;module_param(block_size, int, 0);MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");#define PREFIX "rfd_ftl: "/* This major has been assigned by device@lanana.org */#ifndef RFD_FTL_MAJOR#define RFD_FTL_MAJOR		256#endif/* Maximum number of partitions in an FTL region */#define PART_BITS		4/* An erase unit should start with this value */#define RFD_MAGIC		0x9193/* the second value is 0xffff or 0xffc8; function unknown *//* the third value is always 0xffff, ignored *//* next is an array of mapping for each corresponding sector */#define HEADER_MAP_OFFSET	3#define SECTOR_DELETED		0x0000#define SECTOR_ZERO		0xfffe#define SECTOR_FREE		0xffff#define SECTOR_SIZE		512#define SECTORS_PER_TRACK	63struct block {	enum {		BLOCK_OK,		BLOCK_ERASING,		BLOCK_ERASED,		BLOCK_UNUSED,		BLOCK_FAILED	} state;	int free_sectors;	int used_sectors;	int erases;	u_long offset;};struct partition {	struct mtd_blktrans_dev mbd;	u_int block_size;		/* size of erase unit */	u_int total_blocks;		/* number of erase units */	u_int header_sectors_per_block;	/* header sectors in erase unit */	u_int data_sectors_per_block;	/* data sectors in erase unit */	u_int sector_count;		/* sectors in translated disk */	u_int header_size;		/* bytes in header sector */	int reserved_block;		/* block next up for reclaim */	int current_block;		/* block to write to */	u16 *header_cache;		/* cached header */	int is_reclaiming;	int cylinders;	int errors;	u_long *sector_map;	struct block *blocks;};static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);static int build_block_map(struct partition *part, int block_no){	struct block *block = &part->blocks[block_no];	int i;	block->offset = part->block_size * block_no;	if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {		block->state = BLOCK_UNUSED;		return -ENOENT;	}	block->state = BLOCK_OK;	for (i=0; i<part->data_sectors_per_block; i++) {		u16 entry;		entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);		if (entry == SECTOR_DELETED)			continue;		if (entry == SECTOR_FREE) {			block->free_sectors++;			continue;		}		if (entry == SECTOR_ZERO)			entry = 0;		if (entry >= part->sector_count) {			printk(KERN_WARNING PREFIX				"'%s': unit #%d: entry %d corrupt, "				"sector %d out of range\n",				part->mbd.mtd->name, block_no, i, entry);			continue;		}		if (part->sector_map[entry] != -1) {			printk(KERN_WARNING PREFIX				"'%s': more than one entry for sector %d\n",				part->mbd.mtd->name, entry);			part->errors = 1;			continue;		}		part->sector_map[entry] = block->offset +			(i + part->header_sectors_per_block) * SECTOR_SIZE;		block->used_sectors++;	}	if (block->free_sectors == part->data_sectors_per_block)		part->reserved_block = block_no;	return 0;}static int scan_header(struct partition *part){	int sectors_per_block;	int i, rc = -ENOMEM;	int blocks_found;	size_t retlen;	sectors_per_block = part->block_size / SECTOR_SIZE;	part->total_blocks = part->mbd.mtd->size / part->block_size;	if (part->total_blocks < 2)		return -ENOENT;	/* each erase block has three bytes header, followed by the map */	part->header_sectors_per_block =			((HEADER_MAP_OFFSET + sectors_per_block) *			sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;	part->data_sectors_per_block = sectors_per_block -			part->header_sectors_per_block;	part->header_size = (HEADER_MAP_OFFSET +			part->data_sectors_per_block) * sizeof(u16);	part->cylinders = (part->data_sectors_per_block *			(part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;	part->sector_count = part->cylinders * SECTORS_PER_TRACK;	part->current_block = -1;	part->reserved_block = -1;	part->is_reclaiming = 0;	part->header_cache = kmalloc(part->header_size, GFP_KERNEL);	if (!part->header_cache)		goto err;	part->blocks = kcalloc(part->total_blocks, sizeof(struct block),			GFP_KERNEL);	if (!part->blocks)		goto err;	part->sector_map = vmalloc(part->sector_count * sizeof(u_long));	if (!part->sector_map) {		printk(KERN_ERR PREFIX "'%s': unable to allocate memory for "			"sector map", part->mbd.mtd->name);		goto err;	}	for (i=0; i<part->sector_count; i++)		part->sector_map[i] = -1;	for (i=0, blocks_found=0; i<part->total_blocks; i++) {		rc = part->mbd.mtd->read(part->mbd.mtd,				i * part->block_size, part->header_size,				&retlen, (u_char*)part->header_cache);		if (!rc && retlen != part->header_size)			rc = -EIO;		if (rc)			goto err;		if (!build_block_map(part, i))			blocks_found++;	}	if (blocks_found == 0) {		printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",				part->mbd.mtd->name);		rc = -ENOENT;		goto err;	}	if (part->reserved_block == -1) {		printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",				part->mbd.mtd->name);		part->errors = 1;	}	return 0;err:	vfree(part->sector_map);	kfree(part->header_cache);	kfree(part->blocks);	return rc;}static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf){	struct partition *part = (struct partition*)dev;	u_long addr;	size_t retlen;	int rc;	if (sector >= part->sector_count)		return -EIO;	addr = part->sector_map[sector];	if (addr != -1) {		rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE,						&retlen, (u_char*)buf);		if (!rc && retlen != SECTOR_SIZE)			rc = -EIO;		if (rc) {			printk(KERN_WARNING PREFIX "error reading '%s' at "				"0x%lx\n", part->mbd.mtd->name, addr);			return rc;		}	} else		memset(buf, 0, SECTOR_SIZE);	return 0;}static void erase_callback(struct erase_info *erase){	struct partition *part;	u16 magic;	int i, rc;	size_t retlen;	part = (struct partition*)erase->priv;	i = erase->addr / part->block_size;	if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) {		printk(KERN_ERR PREFIX "erase callback for unknown offset %x "				"on '%s'\n", erase->addr, part->mbd.mtd->name);		return;	}	if (erase->state != MTD_ERASE_DONE) {		printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', "				"state %d\n", erase->addr,				part->mbd.mtd->name, erase->state);		part->blocks[i].state = BLOCK_FAILED;		part->blocks[i].free_sectors = 0;		part->blocks[i].used_sectors = 0;		kfree(erase);		return;	}	magic = const_cpu_to_le16(RFD_MAGIC);	part->blocks[i].state = BLOCK_ERASED;	part->blocks[i].free_sectors = part->data_sectors_per_block;	part->blocks[i].used_sectors = 0;	part->blocks[i].erases++;	rc = part->mbd.mtd->write(part->mbd.mtd,		part->blocks[i].offset, sizeof(magic), &retlen,		(u_char*)&magic);	if (!rc && retlen != sizeof(magic))		rc = -EIO;	if (rc) {		printk(KERN_ERR PREFIX "'%s': unable to write RFD "				"header at 0x%lx\n",				part->mbd.mtd->name,				part->blocks[i].offset);		part->blocks[i].state = BLOCK_FAILED;	}	else		part->blocks[i].state = BLOCK_OK;	kfree(erase);}static int erase_block(struct partition *part, int block){	struct erase_info *erase;	int rc = -ENOMEM;	erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);	if (!erase)		goto err;	erase->mtd = part->mbd.mtd;	erase->callback = erase_callback;	erase->addr = part->blocks[block].offset;	erase->len = part->block_size;	erase->priv = (u_long)part;	part->blocks[block].state = BLOCK_ERASING;	part->blocks[block].free_sectors = 0;	rc = part->mbd.mtd->erase(part->mbd.mtd, erase);	if (rc) {		printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' "				"failed\n", erase->addr, erase->len,				part->mbd.mtd->name);		kfree(erase);	}err:	return rc;}static int move_block_contents(struct partition *part, int block_no, u_long *old_sector){	void *sector_data;	u16 *map;	size_t retlen;	int i, rc = -ENOMEM;	part->is_reclaiming = 1;	sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);	if (!sector_data)		goto err3;	map = kmalloc(part->header_size, GFP_KERNEL);	if (!map)		goto err2;	rc = part->mbd.mtd->read(part->mbd.mtd,		part->blocks[block_no].offset, part->header_size,		&retlen, (u_char*)map);	if (!rc && retlen != part->header_size)		rc = -EIO;	if (rc) {		printk(KERN_ERR PREFIX "error reading '%s' at "			"0x%lx\n", part->mbd.mtd->name,			part->blocks[block_no].offset);		goto err;	}	for (i=0; i<part->data_sectors_per_block; i++) {		u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);		u_long addr;		if (entry == SECTOR_FREE || entry == SECTOR_DELETED)			continue;		if (entry == SECTOR_ZERO)			entry = 0;		/* already warned about and ignored in build_block_map() */		if (entry >= part->sector_count)			continue;		addr = part->blocks[block_no].offset +			(i + part->header_sectors_per_block) * SECTOR_SIZE;		if (*old_sector == addr) {			*old_sector = -1;			if (!part->blocks[block_no].used_sectors--) {				rc = erase_block(part, block_no);				break;			}			continue;		}		rc = part->mbd.mtd->read(part->mbd.mtd, addr,			SECTOR_SIZE, &retlen, sector_data);		if (!rc && retlen != SECTOR_SIZE)			rc = -EIO;		if (rc) {			printk(KERN_ERR PREFIX "'%s': Unable to "				"read sector for relocation\n",				part->mbd.mtd->name);			goto err;		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -