[FL-1061] iButton save and load from sd card (#394)

* SD App: fix queue adresses
* sd-filesystem: fix making path on file select event
* ibutton: add key reading from sd card
* ibutton: save ibutton key to sd card
* ibutton: add deleting keys from sd card
* ibutton: remove KeyStore from application
* ibutton: make directory if necessary on key save

Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
gornekich 2021-03-31 20:47:32 +03:00 committed by GitHub
parent 6375f21cf5
commit 5309bfae41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 132 additions and 256 deletions

View File

@ -177,7 +177,7 @@ const FlipperApplication FLIPPER_APPS[] = {
#endif
#ifdef BUILD_IBUTTON
{.app = app_ibutton, .name = "iButton", .stack_size = 1024, .icon = A_iButton_14},
{.app = app_ibutton, .name = "iButton", .stack_size = 4096, .icon = A_iButton_14},
#endif
#ifdef BUILD_GPIO_DEMO

View File

@ -96,6 +96,9 @@ static bool file_select_input_callback(InputEvent* event, void* context) {
return true;
});
consumed = true;
if(!file_select_fill_strings(file_select)) {
file_select->callback(false, file_select->context);
}
} else if(event->key == InputKeyDown) {
with_view_model(
file_select->view, (FileSelectModel * model) {
@ -121,6 +124,9 @@ static bool file_select_input_callback(InputEvent* event, void* context) {
return true;
});
consumed = true;
if(!file_select_fill_strings(file_select)) {
file_select->callback(false, file_select->context);
}
} else if(event->key == InputKeyOk) {
if(file_select->callback != NULL) {
const char* result;
@ -138,10 +144,6 @@ static bool file_select_input_callback(InputEvent* event, void* context) {
}
consumed = true;
}
if(!file_select_fill_strings(file_select)) {
file_select->callback(false, file_select->context);
}
}
return consumed;
@ -207,6 +209,8 @@ void file_select_set_api(FileSelect* file_select, FS_Api* fs_api) {
}
void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) {
file_select->context = context;
file_select->callback = callback;
}
void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension) {

View File

@ -1,69 +0,0 @@
#include "key-store.h"
#include <furi.h>
uint16_t KeyStore::get_key_count() {
return store.size();
}
uint8_t KeyStore::add_key() {
store.push_back(iButtonKey());
return get_key_count() - 1;
}
void KeyStore::set_key_type(uint8_t index, iButtonKeyType type) {
iButtonKey* key = get_key(index);
key->set_type(type);
}
void KeyStore::set_key_name(uint8_t index, char* name) {
iButtonKey* key = get_key(index);
char* orphan = strdup(name);
key->set_name(orphan);
}
void KeyStore::set_key_data(uint8_t index, uint8_t* data, uint8_t data_size) {
iButtonKey* key = get_key(index);
key->set_data(data, data_size);
}
iButtonKeyType KeyStore::get_key_type(uint8_t index) {
iButtonKey* key = get_key(index);
return key->get_key_type();
}
const char* KeyStore::get_key_name(uint8_t index) {
iButtonKey* key = get_key(index);
return key->get_name();
}
uint8_t* KeyStore::get_key_data(uint8_t index) {
iButtonKey* key = get_key(index);
return key->get_data();
}
void KeyStore::remove_key(uint8_t index) {
furi_check(index >= 0);
furi_check(index < get_key_count());
auto item = std::next(store.begin(), index);
store.erase(item);
}
KeyStore::KeyStore() {
store.push_back(iButtonKey(
iButtonKeyType::KeyDallas, "Dallas_1", 0x01, 0x41, 0xCE, 0x67, 0x0F, 0x00, 0x00, 0xB6));
store.push_back(iButtonKey(
iButtonKeyType::KeyDallas, "Dallas_2", 0x01, 0xFD, 0x0E, 0x84, 0x01, 0x00, 0x00, 0xDB));
store.push_back(iButtonKey(
iButtonKeyType::KeyCyfral, "Cyfral_1", 0xA6, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
store.push_back(iButtonKey(
iButtonKeyType::KeyMetakom, "Metakom_1", 0xB1, 0x2E, 0x47, 0xB2, 0x00, 0x00, 0x00, 0x00));
}
KeyStore::~KeyStore() {
}
iButtonKey* KeyStore::get_key(uint8_t index) {
furi_check(index >= 0);
furi_check(index < get_key_count());
return &(*std::next(store.begin(), index));
}

View File

@ -1,29 +0,0 @@
#pragma once
#include <stdint.h>
#include <list>
#include "key-info.h"
#include "../ibutton-key.h"
class KeyStore {
public:
uint16_t get_key_count();
uint8_t add_key();
void set_key_type(uint8_t index, iButtonKeyType type);
void set_key_name(uint8_t index, char* name);
void set_key_data(uint8_t index, uint8_t* data, uint8_t data_size);
iButtonKeyType get_key_type(uint8_t index);
const char* get_key_name(uint8_t index);
uint8_t* get_key_data(uint8_t index);
void remove_key(uint8_t index);
KeyStore();
~KeyStore();
private:
std::list<iButtonKey> store;
iButtonKey* get_key(uint8_t index);
};

View File

@ -28,12 +28,16 @@ iButtonApp::iButtonApp() {
api_hal_power_insomnia_enter();
key_worker = new KeyWorker(&ibutton_gpio);
sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex"));
fs_api = static_cast<FS_Api*>(furi_record_open("sdcard"));
// we need random
srand(DWT->CYCCNT);
}
iButtonApp::~iButtonApp() {
furi_record_close("sdcard-ex");
furi_record_close("sdcard");
api_hal_power_insomnia_exit();
}
@ -107,6 +111,22 @@ iButtonKey* iButtonApp::get_key() {
return &key;
}
SdCard_Api* iButtonApp::get_sd_ex_api() {
return sd_ex_api;
}
FS_Api* iButtonApp::get_fs_api() {
return fs_api;
}
char* iButtonApp::get_file_name() {
return file_name;
}
uint8_t iButtonApp::get_file_name_size() {
return file_name_size;
}
void iButtonApp::notify_init() {
// TODO open record
const GpioPin* vibro_record = &vibro_gpio;
@ -193,18 +213,6 @@ uint8_t iButtonApp::get_text_store_size() {
return text_store_size;
}
KeyStore* iButtonApp::get_key_store() {
return &store;
}
uint8_t iButtonApp::get_stored_key_index() {
return key_index;
}
void iButtonApp::set_stored_key_index(uint8_t _index) {
key_index = _index;
}
void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) {
const uint8_t prefix_size = 9;
const char* prefix[prefix_size] = {

View File

@ -12,7 +12,6 @@
#include "scene/ibutton-scene-readed-key-menu.h"
#include "scene/ibutton-scene-write.h"
#include "scene/ibutton-scene-write-success.h"
#include "scene/ibutton-scene-saved.h"
#include "scene/ibutton-scene-saved-key-menu.h"
#include "scene/ibutton-scene-delete-confirm.h"
#include "scene/ibutton-scene-delete-success.h"
@ -23,9 +22,11 @@
#include "scene/ibutton-scene-add-type.h"
#include "scene/ibutton-scene-add-value.h"
#include "helpers/key-store.h"
#include "helpers/key-worker.h"
#include <sd-card-api.h>
#include <filesystem-api.h>
#include "one_wire_master.h"
#include "maxim_crc.h"
#include "ibutton-key.h"
@ -48,7 +49,6 @@ public:
SceneWrite,
SceneWriteSuccess,
SceneEmulate,
SceneSavedList,
SceneSavedKeyMenu,
SceneDeleteConfirm,
SceneDeleteSuccess,
@ -88,9 +88,10 @@ public:
char* get_text_store();
uint8_t get_text_store_size();
KeyStore* get_key_store();
uint8_t get_stored_key_index();
void set_stored_key_index(uint8_t index);
SdCard_Api* get_sd_ex_api();
FS_Api* get_fs_api();
char* get_file_name();
uint8_t get_file_name_size();
void generate_random_name(char* name, uint8_t max_name_size);
@ -109,7 +110,6 @@ private:
{Scene::SceneWrite, new iButtonSceneWrite()},
{Scene::SceneWriteSuccess, new iButtonSceneWriteSuccess()},
{Scene::SceneEmulate, new iButtonSceneEmulate()},
{Scene::SceneSavedList, new iButtonSceneSavedList()},
{Scene::SceneSavedKeyMenu, new iButtonSceneSavedKeyMenu()},
{Scene::SceneDeleteConfirm, new iButtonSceneDeleteConfirm()},
{Scene::SceneDeleteSuccess, new iButtonSceneDeleteSuccess()},
@ -123,12 +123,14 @@ private:
KeyWorker* key_worker;
iButtonKey key;
uint8_t key_index = 0;
SdCard_Api* sd_ex_api;
FS_Api* fs_api;
static const uint8_t file_name_size = 100;
char file_name[file_name_size];
static const uint8_t text_store_size = 128;
char text_store[text_store_size + 1];
KeyStore store;
void notify_init();
};

View File

@ -51,28 +51,5 @@ iButtonKeyType iButtonKey::get_key_type() {
return type;
}
iButtonKey::iButtonKey(
iButtonKeyType _type,
const char* _name,
uint8_t d0,
uint8_t d1,
uint8_t d2,
uint8_t d3,
uint8_t d4,
uint8_t d5,
uint8_t d6,
uint8_t d7) {
type = _type;
name = _name;
data[0] = d0;
data[1] = d1;
data[2] = d2;
data[3] = d3;
data[4] = d4;
data[5] = d5;
data[6] = d6;
data[7] = d7;
}
iButtonKey::iButtonKey() {
}

View File

@ -16,19 +16,6 @@ public:
void set_type(iButtonKeyType key_type);
iButtonKeyType get_key_type();
// temporary constructor for KeyStore mockup
iButtonKey(
iButtonKeyType type,
const char* name,
uint8_t d0,
uint8_t d1,
uint8_t d2,
uint8_t d3,
uint8_t d4,
uint8_t d5,
uint8_t d6,
uint8_t d7);
iButtonKey();
private:

View File

@ -55,10 +55,19 @@ bool iButtonSceneDeleteConfirm::on_event(iButtonApp* app, iButtonEvent* event) {
if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
if(event->payload.dialog_result == DialogExResultRight) {
KeyStore* store = app->get_key_store();
store->remove_key(app->get_stored_key_index());
app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteSuccess);
iButtonKey* key = app->get_key();
string_t key_file_name;
string_init_set_str(key_file_name, "ibutton/");
string_cat_str(key_file_name, key->get_name());
bool res =
(app->get_fs_api()->common.remove(string_get_cstr(key_file_name)) == FSE_OK);
string_clear(key_file_name);
if(res) {
app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteSuccess);
} else {
// TODO error file path
// app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteFail);
}
} else {
app->switch_to_previous_scene();
}

View File

@ -25,7 +25,7 @@ bool iButtonSceneDeleteSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeBack) {
app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneSavedList});
app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneStart});
consumed = true;
}

View File

@ -4,6 +4,7 @@
#include "../ibutton-event.h"
#include "../ibutton-key.h"
#include <callback-connector.h>
#include <filesystem-api.h>
void iButtonSceneSaveName::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
@ -28,14 +29,23 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeTextEditResult) {
KeyStore* store = app->get_key_store();
uint8_t key_index = store->add_key();
iButtonKey* key = app->get_key();
store->set_key_type(key_index, key->get_key_type());
store->set_key_name(key_index, app->get_text_store());
store->set_key_data(key_index, key->get_data(), key->get_size());
File key_file;
string_t key_file_name;
string_init_set_str(key_file_name, "ibutton/");
string_cat_str(key_file_name, app->get_text_store());
uint8_t key_data[IBUTTON_KEY_SIZE + 1];
key_data[0] = static_cast<uint8_t>(key->get_key_type());
memcpy(key_data + 1, key->get_data(), IBUTTON_KEY_SIZE);
// Create ibutton directory if necessary
app->get_fs_api()->common.mkdir("ibutton");
bool res = app->get_fs_api()->file.open(
&key_file, string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS);
if(res) {
res = app->get_fs_api()->file.write(&key_file, key_data, IBUTTON_KEY_SIZE + 1);
res = app->get_fs_api()->file.close(&key_file);
}
string_clear(key_file_name);
app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess);
consumed = true;
}

View File

@ -1,62 +0,0 @@
#include "ibutton-scene-saved.h"
#include "../ibutton-app.h"
#include "../ibutton-view-manager.h"
#include "../ibutton-event.h"
#include <callback-connector.h>
void iButtonSceneSavedList::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager();
Submenu* submenu = view_manager->get_submenu();
auto callback = cbc::obtain_connector(this, &iButtonSceneSavedList::submenu_callback);
KeyStore* store = app->get_key_store();
if(store->get_key_count() > 0) {
for(uint8_t i = 0; i < store->get_key_count(); i++) {
submenu_add_item(submenu, store->get_key_name(i), i, callback, app);
}
} else {
submenu_add_item(submenu, "Empty", 0, NULL, NULL);
}
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
}
bool iButtonSceneSavedList::on_event(iButtonApp* app, iButtonEvent* event) {
bool consumed = false;
if(event->type == iButtonEvent::Type::EventTypeMenuSelected) {
uint8_t key_index = event->payload.menu_index;
// store data
iButtonKey* stored_key_data = app->get_key();
KeyStore* store = app->get_key_store();
app->set_stored_key_index(key_index);
stored_key_data->set_name(store->get_key_name(key_index));
stored_key_data->set_type(store->get_key_type(key_index));
stored_key_data->set_data(store->get_key_data(key_index), stored_key_data->get_size());
app->switch_to_next_scene(iButtonApp::Scene::SceneSavedKeyMenu);
consumed = true;
}
return consumed;
}
void iButtonSceneSavedList::on_exit(iButtonApp* app) {
iButtonAppViewManager* view = app->get_view_manager();
Submenu* submenu = view->get_submenu();
submenu_clean(submenu);
}
void iButtonSceneSavedList::submenu_callback(void* context, uint32_t index) {
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
event.type = iButtonEvent::Type::EventTypeMenuSelected;
event.payload.menu_index = index;
app->get_view_manager()->send_event(&event);
}

View File

@ -1,12 +0,0 @@
#pragma once
#include "ibutton-scene-generic.h"
class iButtonSceneSavedList : public iButtonScene {
public:
void on_enter(iButtonApp* app) final;
bool on_event(iButtonApp* app, iButtonEvent* event) final;
void on_exit(iButtonApp* app) final;
private:
void submenu_callback(void* context, uint32_t index);
};

View File

@ -3,6 +3,7 @@
#include "../ibutton-view-manager.h"
#include "../ibutton-event.h"
#include <callback-connector.h>
#include <filesystem-api.h>
typedef enum {
SubmenuIndexRead,
@ -30,9 +31,40 @@ bool iButtonSceneStart::on_event(iButtonApp* app, iButtonEvent* event) {
case SubmenuIndexRead:
app->switch_to_next_scene(iButtonApp::Scene::SceneRead);
break;
case SubmenuIndexSaved:
app->switch_to_next_scene(iButtonApp::Scene::SceneSavedList);
break;
case SubmenuIndexSaved: {
bool res = app->get_sd_ex_api()->file_select(
app->get_sd_ex_api()->context,
"ibutton",
"*",
app->get_file_name(),
app->get_file_name_size());
if(res) {
string_t key_str;
string_init_set_str(key_str, "ibutton/");
string_cat_str(key_str, app->get_file_name());
File key_file;
uint8_t key_data[IBUTTON_KEY_SIZE + 1] = {};
// Read data from file
// TODO handle false return
res = app->get_fs_api()->file.open(
&key_file, string_get_cstr(key_str), FSAM_READ, FSOM_OPEN_EXISTING);
res = app->get_fs_api()->file.read(&key_file, key_data, IBUTTON_KEY_SIZE + 1);
res = app->get_fs_api()->file.close(&key_file);
string_clear(key_str);
// Set key
iButtonKeyType key_type = static_cast<iButtonKeyType>(key_data[0]);
if(key_type > iButtonKeyType::KeyMetakom) {
app->switch_to_next_scene(iButtonApp::Scene::SceneStart);
}
app->get_key()->set_name(app->get_file_name());
app->get_key()->set_type(key_type);
app->get_key()->set_data(key_data + 1, IBUTTON_KEY_SIZE);
app->switch_to_next_scene(iButtonApp::Scene::SceneSavedKeyMenu);
} else {
// TODO add error scene
app->switch_to_next_scene(iButtonApp::Scene::SceneStart);
}
}; break;
case SubmenuIndexAdd:
app->switch_to_next_scene(iButtonApp::Scene::SceneAddType);
break;

View File

@ -325,7 +325,7 @@ bool app_sd_make_path(const char* path) {
if(*path) {
char* file_path = strdup(path);
// Make parent directories
for(char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
*p = '\0';
SDError result = f_mkdir(file_path);
@ -339,6 +339,14 @@ bool app_sd_make_path(const char* path) {
}
*p = '/';
}
// Make origin directory
SDError result = f_mkdir(file_path);
if(result != SD_OK) {
if(result != SD_EXIST) {
free(file_path);
return false;
}
}
free(file_path);
}
@ -393,7 +401,7 @@ bool sd_api_file_select(
SdAppFileSelectResultEvent event;
while(1) {
osStatus_t event_status =
osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever);
osMessageQueueGet(return_event_queue, &event, NULL, osWaitForever);
if(event_status == osOK) {
retval = event.result;
break;
@ -697,7 +705,7 @@ int32_t sd_filesystem(void* p) {
case SdAppStateFileSelect: {
SdAppFileSelectResultEvent retval = {.result = true};
furi_check(
osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) ==
osMessageQueuePut(sd_app->result_receiver, &retval, 0, osWaitForever) ==
osOK);
app_reset_state(sd_app);
}; break;
@ -712,7 +720,7 @@ int32_t sd_filesystem(void* p) {
case SdAppStateFileSelect: {
SdAppFileSelectResultEvent retval = {.result = false};
furi_check(
osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) ==
osMessageQueuePut(sd_app->result_receiver, &retval, 0, osWaitForever) ==
osOK);
app_reset_state(sd_app);
}; break;
@ -799,8 +807,12 @@ int32_t sd_filesystem(void* p) {
break;
case SdAppEventTypeFileSelect:
if(!app_sd_make_path(event.payload.file_select_data.path)) {
SdAppFileSelectResultEvent retval = {.result = false};
furi_check(
osMessageQueuePut(sd_app->result_receiver, &retval, 0, osWaitForever) ==
osOK);
app_reset_state(sd_app);
}
if(try_to_alloc_view_holder(sd_app, gui)) {
sd_app->result_receiver = event.result_receiver;
FileSelect* file_select = alloc_and_attach_file_select(sd_app);
@ -814,8 +826,15 @@ int32_t sd_filesystem(void* p) {
event.payload.file_select_data.result,
event.payload.file_select_data.result_size);
if(!file_select_init(file_select)) {
SdAppFileSelectResultEvent retval = {.result = false};
furi_check(
osMessageQueuePut(
sd_app->result_receiver, &retval, 0, osWaitForever) == osOK);
app_reset_state(sd_app);
} else {
sd_app->sd_app_state = SdAppStateFileSelect;
view_holder_start(sd_app->view_holder);
}
sd_app->sd_app_state = SdAppStateFileSelect;
} else {
SdAppFileSelectResultEvent retval = {.result = false};
furi_check(