#pragma once #include #include #include namespace Pinetime { namespace System { class SystemTask; } namespace Drivers { class SpiNorFlash; } namespace Controllers { class Ble; class DfuService { public: DfuService(Pinetime::System::SystemTask &systemTask, Pinetime::Controllers::Ble &bleController, Pinetime::Drivers::SpiNorFlash &spiNorFlash); void Init(); int OnServiceData(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context); void OnTimeout(); void Reset(); class NotificationManager { public: NotificationManager(); bool AsyncSend(uint16_t connection, uint16_t charactHandle, uint8_t *data, size_t size); void Send(uint16_t connection, uint16_t characteristicHandle, const uint8_t *data, const size_t s); private: TimerHandle_t timer; uint16_t connectionHandle = 0; uint16_t characteristicHandle = 0; size_t size = 0; uint8_t buffer[10]; public: void OnNotificationTimer(); void Reset(); }; class DfuImage { public: DfuImage(Pinetime::Drivers::SpiNorFlash& spiNorFlash) : spiNorFlash{spiNorFlash} {} void Init(size_t chunkSize, size_t totalSize, uint16_t expectedCrc); void Erase(); void Append(uint8_t* data, size_t size); bool Validate(); bool IsComplete(); private: Pinetime::Drivers::SpiNorFlash& spiNorFlash; static constexpr size_t bufferSize = 200; bool ready = false; size_t chunkSize = 0; size_t totalSize = 0; size_t maxSize = 475136; size_t bufferWriteIndex = 0; size_t totalWriteIndex = 0; static constexpr size_t writeOffset = 0x40000; uint8_t tempBuffer[bufferSize]; uint16_t expectedCrc = 0; void WriteMagicNumber(); uint16_t ComputeCrc(uint8_t const *p_data, uint32_t size, uint16_t const *p_crc); }; private: Pinetime::System::SystemTask &systemTask; Pinetime::Controllers::Ble &bleController; DfuImage dfuImage; NotificationManager notificationManager; static constexpr uint16_t dfuServiceId{0x1530}; static constexpr uint16_t packetCharacteristicId{0x1532}; static constexpr uint16_t controlPointCharacteristicId{0x1531}; static constexpr uint16_t revisionCharacteristicId{0x1534}; uint16_t revision{0x0008}; static constexpr ble_uuid128_t serviceUuid{ .u {.type = BLE_UUID_TYPE_128}, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00} }; static constexpr ble_uuid128_t packetCharacteristicUuid{ .u {.type = BLE_UUID_TYPE_128}, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00} }; static constexpr ble_uuid128_t controlPointCharacteristicUuid{ .u {.type = BLE_UUID_TYPE_128}, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00} }; static constexpr ble_uuid128_t revisionCharacteristicUuid{ .u {.type = BLE_UUID_TYPE_128}, .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00} }; struct ble_gatt_chr_def characteristicDefinition[4]; struct ble_gatt_svc_def serviceDefinition[2]; uint16_t packetCharacteristicHandle; uint16_t controlPointCharacteristicHandle; uint16_t revisionCharacteristicHandle; enum class States : uint8_t { Idle, Init, Start, Data, Validate, Validated }; States state = States::Idle; enum class ImageTypes : uint8_t { NoImage = 0x00, SoftDevice = 0x01, Bootloader = 0x02, SoftDeviceAndBootloader = 0x03, Application = 0x04 }; enum class Opcodes : uint8_t { StartDFU = 0x01, InitDFUParameters = 0x02, ReceiveFirmwareImage = 0x03, ValidateFirmware = 0x04, ActivateImageAndReset = 0x05, PacketReceiptNotificationRequest = 0x08, Response = 0x10, PacketReceiptNotification = 0x11 }; enum class ErrorCodes { NoError = 0x01, InvalidState = 0x02, NotSupported = 0x03, DataSizeExceedsLimits = 0x04, CrcError = 0x05, OperationFailed = 0x06 }; uint8_t nbPacketsToNotify = 0; uint32_t nbPacketReceived = 0; uint32_t bytesReceived = 0; uint32_t softdeviceSize = 0; uint32_t bootloaderSize = 0; uint32_t applicationSize = 0; uint16_t expectedCrc = 0; int SendDfuRevision(os_mbuf *om) const; int WritePacketHandler(uint16_t connectionHandle, os_mbuf *om); int ControlPointHandler(uint16_t connectionHandle, os_mbuf *om); TimerHandle_t timeoutTimer; }; } }