diff -uNr mtd-19990817/include/linux/mtd/doc2000.h mtd-19990820/include/linux/mtd/doc2000.h --- mtd-19990817/include/linux/mtd/doc2000.h Tue Aug 17 17:36:03 1999 +++ mtd-19990820/include/linux/mtd/doc2000.h Tue Aug 17 23:57:08 1999 @@ -2,17 +2,20 @@ /* Linux driver for Disk-On-Chip 2000 */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: doc2000.h,v 1.1 1999/08/17 16:36:03 dwmw2 Exp $ */ +/* $Id: doc2000.h,v 1.2 1999/08/17 22:57:08 dwmw2 Exp $ */ #ifndef __MTD_DOC2000_H__ #define __MTD_DOC2000_H__ +#include + static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eecbuf); static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf); static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); -//static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); +int doc_erase (struct mtd_info *mtd, struct erase_info *instr); #define DoC_M_CDSN_IO 0x800 diff -uNr mtd-19990817/include/linux/mtd/mtd.h mtd-19990820/include/linux/mtd/mtd.h --- mtd-19990817/include/linux/mtd/mtd.h Tue Aug 17 17:36:03 1999 +++ mtd-19990820/include/linux/mtd/mtd.h Thu Aug 19 23:59:04 1999 @@ -2,13 +2,20 @@ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ +#ifdef __KERNEL__ +#include +#endif struct erase_info_user { unsigned long start; unsigned long length; }; - +struct mtd_oob_buf { + loff_t start; + ssize_t length; + unsigned char *ptr; +}; #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 @@ -62,6 +69,8 @@ #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) #define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) #ifndef __KERNEL__ diff -uNr mtd-19990817/include/linux/mtd/nand.h mtd-19990820/include/linux/mtd/nand.h --- mtd-19990817/include/linux/mtd/nand.h Tue Aug 17 17:36:03 1999 +++ mtd-19990820/include/linux/mtd/nand.h Tue Aug 17 23:57:08 1999 @@ -2,19 +2,20 @@ /* Defines for NAND flash devices */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nand.h,v 1.1 1999/08/17 16:36:03 dwmw2 Exp $ */ +/* $Id: nand.h,v 1.2 1999/08/17 22:57:08 dwmw2 Exp $ */ #ifndef __MTD_NAND_H__ #define __MTD_NAND_H__ #define NAND_CMD_READ0 0 #define NAND_CMD_READ1 1 -#define NAND_CMD_PGPROG 0x10 +#define NAND_CMD_PAGEPROG 0x10 #define NAND_CMD_READOOB 0x50 -#define NAND_CMD_ERASE 0x60 +#define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 #define NAND_CMD_SEQIN 0x80 #define NAND_CMD_READID 0x90 +#define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff #define NAND_MFR_TOSHIBA 0x98 diff -uNr mtd-19990817/include/linux/mtd/nftl.h mtd-19990820/include/linux/mtd/nftl.h --- mtd-19990817/include/linux/mtd/nftl.h Tue Aug 17 17:36:03 1999 +++ mtd-19990820/include/linux/mtd/nftl.h Fri Aug 20 16:31:01 1999 @@ -2,7 +2,7 @@ /* Defines for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftl.h,v 1.1 1999/08/17 16:36:03 dwmw2 Exp $ */ +/* $Id: nftl.h,v 1.3 1999/08/20 15:31:01 dwmw2 Exp $ */ #ifndef __MTD_NFTL_H__ #define __MTD_NFTL_H__ @@ -12,28 +12,28 @@ /* Block Control Information */ struct nftl_bci { - char ECCSig[6]; - unsigned short Status; + unsigned char ECCSig[6]; + __u16 Status; }__attribute__((packed)); /* Unit Control Information */ struct nftl_uci0 { - unsigned short VirtUnitNum; - unsigned short ReplUnitNum; - unsigned short SpareVirtUnitNum; - unsigned short SpareReplUnitNum; + __u16 VirtUnitNum; + __u16 ReplUnitNum; + __u16 SpareVirtUnitNum; + __u16 SpareReplUnitNum; } __attribute__((packed)); struct nftl_uci1 { - char WearInfo[4]; - short EraseMark; - short EraseMark1; + __u32 WearInfo; + __u16 EraseMark; + __u16 EraseMark1; } __attribute__((packed)); struct nftl_uci2 { - char WriteInh[4]; - char unused[4]; + __u32 WriteInh; + __u32 unused; } __attribute__((packed)); union nftl_uci { @@ -42,42 +42,55 @@ struct nftl_uci2 c; }; +struct nftl_oob { + struct nftl_bci b; + union nftl_uci u; +}; + /* NFTL Media Header */ struct NFTLMediaHeader { char DataOrgID[6]; - unsigned short NumEraseUnits; - unsigned short FirstPhysicalEUN; - unsigned int FormattedSize; + __u16 NumEraseUnits; + __u16 FirstPhysicalEUN; + __u32 FormattedSize; unsigned char UnitSizeFactor; } __attribute__((packed)); +#define MAX_ERASE_ZONES (8192 - 512) + #define ERASE_MARK 0x3c69 #define BLOCK_FREE 0xffff #define BLOCK_USED 0x5555 #define BLOCK_IGNORE 0x1111 #define BLOCK_DELETED 0x0000 +#define ZONE_GOOD 0xff +#define ZONE_BAD_ORIGINAL 0 +#define ZONE_BAD_MARKED 7 - +#ifdef __KERNEL__ struct NFTLrecord { struct mtd_info *mtd; - u16 MediaUnit, SpareMediaUnit; - u32 EraseSize; + __u16 MediaUnit, SpareMediaUnit; + __u32 EraseSize; struct NFTLMediaHeader MediaHdr; - u16 numvunits; - u32 long nr_sects; + __u16 numvunits; + __u16 lastEUN; + __u16 numfreeEUNs; + __u16 LastFreeEUN; /* To speed up finding a free EUN */ + __u32 long nr_sects; int head,sect,cyl; -#define numEUNs MediaHdr.NumEraseUnits - u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ - u16 *VirtualUnitTable; /* [numEUNs]: VirtualUnitNumber for each */ - u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ + __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ + __u16 *VirtualUnitTable; /* [numEUNs]: VirtualUnitNumber for each */ + __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ }; #define NFTL_MAJOR 93 #define MAX_NFTLS 16 +#endif /* __KERNEL__ */ #endif /* __MTD_NFTL_H__ */ diff -uNr mtd-19990817/kernel/Makefile mtd-19990820/kernel/Makefile --- mtd-19990817/kernel/Makefile Tue Aug 10 15:11:59 1999 +++ mtd-19990820/kernel/Makefile Fri Aug 20 16:31:01 1999 @@ -1,9 +1,9 @@ -# $Id: Makefile,v 1.6 1999/08/10 14:00:04 dwmw2 Exp $ +# $Id: Makefile,v 1.7 1999/08/20 15:31:01 dwmw2 Exp $ EXTRA_CFLAGS= -I$(shell pwd)/../include HWDRIVERS = slram.o doc1000.o doc2000.o vmax301.o octagon-5066.o -USERDRIVERS = ftl.o nftl.o mtdblock.o +USERDRIVERS = ftl.o nftl.o mtdblock.o MIX_OBJS = mtd.o mapped.o MI_OBJS = $(HWDRIVERS) $(USERDRIVERS) diff -uNr mtd-19990817/kernel/doc2000.c mtd-19990820/kernel/doc2000.c --- mtd-19990817/kernel/doc2000.c Tue Aug 17 18:37:25 1999 +++ mtd-19990820/kernel/doc2000.c Fri Aug 20 16:31:01 1999 @@ -2,7 +2,7 @@ /* Linux driver for Disk-On-Chip 2000 */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: doc2000.c,v 1.5 1999/08/17 17:37:20 dwmw2 Exp $ */ +/* $Id: doc2000.c,v 1.7 1999/08/20 15:31:01 dwmw2 Exp $ */ /* DOC_PASSIVE_PROBE: @@ -63,7 +63,7 @@ int _DoC_WaitReady (unsigned long docptr) { long c=-1; - + /* Out-of-line routine to wait for chip response */ while (!(ReadDOC(docptr, CSDNControl) & CSDN_CTRL_FR_B) && --c) ; @@ -87,17 +87,17 @@ /* DoC_Command: Send a flash command to the flash chip */ -static inline int DoC_Command(unsigned long docptr, unsigned char command) +static inline int DoC_Command(unsigned long docptr, unsigned char command, unsigned char xtraflags) { /* Assert the CLE (Command Latch Enable) line to the flash chip */ - WriteDOC( CSDN_CTRL_FLASH_IO | CSDN_CTRL_WP | CSDN_CTRL_CLE | CSDN_CTRL_CE, + WriteDOC( CSDN_CTRL_FLASH_IO | xtraflags | CSDN_CTRL_CLE | CSDN_CTRL_CE, docptr, CSDNControl); /* Send the command */ WriteDOC(command, docptr, 2k_CSDN_IO); /* Lower the CLE line */ - WriteDOC( CSDN_CTRL_FLASH_IO | CSDN_CTRL_WP | CSDN_CTRL_CE, docptr, CSDNControl); + WriteDOC( CSDN_CTRL_FLASH_IO | xtraflags | CSDN_CTRL_CE, docptr, CSDNControl); /* Wait for the chip to respond */ return DoC_WaitReady(docptr); @@ -105,19 +105,28 @@ /* DoC_Address: Set the current address for the flash chip */ -static inline int DoC_Address (unsigned long docptr, unsigned long ofs) +static inline int DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) { /* Assert the ALE (Address Latch Enable line to the flash chip */ - WriteDOC( CSDN_CTRL_FLASH_IO | CSDN_CTRL_WP | CSDN_CTRL_ALE | CSDN_CTRL_CE, + WriteDOC( CSDN_CTRL_FLASH_IO | xtraflags1 | CSDN_CTRL_ALE | CSDN_CTRL_CE, docptr, CSDNControl); /* Send the address */ - WriteDOC(ofs & 0xff, docptr, 2k_CSDN_IO); - WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CSDN_IO); - WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CSDN_IO); - + /* Three cases: + numbytes == 1: Send single byte, bits 0-7. + numbytes == 2: Send bits 9-16 followed by 17-23 + numbytes == 3: Send 0-7, 9-16, then 17-23 + */ + if (numbytes != 2) + WriteDOC(ofs & 0xff, docptr, 2k_CSDN_IO); + + if (numbytes != 1) { + WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CSDN_IO); + WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CSDN_IO); + } /* Lower the ALE line */ - WriteDOC( CSDN_CTRL_FLASH_IO | CSDN_CTRL_ECC_IO | CSDN_CTRL_WP | CSDN_CTRL_CE, docptr, CSDNControl); + WriteDOC( CSDN_CTRL_FLASH_IO | xtraflags1 | xtraflags2 | CSDN_CTRL_CE, docptr, CSDNControl); /* Wait for the chip to respond */ return DoC_WaitReady(docptr); @@ -157,21 +166,20 @@ DoC_SelectChip(doc->virtadr, chip); /* Reset the chip */ - if (DoC_Command(doc->virtadr, NAND_CMD_RESET)) { + if (DoC_Command(doc->virtadr, NAND_CMD_RESET, CSDN_CTRL_WP)) { printk("DoC_Command (reset) for %d,%d returned true\n", floor,chip); return 0; } /* Read the NAND chip ID: 1. Send ReadID command */ - if(DoC_Command(doc->virtadr, NAND_CMD_READID)) { + if(DoC_Command(doc->virtadr, NAND_CMD_READID, CSDN_CTRL_WP)) { printk("DoC_Command (ReadID) for %d,%d returned true\n", floor,chip); return 0; } /* Read the NAND chip ID: 2. Send address byte zero - * (actually we send three bytes with the DoC_Address() routine, but that's OK. */ - DoC_Address(doc->virtadr, 0); + DoC_Address(doc->virtadr, 1, 0, CSDN_CTRL_WP, 0); /* Read the manufacturer and device id codes from the device */ mfr = ReadDOC(doc->virtadr, 2k_CSDN_IO); @@ -495,6 +503,7 @@ #ifdef MODULE mtd->module = &__this_module; #endif + mtd->erase = doc_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = doc_read; @@ -502,9 +511,9 @@ mtd->read_ecc = doc_read_ecc; mtd->write_ecc = doc_write_ecc; mtd->read_oob = doc_read_oob; - mtd->write_oob = NULL; //doc_writeoob; + mtd->write_oob = doc_write_oob; mtd->sync = NULL; - + this->physadr = physadr; this->virtadr = docptr; this->ChipID = ChipID; @@ -549,7 +558,7 @@ docptr = this->virtadr; /* Don't allow read past end of device */ - if (from > this->totlen) + if (from >= this->totlen) return -EINVAL; /* Don't allow a single read to cross a 512-byte block boundary */ @@ -577,8 +586,8 @@ WriteDOC ( DOC_ECC_EN, docptr, ECCConf); } - DoC_Command(docptr, (from >> 8) & 1); - DoC_Address(docptr, from); + DoC_Command(docptr, (from >> 8) & 1, CSDN_CTRL_WP); + DoC_Address(docptr, 3, from, CSDN_CTRL_WP , CSDN_CTRL_ECC_IO); for (di=0; di < len ; di++) { buf[di] = ReadDOC(docptr, 2k_CSDN_IO); @@ -600,7 +609,7 @@ /* Check the ECC Status */ if (ReadDOC(docptr, 2k_ECCStatus) & 0x80) { /* There was an ECC error */ - printk("DiskOnChip ECC Error: Read at %lx\n", (long)len); + printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); /* FIXME: Implement ECC error correction, don't just whinge */ @@ -609,6 +618,12 @@ MTD-aware stuff can know about it by checking *retlen */ return -EIO; } +#ifdef PSYCHO_DEBUG + else + printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4], + eccbuf[5]); +#endif /* Reset the ECC engine */ WriteDOC(DOC_ECC_RESV, docptr , ECCConf); @@ -620,54 +635,232 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - return doc_write_ecc(mtd, to, len, retlen, buf, NULL); + static char as[6]; + return doc_write_ecc(mtd, to, len, retlen, buf, as); } -static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eecbuf) +static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf) { - /* Yeah, right */ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int di=0; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + /* Don't allow write past end of device */ + if (to >= this->totlen) + return -EINVAL; +#if 0 + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ( (to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + +#else + /* Don't allow writes which aren't exactly one block */ + if (to & 0x1ff || len != 0x200) + return -EINVAL; +#endif - return -EIO; + /* Find the chip which is to be used and select it */ + mychip = &this->chips[to >> (this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + /* Set device to main plane of flash */ + DoC_Command(docptr, NAND_CMD_RESET, CSDN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READ0, CSDN_CTRL_WP); + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); + WriteDOC ( DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + } + + DoC_Command(docptr, NAND_CMD_SEQIN, 0); + DoC_Address(docptr, 3, to, 0, CSDN_CTRL_ECC_IO); + + for (di=0; di < len ; di++) { + WriteDOC(buf[di], docptr, 2k_CSDN_IO); + } + + + if (eccbuf) { + WriteDOC( CSDN_CTRL_ECC_IO | CSDN_CTRL_CE , docptr, CSDNControl ); + + WriteDOC( 0, docptr, 2k_CSDN_IO); + WriteDOC( 0, docptr, 2k_CSDN_IO); + WriteDOC( 0, docptr, 2k_CSDN_IO); + + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di=0; di<6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); + } +#ifdef PSYCHO_DEBUG + printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long) to, eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5] ); +#endif + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_RESV, docptr , ECCConf); + + } + + DoC_Command(docptr, NAND_CMD_PAGEPROG, 0); + + DoC_Command(docptr, NAND_CMD_STATUS, CSDN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + if (ReadDOC(docptr, 2k_CSDN_IO) & 1) { + printk("Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + return -EIO; + } + + /* Let the caller know we completed it */ + *retlen = len; + + return 0; } static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - int i; - unsigned long docptr; - struct Nand *mychip; - + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int i; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + + + DoC_Command(docptr, NAND_CMD_READOOB, CSDN_CTRL_WP); + DoC_Address(docptr, 3, ofs, CSDN_CTRL_WP, 0); + + for (i=0; ivirtadr; +} - mychip = &this->chips[ofs >> this->chipshift]; +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int i; + unsigned long docptr; + struct Nand *mychip; - if (this->curfloor != mychip->floor) { - DoC_SelectFloor(docptr, mychip->floor); - DoC_SelectChip(docptr, mychip->chip); - } - else if (this->curchip != mychip->chip) { - DoC_SelectChip(docptr, mychip->chip); - } - this->curfloor = mychip->floor; - this->curchip = mychip->chip; + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + DoC_Command(docptr, NAND_CMD_RESET, CSDN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READOOB, CSDN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_SEQIN, 0); + DoC_Address(docptr, 3, ofs, 0, 0); + + for (i=0; ipriv; + unsigned long ofs = instr->addr; + unsigned long len = instr->len; + unsigned long docptr; + struct Nand *mychip; + + if(len != mtd->erasesize) + printk(KERN_WARNING "Erase not right size (%lx != %lx)n", len, mtd->erasesize); + + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + DoC_Command(docptr, NAND_CMD_ERASE1, 0); + DoC_Address(docptr, 2, ofs, 0, 0); + DoC_Command(docptr, NAND_CMD_ERASE2, 0); + + DoC_Command(docptr, NAND_CMD_STATUS, CSDN_CTRL_WP); + + if (ReadDOC(docptr, 2k_CSDN_IO) & 1) { + printk("Error writing\n"); + /* There was an error */ + kfree(instr); + return -EIO; + } + kfree(instr); + return 0; +} + /**************************************************************************** * @@ -684,7 +877,7 @@ printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n"); #ifdef PRERELEASE - printk(KERN_INFO "$Id: doc2000.c,v 1.5 1999/08/17 17:37:20 dwmw2 Exp $\n"); + printk(KERN_INFO "$Id: doc2000.c,v 1.7 1999/08/20 15:31:01 dwmw2 Exp $\n"); #endif for (i=0; doc_locations[i]; i++) { diff -uNr mtd-19990817/kernel/makefree.c mtd-19990820/kernel/makefree.c --- mtd-19990817/kernel/makefree.c Thu Jan 1 01:00:00 1970 +++ mtd-19990820/kernel/makefree.c Fri Aug 20 00:01:07 1999 @@ -0,0 +1,26 @@ + +#include +#include +#include + +#include +extern struct NFTLrecord *NFTLs[]; +extern u16 NFTL_makefreeblock(struct NFTLrecord *NFTL, unsigned block); + +int init_module(void) +{ + + if (NFTLs[0]) { + printk("Calling NFTL_makefreeblock()\n"); + NFTL_makefreeblock(NFTLs[0], 0xffffffff); + } + else + printk("No NFTL\n"); + + return -ENODEV; +} + +int cleanup_module(void) +{ + return 0; +} diff -uNr mtd-19990817/kernel/mtd.c mtd-19990820/kernel/mtd.c --- mtd-19990817/kernel/mtd.c Tue Aug 17 18:37:25 1999 +++ mtd-19990820/kernel/mtd.c Tue Aug 17 23:57:08 1999 @@ -1,6 +1,6 @@ /*====================================================================== - $Id: mtd.c,v 1.6 1999/08/17 17:16:36 dwmw2 Exp $ + $Id: mtd.c,v 1.7 1999/08/17 22:57:08 dwmw2 Exp $ A general driver for accessing Memory Technology Devices @@ -300,8 +300,81 @@ } break; } + + case MEMWRITEOOB: + { + struct mtd_oob_buf buf; + void *databuf; + ssize_t retlen; + + copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)); + + if (buf.length > 0x4096) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); + + if (ret) + return ret; + + databuf = kmalloc(buf.length, GFP_KERNEL); + if (!databuf) + return -ENOMEM; + + copy_from_user(databuf, buf.ptr, buf.length); + + ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); + + copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)); + + kfree(databuf); + break; + + } + + case MEMREADOOB: + { + struct mtd_oob_buf buf; + void *databuf; + ssize_t retlen; + + copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)); + + if (buf.length > 0x4096) + return -EINVAL; + + if (!mtd->read_oob) + ret = -EOPNOTSUPP; + else + ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); + + if (ret) + return ret; + + databuf = kmalloc(buf.length, GFP_KERNEL); + if (!databuf) + return -ENOMEM; + + ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); + + copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)); + + if (retlen) + copy_to_user(buf.ptr, databuf, retlen); + + kfree(databuf); + break; + } + + + + + default: - printk("Invalid ioctl %lx (MEMGETINFO = %lx)\n",cmd, MEMGETINFO); + printk("Invalid ioctl %x (MEMGETINFO = %x)\n",cmd, MEMGETINFO); ret = -EINVAL; } diff -uNr mtd-19990817/kernel/nftl.c mtd-19990820/kernel/nftl.c --- mtd-19990817/kernel/nftl.c Tue Aug 17 18:37:25 1999 +++ mtd-19990820/kernel/nftl.c Fri Aug 20 16:31:01 1999 @@ -2,7 +2,7 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftl.c,v 1.7 1999/08/17 17:16:36 dwmw2 Exp $ */ +/* $Id: nftl.c,v 1.8 1999/08/20 15:31:01 dwmw2 Exp $ */ #define PRERELEASE @@ -32,7 +32,7 @@ struct NFTLrecord *NFTLs[MAX_NFTLS]; int numnftls = 0; -void NFTL_setup(struct mtd_info *mtd, unsigned long ofs, +static void NFTL_setup(struct mtd_info *mtd, unsigned long ofs, struct NFTLMediaHeader *hdr) { int i; @@ -50,9 +50,9 @@ /* OK, it's a new one. Set up all the data structures. */ - +#ifdef PSYCHO_DEBUG printk("Found new NFTL nftl%c at offset %lx\n",numnftls + 'a', ofs); - +#endif if (hdr->UnitSizeFactor != 0xff) { printk("Sorry, we don't support UnitSizeFactor of != 1 yet\n"); return; @@ -82,16 +82,16 @@ } memset(thisNFTL->EUNtable, 0xff, 2 * thisNFTL->numvunits); - thisNFTL->VirtualUnitTable = kmalloc( 2 * thisNFTL->numEUNs , GFP_KERNEL); + thisNFTL->VirtualUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL); if (!thisNFTL->VirtualUnitTable) { printk("ENOMEM\n"); kfree(thisNFTL->EUNtable); kfree(thisNFTL); return; } - memset(thisNFTL->VirtualUnitTable, 0xff, 2 * thisNFTL->numEUNs); + memset(thisNFTL->VirtualUnitTable, 0xff, 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits)); - thisNFTL->ReplUnitTable = kmalloc( 2 * thisNFTL->numEUNs , GFP_KERNEL); + thisNFTL->ReplUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL); if (!thisNFTL->ReplUnitTable) { printk("ENOMEM\n"); kfree(thisNFTL->VirtualUnitTable); @@ -99,17 +99,18 @@ kfree(thisNFTL); return; } - memset(thisNFTL->ReplUnitTable, 0xff, 2 * thisNFTL->numEUNs); + memset(thisNFTL->ReplUnitTable, 0xff, 2 *le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) ); /* Ought to check the media header for bad blocks */ - - + thisNFTL->lastEUN = le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) + + le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN) - 1; + thisNFTL->numfreeEUNs = 0; + /* Scan each physical Erase Unit for validity and to find the Virtual Erase Unit Chain to which it belongs */ for (i=le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); - i < thisNFTL->numEUNs + - le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); i++) { + i <= thisNFTL->lastEUN; i++) { union nftl_uci uci; unsigned long ofs; @@ -122,6 +123,8 @@ uci.b.EraseMark1 != cpu_to_le16(0x3c69)) { printk("EUN %d: EraseMark not 0x3c69 (0x%4.4x 0x%4.4x instead)\n", i, le16_to_cpu(uci.b.EraseMark), le16_to_cpu(uci.b.EraseMark1)); + thisNFTL->VirtualUnitTable[i] = 0x7fff; + thisNFTL->ReplUnitTable[i] = 0xffff; continue; } @@ -142,17 +145,23 @@ thisNFTL->VirtualUnitTable[i] = le16_to_cpu(uci.a.VirtUnitNum); thisNFTL->ReplUnitTable[i] = le16_to_cpu(uci.a.ReplUnitNum); - /* if (!(VUN & 0x8000) && VUN < (arraybounds)).. optimises to: */ if (le16_to_cpu(uci.a.VirtUnitNum) < thisNFTL->numvunits) thisNFTL->EUNtable[le16_to_cpu(uci.a.VirtUnitNum) & 0x7fff] = i; + + if (uci.a.VirtUnitNum == 0xffff) { + /* Free block */ + thisNFTL->LastFreeEUN = i; + thisNFTL->numfreeEUNs++; + } } NFTLs[numnftls++] = thisNFTL; + thisNFTL->LastFreeEUN = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); if (thisNFTL->mtd->module) __MOD_INC_USE_COUNT(thisNFTL->mtd->module); - +#define PSYCHO_DEBUG #ifdef PSYCHO_DEBUG for (i=0; i < 10/* thisNFTL->numvunits*/; i++) { u16 curEUN = thisNFTL->EUNtable[i]; @@ -177,7 +186,7 @@ /* Search each DiskOnChip for NFTL partitions */ -void NFTL_init(void) +static void NFTL_init(void) { int i; struct mtd_info *mtd; @@ -209,6 +218,633 @@ } } +/* Actual NFTL access routines */ + + +static u16 NFTL_findfreeblock( struct NFTLrecord *thisNFTL, int desperate ) +{ + /* For a given Virtual Unit Chain: find or create a free block and + add it to the chain */ + /* We're passed the number of the last EUN in the chain, to save us from + having to look it up again */ + + u16 pot = thisNFTL->LastFreeEUN; + int silly = -1; + + /* Normally, we force a fold to happen before we run out of free blocks completely */ + + if (!desperate && thisNFTL->numfreeEUNs < 2) { + // printk("NFTL_findfreeblock: there are too few free EUNs\n"); + return 0xffff; + } + + /* Scan for a free block */ + + do { + if (thisNFTL->VirtualUnitTable[pot] == 0xffff) { + thisNFTL->LastFreeEUN = pot; + thisNFTL->numfreeEUNs--; + return pot; + } + + if (++pot > thisNFTL->lastEUN) + pot = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); + + if (!silly--) { + printk("Tell Dave he fucked up. LastFreeEUN = %d, FirstEUN = %d\n", + thisNFTL->LastFreeEUN, le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN)); + return 0xffff; + } + + } while (pot != thisNFTL->LastFreeEUN); + + return 0xffff; +} + + + + + +static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pendingblock ) +{ + u16 BlockMap[thisNFTL->EraseSize / 512]; + unsigned char BlockLastState[thisNFTL->EraseSize / 512]; + unsigned char BlockFreeFound[thisNFTL->EraseSize / 512]; + u16 thisEUN; + int block; + int silly = -1; + u16 targetEUN = 0xffff; + struct nftl_oob oob; + int inplace = 1; + + memset(BlockMap, 0xff, sizeof(BlockMap)); + memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); + + thisEUN = thisNFTL->EUNtable[thisVUC]; + + if (thisEUN == 0xffff) { + printk(KERN_WARNING "Trying to fold non-existent Virtual Unit Chain %d!\n", thisVUC); + return 0xffff; + } + + /* Scan to find the Erase Unit which holds the actual data for each + 512-byte block within the Chain. + */ + + while( thisEUN <= thisNFTL->lastEUN ) { + size_t retlen; + + targetEUN = thisEUN; + + for (block = 0 ; block < thisNFTL->EraseSize / 512; block ++) { + + MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + (block * 512),16 , &retlen, (char *)&oob); + + if (block == 2) { + if (oob.u.c.WriteInh != 0xffffffff) { + printk("Write Inhibited on EUN %d\n", thisEUN); + inplace = 0; + } else { + /* There's no other reason not to do inplace, + except ones that come later. So we don't need + to preserve inplace */ + inplace = 1; + } + } + + BlockLastState[block] = (unsigned char) oob.b.Status & 0xff; + + switch(oob.b.Status) { + case cpu_to_le16(BLOCK_FREE): + BlockFreeFound[block]=1; + break; + + case cpu_to_le16(BLOCK_USED): + if (!BlockFreeFound[block]) + BlockMap[block] = thisEUN; + else + printk(KERN_WARNING "BLOCK_USED found after BLOCK_FREE in Virtual Unit Chain %d for block %d\n", thisVUC, block); + break; + case cpu_to_le16(BLOCK_IGNORE): + case cpu_to_le16(BLOCK_DELETED): + break; + default: + printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, oob.b.Status); + } + } + + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; + } + + thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + } + + if (inplace) { + /* We're being asked to be a fold-in-place. Check + that all blocks are either present or BLOCK_FREE + in the target block. If not, we're going to have + to fold out-of-place anyway. + */ + + for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) { + + if (BlockLastState[block] != (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff) && + BlockMap[block] != targetEUN) { + //printk("Setting inplace to 0. VUC %d, block %d was %x lastEUN, and is in EUN %d (%s) %d\n", + // thisVUC, block, BlockLastState[block], BlockMap[block] , BlockMap[block]==targetEUN?"==":"!=", targetEUN); + + inplace = 0; + break; + } + } + + if ( pendingblock >= (thisVUC * (thisNFTL->EraseSize / 512)) && + pendingblock < ((thisVUC + 1)* (thisNFTL->EraseSize / 512)) && + BlockLastState[ pendingblock - (thisVUC * (thisNFTL->EraseSize / 512))] != + (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff)) { + //printk("Pending write not free in EUN %d. Folding out of place.\n", targetEUN); + inplace = 0; + } + + } + + if (!inplace) { + //printk("Cannot fold Virtual Unit Chain %d in place. Trying out-of-place\n", + // thisVUC); + /* We need to find a targetEUN to fold into. */ + targetEUN = NFTL_findfreeblock(thisNFTL, 1); + if (targetEUN == 0xffff) { + /* Ouch. Now we're screwed. We need to do a + fold-in-place of another chain to make room + for this one. We need a better way of selecting + which chain to fold, because makefreeblock will + only ask us to fold the same one again. + */ + printk(KERN_WARNING"NFTL_findfreeblock(desperate) returns 0xffff.\n"); + return 0xffff; + } + + } + + + /* OK. We now know the location of every block in the Virtual Unit Chain, + and the Erase Unit into which we are supposed to be copying. + Go for it. + */ + + // printk("Folding chain %d into unit %d\n", thisVUC, targetEUN); + + for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) { + unsigned char movebuf[512]; + struct nftl_oob oob; + size_t retlen; + + memset(&oob, 0xff, sizeof(oob)); + + /* If it's in the target EUN already, or if it's pending write, do nothing */ + if (BlockMap[block] == targetEUN ||(pendingblock == (thisVUC * (thisNFTL->EraseSize / 512) + block))) { + /* Except if it's the first block, in which case we have to + set the UnitNumbers */ + if (block == 0) { + + thisNFTL->mtd->read_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) , + 16, &retlen, (char *)&oob); + + // printk("Setting VirtUnitNum on EUN %d to %x, was %x\n", targetEUN, thisVUC, + // le16_to_cpu(oob.u.a.VirtUnitNum)); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff); + + thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) , + 16, &retlen, (char *)&oob); + } + continue; + } + + oob.b.Status = BLOCK_USED; + + switch(block) { + case 0: + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff); + // printk("Setting VirtUnitNum on EUN %d to %x\n", targetEUN, thisVUC); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + break; + + case 1: + oob.u.b.WearInfo = cpu_to_le32(3); // We don't use this, but M-Systems' drivers do + oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69); + break; + + case 2: + default: + oob.u.c.WriteInh = 0xffffffff; + oob.u.c.unused = 0xffffffff; + } + if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf, (char *)&oob) == -EIO) { + if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf, (char *)&oob) != -EIO) + printk("Error went away on retry.\n"); + } + + thisNFTL->mtd->write_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob); + + + /* FIXME: Add some error checking.... */ + thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512), + 16, &retlen, (char *)&oob); + + } + + /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ + + /* At this point, we have two different chains for this Virtual Unit, and no way to tell + them apart. If we crash now, we get confused. However, both contain the same data, so we + shouldn't actually lose data in this case. It's just that when we load up on a medium which + has duplicate chains, we need to free one of the chains because it's not necessary any more. + */ + + + thisEUN = thisNFTL->EUNtable[thisVUC]; + +// printk("Want to erase\n"); + /* For each block in the old chain (except the targetEUN of course), + free it and make it available for future use */ + + while( thisEUN <= thisNFTL->lastEUN && thisEUN != targetEUN) { + size_t retlen; + struct erase_info *instr; + u16 EUNtmp; + + instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); + if (!instr) { + printk(KERN_WARNING "Out of memory for struct erase_info\n"); + + EUNtmp = thisEUN; + + thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff; + thisNFTL->VirtualUnitTable[EUNtmp] = 0x7fff; + thisNFTL->ReplUnitTable[EUNtmp] = 0xffff; + } else { + memset(instr, 0, sizeof(struct erase_info)); + instr->addr = thisEUN * thisNFTL->EraseSize; + instr->len = thisNFTL->EraseSize; + + MTD_ERASE(thisNFTL->mtd, instr); + /* This is an async interface. Or will be. At which point + this code will break. */ + +#if 0 + MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob); + + printk("After erasing, EUN %d contains: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + thisEUN, oob.b.ECCSig[0], + oob.b.ECCSig[1], + oob.b.ECCSig[2], + oob.b.ECCSig[3], + oob.b.ECCSig[4], + oob.b.ECCSig[5]); +#endif + memset(&oob, 0xff, sizeof(oob)); + oob.u.b.WearInfo = cpu_to_le32(3); + oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69); + + MTD_WRITEOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob); + + EUNtmp = thisEUN; + + thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff; + thisNFTL->VirtualUnitTable[EUNtmp] = 0xffff; + thisNFTL->ReplUnitTable[EUNtmp] = 0xffff; + + thisNFTL->numfreeEUNs++; + + } + + // shifted upwards: thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + + } + + /* Make this the new start of chain for thisVUC */ + thisNFTL->VirtualUnitTable[targetEUN] = thisVUC; + thisNFTL->ReplUnitTable[targetEUN] = 0xffff; + + thisNFTL->EUNtable[thisVUC] = targetEUN; + return targetEUN; + +} + +u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock) +{ + /* This is the part that needs some cleverness applied. + For now, I'm doing the minimum applicable to actually + get the thing to work. + Wear-levelling and other clever stuff needs to be implemented + and we also need to do some assessment of the results when + the system loses power half-way through the routine. + */ + + u16 LongestChain = 0; + u16 ChainLength = 0, thislen; + u16 chain, EUN; + + + for (chain=0; chain < thisNFTL->MediaHdr.FormattedSize / thisNFTL->EraseSize; chain++) { + EUN = thisNFTL->EUNtable[chain]; + + thislen = 0; + + while (EUN <= thisNFTL->lastEUN) { + thislen++; + // printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); + EUN = thisNFTL->ReplUnitTable[EUN] & 0x7fff; + if (thislen > 0xff00) { + printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN); + } + if (thislen > 0xff10) { + /* Actually, don't return failure. Just ignore this chain and + get on with it. */ + thislen = 0; + break; + } + + } + + + if (thislen > ChainLength) { + // printk("New longest chain is %d with length %d\n", chain, thislen); + ChainLength = thislen; + LongestChain = chain; + } + } + + if (ChainLength < 2) { + printk(KERN_WARNING "No Virtual Unit Chains available for folding. Failing request\n"); + return 0xffff; + } + + return NFTL_foldchain (thisNFTL, LongestChain, pendingblock); +} + +static int NFTL_readblock(struct NFTLrecord *thisNFTL, + unsigned block, char *buffer) +{ + u16 lastgoodEUN = 0xffff; + u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)]; + unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + + int silly = -1; + + if (thisEUN == 0xffff) thisEUN = 0; + + while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) { + struct nftl_bci bci; + size_t retlen; + + MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci); + + switch(bci.Status) { + case cpu_to_le16(BLOCK_FREE): + thisEUN = 0; + break; + case cpu_to_le16(BLOCK_USED): + lastgoodEUN = thisEUN; + break; + case cpu_to_le16(BLOCK_IGNORE): + case cpu_to_le16(BLOCK_DELETED): + break; + default: + printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status); + } + + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",block / (thisNFTL->EraseSize / 512)); + return 1; + } + if (thisEUN) + thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + } + if (lastgoodEUN == 0xffff) { + memset(buffer, 0, 512); + } else { + loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs; + size_t retlen; + u_char eccbuf[6]; + thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf); + } + return 0; +} + +/* NFTL_findwriteunit: Return the unit number into which we can write + for this block. Make it available if it isn't already +*/ + +static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block) +{ + u16 lastEUN; + u16 thisVUC = block / (thisNFTL->EraseSize / 512); + u16 writeEUN; + unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + size_t retlen; + int silly = 0x10000, silly2 = 3; + struct nftl_oob oob; + int debug=0; + + do { + + /* Scan the media to find a unit in the VUC which has + a free space for the block in question. + */ + + /* This condition catches the 0x[7f]fff cases, as well as + being a sanity check for past-end-of-media access + */ + lastEUN = 0xffff; + writeEUN = thisNFTL->EUNtable[thisVUC]; + + while(writeEUN <= thisNFTL->lastEUN) { + struct nftl_bci bci; + size_t retlen; + + lastEUN = writeEUN; + + MTD_READOOB(thisNFTL->mtd, (writeEUN * thisNFTL->EraseSize) + + blockofs,8, &retlen, (char *)&bci); + + if (debug) + printk("Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status)); + + switch(bci.Status) { + case cpu_to_le16(BLOCK_FREE): + return writeEUN; + + case cpu_to_le16(BLOCK_DELETED): + case cpu_to_le16(BLOCK_USED): + case cpu_to_le16(BLOCK_IGNORE): + break; + default: + // Invalid block. Don't use it any more. Must implement. + break; + } + + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; + } + + /* Skip to next block in chain */ + + writeEUN = thisNFTL->ReplUnitTable[writeEUN] & 0x7fff; + } + + /* OK. We didn't find one in the existing chain, or there + is no existing chain. */ + + /* Try to find an already-free block */ + + writeEUN = NFTL_findfreeblock(thisNFTL, 0); + + if (writeEUN == 0xffff) { + /* That didn't work - there were no free blocks just + waiting to be picked up. We're going to have to fold + a chain to make room. + */ + + /* First remember the start of this chain */ + // u16 startEUN = thisNFTL->EUNtable[thisVUC]; + + //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); + writeEUN = NFTL_makefreeblock(thisNFTL, block); + + if (writeEUN == 0xffff) { + /* Ouch. This should never happen - we should + always be able to make some room somehow. + If we get here, we've allocated more storage + space than actual media, or our makefreeblock + routine is missing something. + */ + printk(KERN_WARNING "Cannot make free space.\n"); + return 0xffff; + } + // printk("Restarting scan\n"); + lastEUN = 0xffff; + // debug = 1; + continue; +#if 0 + if (startEUN != thisNFTL->EUNtable[thisVUC]) { + /* The fold operation has moved the chain + that we're looking at. Start the scan again. + */ + continue; + } +#endif + } + + /* We've found a free block. Insert it into the chain. */ + + if (lastEUN != 0xffff) { + /* Addition to an existing chain. Make the previous + last block in the chain point to this one. + */ + + //printk("Linking EUN %d to EUN %d in VUC %d\n", + // lastEUN, writeEUN, thisVUC); + /* Both in our cache... */ + thisNFTL->ReplUnitTable[lastEUN] = writeEUN; + + + /* ... and on the flash itself */ + MTD_READOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen, + (char *)&oob); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); + + MTD_WRITEOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen, + (char *)&oob); + + thisVUC |= 0x8000; /* It's a replacement block */ + } else { + /* The first block in a new chain */ + thisNFTL->EUNtable[thisVUC] = writeEUN; + } + + /* Now set up the actual EUN we're writing into */ + + /* Both in our cache... */ + thisNFTL->VirtualUnitTable[writeEUN] = thisVUC; + thisNFTL->ReplUnitTable[writeEUN] = 0xffff; + + /* ... and on the flash itself */ + MTD_READOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16, + &retlen, (char *)&oob); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + + MTD_WRITEOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16, + &retlen, (char *)&oob); + + return writeEUN; + + } while (silly2--); + + printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; +} + +static int NFTL_writeblock(struct NFTLrecord *thisNFTL, unsigned block, + char *buffer) +{ + u16 writeEUN; + unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + size_t retlen; + u16 eccbuf[8]; + + // if (thisEUN == 0xffff) thisEUN = 0; + + writeEUN = NFTL_findwriteunit(thisNFTL, block); + +// printk("writeblock(%d): Write to Unit %d\n", block, writeEUN); + + if (writeEUN == 0xffff) { + printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n"); + /* If we _still_ haven't got a block to use, we're screwed */ + return 1; + } + // printk("Writing block %lx to EUN %x\n",block, writeEUN); + + + thisNFTL->mtd->write_ecc(thisNFTL->mtd, + (writeEUN * thisNFTL->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)eccbuf); + eccbuf[3] = BLOCK_USED; + eccbuf[4] = eccbuf[5] = eccbuf[6] = eccbuf[7] = 0xffff; + + thisNFTL->mtd->write_oob(thisNFTL->mtd, + (writeEUN * thisNFTL->EraseSize) + blockofs, + 16, &retlen, (char *)eccbuf); + + return 0; +} + + + +/* Linux-specific block device functions */ + +/* I _HATE_ the Linux block device setup more than anything else I've ever + * encountered, except ... + */ + +static int nftl_sizes[256]={0,}; +static int nftl_blocksizes[256] = {0,}; + +/* .. for the Linux partition table handling. */ + +struct hd_struct part_table[256] = {{0,0},}; + void dummy_init (struct gendisk *crap) {} @@ -221,13 +857,10 @@ return -EINVAL; } -struct hd_struct part_table[256] = {{0,0},}; -static int nftl_sizes[256]={0,}; -static int nftl_blocksizes[256] = {0,}; void nftl_request(void) { - unsigned int dev, block, nsect, blockofs; + unsigned int dev, block, nsect; struct NFTLrecord *thisNFTL; char *buffer; repeat: @@ -261,52 +894,29 @@ for ( ; nsect > 0; nsect-- , block++, buffer+= 512) { /* Read a single sector to CURRENT->buffer + (512 * i) */ - u16 lastgoodEUN = 0xffff; - u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)]; - blockofs = (block * 512) & (thisNFTL->EraseSize -1); - - if (thisEUN == 0xffff) thisEUN = 0; - - while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) { - struct nftl_bci bci; - size_t retlen; - - MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci); - - switch(bci.Status) { - case cpu_to_le16(BLOCK_FREE): - thisEUN = 0; - break; - case cpu_to_le16(BLOCK_USED): - lastgoodEUN = thisEUN; - break; - default: - printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status); - } - if (thisEUN) - thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; - } - if (lastgoodEUN == 0xffff) { - memset(buffer, 0, 512); - } else { - loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs; - size_t retlen; - u_char eccbuf[6]; - - thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf); + + if (NFTL_readblock(thisNFTL, block, buffer)) { + end_request(0); + goto repeat; } } - end_request(1); goto repeat; } else if (CURRENT->cmd == WRITE) { - printk("Ouch, gotta write request\n"); - end_request(0); /* fail */ + + for ( ; nsect > 0; nsect-- , block++, buffer+= 512) { + /* Read a single sector to CURRENT->buffer + (512 * i) */ + + if (NFTL_writeblock(thisNFTL, block, buffer)) { + end_request(0); + goto repeat; + } + } + end_request(1); goto repeat; } else { - end_request(0); /* fail */ goto repeat; } @@ -314,9 +924,10 @@ static int nftl_open(struct inode *ip, struct file *fp) { +#if 0 if (fp->f_mode & FMODE_WRITE) return -EROFS; - +#endif MOD_INC_USE_COUNT; return 0; } @@ -326,7 +937,9 @@ struct super_block *sb = get_super(inode->i_rdev); fsync_dev(inode->i_rdev); + if (sb) invalidate_inodes(sb); + invalidate_buffers(inode->i_rdev); // mtd->sync() @@ -380,7 +993,7 @@ printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n"); #ifdef PRERELEASE - printk(KERN_INFO"$Id: nftl.c,v 1.7 1999/08/17 17:16:36 dwmw2 Exp $\n"); + printk(KERN_INFO"$Id: nftl.c,v 1.8 1999/08/20 15:31:01 dwmw2 Exp $\n"); #endif NFTL_init(); @@ -418,7 +1031,6 @@ void cleanup_module(void) { struct gendisk *gd, **gdp; - for (numnftls-- ; numnftls >= 0; numnftls--) { kfree(NFTLs[numnftls]->VirtualUnitTable); diff -uNr mtd-19990817/util/MAKEDEV mtd-19990820/util/MAKEDEV --- mtd-19990817/util/MAKEDEV Thu Jan 1 01:00:00 1970 +++ mtd-19990820/util/MAKEDEV Tue Aug 17 18:54:58 1999 @@ -0,0 +1,54 @@ +#!/bin/bash +# $Id: MAKEDEV,v 1.1 1999/08/17 17:54:58 dwmw2 Exp $ + +function mkftl () { + mknod /dev/ftl$1 b 44 $2 + for a in `seq 1 15`; do + mknod /dev/ftl$1$a b 44 `expr $2 + $a` + done +} +function mknftl () { + mknod /dev/nftl$1 b 44 $2 + for a in `seq 1 15`; do + mknod /dev/nftl$1$a b 93 `expr $2 + $a` + done +} + +mkftl a 0 +mkftl b 16 +mkftl c 32 +mkftl d 64 +mkftl e 80 +mkftl f 96 +mkftl g 112 +mkftl h 128 +mkftl i 144 +mkftl j 160 +mkftl k 176 +mkftl l 192 +mkftl m 208 +mkftl n 224 +mkftl o 240 + +mknftl a 0 +mknftl b 16 +mknftl c 32 +mknftl d 64 +mknftl e 80 +mknftl f 96 +mknftl g 112 +mknftl h 128 +mknftl i 144 +mknftl j 160 +mknftl k 176 +mknftl l 192 +mknftl m 208 +mknftl n 224 +mknftl o 240 + +for a in `seq 0 16` ; do + mknod /dev/mtd$a c 90 `expr $a + $a` + mknod /dev/mtdr$a c 90 `expr $a + $a + 1` + mknod /dev/mtdblock$a b 31 $a +done + diff -uNr mtd-19990817/util/Makefile mtd-19990820/util/Makefile --- mtd-19990817/util/Makefile Mon Aug 9 19:01:45 1999 +++ mtd-19990820/util/Makefile Wed Aug 18 01:32:28 1999 @@ -1,3 +1,4 @@ CFLAGS=-I../include -ftl_format: ftl_format.c +all: ftl_format erase nftldump nanddump doc_loadbios nftl_format + diff -uNr mtd-19990817/util/doc_loadbios.c mtd-19990820/util/doc_loadbios.c --- mtd-19990817/util/doc_loadbios.c Thu Jan 1 01:00:00 1970 +++ mtd-19990820/util/doc_loadbios.c Wed Aug 18 01:31:09 1999 @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +unsigned char databuf[512]; + +int main(int argc,char **argv) +{ + mtd_info_t meminfo; + int ifd,ofd; + struct stat statbuf; + erase_info_t erase; + unsigned long retlen, ofs; + + if (argc < 3) { + fprintf(stderr,"You must specify a device and the source firmware file\n"); + return 1; + } + + // Open and size the device + if ((ofd = open(argv[1],O_RDWR)) < 0) { + perror("Open flash device"); + return 1; + } + + if ((ifd = open(argv[2], O_RDONLY)) < 0) { + perror("Open firmware file\n"); + close(ofd); + return 1; + } + + if (fstat(ifd, &statbuf) != 0) { + perror("Stat firmware file"); + close(ofd); + close(ifd); + return 1; + } + + if (statbuf.st_size > 65536) { + printf("Firmware too large (%ld bytes)\n",statbuf.st_size); + close(ifd); + close(ofd); + return 1; + } + + + if (ioctl(ofd,MEMGETINFO,&meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + close(ifd); + close(ofd); + return 1; + } + + erase.length = meminfo.erasesize; + + for (ofs = 0 ; ofs < statbuf.st_size ; ofs += meminfo.erasesize) { + erase.start = ofs; + printf("Performing Flash Erase of length %lu at offset %lu\n", + erase.length, erase.start); + + if (ioctl(ofd,MEMERASE,&erase) != 0) { + perror("ioctl(MEMERASE)"); + close(ofd); + close(ifd); + return 1; + } + } + + /* Set upper half of buffer to 0xff */ + memset(databuf+256, 0xff, 256); + + do { + retlen = read(ifd, databuf, 256); + if (retlen != 256) + memset(databuf+retlen, 0xff, 256 - retlen); + + write(ofd, databuf, 512); + } while (retlen == 256); +} diff -uNr mtd-19990817/util/erase.c mtd-19990820/util/erase.c --- mtd-19990817/util/erase.c Tue Aug 17 17:37:30 1999 +++ mtd-19990820/util/erase.c Wed Aug 18 01:17:27 1999 @@ -10,7 +10,7 @@ int main(int argc,char *argv[]) { - struct mtd_info meminfo; + mtd_info_t meminfo; int Fd; unsigned long Length; @@ -27,24 +27,20 @@ return 8; } - if (ioctl(Fd,BLKGETSIZE,&Length) < 0) - { - perror("ioctl BLKGETSIZE"); - return 9; - } - else - Length *= 512; if (ioctl(Fd,MEMGETINFO,&meminfo) == 0) { - struct erase_info erase; + erase_info_t erase; - erase.length = Length; + erase.length = meminfo.erasesize; + printf("Erasesize 0x%lx\n", meminfo.erasesize); + if (argc >= 2) - erase.length = atoi(argv[2]); - erase.start = 0; + erase.start = atoi(argv[2]); + else + erase.start = 0; - printf("Performing Flash Erase %lu",erase.length); + printf("Performing Flash Erase of length %lu at offset %lu",erase.length, erase.start); fflush(stdout); if (ioctl(Fd,MEMERASE,&erase) != 0) diff -uNr mtd-19990817/util/ftl_format.c mtd-19990820/util/ftl_format.c --- mtd-19990817/util/ftl_format.c Tue Aug 17 17:37:12 1999 +++ mtd-19990820/util/ftl_format.c Tue Aug 17 20:04:42 1999 @@ -1,6 +1,6 @@ /*====================================================================== - $Id: ftl_format.c,v 1.2 1999/08/09 18:04:57 dwmw2 Exp $ + $Id: ftl_format.c,v 1.3 1999/08/17 19:04:42 dwmw2 Exp $ Utility to create an FTL partition in a memory region diff -uNr mtd-19990817/util/mkfs.ffs2.c mtd-19990820/util/mkfs.ffs2.c --- mtd-19990817/util/mkfs.ffs2.c Tue Aug 17 17:37:58 1999 +++ mtd-19990820/util/mkfs.ffs2.c Tue Aug 17 20:04:42 1999 @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: mkfs.ffs2.c,v 1.1 1999/08/09 14:26:22 dwmw2 Exp $ +// $Id: mkfs.ffs2.c,v 1.2 1999/08/17 19:04:42 dwmw2 Exp $ /* ###################################################################### Microsoft Flash File System 2 diff -uNr mtd-19990817/util/nanddump.c mtd-19990820/util/nanddump.c --- mtd-19990817/util/nanddump.c Thu Jan 1 01:00:00 1970 +++ mtd-19990820/util/nanddump.c Wed Aug 18 15:33:48 1999 @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +char readbuf[512]; +char oobbuf[16]; + + +void main(int argc, char **argv) +{ + unsigned long ofs; + int i,j; + int fd; + int ofd = -1; + struct mtd_oob_buf oob = {0,16,&oobbuf}; + mtd_info_t meminfo; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + exit(1); + } + fd = open(argv[1], O_RDONLY); + + if (fd == -1) { + perror("open flash"); + exit (1); + } + + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit (1); + } + + if (meminfo.oobsize != 16 || meminfo.oobblock!=512) { + printf("Unknown flash (not normal NAND)\n"); + close(fd); + exit(1); + } + + + ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (ofd == -1) { + perror ("open outfile"); + close(fd); + exit(1); + } + + + for (ofs = 0; ofs < meminfo.size ; ofs+=512) { +#if 0 + if ( pread (fd, readbuf, 512, ofs) != 512) { + perror("pread"); + close(fd); + close(ofd); + exit(1); + } + write(ofd, readbuf, 512); +#else + if ( (ofs & 0x1fff) == 0x600) ofs += (8192 -0x600); +#endif + oob.start = ofs; + printf("Dumping %lx\n",ofs); + if (ioctl(fd, MEMREADOOB, &oob) != 0) { + perror("ioctl(MEMREADOOB)"); + close(fd); + close(ofd); + exit(1); + } + write(ofd, oobbuf, 16); + } + close(fd); + close(ofd); +} + diff -uNr mtd-19990817/util/nftl_format.c mtd-19990820/util/nftl_format.c --- mtd-19990817/util/nftl_format.c Thu Jan 1 01:00:00 1970 +++ mtd-19990820/util/nftl_format.c Thu Aug 19 11:40:18 1999 @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +unsigned char BadUnitTable[512]; +unsigned char readbuf[512]; +unsigned char writebuf[4][512]; + +mtd_info_t meminfo; +erase_info_t erase; +unsigned long retlen, ofs; +unsigned long startofs = 0, size = 0; +unsigned long ezones = 0, ezone=0; +int fd; +long MediaUnit1=-1, MediaUnit2=-1; +struct NFTLMediaHeader *NFTLhdr; + +static inline unsigned char checkblock(unsigned long block) +{ + unsigned long ofs = block * meminfo.erasesize; + +#ifdef CHECK_FLASH + unsigned long i; + unsigned long blockofs; + + /* First erase the block */ + + erase.start = ofs; + + if (ioctl(fd,MEMERASE,&erase) != 0) { + return ZONE_BAD_ORIGINAL; + } + + for (blockofs=0; blockofs < meminfo.erasesize; blockofs += 512) { + pread(fd, readbuf, 512, ofs + blockofs); + + if (memcmp(readbuf, writebuf[0], 512)) { + /* Block wasn't 0xff after erase */ + printf("Block not 0xff after erase\n"); + return ZONE_BAD_ORIGINAL; + } + + pwrite(fd, writebuf[1], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + + if (memcmp(readbuf, writebuf[1], 512)) { + printf("Block not zero after clearing\n"); + return ZONE_BAD_ORIGINAL; + } + } + + if (ioctl(fd,MEMERASE,&erase) != 0) { + return ZONE_BAD_ORIGINAL; + } + + for (blockofs=0; blockofs < meminfo.erasesize; blockofs += 512) { + + pwrite(fd, writebuf[2], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + + if (memcmp(readbuf, writebuf[2], 512)) { + printf("Block not 0x5a after writing\n"); + return ZONE_BAD_ORIGINAL; + } + } + + if (ioctl(fd,MEMERASE,&erase) != 0) { + return ZONE_BAD_ORIGINAL; + } + + for (blockofs=0; blockofs < meminfo.erasesize; blockofs += 512) { + + pwrite(fd, writebuf[3], 512, blockofs + ofs); + pread(fd, readbuf, 512, blockofs + ofs); + + if (memcmp(readbuf, writebuf[3], 512)) { + printf("Block not 0xa5 after writing\n"); + return ZONE_BAD_ORIGINAL; + } + } + //printf("block %lx good\n", block); + +#endif + erase.start = ofs; + + if (ioctl(fd,MEMERASE,&erase) != 0) { + return ZONE_BAD_ORIGINAL; + } + return ZONE_GOOD; + +} + + +int main(int argc,char **argv) +{ + unsigned char oobbuf[16]; + struct mtd_oob_buf oob = {0, 16, oobbuf}; + + if (argc < 2) { + fprintf(stderr,"Usage: %s [ []]\n"); + return 1; + } + + if (argc > 2) { + startofs = atoi(argv[2]); + } + if (argc > 3) { + size = atoi(argv[2]); + } + + // Open and size the device + if ((fd = open(argv[1],O_RDWR)) < 0) { + perror("Open flash device"); + return 1; + } + + if (ioctl(fd,MEMGETINFO,&meminfo) != 0) { + perror("ioctl(MEMGETINFO)"); + close(fd); + return 1; + } + + if (meminfo.erasesize != 0x2000) { + printf("Erase size not 8Kb - I'm confused\n"); + close(fd); + return 1; + } + + memset(writebuf[0], 0xff, 512); + memset(writebuf[1], 0x00, 512); + memset(writebuf[2], 0x5a, 512); + memset(writebuf[3], 0xa5, 512); + + if (!size) size = meminfo.size - startofs; + + erase.length = meminfo.erasesize; + + ezones = size / meminfo.erasesize; + + if ( ezones > MAX_ERASE_ZONES ) { + /* Ought to change the UnitSizeFactor. But later. */ + size = meminfo.erasesize * MAX_ERASE_ZONES; + ezones = MAX_ERASE_ZONES; + } + + for (ezone = startofs / meminfo.erasesize; + ezone < ( ezones + startofs / meminfo.erasesize); + ezone++) { + int someflagged; + + if (!(ezone % 512)) { + /* New block of zones */ + memset(BadUnitTable, 0xff, 512); + someflagged = 0; + } + + if ((BadUnitTable[ezone % 512] = checkblock(ezone)) == ZONE_GOOD) { + if (MediaUnit1 == -1) { + MediaUnit1 = ezone; + } else if (MediaUnit2 == -1) { + MediaUnit2 = ezone; + } + } + else { + someflagged = 1; + } + + if (ezone & 0x1ff == 0x1ff) { + /* We have a whole chunk to write. */ + if (MediaUnit1 == -1 || MediaUnit2 == -1) { + printf("Need to write data, but don't yet know MediaUnit - WTF?\n"); + return 1; + } + + if (someflagged) { + /* Only need to write data if there are bad blocks */ + pwrite(fd, BadUnitTable, 512, + (MediaUnit1 * meminfo.erasesize) + 512 + + (ezone % 512)); + + pwrite(fd, BadUnitTable, 512, + (MediaUnit2 * meminfo.erasesize) + 512 + + (ezone % 512)); + } + + } + } + + /* FIXME: We won't actually deal with that info - it's always clean so + far and I'm only doing this so I don't have to keep booting DOS to + fix the NFTL when I break it :) + */ + + NFTLhdr = (struct NFTLMediaHeader *)(writebuf[0]); + + strcpy(NFTLhdr->DataOrgID, "ANAND"); + + size -= (2*meminfo.erasesize); + // startofs += ( 2*meminfo.erasesize); + + NFTLhdr->NumEraseUnits = (size / meminfo.erasesize); + NFTLhdr->FirstPhysicalEUN = MediaUnit2 + 1; + NFTLhdr->FormattedSize = size - 0x4000; + + pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize); + pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize); + + memset(oobbuf, 0xff, 16); + oobbuf[11] = oobbuf[10] = oobbuf[9] = 0; + oobbuf[8] = 3; + oobbuf[12] = oobbuf[14] = 0x69; + oobbuf[13] = oobbuf[15] = 0x3c; + + for (ezone = NFTLhdr->FirstPhysicalEUN ; + ezone < ( ezones + startofs / meminfo.erasesize); ezone++) { + int someflagged; + + oob.start = (ezone * meminfo.erasesize) + 512; + if ( ioctl(fd, MEMWRITEOOB, &oob)) + printf("MEMWRITEOOB at %lx: %s\n",oob.start, sys_errlist[errno]); + + } +} diff -uNr mtd-19990817/util/nftldump.c mtd-19990820/util/nftldump.c --- mtd-19990817/util/nftldump.c Thu Jan 1 01:00:00 1970 +++ mtd-19990820/util/nftldump.c Fri Aug 20 00:59:32 1999 @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + + +char readbuf[512]; + +struct NFTLMediaHeader MedHead[2]; +int NumMedHeads = 0; + +unsigned char BadBlockTable[8192 - 512]; + +#define ERASESIZE 0x2000 +#define NUMVUNITS ((40*1024*1024) / ERASESIZE) + +unsigned short EUNtable[NUMVUNITS]; +union nftl_uci UCItable[NUMVUNITS][3]; + +unsigned short nextEUN(unsigned short curEUN) +{ + return UCItable[curEUN][0].a.ReplUnitNum; +} + +void main(int argc, char **argv) +{ + unsigned long ofs; + int i,j; + int fd = open("/dev/mtd0", O_RDONLY); + int ofd = -1; + unsigned char databuf[512]; + struct nftl_oob oobbuf; + struct mtd_oob_buf oob = {0,16,(unsigned char *)&oobbuf}; + + if (fd == -1) { + perror("open flash"); + exit (1); + } + + if (argc > 1) { + ofd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (ofd == -1) + perror ("open outfile"); + } + + for (ofs = 0; ofs < 4 * 1024 * 1024 ; ofs += ERASESIZE) + { + pread (fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs); + + if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) { + if (!NumMedHeads) { + printf("NFTL Media Header found at offset %lx:\n", ofs); + printf("NumEraseUnits: %d\n",MedHead[NumMedHeads].NumEraseUnits); + printf("FirstPhysicalEUN: %d\n",MedHead[NumMedHeads].FirstPhysicalEUN); + printf("Formatted Size: %d\n",MedHead[NumMedHeads].FormattedSize); + printf("UnitSizeFactor: 0x%x\n",MedHead[NumMedHeads].UnitSizeFactor); + pread(fd, BadBlockTable, 8192-512, ofs + 512); + } + else + printf("Second NFTL Media Header found at offset %lx\n",ofs); + + NumMedHeads ++; + } + } + if (NumMedHeads == 0) { + printf("No NFTL found on device\n"); + exit(1); + } + memset(EUNtable, 0, sizeof(EUNtable)); + + for (i=MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN + + MedHead[0].NumEraseUnits ; i++) { + /* For each EUN */ + + ofs = i * ERASESIZE; + + for (j=0; j<3; j++) { + oob.start = ofs + (j*512); + ioctl(fd, MEMREADOOB, &oob); + memcpy(&UCItable[i][j], &oobbuf.u, 8); + } + + if (UCItable[i][1].b.EraseMark != 0x3c69) + printf("EraseMark not present in unit %d: %x\n",i, UCItable[i][1].b.EraseMark); + else + // printf("EUN #%d is Virtual Unit %d, replaced by %d\n", i, + // UCItable[i][0].a.VirtUnitNum & 0x7fff, + // UCItable[i][0].a.ReplUnitNum != 0xffff?UCItable[i][0].a.ReplUnitNum:0); + + if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) { + /* If this is the first in a chain, store the EUN */ + // printf("Setting EUNtable[%d] to %d\n", + // UCItable[i][0].a.VirtUnitNum & 0x7fff, i); + if (EUNtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) { + printf("Duplicate start of chain for VUC %d: Unit %d replaces Unit %d\n", + UCItable[i][0].a.VirtUnitNum & 0x7fff, i, + EUNtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] ); + } + EUNtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i; + } + if ( UCItable[i][0].a.VirtUnitNum == 0xffff) + printf("Unit %d is free\n", i); + else + printf("Unit %d is in chain %d and %s a replacement\n", i, UCItable[i][0].a.VirtUnitNum & 0x7fff, + UCItable[i][0].a.VirtUnitNum & 0x8000?"is":"is not"); + } + + for (i=0; i< /* NUMVUNITS */ (MedHead[0].FormattedSize / 0x2000) + 1; i++) { + unsigned short curEUN = EUNtable[i]; + + printf("Virtual Unit #%d: ", i); + if (!curEUN) { + printf("Not present\n"); + continue; + } + printf("%d", curEUN); + + while ((curEUN = nextEUN(curEUN)) != 0xffff) { + printf(", %d", curEUN & 0x7fff); + } + printf("\n"); + + if (ofd != -1) { + /* Actually write out the data */ + for (j=0; j < ERASESIZE / 512; j++) { + /* For each sector in the block */ + unsigned short lastgoodEUN = 0xffff, thisEUN = EUNtable[i]; + + if (thisEUN == 0xffff) thisEUN = 0; + + while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) { + oob.start = (thisEUN * ERASESIZE) + (j * 512); + ioctl(fd, MEMREADOOB, &oob); + + + switch (oobbuf.b.Status) { + case 0xffff: /* This is still free. Don't look any more */ + thisEUN = 0; + break; + + case 0x5555: /* BLOCK_USED. This is a good one. */ + lastgoodEUN = thisEUN; + break; + } + + /* Find the next erase unit in this chain, if any */ + if (thisEUN) + thisEUN = nextEUN(thisEUN) & 0x7fff; + } + if (lastgoodEUN == 0xffff) + memset(readbuf,0,512); + else + pread(fd, readbuf, 512, (lastgoodEUN * ERASESIZE) + (j * 512)); + + write(ofd, readbuf, 512); + } + + } + } +}