byte EEPROM_24XX1025::readByte(void) { // Reads a byte from the current position and returns it if (eeprom_pos != curpos) { // If the EEPROM internal position counter has (or might have) changed, // do a "full" read, where we sent the 16-byte address. I2c16.read((uint8_t)(devaddr | ((BLOCKNUM(curpos)) << 2)), TO_PAGEADDR(curpos), 1U); eeprom_pos = curpos; } else { // If we know that the internal counter is correct, don't send the address, but // rely on the EEPROM logic to return the "next" byte properly. This saves // overhead and time. I2c16.read((uint8_t)(devaddr | ((BLOCKNUM(curpos)) << 2)), 1U); } curpos++; eeprom_pos++; if (eeprom_pos == 65536) { // Seems to wrap here. The datasheet could be read as if this were 17-bit, but I don't think it is. eeprom_pos = 0; } if (curpos >= DEVICE_SIZE) { // Wrap around if we overflow the device capacity. curpos %= DEVICE_SIZE; eeprom_pos = 0xffffffff; } return I2c16.receive(); // Returns 0 if no bytes are queued }
// Private method uint8_t EEPROM_24XX1025::writeChunk(uint32_t fulladdr, const void *data, uint8_t bytesToWrite) { // Used to turn 1-128 byte writes into full page writes (i.e. turn them into proper single-page writes) if (bytesToWrite == 0 || bytesToWrite > 128 || fulladdr >= DEVICE_SIZE) return 0; if (fulladdr + bytesToWrite > DEVICE_SIZE) bytesToWrite = DEVICE_SIZE - fulladdr; // Calculate the 16-bit address, and the page number of the first and second (if applicable) // blocks we're going to write to. uint32_t pageaddr = TO_PAGEADDR(fulladdr); uint8_t firstBlock = BLOCKNUM(fulladdr); uint8_t secondBlock = BLOCKNUM(fulladdr + bytesToWrite - 1); // These page numbers are *relative to the block number*, i.e. firstPage = 0 may mean at byte 0 or byte 65536 // depending on firstBlock above. Same goes for secondPage/secondBlock of course. uint16_t firstPage = pageaddr / 128; // pageaddr is already relative to block! uint16_t secondPage = (TO_PAGEADDR(pageaddr + bytesToWrite - 1))/128; if (firstPage == secondPage && firstBlock == secondBlock) { // Data doesn't "cross the border" between pages. Easy! return writeSinglePage(fulladdr, data, bytesToWrite); } else { // The data spans two pages, e.g. begins at address 120 and is 12 bytes long, which would make it go // past the edge of this page (addresses 0 - 127) and onto the next. // We need to split this write manually. uint8_t bytesInFirstPage = ((firstPage + 1) * 128) - pageaddr; uint8_t bytesInSecondPage = bytesToWrite - bytesInFirstPage; uint8_t ret = 0; // Write the data that belongs to the first page if ((ret = writeSinglePage(TO_FULLADDR(firstBlock, pageaddr), data, bytesInFirstPage)) != bytesInFirstPage) { return ret; } // Write the data that belongs to the second page if ((ret = writeSinglePage(TO_FULLADDR(secondBlock, secondPage * 128), (const void*)((byte *)data + bytesInFirstPage), bytesInSecondPage)) != bytesInSecondPage) { return bytesInFirstPage + ret; } } return bytesToWrite; }
/* * write operation on the wing */ int dtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag) { struct puffs_node *pn = opc; struct dtfs_file *df = DTFS_CTOF(opc); uint8_t *src, *dest; size_t copylen; if (pn->pn_va.va_type != VREG) return EISDIR; if (ioflag & PUFFS_IO_APPEND) offset = pn->pn_va.va_size; if (*resid + offset > pn->pn_va.va_size) dtfs_setsize(pn, *resid + offset); src = buf; while (*resid > 0) { int i; copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE)); i = BLOCKNUM(offset, DTFS_BLOCKSHIFT); dest = df->df_blocks[i] + BLOCKOFF(offset, DTFS_BLOCKSIZE); memcpy(dest, src, copylen); offset += copylen; dest += copylen; *resid -= copylen; } dtfs_updatetimes(pn, 0, 1, 1); return 0; }
/* * Read operation, used both for VOP_READ and VOP_GETPAGES */ int dtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag) { struct puffs_node *pn = opc; struct dtfs_file *df = DTFS_CTOF(opc); quad_t xfer, origxfer; uint8_t *src, *dest; size_t copylen; if (pn->pn_va.va_type != VREG) return EISDIR; xfer = MIN(*resid, df->df_datalen - offset); if (xfer < 0) return EINVAL; dest = buf; origxfer = xfer; while (xfer > 0) { copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE)); src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)] + BLOCKOFF(offset, DTFS_BLOCKSIZE); memcpy(dest, src, copylen); offset += copylen; dest += copylen; xfer -= copylen; } *resid -= origxfer; dtfs_updatetimes(pn, 1, 0, 0); return 0; }
//dealloc inode int deallocInode(int inum) { int i,j; inode_t inode; dir_t dirBlock; //Get to start of inode lseek(fdImage,BYOFF_INODE(inum),SEEK_SET); //Read the inode read(fdImage, &inode, sizeof(inode_t)); if(inode.type==MFS_DIRECTORY)//if directory { //should be empty except for the two default entries . .. assert(inode.blockPtrs[0]!=-1);//has to have at least one block for(i=0; i<14; i++) { if(inode.blockPtrs[i]!=-1)//valid blockPtr { //Go to that block lseek(fdImage, inode.blockPtrs[i], SEEK_SET); read(fdImage, &dirBlock, sizeof(dir_t)); //Check all entries in the directory block for(j=0; j<NUM_DIR_ENTS; j++) { if( (dirBlock.dirEnt[j].inum != -1) && !(i==0 && j<2) )//valid directory entry which is not one of the first two return -1;//could not deallocate Inode } } } //empty directory } //Either empty directory or simple file inode.type=MFS_UNUSED;//mark it unused //update as unused inode lseek(fdImage,BYOFF_INODE(inum),SEEK_SET); write(fdImage, &inode, sizeof(inode_t)); //dealloc all its data blocks for(i=0; i<14; i++) { if(inode.blockPtrs[i]!=-1)//valid ptr deallocDataBlock(BLOCKNUM(inode.blockPtrs[i])); } //update superblock inode count super.ninodes--; lseek(fdImage, BYOFF_SUPER, SEEK_SET); write(fdImage, &super, sizeof(superblock_t)); return 0; }
// Private method uint8_t EEPROM_24XX1025::readChunk(uint32_t fulladdr, const void *data, uint8_t bytesToRead) { if (bytesToRead == 0 || fulladdr >= DEVICE_SIZE) return 0; if (fulladdr + bytesToRead > DEVICE_SIZE) bytesToRead = DEVICE_SIZE - fulladdr; uint8_t err = 0; if (fulladdr < 65536 && fulladdr + bytesToRead > 65536) { // This read crosses the "block boundary" and cannot be sequentially read // by the EEPROM itself // Read part 1 (from the first block) err = I2c16.read(devaddr, fulladdr /* always 16-bit */, 65536 - fulladdr, (byte *)data); if (err) { eeprom_pos = 0xffffffff; return 0; } // Read part 2 (from the second block) err = I2c16.read(devaddr | (1 << 2), 0, bytesToRead - (65536 - fulladdr), (byte *)data + (uint16_t)((65536 - fulladdr))); if (err) { eeprom_pos = 0xffffffff; curpos += (65536 - fulladdr); // move the cursor forward the amount we read successfully if (curpos >= DEVICE_SIZE) curpos %= DEVICE_SIZE; return (uint8_t)(65536 - fulladdr); // num bytes read previously } else { eeprom_pos = TO_FULLADDR(1, bytesToRead - (65536 - fulladdr)); curpos += bytesToRead; if (curpos >= DEVICE_SIZE) curpos %= DEVICE_SIZE; return bytesToRead; } } else { // Doesn't cross the block border, so we can do this in one read uint8_t block = BLOCKNUM(fulladdr); err = I2c16.read(devaddr | (block << 2), TO_PAGEADDR(fulladdr), bytesToRead, (byte *)data); if (err) { eeprom_pos = 0xffffffff; return 0; } else { eeprom_pos += bytesToRead; curpos += bytesToRead; if (curpos >= DEVICE_SIZE) curpos %= DEVICE_SIZE; return bytesToRead; } } }
// Private method uint8_t EEPROM_24XX1025::writeSinglePage(uint32_t fulladdr, const void *data, uint8_t bytesToWrite) { // Writes 1 - 128 bytes, but only *within a single page*. Never crosses a page/block border. // Enforcing this is up to the caller. if (bytesToWrite == 0 | bytesToWrite > 128) return 0; uint8_t ret = I2c16.write(devaddr | ((BLOCKNUM(fulladdr)) << 2), TO_PAGEADDR(fulladdr), (byte *)data, bytesToWrite); if (ret != 0) { // We can't be sure what the internal counter is now, since it looks like the write failed. eeprom_pos = 0xffffffff; return 0; } else { // Try to keep track of the internal counter eeprom_pos += bytesToWrite; curpos += bytesToWrite; if (curpos >= DEVICE_SIZE) curpos %= DEVICE_SIZE; } // Wait for the EEPROM to finish this write. To do so, we use acknowledge polling, // a technique described in the datasheet. We sent a START condition and the device address // byte, and see if the device acknowledges (pulls SDA low) or not. Loop until it does. uint32_t start = micros(); while (I2c16.acknowledgePoll(devaddr | ((BLOCKNUM(fulladdr)) << 2)) == 0) { delayMicroseconds(20); } uint32_t end = micros(); if (end - start < 500) { // This write took less than 500 us (typical is 3-4 ms). This most likely means // that the device is write protected, as it will acknowledge new commands at once // when write protect is active. Serial.println("WARNING: EEPROM appears to be write protected!"); return 0; } return bytesToWrite; }
boolean EEPROM_24XX1025::writeByte(byte data) { // Writes a byte to the EEPROM. // WARNING: writing a single byte still uses a full page write, // so writing 128 sequential bytes instead of 1 page write // will use 128 times as many of the chip's limited lifetime writes!! // In otherwords: ONLY USE THIS if you *really* only need to write ONE byte. // Even for just *TWO* bytes, write([pos,] data, bytesToWrite) is "twice as good"! // In short: writeBlock for 128 bytes will use 1 page "life" each on 1 or 2 pages. // writeByte 128 times will use 128 page "lives", spread over 1 or 2 pages. // Find which block the byte is in, based on the full (17-bit) address. // We can only supply 16 bits to the EEPROM, plus a separate "block select" bit. uint8_t block = BLOCKNUM(curpos); uint8_t ret = I2c16.write((uint8_t)(devaddr | (block << 2)), TO_PAGEADDR(curpos), data); if (ret != 0) { // Looks like something failed. Reset the EEPROM counter "copy", since we're no longer // sure what it ACTUALLY is. eeprom_pos = 0xffffffff; return false; } curpos++; eeprom_pos = curpos; // We changed the internal counter when we wrote the address just above. if (curpos >= DEVICE_SIZE) { // Wrap around if we overflow the device capacity. curpos %= DEVICE_SIZE; eeprom_pos = 0xffffffff; // Not sure what the internal counter does. It PROBABLY resets to 0, but... } // Wait for the EEPROM to finish this write. To do so, we use acknowledge polling, // a technique described in the datasheet. We sent a START condition and the device address // byte, and see if the device acknowledges (pulls SDA low) or not. Loop until it does. uint32_t start = micros(); while (I2c16.acknowledgePoll(devaddr | (block << 2)) == 0) { delayMicroseconds(20); } uint32_t end = micros(); if (end - start < 500) { // This write took less than 500 us (typical is 3-4 ms). This most likely means // that the device is write protected, as it will acknowledge new commands at once // when write protect is active. Serial.println("WARNING: EEPROM appears to be write protected!"); return 0; } return true; // success }
static void gcmarkregion(void *start, void *end) { #define STACK 128 void *startstk[STACK]; void *endstk[STACK]; char *low = (char *)g.heaplow; char *high = (char *)g.heaphigh - sizeof (char *); int tos = 1; startstk[0] = start; endstk[0] = end; while (tos-- > 0) { #ifdef PTRMASK char *st = (char *)(((uLong)startstk[tos]) & PTRMASK); char *e = (char *)(((uLong)endstk[tos] - sizeof (char *)) & PTRMASK); #else char *st = (char *)startstk[tos]; char *e = (char *)endstk[tos] - sizeof (char *); #endif char *s; for (s = st; s <= e; s += PTRBUMP) { char *p = *(char **)s; int num; struct pageinfo *pi; void *pp; size_t bb; boolean big = FALSE; if (p < low || p > high) continue; pi = GETPAGEINFO(p); num = PAGENUM(pi); if (num >= MAXPAGES)/* bogus block? */ continue; while (!GETBIT(g.ourpages, num) && num >= 0) { num--; pi = (struct pageinfo *)((char *)pi - BYTESPERPAGE); big = TRUE; } if (num < 0) /* bogus block? */ continue; if (pi->blocksize == 0) /* hit in free block */ { /* pi->history |= 1; */ /* flag the hit */ continue; } if (pi->blocksize == -1) /* big block */ { pp = (char *)pi + sizeof (struct pageinfo); if (g.skipinterior && pp != p) continue; if (pi->refbits[0]) /* already marked and walked */ continue; pi->refbits[0] = 1; bb = pi->count * BYTESPERPAGE - sizeof (struct pageinfo); FPRINTF((stdout, "gcmarkregion: st=%p e=%p s=%p" " p=%p pp=%p bb=%d\n", st, e, s, p, pp, bb)); } else if (big) /* bogus block */ continue; else /* regular block */ { num = BLOCKNUM(pi, p); /* if the last byte of this block is outside a page, it is bogus */ if (num >= pi->numblocks) { /* pi->history |= 1; */ /* flag the hit */ continue; } bb = pi->blocksize; pp = (char *)pi + num * bb + sizeof (struct pageinfo); if (g.skipinterior && pp != p) continue; if (GETBIT(pi->refbits, num)) /* already marked and walked */ continue; SETBIT(pi->refbits, num); } if ((char *)pp < low || (char *)pp > high) crash("gcmarkregion(): block pointer pp not in heap"); if ((char *)pp + bb < low || (char *)pp + bb > (char *)g.heaphigh) crash("gcmarkregion(): end of block pointer pp not in heap"); if (tos >= STACK) gcmarkregion(pp, pp + bb); else { startstk[tos] = pp; endstk[tos] = pp + bb; tos++; } } } }