Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * rfd_ftl.c -- resident flash disk (flash translation layer)
0004  *
0005  * Copyright © 2005  Sean Young <sean@mess.org>
0006  *
0007  * This type of flash translation layer (FTL) is used by the Embedded BIOS
0008  * by General Software. It is known as the Resident Flash Disk (RFD), see:
0009  *
0010  *  http://www.gensw.com/pages/prod/bios/rfd.htm
0011  *
0012  * based on ftl.c
0013  */
0014 
0015 #include <linux/hdreg.h>
0016 #include <linux/init.h>
0017 #include <linux/mtd/blktrans.h>
0018 #include <linux/mtd/mtd.h>
0019 #include <linux/vmalloc.h>
0020 #include <linux/slab.h>
0021 #include <linux/jiffies.h>
0022 #include <linux/module.h>
0023 
0024 #include <asm/types.h>
0025 
0026 static int block_size = 0;
0027 module_param(block_size, int, 0);
0028 MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
0029 
0030 #define PREFIX "rfd_ftl: "
0031 
0032 /* This major has been assigned by device@lanana.org */
0033 #ifndef RFD_FTL_MAJOR
0034 #define RFD_FTL_MAJOR       256
0035 #endif
0036 
0037 /* Maximum number of partitions in an FTL region */
0038 #define PART_BITS       4
0039 
0040 /* An erase unit should start with this value */
0041 #define RFD_MAGIC       0x9193
0042 
0043 /* the second value is 0xffff or 0xffc8; function unknown */
0044 
0045 /* the third value is always 0xffff, ignored */
0046 
0047 /* next is an array of mapping for each corresponding sector */
0048 #define HEADER_MAP_OFFSET   3
0049 #define SECTOR_DELETED      0x0000
0050 #define SECTOR_ZERO     0xfffe
0051 #define SECTOR_FREE     0xffff
0052 
0053 #define SECTOR_SIZE     512
0054 
0055 #define SECTORS_PER_TRACK   63
0056 
0057 struct block {
0058     enum {
0059         BLOCK_OK,
0060         BLOCK_ERASING,
0061         BLOCK_ERASED,
0062         BLOCK_UNUSED,
0063         BLOCK_FAILED
0064     } state;
0065     int free_sectors;
0066     int used_sectors;
0067     int erases;
0068     u_long offset;
0069 };
0070 
0071 struct partition {
0072     struct mtd_blktrans_dev mbd;
0073 
0074     u_int block_size;       /* size of erase unit */
0075     u_int total_blocks;     /* number of erase units */
0076     u_int header_sectors_per_block; /* header sectors in erase unit */
0077     u_int data_sectors_per_block;   /* data sectors in erase unit */
0078     u_int sector_count;     /* sectors in translated disk */
0079     u_int header_size;      /* bytes in header sector */
0080     int reserved_block;     /* block next up for reclaim */
0081     int current_block;      /* block to write to */
0082     u16 *header_cache;      /* cached header */
0083 
0084     int is_reclaiming;
0085     int cylinders;
0086     int errors;
0087     u_long *sector_map;
0088     struct block *blocks;
0089 };
0090 
0091 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);
0092 
0093 static int build_block_map(struct partition *part, int block_no)
0094 {
0095     struct block *block = &part->blocks[block_no];
0096     int i;
0097 
0098     block->offset = part->block_size * block_no;
0099 
0100     if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
0101         block->state = BLOCK_UNUSED;
0102         return -ENOENT;
0103     }
0104 
0105     block->state = BLOCK_OK;
0106 
0107     for (i=0; i<part->data_sectors_per_block; i++) {
0108         u16 entry;
0109 
0110         entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
0111 
0112         if (entry == SECTOR_DELETED)
0113             continue;
0114 
0115         if (entry == SECTOR_FREE) {
0116             block->free_sectors++;
0117             continue;
0118         }
0119 
0120         if (entry == SECTOR_ZERO)
0121             entry = 0;
0122 
0123         if (entry >= part->sector_count) {
0124             printk(KERN_WARNING PREFIX
0125                 "'%s': unit #%d: entry %d corrupt, "
0126                 "sector %d out of range\n",
0127                 part->mbd.mtd->name, block_no, i, entry);
0128             continue;
0129         }
0130 
0131         if (part->sector_map[entry] != -1) {
0132             printk(KERN_WARNING PREFIX
0133                 "'%s': more than one entry for sector %d\n",
0134                 part->mbd.mtd->name, entry);
0135             part->errors = 1;
0136             continue;
0137         }
0138 
0139         part->sector_map[entry] = block->offset +
0140             (i + part->header_sectors_per_block) * SECTOR_SIZE;
0141 
0142         block->used_sectors++;
0143     }
0144 
0145     if (block->free_sectors == part->data_sectors_per_block)
0146         part->reserved_block = block_no;
0147 
0148     return 0;
0149 }
0150 
0151 static int scan_header(struct partition *part)
0152 {
0153     int sectors_per_block;
0154     int i, rc = -ENOMEM;
0155     int blocks_found;
0156     size_t retlen;
0157 
0158     sectors_per_block = part->block_size / SECTOR_SIZE;
0159     part->total_blocks = (u32)part->mbd.mtd->size / part->block_size;
0160 
0161     if (part->total_blocks < 2)
0162         return -ENOENT;
0163 
0164     /* each erase block has three bytes header, followed by the map */
0165     part->header_sectors_per_block =
0166             ((HEADER_MAP_OFFSET + sectors_per_block) *
0167             sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
0168 
0169     part->data_sectors_per_block = sectors_per_block -
0170             part->header_sectors_per_block;
0171 
0172     part->header_size = (HEADER_MAP_OFFSET +
0173             part->data_sectors_per_block) * sizeof(u16);
0174 
0175     part->cylinders = (part->data_sectors_per_block *
0176             (part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;
0177 
0178     part->sector_count = part->cylinders * SECTORS_PER_TRACK;
0179 
0180     part->current_block = -1;
0181     part->reserved_block = -1;
0182     part->is_reclaiming = 0;
0183 
0184     part->header_cache = kmalloc(part->header_size, GFP_KERNEL);
0185     if (!part->header_cache)
0186         goto err;
0187 
0188     part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
0189             GFP_KERNEL);
0190     if (!part->blocks)
0191         goto err;
0192 
0193     part->sector_map = vmalloc(array_size(sizeof(u_long),
0194                           part->sector_count));
0195     if (!part->sector_map)
0196         goto err;
0197 
0198     for (i=0; i<part->sector_count; i++)
0199         part->sector_map[i] = -1;
0200 
0201     for (i=0, blocks_found=0; i<part->total_blocks; i++) {
0202         rc = mtd_read(part->mbd.mtd, i * part->block_size,
0203                   part->header_size, &retlen,
0204                   (u_char *)part->header_cache);
0205 
0206         if (!rc && retlen != part->header_size)
0207             rc = -EIO;
0208 
0209         if (rc)
0210             goto err;
0211 
0212         if (!build_block_map(part, i))
0213             blocks_found++;
0214     }
0215 
0216     if (blocks_found == 0) {
0217         printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
0218                 part->mbd.mtd->name);
0219         rc = -ENOENT;
0220         goto err;
0221     }
0222 
0223     if (part->reserved_block == -1) {
0224         printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
0225                 part->mbd.mtd->name);
0226 
0227         part->errors = 1;
0228     }
0229 
0230     return 0;
0231 
0232 err:
0233     vfree(part->sector_map);
0234     kfree(part->header_cache);
0235     kfree(part->blocks);
0236 
0237     return rc;
0238 }
0239 
0240 static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
0241 {
0242     struct partition *part = container_of(dev, struct partition, mbd);
0243     u_long addr;
0244     size_t retlen;
0245     int rc;
0246 
0247     if (sector >= part->sector_count)
0248         return -EIO;
0249 
0250     addr = part->sector_map[sector];
0251     if (addr != -1) {
0252         rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
0253                   (u_char *)buf);
0254         if (!rc && retlen != SECTOR_SIZE)
0255             rc = -EIO;
0256 
0257         if (rc) {
0258             printk(KERN_WARNING PREFIX "error reading '%s' at "
0259                 "0x%lx\n", part->mbd.mtd->name, addr);
0260             return rc;
0261         }
0262     } else
0263         memset(buf, 0, SECTOR_SIZE);
0264 
0265     return 0;
0266 }
0267 
0268 static int erase_block(struct partition *part, int block)
0269 {
0270     struct erase_info *erase;
0271     int rc;
0272 
0273     erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
0274     if (!erase)
0275         return -ENOMEM;
0276 
0277     erase->addr = part->blocks[block].offset;
0278     erase->len = part->block_size;
0279 
0280     part->blocks[block].state = BLOCK_ERASING;
0281     part->blocks[block].free_sectors = 0;
0282 
0283     rc = mtd_erase(part->mbd.mtd, erase);
0284     if (rc) {
0285         printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
0286                 "failed\n", (unsigned long long)erase->addr,
0287                 (unsigned long long)erase->len, part->mbd.mtd->name);
0288         part->blocks[block].state = BLOCK_FAILED;
0289         part->blocks[block].free_sectors = 0;
0290         part->blocks[block].used_sectors = 0;
0291     } else {
0292         u16 magic = cpu_to_le16(RFD_MAGIC);
0293         size_t retlen;
0294 
0295         part->blocks[block].state = BLOCK_ERASED;
0296         part->blocks[block].free_sectors = part->data_sectors_per_block;
0297         part->blocks[block].used_sectors = 0;
0298         part->blocks[block].erases++;
0299 
0300         rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
0301                    sizeof(magic), &retlen, (u_char *)&magic);
0302         if (!rc && retlen != sizeof(magic))
0303             rc = -EIO;
0304 
0305         if (rc) {
0306             pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
0307                    part->mbd.mtd->name, part->blocks[block].offset);
0308             part->blocks[block].state = BLOCK_FAILED;
0309         } else {
0310             part->blocks[block].state = BLOCK_OK;
0311         }
0312     }
0313 
0314     kfree(erase);
0315 
0316     return rc;
0317 }
0318 
0319 static int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
0320 {
0321     void *sector_data;
0322     u16 *map;
0323     size_t retlen;
0324     int i, rc = -ENOMEM;
0325 
0326     part->is_reclaiming = 1;
0327 
0328     sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
0329     if (!sector_data)
0330         goto err3;
0331 
0332     map = kmalloc(part->header_size, GFP_KERNEL);
0333     if (!map)
0334         goto err2;
0335 
0336     rc = mtd_read(part->mbd.mtd, part->blocks[block_no].offset,
0337               part->header_size, &retlen, (u_char *)map);
0338 
0339     if (!rc && retlen != part->header_size)
0340         rc = -EIO;
0341 
0342     if (rc) {
0343         printk(KERN_ERR PREFIX "error reading '%s' at "
0344             "0x%lx\n", part->mbd.mtd->name,
0345             part->blocks[block_no].offset);
0346 
0347         goto err;
0348     }
0349 
0350     for (i=0; i<part->data_sectors_per_block; i++) {
0351         u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
0352         u_long addr;
0353 
0354 
0355         if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
0356             continue;
0357 
0358         if (entry == SECTOR_ZERO)
0359             entry = 0;
0360 
0361         /* already warned about and ignored in build_block_map() */
0362         if (entry >= part->sector_count)
0363             continue;
0364 
0365         addr = part->blocks[block_no].offset +
0366             (i + part->header_sectors_per_block) * SECTOR_SIZE;
0367 
0368         if (*old_sector == addr) {
0369             *old_sector = -1;
0370             if (!part->blocks[block_no].used_sectors--) {
0371                 rc = erase_block(part, block_no);
0372                 break;
0373             }
0374             continue;
0375         }
0376         rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
0377                   sector_data);
0378 
0379         if (!rc && retlen != SECTOR_SIZE)
0380             rc = -EIO;
0381 
0382         if (rc) {
0383             printk(KERN_ERR PREFIX "'%s': Unable to "
0384                 "read sector for relocation\n",
0385                 part->mbd.mtd->name);
0386 
0387             goto err;
0388         }
0389 
0390         rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
0391                 entry, sector_data);
0392 
0393         if (rc)
0394             goto err;
0395     }
0396 
0397 err:
0398     kfree(map);
0399 err2:
0400     kfree(sector_data);
0401 err3:
0402     part->is_reclaiming = 0;
0403 
0404     return rc;
0405 }
0406 
0407 static int reclaim_block(struct partition *part, u_long *old_sector)
0408 {
0409     int block, best_block, score, old_sector_block;
0410     int rc;
0411 
0412     /* we have a race if sync doesn't exist */
0413     mtd_sync(part->mbd.mtd);
0414 
0415     score = 0x7fffffff; /* MAX_INT */
0416     best_block = -1;
0417     if (*old_sector != -1)
0418         old_sector_block = *old_sector / part->block_size;
0419     else
0420         old_sector_block = -1;
0421 
0422     for (block=0; block<part->total_blocks; block++) {
0423         int this_score;
0424 
0425         if (block == part->reserved_block)
0426             continue;
0427 
0428         /*
0429          * Postpone reclaiming if there is a free sector as
0430          * more removed sectors is more efficient (have to move
0431          * less).
0432          */
0433         if (part->blocks[block].free_sectors)
0434             return 0;
0435 
0436         this_score = part->blocks[block].used_sectors;
0437 
0438         if (block == old_sector_block)
0439             this_score--;
0440         else {
0441             /* no point in moving a full block */
0442             if (part->blocks[block].used_sectors ==
0443                     part->data_sectors_per_block)
0444                 continue;
0445         }
0446 
0447         this_score += part->blocks[block].erases;
0448 
0449         if (this_score < score) {
0450             best_block = block;
0451             score = this_score;
0452         }
0453     }
0454 
0455     if (best_block == -1)
0456         return -ENOSPC;
0457 
0458     part->current_block = -1;
0459     part->reserved_block = best_block;
0460 
0461     pr_debug("reclaim_block: reclaiming block #%d with %d used "
0462          "%d free sectors\n", best_block,
0463          part->blocks[best_block].used_sectors,
0464          part->blocks[best_block].free_sectors);
0465 
0466     if (part->blocks[best_block].used_sectors)
0467         rc = move_block_contents(part, best_block, old_sector);
0468     else
0469         rc = erase_block(part, best_block);
0470 
0471     return rc;
0472 }
0473 
0474 /*
0475  * IMPROVE: It would be best to choose the block with the most deleted sectors,
0476  * because if we fill that one up first it'll have the most chance of having
0477  * the least live sectors at reclaim.
0478  */
0479 static int find_free_block(struct partition *part)
0480 {
0481     int block, stop;
0482 
0483     block = part->current_block == -1 ?
0484             jiffies % part->total_blocks : part->current_block;
0485     stop = block;
0486 
0487     do {
0488         if (part->blocks[block].free_sectors &&
0489                 block != part->reserved_block)
0490             return block;
0491 
0492         if (part->blocks[block].state == BLOCK_UNUSED)
0493             erase_block(part, block);
0494 
0495         if (++block >= part->total_blocks)
0496             block = 0;
0497 
0498     } while (block != stop);
0499 
0500     return -1;
0501 }
0502 
0503 static int find_writable_block(struct partition *part, u_long *old_sector)
0504 {
0505     int rc, block;
0506     size_t retlen;
0507 
0508     block = find_free_block(part);
0509 
0510     if (block == -1) {
0511         if (!part->is_reclaiming) {
0512             rc = reclaim_block(part, old_sector);
0513             if (rc)
0514                 goto err;
0515 
0516             block = find_free_block(part);
0517         }
0518 
0519         if (block == -1) {
0520             rc = -ENOSPC;
0521             goto err;
0522         }
0523     }
0524 
0525     rc = mtd_read(part->mbd.mtd, part->blocks[block].offset,
0526               part->header_size, &retlen,
0527               (u_char *)part->header_cache);
0528 
0529     if (!rc && retlen != part->header_size)
0530         rc = -EIO;
0531 
0532     if (rc) {
0533         printk(KERN_ERR PREFIX "'%s': unable to read header at "
0534                 "0x%lx\n", part->mbd.mtd->name,
0535                 part->blocks[block].offset);
0536         goto err;
0537     }
0538 
0539     part->current_block = block;
0540 
0541 err:
0542     return rc;
0543 }
0544 
0545 static int mark_sector_deleted(struct partition *part, u_long old_addr)
0546 {
0547     int block, offset, rc;
0548     u_long addr;
0549     size_t retlen;
0550     u16 del = cpu_to_le16(SECTOR_DELETED);
0551 
0552     block = old_addr / part->block_size;
0553     offset = (old_addr % part->block_size) / SECTOR_SIZE -
0554         part->header_sectors_per_block;
0555 
0556     addr = part->blocks[block].offset +
0557             (HEADER_MAP_OFFSET + offset) * sizeof(u16);
0558     rc = mtd_write(part->mbd.mtd, addr, sizeof(del), &retlen,
0559                (u_char *)&del);
0560 
0561     if (!rc && retlen != sizeof(del))
0562         rc = -EIO;
0563 
0564     if (rc) {
0565         printk(KERN_ERR PREFIX "error writing '%s' at "
0566             "0x%lx\n", part->mbd.mtd->name, addr);
0567         goto err;
0568     }
0569     if (block == part->current_block)
0570         part->header_cache[offset + HEADER_MAP_OFFSET] = del;
0571 
0572     part->blocks[block].used_sectors--;
0573 
0574     if (!part->blocks[block].used_sectors &&
0575         !part->blocks[block].free_sectors)
0576         rc = erase_block(part, block);
0577 
0578 err:
0579     return rc;
0580 }
0581 
0582 static int find_free_sector(const struct partition *part, const struct block *block)
0583 {
0584     int i, stop;
0585 
0586     i = stop = part->data_sectors_per_block - block->free_sectors;
0587 
0588     do {
0589         if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
0590                 == SECTOR_FREE)
0591             return i;
0592 
0593         if (++i == part->data_sectors_per_block)
0594             i = 0;
0595     }
0596     while(i != stop);
0597 
0598     return -1;
0599 }
0600 
0601 static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
0602 {
0603     struct partition *part = container_of(dev, struct partition, mbd);
0604     struct block *block;
0605     u_long addr;
0606     int i;
0607     int rc;
0608     size_t retlen;
0609     u16 entry;
0610 
0611     if (part->current_block == -1 ||
0612         !part->blocks[part->current_block].free_sectors) {
0613 
0614         rc = find_writable_block(part, old_addr);
0615         if (rc)
0616             goto err;
0617     }
0618 
0619     block = &part->blocks[part->current_block];
0620 
0621     i = find_free_sector(part, block);
0622 
0623     if (i < 0) {
0624         rc = -ENOSPC;
0625         goto err;
0626     }
0627 
0628     addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
0629         block->offset;
0630     rc = mtd_write(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
0631                (u_char *)buf);
0632 
0633     if (!rc && retlen != SECTOR_SIZE)
0634         rc = -EIO;
0635 
0636     if (rc) {
0637         printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
0638                 part->mbd.mtd->name, addr);
0639         goto err;
0640     }
0641 
0642     part->sector_map[sector] = addr;
0643 
0644     entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
0645 
0646     part->header_cache[i + HEADER_MAP_OFFSET] = entry;
0647 
0648     addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
0649     rc = mtd_write(part->mbd.mtd, addr, sizeof(entry), &retlen,
0650                (u_char *)&entry);
0651 
0652     if (!rc && retlen != sizeof(entry))
0653         rc = -EIO;
0654 
0655     if (rc) {
0656         printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
0657                 part->mbd.mtd->name, addr);
0658         goto err;
0659     }
0660     block->used_sectors++;
0661     block->free_sectors--;
0662 
0663 err:
0664     return rc;
0665 }
0666 
0667 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
0668 {
0669     struct partition *part = container_of(dev, struct partition, mbd);
0670     u_long old_addr;
0671     int i;
0672     int rc = 0;
0673 
0674     pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
0675 
0676     if (part->reserved_block == -1) {
0677         rc = -EACCES;
0678         goto err;
0679     }
0680 
0681     if (sector >= part->sector_count) {
0682         rc = -EIO;
0683         goto err;
0684     }
0685 
0686     old_addr = part->sector_map[sector];
0687 
0688     for (i=0; i<SECTOR_SIZE; i++) {
0689         if (!buf[i])
0690             continue;
0691 
0692         rc = do_writesect(dev, sector, buf, &old_addr);
0693         if (rc)
0694             goto err;
0695         break;
0696     }
0697 
0698     if (i == SECTOR_SIZE)
0699         part->sector_map[sector] = -1;
0700 
0701     if (old_addr != -1)
0702         rc = mark_sector_deleted(part, old_addr);
0703 
0704 err:
0705     return rc;
0706 }
0707 
0708 static int rfd_ftl_discardsect(struct mtd_blktrans_dev *dev,
0709                    unsigned long sector, unsigned int nr_sects)
0710 {
0711     struct partition *part = container_of(dev, struct partition, mbd);
0712     u_long addr;
0713     int rc;
0714 
0715     while (nr_sects) {
0716         if (sector >= part->sector_count)
0717             return -EIO;
0718 
0719         addr = part->sector_map[sector];
0720 
0721         if (addr != -1) {
0722             rc = mark_sector_deleted(part, addr);
0723             if (rc)
0724                 return rc;
0725 
0726             part->sector_map[sector] = -1;
0727         }
0728 
0729         sector++;
0730         nr_sects--;
0731     }
0732 
0733     return 0;
0734 }
0735 
0736 static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
0737 {
0738     struct partition *part = container_of(dev, struct partition, mbd);
0739 
0740     geo->heads = 1;
0741     geo->sectors = SECTORS_PER_TRACK;
0742     geo->cylinders = part->cylinders;
0743 
0744     return 0;
0745 }
0746 
0747 static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
0748 {
0749     struct partition *part;
0750 
0751     if ((mtd->type != MTD_NORFLASH && mtd->type != MTD_RAM) ||
0752         mtd->size > UINT_MAX)
0753         return;
0754 
0755     part = kzalloc(sizeof(struct partition), GFP_KERNEL);
0756     if (!part)
0757         return;
0758 
0759     part->mbd.mtd = mtd;
0760 
0761     if (block_size)
0762         part->block_size = block_size;
0763     else {
0764         if (!mtd->erasesize) {
0765             printk(KERN_WARNING PREFIX "please provide block_size");
0766             goto out;
0767         } else
0768             part->block_size = mtd->erasesize;
0769     }
0770 
0771     if (scan_header(part) == 0) {
0772         part->mbd.size = part->sector_count;
0773         part->mbd.tr = tr;
0774         part->mbd.devnum = -1;
0775         if (!(mtd->flags & MTD_WRITEABLE))
0776             part->mbd.readonly = 1;
0777         else if (part->errors) {
0778             printk(KERN_WARNING PREFIX "'%s': errors found, "
0779                     "setting read-only\n", mtd->name);
0780             part->mbd.readonly = 1;
0781         }
0782 
0783         printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
0784                 mtd->name, mtd->type, mtd->flags);
0785 
0786         if (!add_mtd_blktrans_dev(&part->mbd))
0787             return;
0788     }
0789 out:
0790     kfree(part);
0791 }
0792 
0793 static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
0794 {
0795     struct partition *part = container_of(dev, struct partition, mbd);
0796     int i;
0797 
0798     for (i=0; i<part->total_blocks; i++) {
0799         pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
0800             part->mbd.mtd->name, i, part->blocks[i].erases);
0801     }
0802 
0803     vfree(part->sector_map);
0804     kfree(part->header_cache);
0805     kfree(part->blocks);
0806     del_mtd_blktrans_dev(&part->mbd);
0807 }
0808 
0809 static struct mtd_blktrans_ops rfd_ftl_tr = {
0810     .name       = "rfd",
0811     .major      = RFD_FTL_MAJOR,
0812     .part_bits  = PART_BITS,
0813     .blksize    = SECTOR_SIZE,
0814 
0815     .readsect   = rfd_ftl_readsect,
0816     .writesect  = rfd_ftl_writesect,
0817     .discard    = rfd_ftl_discardsect,
0818     .getgeo     = rfd_ftl_getgeo,
0819     .add_mtd    = rfd_ftl_add_mtd,
0820     .remove_dev = rfd_ftl_remove_dev,
0821     .owner      = THIS_MODULE,
0822 };
0823 
0824 module_mtd_blktrans(rfd_ftl_tr);
0825 
0826 MODULE_LICENSE("GPL");
0827 MODULE_AUTHOR("Sean Young <sean@mess.org>");
0828 MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
0829         "used by General Software's Embedded BIOS");
0830