[core] Refactor realtek-ambz, cleanup, reformat, add missing copyrights

This commit is contained in:
Kuba Szczodrzyński
2022-05-06 20:27:06 +02:00
parent 5aba2eb4a1
commit 589d3ef4d8
103 changed files with 1222 additions and 9930 deletions

29
.clang-format Normal file
View File

@@ -0,0 +1,29 @@
Language: Cpp
BasedOnStyle: LLVM
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: true
AlignConsecutiveMacros: AcrossComments
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeTernaryOperators: true
ColumnLimit: 120
ContinuationIndentWidth: 4
EmptyLineBeforeAccessModifier: Always
FixNamespaceComments: true
IndentAccessModifiers: false
IndentCaseLabels: true
IndentWidth: 4
LambdaBodyIndentation: Signature
MaxEmptyLinesToKeep: 1
# PointerAlignment: Left # TODO enable this and reformat project
QualifierAlignment: Left
ReflowComments: true
SeparateDefinitionBlocks: Always
TabWidth: 4
UseTab: Always

View File

@@ -6,7 +6,6 @@ jobs:
lint-clang-format: lint-clang-format:
name: Lint with clang-format name: Lint with clang-format
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ false }} # disable for now
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#include "LibreTuyaAPI.h" #include "LibreTuyaAPI.h"
__weak char *strdup(const char *s) { __weak char *strdup(const char *s) {

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#pragma once #pragma once
// LibreTuya version macros // LibreTuya version macros

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#pragma once #pragma once
// see docs/API Configuration // see docs/API Configuration

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#include "lt_logger.h" #include "lt_logger.h"
#include <Arduino.h> #include <Arduino.h>

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
#pragma once #pragma once
#include "LibreTuyaConfig.h" #include "LibreTuyaConfig.h"

View File

@@ -0,0 +1,55 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
void randomSeed(uint32_t dwSeed) {
if (dwSeed != 0) {
srand(dwSeed);
}
}
long random(long howbig) {
if (howbig == 0) {
return 0;
}
return rand() % howbig;
}
long random(long howsmall, long howbig) {
if (howsmall >= howbig) {
return howsmall;
}
long diff = howbig - howsmall;
return random(diff) + howsmall;
}
extern long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
extern uint16_t makeWord(uint16_t w) {
return w;
}
extern uint16_t makeWord(uint8_t h, uint8_t l) {
return (h << 8) | l;
}

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012 Arduino. All right reserved. Copyright (c) 2014 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@@ -16,19 +16,21 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef RESET_H #include <stdlib.h>
#define RESET_H
#ifdef __cplusplus extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__));
extern "C" { extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__));
#endif
void initiateReset(int ms); void __cxa_pure_virtual(void) {
void tickReset(); // We might want to write some diagnostics to uart in this case
void cancelReset(); // std::terminate();
while (1)
#ifdef __cplusplus ;
} }
#endif
#endif void __cxa_deleted_virtual(void) {
// We might want to write some diagnostics to uart in this case
// std::terminate();
while (1)
;
}

View File

@@ -18,16 +18,11 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifdef ARDUINO_AMEBA
#include <Arduino.h> #include <Arduino.h>
#else
#include <stdio.h>
#endif
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) { char *dtostrf(double val, signed char width, unsigned char prec, char *sout) {
char fmt[20]; char fmt[20];
sprintf(fmt, "%%%d.%df", width, prec); sprintf(fmt, "%%%d.%df", width, prec);
sprintf(sout, fmt, val); sprintf(sout, fmt, val);
return sout; return sout;
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2012 Arduino. All right reserved. Copyright (c) 2015 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@@ -29,12 +29,7 @@ static void __empty() {
// Empty // Empty
} }
#include "cmsis_os.h" void yield(void) __attribute__((weak, alias("__empty")));
void yield(void) {
vTaskDelay(1);
taskYIELD();
}
/** /**
* SysTick hook * SysTick hook
@@ -46,7 +41,8 @@ static int __false() {
// Return false // Return false
return 0; return 0;
} }
int sysTickHook(void) __attribute__ ((weak, alias("__false")));
int sysTickHook(void) __attribute__((weak, alias("__false")));
/** /**
* SVC hook * SVC hook
@@ -60,5 +56,6 @@ static void __halt() {
while (1) while (1)
; ;
} }
void svcHook(void) __attribute__ ((weak, alias("__halt")));
void pendSVHook(void) __attribute__ ((weak, alias("__halt"))); void svcHook(void) __attribute__((weak, alias("__halt")));
void pendSVHook(void) __attribute__((weak, alias("__halt")));

View File

@@ -0,0 +1,111 @@
/*
Copyright (c) 2014 Arduino LLC. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
char *ltoa(long value, char *string, int radix) {
char tmp[33];
char *tp = tmp;
long i;
unsigned long v;
int sign;
char *sp;
if (string == NULL) {
return 0;
}
if (radix > 36 || radix <= 1) {
return 0;
}
sign = (radix == 10 && value < 0);
if (sign) {
v = -value;
} else {
v = (unsigned long)value;
}
while (v || tp == tmp) {
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i + '0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
if (sign)
*sp++ = '-';
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
char *ultoa(unsigned long value, char *string, int radix) {
char tmp[33];
char *tp = tmp;
long i;
unsigned long v = value;
char *sp;
if (string == NULL) {
return 0;
}
if (radix > 36 || radix <= 1) {
return 0;
}
while (v || tp == tmp) {
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i + '0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
char *itoa(int value, char *string, int radix) {
return ltoa(value, string, radix);
}
char *utoa(unsigned int value, char *string, int radix) {
return ultoa(value, string, radix);
}
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,54 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <stdint.h>
uint8_t shiftIn(pin_size_t ulDataPin, pin_size_t ulClockPin, BitOrder ulBitOrder) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(ulClockPin, HIGH);
if (ulBitOrder == LSBFIRST) {
value |= digitalRead(ulDataPin) << i;
} else {
value |= digitalRead(ulDataPin) << (7 - i);
}
digitalWrite(ulClockPin, LOW);
}
return value;
}
void shiftOut(pin_size_t ulDataPin, pin_size_t ulClockPin, BitOrder ulBitOrder, uint8_t ulVal) {
uint8_t i;
for (i = 0; i < 8; i++) {
if (ulBitOrder == LSBFIRST) {
digitalWrite(ulDataPin, !!(ulVal & (1 << i)));
} else {
digitalWrite(ulDataPin, !!(ulVal & (1 << (7 - i))));
}
digitalWrite(ulClockPin, HIGH);
digitalWrite(ulClockPin, LOW);
}
}

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once #pragma once
#include <api/FS.h> #include <api/FS.h>

View File

@@ -1 +1,3 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
// nop // nop

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#pragma once #pragma once
// ESP32 WiFi examples use WiFiAP.h include // ESP32 WiFi examples use WiFiAP.h include

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once #pragma once
#include <api/deprecated-avr-comp/avr/pgmspace.h> #include <api/deprecated-avr-comp/avr/pgmspace.h>

View File

@@ -1 +1,3 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
// nop // nop

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
#pragma once #pragma once
#include <time.h> #include <time.h>

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -17,75 +17,82 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "IPv6Address.h"
#include <Arduino.h> #include <Arduino.h>
#include <api/Print.h> #include <api/Print.h>
#include "IPv6Address.h" IPv6Address::IPv6Address() {
memset(_address.bytes, 0, sizeof(_address.bytes));
IPv6Address::IPv6Address()
{
memset(_address.bytes, 0, sizeof(_address.bytes));
} }
IPv6Address::IPv6Address(const uint8_t *address) IPv6Address::IPv6Address(const uint8_t *address) {
{ memcpy(_address.bytes, address, sizeof(_address.bytes));
memcpy(_address.bytes, address, sizeof(_address.bytes));
} }
IPv6Address::IPv6Address(const uint32_t *address) IPv6Address::IPv6Address(const uint32_t *address) {
{ memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
} }
IPv6Address& IPv6Address::operator=(const uint8_t *address) IPv6Address &IPv6Address::operator=(const uint8_t *address) {
{ memcpy(_address.bytes, address, sizeof(_address.bytes));
memcpy(_address.bytes, address, sizeof(_address.bytes)); return *this;
return *this;
} }
bool IPv6Address::operator==(const uint8_t* addr) const bool IPv6Address::operator==(const uint8_t *addr) const {
{ return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
} }
size_t IPv6Address::printTo(Print& p) const size_t IPv6Address::printTo(Print &p) const {
{ /* size_t n = 0;
/* size_t n = 0; for(int i = 0; i < 16; i+=2) {
for(int i = 0; i < 16; i+=2) { if(i){
if(i){ n += p.print(':');
n += p.print(':'); }
} n += p.printf("%02x", _address.bytes[i]);
n += p.printf("%02x", _address.bytes[i]); n += p.printf("%02x", _address.bytes[i+1]);
n += p.printf("%02x", _address.bytes[i+1]);
} }
return n; */ return n; */
} }
String IPv6Address::toString() const String IPv6Address::toString() const {
{ char szRet[40];
char szRet[40]; sprintf(
sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", szRet,
_address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3], "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
_address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7], _address.bytes[0],
_address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11], _address.bytes[1],
_address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]); _address.bytes[2],
return String(szRet); _address.bytes[3],
_address.bytes[4],
_address.bytes[5],
_address.bytes[6],
_address.bytes[7],
_address.bytes[8],
_address.bytes[9],
_address.bytes[10],
_address.bytes[11],
_address.bytes[12],
_address.bytes[13],
_address.bytes[14],
_address.bytes[15]
);
return String(szRet);
} }
bool IPv6Address::fromString(const char *address) bool IPv6Address::fromString(const char *address) {
{ // format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
//format 0011:2233:4455:6677:8899:aabb:ccdd:eeff if (strlen(address) != 39) {
if(strlen(address) != 39){ return false;
return false; }
} char *pos = (char *)address;
char * pos = (char *)address; size_t i = 0;
size_t i = 0; for (i = 0; i < 16; i += 2) {
for(i = 0; i < 16; i+=2) { if (!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos + 2, "%2hhx", &_address.bytes[i + 1])) {
if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){ return false;
return false; }
} pos += 5;
pos += 5; }
} return true;
return true;
} }

View File

@@ -19,80 +19,79 @@
#pragma once #pragma once
#include <stdint.h>
#include <api/String.h>
#include <api/Print.h> #include <api/Print.h>
#include <api/String.h>
#include <stdint.h>
// A class to make it easier to handle and pass around IP addresses // A class to make it easier to handle and pass around IP addresses
namespace arduino { namespace arduino {
class IPv6Address: public Printable class IPv6Address : public Printable {
{ private:
private: union {
union { uint8_t bytes[16]; // IPv4 address
uint8_t bytes[16]; // IPv4 address uint32_t dword[4];
uint32_t dword[4]; } _address;
} _address;
// Access the raw byte array containing the address. Because this returns a pointer // Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only // to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not // be used when you know that the usage of the returned uint8_t* will be transient and not
// stored. // stored.
uint8_t* raw_address() uint8_t *raw_address() {
{ return _address.bytes;
return _address.bytes; }
}
public: public:
// Constructors // Constructors
IPv6Address(); IPv6Address();
IPv6Address(const uint8_t *address); IPv6Address(const uint8_t *address);
IPv6Address(const uint32_t *address); IPv6Address(const uint32_t *address);
virtual ~IPv6Address() {}
bool fromString(const char *address); virtual ~IPv6Address() {}
bool fromString(const String &address) { return fromString(address.c_str()); }
operator const uint8_t*() const bool fromString(const char *address);
{
return _address.bytes;
}
operator const uint32_t*() const
{
return _address.dword;
}
bool operator==(const IPv6Address& addr) const
{
return (_address.dword[0] == addr._address.dword[0])
&& (_address.dword[1] == addr._address.dword[1])
&& (_address.dword[2] == addr._address.dword[2])
&& (_address.dword[3] == addr._address.dword[3]);
}
bool operator==(const uint8_t* addr) const;
// Overloaded index operator to allow getting and setting individual octets of the address bool fromString(const String &address) {
uint8_t operator[](int index) const return fromString(address.c_str());
{ }
return _address.bytes[index];
}
uint8_t& operator[](int index)
{
return _address.bytes[index];
}
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types operator const uint8_t *() const {
IPv6Address& operator=(const uint8_t *address); return _address.bytes;
}
// TODO implement printTo() operator const uint32_t *() const {
virtual size_t printTo(Print& p) const; return _address.dword;
String toString() const; }
friend class UDP; bool operator==(const IPv6Address &addr) const {
friend class Client; return (_address.dword[0] == addr._address.dword[0]) && (_address.dword[1] == addr._address.dword[1]) &&
friend class Server; (_address.dword[2] == addr._address.dword[2]) && (_address.dword[3] == addr._address.dword[3]);
}
bool operator==(const uint8_t *addr) const;
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const {
return _address.bytes[index];
}
uint8_t &operator[](int index) {
return _address.bytes[index];
}
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
IPv6Address &operator=(const uint8_t *address);
// TODO implement printTo()
virtual size_t printTo(Print &p) const;
String toString() const;
friend class UDP;
friend class Client;
friend class Server;
}; };
} } // namespace arduino
using arduino::IPv6Address; using arduino::IPv6Address;

View File

@@ -19,7 +19,7 @@ size_t LwIPRxBuffer::r_available() {
return 0; return 0;
} }
uint16_t count = 0; uint16_t count = 0;
int res = lwip_ioctl(_sock, FIONREAD, &count); int res = lwip_ioctl(_sock, FIONREAD, &count);
if (res < 0) { if (res < 0) {
_failed = true; _failed = true;
return 0; return 0;

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-30. */
#pragma once #pragma once
#include "../IPv6Address.h" #include "../IPv6Address.h"

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -1,58 +1,29 @@
/* /* Copyright (c) Kuba Szczodrzyński 2022-04-23. */
Arduino.h - Main include file for the Arduino SDK
Copyright (c) 2005-2013 Arduino Team. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once #pragma once
#ifdef __cplusplus #ifdef __cplusplus
#include "WCharacterFixup.h" #include "WCharacterFixup.h"
#endif #endif
#define PinMode PinModeArduino // this conflicts with SDK enum #define PinMode PinModeArduino // this conflicts with SDK enum
#include <api/ArduinoAPI.h> #include <api/ArduinoAPI.h>
#include <api/LibreTuyaAPI.h> #include <api/LibreTuyaAPI.h>
#undef PinMode #undef PinMode
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" uint32_t SystemCoreClock;
#endif // __cplusplus #else
extern uint32_t SystemCoreClock; extern uint32_t SystemCoreClock;
#define clockCyclesPerMicrosecond() ( SystemCoreClock / 1000000L )
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (SystemCoreClock / 1000L) )
#define microsecondsToClockCycles(a) ( (a) * (SystemCoreClock / 1000000L) )
#include "WVariant.h"
#ifdef __cplusplus
} // extern "C"
#endif #endif
#define clockCyclesPerMicrosecond() (SystemCoreClock / 1000000L)
#define clockCyclesToMicroseconds(a) (((a)*1000L) / (SystemCoreClock / 1000L))
#define microsecondsToClockCycles(a) ((a) * (SystemCoreClock / 1000000L))
// Include board variant #define interrupts() vPortClearInterruptMask(0)
#include "variant.h"
#define interrupts() vPortClearInterruptMask(0)
#define noInterrupts() ulPortSetInterruptMask() #define noInterrupts() ulPortSetInterruptMask()
/* // Include platform-specific code
* \brief SAM3 products have only one reference for ADC #include "WVariant.h"
*/ // Include board variant
typedef enum _eAnalogReference #include "variant.h"
{
AR_DEFAULT,
} eAnalogReference ;

View File

@@ -1,7 +0,0 @@
#include <api/Print.h>
class CountingStream : public Print
{
virtual size_t write(uint8_t) { return 1; };
virtual size_t write(const uint8_t *buffer, size_t size) { return size; };
};

View File

@@ -1,198 +0,0 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_FUNCTIONPOINTER_H
#define MBED_FUNCTIONPOINTER_H
#include <string.h>
#include <stdint.h>
/* If we had variaditic templates, this wouldn't be a problem, but until C++11 is enabled, we are stuck with multiple classes... */
/** A class for storing and calling a pointer to a static or member function
*/
template <typename R, typename A1>
class FunctionPointerArg1{
public:
/** Create a FunctionPointer, attaching a static function
*
* @param function The static function to attach (default is none)
*/
FunctionPointerArg1(R (*function)(A1) = 0) {
attach(function);
}
/** Create a FunctionPointer, attaching a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the member function to attach
*/
template<typename T>
FunctionPointerArg1(T *object, R (T::*member)(A1)) {
attach(object, member);
}
/** Attach a static function
*
* @param function The static function to attach (default is none)
*/
void attach(R (*function)(A1)) {
_p.function = function;
_membercaller = 0;
}
/** Attach a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the member function to attach
*/
template<typename T>
void attach(T *object, R (T::*member)(A1)) {
_p.object = static_cast<void*>(object);
*reinterpret_cast<R (T::**)(A1)>(_member) = member;
_membercaller = &FunctionPointerArg1::membercaller<T>;
}
/** Call the attached static or member function
*/
R call(A1 a) {
if (_membercaller == 0 && _p.function) {
return _p.function(a);
} else if (_membercaller && _p.object) {
return _membercaller(_p.object, _member, a);
}
return (R)0;
}
/** Get registered static function
*/
R(*get_function(A1))() {
return _membercaller ? (R(*)(A1))0 : (R(*)(A1))_p.function;
}
R operator ()(A1 a) {
return call(a);
}
operator bool(void) const {
return (_membercaller != NULL ? _p.object : (void*)_p.function) != NULL;
}
private:
template<typename T>
static R membercaller(void *object, uintptr_t *member, A1 a) {
T* o = static_cast<T*>(object);
R (T::**m)(A1) = reinterpret_cast<R (T::**)(A1)>(member);
return (o->**m)(a);
}
union {
R (*function)(A1); // static function pointer
void *object; // object this pointer
} _p;
uintptr_t _member[4]; // aligned raw member function pointer storage - converted back by registered _membercaller
R (*_membercaller)(void*, uintptr_t*, A1); // registered membercaller function to convert back and call _m.member on _object
};
/** A class for storing and calling a pointer to a static or member function (R ()(void))
*/
template <typename R>
class FunctionPointerArg1<R, void>{
public:
/** Create a FunctionPointer, attaching a static function
*
* @param function The static function to attach (default is none)
*/
FunctionPointerArg1(R (*function)(void) = 0) {
attach(function);
}
/** Create a FunctionPointer, attaching a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the void member function to attach
*/
template<typename T>
FunctionPointerArg1(T *object, R (T::*member)(void)) {
attach(object, member);
}
/** Attach a static function
*
* @param function The void static function to attach (default is none)
*/
void attach(R (*function)(void)) {
_p.function = function;
_membercaller = 0;
}
/** Attach a member function
*
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the void member function to attach
*/
template<typename T>
void attach(T *object, R (T::*member)(void)) {
_p.object = static_cast<void*>(object);
*reinterpret_cast<R (T::**)(void)>(_member) = member;
_membercaller = &FunctionPointerArg1::membercaller<T>;
}
/** Call the attached static or member function
*/
R call(){
if (_membercaller == 0 && _p.function) {
return _p.function();
} else if (_membercaller && _p.object) {
return _membercaller(_p.object, _member);
}
return (R)0;
}
/** Get registered static function
*/
R(*get_function())() {
return _membercaller ? (R(*)())0 : (R(*)())_p.function;
}
R operator ()(void) {
return call();
}
operator bool(void) const {
return (_membercaller != NULL ? _p.object : (void*)_p.function) != NULL;
}
private:
template<typename T>
static R membercaller(void *object, uintptr_t *member) {
T* o = static_cast<T*>(object);
R (T::**m)(void) = reinterpret_cast<R (T::**)(void)>(member);
return (o->**m)();
}
union {
R (*function)(void); // static function pointer
void *object; // object this pointer
} _p;
uintptr_t _member[4]; // aligned raw member function pointer storage - converted back by registered _membercaller
R (*_membercaller)(void*, uintptr_t*); // registered membercaller function to convert back and call _m.member on _object
};
typedef FunctionPointerArg1<void, void> FunctionPointer;
typedef FunctionPointerArg1<void, int> event_callback_t;
#endif

View File

@@ -16,117 +16,83 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "Arduino.h"
#include "LOGUARTClass.h" #include "LOGUARTClass.h"
#include <Arduino.h>
#define LOG_UART_MODIFIABLE_BAUD_RATE 1 #define LOG_UART_MODIFIABLE_BAUD_RATE 1
#ifdef __cplusplus
extern "C" {
#endif
#include "osdep_service.h"
#include "rtl8710b.h"
extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c
#ifdef __cplusplus
}
#endif
RingBuffer rx_buffer0; RingBuffer rx_buffer0;
LOGUARTClass::LOGUARTClass(int dwIrq, RingBuffer* pRx_buffer ) LOGUARTClass::LOGUARTClass(int dwIrq, RingBuffer *pRx_buffer) {
{ _rx_buffer = pRx_buffer;
_rx_buffer = pRx_buffer; _dwIrq = dwIrq;
_dwIrq = dwIrq;
} }
// Protected Methods ////////////////////////////////////////////////////////////// void IrqHandler(void) {
uint8_t data = 0;
BOOL PullMode = _FALSE;
uint32_t IrqEn = DiagGetIsrEnReg();
DiagSetIsrEnReg(0);
data = DiagGetChar(PullMode);
if (data > 0)
// Public Methods //////////////////////////////////////////////////////////////
void IrqHandler( void )
{
uint8_t data = 0;
BOOL PullMode = _FALSE;
uint32_t IrqEn = DiagGetIsrEnReg();
DiagSetIsrEnReg(0);
data = DiagGetChar(PullMode);
if ( data > 0 )
rx_buffer0.store_char(data); rx_buffer0.store_char(data);
DiagSetIsrEnReg(IrqEn); DiagSetIsrEnReg(IrqEn);
} }
void LOGUARTClass::begin(const uint32_t dwBaudRate) {
void LOGUARTClass::begin( const uint32_t dwBaudRate ) DIAG_UartReInit((IRQ_FUN)IrqHandler);
{ NVIC_SetPriority(UART_LOG_IRQ, 10);
DIAG_UartReInit((IRQ_FUN) IrqHandler); LOGUART_SetBaud(dwBaudRate);
NVIC_SetPriority(UART_LOG_IRQ, 10);
LOGUART_SetBaud(dwBaudRate);
} }
void LOGUARTClass::end( void ) void LOGUARTClass::end(void) {
{ // clear any received data
// clear any received data _rx_buffer->_iHead = _rx_buffer->_iTail;
_rx_buffer->_iHead = _rx_buffer->_iTail ;
} }
int LOGUARTClass::available( void ) int LOGUARTClass::available(void) {
{ return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE;
return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ;
} }
int LOGUARTClass::peek( void ) int LOGUARTClass::peek(void) {
{
if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) if (_rx_buffer->_iHead == _rx_buffer->_iTail)
return -1 ; return -1;
return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
return _rx_buffer->_aucBuffer[_rx_buffer->_iTail];
} }
int LOGUARTClass::read( void ) int LOGUARTClass::read(void) {
{ // if the head isn't ahead of the tail, we don't have any characters
// if the head isn't ahead of the tail, we don't have any characters if (_rx_buffer->_iHead == _rx_buffer->_iTail)
if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) return -1;
return -1 ;
uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
_rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ;
return uc ;
uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail];
_rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE;
return uc;
} }
void LOGUARTClass::flush( void ) void LOGUARTClass::flush(void) {
{ // TODO:
// TODO: // while ( serial_writable(&(this->sobj)) != 1 );
// while ( serial_writable(&(this->sobj)) != 1 ); /*
/* // Wait for transmission to complete
// Wait for transmission to complete while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) ;
; */
*/
} }
size_t LOGUARTClass::write( const uint8_t uc_data ) size_t LOGUARTClass::write(const uint8_t uc_data) {
{ DiagPutChar(uc_data);
DiagPutChar(uc_data); return 1;
return 1;
} }
LOGUARTClass Serial(UART_LOG_IRQ, &rx_buffer0); LOGUARTClass Serial(UART_LOG_IRQ, &rx_buffer0);
bool Serial_available() { bool Serial_available() {
return Serial.available() > 0; return Serial.available() > 0;
} }

View File

@@ -8,7 +8,7 @@
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
@@ -18,36 +18,41 @@
#pragma once #pragma once
#include "api/HardwareSerial.h" #include <api/HardwareSerial.h>
#include "api/RingBuffer.h" #include <api/RingBuffer.h>
using namespace arduino; using namespace arduino;
class LOGUARTClass : public HardwareSerial // TODO this class begs to be rewritten :(
{
class LOGUARTClass : public HardwareSerial {
public: public:
LOGUARTClass(int dwIrq, RingBuffer* pRx_buffer ); LOGUARTClass(int dwIrq, RingBuffer *pRx_buffer);
void begin(const uint32_t dwBaudRate); void begin(const uint32_t dwBaudRate);
inline void begin(const uint32_t dwBaudRate, uint16_t config) {
begin(dwBaudRate); // TODO implement this properly
}
void end(void);
int available(void);
int peek(void);
int read(void);
void flush(void);
size_t write(const uint8_t c);
using Print::write; // pull in write(str) and write(buf, size) from Print inline void begin(const uint32_t dwBaudRate, uint16_t config) {
begin(dwBaudRate); // TODO implement this properly
}
operator bool() { return true; }; // UART always active void end(void);
int available(void);
int peek(void);
int read(void);
void flush(void);
size_t write(const uint8_t c);
using Print::write; // pull in write(str) and write(buf, size) from Print
operator bool() {
return true; // UART always active
}
protected: protected:
RingBuffer *_rx_buffer; RingBuffer *_rx_buffer;
int _dwIrq; int _dwIrq;
private: private:
friend bool Serial_available(); friend bool Serial_available();
}; };

View File

@@ -1,76 +0,0 @@
#ifndef _POWER_MANAGEMENT_H_
#define _POWER_MANAGEMENT_H_
#include <inttypes.h>
/**
* @class PowerManagementClass PowerManagement.h
* @brief Power management in Ameba
*/
class PowerManagementClass {
public:
/**
* @brief Allow OS automatically save power while idle
*
* As OS consider it would idle for more than 2s, it will invoke system suspend.
* If wlan is associated with AP, than it will under asslociated idle state.
*/
static void sleep(void);
static void sleep(uint32_t bitflg);
/**
* @brief Disallow OS automatically save power while idle
*/
static void active(void);
static void active(uint32_t bitflg);
/**
* @brief Reserved PLL while sleep
*
* Reserve PLL would keep FIFO of peripherals (Ex. UART) but cost more power (around 5mA).
* If we don't reserve PLL, it saves more power but we might missing data because FIFO is turned of this way.
*
* @param[in] reserve true for reserved, false for non-reserved
*/
static void setPllReserved(bool reserve);
/**
* @brief Enter deepsleep immediately
*
* Invoke deepsleep would make system enter deepsleep state immediately.
* It's the state that saves most power.
* As it wakeup from deepsleep, the system would behave just like reboot.
*
* @param[in] duration_ms wakeup after specific time in unit of millisecond
*/
static void deepsleep(uint32_t duration_ms);
/**
* @brief Check if system is allowed enter any power save state
*
* The pin 18 (GPIOE_5) is designed as safe lock.
* If pin 18 is HIGH, then we prevent Ameba enter any power save state.\n\n
* Under any power save state, we are not able to flash image to Ameba.
* Thus if user misuse deepsleep and make Ameba enter deepsleep immediately after boot up,
* then he would find it's hard to flash image.
* In this case, he can pull up pin 18.
*
* @return true if system not allowed enter any power save state, and false vise versa
*/
static bool safeLock();
/**
* @brief Reboot system
*
* Reboot system in soft way. Some registers is not powered off in this case, but mostly we could regard this as reboot.
*/
static void softReset();
private:
static bool reservePLL;
};
extern PowerManagementClass PowerManagement;
#endif

View File

@@ -1,17 +1,12 @@
#if 1 // !defined(BOARD_RTL8710) #include <Arduino.h>
#include "Arduino.h" extern void pinRemoveMode(pin_size_t pinNumber);
#ifdef __cplusplus
extern "C" {
extern void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration); extern void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration);
}
#endif
// a wrapper that support default value of duration void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) {
void tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) _tone(ulPin, frequency, duration);
{
_tone(ulPin, frequency, duration);
} }
#endif void noTone(uint32_t ulPin) {
pinRemoveMode(ulPin);
}

View File

@@ -1,16 +1,14 @@
#include "Arduino.h" #include <Arduino.h>
#include <gpio_api.h>
#ifdef __cplusplus #include <gpio_irq_api.h>
extern "C" { #include <gpio_irq_ex_api.h>
#endif
#include "gpio_api.h"
#include "gpio_irq_api.h"
#include "gpio_irq_ex_api.h"
extern void *gpio_pin_struct[PINS_COUNT]; extern void *gpio_pin_struct[PINS_COUNT];
extern void *gpio_irq_handler_list[PINS_COUNT]; extern void *gpio_irq_handler_list[PINS_COUNT];
extern bool pinInvalid(pin_size_t pinNumber);
extern void pinRemoveMode(pin_size_t pinNumber);
void gpioIrqHandler(uint32_t id, gpio_irq_event event) { void gpioIrqHandler(uint32_t id, gpio_irq_event event) {
if (gpio_irq_handler_list[id] != NULL) { if (gpio_irq_handler_list[id] != NULL) {
((void (*)(uint32_t, uint32_t))gpio_irq_handler_list[id])(id, (uint32_t)event); ((void (*)(uint32_t, uint32_t))gpio_irq_handler_list[id])(id, (uint32_t)event);
@@ -36,7 +34,7 @@ void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus
if (g_APinDescription[interruptNumber].ulPinType == NOT_INITIAL) { if (g_APinDescription[interruptNumber].ulPinType == NOT_INITIAL) {
// allocate memory if pin not used before // allocate memory if pin not used before
gpio = malloc(sizeof(gpio_irq_t)); gpio = malloc(sizeof(gpio_irq_t));
gpio_pin_struct[interruptNumber] = gpio; gpio_pin_struct[interruptNumber] = gpio;
gpio_irq_init(gpio, g_APinDescription[interruptNumber].pinname, gpioIrqHandler, interruptNumber); gpio_irq_init(gpio, g_APinDescription[interruptNumber].pinname, gpioIrqHandler, interruptNumber);
g_APinDescription[interruptNumber].ulPinType = PIO_GPIO_IRQ; g_APinDescription[interruptNumber].ulPinType = PIO_GPIO_IRQ;
@@ -78,7 +76,3 @@ void detachInterrupt(pin_size_t interruptNumber) {
} }
gpio_irq_handler_list[interruptNumber] = NULL; gpio_irq_handler_list[interruptNumber] = NULL;
} }
#ifdef __cplusplus
}
#endif

View File

@@ -1,77 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
#include "api/Common.h"
extern "C" {
extern void rtl_srandom( uint32_t seed );
extern uint32_t rtl_random( void );
}
#ifndef srand
#define srand rtl_srandom
#endif
#ifndef rand
#define rand rtl_random
#endif
extern void randomSeed( uint32_t dwSeed )
{
if ( dwSeed != 0 )
{
srand( dwSeed ) ;
}
}
extern long random( long howbig )
{
if ( howbig == 0 )
{
return 0 ;
}
return rand() % howbig;
}
extern long random( long howsmall, long howbig )
{
if (howsmall >= howbig)
{
return howsmall;
}
long diff = howbig - howsmall;
return random(diff) + howsmall;
}
extern long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
extern uint16_t makeWord( uint16_t w )
{
return w ;
}
extern uint16_t makeWord( uint8_t h, uint8_t l )
{
return (h << 8) | l ;
}

View File

@@ -1,118 +1,60 @@
#pragma once #pragma once
#include "wiring_os.h" #include <stdarg.h>
#include "wiring_watchdog.h" #include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <stddef.h> #include "sdk_extern.h"
#include <stdint.h> #include "sdk_mem.h"
#include <stdarg.h> #include "sdk_os.h"
#define boolean boolean_rtl
#include "rtl_lib.h"
#include "rand.h"
#include "rt_lib_rom.h"
#undef boolean
/* moved from Arduino.h */ #define NOT_INITIAL (1UL << 0)
/* #define PIO_GPIO (1UL << 1)
* \brief Set CPU CLK 166MHz #define PIO_PWM (1UL << 2)
* clk : 0 - 166666666 Hz, 1 - 83333333 Hz, 2 - 41666666 Hz, 3 - 20833333 Hz, 4 - 10416666 Hz, 5 - 4000000 Hz #define PIO_I2C (1UL << 3)
* baud: 38400,... #define PIO_ADC (1UL << 4)
*/ #define PIO_DAC (1UL << 5)
extern void Init_CPU_CLK_UART(int clkn, int baud); #define PIO_GPIO_IRQ (1UL << 6)
extern void sys_info(void);
/* HalGetChipId:
* 0xff - RTL8711AM, 0xfe - RTL8195AM, 0xfd - RTL8711AF,
* 0xfc - RTL8710AF, 0xfb - RTL8711AN, 0xfa - RTL8710AM */
extern unsigned char HalGetChipId(void);
extern unsigned int HalGetCpuClk(void);
extern void wait_us(int us);
#define delay_us wait_us
extern void yield(void);
extern void *pvPortMalloc(size_t xWantedSize);
extern void *pvPortZalloc(size_t size);
extern void *pvPortCalloc(size_t nmemb, size_t size);
extern void *pvPortReAlloc(void *pv, size_t xWantedSize);
extern void vPortFree(void *pv);
#define malloc pvPortMalloc
#define zalloc pvPortZalloc
#define calloc pvPortCalloc
#define realloc pvPortReAlloc
#define free vPortFree
#ifndef printf
#define printf rtl_printf
#endif
#ifndef sprintf
#define sprintf rtl_sprintf
#endif
#define NOT_INITIAL (1UL<<0)
#define PIO_GPIO (1UL<<1)
#define PIO_PWM (1UL<<2)
#define PIO_I2C (1UL<<3)
#define PIO_ADC (1UL<<4)
#define PIO_DAC (1UL<<5)
#define PIO_GPIO_IRQ (1UL<<6)
#define PWM_MODE_ENABLED 1 #define PWM_MODE_ENABLED 1
#define PWM_MODE_DISABLED 0 #define PWM_MODE_DISABLED 0
/* Types used for the tables below */ #define DEFAULT 1
typedef struct _PinDescription
{
// HW PinNames
uint32_t pinname;
// Current Pin Type
uint32_t ulPinType;
// Supported Pin Function
uint32_t ulPinAttribute;
// Current Pin Mode
uint32_t ulPinMode;
} PinDescription ;
/* Pins table to be instanciated into variant.cpp */
extern PinDescription g_APinDescription[];
extern bool pinInvalid(pin_size_t pinNumber);
extern void pinRemoveMode(pin_size_t pinNumber);
/* moved from wiring_digital.h */
/**************************** Extend API by RTK ***********************************/
extern uint32_t digitalPinToPort( uint32_t pinNumber );
extern uint32_t digitalPinToBitMask( uint32_t pinNumber );
/* moved from wiring_analog.h */
/*
* \brief Set the resolution of analogRead return values. Default is 10 bits (range from 0 to 1023).
*
* \param res
*/
extern void analogReadResolution(int res);
/*
* \brief Set the resolution of analogWrite parameters. Default is 8 bits (range from 0 to 255).
*
* \param res
*/
extern void analogWriteResolution(int res);
extern void analogOutputInit( void ) ;
extern void analogWritePeriod(int us);
/* moved from wiring_constants.h */
#define DEFAULT 1
#define EXTERNAL 0 #define EXTERNAL 0
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) #define round(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x)-0.5))
extern uint32_t ulPortSetInterruptMask( void ); typedef enum _eAnalogReference {
extern void vPortClearInterruptMask( uint32_t ulNewMask ); AR_DEFAULT,
} eAnalogReference;
// Types used for the table below
typedef struct _PinDescription {
// HW PinNames
uint32_t pinname;
// Current Pin Type
uint32_t ulPinType;
// Supported Pin Function
uint32_t ulPinAttribute;
// Current Pin Mode
uint32_t ulPinMode;
} PinDescription;
// Pins table to be instantiated into variant.cpp
extern PinDescription g_APinDescription[];
// Additional Wiring functions
extern uint32_t digitalPinToPort(uint32_t pinNumber);
extern uint32_t digitalPinToBitMask(uint32_t pinNumber);
extern void analogReadResolution(int res);
extern void analogWriteResolution(int res);
extern void analogOutputInit(void);
extern void analogWritePeriod(int us);
extern void wait_for_debug();
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

View File

@@ -1,100 +0,0 @@
/*
* WebSocketClient + SSL/TSL
* RTL8710AF pvvx 12/12/2016
*
*/
#include "Arduino.h"
#include "WebSocketClient.h"
WebSocketClient::WebSocketClient() {
}
WebSocketClient::WebSocketClient(char *url, int port, char *path, char* origin) {
client = create_wsclient(url, port, path, origin);
}
WebSocketClient::~WebSocketClient() {
close();
}
int WebSocketClient::begin(char *url, int port, char *path, char* origin) {
client = create_wsclient(url, port, path, origin);
if(client != NULL) return 1;
else return 0;
}
int WebSocketClient::connect() {
if(client != NULL) return ws_connect_url(client);
else return -1;
}
void WebSocketClient::send(char* message, int message_len, int use_mask) {
if(client != NULL && client->readyState > CLOSED) {
ws_send(message, message_len, use_mask, client);
ws_poll(0, client);
}
}
void WebSocketClient::sendBinary(uint8_t* message, int message_len, int use_mask) {
if(client != NULL && client->readyState > CLOSED) {
ws_sendBinary(message, message_len, use_mask, client);
ws_poll(0, client);
}
}
void WebSocketClient::sendPing() {
if(client != NULL && client->readyState > CLOSED) {
ws_sendPing(client);
ws_poll(0, client);
}
}
void WebSocketClient::poll(int timeout) {
if(client != NULL) ws_poll(timeout, client);
}
readyStateValues WebSocketClient::getReadyState() {
if(client != NULL) return ws_getReadyState(client);
else return CLOSED;
}
void WebSocketClient::dispatch(void (*callback)(wsclient_context *, int))
{
if(client != NULL) ws_dispatch(callback);
}
void WebSocketClient::close() {
if(client != NULL) {
ws_close(client);
if(client->ssl) {
free(client->ssl);
client->ssl = NULL;
}
client = NULL;
}
}
extern "C" void set_ssl_func(wsclient_context *wsclient); // in example_wsclient.c
void WebSocketClient::ssl_func_on(void)
{
set_ssl_func(client);
/*
client->fun_ops.ssl_fun_ops.memory_set_own = memory_set_own;
client->fun_ops.ssl_fun_ops.net_connect = net_connect;
client->fun_ops.ssl_fun_ops.ssl_init = ssl_init;
client->fun_ops.ssl_fun_ops.ssl_set_endpoint = ssl_set_endpoint;
client->fun_ops.ssl_fun_ops.ssl_set_authmode = ssl_set_authmode;
client->fun_ops.ssl_fun_ops.ssl_set_rng = ssl_set_rng;
client->fun_ops.ssl_fun_ops.ssl_set_bio = ssl_set_bio;
client->fun_ops.ssl_fun_ops.ssl_handshake = ssl_handshake;
client->fun_ops.ssl_fun_ops.net_close = net_close;
client->fun_ops.ssl_fun_ops.ssl_free = ssl_free;
client->fun_ops.ssl_fun_ops.ssl_read = ssl_read;
client->fun_ops.ssl_fun_ops.ssl_write = ssl_write;
client->fun_ops.ssl_fun_ops.net_recv = net_recv;
client->fun_ops.ssl_fun_ops.net_send = net_send;
client->ssl = (void *)zalloc(sizeof(struct _ssl_context)); // 380 bytes
*/
}

View File

@@ -1,116 +0,0 @@
/*
* WebSocketClient + SSL/TSL
* RTL8710AF pvvx 12/12/2016
*
*/
#ifndef WEBSOCKETCLIENT_H_
#define WEBSOCKETCLIENT_H_
#include <Arduino.h>
extern "C" {
#include "libwsclient.h"
#include "wsclient_api.h"
}
class WebSocketClient {
public:
WebSocketClient();
~WebSocketClient();
/*************************************************************************************************
** Function Name : begin
** Description : Creating the websocket client context structure
** Input : url:websocket server's url
** port:websocket server's port, if not given, default 80 for "ws", 443 for "wss"
** origin: the address or url of your self
** Return : Ok: 1
** Failed: 0
**************************************************************************************************/
WebSocketClient(char *url, int port, char *path = NULL, char* origin = NULL);
int begin(char *url, int port, char *path = NULL, char* origin = NULL);
/*************************************************************************************************
** Function Name : connect
** Description : Connecting to the websocket server
** Input : wsclient: the websocket client context created by create_wsclientfunction
** Return : Connected: the socket value
** Failed: -1
**************************************************************************************************/
int connect();
/*************************************************************************************************
** Function Name : ws_send
** Description : Create the sending string data and copy to tx_buf
** Input : message: the string that send to server(cannot exceeding the MAX_DATA_LEN
** message_len: the length of the string
** use_mask: 0/1; 1 means using mask for bynary
** wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void send(char* message, int message_len, int use_mask);
/*************************************************************************************************
** Function Name : sendBinary
** Description : Create the sending binary data and copy to tx_buf
** Input : message: the binary that send to server(cannot exceeding the MAX_DATA_LEN
** message_len: the length of the binary
** use_mask: 0/1; 1 means using mask for bynary
** wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void sendBinary(uint8_t* message, int message_len, int use_mask);
/*************************************************************************************************
** Function Name : sendPing
** Description : Sending Ping to websocket server
** Input : wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void sendPing();
/*************************************************************************************************
** Function Name : poll
** Description : Receicing data from server and send the data in tx_buf
** Input : timeout(in milliseconds)
wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void poll(int timeout);
/*************************************************************************************************
** Function Name : dispatch
** Description : callback function when getting message from server
** Input : function that resolve the message received and the message length
** Return : None
**************************************************************************************************/
void dispatch(void (*callback)(wsclient_context *, int));
/*************************************************************************************************
** Function Name : getReadyState
** Description : Getting the connection status
** Input : wsclient: the websocket client context
** Return : readyStateValues(4 types: CLOSING, CLOSED, CONNECTING, OPEN )
**************************************************************************************************/
readyStateValues getReadyState();
/*************************************************************************************************
** Function Name : close
** Description : Closing the connection with websocket server
** Input : wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void close();
/*************************************************************************************************
** Function Name : ssl_func_on
** Description : Set SSL/TSL function
** Input : None
** Return : None
**************************************************************************************************/
void ssl_func_on(void);
private:
wsclient_context *client;
};
#endif // WEBSOCKETCLIENT_H_

View File

@@ -1,71 +0,0 @@
// Simple Base64 code
// (c) Copyright 2010 MCQN Ltd.
// Released under Apache License, version 2.0
#include "b64.h"
/* Simple test program
#include <stdio.h>
void main()
{
char* in = "amcewen";
char out[22];
b64_encode(in, 15, out, 22);
out[21] = '\0';
printf(out);
}
*/
int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen)
{
// Work out if we've got enough space to encode the input
// Every 6 bits of input becomes a byte of output
if (aOutputLen < (aInputLen*8)/6)
{
// FIXME Should we return an error here, or just the length
return (aInputLen*8)/6;
}
// If we get here we've got enough space to do the encoding
const char* b64_dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if (aInputLen == 3)
{
aOutput[0] = b64_dictionary[aInput[0] >> 2];
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)];
aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2|(aInput[2]>>6)];
aOutput[3] = b64_dictionary[aInput[2]&0x3F];
}
else if (aInputLen == 2)
{
aOutput[0] = b64_dictionary[aInput[0] >> 2];
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)];
aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2];
aOutput[3] = '=';
}
else if (aInputLen == 1)
{
aOutput[0] = b64_dictionary[aInput[0] >> 2];
aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4];
aOutput[2] = '=';
aOutput[3] = '=';
}
else
{
// Break the input into 3-byte chunks and process each of them
int i;
for (i = 0; i < aInputLen/3; i++)
{
b64_encode(&aInput[i*3], 3, &aOutput[i*4], 4);
}
if (aInputLen % 3 > 0)
{
// It doesn't fit neatly into a 3-byte chunk, so process what's left
b64_encode(&aInput[i*3], aInputLen % 3, &aOutput[i*4], aOutputLen - (i*4));
}
}
}

View File

@@ -1,7 +0,0 @@
#ifndef b64_h
#define b64_h
int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen);
#endif

View File

@@ -1,26 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
extern "C" void __cxa_pure_virtual(void) ;
/* We compile with nodefaultlibs, so we need to provide an error
* handler for an empty pure virtual function */
extern "C" void __cxa_pure_virtual(void) {
while(1)
;
}

View File

@@ -1,36 +0,0 @@
#ifndef ARD_DEBUG_H
#define ARD_DEBUG_H
#include <stddef.h>
#include <stdint.h>
#ifdef DEBUG_RTL_CORE
#define DEBUGV(...) rtl_printf(__VA_ARGS__)
#endif
#ifndef DEBUGV
#define DEBUGV(...)
#endif
#ifdef __cplusplus
void hexdump(void * addr, int len = 16);
#else
void hexdump(void * addr, int len);
#endif
#ifdef __cplusplus
extern "C" {
#endif
void print_udp_pcb(void);
void print_tcp_pcb(void);
void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70 !
void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn));
#define panic() __panic_func(__FILE__, __LINE__, __func__)
#ifdef __cplusplus
}
#endif
#endif//ARD_DEBUG_H

View File

@@ -1,116 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "api/itoa.h"
#include <string.h>
#ifdef __cplusplus
extern "C"{
#endif // __cplusplus
extern char* ltoa(long value, char *string, int radix)
{
char tmp[33];
char *tp = tmp;
long i;
unsigned long v;
int sign;
char *sp;
if ( string == NULL )
{
return 0 ;
}
if (radix > 36 || radix <= 1)
{
return 0 ;
}
sign = (radix == 10 && value < 0);
if (sign)
{
v = -value;
}
else
{
v = (unsigned long)value;
}
while (v || tp == tmp)
{
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i+'0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
if (sign)
*sp++ = '-';
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
extern char* ultoa(unsigned long value, char *string, int radix)
{
char tmp[33];
char *tp = tmp;
long i;
unsigned long v = value;
char *sp;
if ( string == NULL )
{
return 0;
}
if (radix > 36 || radix <= 1)
{
return 0;
}
while (v || tp == tmp)
{
i = v % radix;
v = v / radix;
if (i < 10)
*tp++ = i+'0';
else
*tp++ = i + 'a' - 10;
}
sp = string;
while (tp > tmp)
*sp++ = *--tp;
*sp = 0;
return string;
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View File

@@ -1,120 +0,0 @@
#ifndef EASYWSCLIENT_H
#define EASYWSCLIENT_H
#include <platform/platform_stdlib.h>
/****************Define the debug message level*********************/
#define DEBUG_WSCLIENT 1
#define WSCLIENT_LOG(level, fmt, ...) printf("\n\r[WSCLIENT %s] %s: " fmt "\n", level, __FUNCTION__, ##__VA_ARGS__)
#if DEBUG_WSCLIENT == 2
#define WSCLIENT_DEBUG(fmt, ...) WSCLIENT_LOG("DEBUG", fmt, ##__VA_ARGS__)
#else
#define WSCLIENT_DEBUG(fmt, ...)
#endif
#if DEBUG_WSCLIENT
#define WSCLIENT_ERROR(fmt, ...) WSCLIENT_LOG("ERROR", fmt, ##__VA_ARGS__)
#else
#define WSCLIENT_ERROR(fmt, ...)
#endif
/*******************************************************************/
/****************Define the structures used*************************/
typedef enum{
CLOSING,
CLOSED,
CONNECTING,
OPEN
} readyStateValues;
struct wsheader_type{
unsigned header_size;
int fin;
int mask;
enum opcode_type {
CONTINUATION = 0x0,
TEXT_FRAME = 0x1,
BINARY_FRAME = 0x2,
CLOSE = 8,
PING = 9,
PONG = 0xa,
} opcode;
int N0;
uint64_t N;
uint8_t masking_key[4];
};
struct _wsclient_context;
struct _ssl_context;
struct ssl_fun_ops{
int (*memory_set_own)( void * (*malloc_func)( size_t ),void (*free_func)( void * ) );
int (*net_connect)( int *fd, const char *host, int port );
int (*ssl_init)( struct _ssl_context *ssl );
void (*ssl_set_endpoint)( struct _ssl_context *ssl, int endpoint );
void (*ssl_set_authmode)( struct _ssl_context *ssl, int authmode );
void (*ssl_set_rng)( struct _ssl_context *ssl,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
void (*ssl_set_bio)( struct _ssl_context *ssl,
int (*f_recv)(void *, unsigned char *, size_t), void *p_recv,
int (*f_send)(void *, const unsigned char *, size_t), void *p_send );
int (*ssl_handshake)( struct _ssl_context *ssl );
void (*net_close)( int fd );
void (*ssl_free)( struct _ssl_context *ssl );
int (*ssl_read)( struct _ssl_context *ssl, unsigned char *buf, size_t len );
int (*ssl_write)( struct _ssl_context *ssl, const unsigned char *buf, size_t len );
const char *(*ssl_get_ciphersuite)( const struct _ssl_context *ssl );
int (*net_recv)( void *ctx, unsigned char *buf, size_t len );
int (*net_send)( void *ctx, const unsigned char *buf, size_t len );
};
struct ws_fun_ops{
int (*hostname_connect)(struct _wsclient_context *wsclient);
void (*client_close)(struct _wsclient_context *wsclient);
int (*client_send)(struct _wsclient_context *wsclient, unsigned char *data, size_t data_len);
int (*client_read)(struct _wsclient_context *wsclient, unsigned char *data, size_t data_len);
struct ssl_fun_ops ssl_fun_ops;
};
typedef struct _wsclient_context{
char host[128];
char path[128];
char origin[200];
int port;
uint8_t use_ssl;
int sockfd;
readyStateValues readyState;
int tx_len;
void *ssl;
uint8_t *txbuf;
uint8_t *rxbuf;
uint8_t *receivedData;
struct ws_fun_ops fun_ops;
}wsclient_context;
/*******************************************************************/
/****************General functions used by wsclient*****************/
void ws_get_random_bytes(void *buf, size_t len);
void* ws_malloc(unsigned int size);
void ws_free(void *buf);
int ws_client_handshake(wsclient_context *wsclient);
int ws_check_handshake(wsclient_context *wsclient);
void ws_sendData(uint8_t type, size_t message_size, uint8_t* message, int useMask, wsclient_context *wsclient);
/*******************************************************************/
/*************Functions used by wsclient without SSL****************/
int ws_hostname_connect(wsclient_context *wsclient);
int ws_client_read(wsclient_context *wsclient, unsigned char *data, size_t data_len);
int ws_client_send(wsclient_context *wsclient, unsigned char *data, size_t data_len);
void ws_client_close(wsclient_context *wsclient);
/*******************************************************************/
/***************Functions used by wsclient with SSL*****************/
int wss_hostname_connect(wsclient_context *wsclient);
int wss_client_read(wsclient_context *wsclient, unsigned char *data, size_t data_len);
int wss_client_send(wsclient_context *wsclient, unsigned char *data, size_t data_len);
void wss_client_close(wsclient_context *wsclient);
/*******************************************************************/
#endif

View File

@@ -1,122 +0,0 @@
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "lwip_netconf.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include "lwip/err.h"
//#include "lwip/mem.h"
#include "lwip/tcp.h"
#include "lwip/tcp_impl.h"
#include "lwip/udp.h"
/* Get one byte from the 4-byte address */
#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3])
/* These are cast to u16_t, with the intent that they are often arguments
* to printf using the U16_F format from cc.h. */
#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
#define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \
ip4_addr2_16(ipaddr), \
ip4_addr3_16(ipaddr), \
ip4_addr4_16(ipaddr)
#define IPSTR "%d.%d.%d.%d"
extern const char * const tcp_state_str[];
/*
static const char * const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
*/
/******************************************************************************
* FunctionName : debug
* Parameters :
* Returns :
*******************************************************************************/
void print_udp_pcb(void)
{
struct udp_pcb *pcb;
bool prt_none = true;
rtl_printf("UDP pcbs:\n");
for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("flg:%02x\t" IPSTR ":%d\t" IPSTR ":%d\trecv:%p\n", pcb->flags, IP2STR(&pcb->local_ip), pcb->local_port, IP2STR(&pcb->remote_ip), pcb->remote_port, pcb->recv );
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
}
/******************************************************************************
* FunctionName : debug
* Parameters :
* Returns :
*******************************************************************************/
void print_tcp_pcb(void)
{
struct tcp_pcb *pcb;
rtl_printf("Active PCB states:\n");
bool prt_none = true;
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
rtl_printf("Listen PCB states:\n");
prt_none = true;
for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
rtl_printf("TIME-WAIT PCB states:\n");
prt_none = true;
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]);
prt_none = false;
}
if(prt_none) rtl_printf("none\n");
}
/******************************************************************************
* FunctionName : debug
* Parameters :
* Returns :
*******************************************************************************/
#if 0
//------------------------------------------------------------------------------
void chow_tcp_connection_info(void)
{
rtl_printf("TCP Server connections:\n");
TCP_SERV_CFG * p;
TCP_SERV_CONN * ts_conn;
bool prt_none = true;
for(p = phcfg; p != NULL; p = p->next) {
for(ts_conn = p->conn_links; ts_conn != NULL; ts_conn = ts_conn->next) {
rtl_printf("%d "IPSTR ":%d %s\n", p->port, ts_conn->remote_ip.b[0], ts_conn->remote_ip.b[1], ts_conn->remote_ip.b[2], ts_conn->remote_ip.b[3], ts_conn->remote_port, tspsrv_srvconn_state_msg(ts_conn->state) );
prt_none = false;
}
}
if(prt_none) rtl_printf("none\n");
}
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -18,87 +18,62 @@
*/ */
#define ARDUINO_MAIN #define ARDUINO_MAIN
#include "Arduino.h"
#include "cmsis_os.h"
#ifdef __cplusplus #include <Arduino.h>
extern "C" { #include <cmsis_os.h>
#endif // __cplusplus
#include "rtl8710b.h"
#include "FreeRTOS.h"
#include "task.h"
#include "diag.h"
extern void HalCpuClkConfig(u8 CpuType);
extern void SystemCoreClockUpdate(void);
extern void En32KCalibration(void);
extern int tcm_heap_freeSpace(void);
extern void console_init(void);
#ifdef __cplusplus
} // extern "C"
#endif
osThreadId main_tid = 0;
// Weak empty variant initialization function. // Weak empty variant initialization function.
// May be redefined by variant files. // May be redefined by variant files.
void initVariant() __attribute__((weak)); void initVariant() __attribute__((weak));
void initVariant() { }
void initVariant() {}
// Initialize C library // Initialize C library
extern "C" void __libc_init_array(void); extern "C" void __libc_init_array(void);
/* osThreadId main_tid = 0;
* \brief Init Random()
* \note Use in void __low_level_init(void) { Init_Rand(); } ! void main_task(const void *arg) {
*/ setup();
void Init_Rand(void)
{ for (;;) {
extern u32 _rand_z1, _rand_z2, _rand_z3, _rand_z4, _rand_first; loop();
u32 *p = (u32 *)0x1FFFFF00; if (serialEventRun)
while(p < (u32 *)0x20000000) _rand_z1 ^= *p++; serialEventRun();
_rand_z1 ^= (*((u32 *)0x40002018) << 24) ^ (*((u32 *)0x40002118) << 16) ^ (*((u32 *)0x40002218) << 8) ^ *((u32 *)0x40002318); yield();
_rand_z2 = ((_rand_z1 & 0x007F00FF) << 7) ^ ((_rand_z1 & 0x0F80FF00) >> 8); }
_rand_z3 = ((_rand_z2 & 0x007F00FF) << 7) ^ ((_rand_z2 & 0x0F80FF00) >> 8);
_rand_z4 = ((_rand_z3 & 0x007F00FF) << 7) ^ ((_rand_z3 & 0x0F80FF00) >> 8);
_rand_first = 1;
} }
/* int main(void) {
* \brief handle sketch LT_BANNER();
*/ init();
void main_task( void const *arg )
{
setup();
for (;;) __libc_init_array();
{
loop(); initVariant();
if (serialEventRun) serialEventRun();
yield(); osThreadDef(main_task, osPriorityRealtime, 1, 4096 * 4);
} main_tid = osThreadCreate(osThread(main_task), NULL);
osKernelStart();
while (1)
;
return 0;
} }
void serialEvent() __attribute__((weak));
bool Serial_available() __attribute__((weak));
/* void serialEventRun(void) {
* \brief Main entry point of Arduino application if (Serial_available && serialEvent && Serial_available())
*/ serialEvent();
int main( void ) }
{
LT_BANNER(); void wait_for_debug() {
init(); while (((CoreDebug->DHCSR) & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
asm("nop");
__libc_init_array(); }
delay(1000);
initVariant();
osThreadDef(main_task, osPriorityRealtime, 1, 4096*4);
main_tid = osThreadCreate(osThread (main_task), NULL);
osKernelStart();
while(1);
return 0;
} }

View File

@@ -1,29 +0,0 @@
#ifndef __NETBIOS_H__
#define __NETBIOS_H__
/** default port number for "NetBIOS Name service */
#define NETBIOS_PORT 137
/** size of a NetBIOS name */
#define NETBIOS_NAME_LEN 16
#ifndef NET_IF_NUM
#define NET_IF_NUM 2
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern char netbios_name[NET_IF_NUM][NETBIOS_NAME_LEN + 1]; // default netifs/interfacenum: 0 - SoftAP, 1 - Station, 2 - Ethernet
// struct udp_pcb * netbios_pcb(void);
void netbios_init(void);
bool netbios_set_name(unsigned char interfacenum, char * name); // default netifs/interfacenum: 0 - SoftAP, 1 - Station, 2 - Ethernet
bool netbios_off(void);
#ifdef __cplusplus
}
#endif
#endif /* __NETBIOS_H__ */

View File

@@ -1,88 +0,0 @@
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <assert.h>
#include <debug.h>
#include <Arduino.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <diag.h>
extern void *pvPortMalloc( size_t xWantedSize );
extern size_t xPortGetFreeHeapSize( void );
extern size_t xPortGetMinimumEverFreeHeapSize( void );
extern void *tcm_heap_malloc(int size);
extern void *tcm_heap_calloc(int size);
extern void tcm_heap_free(void * mem);
extern void tcm_heap_dump(void);
extern int tcm_heap_freeSpace(void);
__attribute__((noreturn)) void __panic_func(const char* file, int line, const char* func)
{
DiagPrintf("\r\nPanic: %s, line: %d, %s\r\n");
while(1);
}
#ifdef __cplusplus
} // extern "C"
#endif
void hexdump(void * ptr, int cnt)
{
char * p = (char *) ptr;
int c = cnt;
while(c > 64) {
DumpForOneBytes((void *)p, 64);
p += 64;
c -= 64;
}
if(c != 0) DumpForOneBytes((void *)p, c);
}
void debug_on(void)
{
ConfigDebugErr = -1;
ConfigDebugInfo = -1;
ConfigDebugWarn = -1;
CfgSysDebugErr = -1;
CfgSysDebugInfo = -1;
CfgSysDebugWarn = -1;
}
void sys_info(void) {
rtl_printf("\r\nCLK CPU\t\t%d Hz\r\nRAM heap\t%d bytes\r\nTCM heap\t%d bytes\r\n",
HalGetCpuClk(), xPortGetFreeHeapSize(), tcm_heap_freeSpace());
}
void *pvPortZalloc(size_t size) {
void *pvReturn = pvPortMalloc(size);
if (pvReturn)
memset(pvReturn, 0, size);
return pvReturn;
}
void *pvPortCalloc(size_t nmemb, size_t size) {
return pvPortZalloc(nmemb * size);
}

View File

@@ -0,0 +1,37 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#define boolean boolean_rtl
#include <ameba_soc.h>
#include <gpio_api.h>
#include <rand.h>
#include <rt_lib_rom.h>
#include <rtl_lib.h>
#include <wait_api.h>
#undef boolean
// stdio.h
#define printf rtl_printf
#define sprintf rtl_sprintf
// moved from syscalls.h
#define _close __rtl_close
#define _fstat __rtl_fstat
#define _isatty __rtl_isatty
#define _lseek __rtl_lseek
#define _open __rtl_open
#define _read __rtl_read
#define _write __rtl_write
#define _sbrk __rtl_sbrk
#define delay_us wait_us
extern void wait_us(int us);
extern int LOGUART_SetBaud(uint32_t BaudRate); // from fixups/log_uart.c
extern void DumpForOneBytes(void *addr, int cnt); // cnt max 0x70!
extern void SystemCoreClockUpdate(void);

View File

@@ -0,0 +1,16 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
#include "sdk_mem.h"
#include <Arduino.h> // for memset
void *pvPortZalloc(size_t size) {
void *pvReturn = pvPortMalloc(size);
if (pvReturn)
memset(pvReturn, 0, size);
return pvReturn;
}
void *pvPortCalloc(size_t nmemb, size_t size) {
return pvPortZalloc(nmemb * size);
}

View File

@@ -0,0 +1,19 @@
/* Copyright (c) Kuba Szczodrzyński 2022-05-06. */
#pragma once
#include <stddef.h>
// provide extern functions directly, as callers
// generally don't expect needing to include malloc()
extern void *pvPortMalloc(size_t xWantedSize);
extern void *pvPortZalloc(size_t size);
extern void *pvPortCalloc(size_t nmemb, size_t size);
extern void *pvPortReAlloc(void *pv, size_t xWantedSize);
extern void vPortFree(void *pv);
#define malloc pvPortMalloc
#define zalloc pvPortZalloc
#define calloc pvPortCalloc
#define realloc pvPortReAlloc
#define free vPortFree

View File

@@ -0,0 +1,110 @@
#include "sdk_os.h"
#include <Arduino.h>
#include <cmsis_os.h>
uint32_t os_thread_create(void (*task)(const void *argument), void *argument, int priority, uint32_t stack_size) {
osThreadDef_t thread_def;
thread_def.pthread = task;
thread_def.tpriority = (osPriority)priority;
// the underlying freertos implementation on cmsis os divide stack size by 4
thread_def.stacksize = stack_size * 4;
thread_def.name = "ARDUINO";
return (uint32_t)osThreadCreate(&thread_def, argument);
}
uint32_t os_thread_get_id(void) {
return osThreadGetId();
}
uint32_t os_thread_terminate(uint32_t thread_id) {
return (uint32_t)osThreadTerminate(thread_id);
}
uint32_t os_thread_yield(void) {
return (uint32_t)osThreadYield();
}
uint32_t os_thread_set_priority(uint32_t thread_id, int priority) {
return (uint32_t)osThreadSetPriority(thread_id, (osPriority)priority);
}
int os_thread_get_priority(uint32_t thread_id) {
return (int)osThreadGetPriority(thread_id);
}
int32_t os_signal_set(uint32_t thread_id, int32_t signals) {
return osSignalSet(thread_id, signals);
}
int32_t os_signal_clear(uint32_t thread_id, int32_t signals) {
return osSignalClear(thread_id, signals);
}
os_event_t os_signal_wait(int32_t signals, uint32_t millisec) {
osEvent evt;
os_event_t ret;
evt = osSignalWait(signals, millisec);
ret.status = (uint32_t)evt.status;
ret.value.signals = evt.value.signals;
ret.def.message_id = evt.def.message_id;
return ret;
}
typedef void (*os_ptimer)(const void *argument);
uint32_t os_timer_create(void (*callback)(const void *argument), uint8_t isPeriodic, void *argument) {
osTimerDef_t *pTimerDef;
pTimerDef = (osTimerDef_t *)malloc(sizeof(osTimerDef_t));
pTimerDef->ptimer = callback;
pTimerDef->custom = (struct os_timer_custom *)malloc(sizeof(struct os_timer_custom));
return osTimerCreate(pTimerDef, (isPeriodic ? osTimerPeriodic : osTimerOnce), argument);
}
uint32_t os_timer_start(uint32_t timer_id, uint32_t millisec) {
return osTimerStart(timer_id, millisec);
}
uint32_t os_timer_stop(uint32_t timer_id) {
return osTimerStop(timer_id);
}
uint32_t os_timer_delete(uint32_t timer_id) {
osTimerDef_t *pTimerDef;
pTimerDef = (osTimerDef_t *)pvTimerGetTimerID(timer_id);
free(pTimerDef->custom);
free(pTimerDef);
return osTimerDelete(timer_id);
}
uint32_t os_semaphore_create(int32_t count) {
return (uint32_t)osSemaphoreCreate(NULL, count);
}
int32_t os_semaphore_wait(uint32_t semaphore_id, uint32_t millisec) {
if (osSemaphoreWait((osSemaphoreId)semaphore_id, millisec) == 0) {
return 1;
} else {
return 0;
}
}
uint32_t os_semaphore_release(uint32_t semaphore_id) {
return (uint32_t)osSemaphoreRelease((osSemaphoreId)semaphore_id);
}
uint32_t os_semaphore_delete(uint32_t semaphore_id) {
return (uint32_t)osSemaphoreDelete((osSemaphoreId)semaphore_id);
}

View File

@@ -1,13 +1,4 @@
/** @file wiring_os.h */ #pragma once
/**
* @defgroup wiring_os wiring_os
* OS realted function for thread, signal, software timer, semaphore
* @{
*/
#ifndef _WIRING_OS_H_
#define _WIRING_OS_H_
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -21,33 +12,33 @@ extern "C" {
* @{ * @{
*/ */
/** function completed; no error or event occurred. */ /** function completed; no error or event occurred. */
#define OS_OK 0x00 #define OS_OK 0x00
/** function completed; signal event occurred. */ /** function completed; signal event occurred. */
#define OS_EVENT_SIGNAL 0x08 #define OS_EVENT_SIGNAL 0x08
/** function completed; message event occurred. */ /** function completed; message event occurred. */
#define OS_EVENT_MESSAGE 0x10 #define OS_EVENT_MESSAGE 0x10
/** function completed; mail event occurred. */ /** function completed; mail event occurred. */
#define OS_EVENT_MAIL 0x20 #define OS_EVENT_MAIL 0x20
/** function completed; timeout occurred. */ /** function completed; timeout occurred. */
#define OS_EVENT_TIMEOUT 0x40 #define OS_EVENT_TIMEOUT 0x40
/** parameter error: a mandatory parameter was missing or specified an incorrect object. */ /** parameter error: a mandatory parameter was missing or specified an incorrect object. */
#define OS_ERROR_PARAMETER 0x80 #define OS_ERROR_PARAMETER 0x80
/** resource not available: a specified resource was not available. */ /** resource not available: a specified resource was not available. */
#define OS_ERROR_RESOURCE 0x81 #define OS_ERROR_RESOURCE 0x81
/** resource not available within given time: a specified resource was not available within the timeout period. */ /** resource not available within given time: a specified resource was not available within the timeout period. */
#define OS_ERROR_TIMEOUT_RESOURCE 0xC1 #define OS_ERROR_TIMEOUT_RESOURCE 0xC1
/** not allowed in ISR context: the function cannot be called from interrupt service routines. */ /** not allowed in ISR context: the function cannot be called from interrupt service routines. */
#define OS_ERROR_ISR 0x82 #define OS_ERROR_ISR 0x82
/** function called multiple times from ISR with same object. */ /** function called multiple times from ISR with same object. */
#define OS_ERROR_ISR_RECURSIVE 0x83 #define OS_ERROR_ISR_RECURSIVE 0x83
/** system cannot determine priority or thread has illegal priority. */ /** system cannot determine priority or thread has illegal priority. */
#define OS_ERROR_PRIORITY 0x84 #define OS_ERROR_PRIORITY 0x84
/** system is out of memory: it was impossible to allocate or reserve memory for the operation. */ /** system is out of memory: it was impossible to allocate or reserve memory for the operation. */
#define OS_ERROR_NO_MEMORY 0x85 #define OS_ERROR_NO_MEMORY 0x85
/** value of a parameter is out of range. */ /** value of a parameter is out of range. */
#define OS_ERROR_VALUE 0x86 #define OS_ERROR_VALUE 0x86
/** unspecified RTOS error: run-time error but no other error message fits. */ /** unspecified RTOS error: run-time error but no other error message fits. */
#define OS_ERROR_OS 0xFF #define OS_ERROR_OS 0xFF
/** @} */ // end of group os_status /** @} */ // end of group os_status
/** /**
@@ -56,19 +47,19 @@ extern "C" {
* @{ * @{
*/ */
/** priority: idle (lowest) */ /** priority: idle (lowest) */
#define OS_PRIORITY_IDLE (-3) #define OS_PRIORITY_IDLE (-3)
/** priority: low */ /** priority: low */
#define OS_PRIORITY_LOW (-2) #define OS_PRIORITY_LOW (-2)
/** priority: below normal */ /** priority: below normal */
#define OS_PRIORITY_BELOW_NORMAL (-1) #define OS_PRIORITY_BELOW_NORMAL (-1)
/** priority: normal (default) */ /** priority: normal (default) */
#define OS_PRIORITY_NORMAL ( 0) #define OS_PRIORITY_NORMAL (0)
/** priority: above normal */ /** priority: above normal */
#define OS_PRIORITY_ABOVENORMAL (+1) #define OS_PRIORITY_ABOVENORMAL (+1)
/** priority: high */ /** priority: high */
#define OS_PRIORITY_HIGH (+2) #define OS_PRIORITY_HIGH (+2)
/** priority: realtime (highest) */ /** priority: realtime (highest) */
#define OS_PRIORITY_REALTIME (+3) #define OS_PRIORITY_REALTIME (+3)
/** @} */ // end of group os_priority /** @} */ // end of group os_priority
#ifndef DEFAULT_STACK_SIZE #ifndef DEFAULT_STACK_SIZE
@@ -87,16 +78,18 @@ extern "C" {
* Redefine osEvent in cmsis_os.h * Redefine osEvent in cmsis_os.h
*/ */
typedef struct { typedef struct {
uint32_t status; ///< status code: event or error information uint32_t status; ///< status code: event or error information
union {
uint32_t v; ///< message as 32-bit value union {
void *p; ///< message or mail as void pointer uint32_t v; ///< message as 32-bit value
int32_t signals; ///< signal flags void *p; ///< message or mail as void pointer
} value; ///< event value int32_t signals; ///< signal flags
union { } value; ///< event value
void *mail_id; ///< mail id obtained by osMailCreate
void *message_id; ///< message id obtained by osMessageCreate union {
} def; ///< event definition void *mail_id; ///< mail id obtained by osMailCreate
void *message_id; ///< message id obtained by osMessageCreate
} def; ///< event definition
} os_event_t; } os_event_t;
/** /**
@@ -108,12 +101,14 @@ typedef struct {
* @ingroup thread_management * @ingroup thread_management
* @brief Create a thread and add it to Active Threads and set it to state READY. * @brief Create a thread and add it to Active Threads and set it to state READY.
* *
* @param[in] task Function pointer which is the thread body. It should not run into the end of function unless os_thread_terminate is invoked * @param[in] task Function pointer which is the thread body. It should not run into the end of function unless
* os_thread_terminate is invoked
* @param[in] argument the data pointer which brings to task * @param[in] argument the data pointer which brings to task
* @param[in] priority The underlying os is FreeRTOS. It executes tasks with highest priority which are not in idle state.\n * @param[in] priority The underlying os is FreeRTOS. It executes tasks with highest priority which are not in idle
* If there are more than 2 tasks to be executed, then they share the time slice. * state.\n If there are more than 2 tasks to be executed, then they share the time slice.
* @param[in] stack_size The stack_size is used as memory heap only for this task. \n * @param[in] stack_size The stack_size is used as memory heap only for this task. \n
* The local variables and call stacks would occupy this heap. Please make sure the the stack_size is big enough to avoid curroption * The local variables and call stacks would occupy this heap. Please make sure the the stack_size is big enough to
* avoid curroption
* @return The thread id which is used in thread operation and signaling. * @return The thread id which is used in thread operation and signaling.
*/ */
extern uint32_t os_thread_create(void (*task)(const void *argument), void *argument, int priority, uint32_t stack_size); extern uint32_t os_thread_create(void (*task)(const void *argument), void *argument, int priority, uint32_t stack_size);
@@ -124,7 +119,7 @@ extern uint32_t os_thread_create(void (*task)(const void *argument), void *argum
* *
* @return Current thread id which calls os_thread_get_id * @return Current thread id which calls os_thread_get_id
*/ */
extern uint32_t os_thread_get_id( void ); extern uint32_t os_thread_get_id(void);
/** /**
* @ingroup thread_management * @ingroup thread_management
@@ -141,9 +136,10 @@ extern uint32_t os_thread_terminate(uint32_t thread_id);
* @ingroup thread_management * @ingroup thread_management
* @brief Pass control to next thread that is in state \b READY. * @brief Pass control to next thread that is in state \b READY.
* *
* By default the minimal execution unit is 1 millisecond. In a scenario that if a thread with smaller want to handout execution right to a thread with * By default the minimal execution unit is 1 millisecond. In a scenario that if a thread with smaller want to handout
* higher priority immediately without waiting for the ending of current 1 millisecond, then invoke os_thread_yield can transfer exection right to * execution right to a thread with higher priority immediately without waiting for the ending of current 1 millisecond,
* OS's idle task and check which is the next execution thread. * then invoke os_thread_yield can transfer exection right to OS's idle task and check which is the next execution
* thread.
* *
* @return os_status code * @return os_status code
*/ */
@@ -168,7 +164,7 @@ extern uint32_t os_thread_set_priority(uint32_t thread_id, int priority);
*/ */
extern int os_thread_get_priority(uint32_t thread_id); extern int os_thread_get_priority(uint32_t thread_id);
/** /**
* @defgroup signal_management signal_management * @defgroup signal_management signal_management
* Signaling between threads include set, clear, and wait * Signaling between threads include set, clear, and wait
*/ */
@@ -196,7 +192,7 @@ extern int32_t os_signal_clear(uint32_t thread_id, int32_t signals);
/** /**
* @ingroup signal_management * @ingroup signal_management
* @brief Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread. * @brief Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread.
* *
* @param[in] signals the signals to be wait * @param[in] signals the signals to be wait
* @param[in] millisec the timeout value if no signal comes in. Fill in 0xFFFFFFFF for infinite wait. * @param[in] millisec the timeout value if no signal comes in. Fill in 0xFFFFFFFF for infinite wait.
* @return os_status code * @return os_status code
@@ -208,14 +204,14 @@ extern os_event_t os_signal_wait(int32_t signals, uint32_t millisec);
* Software timer management include create, start, stop, delete. * Software timer management include create, start, stop, delete.
*/ */
/** /**
* @ingroup timer_management * @ingroup timer_management
* @brief specify timer type that invoke only once * @brief specify timer type that invoke only once
*/ */
#define OS_TIMER_ONCE (0) #define OS_TIMER_ONCE (0)
/** /**
* @ingroup timer_management * @ingroup timer_management
* @brief specify timer type that invoke periodically * @brief specify timer type that invoke periodically
*/ */
#define OS_TIMER_PERIODIC (1) #define OS_TIMER_PERIODIC (1)
@@ -229,7 +225,7 @@ extern os_event_t os_signal_wait(int32_t signals, uint32_t millisec);
* @param[in] argument The argument that is bring into callback function * @param[in] argument The argument that is bring into callback function
* @return timer id * @return timer id
*/ */
extern uint32_t os_timer_create(void (*callback)(void const *argument), uint8_t isPeriodic, void *argument); extern uint32_t os_timer_create(void (*callback)(const void *argument), uint8_t isPeriodic, void *argument);
/** /**
* @ingroup timer_management * @ingroup timer_management
@@ -239,7 +235,7 @@ extern uint32_t os_timer_create(void (*callback)(void const *argument), uint8_t
* @param[in] millisec The delays after timer starts * @param[in] millisec The delays after timer starts
* @return os_status code * @return os_status code
*/ */
extern uint32_t os_timer_start (uint32_t timer_id, uint32_t millisec); extern uint32_t os_timer_start(uint32_t timer_id, uint32_t millisec);
/** /**
* @ingroup timer_management * @ingroup timer_management
@@ -248,7 +244,7 @@ extern uint32_t os_timer_start (uint32_t timer_id, uint32_t millisec);
* @param[in] timer_id The timer id obtained from by os_timer_create * @param[in] timer_id The timer id obtained from by os_timer_create
* @return os_status code * @return os_status code
*/ */
extern uint32_t os_timer_stop (uint32_t timer_id); extern uint32_t os_timer_stop(uint32_t timer_id);
/** /**
* @ingroup timer_management * @ingroup timer_management
@@ -304,7 +300,3 @@ extern uint32_t os_semaphore_delete(uint32_t semaphore_id);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif
/** @} */ // end of group wiring_os

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013-2015 Peter Andersson (pelleplutt1976<at>gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,146 +0,0 @@
# SPIFFS (SPI Flash File System)
**V0.3.4**
Copyright (c) 2013-2016 Peter Andersson (pelleplutt1976 at gmail.com)
For legal stuff, see [LICENSE](https://github.com/pellepl/spiffs/blob/master/LICENSE). Basically, you may do whatever you want with the source. Use, modify, sell, print it out, roll it and smoke it - as long as I won't be held responsible.
Love to hear feedback though!
## INTRODUCTION
Spiffs is a file system intended for SPI NOR flash devices on embedded targets.
Spiffs is designed with following characteristics in mind:
- Small (embedded) targets, sparse RAM without heap
- Only big areas of data (blocks) can be erased
- An erase will reset all bits in block to ones
- Writing pulls one to zeroes
- Zeroes can only be pulled to ones by erase
- Wear leveling
## FEATURES
What spiffs does:
- Specifically designed for low ram usage
- Uses statically sized ram buffers, independent of number of files
- Posix-like api: open, close, read, write, seek, stat, etc
- It can be run on any NOR flash, not only SPI flash - theoretically also on embedded flash of an microprocessor
- Multiple spiffs configurations can be run on same target - and even on same SPI flash device
- Implements static wear leveling
- Built in file system consistency checks
What spiffs does not:
- Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path *tmp/myfile.txt* will create a file called *tmp/myfile.txt* instead of a *myfile.txt* under directory *tmp*.
- It is not a realtime stack. One write operation might take much longer than another.
- Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128MB is probably a bad idea. This is a side effect of the design goal to use as little ram as possible.
- Presently, it does not detect or handle bad blocks.
## MORE INFO
See the [wiki](https://github.com/pellepl/spiffs/wiki) for configuring, integrating and using spiffs.
For design, see [docs/TECH_SPEC](https://github.com/pellepl/spiffs/blob/master/docs/TECH_SPEC).
For a generic spi flash driver, see [this](https://github.com/pellepl/spiflash_driver).
## HISTORY
### 0.3.4
- Added user callback file func.
- Fixed a stat bug with obj id.
- SPIFFS_probe_fs added
- Add possibility to compile a read-only version of spiffs
- Make magic dependent on fs length, if needed (see #59 & #66) (thanks @hreintke)
- Exposed SPIFFS_open_by_page_function
- Zero-size file cannot be seek #57 (thanks @lishen2)
- Add tell and eof functions #54 (thanks @raburton)
- Make api string params const #53 (thanks @raburton)
- Preserve user_data during mount() #51 (thanks @rojer)
New API functions:
- `SPIFFS_set_file_callback_func` - register a callback informing about file events
- `SPIFFS_probe_fs` - probe a spi flash trying to figure out size of fs
- `SPIFFS_open_by_page` - open a file by page index
- `SPIFFS_eof` - checks if end of file is reached
- `SPIFFS_tell` - returns current file offset
New config defines:
- `SPIFFS_READ_ONLY`
- `SPIFFS_USE_MAGIC_LENGTH`
### 0.3.3
**Might not be compatible with 0.3.2 structures. See issue #40**
- Possibility to add integer offset to file handles
- Truncate function presumes too few free pages #49
- Bug in truncate function #48 (thanks @PawelDefee)
- Update spiffs_gc.c - remove unnecessary parameter (thanks @PawelDefee)
- Update INTEGRATION docs (thanks @PawelDefee)
- Fix pointer truncation in 64-bit platforms (thanks @igrr)
- Zero-sized files cannot be read #44 (thanks @rojer)
- (More) correct calculation of max_id in obj_lu_find #42 #41 (thanks @lishen2)
- Check correct error code in obj_lu_find_free #41 (thanks @lishen2)
- Moar comments for SPIFFS_lseek (thanks @igrr)
- Fixed padding in spiffs_page_object_ix #40 (thanks @jmattsson @lishen2)
- Fixed gc_quick test (thanks @jmattsson)
- Add SPIFFS_EXCL flag #36
- SPIFFS_close may fail silently if cache is enabled #37
- User data in callbacks #34
- Ignoring SINGLETON build in cache setup (thanks Luca)
- Compilation error fixed #32 (thanks @chotasanjiv)
- Align cand_scores (thanks @hefloryd)
- Fix build warnings when SPIFFS_CACHE is 0 (thanks @ajaybhargav)
New config defines:
- `SPIFFS_FILEHDL_OFFSET`
### 0.3.2
- Limit cache size if too much cache is given (thanks pgeiem)
- New feature - Controlled erase. #23
- SPIFFS_rename leaks file descriptors #28 (thanks benpicco)
- moved dbg print defines in test framework to params_test.h
- lseek should return the resulting offset (thanks hefloryd)
- fixed type on dbg ifdefs
- silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco)
- Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela)
- Cache might writethrough too often #16
- even moar testrunner updates
- Test framework update and some added tests
- Some thoughts for next gen
- Test sigsevs when having too many sectors #13 (thanks alonewolfx2)
- GC might be suboptimal #11
- Fix eternal readdir when objheader at last block, last entry
New API functions:
- `SPIFFS_gc_quick` - call a nonintrusive gc
- `SPIFFS_gc` - call a full-scale intrusive gc
### 0.3.1
- Removed two return warnings, was too triggerhappy on release
### 0.3.0
- Added existing namecheck when creating files
- Lots of static analysis bugs #6
- Added rename func
- Fix SPIFFS_read length when reading beyond file size
- Added reading beyond file length testcase
- Made build a bit more configurable
- Changed name in spiffs from "errno" to "err_code" due to conflicts compiling in mingw
- Improved GC checks, fixed an append bug, more robust truncate for very special case
- GC checks preempts GC, truncate even less picky
- Struct alignment needed for some targets, define in spiffs config #10
- Spiffs filesystem magic, definable in config
New config defines:
- `SPIFFS_USE_MAGIC` - enable or disable magic check upon mount
- `SPIFFS_ALIGNED_OBJECT_INDEX_TABLES` - alignment for certain targets
New API functions:
- `SPIFFS_rename` - rename files
- `SPIFFS_clearerr` - clears last errno
- `SPIFFS_info` - returns info on used and total bytes in fs
- `SPIFFS_format` - formats the filesystem
- `SPIFFS_mounted` - checks if filesystem is mounted

View File

@@ -1,239 +0,0 @@
* USING SPIFFS
TODO
* SPIFFS DESIGN
Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and
for bigger targets with much more ram. Nevertheless, many wise thoughts have
been borrowed from YAFFS when writing spiffs. Kudos!
The main complication writing spiffs was that it cannot be assumed the target
has a heap. Spiffs must go along only with the work ram buffer given to it.
This forces extra implementation on many areas of spiffs.
** SPI flash devices using NOR technology
Below is a small description of how SPI flashes work internally. This is to
give an understanding of the design choices made in spiffs.
SPI flash devices are physically divided in blocks. On some SPI flash devices,
blocks are further divided into sectors. Datasheets sometimes name blocks as
sectors and vice versa.
Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where
blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes
have uniform block sizes, whereas others have non-uniform - the latter meaning
that e.g. the first 16 blocks are 4kB big, and the rest are 64kB.
The entire memory is linear and can be read and written in random access.
Erasing can only be done block- or sectorwise; or by mass erase.
SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
they fail.
A clean SPI flash from factory have all bits in entire memory set to one. A
mass erase will reset the device to this state. Block or sector erasing will
put the all bits in the area given by the sector or block to ones. Writing to a
NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op.
Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010.
This way of "write by nand" is used considerably in spiffs.
Common characteristics of NOR flashes are quick reads, but slow writes.
And finally, unlike NAND flashes, NOR flashes seem to not need any error
correction. They always write correctly I gather.
** Spiffs logical structure
Some terminology before proceeding. Physical blocks/sectors means sizes stated
in the datasheet. Logical blocks and pages is something the integrator choose.
** Blocks and pages
Spiffs is allocated to a part or all of the memory of the SPI flash device.
This area is divided into logical blocks, which in turn are divided into
logical pages. The boundary of a logical block must coincide with one or more
physical blocks. The sizes for logical blocks and logical pages always remain
the same, they are uniform.
Example: non-uniform flash mapped to spiffs with 128kB logical blocks
PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB
+-----------------------+ - - - +-----------------------+
| Block 1 : 16kB | | Block 1 : 128kB |
+-----------------------+ | |
| Block 2 : 16kB | | |
+-----------------------+ | |
| Block 3 : 16kB | | |
+-----------------------+ | |
| Block 4 : 16kB | | |
+-----------------------+ | |
| Block 5 : 64kB | | |
+-----------------------+ - - - +-----------------------+
| Block 6 : 64kB | | Block 2 : 128kB |
+-----------------------+ | |
| Block 7 : 64kB | | |
+-----------------------+ - - - +-----------------------+
| Block 8 : 64kB | | Block 3 : 128kB |
+-----------------------+ | |
| Block 9 : 64kB | | |
+-----------------------+ - - - +-----------------------+
| ... | | ... |
A logical block is divided further into a number of logical pages. A page
defines the smallest data holding element known to spiffs. Hence, if a file
is created being one byte big, it will occupy one page for index and one page
for data - it will occupy 2 x size of a logical page on flash.
So it seems it is good to select a small page size.
Each page has a metadata header being normally 5 to 9 bytes. This said, a very
small page size will make metadata occupy a lot of the memory on the flash. A
page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%.
So it seems it is good to select a big page size.
Also, spiffs uses a ram buffer being two times the page size. This ram buffer
is used for loading and manipulating pages, but it is also used for algorithms
to find free file ids, scanning the file system, etc. Having too small a page
size means less work buffer for spiffs, ending up in more reads operations and
eventually gives a slower file system.
Choosing the page size for the system involves many factors:
- How big is the logical block size
- What is the normal size of most files
- How much ram can be spent
- How much data (vs metadata) must be crammed into the file system
- How fast must spiffs be
- Other things impossible to find out
So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
fret - there is no optimal page size. This varies from how the target will use
spiffs. Use the golden rule:
~~~ Logical Page Size = Logical Block Size / 256 ~~~
This is a good starting point. The final page size can then be derived through
heuristical experimenting for us non-analytical minds.
** Objects, indices and look-ups
A file, or an object as called in spiffs, is identified by an object id.
Another YAFFS rip-off. This object id is a part of the page header. So, all
pages know to which object/file they belong - not counting the free pages.
An object is made up of two types of pages: object index pages and data pages.
Data pages contain the data written by user. Index pages contain metadata about
the object, more specifically what data pages are part of the object.
The page header also includes something called a span index. Let's say a file
is written covering three data pages. The first data page will then have span
index 0, the second span index 1, and the last data page will have span index
2. Simple as that.
Finally, each page header contain flags, telling if the page is used,
deleted, finalized, holds index or data, and more.
Object indices also have span indices, where an object index with span index 0
is referred to as the object index header. This page does not only contain
references to data pages, but also extra info such as object name, object size
in bytes, flags for file or directory, etc.
If one were to create a file covering three data pages, named e.g.
"spandex-joke.txt", given object id 12, it could look like this:
PAGE 0 <things to be unveiled soon>
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA]
<first data page of joke>
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA]
<second data page of joke>
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA]
<some data belonging to object 545, probably not very amusing>
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA]
<third data page of joke>
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX]
obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE]
obj ix: [1 2 4]
Looking in detail at page 5, the object index header page, the object index
array refers to each data page in order, as mentioned before. The index of the
object index array correlates with the data page span index.
entry ix: 0 1 2
obj ix: [1 2 4]
| | |
PAGE 1, DATA, SPAN_IX 0 --------/ | |
PAGE 2, DATA, SPAN_IX 1 --------/ |
PAGE 4, DATA, SPAN_IX 2 --------/
Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on
ram. We cannot keep a dynamic list on the whereabouts of each object index
header so we can find a file fast. There might not even be a heap! But, we do
not want to scan all page headers on the flash to find the object index header.
The first page(s) of each block contains the so called object look-up. These
are not normal pages, they do not have a header. Instead, they are arrays
pointing out what object-id the rest of all pages in the block belongs to.
By this look-up, only the first page(s) in each block must to scanned to find
the actual page which contains the object index header of the desired object.
The object lookup is redundant metadata. The assumption is that it presents
less overhead reading a full page of data to memory from each block and search
that, instead of reading a small amount of data from each page (i.e. the page
header) in all blocks. Each read operation from SPI flash normally contains
extra data as the read command itself and the flash address. Also, depending on
the underlying implementation, other criterions may need to be passed for each
read transaction, like mutexes and such.
The veiled example unveiled would look like this, with some extra pages:
PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...]
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ...
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ...
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ...
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ...
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ...
PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ...
PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ...
PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ...
PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ...
PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ...
PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ...
PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ...
...
Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These
pages are deleted, so this is marked both in page header flags and in the look
up. This is an example where spiffs uses NOR flashes "nand-way" of writing.
As a matter of fact, there are two object id's which are special:
obj id 0 (all bits zeroes) - indicates a deleted page in object look up
obj id 0xff.. (all bits ones) - indicates a free page in object look up
Actually, the object id's have another quirk: if the most significant bit is
set, this indicates an object index page. If the most significant bit is zero,
this indicates a data page. So to be fully correct, page 0 in above example
would look like this:
PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...]
where the asterisk means the msb of the object id is set.
This is another way to speed up the searches when looking for object indices.
By looking on the object id's msb in the object lookup, it is also possible
to find out whether the page is an object index page or a data page.

View File

@@ -1,15 +0,0 @@
* When mending lost pages, also see if they fit into length specified in object index header
SPIFFS2 thoughts
* Instead of exact object id:s in the object lookup tables, use a hash of span index and object id.
Eg. object id xor:ed with bit-reversed span index.
This should decrease number of actual pages that needs to be visited when looking thru the obj lut.
* Logical number of each block. When moving stuff in a garbage collected page, the free
page is assigned the same number as the garbage collected. Thus, object index pages do not have to
be rewritten.
* Steal one page, use as a bit parity page. When starting an fs modification operation, write one bit
as zero. When ending, write another bit as zero. On mount, if number of zeroes in page is uneven, a
check is automatically run.

View File

@@ -1,681 +0,0 @@
/*
* spiffs.h
*
* Created on: May 26, 2013
* Author: petera
*/
#ifndef SPIFFS_H_
#define SPIFFS_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "spiffs_config.h"
#define SPIFFS_OK 0
#define SPIFFS_ERR_NOT_MOUNTED -10000
#define SPIFFS_ERR_FULL -10001
#define SPIFFS_ERR_NOT_FOUND -10002
#define SPIFFS_ERR_END_OF_OBJECT -10003
#define SPIFFS_ERR_DELETED -10004
#define SPIFFS_ERR_NOT_FINALIZED -10005
#define SPIFFS_ERR_NOT_INDEX -10006
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
#define SPIFFS_ERR_FILE_CLOSED -10008
#define SPIFFS_ERR_FILE_DELETED -10009
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
#define SPIFFS_ERR_IS_INDEX -10011
#define SPIFFS_ERR_IS_FREE -10012
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
#define SPIFFS_ERR_INDEX_REF_FREE -10015
#define SPIFFS_ERR_INDEX_REF_LU -10016
#define SPIFFS_ERR_INDEX_REF_INVALID -10017
#define SPIFFS_ERR_INDEX_FREE -10018
#define SPIFFS_ERR_INDEX_LU -10019
#define SPIFFS_ERR_INDEX_INVALID -10020
#define SPIFFS_ERR_NOT_WRITABLE -10021
#define SPIFFS_ERR_NOT_READABLE -10022
#define SPIFFS_ERR_CONFLICTING_NAME -10023
#define SPIFFS_ERR_NOT_CONFIGURED -10024
#define SPIFFS_ERR_NOT_A_FS -10025
#define SPIFFS_ERR_MOUNTED -10026
#define SPIFFS_ERR_ERASE_FAIL -10027
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029
#define SPIFFS_ERR_FILE_EXISTS -10030
#define SPIFFS_ERR_NOT_A_FILE -10031
#define SPIFFS_ERR_RO_NOT_IMPL -10032
#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033
#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034
#define SPIFFS_ERR_PROBE_NOT_A_FS -10035
#define SPIFFS_ERR_INTERNAL -10050
#define SPIFFS_ERR_TEST -10100
// spiffs file descriptor index type. must be signed
typedef s16_t spiffs_file;
// spiffs file descriptor flags
typedef u16_t spiffs_flags;
// spiffs file mode
typedef u16_t spiffs_mode;
// object type
typedef u8_t spiffs_obj_type;
struct spiffs_t;
#if SPIFFS_HAL_CALLBACK_EXTRA
/* spi read call function type */
typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst);
/* spi write call function type */
typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src);
/* spi erase call function type */
typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size);
#else // SPIFFS_HAL_CALLBACK_EXTRA
/* spi read call function type */
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
/* spi write call function type */
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
/* spi erase call function type */
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
#endif // SPIFFS_HAL_CALLBACK_EXTRA
/* file system check callback report operation */
typedef enum {
SPIFFS_CHECK_LOOKUP = 0,
SPIFFS_CHECK_INDEX,
SPIFFS_CHECK_PAGE
} spiffs_check_type;
/* file system check callback report type */
typedef enum {
SPIFFS_CHECK_PROGRESS = 0,
SPIFFS_CHECK_ERROR,
SPIFFS_CHECK_FIX_INDEX,
SPIFFS_CHECK_FIX_LOOKUP,
SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
SPIFFS_CHECK_DELETE_PAGE,
SPIFFS_CHECK_DELETE_BAD_FILE,
} spiffs_check_report;
/* file system check callback function */
#if SPIFFS_HAL_CALLBACK_EXTRA
typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report,
u32_t arg1, u32_t arg2);
#else // SPIFFS_HAL_CALLBACK_EXTRA
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
u32_t arg1, u32_t arg2);
#endif // SPIFFS_HAL_CALLBACK_EXTRA
/* file system listener callback operation */
typedef enum {
/* the file has been created */
SPIFFS_CB_CREATED = 0,
/* the file has been updated or moved to another page */
SPIFFS_CB_UPDATED,
/* the file has been deleted */
SPIFFS_CB_DELETED,
} spiffs_fileop_type;
/* file system listener callback function */
typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix);
#ifndef SPIFFS_DBG
#define SPIFFS_DBG(...) \
print(__VA_ARGS__)
#endif
#ifndef SPIFFS_GC_DBG
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
#endif
#ifndef SPIFFS_CACHE_DBG
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__)
#endif
#ifndef SPIFFS_CHECK_DBG
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__)
#endif
/* Any write to the filehandle is appended to end of the file */
#define SPIFFS_APPEND (1<<0)
/* If the opened file exists, it will be truncated to zero length before opened */
#define SPIFFS_TRUNC (1<<1)
/* If the opened file does not exist, it will be created before opened */
#define SPIFFS_CREAT (1<<2)
/* The opened file may only be read */
#define SPIFFS_RDONLY (1<<3)
/* The opened file may only be writted */
#define SPIFFS_WRONLY (1<<4)
/* The opened file may be both read and writted */
#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY)
/* Any writes to the filehandle will never be cached */
#define SPIFFS_DIRECT (1<<5)
/* If SPIFFS_CREAT and SPIFFS_EXCL are set, SPIFFS_open() shall fail if the file exists */
#define SPIFFS_EXCL (1<<6)
#define SPIFFS_SEEK_SET (0)
#define SPIFFS_SEEK_CUR (1)
#define SPIFFS_SEEK_END (2)
#define SPIFFS_TYPE_FILE (1)
#define SPIFFS_TYPE_DIR (2)
#define SPIFFS_TYPE_HARD_LINK (3)
#define SPIFFS_TYPE_SOFT_LINK (4)
#ifndef SPIFFS_LOCK
#define SPIFFS_LOCK(fs)
#endif
#ifndef SPIFFS_UNLOCK
#define SPIFFS_UNLOCK(fs)
#endif
// phys structs
// spiffs spi configuration struct
typedef struct {
// physical read function
spiffs_read hal_read_f;
// physical write function
spiffs_write hal_write_f;
// physical erase function
spiffs_erase hal_erase_f;
#if SPIFFS_SINGLETON == 0
// physical size of the spi flash
u32_t phys_size;
// physical offset in spi flash used for spiffs,
// must be on block boundary
u32_t phys_addr;
// physical size when erasing a block
u32_t phys_erase_block;
// logical size of a block, must be on physical
// block size boundary and must never be less than
// a physical block
u32_t log_block_size;
// logical size of a page, must be at least
// log_block_size / 8
u32_t log_page_size;
#endif
#if SPIFFS_FILEHDL_OFFSET
// an integer offset added to each file handle
u16_t fh_ix_offset;
#endif
} spiffs_config;
typedef struct spiffs_t {
// file system configuration
spiffs_config cfg;
// number of logical blocks
u32_t block_count;
// cursor for free blocks, block index
spiffs_block_ix free_cursor_block_ix;
// cursor for free blocks, entry index
int free_cursor_obj_lu_entry;
// cursor when searching, block index
spiffs_block_ix cursor_block_ix;
// cursor when searching, entry index
int cursor_obj_lu_entry;
// primary work buffer, size of a logical page
u8_t *lu_work;
// secondary work buffer, size of a logical page
u8_t *work;
// file descriptor memory area
u8_t *fd_space;
// available file descriptors
u32_t fd_count;
// last error
s32_t err_code;
// current number of free blocks
u32_t free_blocks;
// current number of busy pages
u32_t stats_p_allocated;
// current number of deleted pages
u32_t stats_p_deleted;
// flag indicating that garbage collector is cleaning
u8_t cleaning;
// max erase count amongst all blocks
spiffs_obj_id max_erase_count;
#if SPIFFS_GC_STATS
u32_t stats_gc_runs;
#endif
#if SPIFFS_CACHE
// cache memory
void *cache;
// cache size
u32_t cache_size;
#if SPIFFS_CACHE_STATS
u32_t cache_hits;
u32_t cache_misses;
#endif
#endif
// check callback function
spiffs_check_callback check_cb_f;
// file callback function
spiffs_file_callback file_cb_f;
// mounted flag
u8_t mounted;
// user data
void *user_data;
// config magic
u32_t config_magic;
} spiffs;
/* spiffs file status struct */
typedef struct {
spiffs_obj_id obj_id;
u32_t size;
spiffs_obj_type type;
spiffs_page_ix pix;
u8_t name[SPIFFS_OBJ_NAME_LEN];
} spiffs_stat;
struct spiffs_dirent {
spiffs_obj_id obj_id;
u8_t name[SPIFFS_OBJ_NAME_LEN];
spiffs_obj_type type;
u32_t size;
spiffs_page_ix pix;
};
typedef struct {
spiffs *fs;
spiffs_block_ix block;
int entry;
} spiffs_DIR;
// functions
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
/**
* Special function. This takes a spiffs config struct and returns the number
* of blocks this file system was formatted with. This function relies on
* that following info is set correctly in given config struct:
*
* phys_addr, log_page_size, and log_block_size.
*
* Also, hal_read_f must be set in the config struct.
*
* One must be sure of the correct page size and that the physical address is
* correct in the probed file system when calling this function. It is not
* checked if the phys_addr actually points to the start of the file system,
* so one might get a false positive if entering a phys_addr somewhere in the
* middle of the file system at block boundary. In addition, it is not checked
* if the page size is actually correct. If it is not, weird file system sizes
* will be returned.
*
* If this function detects a file system it returns the assumed file system
* size, which can be used to set the phys_size.
*
* Otherwise, it returns an error indicating why it is not regarded as a file
* system.
*
* Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK
* macros. It returns the error code directly, instead of as read by
* SPIFFS_errno.
*
* @param config essential parts of the physical and logical
* configuration of the file system.
*/
s32_t SPIFFS_probe_fs(spiffs_config *config);
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
/**
* Initializes the file system dynamic parameters and mounts the filesystem.
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
* if the flash does not contain a recognizable file system.
* In this case, SPIFFS_format must be called prior to remounting.
* @param fs the file system struct
* @param config the physical and logical configuration of the file system
* @param work a memory work buffer comprising 2*config->log_page_size
* bytes used throughout all file system operations
* @param fd_space memory for file descriptors
* @param fd_space_size memory size of file descriptors
* @param cache memory for cache, may be null
* @param cache_size memory size of cache
* @param check_cb_f callback function for reporting during consistency checks
*/
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
u8_t *fd_space, u32_t fd_space_size,
void *cache, u32_t cache_size,
spiffs_check_callback check_cb_f);
/**
* Unmounts the file system. All file handles will be flushed of any
* cached writes and closed.
* @param fs the file system struct
*/
void SPIFFS_unmount(spiffs *fs);
/**
* Creates a new file.
* @param fs the file system struct
* @param path the path of the new file
* @param mode ignored, for posix compliance
*/
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
/**
* Opens/creates a file.
* @param fs the file system struct
* @param path the path of the new file
* @param flags the flags for the open command, can be combinations of
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT
* @param mode ignored, for posix compliance
*/
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode);
/**
* Opens a file by given dir entry.
* Optimization purposes, when traversing a file system with SPIFFS_readdir
* a normal SPIFFS_open would need to traverse the filesystem again to find
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
* @param fs the file system struct
* @param e the dir entry to the file
* @param flags the flags for the open command, can be combinations of
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
* SPIFFS_CREAT will have no effect in this case.
* @param mode ignored, for posix compliance
*/
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode);
/**
* Opens a file by given page index.
* Optimization purposes, opens a file by directly pointing to the page
* index in the spi flash.
* If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE
* is returned.
* @param fs the file system struct
* @param page_ix the page index
* @param flags the flags for the open command, can be combinations of
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
* SPIFFS_CREAT will have no effect in this case.
* @param mode ignored, for posix compliance
*/
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode);
/**
* Reads from given filehandle.
* @param fs the file system struct
* @param fh the filehandle
* @param buf where to put read data
* @param len how much to read
* @returns number of bytes read, or -1 if error
*/
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
/**
* Writes to given filehandle.
* @param fs the file system struct
* @param fh the filehandle
* @param buf the data to write
* @param len how much to write
* @returns number of bytes written, or -1 if error
*/
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
/**
* Moves the read/write file offset. Resulting offset is returned or negative if error.
* lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset.
* @param fs the file system struct
* @param fh the filehandle
* @param offs how much/where to move the offset
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative
*/
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
/**
* Removes a file by path
* @param fs the file system struct
* @param path the path of the file to remove
*/
s32_t SPIFFS_remove(spiffs *fs, const char *path);
/**
* Removes a file by filehandle
* @param fs the file system struct
* @param fh the filehandle of the file to remove
*/
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
/**
* Gets file status by path
* @param fs the file system struct
* @param path the path of the file to stat
* @param s the stat struct to populate
*/
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
/**
* Gets file status by filehandle
* @param fs the file system struct
* @param fh the filehandle of the file to stat
* @param s the stat struct to populate
*/
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
/**
* Flushes all pending write operations from cache for given file
* @param fs the file system struct
* @param fh the filehandle of the file to flush
*/
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
/**
* Closes a filehandle. If there are pending write operations, these are finalized before closing.
* @param fs the file system struct
* @param fh the filehandle of the file to close
*/
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
/**
* Renames a file
* @param fs the file system struct
* @param old path of file to rename
* @param newPath new path of file
*/
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
/**
* Returns last error of last file operation.
* @param fs the file system struct
*/
s32_t SPIFFS_errno(spiffs *fs);
/**
* Clears last error.
* @param fs the file system struct
*/
void SPIFFS_clearerr(spiffs *fs);
/**
* Opens a directory stream corresponding to the given name.
* The stream is positioned at the first entry in the directory.
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond
* to a flat file structure - no directories.
* @param fs the file system struct
* @param name the name of the directory
* @param d pointer the directory stream to be populated
*/
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
/**
* Closes a directory stream
* @param d the directory stream to close
*/
s32_t SPIFFS_closedir(spiffs_DIR *d);
/**
* Reads a directory into given spifs_dirent struct.
* @param d pointer to the directory stream
* @param e the dirent struct to be populated
* @returns null if error or end of stream, else given dirent is returned
*/
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
/**
* Runs a consistency check on given filesystem.
* @param fs the file system struct
*/
s32_t SPIFFS_check(spiffs *fs);
/**
* Returns number of total bytes available and number of used bytes.
* This is an estimation, and depends on if there a many files with little
* data or few files with much data.
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
* run. This indicates a power loss in midst of things. In worst case
* (repeated powerlosses in mending or gc) you might have to delete some files.
*
* @param fs the file system struct
* @param total total number of bytes in filesystem
* @param used used number of bytes in filesystem
*/
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
/**
* Formats the entire file system. All data will be lost.
* The filesystem must not be mounted when calling this.
*
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
* MUST be called prior to formatting in order to configure the filesystem.
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
* SPIFFS_format.
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
* SPIFFS_unmount first.
*
* @param fs the file system struct
*/
s32_t SPIFFS_format(spiffs *fs);
/**
* Returns nonzero if spiffs is mounted, or zero if unmounted.
* @param fs the file system struct
*/
u8_t SPIFFS_mounted(spiffs *fs);
/**
* Tries to find a block where most or all pages are deleted, and erase that
* block if found. Does not care for wear levelling. Will not move pages
* around.
* If parameter max_free_pages are set to 0, only blocks with only deleted
* pages will be selected.
*
* NB: the garbage collector is automatically called when spiffs needs free
* pages. The reason for this function is to give possibility to do background
* tidying when user knows the system is idle.
*
* Use with care.
*
* Setting max_free_pages to anything larger than zero will eventually wear
* flash more as a block containing free pages can be erased.
*
* Will set err_no to SPIFFS_OK if a block was found and erased,
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
* or other error.
*
* @param fs the file system struct
* @param max_free_pages maximum number allowed free pages in block
*/
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
/**
* Will try to make room for given amount of bytes in the filesystem by moving
* pages and erasing blocks.
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
* there already is this amount (or more) of free space, SPIFFS_gc will
* silently return. It is recommended to call SPIFFS_info before invoking
* this method in order to determine what amount of bytes to give.
*
* NB: the garbage collector is automatically called when spiffs needs free
* pages. The reason for this function is to give possibility to do background
* tidying when user knows the system is idle.
*
* Use with care.
*
* @param fs the file system struct
* @param size amount of bytes that should be freed
*/
s32_t SPIFFS_gc(spiffs *fs, u32_t size);
/**
* Check if EOF reached.
* @param fs the file system struct
* @param fh the filehandle of the file to check
*/
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
/**
* Get position in file.
* @param fs the file system struct
* @param fh the filehandle of the file to check
*/
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
/**
* Registers a callback function that keeps track on operations on file
* headers. Do note, that this callback is called from within internal spiffs
* mechanisms. Any operations on the actual file system being callbacked from
* in this callback will mess things up for sure - do not do this.
* This can be used to track where files are and move around during garbage
* collection, which in turn can be used to build location tables in ram.
* Used in conjuction with SPIFFS_open_by_page this may improve performance
* when opening a lot of files.
* Must be invoked after mount.
*
* @param fs the file system struct
* @param cb_func the callback on file operations
*/
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
#if SPIFFS_TEST_VISUALISATION
/**
* Prints out a visualization of the filesystem.
* @param fs the file system struct
*/
s32_t SPIFFS_vis(spiffs *fs);
#endif
#if SPIFFS_BUFFER_HELP
/**
* Returns number of bytes needed for the filedescriptor buffer given
* amount of file descriptors.
*/
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
#if SPIFFS_CACHE
/**
* Returns number of bytes needed for the cache buffer given
* amount of cache pages.
*/
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
#endif
#endif
#if SPIFFS_CACHE
#endif
#if defined(__cplusplus)
}
#endif
#endif /* SPIFFS_H_ */

View File

@@ -1,299 +0,0 @@
/*
* spiffs_cache.c
*
* Created on: Jun 23, 2013
* Author: petera
*/
#include "spiffs.h"
#include "spiffs_nucleus.h"
#if SPIFFS_CACHE
// returns cached page for give page index, or null if no such cached page
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
spiffs_cache *cache = spiffs_get_cache(fs);
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
int i;
for (i = 0; i < cache->cpage_count; i++) {
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
if ((cache->cpage_use_map & (1<<i)) &&
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
cp->pix == pix ) {
SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix);
cp->last_access = cache->last_access;
return cp;
}
}
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix);
return 0;
}
// frees cached page
static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
s32_t res = SPIFFS_OK;
spiffs_cache *cache = spiffs_get_cache(fs);
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
if (cache->cpage_use_map & (1<<ix)) {
if (write_back &&
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
}
cp->flags = 0;
cache->cpage_use_map &= ~(1 << ix);
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id);
} else {
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix);
}
}
return res;
}
// removes the oldest accessed cached page
static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
s32_t res = SPIFFS_OK;
spiffs_cache *cache = spiffs_get_cache(fs);
if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
// at least one free cpage
return SPIFFS_OK;
}
// all busy, scan thru all to find the cpage which has oldest access
int i;
int cand_ix = -1;
u32_t oldest_val = 0;
for (i = 0; i < cache->cpage_count; i++) {
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
if ((cache->last_access - cp->last_access) > oldest_val &&
(cp->flags & flag_mask) == flags) {
oldest_val = cache->last_access - cp->last_access;
cand_ix = i;
}
}
if (cand_ix >= 0) {
res = spiffs_cache_page_free(fs, cand_ix, 1);
}
return res;
}
// allocates a new cached page and returns it, or null if all cache pages are busy
static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
spiffs_cache *cache = spiffs_get_cache(fs);
if (cache->cpage_use_map == 0xffffffff) {
// out of cache memory
return 0;
}
int i;
for (i = 0; i < cache->cpage_count; i++) {
if ((cache->cpage_use_map & (1<<i)) == 0) {
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
cache->cpage_use_map |= (1<<i);
cp->last_access = cache->last_access;
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i);
return cp;
}
}
// out of cache entries
return 0;
}
// drops the cache page for give page index
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) {
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
if (cp) {
spiffs_cache_page_free(fs, cp->ix, 0);
}
}
// ------------------------------
// reads from spi flash or the cache
s32_t spiffs_phys_rd(
spiffs *fs,
u8_t op,
spiffs_file fh,
u32_t addr,
u32_t len,
u8_t *dst) {
(void)fh;
s32_t res = SPIFFS_OK;
spiffs_cache *cache = spiffs_get_cache(fs);
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
cache->last_access++;
if (cp) {
#if SPIFFS_CACHE_STATS
fs->cache_hits++;
#endif
cp->last_access = cache->last_access;
} else {
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
// for second layer lookup functions, we do not cache in order to prevent shredding
return SPIFFS_HAL_READ(fs, addr, len, dst);
}
#if SPIFFS_CACHE_STATS
fs->cache_misses++;
#endif
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
cp = spiffs_cache_page_allocate(fs);
if (cp) {
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
}
s32_t res2 = SPIFFS_HAL_READ(fs,
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
SPIFFS_CFG_LOG_PAGE_SZ(fs),
spiffs_get_cache_page(fs, cache, cp->ix));
if (res2 != SPIFFS_OK) {
res = res2;
}
}
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
return res;
}
// writes to spi flash and/or the cache
s32_t spiffs_phys_wr(
spiffs *fs,
u8_t op,
spiffs_file fh,
u32_t addr,
u32_t len,
u8_t *src) {
(void)fh;
spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
spiffs_cache *cache = spiffs_get_cache(fs);
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) {
// have a cache page
// copy in data to cache page
if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE &&
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
// page is being deleted, wipe from cache - unless it is a lookup page
spiffs_cache_page_free(fs, cp->ix, 0);
return SPIFFS_HAL_WRITE(fs, addr, len, src);
}
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
cache->last_access++;
cp->last_access = cache->last_access;
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) {
// page is being updated, no write-cache, just pass thru
return SPIFFS_HAL_WRITE(fs, addr, len, src);
} else {
return SPIFFS_OK;
}
} else {
// no cache page, no write cache - just write thru
return SPIFFS_HAL_WRITE(fs, addr, len, src);
}
}
#if SPIFFS_CACHE_WR
// returns the cache page that this fd refers, or null if no cache page
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) {
spiffs_cache *cache = spiffs_get_cache(fs);
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) {
// all cpages free, no cpage cannot be assigned to obj_id
return 0;
}
int i;
for (i = 0; i < cache->cpage_count; i++) {
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
if ((cache->cpage_use_map & (1<<i)) &&
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) &&
cp->obj_id == fd->obj_id) {
return cp;
}
}
return 0;
}
// allocates a new cache page and refers this to given fd - flushes an old cache
// page if all cache is busy
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
// before this function is called, it is ensured that there is no already existing
// cache page with same object id
spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
spiffs_cache_page *cp = spiffs_cache_page_allocate(fs);
if (cp == 0) {
// could not get cache page
return 0;
}
cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
cp->obj_id = fd->obj_id;
fd->cache_page = cp;
return cp;
}
// unrefers all fds that this cache page refers to and releases the cache page
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
if (cp == 0) return;
u32_t i;
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
for (i = 0; i < fs->fd_count; i++) {
spiffs_fd *cur_fd = &fds[i];
if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) {
cur_fd->cache_page = 0;
}
}
spiffs_cache_page_free(fs, cp->ix, 0);
cp->obj_id = 0;
}
#endif
// initializes the cache
void spiffs_cache_init(spiffs *fs) {
if (fs->cache == 0) return;
u32_t sz = fs->cache_size;
u32_t cache_mask = 0;
int i;
int cache_entries =
(sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs));
if (cache_entries <= 0) return;
for (i = 0; i < cache_entries; i++) {
cache_mask <<= 1;
cache_mask |= 1;
}
spiffs_cache cache;
memset(&cache, 0, sizeof(spiffs_cache));
cache.cpage_count = cache_entries;
cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache));
cache.cpage_use_map = 0xffffffff;
cache.cpage_use_mask = cache_mask;
memcpy(fs->cache, &cache, sizeof(spiffs_cache));
spiffs_cache *c = spiffs_get_cache(fs);
memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
c->cpage_use_map &= ~(c->cpage_use_mask);
for (i = 0; i < cache.cpage_count; i++) {
spiffs_get_cache_page_hdr(fs, c, i)->ix = i;
}
}
#endif // SPIFFS_CACHE

View File

@@ -1,995 +0,0 @@
/*
* spiffs_check.c
*
* Contains functionality for checking file system consistency
* and mending problems.
* Three levels of consistency checks are implemented:
*
* Look up consistency
* Checks if indices in lookup pages are coherent with page headers
* Object index consistency
* Checks if there are any orphaned object indices (missing object index headers).
* If an object index is found but not its header, the object index is deleted.
* This is critical for the following page consistency check.
* Page consistency
* Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed
*
*
* Created on: Jul 7, 2013
* Author: petera
*/
#include "spiffs.h"
#include "spiffs_nucleus.h"
#if !SPIFFS_READ_ONLY
#if SPIFFS_HAL_CALLBACK_EXTRA
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
do { \
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \
} while (0)
#else
#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
do { \
if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \
} while (0)
#endif
//---------------------------------------
// Look up consistency
// searches in the object indices and returns the referenced page index given
// the object id and the data span index
// destroys fs->lu_work
static s32_t spiffs_object_get_data_page_index_reference(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix data_spix,
spiffs_page_ix *pix,
spiffs_page_ix *objix_pix) {
s32_t res;
// calculate object index span index for given data page span index
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
// find obj index for obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix);
SPIFFS_CHECK_RES(res);
// load obj index entry
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix);
if (objix_spix == 0) {
// get referenced page from object index header
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix);
} else {
// get referenced page from object index
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix);
}
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix);
return res;
}
// copies page contents to a new page
static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) {
s32_t res;
res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_phys_cpy(fs, 0,
SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header),
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res);
return res;
}
// rewrites the object index for given object id and replaces the
// data page index to a new page index
static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) {
s32_t res;
spiffs_block_ix bix;
int entry;
spiffs_page_ix free_pix;
obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
// find free entry
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
SPIFFS_CHECK_RES(res);
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
// calculate object index span index for given data page span index
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
if (objix_spix == 0) {
// calc index in index header
entry = data_spix;
} else {
// calc entry in index
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
}
// load index
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
// be ultra safe, double check header against provided data
if (objix_p_hdr->obj_id != obj_id) {
spiffs_page_delete(fs, free_pix);
return SPIFFS_ERR_CHECK_OBJ_ID_MISM;
}
if (objix_p_hdr->span_ix != objix_spix) {
spiffs_page_delete(fs, free_pix);
return SPIFFS_ERR_CHECK_SPIX_MISM;
}
if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX |
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) !=
(SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) {
spiffs_page_delete(fs, free_pix);
return SPIFFS_ERR_CHECK_FLAGS_BAD;
}
// rewrite in mem
if (objix_spix == 0) {
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
} else {
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
}
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
sizeof(spiffs_obj_id),
(u8_t *)&obj_id);
SPIFFS_CHECK_RES(res);
res = spiffs_page_delete(fs, objix_pix);
return res;
}
// deletes an object just by marking object index header as deleted
static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
spiffs_page_ix objix_hdr_pix;
s32_t res;
res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
return SPIFFS_OK;
}
SPIFFS_CHECK_RES(res);
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&flags);
return res;
}
// validates the given look up entry
static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr,
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) {
(void)cur_block;
(void)cur_entry;
u8_t delete_page = 0;
s32_t res = SPIFFS_OK;
spiffs_page_ix objix_pix;
spiffs_page_ix ref_pix;
// check validity, take actions
if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) ||
((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) {
// look up entry deleted / free but used in page header
SPIFFS_CHECK_DBG("LU: pix %04x deleted/free in lu but not on page\n", cur_pix);
*reload_lu = 1;
delete_page = 1;
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
// header says data page
// data page can be removed if not referenced by some object index
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
// no object with this id, so remove page safely
res = SPIFFS_OK;
} else {
SPIFFS_CHECK_RES(res);
if (ref_pix == cur_pix) {
// data page referenced by object index but deleted in lu
// copy page to new place and re-write the object index to new place
spiffs_page_ix new_pix;
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix);
SPIFFS_CHECK_RES(res);
*reload_lu = 1;
SPIFFS_CHECK_DBG("LU: FIXUP: %04x rewritten to %04x, affected objix_pix %04x\n", cur_pix, new_pix, objix_pix);
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file
SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res);
res = spiffs_page_delete(fs, new_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
} else {
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
}
SPIFFS_CHECK_RES(res);
}
}
} else {
// header says index page
// index page can be removed if other index with same obj_id and spanix is found
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0);
if (res == SPIFFS_ERR_NOT_FOUND) {
// no such index page found, check for a data page amongst page headers
// lu cannot be trusted
res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0);
if (res == SPIFFS_OK) { // ignore other errors
// got a data page also, assume lu corruption only, rewrite to new page
spiffs_page_ix new_pix;
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix);
SPIFFS_CHECK_RES(res);
*reload_lu = 1;
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
}
} else {
SPIFFS_CHECK_RES(res);
}
}
}
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) {
// look up entry used
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) {
SPIFFS_CHECK_DBG("LU: pix %04x differ in obj_id lu:%04x ph:%04x\n", cur_pix, lu_obj_id, p_hdr->obj_id);
delete_page = 1;
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) ||
(p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) {
// page deleted or not finalized, just remove it
} else {
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
// if data page, check for reference to this page
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
// no object with this id, so remove page safely
res = SPIFFS_OK;
} else {
SPIFFS_CHECK_RES(res);
// if found, rewrite page with object id, update index, and delete current
if (ref_pix == cur_pix) {
spiffs_page_ix new_pix;
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file
SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res);
res = spiffs_page_delete(fs, new_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
*reload_lu = 1;
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
}
SPIFFS_CHECK_RES(res);
}
}
} else {
// else if index, check for other pages with both obj_id's and spanix
spiffs_page_ix objix_pix_lu, objix_pix_ph;
// see if other object index page exists for lookup obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
objix_pix_lu = 0;
}
SPIFFS_CHECK_RES(res);
// see if other object index exists for page header obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
objix_pix_ph = 0;
}
SPIFFS_CHECK_RES(res);
// if both obj_id's found, just delete current
if (objix_pix_ph == 0 || objix_pix_lu == 0) {
// otherwise try finding first corresponding data pages
spiffs_page_ix data_pix_lu, data_pix_ph;
// see if other data page exists for look up obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
objix_pix_lu = 0;
}
SPIFFS_CHECK_RES(res);
// see if other data page exists for page header obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
objix_pix_ph = 0;
}
SPIFFS_CHECK_RES(res);
spiffs_page_header new_ph;
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL);
new_ph.span_ix = p_hdr->span_ix;
spiffs_page_ix new_pix;
if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) ||
(objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) {
// got a data page for page header obj id
// rewrite as obj_id_ph
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x to pix %04x\n", cur_pix, new_ph.obj_id, new_pix);
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
SPIFFS_CHECK_RES(res);
*reload_lu = 1;
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) ||
(objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) {
// got a data page for look up obj id
// rewrite as obj_id_lu
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x\n", cur_pix, new_ph.obj_id);
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
SPIFFS_CHECK_RES(res);
*reload_lu = 1;
} else {
// cannot safely do anything
SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n");
}
}
}
}
} else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) ||
((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) {
SPIFFS_CHECK_DBG("LU: %04x lu/page index marking differ\n", cur_pix);
spiffs_page_ix data_pix, objix_pix_d;
// see if other data page exists for given obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
data_pix = 0;
}
SPIFFS_CHECK_RES(res);
// see if other object index exists for given obj id and span index
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
objix_pix_d = 0;
}
SPIFFS_CHECK_RES(res);
delete_page = 1;
// if other data page exists and object index exists, just delete page
if (data_pix && objix_pix_d) {
SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n");
} else
// if only data page exists, make this page index
if (data_pix && objix_pix_d == 0) {
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n");
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix);
spiffs_page_header new_ph;
spiffs_page_ix new_pix;
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
new_ph.span_ix = p_hdr->span_ix;
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
SPIFFS_CHECK_RES(res);
} else
// if only index exists, make data page
if (data_pix == 0 && objix_pix_d) {
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n");
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix);
spiffs_page_header new_ph;
spiffs_page_ix new_pix;
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
new_ph.span_ix = p_hdr->span_ix;
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
SPIFFS_CHECK_RES(res);
} else {
// if nothing exists, we cannot safely make a decision - delete
}
}
else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
SPIFFS_CHECK_DBG("LU: pix %04x busy in lu but deleted on page\n", cur_pix);
delete_page = 1;
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
SPIFFS_CHECK_DBG("LU: pix %04x busy but not final\n", cur_pix);
// page can be removed if not referenced by object index
*reload_lu = 1;
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
// no object with this id, so remove page safely
res = SPIFFS_OK;
delete_page = 1;
} else {
SPIFFS_CHECK_RES(res);
if (ref_pix != cur_pix) {
SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n");
delete_page = 1;
} else {
// page referenced by object index but not final
// just finalize
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t), (u8_t*)&flags);
}
}
}
}
if (delete_page) {
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix);
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
}
return res;
}
static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
const void *user_const_p, void *user_var_p) {
(void)user_const_p;
(void)user_var_p;
s32_t res = SPIFFS_OK;
spiffs_page_header p_hdr;
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
(cur_block * 256)/fs->block_count, 0);
// load header
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
int reload_lu = 0;
res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu);
SPIFFS_CHECK_RES(res);
if (res == SPIFFS_OK) {
return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE;
}
return res;
}
// Scans all object look up. For each entry, corresponding page header is checked for validity.
// If an object index header page is found, this is also checked
s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
(void)check_all_objects;
s32_t res = SPIFFS_OK;
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0);
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_OK;
}
if (res != SPIFFS_OK) {
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
}
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
return res;
}
//---------------------------------------
// Page consistency
// Scans all pages (except lu pages), reserves 4 bits in working memory for each page
// bit 0: 0 == FREE|DELETED, 1 == USED
// bit 1: 0 == UNREFERENCED, 1 == REFERENCED
// bit 2: 0 == NOT_INDEX, 1 == INDEX
// bit 3: unused
// A consistent file system will have only pages being
// * x000 free, unreferenced, not index
// * x011 used, referenced only once, not index
// * x101 used, unreferenced, index
// The working memory might not fit all pages so several scans might be needed
static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
const u32_t bits = 4;
const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits;
s32_t res = SPIFFS_OK;
spiffs_page_ix pix_offset = 0;
// for each range of pages fitting into work memory
while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) {
// set this flag to abort all checks and rescan the page range
u8_t restart = 0;
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
spiffs_block_ix cur_block = 0;
// build consistency bitmap for id range traversing all blocks
while (!restart && cur_block < fs->block_count) {
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS,
(pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) +
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count),
0);
// traverse each page except for lookup pages
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) {
//if ((cur_pix & 0xff) == 0)
// SPIFFS_CHECK_DBG("PA: processing pix %08x, block %08x of pix %08x, block %08x\n",
// cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count);
// read header
spiffs_page_header p_hdr;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan);
const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits);
const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits;
if (within_range &&
(p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) {
// used
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0));
}
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) &&
(p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) &&
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) {
// found non-deleted index
if (within_range) {
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2));
}
// load non-deleted index
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
// traverse index for referenced pages
spiffs_page_ix *object_page_index;
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
int entries;
int i;
spiffs_span_ix data_spix_offset;
if (p_hdr.span_ix == 0) {
// object header page index
entries = SPIFFS_OBJ_HDR_IX_LEN(fs);
data_spix_offset = 0;
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header));
} else {
// object page index
entries = SPIFFS_OBJ_IX_LEN(fs);
data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1);
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix));
}
// for all entries in index
for (i = 0; !restart && i < entries; i++) {
spiffs_page_ix rpix = object_page_index[i];
u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan;
if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs))
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
// bad reference
SPIFFS_CHECK_DBG("PA: pix %04x bad pix / LU referenced from page %04x\n",
rpix, cur_pix);
// check for data page elsewhere
spiffs_page_ix data_pix;
res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
data_spix_offset + i, 0, &data_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
data_pix = 0;
}
SPIFFS_CHECK_RES(res);
if (data_pix == 0) {
// if not, allocate free page
spiffs_page_header new_ph;
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
new_ph.span_ix = data_spix_offset + i;
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix);
SPIFFS_CHECK_RES(res);
SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ %04x\n", data_pix);
}
// remap index
SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix %04x\n", cur_pix);
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
data_spix_offset + i, data_pix, cur_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res);
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
// delete file
res = spiffs_page_delete(fs, cur_pix);
} else {
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix);
}
SPIFFS_CHECK_RES(res);
restart = 1;
} else if (rpix_within_range) {
// valid reference
// read referenced page header
spiffs_page_header rp_hdr;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
SPIFFS_CHECK_RES(res);
// cross reference page header check
if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) ||
rp_hdr.span_ix != data_spix_offset + i ||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) {
SPIFFS_CHECK_DBG("PA: pix %04x has inconsistent page header ix id/span:%04x/%04x, ref id/span:%04x/%04x flags:%02x\n",
rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i,
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags);
// try finding correct page
spiffs_page_ix data_pix;
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
data_spix_offset + i, rpix, &data_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK;
data_pix = 0;
}
SPIFFS_CHECK_RES(res);
if (data_pix == 0) {
// not found, this index is badly borked
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id);
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
SPIFFS_CHECK_RES(res);
break;
} else {
// found it, so rewrite index
SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix %04x, rewrite ix pix %04x id %04x\n",
data_pix, cur_pix, p_hdr.obj_id);
res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
} else {
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
}
SPIFFS_CHECK_RES(res);
restart = 1;
}
}
else {
// mark rpix as referenced
const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits);
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) {
SPIFFS_CHECK_DBG("PA: pix %04x multiple referenced from page %04x\n",
rpix, cur_pix);
// Here, we should have fixed all broken references - getting this means there
// must be multiple files with same object id. Only solution is to delete
// the object which is referring to this page
SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n",
p_hdr.obj_id, cur_pix);
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
SPIFFS_CHECK_RES(res);
// extra precaution, delete this page also
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
restart = 1;
}
fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1));
}
}
} // for all index entries
} // found index
// next page
cur_pix++;
}
// next block
cur_block++;
}
// check consistency bitmap
if (!restart) {
spiffs_page_ix objix_pix;
spiffs_page_ix rpix;
u32_t byte_ix;
u8_t bit_ix;
for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) {
for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) {
u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7;
spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix;
// 000 ok - free, unreferenced, not index
if (bitmask == 0x1) {
// 001
SPIFFS_CHECK_DBG("PA: pix %04x USED, UNREFERENCED, not index\n", cur_pix);
u8_t rewrite_ix_to_this = 0;
u8_t delete_page = 0;
// check corresponding object index entry
spiffs_page_header p_hdr;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix,
&rpix, &objix_pix);
if (res == SPIFFS_OK) {
if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) {
// pointing to a bad page altogether, rewrite index to this
rewrite_ix_to_this = 1;
SPIFFS_CHECK_DBG("PA: corresponding ref is bad: %04x, rewrite to this %04x\n", rpix, cur_pix);
} else {
// pointing to something else, check what
spiffs_page_header rp_hdr;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
SPIFFS_CHECK_RES(res);
if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) &&
((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) ==
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) {
// pointing to something else valid, just delete this page then
SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: %04x, delete this %04x\n", rpix, cur_pix);
delete_page = 1;
} else {
// pointing to something weird, update index to point to this page instead
if (rpix != cur_pix) {
SPIFFS_CHECK_DBG("PA: corresponding ref is weird: %04x %s%s%s%s, rewrite this %04x\n", rpix,
(rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ",
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "",
(rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "",
cur_pix);
rewrite_ix_to_this = 1;
} else {
// should not happen, destined for fubar
}
}
}
} else if (res == SPIFFS_ERR_NOT_FOUND) {
SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete %04x\n", cur_pix);
delete_page = 1;
res = SPIFFS_OK;
}
if (rewrite_ix_to_this) {
// if pointing to invalid page, redirect index to this page
SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id %04x data spix %04x to point to this pix: %04x\n",
p_hdr.obj_id, p_hdr.span_ix, cur_pix);
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
} else {
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
}
SPIFFS_CHECK_RES(res);
restart = 1;
continue;
} else if (delete_page) {
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix);
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
res = spiffs_page_delete(fs, cur_pix);
}
SPIFFS_CHECK_RES(res);
}
if (bitmask == 0x2) {
// 010
SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, not index\n", cur_pix);
// no op, this should be taken care of when checking valid references
}
// 011 ok - busy, referenced, not index
if (bitmask == 0x4) {
// 100
SPIFFS_CHECK_DBG("PA: pix %04x FREE, unreferenced, INDEX\n", cur_pix);
// this should never happen, major fubar
}
// 101 ok - busy, unreferenced, index
if (bitmask == 0x6) {
// 110
SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, INDEX\n", cur_pix);
// no op, this should be taken care of when checking valid references
}
if (bitmask == 0x7) {
// 111
SPIFFS_CHECK_DBG("PA: pix %04x USED, REFERENCED, INDEX\n", cur_pix);
// no op, this should be taken care of when checking valid references
}
}
}
}
SPIFFS_CHECK_DBG("PA: processed %04x, restart %i\n", pix_offset, restart);
// next page range
if (!restart) {
pix_offset += pages_per_scan;
}
} // while page range not reached end
return res;
}
// Checks consistency amongst all pages and fixes irregularities
s32_t spiffs_page_consistency_check(spiffs *fs) {
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
s32_t res = spiffs_page_consistency_check_i(fs);
if (res != SPIFFS_OK) {
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
}
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
return res;
}
//---------------------------------------
// Object index consistency
// searches for given object id in temporary object id index,
// returns the index or -1
static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) {
u32_t i;
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) {
if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) {
return i;
}
}
return -1;
}
static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block,
int cur_entry, const void *user_const_p, void *user_var_p) {
(void)user_const_p;
s32_t res_c = SPIFFS_VIS_COUNTINUE;
s32_t res = SPIFFS_OK;
u32_t *log_ix = (u32_t*)user_var_p;
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS,
(cur_block * 256)/fs->block_count, 0);
if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
spiffs_page_header p_hdr;
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
// load header
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
if (p_hdr.span_ix == 0 &&
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
(SPIFFS_PH_FLAG_DELET)) {
SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n",
cur_pix, obj_id, p_hdr.span_ix);
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id);
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
return res_c;
}
if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
return res_c;
}
if (p_hdr.span_ix == 0) {
// objix header page, register objid as reachable
int r = spiffs_object_index_search(fs, obj_id);
if (r == -1) {
// not registered, do it
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
(*log_ix)++;
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
*log_ix = 0;
}
}
} else { // span index
// objix page, see if header can be found
int r = spiffs_object_index_search(fs, obj_id);
u8_t delete = 0;
if (r == -1) {
// not in temporary index, try finding it
spiffs_page_ix objix_hdr_pix;
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix);
res_c = SPIFFS_VIS_COUNTINUE_RELOAD;
if (res == SPIFFS_OK) {
// found, register as reachable
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
} else if (res == SPIFFS_ERR_NOT_FOUND) {
// not found, register as unreachable
delete = 1;
obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG;
} else {
SPIFFS_CHECK_RES(res);
}
(*log_ix)++;
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
*log_ix = 0;
}
} else {
// in temporary index, check reachable flag
if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) {
// registered as unreachable
delete = 1;
}
}
if (delete) {
SPIFFS_CHECK_DBG("IX: FIXUP: pix %04x, obj id:%04x spix:%04x is orphan index - deleting\n",
cur_pix, obj_id, p_hdr.span_ix);
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id);
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
}
} // span index
} // valid object index id
return res_c;
}
// Removes orphaned and partially deleted index pages.
// Scans for index pages. When an index page is found, corresponding index header is searched for.
// If no such page exists, the index page cannot be reached as no index header exists and must be
// deleted.
s32_t spiffs_object_index_consistency_check(spiffs *fs) {
s32_t res = SPIFFS_OK;
// impl note:
// fs->work is used for a temporary object index memory, listing found object ids and
// indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit.
// In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate
// a reachable/unreachable object id.
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
u32_t obj_id_log_ix = 0;
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0);
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, &obj_id_log_ix,
0, 0, 0);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_OK;
}
if (res != SPIFFS_OK) {
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
}
CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
return res;
}
#endif // !SPIFFS_READ_ONLY

View File

@@ -1,253 +0,0 @@
/*
* spiffs_config.h
*
* Created on: Jul 3, 2013
* Author: petera
*/
#ifndef SPIFFS_CONFIG_H_
#define SPIFFS_CONFIG_H_
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#define c_memcpy memcpy
#define c_printf printf
#define c_memset memset
typedef int16_t file_t;
typedef int32_t s32_t;
typedef uint32_t u32_t;
typedef int16_t s16_t;
typedef uint16_t u16_t;
typedef int8_t s8_t;
typedef uint8_t u8_t;
#ifndef SEEK_SET
#define SEEK_SET 0 /* set file offset to offset */
#endif
#ifndef SEEK_CUR
#define SEEK_CUR 1 /* set file offset to current plus offset */
#endif
#ifndef SEEK_END
#define SEEK_END 2 /* set file offset to EOF plus offset */
#endif
#ifndef EOF
#define EOF (-1)
#endif
// compile time switches
// Set generic spiffs debug output call.
#ifndef SPIFFS_DBG
#define SPIFFS_DBG(...) //printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for garbage collecting.
#ifndef SPIFFS_GC_DBG
#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for caching.
#ifndef SPIFFS_CACHE_DBG
#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for system consistency checks.
#ifndef SPIFFS_CHECK_DBG
#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
#endif
// Enable/disable API functions to determine exact number of bytes
// for filedescriptor and cache buffers. Once decided for a configuration,
// this can be disabled to reduce flash.
#ifndef SPIFFS_BUFFER_HELP
#define SPIFFS_BUFFER_HELP 1
#endif
// Enables/disable memory read caching of nucleus file system operations.
// If enabled, memory area must be provided for cache in SPIFFS_mount.
#ifndef SPIFFS_CACHE
#define SPIFFS_CACHE 1
#endif
#if SPIFFS_CACHE
// Enables memory write caching for file descriptors in hydrogen
#ifndef SPIFFS_CACHE_WR
#define SPIFFS_CACHE_WR 1
#endif
// Enable/disable statistics on caching. Debug/test purpose only.
#ifndef SPIFFS_CACHE_STATS
#define SPIFFS_CACHE_STATS 0
#endif
#endif
// Always check header of each accessed page to ensure consistent state.
// If enabled it will increase number of reads, will increase flash.
#ifndef SPIFFS_PAGE_CHECK
#define SPIFFS_PAGE_CHECK 1
#endif
// Define maximum number of gc runs to perform to reach desired free pages.
#ifndef SPIFFS_GC_MAX_RUNS
#define SPIFFS_GC_MAX_RUNS 5
#endif
// Enable/disable statistics on gc. Debug/test purpose only.
#ifndef SPIFFS_GC_STATS
#define SPIFFS_GC_STATS 0
#endif
// Garbage collecting examines all pages in a block which and sums up
// to a block score. Deleted pages normally gives positive score and
// used pages normally gives a negative score (as these must be moved).
// To have a fair wear-leveling, the erase age is also included in score,
// whose factor normally is the most positive.
// The larger the score, the more likely it is that the block will
// picked for garbage collection.
// Garbage collecting heuristics - weight used for deleted pages.
#ifndef SPIFFS_GC_HEUR_W_DELET
#define SPIFFS_GC_HEUR_W_DELET (5)
#endif
// Garbage collecting heuristics - weight used for used pages.
#ifndef SPIFFS_GC_HEUR_W_USED
#define SPIFFS_GC_HEUR_W_USED (-1)
#endif
// Garbage collecting heuristics - weight used for time between
// last erased and erase of this block.
#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
#endif
// Object name maximum length.
#ifndef SPIFFS_OBJ_NAME_LEN
#define SPIFFS_OBJ_NAME_LEN (32)
#endif
// Size of buffer allocated on stack used when copying data.
// Lower value generates more read/writes. No meaning having it bigger
// than logical page size.
#ifndef SPIFFS_COPY_BUFFER_STACK
#define SPIFFS_COPY_BUFFER_STACK (64)
#endif
// Enable this to have an identifiable spiffs filesystem. This will look for
// a magic in all sectors to determine if this is a valid spiffs system or
// not on mount point. If not, SPIFFS_format must be called prior to mounting
// again.
#ifndef SPIFFS_USE_MAGIC
#define SPIFFS_USE_MAGIC (1)
#endif
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
// These should be defined on a multithreaded system
// define this to enter a mutex if you're running on a multithreaded system
#ifndef SPIFFS_LOCK
#define SPIFFS_LOCK(fs)
#endif
// define this to exit a mutex if you're running on a multithreaded system
#ifndef SPIFFS_UNLOCK
#define SPIFFS_UNLOCK(fs)
#endif
// Enable if only one spiffs instance with constant configuration will exist
// on the target. This will reduce calculations, flash and memory accesses.
// Parts of configuration must be defined below instead of at time of mount.
#ifndef SPIFFS_SINGLETON
#define SPIFFS_SINGLETON 0
#endif
#if SPIFFS_SINGLETON
// Instead of giving parameters in config struct, singleton build must
// give parameters in defines below.
#ifndef SPIFFS_CFG_PHYS_SZ
#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2)
#endif
#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536)
#endif
#ifndef SPIFFS_CFG_PHYS_ADDR
#define SPIFFS_CFG_PHYS_ADDR(ignore) (0)
#endif
#ifndef SPIFFS_CFG_LOG_PAGE_SZ
#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256)
#endif
#ifndef SPIFFS_CFG_LOG_BLOCK_SZ
#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536)
#endif
#endif
// Enable this if your target needs aligned data for index tables
#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1
#endif
// Enable this if you want the HAL callbacks to be called with the spiffs struct
#ifndef SPIFFS_HAL_CALLBACK_EXTRA
#define SPIFFS_HAL_CALLBACK_EXTRA 0
#endif
// Enable this if you want to add an integer offset to all file handles
// (spiffs_file). This is useful if running multiple instances of spiffs on
// same target, in order to recognise to what spiffs instance a file handle
// belongs.
// NB: This adds config field fh_ix_offset in the configuration struct when
// mounting, which must be defined.
#ifndef SPIFFS_FILEHDL_OFFSET
#define SPIFFS_FILEHDL_OFFSET 0
#endif
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
// in the api. This function will visualize all filesystem using given printf
// function.
#ifndef SPIFFS_TEST_VISUALISATION
#define SPIFFS_TEST_VISUALISATION 1
#endif
#if SPIFFS_TEST_VISUALISATION
#ifndef spiffs_printf
#define spiffs_printf(...) c_printf(__VA_ARGS__)
#endif
// spiffs_printf argument for a free page
#ifndef SPIFFS_TEST_VIS_FREE_STR
#define SPIFFS_TEST_VIS_FREE_STR "_"
#endif
// spiffs_printf argument for a deleted page
#ifndef SPIFFS_TEST_VIS_DELE_STR
#define SPIFFS_TEST_VIS_DELE_STR "/"
#endif
// spiffs_printf argument for an index page for given object id
#ifndef SPIFFS_TEST_VIS_INDX_STR
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
#endif
// spiffs_printf argument for a data page for given object id
#ifndef SPIFFS_TEST_VIS_DATA_STR
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
#endif
#endif
// Types depending on configuration such as the amount of flash bytes
// given to spiffs file system in total (spiffs_file_system_size),
// the logical block size (log_block_size), and the logical page size
// (log_page_size)
// Block index type. Make sure the size of this type can hold
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
typedef u16_t spiffs_block_ix;
// Page index type. Make sure the size of this type can hold
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
typedef u16_t spiffs_page_ix;
// Object id type - most significant bit is reserved for index flag. Make sure the
// size of this type can hold the highest object id on a full system,
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
typedef u16_t spiffs_obj_id;
// Object span index type. Make sure the size of this type can
// hold the largest possible span index on the system -
// i.e. (spiffs_file_system_size / log_page_size) - 1
typedef u16_t spiffs_span_ix;
#endif /* SPIFFS_CONFIG_H_ */

View File

@@ -1,576 +0,0 @@
#include "spiffs.h"
#include "spiffs_nucleus.h"
#if !SPIFFS_READ_ONLY
// Erases a logical block and updates the erase counter.
// If cache is enabled, all pages that might be cached in this block
// is dropped.
static s32_t spiffs_gc_erase_block(
spiffs *fs,
spiffs_block_ix bix) {
s32_t res;
SPIFFS_GC_DBG("gc: erase block %i\n", bix);
res = spiffs_erase_block(fs, bix);
SPIFFS_CHECK_RES(res);
#if SPIFFS_CACHE
{
u32_t i;
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
}
}
#endif
return res;
}
// Searches for blocks where all entries are deleted - if one is found,
// the block is erased. Compared to the non-quick gc, the quick one ensures
// that no updates are needed on existing objects on pages that are erased.
s32_t spiffs_gc_quick(
spiffs *fs, u16_t max_free_pages) {
s32_t res = SPIFFS_OK;
u32_t blocks = fs->block_count;
spiffs_block_ix cur_block = 0;
u32_t cur_block_addr = 0;
int cur_entry = 0;
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
SPIFFS_GC_DBG("gc_quick: running\n");
#if SPIFFS_GC_STATS
fs->stats_gc_runs++;
#endif
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
// find fully deleted blocks
// check each block
while (res == SPIFFS_OK && blocks--) {
u16_t deleted_pages_in_block = 0;
u16_t free_pages_in_block = 0;
int obj_lookup_page = 0;
// check each object lookup page
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
int entry_offset = obj_lookup_page * entries_per_page;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
// check each entry
while (res == SPIFFS_OK &&
cur_entry - entry_offset < entries_per_page &&
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
if (obj_id == SPIFFS_OBJ_ID_DELETED) {
deleted_pages_in_block++;
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
// kill scan, go for next block
free_pages_in_block++;
if (free_pages_in_block > max_free_pages) {
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
res = 1; // kill object lu loop
break;
}
} else {
// kill scan, go for next block
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
res = 1; // kill object lu loop
break;
}
cur_entry++;
} // per entry
obj_lookup_page++;
} // per object lookup page
if (res == 1) res = SPIFFS_OK;
if (res == SPIFFS_OK &&
deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
free_pages_in_block <= max_free_pages) {
// found a fully deleted block
fs->stats_p_deleted -= deleted_pages_in_block;
res = spiffs_gc_erase_block(fs, cur_block);
return res;
}
cur_entry = 0;
cur_block++;
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
} // per block
if (res == SPIFFS_OK) {
res = SPIFFS_ERR_NO_DELETED_BLOCKS;
}
return res;
}
// Checks if garbage collecting is necessary. If so a candidate block is found,
// cleansed and erased
s32_t spiffs_gc_check(
spiffs *fs,
u32_t len) {
s32_t res;
s32_t free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
- fs->stats_p_allocated - fs->stats_p_deleted;
int tries = 0;
if (fs->free_blocks > 3 &&
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
return SPIFFS_OK;
}
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
// return SPIFFS_ERR_FULL;
// }
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
return SPIFFS_ERR_FULL;
}
do {
SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
tries,
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
spiffs_block_ix *cands;
int count;
spiffs_block_ix cand;
s32_t prev_free_pages = free_pages;
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
SPIFFS_CHECK_RES(res);
if (count == 0) {
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
}
#if SPIFFS_GC_STATS
fs->stats_gc_runs++;
#endif
cand = cands[0];
fs->cleaning = 1;
//printf("gcing: cleaning block %i\n", cand);
res = spiffs_gc_clean(fs, cand);
fs->cleaning = 0;
if (res < 0) {
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
} else {
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
}
SPIFFS_CHECK_RES(res);
res = spiffs_gc_erase_page_stats(fs, cand);
SPIFFS_CHECK_RES(res);
res = spiffs_gc_erase_block(fs, cand);
SPIFFS_CHECK_RES(res);
free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
- fs->stats_p_allocated - fs->stats_p_deleted;
if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
// abort early to reduce wear, at least tried once
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
break;
}
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
- fs->stats_p_allocated - fs->stats_p_deleted;
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
res = SPIFFS_ERR_FULL;
}
SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
fs->stats_p_allocated + fs->stats_p_deleted,
fs->free_blocks, free_pages, tries, res);
return res;
}
// Updates page statistics for a block that is about to be erased
s32_t spiffs_gc_erase_page_stats(
spiffs *fs,
spiffs_block_ix bix) {
s32_t res = SPIFFS_OK;
int obj_lookup_page = 0;
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
int cur_entry = 0;
u32_t dele = 0;
u32_t allo = 0;
// check each object lookup page
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
int entry_offset = obj_lookup_page * entries_per_page;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
// check each entry
while (res == SPIFFS_OK &&
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
if (obj_id == SPIFFS_OBJ_ID_FREE) {
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
dele++;
} else {
allo++;
}
cur_entry++;
} // per entry
obj_lookup_page++;
} // per object lookup page
SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
fs->stats_p_allocated -= allo;
fs->stats_p_deleted -= dele;
return res;
}
// Finds block candidates to erase
s32_t spiffs_gc_find_candidate(
spiffs *fs,
spiffs_block_ix **block_candidates,
int *candidate_count,
char fs_crammed) {
s32_t res = SPIFFS_OK;
u32_t blocks = fs->block_count;
spiffs_block_ix cur_block = 0;
u32_t cur_block_addr = 0;
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
int cur_entry = 0;
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
*candidate_count = 0;
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
// divide up work area into block indices and scores
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
// align cand_scores on s32_t boundary
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
cand_scores = (s32_t*)(((ptrdiff_t)cand_scores + sizeof(ptrdiff_t) - 1) & ~(sizeof(ptrdiff_t) - 1));
#pragma GCC diagnostic pop
*block_candidates = cand_blocks;
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
// check each block
while (res == SPIFFS_OK && blocks--) {
u16_t deleted_pages_in_block = 0;
u16_t used_pages_in_block = 0;
int obj_lookup_page = 0;
// check each object lookup page
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
int entry_offset = obj_lookup_page * entries_per_page;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
// check each entry
while (res == SPIFFS_OK &&
cur_entry - entry_offset < entries_per_page &&
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
if (obj_id == SPIFFS_OBJ_ID_FREE) {
// when a free entry is encountered, scan logic ensures that all following entries are free also
res = 1; // kill object lu loop
break;
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
deleted_pages_in_block++;
} else {
used_pages_in_block++;
}
cur_entry++;
} // per entry
obj_lookup_page++;
} // per object lookup page
if (res == 1) res = SPIFFS_OK;
// calculate score and insert into candidate table
// stoneage sort, but probably not so many blocks
if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
// read erase count
spiffs_obj_id erase_count;
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
sizeof(spiffs_obj_id), (u8_t *)&erase_count);
SPIFFS_CHECK_RES(res);
spiffs_obj_id erase_age;
if (fs->max_erase_count > erase_count) {
erase_age = fs->max_erase_count - erase_count;
} else {
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
}
s32_t score =
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
int cand_ix = 0;
SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
while (cand_ix < max_candidates) {
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
cand_blocks[cand_ix] = cur_block;
cand_scores[cand_ix] = score;
break;
} else if (cand_scores[cand_ix] < score) {
int reorder_cand_ix = max_candidates - 2;
while (reorder_cand_ix >= cand_ix) {
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
reorder_cand_ix--;
}
cand_blocks[cand_ix] = cur_block;
cand_scores[cand_ix] = score;
break;
}
cand_ix++;
}
(*candidate_count)++;
}
cur_entry = 0;
cur_block++;
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
} // per block
return res;
}
typedef enum {
FIND_OBJ_DATA,
MOVE_OBJ_DATA,
MOVE_OBJ_IX,
FINISHED
} spiffs_gc_clean_state;
typedef struct {
spiffs_gc_clean_state state;
spiffs_obj_id cur_obj_id;
spiffs_span_ix cur_objix_spix;
spiffs_page_ix cur_objix_pix;
int stored_scan_entry_index;
u8_t obj_id_found;
} spiffs_gc;
// Empties given block by moving all data into free pages of another block
// Strategy:
// loop:
// scan object lookup for object data pages
// for first found id, check spix and load corresponding object index page to memory
// push object scan lookup entry index
// rescan object lookup, find data pages with same id and referenced by same object index
// move data page, update object index in memory
// when reached end of lookup, store updated object index
// pop object scan lookup entry index
// repeat loop until end of object lookup
// scan object lookup again for remaining object index pages, move to new page in other block
//
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
s32_t res = SPIFFS_OK;
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
int cur_entry = 0;
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
spiffs_gc gc;
spiffs_page_ix cur_pix = 0;
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);
memset(&gc, 0, sizeof(spiffs_gc));
gc.state = FIND_OBJ_DATA;
if (fs->free_cursor_block_ix == bix) {
// move free cursor to next block, cannot use free pages from the block we want to clean
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
fs->free_cursor_obj_lu_entry = 0;
SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
}
while (res == SPIFFS_OK && gc.state != FINISHED) {
SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
gc.obj_id_found = 0;
// scan through lookup pages
int obj_lookup_page = cur_entry / entries_per_page;
u8_t scan = 1;
// check each object lookup page
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
int entry_offset = obj_lookup_page * entries_per_page;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
// check each entry
while (scan && res == SPIFFS_OK &&
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
// act upon object id depending on gc state
switch (gc.state) {
case FIND_OBJ_DATA:
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
gc.obj_id_found = 1;
gc.cur_obj_id = obj_id;
scan = 0;
}
break;
case MOVE_OBJ_DATA:
if (obj_id == gc.cur_obj_id) {
spiffs_page_header p_hdr;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
} else {
spiffs_page_ix new_data_pix;
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
// move page
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
SPIFFS_CHECK_RES(res);
// move wipes obj_lu, reload it
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
} else {
// page is deleted but not deleted in lookup, scrap it
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
new_data_pix = SPIFFS_OBJ_ID_FREE;
}
// update memory representation of object index page with new data page
if (gc.cur_objix_spix == 0) {
// update object index header page
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
} else {
// update object index page
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
}
}
}
break;
case MOVE_OBJ_IX:
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
// found an index object id
spiffs_page_header p_hdr;
spiffs_page_ix new_pix;
// load header
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
// move page
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
// move wipes obj_lu, reload it
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
} else {
// page is deleted but not deleted in lookup, scrap it
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
res = spiffs_page_delete(fs, cur_pix);
if (res == SPIFFS_OK) {
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
}
}
SPIFFS_CHECK_RES(res);
}
break;
default:
scan = 0;
break;
}
cur_entry++;
} // per entry
obj_lookup_page++;
} // per object lookup page
if (res != SPIFFS_OK) break;
// state finalization and switch
switch (gc.state) {
case FIND_OBJ_DATA:
if (gc.obj_id_found) {
// find out corresponding obj ix page and load it to memory
spiffs_page_header p_hdr;
spiffs_page_ix objix_pix;
gc.stored_scan_entry_index = cur_entry;
cur_entry = 0;
gc.state = MOVE_OBJ_DATA;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
SPIFFS_CHECK_RES(res);
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
gc.cur_objix_pix = objix_pix;
} else {
gc.state = MOVE_OBJ_IX;
cur_entry = 0; // restart entry scan index
}
break;
case MOVE_OBJ_DATA: {
// store modified objix (hdr) page
spiffs_page_ix new_objix_pix;
gc.state = FIND_OBJ_DATA;
cur_entry = gc.stored_scan_entry_index;
if (gc.cur_objix_spix == 0) {
// store object index header page
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
SPIFFS_CHECK_RES(res);
} else {
// store object index page
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
}
}
break;
case MOVE_OBJ_IX:
gc.state = FINISHED;
break;
default:
cur_entry = 0;
break;
}
SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
} // while state != FINISHED
return res;
}
#endif // !SPIFFS_READ_ONLY

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,751 +0,0 @@
/*
* spiffs_nucleus.h
*
* Created on: Jun 15, 2013
* Author: petera
*/
/* SPIFFS layout
*
* spiffs is designed for following spi flash characteristics:
* - only big areas of data (blocks) can be erased
* - erasing resets all bits in a block to ones
* - writing pulls ones to zeroes
* - zeroes cannot be pulled to ones, without erase
* - wear leveling
*
* spiffs is also meant to be run on embedded, memory constraint devices.
*
* Entire area is divided in blocks. Entire area is also divided in pages.
* Each block contains same number of pages. A page cannot be erased, but a
* block can be erased.
*
* Entire area must be block_size * x
* page_size must be block_size / (2^y) where y > 2
*
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
*
* BLOCK 0 PAGE 0 object lookup 1
* PAGE 1 object lookup 2
* ...
* PAGE n-1 object lookup n
* PAGE n object data 1
* PAGE n+1 object data 2
* ...
* PAGE n+m-1 object data m
*
* BLOCK 1 PAGE n+m object lookup 1
* PAGE n+m+1 object lookup 2
* ...
* PAGE 2n+m-1 object lookup n
* PAGE 2n+m object data 1
* PAGE 2n+m object data 2
* ...
* PAGE 2n+2m-1 object data m
* ...
*
* n is number of object lookup pages, which is number of pages needed to index all pages
* in a block by object id
* : block_size / page_size * sizeof(obj_id) / page_size
* m is number data pages, which is number of pages in block minus number of lookup pages
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
* thus, n+m is total number of pages in a block
* : block_size / page_size
*
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
*
* Object lookup pages contain object id entries. Each entry represent the corresponding
* data page.
* Assuming a 16 bit object id, an object id being 0xffff represents a free page.
* An object id being 0x0000 represents a deleted page.
*
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
* page 2 : data : data for object id 0008
* page 3 : data : data for object id 0001
* page 4 : data : data for object id 0aaa
* ...
*
*
* Object data pages can be either object index pages or object content.
* All object data pages contains a data page header, containing object id and span index.
* The span index denotes the object page ordering amongst data pages with same object id.
* This applies to both object index pages (when index spans more than one page of entries),
* and object data pages.
* An object index page contains page entries pointing to object content page. The entry index
* in a object index page correlates to the span index in the actual object data page.
* The first object index page (span index 0) is called object index header page, and also
* contains object flags (directory/file), size, object name etc.
*
* ex:
* BLOCK 1
* PAGE 256: objectl lookup page 1
* [*123] [ 123] [ 123] [ 123]
* [ 123] [*123] [ 123] [ 123]
* [free] [free] [free] [free] ...
* PAGE 257: objectl lookup page 2
* [free] [free] [free] [free] ...
* PAGE 258: object index page (header)
* obj.id:0123 span.ix:0000 flags:INDEX
* size:1600 name:ex.txt type:file
* [259] [260] [261] [262]
* PAGE 259: object data page
* obj.id:0123 span.ix:0000 flags:DATA
* PAGE 260: object data page
* obj.id:0123 span.ix:0001 flags:DATA
* PAGE 261: object data page
* obj.id:0123 span.ix:0002 flags:DATA
* PAGE 262: object data page
* obj.id:0123 span.ix:0003 flags:DATA
* PAGE 263: object index page
* obj.id:0123 span.ix:0001 flags:INDEX
* [264] [265] [fre] [fre]
* [fre] [fre] [fre] [fre]
* PAGE 264: object data page
* obj.id:0123 span.ix:0004 flags:DATA
* PAGE 265: object data page
* obj.id:0123 span.ix:0005 flags:DATA
*
*/
#ifndef SPIFFS_NUCLEUS_H_
#define SPIFFS_NUCLEUS_H_
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1)
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1)
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2)
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
#define SPIFFS_EV_IX_UPD 0
#define SPIFFS_EV_IX_NEW 1
#define SPIFFS_EV_IX_DEL 2
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1)
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
#if SPIFFS_USE_MAGIC
#if !SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#else // SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
#endif // SPIFFS_USE_MAGIC_LENGTH
#endif // SPIFFS_USE_MAGIC
#define SPIFFS_CONFIG_MAGIC (0x20090315)
#if SPIFFS_SINGLETON == 0
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
((fs)->cfg.log_page_size)
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \
((fs)->cfg.log_block_size)
#define SPIFFS_CFG_PHYS_SZ(fs) \
((fs)->cfg.phys_size)
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \
((fs)->cfg.phys_erase_block)
#define SPIFFS_CFG_PHYS_ADDR(fs) \
((fs)->cfg.phys_addr)
#endif
// total number of pages
#define SPIFFS_MAX_PAGES(fs) \
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// total number of pages per block, including object lookup pages
#define SPIFFS_PAGES_PER_BLOCK(fs) \
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// number of object lookup pages per block
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) )
// checks if page index belongs to object lookup
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
// number of object lookup entries in all object lookup pages
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))
// converts a block to physical address
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) )
// converts a object lookup entry to page index
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry))
// converts a object lookup entry to physical address of corresponding page
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// converts a page to physical address
#define SPIFFS_PAGE_TO_PADDR(fs, page) \
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// converts a physical address to page
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// gives index in page for a physical address
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// returns containing block for given page
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) )
// returns starting page for block
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) )
// converts page to entry in object lookup page
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) )
// returns data size in a data page
#define SPIFFS_DATA_PAGE_SIZE(fs) \
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
// returns physical address for block's erase count,
// always in the physical last entry of the last object lookup page
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
// returns physical address for block's magic,
// always in the physical second last entry of the last object lookup page
#define SPIFFS_MAGIC_PADDR(fs, bix) \
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
// checks if there is any room for magic in the object luts
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
// define helpers object
// entries in an object header page index
#define SPIFFS_OBJ_HDR_IX_LEN(fs) \
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix))
// entries in an object page index
#define SPIFFS_OBJ_IX_LEN(fs) \
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix))
// object index entry for given data span index
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs)))
// object index span index number for given data span index or entry
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
#define SPIFFS_OP_T_OBJ_LU (0<<0)
#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
#define SPIFFS_OP_T_OBJ_IX (2<<0)
#define SPIFFS_OP_T_OBJ_DA (3<<0)
#define SPIFFS_OP_C_DELE (0<<2)
#define SPIFFS_OP_C_UPDT (1<<2)
#define SPIFFS_OP_C_MOVS (2<<2)
#define SPIFFS_OP_C_MOVD (3<<2)
#define SPIFFS_OP_C_FLSH (4<<2)
#define SPIFFS_OP_C_READ (5<<2)
#define SPIFFS_OP_C_WRTHRU (6<<2)
#define SPIFFS_OP_TYPE_MASK (3<<0)
#define SPIFFS_OP_COM_MASK (7<<2)
// if 0, this page is written to, else clean
#define SPIFFS_PH_FLAG_USED (1<<0)
// if 0, writing is finalized, else under modification
#define SPIFFS_PH_FLAG_FINAL (1<<1)
// if 0, this is an index page, else a data page
#define SPIFFS_PH_FLAG_INDEX (1<<2)
// if 0, page is deleted, else valid
#define SPIFFS_PH_FLAG_DELET (1<<7)
// if 0, this index header is being deleted
#define SPIFFS_PH_FLAG_IXDELE (1<<6)
#define SPIFFS_CHECK_MOUNT(fs) \
((fs)->mounted != 0)
#define SPIFFS_CHECK_CFG(fs) \
((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
#define SPIFFS_CHECK_RES(res) \
do { \
if ((res) < SPIFFS_OK) return (res); \
} while (0);
#define SPIFFS_API_CHECK_MOUNT(fs) \
if (!SPIFFS_CHECK_MOUNT((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
return -1; \
}
#define SPIFFS_API_CHECK_CFG(fs) \
if (!SPIFFS_CHECK_CFG((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
return -1; \
}
#define SPIFFS_API_CHECK_RES(fs, res) \
if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \
return -1; \
}
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \
SPIFFS_UNLOCK(fs); \
return -1; \
}
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
// check id
#define SPIFFS_VIS_CHECK_ID (1<<0)
// report argument object id to visitor - else object lookup id is reported
#define SPIFFS_VIS_CHECK_PH (1<<1)
// stop searching at end of all look up pages
#define SPIFFS_VIS_NO_WRAP (1<<2)
#if SPIFFS_HAL_CALLBACK_EXTRA
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
(_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src))
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
(_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst))
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
(_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len))
#else // SPIFFS_HAL_CALLBACK_EXTRA
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
(_fs)->cfg.hal_write_f((_paddr), (_len), (_src))
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
(_fs)->cfg.hal_read_f((_paddr), (_len), (_dst))
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
(_fs)->cfg.hal_erase_f((_paddr), (_len))
#endif // SPIFFS_HAL_CALLBACK_EXTRA
#if SPIFFS_CACHE
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1)
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2)
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3)
#define SPIFFS_CACHE_FLAG_DATA (1<<4)
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7)
#define SPIFFS_CACHE_PAGE_SIZE(fs) \
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs))
#define spiffs_get_cache(fs) \
((spiffs_cache *)((fs)->cache))
#define spiffs_get_cache_page_hdr(fs, c, ix) \
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])))
#define spiffs_get_cache_page(fs, c, ix) \
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
// cache page struct
typedef struct {
// cache flags
u8_t flags;
// cache page index
u8_t ix;
// last access of this cache page
u32_t last_access;
union {
// type read cache
struct {
// read cache page index
spiffs_page_ix pix;
};
#if SPIFFS_CACHE_WR
// type write cache
struct {
// write cache
spiffs_obj_id obj_id;
// offset in cache page
u32_t offset;
// size of cache page
u16_t size;
};
#endif
};
} spiffs_cache_page;
// cache struct
typedef struct {
u8_t cpage_count;
u32_t last_access;
u32_t cpage_use_map;
u32_t cpage_use_mask;
u8_t *cpages;
} spiffs_cache;
#endif
// spiffs nucleus file descriptor
typedef struct {
// the filesystem of this descriptor
spiffs *fs;
// number of file descriptor - if 0, the file descriptor is closed
spiffs_file file_nbr;
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted
spiffs_obj_id obj_id;
// size of the file
u32_t size;
// cached object index header page index
spiffs_page_ix objix_hdr_pix;
// cached offset object index page index
spiffs_page_ix cursor_objix_pix;
// cached offset object index span index
spiffs_span_ix cursor_objix_spix;
// current absolute offset
u32_t offset;
// current file descriptor offset
u32_t fdoffset;
// fd flags
spiffs_flags flags;
#if SPIFFS_CACHE_WR
spiffs_cache_page *cache_page;
#endif
} spiffs_fd;
// object structs
// page header, part of each page except object lookup pages
// NB: this is always aligned when the data page is an object index,
// as in this case struct spiffs_page_object_ix is used
typedef struct __attribute(( packed )) {
// object id
spiffs_obj_id obj_id;
// object span index
spiffs_span_ix span_ix;
// flags
u8_t flags;
} spiffs_page_header;
// object index header page header
typedef struct __attribute(( packed ))
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
#endif
{
// common page header
spiffs_page_header p_hdr;
// alignment
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
// size of object
u32_t size;
// type of object
spiffs_obj_type type;
// name of object
u8_t name[SPIFFS_OBJ_NAME_LEN];
} spiffs_page_object_ix_header;
// object index page header
typedef struct __attribute(( packed )) {
spiffs_page_header p_hdr;
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
} spiffs_page_object_ix;
// callback func for object lookup visitor
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
const void *user_const_p, void *user_var_p);
#if SPIFFS_CACHE
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst))
#define _spiffs_wr(fs, op, fh, addr, len, src) \
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src))
#else
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
spiffs_phys_rd((fs), (addr), (len), (dst))
#define _spiffs_wr(fs, op, fh, addr, len, src) \
spiffs_phys_wr((fs), (addr), (len), (src))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
// ---------------
s32_t spiffs_phys_rd(
spiffs *fs,
#if SPIFFS_CACHE
u8_t op,
spiffs_file fh,
#endif
u32_t addr,
u32_t len,
u8_t *dst);
s32_t spiffs_phys_wr(
spiffs *fs,
#if SPIFFS_CACHE
u8_t op,
spiffs_file fh,
#endif
u32_t addr,
u32_t len,
u8_t *src);
s32_t spiffs_phys_cpy(
spiffs *fs,
spiffs_file fh,
u32_t dst,
u32_t src,
u32_t len);
s32_t spiffs_phys_count_free_blocks(
spiffs *fs);
s32_t spiffs_obj_lu_find_entry_visitor(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
u8_t flags,
spiffs_obj_id obj_id,
spiffs_visitor_f v,
const void *user_const_p,
void *user_var_p,
spiffs_block_ix *block_ix,
int *lu_entry);
s32_t spiffs_erase_block(
spiffs *fs,
spiffs_block_ix bix);
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
s32_t spiffs_probe(
spiffs_config *cfg);
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
// ---------------
s32_t spiffs_obj_lu_scan(
spiffs *fs);
s32_t spiffs_obj_lu_find_free_obj_id(
spiffs *fs,
spiffs_obj_id *obj_id,
const u8_t *conflicting_name);
s32_t spiffs_obj_lu_find_free(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
spiffs_block_ix *block_ix,
int *lu_entry);
s32_t spiffs_obj_lu_find_id(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
spiffs_obj_id obj_id,
spiffs_block_ix *block_ix,
int *lu_entry);
s32_t spiffs_obj_lu_find_id_and_span(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix exclusion_pix,
spiffs_page_ix *pix);
s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix exclusion_pix,
spiffs_page_ix *pix);
// ---------------
s32_t spiffs_page_allocate_data(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_page_header *ph,
u8_t *data,
u32_t len,
u32_t page_offs,
u8_t finalize,
spiffs_page_ix *pix);
s32_t spiffs_page_move(
spiffs *fs,
spiffs_file fh,
u8_t *page_data,
spiffs_obj_id obj_id,
spiffs_page_header *page_hdr,
spiffs_page_ix src_pix,
spiffs_page_ix *dst_pix);
s32_t spiffs_page_delete(
spiffs *fs,
spiffs_page_ix pix);
// ---------------
s32_t spiffs_object_create(
spiffs *fs,
spiffs_obj_id obj_id,
const u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_obj_type type,
spiffs_page_ix *objix_hdr_pix);
s32_t spiffs_object_update_index_hdr(
spiffs *fs,
spiffs_fd *fd,
spiffs_obj_id obj_id,
spiffs_page_ix objix_hdr_pix,
u8_t *new_objix_hdr_data,
const u8_t name[SPIFFS_OBJ_NAME_LEN],
u32_t size,
spiffs_page_ix *new_pix);
void spiffs_cb_object_event(
spiffs *fs,
spiffs_fd *fd,
int ev,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix new_pix,
u32_t new_size);
s32_t spiffs_object_open_by_id(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_fd *f,
spiffs_flags flags,
spiffs_mode mode);
s32_t spiffs_object_open_by_page(
spiffs *fs,
spiffs_page_ix pix,
spiffs_fd *f,
spiffs_flags flags,
spiffs_mode mode);
s32_t spiffs_object_append(
spiffs_fd *fd,
u32_t offset,
u8_t *data,
u32_t len);
s32_t spiffs_object_modify(
spiffs_fd *fd,
u32_t offset,
u8_t *data,
u32_t len);
s32_t spiffs_object_read(
spiffs_fd *fd,
u32_t offset,
u32_t len,
u8_t *dst);
s32_t spiffs_object_truncate(
spiffs_fd *fd,
u32_t new_len,
u8_t remove_object);
s32_t spiffs_object_find_object_index_header_by_name(
spiffs *fs,
const u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_page_ix *pix);
// ---------------
s32_t spiffs_gc_check(
spiffs *fs,
u32_t len);
s32_t spiffs_gc_erase_page_stats(
spiffs *fs,
spiffs_block_ix bix);
s32_t spiffs_gc_find_candidate(
spiffs *fs,
spiffs_block_ix **block_candidate,
int *candidate_count,
char fs_crammed);
s32_t spiffs_gc_clean(
spiffs *fs,
spiffs_block_ix bix);
s32_t spiffs_gc_quick(
spiffs *fs, u16_t max_free_pages);
// ---------------
s32_t spiffs_fd_find_new(
spiffs *fs,
spiffs_fd **fd);
s32_t spiffs_fd_return(
spiffs *fs,
spiffs_file f);
s32_t spiffs_fd_get(
spiffs *fs,
spiffs_file f,
spiffs_fd **fd);
#if SPIFFS_CACHE
void spiffs_cache_init(
spiffs *fs);
void spiffs_cache_drop_page(
spiffs *fs,
spiffs_page_ix pix);
#if SPIFFS_CACHE_WR
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
spiffs *fs,
spiffs_fd *fd);
void spiffs_cache_fd_release(
spiffs *fs,
spiffs_cache_page *cp);
spiffs_cache_page *spiffs_cache_page_get_by_fd(
spiffs *fs,
spiffs_fd *fd);
#endif
#endif
s32_t spiffs_lookup_consistency_check(
spiffs *fs,
u8_t check_all_objects);
s32_t spiffs_page_consistency_check(
spiffs *fs);
s32_t spiffs_object_index_consistency_check(
spiffs *fs);
#endif /* SPIFFS_NUCLEUS_H_ */

View File

@@ -1,60 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* \file syscalls.h
*
* Implementation of newlib syscall.
*
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
#ifdef __cplusplus
extern "C" {
#endif
extern caddr_t _sbrk( int incr ) ;
extern int link( char *cOld, char *cNew ) ;
extern int _close( int file ) ;
extern int _fstat( int file, struct stat *st ) ;
extern int _isatty( int file ) ;
extern int _lseek( int file, int ptr, int dir ) ;
extern int _read(int file, char *ptr, int len) ;
extern int _write( int file, char *ptr, int len ) ;
#ifdef __cplusplus
}
#endif

View File

@@ -16,107 +16,80 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "Arduino.h" #include <Arduino.h>
#include <cmsis_os.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "cmsis_os.h"
#ifndef portNVIC_SYSTICK_CURRENT_VALUE_REG #ifndef portNVIC_SYSTICK_CURRENT_VALUE_REG
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) #define portNVIC_SYSTICK_CURRENT_VALUE_REG (*((volatile uint32_t *)0xe000e018))
#endif #endif
extern uint32_t xTaskGetTickCount(); extern uint32_t xTaskGetTickCount();
extern uint32_t xTaskGetTickCountFromISR(); extern uint32_t xTaskGetTickCountFromISR();
static __inline uint32_t __get_ipsr__(void) static __inline uint32_t __get_ipsr__(void) {
{ volatile uint32_t __regIPSR __asm("ipsr");
volatile uint32_t __regIPSR __asm("ipsr"); return (__regIPSR);
return(__regIPSR);
} }
void init(void) void init(void) {
{ // nop
// nop
} }
void delay( uint32_t ms ) void delay(uint32_t ms) {
{ /* osStatus ret; */
osStatus ret;
ret = osDelay(ms); /* ret = */ osDelay(ms);
if ( (ret != osEventTimeout) && (ret != osOK) ) { /* if ((ret != osEventTimeout) && (ret != osOK)) {
//printf("delay : ERROR : 0x%x \n", ret); printf("delay : ERROR : 0x%x \n", ret);
} */
}
void delayMicroseconds(unsigned int us) {
int i;
uint32_t t0, tn;
int dfactor = 20 * us - 10 + (81 * us / 100);
if (us > 100) {
t0 = micros();
do {
tn = micros();
} while (tn >= t0 && tn < (t0 + us - 1));
} else {
for (i = 0; i < dfactor; i++) {
asm("nop");
}
} }
} }
void delayMicroseconds(unsigned int us) uint32_t millis(void) {
{ return (__get_ipsr__() == 0) ? xTaskGetTickCount() : xTaskGetTickCountFromISR();
int i;
uint32_t t0, tn;
int dfactor = 0;
#if defined(BOARD_RTL8710)
// dfactor = 10 * us - 10 + (40 * us / 100);
dfactor = 20 * us - 10 + (81 * us / 100);
#elif defined(BOARD_RTL8195A)
dfactor = 20 * us - 10 + (81 * us / 100);
#else
dfactor = 20 * us - 10 + (81 * us / 100);
#endif
if ( us > 100 ) {
t0 = micros();
do {
tn = micros();
} while ( tn >= t0 && tn < (t0 + us - 1) );
} else {
for (i=0; i<dfactor; i++) {
asm("nop");
}
}
} }
uint32_t millis( void ) uint32_t micros(void) {
{ uint32_t tick1, tick2;
return (__get_ipsr__() == 0) ? xTaskGetTickCount() : xTaskGetTickCountFromISR(); uint32_t us;
uint32_t tick_per_us = 166666;
if (__get_ipsr__() == 0) {
tick1 = xTaskGetTickCount();
us = portNVIC_SYSTICK_CURRENT_VALUE_REG;
tick2 = xTaskGetTickCount();
} else {
tick1 = xTaskGetTickCountFromISR();
us = portNVIC_SYSTICK_CURRENT_VALUE_REG;
tick2 = xTaskGetTickCountFromISR();
}
if (tick1 == tick2) {
return tick1 * 1000 - us * 1000 / tick_per_us;
} else if ((us * 1000 / tick_per_us) < 500) {
return tick1 * 1000 - us * 1000 / tick_per_us;
} else {
return tick1 * 1000;
}
} }
uint32_t micros( void ) void yield(void) {
{ vTaskDelay(1);
uint32_t tick1, tick2; taskYIELD();
uint32_t us;
uint32_t tick_per_us;
#if defined(BOARD_RTL8710)
tick_per_us = 166666; // 83333;
#elif defined(BOARD_RTL8195A)
tick_per_us = 166666;
#else
tick_per_us = 166666;
#endif
if (__get_ipsr__() == 0) {
tick1 = xTaskGetTickCount();
us = portNVIC_SYSTICK_CURRENT_VALUE_REG;
tick2 = xTaskGetTickCount();
} else {
tick1 = xTaskGetTickCountFromISR();
us = portNVIC_SYSTICK_CURRENT_VALUE_REG;
tick2 = xTaskGetTickCountFromISR();
}
if (tick1 == tick2) {
return tick1 * 1000 - us*1000 / tick_per_us;
} else if( (us*1000 / tick_per_us) < 500 ) {
return tick1 * 1000 - us*1000 / tick_per_us;
} else {
return tick1 * 1000;
}
} }
#ifdef __cplusplus
}
#endif

View File

@@ -16,225 +16,190 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
// RTL8710 module do not support ADC/DAC #include <Arduino.h>
#if 1 // !defined(BOARD_RTL8710) #include <analogin_api.h>
#include <analogout_api.h>
#include "Arduino.h" #include <gpio_ex_api.h>
#include <pwmout_api.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "analogin_api.h"
#include "analogout_api.h"
#include "pwmout_api.h"
#include "gpio_ex_api.h"
/* ADC */ /* ADC */
analogin_t adc1; analogin_t adc1;
analogin_t adc2; analogin_t adc2;
analogin_t adc3; analogin_t adc3;
static const float ADC_slope1 = (3.12)/(3410.0-674.0); static const float ADC_slope1 = (3.12) / (3410.0 - 674.0);
static const float ADC_slope2 = (3.3-3.12)/(3454.0-3410.0); static const float ADC_slope2 = (3.3 - 3.12) / (3454.0 - 3410.0);
bool g_adc_enabled[] = { bool g_adc_enabled[] = {false, false, false};
false, false, false
};
extern void *gpio_pin_struct[]; extern void *gpio_pin_struct[];
// extern void pinRemoveMode(pin_size_t pinNumber);
// Arduino
//
static int _readResolution = 10; static int _readResolution = 10;
static int _writeResolution = 8; static int _writeResolution = 8;
static int _writePeriod = 20000; static int _writePeriod = 20000;
void analogReadResolution(int res) { void analogReadResolution(int res) {
_readResolution = res; _readResolution = res;
} }
void analogWriteResolution(int res) { void analogWriteResolution(int res) {
_writeResolution = res; _writeResolution = res;
} }
void analogWritePeriod(int us) { void analogWritePeriod(int us) {
_writePeriod = us; _writePeriod = us;
} }
static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) { static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) {
if (from == to) if (from == to)
return value; return value;
if (from > to) if (from > to)
return value >> (from-to); return value >> (from - to);
else else
return value << (to-from); return value << (to - from);
} }
uint8_t analog_reference = AR_DEFAULT; uint8_t analog_reference = AR_DEFAULT;
void analogReference(uint8_t mode) void analogReference(uint8_t mode) {
{ analog_reference = mode;
analog_reference = mode;
} }
int analogRead(pin_size_t pinNumber) int analogRead(pin_size_t pinNumber) {
{ uint32_t ulValue = 0;
uint32_t ulValue = 0; uint32_t ulChannel;
uint32_t ulChannel; uint16_t ret = 0;
uint16_t ret = 0; float voltage;
float voltage; float adc_value;
float adc_value;
switch ( pinNumber ) { switch (pinNumber) {
case PIN_A0: case PIN_A0:
if (g_adc_enabled[0] == false) if (g_adc_enabled[0] == false) {
{ analogin_init(&adc1, AD_1);
analogin_init(&adc1, AD_1); g_adc_enabled[0] = true;
g_adc_enabled[0] = true; }
} case PIN_A1:
case PIN_A1: if (g_adc_enabled[1] == false) {
if (g_adc_enabled[1] == false) analogin_init(&adc2, AD_2);
{ g_adc_enabled[1] = true;
analogin_init(&adc2, AD_2); }
g_adc_enabled[1] = true; ret = analogin_read_u16(&adc2);
} break;
ret = analogin_read_u16(&adc2); case PIN_A2:
break; if (g_adc_enabled[2] == false) {
case PIN_A2: analogin_init(&adc3, AD_3);
if (g_adc_enabled[2] == false) g_adc_enabled[2] = true;
{ }
analogin_init(&adc3, AD_3); ret = analogin_read_u16(&adc3);
g_adc_enabled[2] = true; break;
} default:
ret = analogin_read_u16(&adc3); printf("%s : pinNumber %d wrong\n", __FUNCTION__, pinNumber);
break; return 0;
default: }
printf("%s : pinNumber %d wrong\n", __FUNCTION__, pinNumber);
return 0;
}
ret >>= 4; ret >>= 4;
if (ret < 674) { if (ret < 674) {
voltage = 0; voltage = 0;
} else if ( ret > 3410){ } else if (ret > 3410) {
voltage = (float)(ret - 3410)*ADC_slope2 + 3.12; voltage = (float)(ret - 3410) * ADC_slope2 + 3.12;
} else { } else {
voltage = (float)(ret-674)*ADC_slope1; voltage = (float)(ret - 674) * ADC_slope1;
} }
ret = round((1<<_readResolution)*voltage/3.3);
if ( ret >= (1<<_readResolution) ) ret = (1<<_readResolution) - 1;
return ret;
ret = round((1 << _readResolution) * voltage / 3.3);
if (ret >= (1 << _readResolution))
ret = (1 << _readResolution) - 1;
return ret;
} }
void analogOutputInit(void) { void analogOutputInit(void) {
// nop // nop
} }
// Right now, PWM output only works on the pins with // Right now, PWM output only works on the pins with
// hardware support. These are defined in the appropriate // hardware support. These are defined in the appropriate
// pins_*.c file. For the rest of the pins, we default // pins_*.c file. For the rest of the pins, we default
// to digital output. // to digital output.
void analogWrite(pin_size_t pinNumber, int value) void analogWrite(pin_size_t pinNumber, int value) {
{ pwmout_t *obj;
pwmout_t *obj;
if ((g_APinDescription[pinNumber].ulPinAttribute & PIO_PWM) == PIO_PWM) { if ((g_APinDescription[pinNumber].ulPinAttribute & PIO_PWM) == PIO_PWM) {
/* Handle */ /* Handle */
if ( g_APinDescription[pinNumber].ulPinType != PIO_PWM ) if (g_APinDescription[pinNumber].ulPinType != PIO_PWM) {
{ if ((g_APinDescription[pinNumber].ulPinType == PIO_GPIO) ||
if ( (g_APinDescription[pinNumber].ulPinType == PIO_GPIO) || (g_APinDescription[pinNumber].ulPinType == PIO_GPIO_IRQ) ) { (g_APinDescription[pinNumber].ulPinType == PIO_GPIO_IRQ)) {
pinRemoveMode(pinNumber); pinRemoveMode(pinNumber);
} }
gpio_pin_struct[pinNumber] = malloc ( sizeof(pwmout_t) ); gpio_pin_struct[pinNumber] = malloc(sizeof(pwmout_t));
pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber]; pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber];
pwmout_init( obj, g_APinDescription[pinNumber].pinname ); pwmout_init(obj, g_APinDescription[pinNumber].pinname);
pwmout_period_us( obj, _writePeriod); pwmout_period_us(obj, _writePeriod);
pwmout_write( obj, value * 1.0 / (1<<_writeResolution)); pwmout_write(obj, value * 1.0 / (1 << _writeResolution));
g_APinDescription[pinNumber].ulPinType = PIO_PWM; g_APinDescription[pinNumber].ulPinType = PIO_PWM;
g_APinDescription[pinNumber].ulPinMode = PWM_MODE_ENABLED; g_APinDescription[pinNumber].ulPinMode = PWM_MODE_ENABLED;
} else { } else {
pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber]; pwmout_t *obj = (pwmout_t *)gpio_pin_struct[pinNumber];
pwmout_period_us( obj, _writePeriod); pwmout_period_us(obj, _writePeriod);
pwmout_write( obj, value * 1.0 / (1<<_writeResolution)); pwmout_write(obj, value * 1.0 / (1 << _writeResolution));
/* if ( g_APinDescription[pinNumber].ulPinMode == PWM_MODE_DISABLED ) { /* if ( g_APinDescription[pinNumber].ulPinMode == PWM_MODE_DISABLED ) {
HAL_Pwm_Enable( &obj->pwm_hal_adp ); HAL_Pwm_Enable( &obj->pwm_hal_adp );
} */ } */
} }
} }
} }
typedef struct _tone_argument { typedef struct _tone_argument {
uint32_t ulPin; uint32_t ulPin;
uint32_t timer_id; uint32_t timer_id;
}; };
void _tone_timer_handler(void const *argument) void _tone_timer_handler(const void *argument) {
{ struct _tone_argument *arg = (struct _tone_argument *)argument;
struct _tone_argument *arg = (struct _tone_argument *)argument;
uint32_t ulPin = (uint32_t) argument; uint32_t ulPin = (uint32_t)argument;
noTone(arg->ulPin); noTone(arg->ulPin);
os_timer_delete(arg->timer_id); os_timer_delete(arg->timer_id);
free( (struct _tone_argument *) arg ); free((struct _tone_argument *)arg);
} }
void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) void _tone(uint32_t ulPin, unsigned int frequency, unsigned long duration) {
{ pwmout_t *obj;
pwmout_t *obj;
if ((g_APinDescription[ulPin].ulPinAttribute & PIO_PWM) != PIO_PWM) { if ((g_APinDescription[ulPin].ulPinAttribute & PIO_PWM) != PIO_PWM) {
return; return;
} }
if ( g_APinDescription[ulPin].ulPinType != PIO_PWM ) if (g_APinDescription[ulPin].ulPinType != PIO_PWM) {
{ if ((g_APinDescription[ulPin].ulPinType == PIO_GPIO) || (g_APinDescription[ulPin].ulPinType == PIO_GPIO_IRQ)) {
if ( (g_APinDescription[ulPin].ulPinType == PIO_GPIO) || (g_APinDescription[ulPin].ulPinType == PIO_GPIO_IRQ) ) { pinRemoveMode(ulPin);
pinRemoveMode(ulPin); }
} gpio_pin_struct[ulPin] = malloc(sizeof(pwmout_t));
gpio_pin_struct[ulPin] = malloc ( sizeof(pwmout_t) ); pwmout_t *obj = (pwmout_t *)gpio_pin_struct[ulPin];
pwmout_t *obj = (pwmout_t *)gpio_pin_struct[ulPin]; pwmout_init(obj, g_APinDescription[ulPin].pinname);
pwmout_init( obj, g_APinDescription[ulPin].pinname); pwmout_period(obj, 1.0 / frequency);
pwmout_period( obj, 1.0/frequency ); pwmout_pulsewidth(obj, 1.0 / (frequency * 2));
pwmout_pulsewidth( obj, 1.0/(frequency * 2) ); g_APinDescription[ulPin].ulPinType = PIO_PWM;
g_APinDescription[ulPin].ulPinType = PIO_PWM; g_APinDescription[ulPin].ulPinMode = PWM_MODE_ENABLED;
g_APinDescription[ulPin].ulPinMode = PWM_MODE_ENABLED;
} else { } else {
// There is already a PWM configured // There is already a PWM configured
pwmout_t *obj = (pwmout_t *)gpio_pin_struct[ulPin]; pwmout_t *obj = (pwmout_t *)gpio_pin_struct[ulPin];
pwmout_period( obj, 1.0/frequency ); pwmout_period(obj, 1.0 / frequency);
pwmout_pulsewidth( obj, 1.0/(frequency * 2)); pwmout_pulsewidth(obj, 1.0 / (frequency * 2));
/* if (g_APinDescription[ulPin].ulPinMode == PWM_MODE_DISABLED) { /* if (g_APinDescription[ulPin].ulPinMode == PWM_MODE_DISABLED) {
HAL_Pwm_Enable( &obj->pwm_hal_adp ); HAL_Pwm_Enable( &obj->pwm_hal_adp );
} */ } */
} }
if (duration > 0) { if (duration > 0) {
struct _tone_argument *arg = (struct _tone_argument *) malloc ( sizeof(struct _tone_argument) ); struct _tone_argument *arg = (struct _tone_argument *)malloc(sizeof(struct _tone_argument));
arg->ulPin = ulPin; arg->ulPin = ulPin;
arg->timer_id = os_timer_create(_tone_timer_handler, 0, arg); arg->timer_id = os_timer_create(_tone_timer_handler, 0, arg);
os_timer_start(arg->timer_id, duration); os_timer_start(arg->timer_id, duration);
} }
} }
void noTone(uint32_t ulPin)
{
pinRemoveMode(ulPin);
}
#ifdef __cplusplus
}
#endif
//#else // #if !defined(BOARD_RTL8710)
#endif // #if !defined(BOARD_RTL8710)

View File

@@ -1,13 +1,8 @@
#include "Arduino.h" #include <Arduino.h>
#include <gpio_api.h>
#ifdef __cplusplus #include <gpio_irq_api.h>
extern "C" { #include <gpio_irq_ex_api.h>
#endif #include <pwmout_api.h>
#include "gpio_api.h"
#include "gpio_irq_api.h"
#include "gpio_irq_ex_api.h"
#include "pwmout_api.h"
extern void *gpio_pin_struct[PINS_COUNT]; extern void *gpio_pin_struct[PINS_COUNT];
extern void *gpio_irq_handler_list[PINS_COUNT]; extern void *gpio_irq_handler_list[PINS_COUNT];
@@ -33,7 +28,7 @@ void pinRemoveMode(pin_size_t pinNumber) {
gpio_irq_deinit(obj); gpio_irq_deinit(obj);
free(obj); free(obj);
} }
gpio_pin_struct[pinNumber] = NULL; gpio_pin_struct[pinNumber] = NULL;
g_APinDescription[pinNumber].ulPinType = NOT_INITIAL; g_APinDescription[pinNumber].ulPinType = NOT_INITIAL;
g_APinDescription[pinNumber].ulPinMode = NOT_INITIAL; g_APinDescription[pinNumber].ulPinMode = NOT_INITIAL;
} }
@@ -59,7 +54,7 @@ void pinMode(pin_size_t pinNumber, PinModeArduino pinMode) {
if (g_APinDescription[pinNumber].ulPinType == NOT_INITIAL) { if (g_APinDescription[pinNumber].ulPinType == NOT_INITIAL) {
// allocate memory if pin not used before // allocate memory if pin not used before
gpio = malloc(sizeof(gpio_t)); gpio = malloc(sizeof(gpio_t));
gpio_pin_struct[pinNumber] = gpio; gpio_pin_struct[pinNumber] = gpio;
gpio_init(gpio, g_APinDescription[pinNumber].pinname); gpio_init(gpio, g_APinDescription[pinNumber].pinname);
g_APinDescription[pinNumber].ulPinType = PIO_GPIO; g_APinDescription[pinNumber].ulPinType = PIO_GPIO;
@@ -74,23 +69,23 @@ void pinMode(pin_size_t pinNumber, PinModeArduino pinMode) {
switch (pinMode) { switch (pinMode) {
case INPUT: case INPUT:
dir = PIN_INPUT; dir = PIN_INPUT;
mode = PullNone; mode = PullNone;
break; break;
case INPUT_PULLDOWN: case INPUT_PULLDOWN:
dir = PIN_INPUT; dir = PIN_INPUT;
mode = PullDown; mode = PullDown;
break; break;
case INPUT_PULLUP: case INPUT_PULLUP:
dir = PIN_INPUT; dir = PIN_INPUT;
mode = PullUp; mode = PullUp;
break; break;
case OUTPUT: case OUTPUT:
dir = PIN_OUTPUT; dir = PIN_OUTPUT;
mode = PullNone; mode = PullNone;
break; break;
case OUTPUT_OPENDRAIN: case OUTPUT_OPENDRAIN:
dir = PIN_OUTPUT; dir = PIN_OUTPUT;
mode = OpenDrain; mode = OpenDrain;
break; break;
default: default:
@@ -138,7 +133,3 @@ uint32_t digitalPinToBitMask(uint32_t pinNumber) {
uint32_t pin_name = HAL_GPIO_GetPinName(g_APinDescription[pinNumber].pinname); uint32_t pin_name = HAL_GPIO_GetPinName(g_APinDescription[pinNumber].pinname);
return 1 << (HAL_GPIO_GET_PIN_BY_NAME(pin_name)); return 1 << (HAL_GPIO_GET_PIN_BY_NAME(pin_name));
} }
#ifdef __cplusplus
}
#endif

View File

@@ -1,118 +0,0 @@
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "wiring_os.h"
#include "cmsis_os.h"
uint32_t os_thread_create( void (*task)(const void *argument), void *argument, int priority, uint32_t stack_size ) {
osThreadDef_t thread_def;
thread_def.pthread = task;
thread_def.tpriority = (osPriority)priority;
// the underlying freertos implementation on cmsis os divide stack size by 4
thread_def.stacksize = stack_size * 4;
thread_def.name = "ARDUINO";
return (uint32_t)osThreadCreate(&thread_def, argument);
}
uint32_t os_thread_get_id( void ) {
return osThreadGetId();
}
uint32_t os_thread_terminate( uint32_t thread_id ) {
return (uint32_t)osThreadTerminate(thread_id);
}
uint32_t os_thread_yield( void ) {
return (uint32_t)osThreadYield();
}
uint32_t os_thread_set_priority( uint32_t thread_id, int priority ) {
return (uint32_t)osThreadSetPriority(thread_id, (osPriority)priority);
}
int os_thread_get_priority( uint32_t thread_id ) {
return (int)osThreadGetPriority(thread_id);
}
int32_t os_signal_set( uint32_t thread_id, int32_t signals ) {
return osSignalSet(thread_id, signals);
}
int32_t os_signal_clear( uint32_t thread_id, int32_t signals ) {
return osSignalClear(thread_id, signals);
}
os_event_t os_signal_wait( int32_t signals, uint32_t millisec ) {
osEvent evt;
os_event_t ret;
evt = osSignalWait(signals, millisec);
ret.status = (uint32_t)evt.status;
ret.value.signals = evt.value.signals;
ret.def.message_id = evt.def.message_id;
return ret;
}
typedef void (*os_ptimer) (void const *argument);
uint32_t os_timer_create(void (*callback)(void const *argument), uint8_t isPeriodic, void *argument) {
osTimerDef_t *pTimerDef;
pTimerDef = (osTimerDef_t *) malloc ( sizeof(osTimerDef_t) );
pTimerDef->ptimer = callback;
pTimerDef->custom = (struct os_timer_custom *) malloc ( sizeof (struct os_timer_custom) );
return osTimerCreate(pTimerDef, (isPeriodic ? osTimerPeriodic : osTimerOnce), argument);
}
uint32_t os_timer_start (uint32_t timer_id, uint32_t millisec) {
return osTimerStart (timer_id, millisec);
}
uint32_t os_timer_stop (uint32_t timer_id) {
return osTimerStop(timer_id);
}
uint32_t os_timer_delete(uint32_t timer_id) {
osTimerDef_t *pTimerDef;
pTimerDef = (osTimerDef_t *) pvTimerGetTimerID(timer_id);
free (pTimerDef->custom);
free (pTimerDef);
return osTimerDelete(timer_id);
}
uint32_t os_semaphore_create(int32_t count) {
return (uint32_t)osSemaphoreCreate(NULL, count);
}
int32_t os_semaphore_wait(uint32_t semaphore_id, uint32_t millisec) {
if (osSemaphoreWait((osSemaphoreId)semaphore_id, millisec) == 0) {
return 1;
} else {
return 0;
}
}
uint32_t os_semaphore_release(uint32_t semaphore_id) {
return (uint32_t)osSemaphoreRelease((osSemaphoreId)semaphore_id);
}
uint32_t os_semaphore_delete(uint32_t semaphore_id) {
return (uint32_t)osSemaphoreDelete((osSemaphoreId)semaphore_id);
}
#ifdef __cplusplus
}
#endif

View File

@@ -8,7 +8,7 @@
This library is distributed in the hope that it will be useful, This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
@@ -16,16 +16,11 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "Arduino.h" #include <Arduino.h>
#include <PinNames.h>
#ifdef __cplusplus #include <gpio_api.h>
extern "C" { #include <objects.h>
#endif #include <us_ticker_api.h>
#include "PinNames.h"
#include "objects.h"
#include "gpio_api.h"
#include "us_ticker_api.h"
extern void *gpio_pin_struct[]; extern void *gpio_pin_struct[];
@@ -33,8 +28,7 @@ extern void *gpio_pin_struct[];
* or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds
* to 3 minutes in length, but must be called at least a few dozen microseconds * to 3 minutes in length, but must be called at least a few dozen microseconds
* before the start of the pulse. */ * before the start of the pulse. */
extern unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) extern unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) {
{
// cache the port and bit of the pin in order to speed up the // cache the port and bit of the pin in order to speed up the
// pulse width measuring loop and achieve finer resolution. calling // pulse width measuring loop and achieve finer resolution. calling
// digitalRead() instead yields much coarser resolution. // digitalRead() instead yields much coarser resolution.
@@ -42,12 +36,12 @@ extern unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
uint32_t start_ticks, cur_ticks; uint32_t start_ticks, cur_ticks;
if ( pin < 0 || pin > PINS_COUNT || (g_APinDescription[pin].pinname == NC) ) return 0; if (pin < 0 || pin > PINS_COUNT || (g_APinDescription[pin].pinname == NC))
return 0;
/* Handle */ /* Handle */
if ( g_APinDescription[pin].ulPinType != PIO_GPIO ) if (g_APinDescription[pin].ulPinType != PIO_GPIO) {
{ return 0;
return 0;
} }
pGpio_t = (gpio_t *)gpio_pin_struct[pin]; pGpio_t = (gpio_t *)gpio_pin_struct[pin];
@@ -56,20 +50,23 @@ extern unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
start_ticks = us_ticker_read(); start_ticks = us_ticker_read();
while (gpio_read(pGpio_t) == state) { while (gpio_read(pGpio_t) == state) {
cur_ticks = us_ticker_read(); cur_ticks = us_ticker_read();
if ( cur_ticks - start_ticks > timeout ) return 0; if (cur_ticks - start_ticks > timeout)
return 0;
} }
// wait for the pulse to start // wait for the pulse to start
while (gpio_read(pGpio_t) != state) { while (gpio_read(pGpio_t) != state) {
cur_ticks = us_ticker_read(); cur_ticks = us_ticker_read();
if ( cur_ticks - start_ticks > timeout ) return 0; if (cur_ticks - start_ticks > timeout)
return 0;
} }
// wait for the pulse to stop // wait for the pulse to stop
start_ticks = us_ticker_read(); start_ticks = us_ticker_read();
while (gpio_read(pGpio_t) == state) { while (gpio_read(pGpio_t) == state) {
cur_ticks = us_ticker_read(); cur_ticks = us_ticker_read();
if ( cur_ticks - start_ticks > timeout ) return 0; if (cur_ticks - start_ticks > timeout)
return 0;
} }
cur_ticks = us_ticker_read(); cur_ticks = us_ticker_read();
@@ -80,7 +77,3 @@ extern unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
// the interrupt handlers. // the interrupt handlers.
return cur_ticks - start_ticks; return cur_ticks - start_ticks;
} }
#ifdef __cplusplus
}
#endif

View File

@@ -1,71 +0,0 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Arduino.h"
#ifdef __cplusplus
extern "C"{
#endif
uint8_t shiftIn(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder)
{
uint8_t value = 0 ;
uint8_t i ;
for ( i=0 ; i < 8 ; ++i )
{
digitalWrite( dataPin, HIGH ) ;
if ( bitOrder == LSBFIRST )
{
value |= digitalRead( dataPin ) << i ;
}
else
{
value |= digitalRead( dataPin ) << (7 - i) ;
}
digitalWrite( clockPin, LOW ) ;
}
return value ;
}
void shiftOut(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder, uint8_t val)
{
uint8_t i ;
for ( i=0 ; i < 8 ; i++ )
{
if ( bitOrder == LSBFIRST )
{
digitalWrite( dataPin, !!(val & (1 << i)) ) ;
}
else
{
digitalWrite( dataPin, !!(val & (1 << (7 - i))) ) ;
}
digitalWrite( clockPin, HIGH ) ;
digitalWrite( clockPin, LOW ) ;
}
}
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,37 +0,0 @@
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "wdt_api.h"
extern uint32_t ConfigDebugErr;
void wdt_reset() {
watchdog_refresh();
}
void wdt_enable(uint32_t timeout_ms) {
uint32_t backup_ConfigDebugErr;
backup_ConfigDebugErr = ConfigDebugErr;
ConfigDebugErr = 0x00000000;
if (timeout_ms > 8000) {
timeout_ms = 8000;
}
watchdog_init(timeout_ms);
watchdog_start();
ConfigDebugErr = backup_ConfigDebugErr;
}
void wdt_disable() {
watchdog_stop();
}
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,46 +0,0 @@
/** @file wiring_watchdog.h */
/**
* @defgroup wiring_watchdog wiring_watchdog
* watchdog is used for reboot system when device hang
* @{
*/
#ifndef _WIRING_WATCHDOG_H_
#define _WIRING_WATCHDOG_H_
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup watchdog */
/**
* @ingroup watchdog
* Feed watchdog to avoid it barks
*/
extern void wdt_reset();
/**
* @ingroup watchdog
* @brief Enable watchdog
*
* After enbling watchdog, user defined code needs to reset it before watchdog barks. Otherwise it would make system reboot.
*
* @param[in] timeout_ms The timeout value that watchdog barks. The maximum value is 8 seconds.
*/
extern void wdt_enable(uint32_t timeout_ms);
/**
* @ingroup watchdog
* Disable watchdog
*/
extern void wdt_disable();
#ifdef __cplusplus
}
#endif
#endif /* _WIRING_WATCHDOG_H_ */
/** @} */ // end of group wiring_watchdog

View File

@@ -1,98 +0,0 @@
/*
wl_definitions.h - Library for Arduino Wifi shield.
Copyright (c) 2011-2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* wl_definitions.h
*
* Created on: Mar 6, 2011
* Author: dlafauci
*/
#ifndef WL_DEFINITIONS_H_
#define WL_DEFINITIONS_H_
// Maximum size of a SSID
#define WL_SSID_MAX_LENGTH 32
// Length of passphrase. Valid lengths are 8-63.
#define WL_WPA_KEY_MAX_LENGTH 63
// Length of key in bytes. Valid values are 5 and 13.
#define WL_WEP_KEY_MAX_LENGTH 13
// Size of a MAC-address or BSSID
#define WL_MAC_ADDR_LENGTH 6
// Size of a MAC-address or BSSID
#define WL_IPV4_LENGTH 4
// Maximum size of a SSID list
#define WL_NETWORKS_LIST_MAXNUM 50
// Maxmium number of socket
#define MAX_SOCK_NUM 4
// Socket not available constant
#define SOCK_NOT_AVAIL 255
// Default state value for Wifi state field
#define NA_STATE -1
//Maximum number of attempts to establish wifi connection
#define WL_MAX_ATTEMPT_CONNECTION 10
typedef enum {
WL_NO_SHIELD = 255,
WL_IDLE_STATUS = 0,
WL_NO_SSID_AVAIL,
WL_SCAN_COMPLETED,
WL_CONNECTED,
WL_CONNECT_FAILED,
WL_CONNECTION_LOST,
WL_DISCONNECTED
} wl_status_t;
/* Encryption modes */
enum wl_enc_type { /* Values map to 802.11 encryption suites... */
ENC_TYPE_WEP = 5,
ENC_TYPE_TKIP = 2,
ENC_TYPE_CCMP = 4,
/* ... except these two, 7 and 8 are reserved in 802.11-2007 */
ENC_TYPE_NONE = 7,
ENC_TYPE_AUTO = 8
};
/* RTK added type */
#ifndef WEP_ENABLED
#define WEP_ENABLED 0x0001
#define TKIP_ENABLED 0x0002
#define AES_ENABLED 0x0004
#define WSEC_SWFLAG 0x0008
#define SHARED_ENABLED 0x00008000
#define WPA_SECURITY 0x00200000
#define WPA2_SECURITY 0x00400000
#define WPS_ENABLED 0x10000000
#endif // #ifndef WEP_ENABLED
/* redefined from enum rtw_security_t */
#define SECURITY_OPEN ( 0 )
#define SECURITY_WEP_PSK ( WEP_ENABLED )
#define SECURITY_WEP_SHARED ( WEP_ENABLED | SHARED_ENABLED )
#define SECURITY_WPA_TKIP_PSK ( WPA_SECURITY | TKIP_ENABLED )
#define SECURITY_WPA_AES_PSK ( WPA_SECURITY | AES_ENABLED )
#define SECURITY_WPA2_AES_PSK ( WPA2_SECURITY | AES_ENABLED )
#define SECURITY_WPA2_TKIP_PSK ( WPA2_SECURITY | TKIP_ENABLED )
#define SECURITY_WPA2_MIXED_PSK ( WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED )
#define SECURITY_WPA_WPA2_MIXED ( WPA_SECURITY | WPA2_SECURITY )
#endif /* WL_DEFINITIONS_H_ */

View File

@@ -1,49 +0,0 @@
/*
wl_types.h - Library for Arduino Wifi shield.
Copyright (c) 2011-2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* wl_types.h
*
* Created on: Jul 30, 2010
* Author: dlafauci
*/
#ifndef _WL_TYPES_H_
#define _WL_TYPES_H_
#include <inttypes.h>
typedef enum {
WL_FAILURE = -1,
WL_SUCCESS = 1,
} wl_error_code_t;
/* Authentication modes */
enum wl_auth_mode {
AUTH_MODE_INVALID,
AUTH_MODE_AUTO,
AUTH_MODE_OPEN_SYSTEM,
AUTH_MODE_SHARED_KEY,
AUTH_MODE_WPA,
AUTH_MODE_WPA2,
AUTH_MODE_WPA_PSK,
AUTH_MODE_WPA2_PSK
};
#endif //_WL_TYPES_H_

View File

@@ -1,108 +0,0 @@
#ifndef WSCLIENT_H
#define WSCLIENT_H
#include <websocket/libwsclient.h>
/******Define the maximum bytes of data send and receive********/
#define MAX_DATA_LEN 1500
/****************Define if using the polarssl*******************/
#define USING_SSL
/******************Define the function used*********************/
#ifdef USING_SSL
int wss_set_fun_ops(wsclient_context *wsclient);
#define wsclient_set_fun_ops(wsclient) wss_set_fun_ops(wsclient)
#else
int ws_set_fun_ops(wsclient_context *wsclient);
#define wsclient_set_fun_ops(wsclient) ws_set_fun_ops(wsclient)
#endif
/***************************************************************/
/*************************************************************************************************
** Function Name : create_wsclient
** Description : Creating the websocket client context structure
** Input : url:websocket server's url
** port:websocket server's port, if not given, default 80 for "ws", 443 for "wss"
** origin: the address or url of your self
** Return : Created: websocket client context structure
** Failed: NULL
**************************************************************************************************/
wsclient_context *create_wsclient(char *url, int port,char *path, char* origin);
/*************************************************************************************************
** Function Name : ws_connect_url
** Description : Connecting to the websocket server
** Input : wsclient: the websocket client context created by create_wsclientfunction
** Return : Connected: the socket value
** Failed: -1
**************************************************************************************************/
int ws_connect_url(wsclient_context *wsclient);
/*************************************************************************************************
** Function Name : ws_send
** Description : Create the sending string data and copy to tx_buf
** Input : message: the string that send to server(cannot exceeding the MAX_DATA_LEN
** message_len: the length of the string
** use_mask: 0/1; 1 means using mask for bynary
** wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void ws_send(char* message, int message_len, int use_mask, wsclient_context *wsclient);
/*************************************************************************************************
** Function Name : ws_sendBinary
** Description : Create the sending binary data and copy to tx_buf
** Input : message: the binary that send to server(cannot exceeding the MAX_DATA_LEN
** message_len: the length of the binary
** use_mask: 0/1; 1 means using mask for bynary
** wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void ws_sendBinary(uint8_t* message, int message_len, int use_mask, wsclient_context *wsclient);
/*************************************************************************************************
** Function Name : ws_sendPing
** Description : Sending Ping to websocket server
** Input : wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void ws_sendPing(wsclient_context *wsclient);
/*************************************************************************************************
** Function Name : ws_poll
** Description : Receicing data from server and send the data in tx_buf
** Input : timeout(in milliseconds)
wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void ws_poll(int timeout, wsclient_context *wsclient);
/*************************************************************************************************
** Function Name : ws_dispatch
** Description : callback function when getting message from server
** Input : function that resolve the message received and the message length
** Return : None
**************************************************************************************************/
void ws_dispatch(void (*callback)(wsclient_context *, int)) ;
/*************************************************************************************************
** Function Name : ws_getReadyState
** Description : Getting the connection status
** Input : wsclient: the websocket client context
** Return : readyStateValues(4 types:CLOSING, CLOSED, CONNECTING, OPEN)
**************************************************************************************************/
readyStateValues ws_getReadyState(wsclient_context *wsclient);
/*************************************************************************************************
** Function Name : ws_close
** Description : Closing the connection with websocket server
** Input : wsclient: the websocket client context
** Return : None
**************************************************************************************************/
void ws_close(wsclient_context *wsclient);
#endif

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
#include "Flash.h" #include "Flash.h"
#include <Arduino.h> #include <Arduino.h>

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
#pragma once #pragma once
#include "api/Flash.h" #include "api/Flash.h"

View File

@@ -1,16 +1,15 @@
#include "PowerManagement.h" #include "PowerManagement.h"
#include "Arduino.h"
#include <Arduino.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#include "freertos_pmu.h" #include <freertos_pmu.h>
#include "sys_api.h" #include <sleep_ex_api.h>
#include "sleep_ex_api.h" #include <sys_api.h>
} }
#endif #endif
#include "variant.h"
#if defined(BOARD_RTL8195A) #if defined(BOARD_RTL8195A)
#define SAVE_LOCK_PIN 18 #define SAVE_LOCK_PIN 18
#elif defined(BOARD_RTL8710) #elif defined(BOARD_RTL8710)
@@ -22,38 +21,38 @@ extern "C" {
bool PowerManagementClass::reservePLL = true; bool PowerManagementClass::reservePLL = true;
void PowerManagementClass::sleep(uint32_t bitflg) { void PowerManagementClass::sleep(uint32_t bitflg) {
if (!safeLock()) { if (!safeLock()) {
pmu_release_wakelock(bitflg); pmu_release_wakelock(bitflg);
} }
} }
void PowerManagementClass::sleep(void) { void PowerManagementClass::sleep(void) {
if (!safeLock()) { if (!safeLock()) {
pmu_release_wakelock(BIT(PMU_OS)); pmu_release_wakelock(BIT(PMU_OS));
} }
} }
void PowerManagementClass::active(uint32_t bitflg) { void PowerManagementClass::active(uint32_t bitflg) {
pmu_acquire_wakelock(bitflg); pmu_acquire_wakelock(bitflg);
} }
void PowerManagementClass::active(void) { void PowerManagementClass::active(void) {
pmu_acquire_wakelock(BIT(PMU_OS)); pmu_acquire_wakelock(BIT(PMU_OS));
} }
void PowerManagementClass::deepsleep(uint32_t duration_ms) { void PowerManagementClass::deepsleep(uint32_t duration_ms) {
if (!safeLock()) { if (!safeLock()) {
deepsleep_ex(DSLEEP_WAKEUP_BY_TIMER, duration_ms); deepsleep_ex(DSLEEP_WAKEUP_BY_TIMER, duration_ms);
} }
} }
bool PowerManagementClass::safeLock() { bool PowerManagementClass::safeLock() {
pinMode(SAVE_LOCK_PIN, INPUT_PULLUP); pinMode(SAVE_LOCK_PIN, INPUT_PULLUP);
return (digitalRead(SAVE_LOCK_PIN) == 1) ? false : true; return (digitalRead(SAVE_LOCK_PIN) == 1) ? false : true;
} }
void PowerManagementClass::softReset() { void PowerManagementClass::softReset() {
sys_reset(); sys_reset();
} }
PowerManagementClass PowerManagement; PowerManagementClass PowerManagement;

View File

@@ -0,0 +1,73 @@
#pragma once
#include <inttypes.h>
/**
* @class PowerManagementClass PowerManagement.h
* @brief Power management in Ameba
*/
class PowerManagementClass {
public:
/**
* @brief Allow OS automatically save power while idle
*
* As OS consider it would idle for more than 2s, it will invoke system suspend.
* If wlan is associated with AP, than it will under asslociated idle state.
*/
static void sleep(void);
static void sleep(uint32_t bitflg);
/**
* @brief Disallow OS automatically save power while idle
*/
static void active(void);
static void active(uint32_t bitflg);
/**
* @brief Reserved PLL while sleep
*
* Reserve PLL would keep FIFO of peripherals (Ex. UART) but cost more power (around 5mA).
* If we don't reserve PLL, it saves more power but we might missing data because FIFO is turned of this way.
*
* @param[in] reserve true for reserved, false for non-reserved
*/
static void setPllReserved(bool reserve);
/**
* @brief Enter deepsleep immediately
*
* Invoke deepsleep would make system enter deepsleep state immediately.
* It's the state that saves most power.
* As it wakeup from deepsleep, the system would behave just like reboot.
*
* @param[in] duration_ms wakeup after specific time in unit of millisecond
*/
static void deepsleep(uint32_t duration_ms);
/**
* @brief Check if system is allowed enter any power save state
*
* The pin 18 (GPIOE_5) is designed as safe lock.
* If pin 18 is HIGH, then we prevent Ameba enter any power save state.\n\n
* Under any power save state, we are not able to flash image to Ameba.
* Thus if user misuse deepsleep and make Ameba enter deepsleep immediately after boot up,
* then he would find it's hard to flash image.
* In this case, he can pull up pin 18.
*
* @return true if system not allowed enter any power save state, and false vise versa
*/
static bool safeLock();
/**
* @brief Reboot system
*
* Reboot system in soft way. Some registers is not powered off in this case, but mostly we could regard this as
* reboot.
*/
static void softReset();
private:
static bool reservePLL;
};
extern PowerManagementClass PowerManagement;

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h" #include "WiFi.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h" #include "WiFi.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#include "WiFiClient.h" #include "WiFiClient.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,7 +1,9 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#pragma once #pragma once
#include <api/WiFiClient.h>
#include <LwIPRxBuffer.h> #include <LwIPRxBuffer.h>
#include <api/WiFiClient.h>
#include <memory> #include <memory>
class SocketHandle; class SocketHandle;

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h" #include "WiFi.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#pragma once #pragma once
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h" #include "WiFi.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-25. */
#include "WiFi.h" #include "WiFi.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#include "WiFiServer.h" #include "WiFiServer.h"
#include "WiFiPriv.h" #include "WiFiPriv.h"

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-26. */
#pragma once #pragma once
#include <api/WiFiServer.h> #include <api/WiFiServer.h>

View File

@@ -1,20 +1 @@
/*
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// API compatibility
#include "variant.h" #include "variant.h"

View File

@@ -1,33 +1,13 @@
/* /* Copyright (c) Kuba Szczodrzyński 2022-04-22. */
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Arduino.h"
#include "variant.h" #include "variant.h"
#include "gpio_api.h"
#ifdef __cplusplus
extern "C" { extern "C" {
#endif
#include "PinNames.h"
/* /*
* Pins descriptions * Pins descriptions
*/ */
// clang-format off
PinDescription g_APinDescription[PINS_COUNT] = { PinDescription g_APinDescription[PINS_COUNT] = {
// D0: UART0_RTS, SPI1_MISO, SPI0_MISO, I2C0_SCL, SD_D0, PWM5, I2S_WS, WAKEUP_2 // D0: UART0_RTS, SPI1_MISO, SPI0_MISO, I2C0_SCL, SD_D0, PWM5, I2S_WS, WAKEUP_2
{PA_22, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL}, {PA_22, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL},
@@ -48,31 +28,15 @@ PinDescription g_APinDescription[PINS_COUNT] = {
// D8: SDIO_SIDEBAND_INT, PWM4, WAKEUP_1 // D8: SDIO_SIDEBAND_INT, PWM4, WAKEUP_1
{PA_5, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL}, {PA_5, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL},
// D9: PWM3 // D9: PWM3
{PA_12, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL}, {PA_12, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL},
// D10: UART0_RXD, SPI1_CLK, SPI0_SCK, I2C1_SCL, SD_D2, TIMER4_TRIG, I2S_MCK, WAKEUP_0 // D10: UART0_RXD, SPI1_CLK, SPI0_SCK, I2C1_SCL, SD_D2, TIMER4_TRIG, I2S_MCK, WAKEUP_0
{PA_18, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ, NOT_INITIAL}, {PA_18, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ, NOT_INITIAL},
// D11: UART0_TXD, SPI1_MOSI, SPI0_MOSI, I2C1_SDA, SD_D1, PWM0, WAKEUP_3 // D11: UART0_TXD, SPI1_MOSI, SPI0_MOSI, I2C1_SDA, SD_D1, PWM0, WAKEUP_3
{PA_23, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL}, {PA_23, NOT_INITIAL, PIO_GPIO | PIO_GPIO_IRQ | PIO_PWM, NOT_INITIAL},
}; };
// clang-format on
void *gpio_pin_struct[PINS_COUNT] = {NULL}; void *gpio_pin_struct[PINS_COUNT] = {NULL};
void *gpio_irq_handler_list[PINS_COUNT] = {NULL}; void *gpio_irq_handler_list[PINS_COUNT] = {NULL};
#ifdef __cplusplus
} // extern C } // extern C
#endif
void serialEvent() __attribute__((weak));
bool Serial_available() __attribute__((weak));
void serialEventRun(void) {
if (Serial_available && serialEvent && Serial_available())
serialEvent();
}
void wait_for_debug() {
while (((CoreDebug->DHCSR) & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
asm("nop");
}
delay(1000);
}

View File

@@ -1,20 +1,4 @@
/* /* Copyright (c) Kuba Szczodrzyński 2022-04-23. */
Copyright (c) 2011 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once #pragma once
@@ -24,10 +8,10 @@
// ---- // ----
// Number of pins defined in PinDescription array // Number of pins defined in PinDescription array
#define PINS_COUNT (12u) #define PINS_COUNT (12u)
#define NUM_DIGITAL_PINS (12u) #define NUM_DIGITAL_PINS (12u)
#define NUM_ANALOG_INPUTS (2u) #define NUM_ANALOG_INPUTS (2u)
#define NUM_ANALOG_OUTPUTS (0u) #define NUM_ANALOG_OUTPUTS (0u)
// Low-level pin register query macros // Low-level pin register query macros
// ----------------------------------- // -----------------------------------
@@ -37,18 +21,18 @@
// LEDs // LEDs
// ---- // ----
#define PIN_LED (4u) #define PIN_LED (4u)
#define LED_BUILTIN PIN_LED #define LED_BUILTIN PIN_LED
// Analog pins // Analog pins
// ----------- // -----------
#define PIN_A0 (1u) // PA_19 #define PIN_A0 (1u) // PA_19
#define PIN_A1 (0u) // dummy, this is NOT usable #define PIN_A1 (0u) // dummy, this is NOT usable
#define PIN_A2 (5u) // PA_20 #define PIN_A2 (5u) // PA_20
static const uint8_t A0 = PIN_A0; static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1; static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2; static const uint8_t A2 = PIN_A2;
// SPI Interfaces // SPI Interfaces
// -------------- // --------------
@@ -64,30 +48,14 @@ static const uint8_t A2 = PIN_A2;
#include "LOGUARTClass.h" #include "LOGUARTClass.h"
extern LOGUARTClass Serial; extern LOGUARTClass Serial;
// LOGUARTClass // LOGUARTClass
#define PIN_SERIAL2_RX PA_29 #define PIN_SERIAL2_RX PA_29
#define PIN_SERIAL2_TX PA_30 #define PIN_SERIAL2_TX PA_30
// UARTClassOne // UARTClassOne
#define PIN_SERIAL0_RX PA_18 #define PIN_SERIAL0_RX PA_18
#define PIN_SERIAL0_TX PA_23 #define PIN_SERIAL0_TX PA_23
#endif // __cplusplus #endif // __cplusplus
#define SERIAL_PORT_USBVIRTUAL Serial #define SERIAL_PORT_USBVIRTUAL Serial
#define SERIAL_PORT_MONITOR Serial #define SERIAL_PORT_MONITOR Serial
#define SERIAL_PORT_HARDWARE Serial #define SERIAL_PORT_HARDWARE Serial
#define SERIAL_PORT_HARDWARE_OPEN Serial #define SERIAL_PORT_HARDWARE_OPEN Serial
// Misc
// ----
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/*
* Wait until enter debug mode
*
* Check DHCSR(0xE000EDF0) register and hold until bit C_DEBUGEN is set.
* Use this function along with J-LINK or other debug tool
**/
extern void wait_for_debug();
#ifdef __cplusplus
}
#endif

View File

@@ -36,6 +36,7 @@ env.AddLibrary(
base_dir=LT_API_DIR, base_dir=LT_API_DIR,
srcs=[ srcs=[
"+<api/*.c*>", "+<api/*.c*>",
"+<common/*.c*>",
"+<libraries/**/*.c*>", "+<libraries/**/*.c*>",
], ],
includes=[ includes=[

View File

@@ -70,38 +70,12 @@ env.AddLibrary(
base_dir="$ARDUINO_DIR", base_dir="$ARDUINO_DIR",
srcs=[ srcs=[
# Wiring core # Wiring core
"+<cores/arduino/avr/dtostrf.c>", "+<cores/arduino/*.c>",
"+<cores/arduino/b64.cpp>", "+<cores/arduino/*.cpp>",
"+<cores/arduino/cxxabi-compat.cpp>",
"+<cores/arduino/hooks.c>",
"+<cores/arduino/itoa.c>",
"+<cores/arduino/LOGUARTClass.cpp>",
"+<cores/arduino/lwip_info.c>",
"+<cores/arduino/main.cpp>",
"+<cores/arduino/PowerManagement.cpp>",
"+<cores/arduino/rtl_sys.cpp>",
"+<cores/arduino/spiffs/spiffs_cache.c>",
"+<cores/arduino/spiffs/spiffs_check.c>",
"+<cores/arduino/spiffs/spiffs_gc.c>",
"+<cores/arduino/spiffs/spiffs_hydrogen.c>",
"+<cores/arduino/spiffs/spiffs_nucleus.c>",
"+<cores/arduino/Tone.cpp>",
"+<cores/arduino/WebSocketClient.cpp>",
"+<cores/arduino/WInterrupts.c>",
"+<cores/arduino/wiring.c>",
"+<cores/arduino/wiring_analog.c>",
"+<cores/arduino/wiring_digital.c>",
"+<cores/arduino/wiring_os.c>",
"+<cores/arduino/wiring_pulse.cpp>",
"+<cores/arduino/wiring_shift.c>",
"+<cores/arduino/wiring_watchdog.c>",
"+<cores/arduino/WMath.cpp>",
], ],
includes=[ includes=[
# prepend these as the Arduino core is incorrectly picking some includes from SDK # prepend these as the Arduino core is incorrectly picking some includes from SDK
"!<cores/arduino>", "!<cores/arduino>",
"!<cores/arduino/avr>",
"!<cores/arduino/spiffs>",
# includes that are missing in the vanilla SDK makefiles # includes that are missing in the vanilla SDK makefiles
"+<$SDK_DIR/component/common/drivers/sdio/realtek/sdio_host/inc>", "+<$SDK_DIR/component/common/drivers/sdio/realtek/sdio_host/inc>",
"+<$SDK_DIR/component/common/file_system/fatfs>", "+<$SDK_DIR/component/common/file_system/fatfs>",

View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

View File

@@ -1,3 +1,5 @@
/* Copyright (c) Kuba Szczodrzyński 2022-04-23. */
/* WCharacter.h uses deprecated isascii() and toascii(), /* WCharacter.h uses deprecated isascii() and toascii(),
which are available in gnu++11 but not c++11 */ which are available in gnu++11 but not c++11 */
#include <cstddef> #include <cstddef>

Some files were not shown because too many files have changed in this diff Show More