diff --git a/firmware/bootloader/openblt_chibios/openblt_net.cpp b/firmware/bootloader/openblt_chibios/openblt_net.cpp index e7ecd51a89..7ade70f8e0 100644 --- a/firmware/bootloader/openblt_chibios/openblt_net.cpp +++ b/firmware/bootloader/openblt_chibios/openblt_net.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "wifi_socket.h" +#include "wifi_sd_firmware_updater.h" #include "thread_controller.h" #include "socket/include/socket.h" @@ -40,6 +41,10 @@ void NetDeferredInit() { didInit = true; + // In the bootloader the SD-card WiFi firmware update never runs, so unblock + // WifiHelperThread (which waits on this semaphore before it calls initWifi()). + signalWifiSdUpdateComplete(); + initWifi(); wifiInitFinisher.startThread(); diff --git a/firmware/console/binary_log/sd_file_log.cpp b/firmware/console/binary_log/sd_file_log.cpp index 7308197d92..3ecaf5e226 100644 --- a/firmware/console/binary_log/sd_file_log.cpp +++ b/firmware/console/binary_log/sd_file_log.cpp @@ -243,7 +243,13 @@ static void sdTriggerLogger() { #endif /* EFI_TOOTH_LOGGER */ } -static THD_WORKING_AREA(sdCardLoggerStack, 3 * UTILITY_THREAD_STACK_SIZE); // MMC monitor thread +#if EFI_WIFI +// WiFi needs extra stack, as WiFi firmware update happens from here +static THD_WORKING_AREA(sdCardLoggerStack, 6 * UTILITY_THREAD_STACK_SIZE); +#else // not EFI_WIFI +static THD_WORKING_AREA(sdCardLoggerStack, 4 * UTILITY_THREAD_STACK_SIZE); +#endif + static THD_FUNCTION(sdCardLoggerThread, arg) { (void)arg; chRegSetThreadName("MMC Card Logger"); diff --git a/firmware/ext/atwinc1500/spi_flash/source/spi_flash.cpp b/firmware/ext/atwinc1500/spi_flash/source/spi_flash.cpp index 4b41087444..7f159a26bd 100755 --- a/firmware/ext/atwinc1500/spi_flash/source/spi_flash.cpp +++ b/firmware/ext/atwinc1500/spi_flash/source/spi_flash.cpp @@ -687,7 +687,7 @@ sint8 spi_flash_erase(uint32 u32Offset, uint32 u32Sz) uint32 t; t = GetTickCount(); #endif - M2M_PRINT("\r\n>Start erasing...\r\n"); + M2M_INFO(">Start erasing..."); for(i = u32Offset; i < (u32Sz +u32Offset); i += (16*FLASH_PAGE_SZ)) { ret += spi_flash_write_enable(); @@ -696,14 +696,15 @@ sint8 spi_flash_erase(uint32 u32Offset, uint32 u32Sz) ret += spi_flash_read_status_reg(&tmp); do { + nm_bsp_sleep(10); if(ret != M2M_SUCCESS) goto ERR; ret += spi_flash_read_status_reg(&tmp); }while(tmp & 0x01); } - M2M_PRINT("Done\r\n"); + M2M_INFO("Done!"); #ifdef PROFILING - M2M_PRINT("#Erase time = %f sec\n", (GetTickCount()-t)/1000.0); + M2M_INFO("#Erase time = %f sec\n", (GetTickCount()-t)/1000.0); #endif ERR: return ret; diff --git a/firmware/hw_layer/mmc_card.h b/firmware/hw_layer/mmc_card.h index cfdff1d125..d9004ab7ef 100644 --- a/firmware/hw_layer/mmc_card.h +++ b/firmware/hw_layer/mmc_card.h @@ -11,6 +11,8 @@ #include "buffered_writer.h" +#include + BaseBlockDevice* initializeMmcBlockDevice(); void stopMmcBlockDevice(); @@ -45,5 +47,6 @@ namespace sd_mem { FATFS* getFs(); FIL* getLogFileFd(); SdLogBufferWriter& getLogBuffer(); +std::array& getWifiUpdateBuffer(); } // namespace sd_mem #endif // EFI_PROD_CODE diff --git a/firmware/hw_layer/mmc_card_attach.cpp b/firmware/hw_layer/mmc_card_attach.cpp index f9e7142f69..daab26c2da 100644 --- a/firmware/hw_layer/mmc_card_attach.cpp +++ b/firmware/hw_layer/mmc_card_attach.cpp @@ -36,13 +36,14 @@ struct { FATFS fs; FIL file; SdLogBufferWriter logBuffer; + std::array wifiUpdateBuffer; } usedPart; - static_assert(sizeof(usedPart) <= 2048); + static_assert(sizeof(usedPart) <= 4096); // Fill the struct out to a full MPU region - uint8_t padding[2048 - sizeof(usedPart)]; -} mmcCardCacheControlledStorage SDMMC_MEMORY(2048); + uint8_t padding[4096 - sizeof(usedPart)]; +} mmcCardCacheControlledStorage SDMMC_MEMORY(4096); namespace sd_mem { FATFS* getFs() { @@ -56,6 +57,10 @@ FIL* getLogFileFd() { SdLogBufferWriter& getLogBuffer() { return mmcCardCacheControlledStorage.usedPart.logBuffer; } + +std::array& getWifiUpdateBuffer() { + return mmcCardCacheControlledStorage.usedPart.wifiUpdateBuffer; +} } // namespace sd_mem #endif // !EFI_BOOTLOADER @@ -132,8 +137,8 @@ BaseBlockDevice* initializeMmcBlockDevice() { #if defined(STM32H7XX) && !EFI_BOOTLOADER { void* base = &mmcCardCacheControlledStorage; - static_assert(sizeof(mmcCardCacheControlledStorage) == 2048); - uint32_t size = MPU_RASR_SIZE_2K; + static_assert(sizeof(mmcCardCacheControlledStorage) == 4096); + uint32_t size = MPU_RASR_SIZE_4K; mpuConfigureRegion( MPU_REGION_5, diff --git a/firmware/hw_layer/mmc_card_mount.cpp b/firmware/hw_layer/mmc_card_mount.cpp index 2f6f17d096..b368487986 100644 --- a/firmware/hw_layer/mmc_card_mount.cpp +++ b/firmware/hw_layer/mmc_card_mount.cpp @@ -6,6 +6,11 @@ #include "ff.h" #include "mass_storage_init.h" +#include "usbconsole.h" + +#if EFI_WIFI +#include "wifi_sd_firmware_updater.h" +#endif static bool fs_ready = false; @@ -18,6 +23,9 @@ void onUsbConnectedNotifyMmcI() { #endif /* HAL_USE_USB_MSD */ bool mountSdFilesystem() { + Timer t; + t.reset(); + auto cardBlockDevice = initializeMmcBlockDevice(); #if EFI_TUNER_STUDIO @@ -27,18 +35,45 @@ bool mountSdFilesystem() { // if no card, don't try to mount FS if (!cardBlockDevice) { +#if EFI_WIFI + signalWifiSdUpdateComplete(); +#endif +#if HAL_USE_USB_MSD + // No SD card - allow USB to enumerate so serial and INI drive still work + allowUsbEnumeration(); +#endif return false; } + // Mount filesystem temporarily for WiFi update check + bool mounted = f_mount(sd_mem::getFs(), "/", 1) == FR_OK; + + if (mounted) { +#if EFI_WIFI + // Check for WiFi firmware update/dump trigger files on SD card + tryUpdateWifiFirmwareFromSd(); + tryDumpWifiFirmwareToSd(); +#endif + // Unmount — we'll either hand the card to USB or re-mount for logging + f_mount(nullptr, "/", 0); + } else { + efiPrintf("SD card failed to mount filesystem"); + } + +#if EFI_WIFI + signalWifiSdUpdateComplete(); +#endif + #if HAL_USE_USB_MSD - // Wait for the USB stack to wake up, or a 5 second timeout, whichever occurs first + // Allow USB to enumerate - the host will see the real SD card + allowUsbEnumeration(); + + // Wait for the USB enumeration, or a 5 second timeout, whichever occurs first msg_t usbResult = usbConnectedSemaphore.wait(TIME_MS2I(5000)); bool hasUsb = usbResult == MSG_OK; - // If we have a device AND USB is connected, mount the card to USB, otherwise - // mount the null device and try to mount the filesystem ourselves - if (cardBlockDevice && hasUsb) { + if (hasUsb) { // Mount the real card to USB attachMsdSdCard(cardBlockDevice); diff --git a/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.cpp b/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.cpp index ffe2be9f1d..25d76c1466 100644 --- a/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.cpp +++ b/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.cpp @@ -16,6 +16,12 @@ static bool isUsbSerialInitialized = false; +#if HAL_USE_USB_MSD +// When MSD is enabled, defer USB bus connection until the SD card thread +// has finished any WiFi firmware updates and is ready to present media. +static chibios_rt::BinarySemaphore usbEnumerationAllowed(/* taken =*/true); +#endif + /** * start USB serial using hard-coded communications pins (see comments inside the code) */ @@ -42,11 +48,23 @@ void usb_serial_start() { chThdSleepMilliseconds(250); #endif /* EFI_SKIP_USB_DISCONNECT */ usbStart(serusbcfg.usbp, &usbcfg); + +#if HAL_USE_USB_MSD + // Wait for the SD card thread to signal that it's ready + usbEnumerationAllowed.wait(); +#endif + usbConnectBus(serusbcfg.usbp); isUsbSerialInitialized = true; } +void allowUsbEnumeration() { +#if HAL_USE_USB_MSD + usbEnumerationAllowed.signal(); +#endif +} + bool is_usb_serial_ready() { return isUsbSerialInitialized && SDU1.config->usbp->state == USB_ACTIVE; } diff --git a/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.h b/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.h index 171b1d9af8..651fe70c24 100644 --- a/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.h +++ b/firmware/hw_layer/ports/stm32/serial_over_usb/usbconsole.h @@ -7,13 +7,9 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ +void usb_serial_start(); +bool is_usb_serial_ready(); -void usb_serial_start(void); -bool is_usb_serial_ready(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ +#if HAL_USE_USB_MSD +void allowUsbEnumeration(); +#endif diff --git a/firmware/net/net.mk b/firmware/net/net.mk index ffb32e552b..0311a8dd8d 100644 --- a/firmware/net/net.mk +++ b/firmware/net/net.mk @@ -1,6 +1,6 @@ ALLCPPSRC += \ $(PROJECT_DIR)/net/wifi_socket.cpp \ - # $(PROJECT_DIR)/net/wifi_firmware_updater.cpp \ + $(PROJECT_DIR)/net/wifi_sd_firmware_updater.cpp ALLINC += \ $(PROJECT_DIR)/net \ diff --git a/firmware/net/wifi_firmware_updater.cpp b/firmware/net/wifi_firmware_updater.cpp deleted file mode 100644 index 6eb990988e..0000000000 --- a/firmware/net/wifi_firmware_updater.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "spi_flash/include/spi_flash.h" - -#include "usbcfg.h" - -static bool isBigEndian() { - uint32_t test = 0x11223344; - uint8_t* pTest = reinterpret_cast(&test); - return pTest[0] == 0x11; -} - -static uint32_t fromNetwork32(uint32_t from) { - static const bool be = isBigEndian(); - if (be) { - return from; - } else { - uint8_t* pFrom = reinterpret_cast(&from); - uint32_t to; - to = pFrom[0]; - to <<= 8; - to |= pFrom[1]; - to <<= 8; - to |= pFrom[2]; - to <<= 8; - to |= pFrom[3]; - return to; - } -} - -static uint16_t fromNetwork16(uint16_t from) { - static bool be = isBigEndian(); - if (be) { - return from; - } else { - uint8_t* pFrom = reinterpret_cast(&from); - uint16_t to; - to = pFrom[0]; - to <<= 8; - to |= pFrom[1]; - return to; - } -} - -static uint32_t toNetwork32(uint32_t to) { - return fromNetwork32(to); -} - -static uint16_t toNetwork16(uint16_t to) { - return fromNetwork16(to); -} - -typedef struct __attribute__((__packed__)) { - uint8_t command; - uint32_t address; - uint32_t arg1; - uint16_t payloadLength; - - // payloadLenght bytes of data follows... -} UartPacket; - -static const int MAX_PAYLOAD_SIZE = 256; - -#define CMD_READ_FLASH 0x01 -#define CMD_WRITE_FLASH 0x02 -#define CMD_ERASE_FLASH 0x03 -#define CMD_MAX_PAYLOAD_SIZE 0x50 -#define CMD_HELLO 0x99 - -static int readch() { - uint8_t buf; - int ret = chnReadTimeout(&SDU1, &buf, 1, TIME_MS2I(1)); - - if (ret == 0) { - return -1; - } else { - return (int)buf; - } -} - -static void receivePacket(UartPacket* pkt, uint8_t* payload) { - // Read command - uint8_t* p = reinterpret_cast(pkt); - uint16_t l = sizeof(UartPacket); - while (l > 0) { - int c = readch(); - if (c == -1) - continue; - *p++ = c; - l--; - } - - // Convert parameters from network byte order to cpu byte order - pkt->address = fromNetwork32(pkt->address); - pkt->arg1 = fromNetwork32(pkt->arg1); - pkt->payloadLength = fromNetwork16(pkt->payloadLength); - - // Read payload - l = pkt->payloadLength; - while (l > 0) { - int c = readch(); - if (c == -1) - continue; - *payload++ = c; - l--; - } -} - -// Allocated statically so the compiler can tell us -// about the amount of used RAM -static UartPacket pkt; -static uint8_t payload[MAX_PAYLOAD_SIZE]; - -static void serialPrint(const uint8_t* data, size_t length) { - chnWriteTimeout(&SDU1, data, length, TIME_MS2I(100)); -} - -static void serialPrintStr(const char* str) { - size_t len = strlen(str); - serialPrint(reinterpret_cast(str), len); -} - -struct WifiUpdaterThread : public ThreadController<4096> { - WifiUpdaterThread() - : ThreadController("WifiPump", NORMALPRIO - 10) {} - void ThreadTask() override { - if (M2M_SUCCESS != m2m_wifi_download_mode()) { - return; - } - - auto flashSize = spi_flash_get_size(); - (void)flashSize; - - // int ret = spi_flash_erase(0, FLASH_SECTOR_SZ); - //(void)ret; - - usb_serial_start(); - - while (true) { - while (!is_usb_serial_ready()) { - chThdSleepMilliseconds(1); - } - - receivePacket(&pkt, payload); - - if (pkt.command == CMD_HELLO) { - if (pkt.address == 0x11223344 && pkt.arg1 == 0x55667788) { - serialPrintStr("v10000"); - } - } - - if (pkt.command == CMD_MAX_PAYLOAD_SIZE) { - uint16_t res = toNetwork16(MAX_PAYLOAD_SIZE); - serialPrint(reinterpret_cast(&res), sizeof(res)); - } - - if (pkt.command == CMD_READ_FLASH) { - uint32_t address = pkt.address; - uint32_t len = pkt.arg1; - if (spi_flash_read(payload, address, len) != M2M_SUCCESS) { - serialPrintStr("ER"); - } else { - serialPrint(payload, len); - serialPrintStr("OK"); - } - } - - if (pkt.command == CMD_WRITE_FLASH) { - uint32_t address = pkt.address; - uint32_t len = pkt.payloadLength; - if (spi_flash_write(payload, address, len) != M2M_SUCCESS) { - serialPrintStr("ER"); - } else { - serialPrintStr("OK"); - } - } - - if (pkt.command == CMD_ERASE_FLASH) { - uint32_t address = pkt.address; - uint32_t len = pkt.arg1; - if (spi_flash_erase(address, len) != M2M_SUCCESS) { - serialPrintStr("ER"); - // serialPrintStr("OK"); - } else { - serialPrintStr("OK"); - } - } - } - } -}; - -static WifiUpdaterThread wifiUpdater; - -void startWifiUpdater() { - wifiUpdater.start(); -} diff --git a/firmware/net/wifi_sd_firmware_updater.cpp b/firmware/net/wifi_sd_firmware_updater.cpp new file mode 100644 index 0000000000..56291a7417 --- /dev/null +++ b/firmware/net/wifi_sd_firmware_updater.cpp @@ -0,0 +1,162 @@ +#include "pch.h" + +#if EFI_WIFI + +#include "ff.h" +#include "mmc_card.h" +#include "spi_flash/include/spi_flash.h" +#include "driver/include/m2m_wifi.h" +#include "programmer/programmer.h" + +static chibios_rt::BinarySemaphore sdUpdateDoneSemaphore(/* taken =*/true); + +void signalWifiSdUpdateComplete() { + sdUpdateDoneSemaphore.signal(); +} + +void waitForWifiSdUpdateComplete() { + sdUpdateDoneSemaphore.wait(); +} + +void tryUpdateWifiFirmwareFromSd() { + FILINFO finfo; + if (f_stat("atwinc1500.bin", &finfo) != FR_OK) { + efiPrintf("WiFi: atwinc1500.bin not found, skipping update."); + return; // no file, nothing to do + } + + Timer t; + t.reset(); + + efiPrintf("WiFi: found atwinc1500.bin (%lu bytes), updating...", (uint32_t)finfo.fsize); + + if (m2m_wifi_download_mode() != M2M_SUCCESS) { + efiPrintf("WiFi: failed to enter download mode"); + return; + } + + FIL* fd = sd_mem::getLogFileFd(); // reuse DMA-safe FIL + if (f_open(fd, "atwinc1500.bin", FA_READ) != FR_OK) { + efiPrintf("WiFi: failed to open file"); + m2m_wifi_deinit(nullptr); + return; + } + + uint32_t fileSize = finfo.fsize; + uint32_t eraseSize = ((fileSize + FLASH_SECTOR_SZ - 1) / FLASH_SECTOR_SZ) * FLASH_SECTOR_SZ; + + efiPrintf("WiFi: erasing %lu bytes", eraseSize); + if (spi_flash_erase(0, eraseSize) != M2M_SUCCESS) { + efiPrintf("WiFi: erase failed"); + f_close(fd); + m2m_wifi_deinit(nullptr); + return; + } + + auto& wifiUpdateBuffer = sd_mem::getWifiUpdateBuffer(); + + uint32_t offset = 0; + int lastPct = -1; + while (offset < fileSize) { + UINT bytesRead; + if (f_read(fd, wifiUpdateBuffer.data(), wifiUpdateBuffer.size(), &bytesRead) != FR_OK || bytesRead == 0) { + efiPrintf("WiFi: read error at offset %lu", offset); + f_close(fd); + m2m_wifi_deinit(nullptr); + return; + } + + if (spi_flash_write(wifiUpdateBuffer.data(), offset, bytesRead) != M2M_SUCCESS) { + efiPrintf("WiFi: write failed at offset %lu", offset); + f_close(fd); + m2m_wifi_deinit(nullptr); + return; + } + + offset += bytesRead; + int pct = offset * 100 / fileSize; + if (pct / 5 != lastPct / 5) { + efiPrintf("WiFi: writing %d%% (%lu / %lu)", pct, offset, fileSize); + lastPct = pct; + } + } + + f_close(fd); + + m2m_wifi_deinit(nullptr); + + // Delete .done if it exists from a previous update, then rename + f_unlink("atwinc1500.done"); + f_rename("atwinc1500.bin", "atwinc1500.done"); + efiPrintf("WiFi: firmware update complete in %.1f sec!", t.getElapsedSeconds()); +} + +void tryDumpWifiFirmwareToSd() { + if (f_stat("atwinc1500.read", nullptr) != FR_OK) { + return; // no trigger file, nothing to do + } + + Timer t; + t.reset(); + + efiPrintf("WiFi: found atwinc1500.read, dumping firmware..."); + + if (m2m_wifi_download_mode() != M2M_SUCCESS) { + efiPrintf("WiFi: failed to enter download mode"); + m2m_wifi_deinit(nullptr); + return; + } + + uint32_t flashSize = programmer_get_flash_size(); + efiPrintf("WiFi: flash size %lu bytes", flashSize); + + FIL* fd = sd_mem::getLogFileFd(); + f_unlink("atwinc1500_dump.bin"); + if (f_open(fd, "atwinc1500_dump.bin", FA_CREATE_NEW | FA_WRITE) != FR_OK) { + efiPrintf("WiFi: failed to create dump file"); + m2m_wifi_deinit(nullptr); + return; + } + + auto& wifiUpdateBuffer = sd_mem::getWifiUpdateBuffer(); + + uint32_t offset = 0; + int lastPct = -1; + while (offset < flashSize) { + uint32_t chunkSize = flashSize - offset; + if (chunkSize > wifiUpdateBuffer.size()) { + chunkSize = wifiUpdateBuffer.size(); + } + + if (spi_flash_read(wifiUpdateBuffer.data(), offset, chunkSize) != M2M_SUCCESS) { + efiPrintf("WiFi: read failed at offset %lu", offset); + f_close(fd); + m2m_wifi_deinit(nullptr); + return; + } + + UINT bytesWritten; + if (f_write(fd, wifiUpdateBuffer.data(), chunkSize, &bytesWritten) != FR_OK || bytesWritten != chunkSize) { + efiPrintf("WiFi: file write error at offset %lu", offset); + f_close(fd); + m2m_wifi_deinit(nullptr); + return; + } + + offset += chunkSize; + int pct = offset * 100 / flashSize; + if (pct / 5 != lastPct / 5) { + efiPrintf("WiFi: dumping %d%% (%lu / %lu)", pct, offset, flashSize); + lastPct = pct; + } + } + + f_close(fd); + m2m_wifi_deinit(nullptr); + + f_unlink("atwinc1500.read"); + + efiPrintf("WiFi: firmware dump complete in %.1f sec!", t.getElapsedSeconds()); +} + +#endif // EFI_WIFI diff --git a/firmware/net/wifi_sd_firmware_updater.h b/firmware/net/wifi_sd_firmware_updater.h new file mode 100644 index 0000000000..a2f1c7f031 --- /dev/null +++ b/firmware/net/wifi_sd_firmware_updater.h @@ -0,0 +1,11 @@ +#pragma once + +void tryUpdateWifiFirmwareFromSd(); +void tryDumpWifiFirmwareToSd(); + +// Signal that SD-based WiFi firmware operations are complete. +// Must be called even if no update was performed, to unblock WiFi init. +void signalWifiSdUpdateComplete(); + +// Block until SD-based WiFi firmware operations are complete (or skipped). +void waitForWifiSdUpdateComplete(); diff --git a/firmware/net/wifi_socket.cpp b/firmware/net/wifi_socket.cpp index a45d17a603..a4b90d6941 100644 --- a/firmware/net/wifi_socket.cpp +++ b/firmware/net/wifi_socket.cpp @@ -3,6 +3,7 @@ #if EFI_WIFI #include "wifi_socket.h" +#include "wifi_sd_firmware_updater.h" #include "thread_controller.h" #include "driver/include/m2m_wifi.h" #include "socket/include/socket.h" @@ -230,6 +231,10 @@ class WifiHelperThread : public ThreadController<4096> { WifiHelperThread() : ThreadController("WiFi", WIFI_THREAD_PRIORITY) {} void ThreadTask() override { + // Wait for any SD-based WiFi firmware update to finish + // before we try to initialize the WiFi module + waitForWifiSdUpdateComplete(); + if (!initWifi()) { return; }