30
30
#include " platform/CircularBuffer.h"
31
31
#include " platform/PlatformMutex.h"
32
32
33
+ #include " events/EventQueue.h"
34
+
33
35
#include " BlockDevice.h"
34
36
37
+ #include " descriptors/CharacteristicUserDescriptionDescriptor.h"
38
+
35
39
/* *
36
40
* Maximum length of data (in bytes) that the DFU service
37
41
* can receive at one time.
45
49
#endif
46
50
47
51
/* *
48
- * RX Buffer Sizes
52
+ * RX Buffer Size
53
+ * This should be at least 1.5X the maximum MTU you expect to accept
54
+ * Defaults to 2.5X maximum MTU
49
55
*/
50
56
#ifndef MBED_CONF_BLE_DFU_SERVICE_RX_BUFFER_SIZE
51
- #define MBED_CONF_BLE_DFU_SERVICE_RX_BUFFER_SIZE 256
57
+ #define MBED_CONF_BLE_DFU_SERVICE_RX_BUFFER_SIZE ((BLE_DFU_SERVICE_MAX_DATA_LEN << 1 ) +\
58
+ (BLE_DFU_SERVICE_MAX_DATA_LEN >> 1 ))
59
+ #endif
60
+
61
+ /* *
62
+ * RX Buffer Flow Control Pause Threshold
63
+ * This is the maximum number of bytes that can be placed into the RX buffer
64
+ * before the server automatically sets the flow control pause bit.
65
+ *
66
+ * By default this is 2X the maximum MTU
67
+ */
68
+ #ifndef MBED_CONF_BLE_DFU_SERVICE_RX_FC_PAUSE_THRESHOLD
69
+ #define MBED_CONF_BLE_DFU_SERVICE_RX_FC_PAUSE_THRESHOLD (BLE_DFU_SERVICE_MAX_DATA_LEN << 1 )
70
+ #endif
71
+
72
+ /* *
73
+ * RX Buffer Flow Control Unpause Threshold
74
+ * This is the maximum number of bytes that can be in the RX buffer
75
+ * before the server automatically clears the flow control pause bit.
76
+ *
77
+ * By default this is 1X the maximum MTU
78
+ */
79
+ #ifndef MBED_CONF_BLE_DFU_SERVICE_RX_FC_UNPAUSE_THRESHOLD
80
+ #define MBED_CONF_BLE_DFU_SERVICE_RX_FC_UNPAUSE_THRESHOLD BLE_DFU_SERVICE_MAX_DATA_LEN
52
81
#endif
53
82
54
83
/* *
58
87
#define MBED_CONF_BLE_DFU_SERVICE_MAX_SLOTS 3
59
88
#endif
60
89
90
+ /* *
91
+ * Bit flags
92
+ */
93
+ #define DFU_CTRL_ENABLE_BIT (1 << 0 )
94
+ #define DFU_CTRL_COMMIT_BIT (1 << 1 )
95
+ #define DFU_CTRL_DELTA_MODE_EN_BIT (1 << 2 )
96
+ #define DFU_CTRL_FC_PAUSE_BIT (1 << 7 )
97
+
98
+ /* Bitmask of read-only bits in the DFU Ctrl bit set */
99
+ #define DFU_CTRL_READONLY_BITS (DFU_CTRL_FC_PAUSE_BIT)
100
+
61
101
/* *
62
102
* UUIDs
63
103
*/
64
104
namespace uuids {
65
105
namespace DFUService {
66
106
67
- const UUID BaseUUID ( " 53880000-65fd-4651-ba8e-91527f06c887 " ) ;
68
- const UUID SlotUUID ( " 53880001-65fd-4651-ba8e-91527f06c887 " ) ;
69
- const UUID OffsetUUID ( " 53880002-65fd-4651-ba8e-91527f06c887 " ) ;
70
- const UUID BinaryStreamUUID ( " 53880003-65fd-4651-ba8e-91527f06c887 " ) ;
71
- const UUID ControlUUID ( " 53880004-65fd-4651-ba8e-91527f06c887 " ) ;
72
- const UUID StatusUUID ( " 53880005-65fd-4651-ba8e-91527f06c887 " ) ;
107
+ extern const char BaseUUID[] ;
108
+ extern const char SlotUUID[] ;
109
+ extern const char OffsetUUID[] ;
110
+ extern const char BinaryStreamUUID[] ;
111
+ extern const char ControlUUID[] ;
112
+ extern const char StatusUUID[] ;
73
113
74
114
}}
75
115
@@ -99,6 +139,8 @@ namespace DFUService {
99
139
* - Status characteristic
100
140
* - Notify/Indicate/Read
101
141
* - Error code (eg: update too large, invalid update (after reboot), etc)
142
+ * - If highest bit is set it indicates a sync lost notification
143
+ * ---> The 7LSB will then indicate the expected sequence ID that did not match. The client should restart transmission from this sequence ID.
102
144
* - Selected Slot
103
145
* - Write (w/ response)
104
146
* - Write is only allowed if slot has valid block device
@@ -133,9 +175,26 @@ class DFUService : public ble::GattServer::EventHandler,
133
175
* defined in the GattAuthCallbackReply_t enum.
134
176
*/
135
177
enum ApplicationError_t {
136
-
178
+ AUTH_CALLBACK_REPLY_ATTERR_APP_NOT_ALLOWED = 0x019C , /* * Response when client attempts to enable DFU when disallowed */
179
+ AUTH_CALLBACK_REPLY_ATTERR_APP_READONLY = 0x019D , /* * A write request was made that modifies data that is read-only */
137
180
AUTH_CALLBACK_REPLY_ATTERR_APP_BUSY = 0x019E , /* * DFUService is busy (eg: flush in progress) */
138
181
AUTH_CALLBACK_REPLY_ATTERR_APP_INVALID_SLOT_NUM = 0x019F , /* * Client requested invalid slot index */
182
+ };
183
+
184
+ /* *
185
+ * DFU-specific status codes
186
+ */
187
+ enum StatusCode_t{
188
+ DFU_STATE_IDLE = 0x00 , /* * Neutral state */
189
+ DFU_STATE_UPDATE_SUCCESSFUL = 0x01 ,
190
+ DFU_STATE_UNKNOWN_FAILURE = 0x02 ,
191
+ DFU_STATE_VALIDATION_FAILURE = 0x03 , /* * Validation/Authentication of update candidate failed */
192
+ DFU_STATE_INSTALLATION_FAILURE = 0x04 , /* * Installation of update candidate failed */
193
+ DFU_STATE_APPLICATION_OVERSIZE = 0x05 , /* * Update candidate exceeded memory bounds */
194
+ DFU_STATE_FLASH_ERROR = 0x06 , /* * Flash error */
195
+ DFU_STATE_HARDWARE_ERROR = 0x07 , /* * Hardware failure */
196
+
197
+ DFU_STATE_SYNC_LOSS_BIT = 0x80 , /* * If the MSbit is set in the status, the 7LSB indicate the sequence ID at which sync was lost */
139
198
140
199
};
141
200
@@ -179,11 +238,27 @@ class DFUService : public ble::GattServer::EventHandler,
179
238
180
239
public:
181
240
241
+ /* *
242
+ * Protip: as a general rule, when using delegated constructors, you should
243
+ * fully specify (ie: initialize all members in the initializer list) the version
244
+ * of the constructor that takes the largest number of arguments.
245
+ */
246
+
182
247
/* *
183
248
* Instantiate a DFUService instance
184
249
* @param[in] bd BlockDevice to use for storing update candidates in slot 0
250
+ * @param[in] queue EventQueue to process memory writes on
251
+ * @param[in] fw_rev Optional, Current firmware revision string
252
+ * @param[in] dev_desc Optional, Description of the device that this firmware is executed on
253
+ *
254
+ * @note The optional parameters MUST be supplied if your GattServer has multiple DFUService
255
+ * instances available. They are optional if your GattServer has only one DFUService instance.
256
+ * Each DFUService must implement a firmware revision characteristic with an
257
+ * associated characteristic user description descriptor that uniquely identifies
258
+ * the device that executes the firmware targeted by the DFUService.
185
259
*/
186
- DFUService (mbed::BlockDevice *bd);
260
+ DFUService (mbed::BlockDevice *bd, events::EventQueue &queue,
261
+ const char *fw_rev = nullptr , const char *dev_desc = nullptr );
187
262
188
263
virtual ~DFUService ();
189
264
@@ -192,7 +267,7 @@ class DFUService : public ble::GattServer::EventHandler,
192
267
}
193
268
194
269
bool is_dfu_enabled () const {
195
- // TODO return _dfu_contrl & DFU_ENABLED_BIT
270
+ return (_dfu_control & DFU_CTRL_ENABLE_BIT);
196
271
}
197
272
198
273
void start (BLE &ble_interface);
@@ -226,7 +301,84 @@ class DFUService : public ble::GattServer::EventHandler,
226
301
227
302
protected:
228
303
229
- void set_status ();
304
+ /* *
305
+ * Initialize and erase the selected flash slot
306
+ *
307
+ * @note This function may run for several seconds while erasing the currently
308
+ * selected slot, depending on the size of the slot and flash speed.
309
+ */
310
+ void init_selected_slot (void );
311
+
312
+ /* *
313
+ * Internal function to process buffered binary serial data
314
+ */
315
+ void process_buffer (void );
316
+
317
+ /* *
318
+ * Schedule a serialized call to process_buffer on the event queue.
319
+ *
320
+ * @note this function has no effect if a write has already been scheduled
321
+ */
322
+ void schedule_write (void ) {
323
+ if (!_scheduled_write) {
324
+ _scheduled_write = _queue.call (
325
+ mbed::callback (this , &DFUService::process_buffer));
326
+ }
327
+ }
328
+
329
+ /* *
330
+ * Set the status of the DFUService and notify any subscribed peers
331
+ */
332
+ void set_status (uint8_t status);
333
+
334
+ /* *
335
+ * Set the DFU control characteristic and notify any subscribedd peers
336
+ */
337
+ void set_dfu_ctrl (uint8_t ctrl);
338
+
339
+ /* *
340
+ * Sets the flow control pause bit and notifies any subscribed peers
341
+ *
342
+ * @note This will not have any effect if the bit is already set
343
+ */
344
+ inline void set_fc_bit (void ) {
345
+ mbed::ScopedLock<PlatformMutex> lock (_mutex);
346
+ if (!(_dfu_control & DFU_CTRL_FC_PAUSE_BIT)) {
347
+ set_dfu_ctrl (_dfu_control | DFU_CTRL_FC_PAUSE_BIT);
348
+ }
349
+ }
350
+
351
+ /* *
352
+ * Clears the flow control pause bit and notifies any subscribed peers
353
+ *
354
+ * @note This will not have any effect if the bit is already cleared
355
+ */
356
+ inline void clear_fc_bit (void ) {
357
+ mbed::ScopedLock<PlatformMutex> lock (_mutex);
358
+ if (_dfu_control & DFU_CTRL_FC_PAUSE_BIT) {
359
+ set_dfu_ctrl (_dfu_control & ~(DFU_CTRL_FC_PAUSE_BIT));
360
+ }
361
+ }
362
+
363
+ /* *
364
+ * Initiates a binary data stream buffer flush and sets the flow control bit
365
+ */
366
+ inline void initiate_flush (void ) {
367
+ mbed::ScopedLock<PlatformMutex> lock (_mutex);
368
+ /* Initiate a flush and set the FC pause bit */
369
+ _flush_bin_buf = true ;
370
+ set_fc_bit ();
371
+ schedule_write ();
372
+ }
373
+
374
+ /* *
375
+ * Completes a binary data stream buffer flush and clears the flow control bit
376
+ */
377
+ void flush_complete (void ) {
378
+ mbed::ScopedLock<PlatformMutex> lock (_mutex);
379
+ _flush_bin_buf = false ;
380
+ clear_fc_bit ();
381
+ }
230
382
231
383
/* * GattServer::EventHandler overrides */
232
384
void onDataWritten (const GattWriteCallbackParams ¶ms) override ;
@@ -263,21 +415,31 @@ class DFUService : public ble::GattServer::EventHandler,
263
415
/* * Update status */
264
416
uint8_t _status = 0 ;
265
417
418
+ /* * Optional firmware revision and description strings */
419
+ const char *_fw_rev_str;
420
+
421
+ /* * Optional firmware characteristic user description descriptor */
422
+ CharacteristicUserDescriptionDescriptor _fw_cudd;
423
+
424
+ /* * GattCharacteristic constructor requires a list of pointers to descriptors... */
425
+ GattAttribute *_fw_descs[1 ] = { (GattAttribute *) &_fw_cudd };
426
+
266
427
/* * Gatt Characteristics */
267
428
GattCharacteristic _slot_char;
268
429
GattCharacteristic _offset_char;
269
430
GattCharacteristic _rx_char;
270
431
GattCharacteristic _dfu_ctrl_char;
271
432
GattCharacteristic _status_char;
433
+ GattCharacteristic _firmware_rev_char;
272
434
273
- GattCharacteristic* _characteristics[5 ];
435
+ GattCharacteristic * _characteristics[6 ];
274
436
275
437
GattService _dfu_service;
276
438
277
- GattServer* _server;
439
+ GattServer * _server;
278
440
279
441
/* * Slot BlockDevices */
280
- mbed::BlockDevice *_slot_bds[MBED_CONF_BLE_DFU_SERVICE_MAX_SLOTS] = { 0 } ;
442
+ mbed::BlockDevice *_slot_bds[MBED_CONF_BLE_DFU_SERVICE_MAX_SLOTS];
281
443
282
444
/* * Application callbacks */
283
445
mbed::Callback<GattAuthCallbackReply_t(ControlChange&)> _ctrl_req_cb = nullptr ;
@@ -292,6 +454,14 @@ class DFUService : public ble::GattServer::EventHandler,
292
454
/* * Mutex */
293
455
PlatformMutex _mutex;
294
456
457
+ events::EventQueue &_queue;
458
+
459
+ /* * Queued event ID for scheduling flash writes */
460
+ uint32_t _scheduled_write = 0 ;
461
+
462
+ /* * Sequence ID for synchronization with client */
463
+ uint8_t _seq_id = 0 ;
464
+
295
465
};
296
466
297
467
#endif // BLE_FEATURE_GATT_SERVER
0 commit comments