📄 ftl.c
字号:
/* Invalidate cache */ part->bam_index = 0xffff; ret = part->mbd.mtd->read(part->mbd.mtd, part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), part->BlocksPerUnit * sizeof(u_int32_t), &retlen, (u_char *) (part->bam_cache)); if (ret) { printk(KERN_WARNING"ftl: Error reading BAM in find_free\n"); return 0; } part->bam_index = eun; } /* Find a free block */ for (blk = 0; blk < part->BlocksPerUnit; blk++) if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break; if (blk == part->BlocksPerUnit) {#ifdef PSYCHO_DEBUG static int ne = 0; if (++ne == 1) dump_lists(part);#endif printk(KERN_NOTICE "ftl_cs: bad free list!\n"); return 0; } DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun); return blk;} /* find_free *//*====================================================================== Read a series of sectors from an FTL partition.======================================================================*/static int ftl_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks){ u_int32_t log_addr, bsize; u_long i; int ret; size_t offset, retlen; DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n", part, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition\n"); return -EIO; } bsize = 1 << part->header.EraseUnitSize; for (i = 0; i < nblocks; i++) { if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) { printk(KERN_NOTICE "ftl_cs: bad read offset\n"); return -EIO; } log_addr = part->VirtualBlockMap[sector+i]; if (log_addr == 0xffffffff) memset(buffer, 0, SECTOR_SIZE); else { offset = (part->EUNInfo[log_addr / bsize].Offset + (log_addr % bsize)); ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, (u_char *) buffer); if (ret) { printk(KERN_WARNING "Error reading MTD device in ftl_read()\n"); return ret; } } buffer += SECTOR_SIZE; } return 0;} /* ftl_read *//*====================================================================== Write a series of sectors to an FTL partition======================================================================*/static int set_bam_entry(partition_t *part, u_int32_t log_addr, u_int32_t virt_addr){ u_int32_t bsize, blk, le_virt_addr;#ifdef PSYCHO_DEBUG u_int32_t old_addr;#endif u_int16_t eun; int ret; size_t retlen, offset; DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n", part, log_addr, virt_addr); bsize = 1 << part->header.EraseUnitSize; eun = log_addr / bsize; blk = (log_addr % bsize) / SECTOR_SIZE; offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) + le32_to_cpu(part->header.BAMOffset));#ifdef PSYCHO_DEBUG ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&old_addr); if (ret) { printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); return ret; } old_addr = le32_to_cpu(old_addr); if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) || ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) || (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x" ", new = 0x%x\n", log_addr, old_addr, virt_addr); } return -EIO; }#endif le_virt_addr = cpu_to_le32(virt_addr); if (part->bam_index == eun) {#ifdef PSYCHO_DEBUG if (le32_to_cpu(part->bam_cache[blk]) != old_addr) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() " "inconsistency!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache" " = 0x%x\n", le32_to_cpu(part->bam_cache[blk]), old_addr); } return -EIO; }#endif part->bam_cache[blk] = le_virt_addr; } ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&le_virt_addr); if (ret) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n", log_addr, virt_addr); } return ret;} /* set_bam_entry */static int ftl_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks){ u_int32_t bsize, log_addr, virt_addr, old_addr, blk; u_long i; int ret; size_t retlen, offset; DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n", part, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition\n"); return -EIO; } /* See if we need to reclaim space, before we start */ while (part->FreeTotal < nblocks) { ret = reclaim_block(part); if (ret) return ret; } bsize = 1 << part->header.EraseUnitSize; virt_addr = sector * SECTOR_SIZE | BLOCK_DATA; for (i = 0; i < nblocks; i++) { if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) { printk(KERN_NOTICE "ftl_cs: bad write offset\n"); return -EIO; } /* Grab a free block */ blk = find_free(part); if (blk == 0) { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: internal error: " "no free blocks!\n"); return -ENOSPC; } /* Tag the BAM entry, and write the new block */ log_addr = part->bam_index * bsize + blk * SECTOR_SIZE; part->EUNInfo[part->bam_index].Free--; part->FreeTotal--; if (set_bam_entry(part, log_addr, 0xfffffffe)) return -EIO; part->EUNInfo[part->bam_index].Deleted++; offset = (part->EUNInfo[part->bam_index].Offset + blk * SECTOR_SIZE); ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer); if (ret) { printk(KERN_NOTICE "ftl_cs: block write failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr, offset); return -EIO; } /* Only delete the old entry when the new entry is ready */ old_addr = part->VirtualBlockMap[sector+i]; if (old_addr != 0xffffffff) { part->VirtualBlockMap[sector+i] = 0xffffffff; part->EUNInfo[old_addr/bsize].Deleted++; if (set_bam_entry(part, old_addr, 0)) return -EIO; } /* Finally, set up the new pointers */ if (set_bam_entry(part, log_addr, virt_addr)) return -EIO; part->VirtualBlockMap[sector+i] = log_addr; part->EUNInfo[part->bam_index].Deleted--; buffer += SECTOR_SIZE; virt_addr += SECTOR_SIZE; } return 0;} /* ftl_write */static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo){ partition_t *part = (void *)dev; u_long sect; /* Sort of arbitrary: round size down to 4KiB boundary */ sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; geo->heads = 1; geo->sectors = 8; geo->cylinders = sect >> 3; return 0;}static int ftl_readsect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf){ return ftl_read((void *)dev, buf, block, 1);}static int ftl_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf){ return ftl_write((void *)dev, buf, block, 1);}static int ftl_discardsect(struct mtd_blktrans_dev *dev, unsigned long sector, unsigned nr_sects){ partition_t *part = (void *)dev; uint32_t bsize = 1 << part->header.EraseUnitSize; DEBUG(1, "FTL erase sector %ld for %d sectors\n", sector, nr_sects); while (nr_sects) { uint32_t old_addr = part->VirtualBlockMap[sector]; if (old_addr != 0xffffffff) { part->VirtualBlockMap[sector] = 0xffffffff; part->EUNInfo[old_addr/bsize].Deleted++; if (set_bam_entry(part, old_addr, 0)) return -EIO; } nr_sects--; sector++; } return 0;}/*====================================================================*/static void ftl_freepart(partition_t *part){ vfree(part->VirtualBlockMap); part->VirtualBlockMap = NULL; kfree(part->VirtualPageMap); part->VirtualPageMap = NULL; kfree(part->EUNInfo); part->EUNInfo = NULL; kfree(part->XferInfo); part->XferInfo = NULL; kfree(part->bam_cache); part->bam_cache = NULL;} /* ftl_freepart */static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd){ partition_t *partition; partition = kzalloc(sizeof(partition_t), GFP_KERNEL); if (!partition) { printk(KERN_WARNING "No memory to scan for FTL on %s\n", mtd->name); return; } partition->mbd.mtd = mtd; if ((scan_header(partition) == 0) && (build_maps(partition) == 0)) { partition->state = FTL_FORMATTED;#ifdef PCMCIA_DEBUG printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n", le32_to_cpu(partition->header.FormattedSize) >> 10);#endif partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; partition->mbd.tr = tr; partition->mbd.devnum = -1; if (!add_mtd_blktrans_dev((void *)partition)) return; } ftl_freepart(partition); kfree(partition);}static void ftl_remove_dev(struct mtd_blktrans_dev *dev){ del_mtd_blktrans_dev(dev); ftl_freepart((partition_t *)dev); kfree(dev);}static struct mtd_blktrans_ops ftl_tr = { .name = "ftl", .major = FTL_MAJOR, .part_bits = PART_BITS, .blksize = SECTOR_SIZE, .readsect = ftl_readsect, .writesect = ftl_writesect, .discard = ftl_discardsect, .getgeo = ftl_getgeo, .add_mtd = ftl_add_mtd, .remove_dev = ftl_remove_dev, .owner = THIS_MODULE,};static int init_ftl(void){ return register_mtd_blktrans(&ftl_tr);}static void __exit cleanup_ftl(void){ deregister_mtd_blktrans(&ftl_tr);}module_init(init_ftl);module_exit(cleanup_ftl);MODULE_LICENSE("Dual MPL/GPL");MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -