diff --git a/firmware/application/ui_external_items_menu_loader.cpp b/firmware/application/ui_external_items_menu_loader.cpp index f1f5390a1..c0532579b 100644 --- a/firmware/application/ui_external_items_menu_loader.cpp +++ b/firmware/application/ui_external_items_menu_loader.cpp @@ -132,20 +132,36 @@ namespace ui { gridItem.on_select = [&nav, appInfo, i]() { auto dev2 = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD); + dev2->lockDevice(); if (dev2) { - auto app_image = reinterpret_cast(portapack::memory::map::m4_code.end() - appInfo->binary_size); + // auto app_image = reinterpret_cast(portapack::memory::map::m4_code.end() - appInfo->binary_size); + auto app_image = reinterpret_cast(portapack::memory::map::local_sram_0.base()); + uint8_t errorcnt = 0; for (size_t j = 0; j < appInfo->binary_size; j += 128) { - auto segment = dev2->downloadStandaloneApp(i, j); - if (segment.size() != 128) { - continue; + errorcnt = 0; + do { + auto segment = dev2->downloadStandaloneApp(i, j); + if (segment.size() != 128) { + errorcnt++; + if (errorcnt > 5) { + break; + } + } else { + std::copy(segment.begin(), segment.end(), app_image + j); + break; + } + } while (1); // when ok, break. when errorcnt > 5, break + if (errorcnt > 5) { + nav.display_modal("Error", "Unable to download app."); + dev2->unlockDevice(); + return; } - - std::copy(segment.begin(), segment.end(), app_image + j); } if (!run_module_app(nav, app_image, appInfo->binary_size)) { nav.display_modal("Error", "Unable to run downloaded app."); } + dev2->unlockDevice(); } else nav.display_modal("Error", "Unable to download app."); }; @@ -346,7 +362,6 @@ namespace ui { if (openError) return false; - // TODO: move this to m4 memory space auto app_image = reinterpret_cast(portapack::memory::map::local_sram_0.base()); // read file in 512 byte chunks diff --git a/firmware/common/i2cdev_ppmod.cpp b/firmware/common/i2cdev_ppmod.cpp index 9699b5fa1..2e4b8ce3b 100644 --- a/firmware/common/i2cdev_ppmod.cpp +++ b/firmware/common/i2cdev_ppmod.cpp @@ -46,6 +46,7 @@ bool I2cDev_PPmod::init(uint8_t addr_) { } void I2cDev_PPmod::update() { + if (isLocked()) return; // device is busy // mask = get_features_mask(); //saved on init. replug device if something changed. //needs to revise when modules come out. if (mask & (uint64_t)SupportedFeatures::FEAT_GPS && self_timer % SENSORUPDATETIME == 0) { auto data = get_gps_data(); @@ -101,11 +102,13 @@ void I2cDev_PPmod::update() { } bool I2cDev_PPmod::get_shell_get_buffer_data(uint8_t* buff, size_t len) { + if (isLocked()) return false; // device is busy Command cmd = Command::COMMAND_SHELL_MODTOPP_DATA; return i2c_read((uint8_t*)&cmd, 2, buff, len); } std::optional I2cDev_PPmod::get_orientation_data() { + if (isLocked()) return std::nullopt; // device is busy Command cmd = Command::COMMAND_GETFEAT_DATA_ORIENTATION; orientation_t data; bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&data, sizeof(orientation_t)); @@ -116,6 +119,7 @@ std::optional I2cDev_PPmod::get_orientation_data() { } std::optional I2cDev_PPmod::get_gps_data() { + if (isLocked()) return std::nullopt; // device is busy Command cmd = Command::COMMAND_GETFEAT_DATA_GPS; gpssmall_t data; bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&data, sizeof(gpssmall_t)); @@ -126,6 +130,7 @@ std::optional I2cDev_PPmod::get_gps_data() { } std::optional I2cDev_PPmod::get_environment_data() { + if (isLocked()) return std::nullopt; // device is busy Command cmd = Command::COMMAND_GETFEAT_DATA_ENVIRONMENT; environment_t data; bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&data, sizeof(environment_t)); @@ -136,6 +141,7 @@ std::optional I2cDev_PPmod::get_environment_data() { } std::optional I2cDev_PPmod::get_light_data() { + if (isLocked()) return std::nullopt; // device is busy Command cmd = Command::COMMAND_GETFEAT_DATA_LIGHT; uint16_t data; bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&data, sizeof(uint16_t)); @@ -146,6 +152,7 @@ std::optional I2cDev_PPmod::get_light_data() { } uint16_t I2cDev_PPmod::get_shell_buffer_bytes() { + if (isLocked()) return 0; // device is busy Command cmd = Command::COMMAND_SHELL_MODTOPP_DATA_SIZE; uint16_t data; bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&data, sizeof(uint16_t)); @@ -156,6 +163,7 @@ uint16_t I2cDev_PPmod::get_shell_buffer_bytes() { } uint64_t I2cDev_PPmod::get_features_mask() { + if (isLocked()) return 0; // device is busy uint64_t mask_ = 0; Command cmd = Command::COMMAND_GETFEATURE_MASK; bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&mask_, sizeof(mask_)); @@ -170,6 +178,7 @@ uint64_t I2cDev_PPmod::get_features_mask() { } std::optional I2cDev_PPmod::readDeviceInfo() { + if (isLocked()) return std::nullopt; // device is busy Command cmd = Command::COMMAND_INFO; I2cDev_PPmod::device_info info; diff --git a/firmware/common/i2cdevmanager.cpp b/firmware/common/i2cdevmanager.cpp index a28ced398..67a2ea57c 100644 --- a/firmware/common/i2cdevmanager.cpp +++ b/firmware/common/i2cdevmanager.cpp @@ -108,6 +108,19 @@ FROM HERE YOU SHOULDN'T WRITE ANYTHING IF YOU JUST IMPLEMENT A NEW DRIVER */ +void I2cDev::unlockDevice() { + mtx = 0; +} + +// when device is locked, all not important queries will return 0 or nullopt, so no communication broken. +bool I2cDev::lockDevice() { + if (mtx == 0) { + mtx = 1; + return true; + } + return false; +} + void I2cDev::set_update_interval(uint8_t interval) { query_interval = interval; } diff --git a/firmware/common/i2cdevmanager.hpp b/firmware/common/i2cdevmanager.hpp index 4c87bcc3d..203598dbc 100644 --- a/firmware/common/i2cdevmanager.hpp +++ b/firmware/common/i2cdevmanager.hpp @@ -41,8 +41,8 @@ namespace i2cdev { class I2cDev { public: virtual ~I2cDev(){}; - virtual bool init(uint8_t addr); // returns true if it is that that device we are looking for. - virtual void update() = 0; // override this, and you'll be able to query your device and broadcast the result to the system + virtual bool init(uint8_t addr) = 0; // returns true if it is that that device we are looking for. + virtual void update() = 0; // override this, and you'll be able to query your device and broadcast the result to the system void set_update_interval(uint8_t interval); // sets the device's update interval in sec. if you change it, don't forget to change back to it's original value after you finished! uint8_t get_update_interval(); // gets the device's update interval in sec @@ -59,6 +59,10 @@ class I2cDev { int16_t readS16_LE_1(uint8_t reg); uint32_t read24_1(uint8_t reg); + bool lockDevice(); // locks the device's "mutex". returns true on success. guard important communication with this + void unlockDevice(); // unlocks the device's "mutex" + inline bool isLocked() { return mtx != 0; } + bool need_del = false; // device can self destruct, and re-init when new scan discovers it I2C_DEVMDL model = I2CDEVMDL_NOTSET; // overwrite it in the init()!!! uint8_t query_interval = 5; // in seconds. can be overriden in init() if necessary @@ -68,6 +72,7 @@ class I2cDev { uint8_t addr = 0; // some devices can have different addresses, so we store what was it wound with uint8_t errcnt = 0; // error count during communication. if it reaches a threshold set need_del to remove itself from the device list + uint8_t mtx = 0; // internal lock, so important client code can be skipped when lock is on. check PPMod for an example }; // store for the devices. may not have a driver if not supported diff --git a/hackrf b/hackrf index 651d0a7f2..03e4732a5 160000 --- a/hackrf +++ b/hackrf @@ -1 +1 @@ -Subproject commit 651d0a7f232e12016b3b12fb6a9b3fea08eb52fd +Subproject commit 03e4732a5308a3d0de5b0341004d7a2fcc825806