/*************************************************************
Logging File System for Arduino
MJB 12/31/19
This version lacks the low-power elements of the MSP430 version
And relies on the Arduino SDCard library from SDFat 2.0 Beta
***************************************************************
Basic Principles:
while a file is open the static direntry is valid
and opendirblock is the SD block address of the directory entry
***********************************************************************/
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define FILEOK 0
#define FILEERROR -1
#define OPENREAD 1
#define OPENWRITE 2
#define SECTORSIZE 512
/*
extern uint32_t CardCapacity(void);
extern void DirLineMessage(uint16_t fnum, uint32_t ctime, uint32_t flen);
extern int16_t SD_Init(void);
extern void SendBytes(unsigned char *cp, uint16_t numbytes);
extern int16_t SD_write( uint32_t secnum, uint8_t *buffptr);
extern int16_t SD_read( uint32_t secnum, uint8_t *buffptr);
extern uint32_t UnixTime(void);
// No header files needed for Arduins
*/
unsigned int hardwrerror, softwrerror;
// the main program must provide the means to output
// error and status messages with or without 1 integer value
//extern void Message(const char *ch);
//extern void Message1(const char *ch, uint32_t num);
// this structure is contained in the first block of every
// file
struct direntry {
uint32_t blocktype;
uint32_t firstblock;
uint32_t lastblock;
uint32_t createtime;
uint32_t closetime; // 20 bytes
uint32_t length;
uint32_t reserved [10]; //64 bytes to here
unsigned char comment[448]; // We use full block size
// because we always read full sector into direntry
};
// Default to one directory sector for 128 files
#define DIRSECTORS 1
#define NUMDIRENTRIES 128 * DIRSECTORS
static uint32_t lastblock;
static uint32_t nextblock;
static uint32_t openblock;
static uint16_t writept;
static uint16_t readpt;
static uint16_t openfile;
static uint32_t opendirblock;
static uint32_t openlength;
static uint32_t openpos;
static uint16_t fstatus;
// The directory entry requires 512 bytes RAM
static struct direntry de;
struct direntry *dirptr;
// use just one sector buffer of 512 bytes
uint8_t filebuffer[512];
// read the directory entry into the static variable
int16_t GetDirBlock(uint32_t bnum) {
int16_t result;
result = SD_read(bnum, (unsigned char *)&de);
return result;
}
// traverse the directory and set nextblock as one past
// the last used block in the last file
uint32_t FindNextBlock(uint32_t *p) {
uint32_t *lp, nb, hdrptr;
int16_t i, result;
lp = p; hdrptr = 1;
for (i = 0; i < 128; i++) {
if (lp[i] != 0) {
hdrptr = lp[i];
} else break;
}
// read the directory block
result = GetDirBlock(hdrptr);
if (result != 0) return 1;
nb = de.lastblock + 1;
return nb;
}
//------------------------------------------------------------------------------
// flash erase all data on SD Card
uint32_t const ERASE_SIZE = 262144L;
void EraseCard() {
uint32_t cardSectorCount = 0;
uint32_t firstBlock = 0;
uint32_t lastBlock;
//------------------------------------------------------------------------------
// SdCardFactory constructs and initializes the appropriate card.
SdCardFactory cardFactory;
Message("Block Erasing Card\n");
// Pointer to generic SD card.
SdCard* m_card = nullptr;
m_card = cardFactory.newCard(SD_CONFIG);
if (!m_card || m_card->errorCode()) {
Message("card init failed.");
return;
}
cardSectorCount = m_card->sectorCount();
if (!cardSectorCount) {
Message("Get sector count failed.");
return;
}
do {
lastBlock = firstBlock + ERASE_SIZE - 1;
if (lastBlock >= cardSectorCount) {
lastBlock = cardSectorCount - 1;
}
if (!m_card->erase(firstBlock, lastBlock)) {
Message("erase failed");
}
firstBlock += ERASE_SIZE;
} while (firstBlock < cardSectorCount);
if (!m_card->readSector(0, filebuffer)) {
Message("SD Card ReadBlock error");
}
Message("Block Erase Complete\n");
}
//------------------------------------------------------------------------------
/******************************************************
This function zeros out the pointer block---effectively
removing access to the files on the SD Card.
Access to this function should be guarded by confirmation
in the main program
*******************************************************/
int16_t FormatCard(void) {
int16_t i, *ip, result;
result = FILEERROR;
Message("\nInitializing pointer block.\n");
// The directory pointers are at block zero. Set them all to zero
ip = (int16_t *)&filebuffer;
for (i = 0; i < 256; i++) *ip++ = 0;
result = SD_write(0, filebuffer);
if (result != FILEOK) {
Message("Could not initialize file system\n");
}
return result;
}
/**********************************************************
* Initialize the LFS by checking pointer block and
* determining SDC size.
* If the pointer block hasn't been initialized,
* do so at this time.
******************************************************/
int16_t InitLFS(void) {
int16_t result;
uint32_t *lp, faddr, cardcap;
/*************************************************
// SD Card initialized by sdc.begin()
*****************************************************/
cardcap = CardCapacity();
Message1("Card Capacity in MB ", cardcap / 2048);
Message(" MBytes\n");
lastblock = cardcap - 10; // reserve some blocks at end of card
// now read the directory pointer block
result = SD_read(0, filebuffer);
lp = (uint32_t *) filebuffer;
faddr = *lp;
// only two possibilities are valid for initialized file system:
// A. faddr = 0; no files have been created since format
// B. faddr = DIRSECTORS; the first file has been created
if ((faddr != 0) & (faddr != DIRSECTORS)) {
Message("Directory has not been initialized.\n");
result = FormatCard();
nextblock = 1;
return result;
}
if (faddr == 0) nextblock = 1; else nextblock = FindNextBlock((uint32_t*)&filebuffer);
Message1("Next file block will be ", nextblock);
return FILEOK;
}
// returns file number for new file
int16_t NewFile(void) {
int16_t result, fnum;
uint32_t *fp, bnum;
// read the pointer block
result = SD_read(0, filebuffer);
if (result != FILEOK) {
Message("Error reading pointer block in NewFile\n");
return FILEERROR;
}
fnum = -1;
fp = (uint32_t *)&filebuffer;
for (fnum = 0; fnum < 128; fnum++) {
if (*fp == 0) { // found an empty file pointer
result = fnum;
break;
}
fp++;
} // end of for(fnum...
if (fnum == NUMDIRENTRIES) { // directory is full
Message("No more pointer entries!\n");
return FILEERROR;
}
// Message1("Next file number: ", (int32_t)fnum);
// file number is valid, mark the ponter block with directory address
bnum = nextblock;
*fp = bnum;
// now write back the modified pointer block
result = SD_write(0, filebuffer);
if (result != 0) { // if write fails do one retry
result = SD_write(0, filebuffer);
if (result != FILEOK){
Message("Error writing File Pointer block 0\n");
return result;
}
}
openfile = fnum;
nextblock++;
de.blocktype = 0xFDFDFDFD;
de.firstblock = bnum;
de.lastblock = bnum;
de.createtime = UnixTime();
de.closetime = UnixTime();
// now write the directory block at start of file
result = SD_write(bnum, (unsigned char *)&de);
if (result != FILEOK){
Message1("Error writing Directory block \n", (int32_t)bnum);
return FILEERROR;
}
opendirblock = bnum;
fstatus = OPENWRITE;
openblock = bnum + 1;
openlength = 0;
writept = 0;
// Message1("Created file. First block at \n", (int32_t)opendirblock);
return fnum;
}
int16_t FileStatus(void) {
return fstatus;
}
// open a file for reading
int16_t OpenFile(uint16_t filenumber) {
uint32_t *lp, hdrptr;
int16_t result;
// now read the directory pointer block
result = SD_read(0, filebuffer);
lp = (uint32_t *)filebuffer;
if (lp[filenumber] != 0) {
hdrptr = lp[filenumber];
result = GetDirBlock(hdrptr);
if (result != 0) return FILEERROR;
openlength = de.length;
openblock = de.firstblock + 1; // data starts after header block
// Message1("Opened File ", (int32_t)filenumber);
opendirblock = hdrptr;
}
fstatus = OPENREAD;
openpos = 0;
readpt = 0;
openfile = filenumber;
return result;
}
/**************************************************************/
int16_t CloseFile(void) {
int16_t result, i;
// struct direntry de;
// write any buffered bytes
if (writept != 0) {
for (i = writept; i < SECTORSIZE; i++) filebuffer[i] = 0;
SD_write(openblock, filebuffer);
nextblock++;
}
de.lastblock = openblock;
de.closetime = UnixTime();
de.length = openlength;
result = SD_write(opendirblock, (unsigned char *)&de);
// Message1("Updating file directory for file ", (int32_t)openfile);
if (result != 0) {
result = SD_write(opendirblock, (unsigned char *)&de);
if (result != 0) Message1("Error writing file header block ", opendirblock);
}
fstatus = FILEOK;
opendirblock = 0;
return 0;
}
int16_t UpdateDirectory(void) {
int16_t result;
// SD_WriteStart();
de.lastblock = openblock;
de.closetime = UnixTime();
de.length = openlength;
result = SD_write(opendirblock, (unsigned char *)&de);
if (result != 0) {
result = SD_write(opendirblock, (unsigned char *)&de);
if (result != 0) Message1("Error writing directory block \n", (int32_t)opendirblock);
}
fstatus = FILEOK;
return 0;
}
/******************************************************
* Write3 bytes to the file. Write the buffer to SDC
* as required.
*/
int16_t WriteFile( unsigned char *datap, uint16_t Num2Write) {
int16_t spaceleft;
unsigned char *dptr, *wrptr;
if (Num2Write > 512) return FILEERROR;
dptr = datap;
// first, copy the data to proper position in filebuffer
spaceleft = SECTORSIZE - writept;
if (Num2Write >= spaceleft) { // fill rest of buffer and write sector
wrptr = filebuffer + writept;
memcpy(wrptr, dptr, spaceleft);
writept = 0; Num2Write -= spaceleft;
dptr += spaceleft;
SD_write(openblock, filebuffer);
nextblock++;
openblock = nextblock;
openlength += spaceleft;
}
wrptr = filebuffer + writept;
memcpy(wrptr, dptr, Num2Write);
writept += Num2Write;
openlength += Num2Write;
if (nextblock > lastblock) return FILEERROR;
return FILEOK;
}
/****************************************************************
* Read bytes from a file. File is assumed to be open.
* Read new sector as required
*/
int16_t ReadFile(unsigned char *datap, uint16_t Num2Read, uint16_t *NumRead) {
int16_t spaceleft;
unsigned char *dptr, *rdptr;
if (Num2Read > 512) return FILEERROR;
dptr = datap;
*NumRead = 0;
if (openpos + Num2Read > openlength) Num2Read = openlength - openpos;
*NumRead = Num2Read;
// first, copy the data from proper position in filebuffer
spaceleft = SECTORSIZE - readpt;
if (Num2Read >= spaceleft) { // read what we have and get next sector
rdptr = filebuffer + readpt;
memcpy(dptr, rdptr, spaceleft);
readpt = 0; Num2Read -= spaceleft;
dptr += spaceleft;
openblock++;
SD_read(openblock, filebuffer);
openpos += spaceleft;
}
rdptr = filebuffer + readpt;
memcpy(dptr, rdptr, Num2Read);
readpt += Num2Read;
openpos += Num2Read;
// SD_TransferStop();
return FILEOK;
}
/**************************************************************
*
* open a file for reading and send to host
* This function not tested yet
*/
int16_t BinSendFile(uint16_t filenumber) {
//struct direntry de;
uint32_t *lp, hdrptr;
uint16_t bpos, bytesleft;
uint32_t flength, fpos;
uint32_t fblock = 0;
unsigned char *cp;
int16_t result;
// now read the directory pointer block
result = SD_read(0, filebuffer);
lp = (uint32_t *)filebuffer;
if (lp[filenumber] != 0) { // test for valid pointer
hdrptr = lp[filenumber];
result = GetDirBlock(hdrptr);
fblock = hdrptr + 1;
if (result != 0) return FILEERROR;
}
flength = de.length;
fpos = 0;
while ((flength - fpos) > 512) {
result = SD_read(fblock, filebuffer);
fblock++;
bpos = 0;
fstatus = FILEOK;
if (result != 0) {
Message1("Error reading file block ", (int32_t)fblock);
fstatus = FILEERROR;
return fstatus;
}
cp = &filebuffer[0];
for (bpos = 0; bpos < 512; bpos++) {
SendBytes(cp, 512);
}
fpos += 512;
}
// now read the last part
bytesleft = flength - fpos;
if (bytesleft > 0) {
result = SD_read(fblock, filebuffer);
cp = &filebuffer[0];
bpos = 0;
SendBytes(cp, bytesleft);
}
fpos = 0;
readpt = 0;
return fstatus;
}
/******************************************************
* Read the pointer block, into the file buffer
* Then step through the pointers and show directory
* information for existing file. The directory blocks
* are read into the static dirEntry block.
*/
void ShowDirectory(void) {
uint32_t *lp, hdrptr;
int16_t i, result;
// now read the directory pointer block
result = SD_read(0, filebuffer);
lp = (uint32_t*)filebuffer;
Message("\nFile Last Written Size\n");
for (i = 0; i < NUMDIRENTRIES; i++) {
if (lp[i] != 0) {
hdrptr = lp[i];
result = GetDirBlock(hdrptr);
if (result != 0) return;
DirLineMessage(i, de.closetime, de.length);
} else break;
}
}