*
* Copyright (C) 2005 Sean Young <sean@mess.org>
*
- * $Id: rfd_ftl.c,v 1.5 2005/11/07 11:14:21 gleixner Exp $
- *
* 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:
*
#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_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");
#define PREFIX "rfd_ftl: "
-/* Major device # for FTL device */
-
-/* A request for this major has been sent to device@lanana.org */
+/* This major has been assigned by device@lanana.org */
#ifndef RFD_FTL_MAJOR
-#define RFD_FTL_MAJOR 95
+#define RFD_FTL_MAJOR 256
#endif
/* Maximum number of partitions in an FTL region */
BLOCK_OK,
BLOCK_ERASING,
BLOCK_ERASED,
+ BLOCK_UNUSED,
BLOCK_FAILED
} state;
int free_sectors;
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;
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);
}
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;
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;
/* 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;
}
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;
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;
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;
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);
rc = part->mbd.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);
}
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);
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);
* 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;
block != part->reserved_block)
return block;
+ if (part->blocks[block].state == BLOCK_UNUSED)
+ erase_block(part, block);
+
if (++block >= part->total_blocks)
block = 0;
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;
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;
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 -
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;
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;
}
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;
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;
{
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;
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;
}
if (!add_mtd_blktrans_dev((void*)part))
return;
}
-
+out:
kfree(part);
}
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,