ESP8266 part of the f105-motor-demo project (see f105-motor-demo_stm32)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
8.2 KiB

8 years ago
/*
This is a simple read-only implementation of a file system. It uses a block of data coming from the
mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there.
It's written for use with httpd, but doesn't need to be used as such.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
//These routines can also be tested by comping them in with the espfstest tool. This
//simplifies debugging, but needs some slightly different headers. The #ifdef takes
//care of that.
#ifdef __ets__
//esp build
#include <esp8266.h>
#else
//Test build
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define ICACHE_FLASH_ATTR
#endif
#include <esp8266.h>
#include "espfsformat.h"
#include "espfs.h"
#ifdef ESPFS_HEATSHRINK
#include "heatshrink_config_custom.h"
#include "heatshrink_decoder.h"
#endif
static const char* espFsData = NULL;
8 years ago
struct EspFsFile {
const EspFsHeader *header;
8 years ago
char decompressor;
int32_t posDecomp;
const char *posStart;
const char *posComp;
8 years ago
void *decompData;
};
/*
Available locations, at least in my flash, with boundaries partially guessed. This
is using 0.9.1/0.9.2 SDK on a not-too-new module.
0x00000 (0x10000): Code/data (RAM data?)
0x10000 (0x02000): Gets erased by something?
0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
0x40000 (0x20000): Code/data (ROM data?)
0x60000 (0x1C000): Free
0x7c000 (0x04000): Param store
0x80000 - end of flash
Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
*must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
a memory exception, crashing the program.
*/
EspFsInitResult ICACHE_FLASH_ATTR espFsInit(const void *flashAddress)
{
if ((uint32_t)flashAddress > 0x40200000) {
flashAddress = (void*)((uint32_t)flashAddress - 0x40200000);
8 years ago
}
// base address must be aligned to 4 bytes
if (((int)flashAddress & 3) != 0) {
return ESPFS_INIT_RESULT_BAD_ALIGN;
}
// check if there is valid header at address
EspFsHeader testHeader;
spi_flash_read((uint32)flashAddress, (uint32*)&testHeader, sizeof(EspFsHeader));
if (testHeader.magic != ESPFS_MAGIC) {
return ESPFS_INIT_RESULT_NO_IMAGE;
}
espFsData = (const char *)flashAddress;
8 years ago
return ESPFS_INIT_RESULT_OK;
}
//Copies len bytes over from dst to src, but does it using *only*
//aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works.
//ToDo: perhaps memcpy also does unaligned accesses?
#ifdef __ets__
void ICACHE_FLASH_ATTR readFlashUnaligned(char *dst, char *src, int len)
{
8 years ago
uint8_t src_offset = ((uint32_t)src) & 3;
uint32_t src_address = ((uint32_t)src) - src_offset;
uint32_t tmp_buf[len / 4 + 2];
spi_flash_read((uint32)src_address, (uint32*)tmp_buf, len + src_offset);
memcpy(dst, ((uint8_t*)tmp_buf) + src_offset, len);
8 years ago
}
#else
#define readFlashUnaligned memcpy
#endif
// Returns flags of opened file.
int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh)
{
8 years ago
if (fh == NULL) {
httpd_printf("File handle not ready\n");
return -1;
}
int8_t flags;
readFlashUnaligned((char*)&flags, (char*)&fh->header->flags, 1);
return (int)flags;
}
//Open a file and return a pointer to the file desc struct.
EspFsFile ICACHE_FLASH_ATTR *espFsOpen(const char *fileName)
{
printf("Open file %s\n", fileName);
8 years ago
if (espFsData == NULL) {
httpd_printf("Call espFsInit first!\n");
return NULL;
}
const char *p = espFsData;
const char *hpos;
8 years ago
char namebuf[256];
EspFsHeader h;
EspFsFile *r;
//Strip initial slashes
while (fileName[0] == '/') fileName++;
8 years ago
//Go find that file!
while (1) {
hpos = p;
8 years ago
//Grab the next file header.
spi_flash_read((uint32)p, (uint32*)&h, sizeof(EspFsHeader));
if (h.magic != ESPFS_MAGIC) {
8 years ago
httpd_printf("Magic mismatch. EspFS image broken.\n");
return NULL;
}
if (h.flags & FLAG_LASTFILE) {
httpd_printf("File %s not found in EspFS.\n", fileName);
8 years ago
return NULL;
}
//Grab the name of the file.
p += sizeof(EspFsHeader);
8 years ago
spi_flash_read((uint32)p, (uint32*)&namebuf, sizeof(namebuf));
// httpd_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n",
// namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags);
if (strcmp(namebuf, fileName) == 0) {
8 years ago
//Yay, this is the file we need!
p += h.nameLen; //Skip to content.
r = (EspFsFile *)malloc(sizeof(EspFsFile)); //Alloc file desc mem
// httpd_printf("Alloc %p\n", r);
if (r == NULL) return NULL;
r->header = (const EspFsHeader *)hpos;
r->decompressor = h.compression;
r->posComp = p;
r->posStart = p;
r->posDecomp = 0;
if (h.compression == COMPRESS_NONE) {
r->decompData = NULL;
8 years ago
#ifdef ESPFS_HEATSHRINK
} else if (h.compression == COMPRESS_HEATSHRINK) {
8 years ago
//File is compressed with Heatshrink.
char parm;
heatshrink_decoder *dec;
//Decoder params are stored in 1st byte.
readFlashUnaligned(&parm, (char*)r->posComp, 1);
8 years ago
r->posComp++;
httpd_printf("Heatshrink compressed file; decode parms = %x\n", parm);
dec = heatshrink_decoder_alloc(16, (parm >> 4) & 0xf, parm & 0xf);
r->decompData = dec;
8 years ago
#endif
} else {
httpd_printf("Invalid compression: %d\n", h.compression);
return NULL;
}
return r;
}
//We don't need this file. Skip name and file
p += h.nameLen + h.fileLenComp;
if ((int)p & 3) p += 4 - ((int)p & 3); //align to next 32bit val
8 years ago
}
}
//Read len bytes from the given file into buff. Returns the actual amount of bytes read.
int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len)
{
8 years ago
int flen, fdlen;
if (fh == NULL) return 0;
8 years ago
readFlashUnaligned((char*)&flen, (char*)&fh->header->fileLenComp, 4);
//Cache file length.
//Do stuff depending on the way the file is compressed.
if (fh->decompressor == COMPRESS_NONE) {
8 years ago
int toRead;
toRead = flen - (fh->posComp - fh->posStart);
if (len > toRead) len = toRead;
// httpd_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp);
readFlashUnaligned(buff, (char*)fh->posComp, len);
fh->posDecomp += len;
fh->posComp += len;
// httpd_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
8 years ago
return len;
#ifdef ESPFS_HEATSHRINK
} else if (fh->decompressor == COMPRESS_HEATSHRINK) {
8 years ago
readFlashUnaligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4);
int decoded = 0;
8 years ago
size_t elen, rlen;
char ebuff[16];
heatshrink_decoder *dec = (heatshrink_decoder *)fh->decompData;
// httpd_printf("Alloc %p\n", dec);
8 years ago
if (fh->posDecomp == fdlen) {
return 0;
}
// We must ensure that whole file is decompressed and written to output buffer.
// This means even when there is no input data (elen==0) try to poll decoder until
// posDecomp equals decompressed file length
while (decoded < len) {
8 years ago
//Feed data into the decompressor
//ToDo: Check ret val of heatshrink fns for errors
elen = flen - (fh->posComp - fh->posStart);
if (elen > 0) {
readFlashUnaligned(ebuff, (char*)fh->posComp, 16);
heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen > 16) ? 16 : elen, &rlen);
fh->posComp += rlen;
8 years ago
}
//Grab decompressed data and put into buff
heatshrink_decoder_poll(dec, (uint8_t *)buff, len - decoded, &rlen);
fh->posDecomp += rlen;
buff += rlen;
decoded += rlen;
8 years ago
// httpd_printf("Elen %d rlen %d d %d pd %ld fdl %d\n",elen,rlen,decoded, fh->posDecomp, fdlen);
8 years ago
if (elen == 0) {
if (fh->posDecomp == fdlen) {
// httpd_printf("Decoder finish\n");
8 years ago
heatshrink_decoder_finish(dec);
}
return decoded;
}
}
return len;
#endif
}
return 0;
}
//Close the file.
void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh)
{
if (fh == NULL) return;
8 years ago
#ifdef ESPFS_HEATSHRINK
if (fh->decompressor == COMPRESS_HEATSHRINK) {
heatshrink_decoder *dec = (heatshrink_decoder *)fh->decompData;
8 years ago
heatshrink_decoder_free(dec);
// httpd_printf("Freed %p\n", dec);
8 years ago
}
#endif
// httpd_printf("Freed %p\n", fh);
8 years ago
free(fh);
}