Files
edgetx-gx12/radio/src/storage/sdcard_raw.cpp
T

471 lines
11 KiB
C++

/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*/
#include "../opentx.h"
void getModelPath(char * path, const char * filename)
{
strcpy(path, STR_MODELS_PATH);
path[sizeof(MODELS_PATH)-1] = '/';
strcpy(&path[sizeof(MODELS_PATH)], filename);
}
const char * writeFile(const char * filename, const uint8_t * data, uint16_t size)
{
FIL file;
char buf[8];
UINT written;
FRESULT result = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
*(uint32_t*)&buf[0] = O9X_FOURCC;
buf[4] = EEPROM_VER;
buf[5] = 'M';
*(uint16_t*)&buf[6] = size;
result = f_write(&file, buf, 8, &written);
if (result != FR_OK || written != 8) {
f_close(&file);
return SDCARD_ERROR(result);
}
result = f_write(&file, data, size, &written);
if (result != FR_OK || written != size) {
f_close(&file);
return SDCARD_ERROR(result);
}
f_close(&file);
return NULL;
}
const char * writeModel()
{
char path[256];
getModelPath(path, g_eeGeneral.currModelFilename);
return writeFile(path, (uint8_t *)&g_model, sizeof(g_model));
}
const char * loadFile(const char * filename, uint8_t * data, uint16_t maxsize)
{
FIL file;
char buf[8];
UINT read;
FRESULT result = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
if (f_size(&file) < 8) {
f_close(&file);
return STR_INCOMPATIBLE;
}
result = f_read(&file, (uint8_t *)buf, 8, &read);
if (result != FR_OK || read != 8) {
f_close(&file);
return SDCARD_ERROR(result);
}
uint8_t version = (uint8_t)buf[4];
if (*(uint32_t*)&buf[0] != O9X_FOURCC || version < FIRST_CONV_EEPROM_VER || version > EEPROM_VER || buf[5] != 'M') {
f_close(&file);
return STR_INCOMPATIBLE;
}
uint16_t size = min<uint16_t>(maxsize, *(uint16_t*)&buf[6]);
result = f_read(&file, data, size, &read);
if (result != FR_OK || read != size) {
f_close(&file);
return SDCARD_ERROR(result);
}
f_close(&file);
return NULL;
}
const char * readModel(const char * filename, uint8_t * buffer, uint32_t size)
{
char path[256];
getModelPath(path, filename);
return loadFile(path, buffer, size);
}
const char * loadModel(const char * filename)
{
preModelLoad();
const char * error = readModel(filename, (uint8_t *)&g_model, sizeof(g_model));
if (error) {
TRACE("loadModel error=%s", error);
}
bool newModel = false;
if (error) {
modelDefault(0) ;
storageCheck(true);
newModel = true;
}
postModelLoad(newModel);
return error;
}
const char RADIO_SETTINGS_PATH[] = RADIO_PATH "/radio.bin";
const char RADIO_MODELSLIST_PATH[] = RADIO_PATH "/models.txt";
const char * loadGeneralSettings()
{
const char * error = loadFile(RADIO_SETTINGS_PATH, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral));
if (error) {
TRACE("loadGeneralSettings error=%s", error);
}
// TODO this is temporary, we only have one model for now
return error;
}
const char * writeGeneralSettings()
{
return writeFile(RADIO_SETTINGS_PATH, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral));
}
void storageCheck(bool immediately)
{
if (storageDirtyMsk & EE_GENERAL) {
TRACE("eeprom write general");
storageDirtyMsk -= EE_GENERAL;
const char * error = writeGeneralSettings();
if (error) {
TRACE("writeGeneralSettings error=%s", error);
}
}
if (storageDirtyMsk & EE_MODEL) {
TRACE("eeprom write model");
storageDirtyMsk -= EE_MODEL;
const char * error = writeModel();
if (error) {
TRACE("writeModel error=%s", error);
}
}
}
void storageReadAll()
{
sdInit();
if (loadGeneralSettings() != NULL) {
storageEraseAll(true);
}
stickMode = g_eeGeneral.stickMode;
#if defined(CPUARM)
for (uint8_t i=0; languagePacks[i]!=NULL; i++) {
if (!strncmp(g_eeGeneral.ttsLanguage, languagePacks[i]->id, 2)) {
currentLanguagePackIdx = i;
currentLanguagePack = languagePacks[i];
}
}
#endif
loadModel(g_eeGeneral.currModelFilename);
}
void storageCreateModelsList()
{
FIL file;
FRESULT result = f_open(&file, RADIO_MODELSLIST_PATH, FA_CREATE_ALWAYS | FA_WRITE);
if (result == FR_OK) {
f_puts("[" DEFAULT_CATEGORY "]\n" DEFAULT_MODEL_FILENAME "\n", &file);
f_close(&file);
}
}
void storageFormat()
{
sdCheckAndCreateDirectory(RADIO_PATH);
sdCheckAndCreateDirectory(MODELS_PATH);
storageCreateModelsList();
}
const char * storageOpenModelsList(StorageModelsList * storage)
{
FRESULT result = f_open(&storage->file, RADIO_MODELSLIST_PATH, FA_OPEN_EXISTING | FA_READ);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
return NULL;
}
bool storageReadNextLine(StorageModelsList * storage, char * line, int maxlen)
{
char c;
unsigned int read;
int len=0;
while (1) {
FRESULT result = f_read(&storage->file, (uint8_t *)&c, 1, &read);
if (result != FR_OK || read != 1) {
line[len] = '\0';
return false;
}
if (c == '\n') {
if (len > 0) {
// we skip empty lines
line[len] = '\0';
return true;
}
}
else if (c != '\r' && len < maxlen) {
line[len++] = c;
}
}
}
int storageGetCategoryLength(const char * line)
{
int len = strlen(line);
if (len > 2 && line[0] == '[' && line[len-1] == ']') {
return len-2;
}
else {
return 0;
}
}
bool storageReadNextCategory(StorageModelsList * storage, char * line, int maxlen)
{
bool result = true;
while (result) {
result = storageReadNextLine(storage, line, maxlen);
int len = storageGetCategoryLength(line);
if (len > 0) {
memmove(line, &line[1], len);
line[len] = '\0';
return result;
}
}
line[0] = '\0';
return false;
}
bool storageSeekCategory(StorageModelsList * storage, int category)
{
f_lseek(&storage->file, 0);
char line[256] = "";
int result = true;
for (int i=0; result && i<=category; i++) {
result = storageReadNextCategory(storage, line, sizeof(line)-1);
}
return line[0] != '\0';
}
bool storageReadNextModel(StorageModelsList * storage, char * line, int maxlen)
{
bool result = true;
while (result) {
result = storageReadNextLine(storage, line, maxlen);
if (line[0] == '[')
return false;
else if (line[0] != '\0')
return result;
}
line[0] = '\0';
return false;
}
enum StorageOperation {
STORAGE_INSERT_MODEL,
STORAGE_REMOVE_MODEL,
STORAGE_RENAME_MODEL,
STORAGE_INSERT_CATEGORY,
STORAGE_RENAME_CATEGORY,
STORAGE_REMOVE_CATEGORY
};
const char * storageModifyLine(unsigned int operation, int category, int position, const char * name="")
{
StorageModelsList storage;
FIL file;
const char * error = storageOpenModelsList(&storage);
if (error) {
return error;
}
{
FRESULT result = f_open(&file, RADIO_PATH "/models.tmp", FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
}
bool operationdone = false;
bool result = true;
int categoryindex = -1;
int modelindex = 0;
while (result) {
char line[256];
result = storageReadNextLine(&storage, line, sizeof(line)-1);
if (!operationdone) {
int len = storageGetCategoryLength(line);
if (len > 0) {
if (category >= 0) {
if (categoryindex == category && operation == STORAGE_INSERT_MODEL) {
f_puts(name, &file);
f_putc('\n', &file);
operationdone = true;
}
categoryindex++;
if (categoryindex == category) {
if (operation == STORAGE_RENAME_CATEGORY) {
f_puts("[", &file);
f_puts(name, &file);
f_puts("]", &file);
f_putc('\n', &file);
operationdone = true;
continue;
}
else if (operation == STORAGE_REMOVE_CATEGORY) {
operationdone = true;
continue;
}
}
}
}
else if (categoryindex == category) {
if (modelindex++ == position) {
operationdone = true;
if (operation == STORAGE_INSERT_MODEL) {
f_puts(name, &file);
f_putc('\n', &file);
}
else if (operation == STORAGE_RENAME_MODEL) {
f_puts(name, &file);
f_putc('\n', &file);
continue;
}
else if (operation == STORAGE_REMOVE_MODEL) {
TRACE("skip %s", line);
continue;
}
}
}
}
f_puts(line, &file);
f_putc('\n', &file);
}
if (!operationdone) {
if (categoryindex>=0 && operation==STORAGE_INSERT_MODEL) {
f_puts(name, &file);
f_putc('\n', &file);
}
else if (operation==STORAGE_INSERT_CATEGORY) {
f_puts("[", &file);
f_puts(name, &file);
f_puts("]", &file);
f_putc('\n', &file);
}
}
f_close(&file);
f_unlink(RADIO_PATH "/models.old");
f_rename(RADIO_MODELSLIST_PATH, RADIO_PATH "/models.old");
f_rename(RADIO_PATH "/models.tmp", RADIO_MODELSLIST_PATH);
return NULL;
}
const char * storageInsertCategory(const char * name, int position)
{
return storageModifyLine(STORAGE_INSERT_CATEGORY, position, -1, name);
}
const char * storageRenameCategory(int category, const char * name)
{
return storageModifyLine(STORAGE_RENAME_CATEGORY, category, -1, name);
}
const char * storageRemoveCategory(int category)
{
return storageModifyLine(STORAGE_REMOVE_CATEGORY, category, -1);
}
const char * storageInsertModel(const char * name, int category, int position)
{
return storageModifyLine(STORAGE_INSERT_MODEL, category, position, name);
}
const char * storageRemoveModel(int category, int position)
{
return storageModifyLine(STORAGE_REMOVE_MODEL, category, position);
}
const char * storageRenameModel(const char * name, int category, int position)
{
return storageModifyLine(STORAGE_RENAME_MODEL, category, position, name);
}
const char * createModel(int category)
{
preModelLoad();
char filename[LEN_MODEL_FILENAME+1];
memset(filename, 0, sizeof(filename));
strcpy(filename, "model.bin");
int index = findNextFileIndex(filename, MODELS_PATH);
if (index > 0) {
modelDefault(index);
memcpy(g_eeGeneral.currModelFilename, filename, sizeof(g_eeGeneral.currModelFilename));
storageDirty(EE_GENERAL);
storageDirty(EE_MODEL);
storageCheck(true);
storageInsertModel(filename, category, -1);
}
postModelLoad(true);
return NULL;
}
void storageEraseAll(bool warn)
{
TRACE("storageEraseAll()");
generalDefault();
modelDefault(1);
if (warn) {
ALERT(STR_STORAGE_WARNING, STR_BAD_RADIO_DATA, AU_BAD_RADIODATA);
}
MESSAGE(STR_STORAGE_WARNING, STR_STORAGE_FORMAT, NULL, AU_STORAGE_FORMAT);
storageFormat();
storageDirty(EE_GENERAL|EE_MODEL);
storageCheck(true);
}