diff options
Diffstat (limited to 'drivers/mtd/rfd_ftl.c')
| -rw-r--r-- | drivers/mtd/rfd_ftl.c | 148 |
1 files changed, 71 insertions, 77 deletions
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index a3e00a4635a..d1cbf26db2c 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -1,9 +1,7 @@ /* * rfd_ftl.c -- resident flash disk (flash translation layer) * - * Copyright (C) 2005 Sean Young <sean@mess.org> - * - * $Id: rfd_ftl.c,v 1.5 2005/11/07 11:14:21 gleixner Exp $ + * Copyright © 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: @@ -20,11 +18,10 @@ #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/jiffies.h> +#include <linux/module.h> #include <asm/types.h> -#define const_cpu_to_le16 __constant_cpu_to_le16 - static 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"); @@ -61,6 +58,7 @@ struct block { BLOCK_OK, BLOCK_ERASING, BLOCK_ERASED, + BLOCK_UNUSED, BLOCK_FAILED } state; int free_sectors; @@ -99,10 +97,8 @@ static int build_block_map(struct partition *part, int block_no) block->offset = part->block_size * block_no; if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) { - block->state = BLOCK_ERASED; /* assumption */ - block->free_sectors = part->data_sectors_per_block; - part->reserved_block = block_no; - return 1; + block->state = BLOCK_UNUSED; + return -ENOENT; } block->state = BLOCK_OK; @@ -124,7 +120,7 @@ static int build_block_map(struct partition *part, int block_no) entry = 0; if (entry >= part->sector_count) { - printk(KERN_NOTICE PREFIX + printk(KERN_WARNING PREFIX "'%s': unit #%d: entry %d corrupt, " "sector %d out of range\n", part->mbd.mtd->name, block_no, i, entry); @@ -132,7 +128,7 @@ static int build_block_map(struct partition *part, int block_no) } if (part->sector_map[entry] != -1) { - printk(KERN_NOTICE PREFIX + printk(KERN_WARNING PREFIX "'%s': more than one entry for sector %d\n", part->mbd.mtd->name, entry); part->errors = 1; @@ -159,7 +155,7 @@ static int scan_header(struct partition *part) size_t retlen; sectors_per_block = part->block_size / SECTOR_SIZE; - part->total_blocks = part->mbd.mtd->size / part->block_size; + part->total_blocks = (u32)part->mbd.mtd->size / part->block_size; if (part->total_blocks < 2) return -ENOENT; @@ -167,7 +163,7 @@ static int scan_header(struct partition *part) /* 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; + sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE; part->data_sectors_per_block = sectors_per_block - part->header_sectors_per_block; @@ -204,9 +200,9 @@ static int scan_header(struct partition *part) 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); + rc = 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; @@ -226,7 +222,7 @@ static int scan_header(struct partition *part) } if (part->reserved_block == -1) { - printk(KERN_NOTICE PREFIX "'%s': no empty erase unit found\n", + printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n", part->mbd.mtd->name); part->errors = 1; @@ -254,8 +250,8 @@ static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *b addr = part->sector_map[sector]; if (addr != -1) { - rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE, - &retlen, (u_char*)buf); + rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen, + (u_char *)buf); if (!rc && retlen != SECTOR_SIZE) rc = -EIO; @@ -279,16 +275,17 @@ static void erase_callback(struct erase_info *erase) 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); + i = (u32)erase->addr / part->block_size; + if (i >= part->total_blocks || part->blocks[i].offset != erase->addr || + erase->addr > UINT_MAX) { + printk(KERN_ERR PREFIX "erase callback for unknown offset %llx " + "on '%s'\n", (unsigned long long)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, + printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', " + "state %d\n", (unsigned long long)erase->addr, part->mbd.mtd->name, erase->state); part->blocks[i].state = BLOCK_FAILED; @@ -300,22 +297,21 @@ static void erase_callback(struct erase_info *erase) return; } - magic = const_cpu_to_le16(RFD_MAGIC); + magic = 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); + rc = 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_NOTICE PREFIX "'%s': unable to write RFD " + printk(KERN_ERR PREFIX "'%s': unable to write RFD " "header at 0x%lx\n", part->mbd.mtd->name, part->blocks[i].offset); @@ -345,12 +341,12 @@ static int erase_block(struct partition *part, int block) part->blocks[block].state = BLOCK_ERASING; part->blocks[block].free_sectors = 0; - rc = part->mbd.mtd->erase(part->mbd.mtd, erase); + rc = mtd_erase(part->mbd.mtd, erase); if (rc) { - printk(KERN_WARNING PREFIX "erase of region %x,%x on '%s' " - "failed\n", erase->addr, erase->len, - part->mbd.mtd->name); + printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' " + "failed\n", (unsigned long long)erase->addr, + (unsigned long long)erase->len, part->mbd.mtd->name); kfree(erase); } @@ -375,15 +371,14 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old if (!map) goto err2; - rc = part->mbd.mtd->read(part->mbd.mtd, - part->blocks[block_no].offset, part->header_size, - &retlen, (u_char*)map); + rc = 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_NOTICE PREFIX "error reading '%s' at " + printk(KERN_ERR PREFIX "error reading '%s' at " "0x%lx\n", part->mbd.mtd->name, part->blocks[block_no].offset); @@ -416,14 +411,14 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old } continue; } - rc = part->mbd.mtd->read(part->mbd.mtd, addr, - SECTOR_SIZE, &retlen, sector_data); + rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen, + sector_data); if (!rc && retlen != SECTOR_SIZE) rc = -EIO; if (rc) { - printk(KERN_NOTICE PREFIX "'%s': Unable to " + printk(KERN_ERR PREFIX "'%s': Unable to " "read sector for relocation\n", part->mbd.mtd->name); @@ -453,8 +448,7 @@ static int reclaim_block(struct partition *part, u_long *old_sector) int rc; /* we have a race if sync doesn't exist */ - if (part->mbd.mtd->sync) - part->mbd.mtd->sync(part->mbd.mtd); + mtd_sync(part->mbd.mtd); score = 0x7fffffff; /* MAX_INT */ best_block = -1; @@ -520,7 +514,7 @@ static int reclaim_block(struct partition *part, u_long *old_sector) * because if we fill that one up first it'll have the most chance of having * the least live sectors at reclaim. */ -static int find_free_block(const struct partition *part) +static int find_free_block(struct partition *part) { int block, stop; @@ -533,6 +527,9 @@ static int find_free_block(const struct partition *part) block != part->reserved_block) return block; + if (part->blocks[block].state == BLOCK_UNUSED) + erase_block(part, block); + if (++block >= part->total_blocks) block = 0; @@ -541,7 +538,7 @@ static int find_free_block(const struct partition *part) return -1; } -static int find_writeable_block(struct partition *part, u_long *old_sector) +static int find_writable_block(struct partition *part, u_long *old_sector) { int rc, block; size_t retlen; @@ -563,14 +560,15 @@ static int find_writeable_block(struct partition *part, u_long *old_sector) } } - rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block].offset, - part->header_size, &retlen, (u_char*)part->header_cache); + rc = mtd_read(part->mbd.mtd, part->blocks[block].offset, + part->header_size, &retlen, + (u_char *)part->header_cache); if (!rc && retlen != part->header_size) rc = -EIO; if (rc) { - printk(KERN_NOTICE PREFIX "'%s': unable to read header at " + printk(KERN_ERR PREFIX "'%s': unable to read header at " "0x%lx\n", part->mbd.mtd->name, part->blocks[block].offset); goto err; @@ -587,7 +585,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr) int block, offset, rc; u_long addr; size_t retlen; - u16 del = const_cpu_to_le16(SECTOR_DELETED); + u16 del = cpu_to_le16(SECTOR_DELETED); block = old_addr / part->block_size; offset = (old_addr % part->block_size) / SECTOR_SIZE - @@ -595,17 +593,16 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr) addr = part->blocks[block].offset + (HEADER_MAP_OFFSET + offset) * sizeof(u16); - rc = part->mbd.mtd->write(part->mbd.mtd, addr, - sizeof(del), &retlen, (u_char*)&del); + rc = mtd_write(part->mbd.mtd, addr, sizeof(del), &retlen, + (u_char *)&del); if (!rc && retlen != sizeof(del)) rc = -EIO; if (rc) { - printk(KERN_WARNING PREFIX "error writing '%s' at " + printk(KERN_ERR PREFIX "error writing '%s' at " "0x%lx\n", part->mbd.mtd->name, addr); - if (rc) - goto err; + goto err; } if (block == part->current_block) part->header_cache[offset + HEADER_MAP_OFFSET] = del; @@ -652,7 +649,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, if (part->current_block == -1 || !part->blocks[part->current_block].free_sectors) { - rc = find_writeable_block(part, old_addr); + rc = find_writable_block(part, old_addr); if (rc) goto err; } @@ -668,17 +665,16 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, addr = (i + part->header_sectors_per_block) * SECTOR_SIZE + block->offset; - rc = part->mbd.mtd->write(part->mbd.mtd, - addr, SECTOR_SIZE, &retlen, (u_char*)buf); + rc = mtd_write(part->mbd.mtd, addr, SECTOR_SIZE, &retlen, + (u_char *)buf); if (!rc && retlen != SECTOR_SIZE) rc = -EIO; if (rc) { - printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n", + printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n", part->mbd.mtd->name, addr); - if (rc) - goto err; + goto err; } part->sector_map[sector] = addr; @@ -688,17 +684,16 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, part->header_cache[i + HEADER_MAP_OFFSET] = entry; addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16); - rc = part->mbd.mtd->write(part->mbd.mtd, addr, - sizeof(entry), &retlen, (u_char*)&entry); + rc = mtd_write(part->mbd.mtd, addr, sizeof(entry), &retlen, + (u_char *)&entry); if (!rc && retlen != sizeof(entry)) rc = -EIO; if (rc) { - printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n", + printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n", part->mbd.mtd->name, addr); - if (rc) - goto err; + goto err; } block->used_sectors++; block->free_sectors--; @@ -763,10 +758,10 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { struct partition *part; - if (mtd->type != MTD_NORFLASH) + if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX) return; - part = kcalloc(1, sizeof(struct partition), GFP_KERNEL); + part = kzalloc(sizeof(struct partition), GFP_KERNEL); if (!part) return; @@ -776,23 +771,21 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) part->block_size = block_size; else { if (!mtd->erasesize) { - printk(KERN_NOTICE PREFIX "please provide block_size"); - return; - } - else + printk(KERN_WARNING PREFIX "please provide block_size"); + goto out; + } else part->block_size = mtd->erasesize; } if (scan_header(part) == 0) { part->mbd.size = part->sector_count; - part->mbd.blksize = SECTOR_SIZE; part->mbd.tr = tr; part->mbd.devnum = -1; if (!(mtd->flags & MTD_WRITEABLE)) part->mbd.readonly = 1; else if (part->errors) { - printk(KERN_NOTICE PREFIX "'%s': errors found, " - "setting read-only", mtd->name); + printk(KERN_WARNING PREFIX "'%s': errors found, " + "setting read-only\n", mtd->name); part->mbd.readonly = 1; } @@ -802,7 +795,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) if (!add_mtd_blktrans_dev((void*)part)) return; } - +out: kfree(part); } @@ -820,13 +813,14 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) vfree(part->sector_map); kfree(part->header_cache); kfree(part->blocks); - kfree(part); } -struct mtd_blktrans_ops rfd_ftl_tr = { +static struct mtd_blktrans_ops rfd_ftl_tr = { .name = "rfd", .major = RFD_FTL_MAJOR, .part_bits = PART_BITS, + .blksize = SECTOR_SIZE, + .readsect = rfd_ftl_readsect, .writesect = rfd_ftl_writesect, .getgeo = rfd_ftl_getgeo, |
