@@ -75,21 +75,19 @@ DFUService::DFUService(mbed::BlockDevice *bd, events::EventQueue &queue, const c
75
75
_dfu_ctrl_char(uuids::DFUService::ControlUUID, &_dfu_control, 1 , 1 ,
76
76
(GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ |
77
77
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
78
- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
79
- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE),
80
- nullptr, 0, false),
78
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)),
81
79
_status_char(uuids::DFUService::StatusUUID, &_status, 1 , 1 ,
82
80
(GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ |
83
- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
84
- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE),
85
- nullptr, 0, false),
81
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)),
86
82
/* FW Rev Char will be dropped by GattServer if val in nullptr, len is 0, and it's readable */
87
83
_firmware_rev_char(GattCharacteristic::UUID_FIRMWARE_REVISION_STRING_CHAR,
88
84
(uint8_t *) fw_rev,
89
85
(fw_rev != nullptr ) ? strlen(fw_rev) : 0, /* Min length */
90
86
(fw_rev != nullptr ) ? strlen(fw_rev) : 0, /* Max length */
91
87
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ,
92
- _fw_descs, 1, true),
88
+ _fw_descs,
89
+ (dev_desc != nullptr ) ? 1 : 0,
90
+ true),
93
91
_dfu_service(uuids::DFUService::BaseUUID,
94
92
_characteristics,
95
93
(fw_rev != nullptr ) ?
@@ -156,11 +154,30 @@ void DFUService::onDataWritten(const GattWriteCallbackParams ¶ms) {
156
154
}
157
155
}
158
156
157
+ void DFUService::onUpdatesEnabled (const GattUpdatesEnabledCallbackParams ¶ms) {
158
+ if (params.attHandle == _dfu_ctrl_char.getValueHandle ()) {
159
+ TRACE_IF (tr_debug (" Updates enabled for control characteristic" ));
160
+ } else if (params.attHandle == _status_char.getValueHandle ()) {
161
+ TRACE_IF (tr_debug (" Updates enabled for status characteristic" ))
162
+ }
163
+ }
164
+
165
+ void DFUService::onUpdatesDisabled (const GattUpdatesDisabledCallbackParams ¶ms) {
166
+ if (params.attHandle == _dfu_ctrl_char.getValueHandle ()) {
167
+ TRACE_IF (tr_debug (" Updates disabled for control characteristic" ));
168
+ } else if (params.attHandle == _status_char.getValueHandle ()) {
169
+ TRACE_IF (tr_debug (" Updates disabled for status characteristic" ))
170
+ }
171
+ }
172
+
173
+
159
174
void DFUService::set_status (uint8_t status) {
175
+ TRACE_IF (tr_debug (" notifying status: %d" , status));
160
176
_server->write (_status_char.getValueHandle (), &status, 1 , false );
161
177
}
162
178
163
179
void DFUService::set_dfu_ctrl (uint8_t bits) {
180
+ TRACE_IF (tr_debug (" notifying ctrl: %d" , bits));
164
181
_server->write (_dfu_ctrl_char.getValueHandle (), &bits, 1 , false );
165
182
}
166
183
@@ -189,13 +206,16 @@ void DFUService::on_slot_write_request(GattWriteAuthCallbackParams *params) {
189
206
}
190
207
191
208
void DFUService::on_slot_written (uint8_t new_slot) {
192
- TRACE_IF (tr_debug (" slot written: %d" , new_slot));
193
- if (_slot_bds[new_slot] != nullptr ) {
194
- mbed::ScopedLock<PlatformMutex> lock (_mutex);
195
- _slot_bds[_selected_slot]->deinit ();
196
- _selected_slot = new_slot;
197
- /* Initialize and erase the selected slot */
198
- _queue.call (mbed::callback (this , &DFUService::init_selected_slot));
209
+ /* Ignore if selecting the same slot */
210
+ if (_selected_slot != new_slot) {
211
+ TRACE_IF (tr_debug (" slot written: %d" , new_slot));
212
+ if (_slot_bds[new_slot] != nullptr ) {
213
+ mbed::ScopedLock<PlatformMutex> lock (_mutex);
214
+ _slot_bds[_selected_slot]->deinit ();
215
+ _selected_slot = new_slot;
216
+ /* Initialize and erase the selected slot */
217
+ _queue.call (mbed::callback (this , &DFUService::init_selected_slot));
218
+ }
199
219
}
200
220
}
201
221
@@ -220,26 +240,31 @@ void DFUService::on_offset_written(uint32_t new_offset) {
220
240
void DFUService::on_bds_written (mbed::Span<const uint8_t > data) {
221
241
222
242
uint8_t seq_id = *data.data ();
223
- /* 7-bit sequence ID rolls over at 127 */
224
- uint8_t expected_id = ((_seq_id + 1 ) & 0x7F );
225
-
226
243
TRACE_IF (tr_debug (" bds written, sequence num: %d, %i bytes in payload" , seq_id, data.size ()-1 ));
227
244
245
+ /* Ignore 0-length writes */
246
+ if ((data.size () - 1 ) == 0 ) {
247
+ TRACE_IF (tr_warn (" zero-length packet written, ignoring" ));
248
+ return ;
249
+ }
250
+
228
251
/* Writes to the bds characteristic will be ignored if the flow control bit is set */
229
252
if (!_flush_bin_buf && !(_dfu_control & DFU_CTRL_FC_PAUSE_BIT)) {
230
253
231
254
/* Check sequence number and make sure it's what we expected */
232
- if (seq_id == expected_id) {
233
- _seq_id = expected_id;
255
+ if (seq_id == _seq_id) {
256
+ /* 7-bit sequence ID rolls over at 127 */
257
+ _seq_id++;
258
+ _seq_id &= 0x7F ;
234
259
_bin_stream_buf.push (data.subspan (1 ));
235
260
if (_bin_stream_buf.size () >= MBED_CONF_BLE_DFU_SERVICE_RX_FC_PAUSE_THRESHOLD) {
236
261
set_fc_bit ();
237
262
}
238
263
schedule_write ();
239
264
} else {
240
265
/* Otherwise, notify the client that the expected sequence ID did not match using the status characteristic */
241
- TRACE_IF (tr_warn (" sequence number does not match; expected: %d, actual: %d" , expected_id , seq_id));
242
- set_status (DFU_STATE_SYNC_LOSS_BIT | expected_id );
266
+ TRACE_IF (tr_warn (" sequence number does not match; expected: %d, actual: %d" , _seq_id , seq_id));
267
+ set_status (DFU_STATE_SYNC_LOSS_BIT | _seq_id );
243
268
}
244
269
}
245
270
}
@@ -260,12 +285,6 @@ void DFUService::on_dfu_ctrl_write_request(
260
285
/* Forward request to the application */
261
286
params->authorizationReply = _ctrl_req_cb (change);
262
287
TRACE_IF (tr_debug (" dfu_ctrl write request: accepted (by application)" ));
263
-
264
- if (params->authorizationReply == AUTH_CALLBACK_REPLY_SUCCESS) {
265
- /* If DFU is being enabled, clear the currently-selected update slot */
266
- _queue.call (mbed::callback (this , &DFUService::init_selected_slot));
267
- }
268
-
269
288
} else {
270
289
/* If no application handler, accept by default */
271
290
TRACE_IF (tr_debug (" dfu_ctrl write request: accepted" ));
@@ -276,55 +295,78 @@ void DFUService::on_dfu_ctrl_write_request(
276
295
void DFUService::on_dfu_ctrl_written (uint8_t new_ctrl) {
277
296
TRACE_IF (tr_debug (" dfu_ctrl written: %d" , new_ctrl));
278
297
mbed::ScopedLock<PlatformMutex> lock (_mutex);
298
+ ControlChange change (*this , new_ctrl);
279
299
/* Call application handler for control updates, if available */
280
300
if (_ctrl_update_cb) {
281
- ControlChange change (*this , new_ctrl);
282
301
_ctrl_update_cb (change);
302
+ }
283
303
284
- if (change.get_changed_bits () & DFU_CTRL_ENABLE_BIT) {
285
- TRACE_IF (tr_debug (" dfu mode enabled" ));
286
- }
304
+ if (change.get_changed_bits () & DFU_CTRL_ENABLE_BIT) {
305
+ TRACE_IF (tr_debug (" dfu mode %s" , (change.value () & DFU_CTRL_ENABLE_BIT) ? " enabled" : " aborted" ));
287
306
288
- if (change.get_changed_bits () & DFU_CTRL_DELTA_MODE_EN_BIT) {
289
- TRACE_IF (tr_debug (" delta mode enabled" ));
307
+ if (change.value () & DFU_CTRL_ENABLE_BIT) {
308
+ /* If DFU is being enabled, clear the currently-selected update slot */
309
+ _queue.call (mbed::callback (this , &DFUService::init_selected_slot));
290
310
}
311
+ }
291
312
292
- if (change.get_changed_bits () & DFU_CTRL_COMMIT_BIT) {
293
- TRACE_IF (tr_debug (" dfu commit" ));
294
- }
313
+ if (change.get_changed_bits () & DFU_CTRL_DELTA_MODE_EN_BIT) {
314
+ TRACE_IF (tr_debug (" delta mode %s" , (change.value () & DFU_CTRL_DELTA_MODE_EN_BIT) ? " enabled" : " disabled" ));
315
+ }
316
+
317
+ if (change.get_changed_bits () & DFU_CTRL_COMMIT_BIT) {
318
+ TRACE_IF (tr_debug (" dfu commit" ));
295
319
}
320
+
296
321
_dfu_control = new_ctrl;
297
322
}
298
323
299
324
void DFUService::init_selected_slot (void ) {
300
325
mbed::ScopedLock<PlatformMutex> lock (_mutex); // TODO mutex lock necessary here?
326
+ TRACE_IF (tr_debug (" initializing slot %d" , _selected_slot))
301
327
mbed::BlockDevice* slot = _slot_bds[_selected_slot];
302
328
slot->init ();
303
329
slot->erase (0 , slot->size ());
330
+ // Send a neutral notification of the status characteristic to tell the client we're ready
331
+ set_status (DFU_STATE_IDLE);
304
332
}
305
333
306
334
307
335
void DFUService::process_buffer (void ) {
336
+
337
+ /* TODO Rework the whole writing buffered data stuff */
338
+
308
339
mbed::BlockDevice* slot = _slot_bds[_selected_slot];
309
340
/* Attempt to write as many multiples of program size as possible at once */
310
341
311
342
/* *
312
343
* TODO is there a better way to do this? Without dynamic allocation, we would
313
344
* have to program byte-by-byte. This would likely be a significant hit in speed.
314
345
*/
315
- bd_size_t write_size = (_bin_stream_buf.size () / slot->get_program_size ());
346
+ bd_size_t write_size = (_bin_stream_buf.size () / slot->get_program_size ()) * slot->get_program_size ();
347
+ TRACE_IF (tr_debug (" processing buffer: %lu => %lu" ,
348
+ _bin_stream_buf.size (),
349
+ write_size));
350
+ if (write_size == 0 ) {
351
+ /* Skip 0-length writes */
352
+ _scheduled_write = 0 ;
353
+ return ;
354
+ }
316
355
uint8_t *temp_buf = new uint8_t [write_size];
317
356
_bin_stream_buf.pop (temp_buf, write_size);
318
357
int result = slot->program (temp_buf, _current_offset, write_size);
319
358
if (result) {
359
+ TRACE_IF (tr_err (" programming memory error: %d" , result));
320
360
set_status (DFU_STATE_FLASH_ERROR);
321
361
}
322
362
_current_offset += write_size;
323
363
delete[] temp_buf;
324
364
325
365
/* If the buffer isn't empty and a flush should be performed, pad the write */
326
366
if (_bin_stream_buf.size () && _flush_bin_buf) {
327
-
367
+ TRACE_IF (tr_debug (" flushing buffer: %lu => %lu" ,
368
+ _bin_stream_buf.size (),
369
+ write_size));
328
370
write_size = slot->get_program_size ();
329
371
temp_buf = new uint8_t [write_size];
330
372
/* Pad the write buffer with the BD's erase value */
@@ -341,6 +383,10 @@ void DFUService::process_buffer(void) {
341
383
delete[] temp_buf;
342
384
flush_complete ();
343
385
}
386
+
387
+ _scheduled_write = 0 ;
388
+ schedule_write ();
389
+
344
390
}
345
391
346
392
0 commit comments