InfiniTime.git

ref: e5b73212f6addcfdb5e306df63d7135e543c4f8d

src/components/ble/DfuService.h


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#pragma once

#include <cstdint>
#include <array>

#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
#undef max
#undef min

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;
    };
  }
}