mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
690 lines
20 KiB
C
Executable File
690 lines
20 KiB
C
Executable File
/*
|
|
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
|
|
*
|
|
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* This file is designed to be parsed during the build process
|
|
*
|
|
* Contains built-in functions for SD card access
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
#include "jswrap_file.h"
|
|
#include "jsparse.h"
|
|
#include "jsflags.h"
|
|
|
|
#define JS_FS_DATA_NAME JS_HIDDEN_CHAR_STR"FSd" // the data in each file
|
|
#define JS_FS_OPEN_FILES_NAME "FSopen" // the list of open files
|
|
#if !defined(LINUX) && !defined(USE_FILESYSTEM_SDIO) && !defined(USE_FLASHFS)
|
|
#define SD_CARD_ANYWHERE
|
|
#endif
|
|
|
|
|
|
#ifndef LINUX
|
|
FATFS jsfsFAT;
|
|
bool fat_initialised = false;
|
|
#endif
|
|
|
|
#ifdef SD_CARD_ANYWHERE
|
|
void sdSPISetup(JsVar *spi, Pin csPin);
|
|
bool isSdSPISetup();
|
|
#endif
|
|
|
|
#ifdef USE_FLASHFS
|
|
#include "flash_diskio.h"
|
|
#endif
|
|
|
|
// 'path' must be of JS_DIR_BUF_SIZE
|
|
bool jsfsGetPathString(char *pathStr, JsVar *path) {
|
|
if (jsvGetString(path, pathStr, JS_DIR_BUF_SIZE)==JS_DIR_BUF_SIZE) {
|
|
jsExceptionHere(JSET_ERROR, "File path too long");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void jsfsReportError(const char *msg, FRESULT res) {
|
|
const char *errStr = "UNKNOWN";
|
|
if (res==FR_OK ) errStr = "OK";
|
|
#ifndef LINUX
|
|
else if (res==FR_DISK_ERR ) errStr = "DISK_ERR";
|
|
else if (res==FR_INT_ERR ) errStr = "INT_ERR";
|
|
else if (res==FR_NOT_READY ) errStr = "NOT_READY";
|
|
else if (res==FR_NO_FILE ) errStr = "NO_FILE";
|
|
else if (res==FR_NO_PATH ) errStr = "NO_PATH";
|
|
else if (res==FR_INVALID_NAME ) errStr = "INVALID_NAME";
|
|
else if (res==FR_DENIED ) errStr = "DENIED";
|
|
else if (res==FR_EXIST ) errStr = "EXIST";
|
|
else if (res==FR_INVALID_OBJECT ) errStr = "INVALID_OBJECT";
|
|
else if (res==FR_WRITE_PROTECTED) errStr = "WRITE_PROTECTED";
|
|
else if (res==FR_INVALID_DRIVE ) errStr = "INVALID_DRIVE";
|
|
else if (res==FR_NOT_ENABLED ) errStr = "NOT_ENABLED";
|
|
else if (res==FR_NO_FILESYSTEM ) errStr = "NO_FILESYSTEM";
|
|
else if (res==FR_MKFS_ABORTED ) errStr = "MKFS_ABORTED";
|
|
else if (res==FR_TIMEOUT ) errStr = "TIMEOUT";
|
|
#endif
|
|
jsExceptionHere(JSET_ERROR,"%s : %s", msg, errStr);
|
|
}
|
|
|
|
bool jsfsInit() {
|
|
#ifdef SD_POWER_PIN
|
|
if (jshPinGetValue(SD_POWER_PIN)==0) {
|
|
jshPinOutput(SD_POWER_PIN, 1);
|
|
jshDelayMicroseconds(5000);
|
|
}
|
|
#endif
|
|
#ifndef LINUX
|
|
if (!fat_initialised) {
|
|
#ifndef USE_FLASHFS
|
|
#ifdef SD_CARD_ANYWHERE
|
|
if (!isSdSPISetup()) {
|
|
#ifdef SD_SPI
|
|
const char *deviceStr = jshGetDeviceString(SD_SPI);
|
|
JsVar *spi = jsvSkipNameAndUnLock(jspGetNamedVariable(deviceStr));
|
|
JshSPIInfo inf;
|
|
jshSPIInitInfo(&inf);
|
|
inf.baudRate = 4000000; // 4Mhz bit rate for onboard SD cards
|
|
inf.pinMISO = SD_DO_PIN;
|
|
inf.pinMOSI = SD_DI_PIN;
|
|
inf.pinSCK = SD_CLK_PIN;
|
|
jshSPISetup(SD_SPI, &inf);
|
|
sdSPISetup(spi, SD_CS_PIN);
|
|
jsvUnLock(spi);
|
|
#else
|
|
jsExceptionHere(JSET_ERROR,"SD card must be setup with E.connectSDCard first");
|
|
return false;
|
|
#endif // SD_SPI
|
|
}
|
|
#endif // SD_CARD_ANYWHER
|
|
#endif // USE_FLASHFS
|
|
FRESULT res;
|
|
|
|
if ((res = f_mount(&jsfsFAT, "", 1/*immediate mount*/)) != FR_OK) {
|
|
#ifndef PIPBOY // Don't throw an error for the Pip-Boy - just return false
|
|
jsfsReportError("Unable to mount media", res);
|
|
#endif
|
|
return false;
|
|
}
|
|
fat_initialised = true;
|
|
}
|
|
#endif // LINUX
|
|
return true;
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "staticmethod",
|
|
"class" : "E",
|
|
"name" : "connectSDCard",
|
|
"generate" : "jswrap_E_connectSDCard",
|
|
"ifndef" : "SAVE_ON_FLASH",
|
|
"params" : [
|
|
["spi","JsVar","The SPI object to use for communication"],
|
|
["csPin","pin","The pin to use for Chip Select"]
|
|
]
|
|
}
|
|
Setup the filesystem so that subsequent calls to `E.openFile` and
|
|
`require('fs').*` will use an SD card on the supplied SPI device and pin.
|
|
|
|
It can even work using software SPI - for instance:
|
|
|
|
```
|
|
// DI/CMD = C7
|
|
// DO/DAT0 = C8
|
|
// CK/CLK = C9
|
|
// CD/CS/DAT3 = C6
|
|
var spi = new SPI();
|
|
spi.setup({mosi:C7, miso:C8, sck:C9});
|
|
E.connectSDCard(spi, C6);
|
|
console.log(require("fs").readdirSync());
|
|
```
|
|
|
|
See [the page on File IO](http://www.espruino.com/File+IO) for more information.
|
|
|
|
**Note:** We'd strongly suggest you add a pullup resistor from CD/CS pin to
|
|
3.3v. It is good practise to avoid accidental writes before Espruino is
|
|
initialised, and some cards will not work reliably without one.
|
|
|
|
**Note:** If you want to remove an SD card after you have started using it, you
|
|
*must* call `E.unmountSD()` or you may cause damage to the card.
|
|
*/
|
|
void jswrap_E_connectSDCard(JsVar *spi, Pin csPin) {
|
|
#ifdef SD_CARD_ANYWHERE
|
|
if (!jsvIsObject(spi)) {
|
|
jsExceptionHere(JSET_ERROR, "First argument is a %t, not an SPI object", spi);
|
|
return;
|
|
}
|
|
if (!jshIsPinValid(csPin)) {
|
|
jsExceptionHere(JSET_ERROR, "Second argument is not a valid pin");
|
|
return;
|
|
}
|
|
jswrap_E_unmountSD();
|
|
sdSPISetup(spi, csPin);
|
|
#else
|
|
NOT_USED(spi);
|
|
NOT_USED(csPin);
|
|
jsExceptionHere(JSET_ERROR, "Unimplemented on Linux");
|
|
#endif
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "class",
|
|
"class" : "File"
|
|
}
|
|
This is the File object - it allows you to stream data to and from files (As
|
|
opposed to the `require('fs').readFile(..)` style functions that read an entire
|
|
file).
|
|
|
|
To create a File object, you must type ```var fd =
|
|
E.openFile('filepath','mode')``` - see [E.openFile](#l_E_openFile) for more
|
|
information.
|
|
|
|
**Note:** If you want to remove an SD card after you have started using it, you
|
|
*must* call `E.unmountSD()` or you may cause damage to the card.
|
|
*/
|
|
|
|
static JsVar* fsGetArray(bool create) {
|
|
return jsvObjectGetChild(execInfo.hiddenRoot, JS_FS_OPEN_FILES_NAME, create ? JSV_ARRAY : 0);
|
|
}
|
|
|
|
static bool fileGetFromVar(JsFile *file, JsVar *parent) {
|
|
bool ret = false;
|
|
JsVar *fHandle = jsvObjectGetChildIfExists(parent, JS_FS_DATA_NAME);
|
|
if (fHandle && jsvIsFlatString(fHandle)) {
|
|
file->data = (JsFileData*)jsvGetFlatStringPointer(fHandle);
|
|
file->fileVar = parent;
|
|
if(file->data->state == FS_OPEN) {// return false if the file has been closed.
|
|
ret = true;
|
|
}
|
|
}
|
|
jsvUnLock(fHandle);
|
|
return ret;
|
|
}
|
|
|
|
/// Uninit all software-related SD card stuff - but don't de-init hardware
|
|
void jswrap_file_kill_sw() {
|
|
JsVar *arr = fsGetArray(false);
|
|
if (arr) {
|
|
JsvObjectIterator it;
|
|
jsvObjectIteratorNew(&it, arr);
|
|
while (jsvObjectIteratorHasValue(&it)) {
|
|
JsVar *file = jsvObjectIteratorGetValue(&it);
|
|
jswrap_file_close(file);
|
|
jsvUnLock(file);
|
|
jsvObjectIteratorNext(&it);
|
|
}
|
|
jsvObjectIteratorFree(&it);
|
|
jsvRemoveAllChildren(arr);
|
|
jsvUnLock(arr);
|
|
}
|
|
// close fs library
|
|
#ifndef LINUX
|
|
if (fat_initialised) {
|
|
fat_initialised = false;
|
|
f_mount(0, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "kill",
|
|
"generate" : "jswrap_file_kill"
|
|
}*/
|
|
void jswrap_file_kill() {
|
|
jswrap_file_kill_sw();
|
|
#ifdef SD_CARD_ANYWHERE
|
|
sdSPISetup(0, PIN_UNDEFINED);
|
|
#endif
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "staticmethod",
|
|
"class" : "E",
|
|
"name" : "unmountSD",
|
|
"generate" : "jswrap_E_unmountSD"
|
|
}
|
|
Unmount the SD card, so it can be removed. If you remove the SD card without
|
|
calling this you may cause corruption, and you will be unable to access another
|
|
SD card until you reset Espruino or call `E.unmountSD()`.
|
|
*/
|
|
void jswrap_E_unmountSD() {
|
|
jswrap_file_kill();
|
|
}
|
|
|
|
static bool allocateJsFile(JsFile* file,FileMode mode, FileType type) {
|
|
JsVar *parent = jspNewObject(0, "File");
|
|
if (!parent) return false; // low memory
|
|
|
|
JsVar *data = jsvNewFlatStringOfLength(sizeof(JsFileData));
|
|
if (!data) { // out of memory for flat string
|
|
jsErrorFlags |= JSERR_LOW_MEMORY; // flag this up as an issue
|
|
jsvUnLock(parent);
|
|
return false;
|
|
}
|
|
file->data = (JsFileData*)jsvGetFlatStringPointer(data);
|
|
jsvObjectSetChildAndUnLock(parent, JS_FS_DATA_NAME, data);
|
|
file->fileVar = parent;
|
|
assert(file->data);
|
|
file->data->mode = mode;
|
|
file->data->type = type;
|
|
file->data->state = FS_NONE;
|
|
return true;
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "staticmethod",
|
|
"class" : "E",
|
|
"name" : "openFile",
|
|
"generate" : "jswrap_E_openFile",
|
|
"params" : [
|
|
["path","JsVar","the path to the file to open."],
|
|
["mode","JsVar","The mode to use when opening the file. Valid values for mode are 'r' for read, 'w' for write new, 'w+' for write existing, and 'a' for append. If not specified, the default is 'r'."]
|
|
],
|
|
"return" : ["JsVar","A File object"],
|
|
"return_object" : "File"
|
|
}
|
|
Open a file
|
|
*/
|
|
JsVar *jswrap_E_openFile(JsVar* path, JsVar* mode) {
|
|
FRESULT res = FR_INVALID_NAME;
|
|
JsFile file;
|
|
file.data = 0;
|
|
file.fileVar = 0;
|
|
FileMode fMode = FM_NONE;
|
|
if (jsfsInit()) {
|
|
JsVar *arr = fsGetArray(true);
|
|
if (!arr) return 0; // out of memory
|
|
|
|
char pathStr[JS_DIR_BUF_SIZE] = "";
|
|
char modeStr[3] = "r";
|
|
if (!jsvIsUndefined(path)) {
|
|
if (!jsfsGetPathString(pathStr, path)) {
|
|
jsvUnLock(arr);
|
|
return 0;
|
|
}
|
|
|
|
if (!jsvIsUndefined(mode))
|
|
jsvGetString(mode, modeStr, 3);
|
|
|
|
#ifndef LINUX
|
|
BYTE ff_mode = 0;
|
|
bool append = false;
|
|
#endif
|
|
|
|
if(strcmp(modeStr,"r") == 0) {
|
|
fMode = FM_READ;
|
|
#ifndef LINUX
|
|
ff_mode = FA_READ | FA_OPEN_EXISTING;
|
|
#endif
|
|
} else if(strcmp(modeStr,"a") == 0) {
|
|
fMode = FM_WRITE;
|
|
#ifndef LINUX
|
|
ff_mode = FA_WRITE | FA_OPEN_ALWAYS;
|
|
append = true;
|
|
#endif
|
|
} else if(strcmp(modeStr,"w") == 0) {
|
|
fMode = FM_WRITE;
|
|
#ifndef LINUX
|
|
ff_mode = FA_WRITE | FA_CREATE_ALWAYS;
|
|
#endif
|
|
} else if(strcmp(modeStr,"w+") == 0) {
|
|
fMode = FM_READ_WRITE;
|
|
#ifndef LINUX
|
|
ff_mode = FA_WRITE | FA_OPEN_ALWAYS;
|
|
#endif
|
|
}
|
|
if(fMode != FM_NONE && allocateJsFile(&file, fMode, FT_FILE)) {
|
|
#ifndef LINUX
|
|
if ((res=f_open(&file.data->handle, pathStr, ff_mode)) == FR_OK) {
|
|
if (append) f_lseek(&file.data->handle, file.data->handle.obj.objsize); // move to end of file
|
|
#else
|
|
file.data->handle = fopen(pathStr, modeStr);
|
|
if (file.data->handle) {
|
|
res=FR_OK;
|
|
#endif
|
|
file.data->state = FS_OPEN;
|
|
// add to list of open files
|
|
jsvArrayPush(arr, file.fileVar);
|
|
} else {
|
|
// File open failed
|
|
jsvUnLock(file.fileVar);
|
|
file.fileVar = 0;
|
|
}
|
|
|
|
if(res != FR_OK)
|
|
jsfsReportError("Could not open file", res);
|
|
|
|
}
|
|
} else {
|
|
jsExceptionHere(JSET_ERROR,"Path is undefined");
|
|
}
|
|
|
|
jsvUnLock(arr);
|
|
}
|
|
|
|
|
|
return file.fileVar;
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "method",
|
|
"class" : "File",
|
|
"name" : "close",
|
|
"generate_full" : "jswrap_file_close(parent)"
|
|
}
|
|
Close an open file.
|
|
*/
|
|
void jswrap_file_close(JsVar* parent) {
|
|
if (jsfsInit()) {
|
|
JsFile file;
|
|
if (fileGetFromVar(&file, parent) && file.data->state == FS_OPEN) {
|
|
#ifndef LINUX
|
|
f_close(&file.data->handle);
|
|
#else
|
|
fclose(file.data->handle);
|
|
file.data->handle = 0;
|
|
#endif
|
|
file.data->state = FS_CLOSED;
|
|
// TODO: could try and free the memory used by file.data ?
|
|
|
|
JsVar *arr = fsGetArray(false);
|
|
if (arr) {
|
|
JsVar *idx = jsvGetIndexOf(arr, file.fileVar, true);
|
|
if (idx)
|
|
jsvRemoveChildAndUnLock(arr, idx);
|
|
jsvUnLock(arr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "method",
|
|
"class" : "File",
|
|
"name" : "write",
|
|
"generate" : "jswrap_file_write",
|
|
"params" : [
|
|
["buffer","JsVar","A string containing the bytes to write"]
|
|
],
|
|
"return" : ["int32","the number of bytes written"]
|
|
}
|
|
Write data to a file.
|
|
|
|
**Note:** By default this function flushes all changes to the SD card, which
|
|
makes it slow (but also safe!). You can use `E.setFlags({unsyncFiles:1})` to
|
|
disable this behaviour and really speed up writes - but then you must be sure to
|
|
close all files you are writing before power is lost or you will cause damage to
|
|
your SD card's filesystem.
|
|
*/
|
|
size_t jswrap_file_write(JsVar* parent, JsVar* buffer) {
|
|
if (!buffer) return 0;
|
|
FRESULT res = 0;
|
|
size_t bytesWritten = 0;
|
|
if (jsfsInit()) {
|
|
JsFile file;
|
|
if (fileGetFromVar(&file, parent)) {
|
|
if(file.data->mode == FM_WRITE || file.data->mode == FM_READ_WRITE) {
|
|
JsvIterator it;
|
|
jsvIteratorNew(&it, buffer, JSIF_EVERY_ARRAY_ELEMENT);
|
|
#ifdef ESPR_FS_LARGE_WRITE_BUFFER
|
|
// writes are faster with sector-size buffers but we can't always safely allocate 512b on the stack unless we're sure we have a big stack
|
|
char buf[512];
|
|
#else
|
|
char buf[32];
|
|
#endif
|
|
|
|
while (jsvIteratorHasElement(&it)) {
|
|
// pull in a buffer's worth of data
|
|
size_t n = 0;
|
|
while (jsvIteratorHasElement(&it) && n<sizeof(buf)) {
|
|
buf[n++] = (char)jsvIteratorGetIntegerValue(&it);
|
|
jsvIteratorNext(&it);
|
|
}
|
|
// write it out
|
|
size_t written = 0;
|
|
#ifndef LINUX
|
|
res = f_write(&file.data->handle, &buf, n, &written);
|
|
#else
|
|
written = fwrite(&buf, 1, n, file.data->handle);
|
|
#endif
|
|
bytesWritten += written;
|
|
if(written == 0)
|
|
res = FR_DISK_ERR;
|
|
if (res) break;
|
|
}
|
|
jsvIteratorFree(&it);
|
|
// finally, sync - just in case there's a reset or something
|
|
if (!jsfGetFlag(JSF_UNSYNC_FILES)) {
|
|
#ifndef LINUX
|
|
f_sync(&file.data->handle);
|
|
#else
|
|
fflush(file.data->handle);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res) {
|
|
jsfsReportError("Unable to write file", res);
|
|
}
|
|
return bytesWritten;
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "method",
|
|
"class" : "File",
|
|
"name" : "read",
|
|
"generate" : "jswrap_file_read",
|
|
"params" : [
|
|
["length","int32","is an integer specifying the number of bytes to read."]
|
|
],
|
|
"return" : ["JsVar","A string containing the characters that were read"]
|
|
}
|
|
Read data in a file in byte size chunks
|
|
*/
|
|
JsVar *jswrap_file_read(JsVar* parent, int length) {
|
|
if (length<0) length=0;
|
|
JsVar *buffer = 0;
|
|
JsvStringIterator it;
|
|
FRESULT res = 0;
|
|
size_t bytesRead = 0;
|
|
if (jsfsInit()) {
|
|
JsFile file;
|
|
if (fileGetFromVar(&file, parent)) {
|
|
if(file.data->mode == FM_READ || file.data->mode == FM_READ_WRITE) {
|
|
size_t actual = 0;
|
|
#ifndef LINUX
|
|
// if we're able to load this into a flat string, do it!
|
|
size_t len = f_size(&file.data->handle)-f_tell(&file.data->handle);
|
|
if ( len == 0 ) { // file all read
|
|
return 0; // if called from a pipe signal end callback
|
|
}
|
|
if (len > (size_t)length) len = (size_t)length;
|
|
buffer = jsvNewFlatStringOfLength(len);
|
|
if (buffer) {
|
|
res = f_read(&file.data->handle, jsvGetFlatStringPointer(buffer), len, &actual);
|
|
if (res) jsfsReportError("Unable to read file", res);
|
|
return buffer;
|
|
}
|
|
#endif
|
|
char buf[32];
|
|
|
|
while (bytesRead < (size_t)length) {
|
|
size_t requested = (size_t)length - bytesRead;
|
|
if (requested > sizeof( buf ))
|
|
requested = sizeof( buf );
|
|
actual = 0;
|
|
#ifndef LINUX
|
|
res = f_read(&file.data->handle, buf, requested, &actual);
|
|
if(res) break;
|
|
#else
|
|
actual = fread(buf, 1, requested, file.data->handle);
|
|
#endif
|
|
if (actual>0) {
|
|
if (!buffer) {
|
|
buffer = jsvNewFromEmptyString();
|
|
if (!buffer) return 0; // out of memory
|
|
jsvStringIteratorNew(&it, buffer, 0);
|
|
}
|
|
size_t i;
|
|
for (i=0;i<actual;i++)
|
|
jsvStringIteratorAppend(&it, buf[i]);
|
|
}
|
|
bytesRead += actual;
|
|
if(actual != requested) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (res) jsfsReportError("Unable to read file", res);
|
|
|
|
if (buffer)
|
|
jsvStringIteratorFree(&it);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "method",
|
|
"class" : "File",
|
|
"name" : "skip",
|
|
"generate_full" : "jswrap_file_skip_or_seek(parent,nBytes,true)",
|
|
"params" : [
|
|
["nBytes","int32","is a positive integer specifying the number of bytes to skip forwards."]
|
|
]
|
|
}
|
|
Skip the specified number of bytes forward in the file
|
|
*/
|
|
/*JSON{
|
|
"type" : "method",
|
|
"class" : "File",
|
|
"name" : "seek",
|
|
"generate_full" : "jswrap_file_skip_or_seek(parent,nBytes,false)",
|
|
"params" : [
|
|
["nBytes","int32","is an integer specifying the number of bytes to skip forwards."]
|
|
]
|
|
}
|
|
Seek to a certain position in the file
|
|
*/
|
|
void jswrap_file_skip_or_seek(JsVar* parent, int nBytes, bool is_skip) {
|
|
if (nBytes<0) {
|
|
if ( is_skip )
|
|
jsWarn("Bytes to skip must be >=0");
|
|
else
|
|
jsWarn("Position to seek to must be >=0");
|
|
return;
|
|
}
|
|
FRESULT res = 0;
|
|
if (jsfsInit()) {
|
|
JsFile file;
|
|
if (fileGetFromVar(&file, parent)) {
|
|
if(file.data->mode == FM_READ || file.data->mode == FM_WRITE || file.data->mode == FM_READ_WRITE) {
|
|
#ifndef LINUX
|
|
res = (FRESULT)f_lseek(&file.data->handle, (DWORD)(is_skip ? f_tell(&file.data->handle) : 0) + (DWORD)nBytes);
|
|
#else
|
|
fseek(file.data->handle, nBytes, is_skip ? SEEK_CUR : SEEK_SET);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if (res) jsfsReportError(is_skip?"Unable to skip":"Unable to seek", res);
|
|
}
|
|
|
|
/*JSON{
|
|
"type" : "method",
|
|
"class" : "File",
|
|
"name" : "pipe",
|
|
"ifndef" : "SAVE_ON_FLASH",
|
|
"generate" : "jswrap_pipe",
|
|
"params" : [
|
|
["destination","JsVar","The destination file/stream that will receive content from the source."],
|
|
["options","JsVar",["[optional] An object `{ chunkSize : int=32, end : bool=true, complete : function }`","chunkSize : The amount of data to pipe from source to destination at a time","complete : a function to call when the pipe activity is complete","end : call the 'end' function on the destination when the source is finished"]]
|
|
],
|
|
"typescript": "pipe(destination: any, options?: PipeOptions): void"
|
|
}
|
|
Pipe this file to a stream (an object with a 'write' method)
|
|
*/
|
|
|
|
#ifdef USE_FLASHFS
|
|
|
|
/*JSON{
|
|
"type" : "staticmethod",
|
|
"class" : "E",
|
|
"name" : "flashFatFS",
|
|
"generate" : "jswrap_E_flashFatFS",
|
|
"ifdef" : "USE_FLASHFS",
|
|
"params" : [
|
|
["options","JsVar",["[optional] An object `{ addr : int=0x300000, sectors : int=256, format : bool=false }`","addr : start address in flash","sectors: number of sectors to use","format: Format the media"]]
|
|
],
|
|
"return" : ["bool","True on success, or false on failure"]
|
|
}
|
|
Change the parameters used for the flash filesystem. The default address is the
|
|
last 1Mb of 4Mb Flash, 0x300000, with total size of 1Mb.
|
|
|
|
Before first use the media needs to be formatted.
|
|
|
|
```
|
|
fs=require("fs");
|
|
try {
|
|
fs.readdirSync();
|
|
} catch (e) { //'Uncaught Error: Unable to mount media : NO_FILESYSTEM'
|
|
console.log('Formatting FS - only need to do once');
|
|
E.flashFatFS({ format: true });
|
|
}
|
|
fs.writeFileSync("bang.txt", "This is the way the world ends\nnot with a bang but a whimper.\n");
|
|
fs.readdirSync();
|
|
```
|
|
|
|
This will create a drive of 100 * 4096 bytes at 0x300000. Be careful with the
|
|
selection of flash addresses as you can overwrite firmware! You only need to
|
|
format once, as each will erase the content.
|
|
|
|
`E.flashFatFS({ addr:0x300000,sectors:100,format:true });`
|
|
*/
|
|
|
|
int jswrap_E_flashFatFS(JsVar* options) {
|
|
uint32_t addr = FS_FLASH_BASE;
|
|
uint16_t sectors = FS_SECTOR_COUNT;
|
|
uint8_t format = 0;
|
|
if (jsvIsObject(options)) {
|
|
JsVar *a = jsvObjectGetChildIfExists(options, "addr");
|
|
if (a) {
|
|
if (jsvIsNumeric(a) && jsvGetInteger(a)>0x100000)
|
|
addr = (uint32_t)jsvGetInteger(a);
|
|
}
|
|
JsVar *s = jsvObjectGetChildIfExists(options, "sectors");
|
|
if (s) {
|
|
if (jsvIsNumeric(s) && jsvGetInteger(s)>0)
|
|
sectors = (uint16_t)jsvGetInteger(s);
|
|
}
|
|
JsVar *f = jsvObjectGetChildIfExists(options, "format");
|
|
if (f) {
|
|
if (jsvIsBoolean(f))
|
|
format = jsvGetBool(f);
|
|
}
|
|
}
|
|
else if (!jsvIsUndefined(options)) {
|
|
jsExceptionHere(JSET_TYPEERROR, "'options' must be an object, or undefined");
|
|
}
|
|
|
|
uint8_t init=flashFatFsInit(addr, sectors);
|
|
if (init) {
|
|
if ( format ) {
|
|
uint8_t res = f_mount(&jsfsFAT, "", 0/*delayed mount?*/);
|
|
jsDebug(DBG_INFO,"Formatting Flash\n");
|
|
res = f_mkfs("", 1, 0); // Super Floppy format, using all space (not partition table)
|
|
if (res != FR_OK) {
|
|
jsExceptionHere(JSET_INTERNALERROR, "Flash Formatting error:",res);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
jsfsInit();
|
|
return true;
|
|
}
|
|
#endif
|