@@ -75,18 +75,32 @@ MEMFAULT_STATIC_ASSERT(configTIMER_TASK_STACK_DEPTH >= 256,
75
75
#define MDS_DYNAMIC_ACCESS_CONTROL 0
76
76
#endif
77
77
78
- #define MDS_VERSION 0x01
78
+ //! Payload returned via a read to "MDS Supported Features Characteristic"
79
+ const static uint8_t s_mds_supported_features [] = {
80
+ // no feature additions since the first spin of the profile
81
+ 0x0
82
+ };
79
83
80
- #define MDS_MAJOR_VERSION 0x01
81
- #define MDS_MINOR_VERSION 0x00
82
- #define MDS_PATCH_VERSION 0x00
84
+ //! Valid SNs used when sending data are 0-31
85
+ #define MDS_TOTAL_SEQ_NUMBERS 32
86
+
87
+ typedef enum {
88
+ kMdsDataExportMode_StreamingDisabled = 0x00 ,
89
+ kMdsDataExportMode_FullStreamingEnabled = 0x01 ,
90
+ } eMdsDataExportMode ;
83
91
84
- const static uint8_t s_mds_version [] = { MDS_VERSION , MDS_MINOR_VERSION , MDS_PATCH_VERSION };
92
+
93
+ typedef MEMFAULT_PACKED_STRUCT {
94
+ // bits 5-7: rsvd for future use
95
+ // bits 0-4: sequence number
96
+ uint8_t hdr ;
97
+ uint8_t chunk [];
98
+ } sMdsDataExportPayload ;
85
99
86
100
typedef struct {
87
101
ble_service_t svc ;
88
102
89
- uint16_t version_h ;
103
+ uint16_t supported_features_h ;
90
104
uint16_t data_uri_h ;
91
105
uint16_t auth_h ;
92
106
uint16_t device_id_h ;
@@ -100,13 +114,13 @@ typedef struct {
100
114
// second request will be ignored.
101
115
struct {
102
116
bool active ;
117
+ eMdsDataExportMode mode ;
118
+ uint8_t seq_num ; // current sequence number to use
103
119
uint16_t conn_idx ;
104
120
} subscriber ;
105
121
106
- struct {
107
- void * buf ;
108
- size_t buf_len ;
109
- } chunk ;
122
+ sMdsDataExportPayload * payload ;
123
+ size_t chunk_len ;
110
124
111
125
TimerHandle_t timer ;
112
126
} md_service_t ;
@@ -115,6 +129,7 @@ typedef enum {
115
129
kMdsAppError_InvalidLength = ATT_ERROR_APPLICATION_ERROR ,
116
130
kMdsAppError_ClientAlreadySubscribed ,
117
131
kMdsAppError_InsufficientLength ,
132
+ kMdsAppError_ClientNotSubscribed ,
118
133
} eMdsAppError ;
119
134
120
135
static md_service_t * s_mds ;
@@ -135,6 +150,8 @@ static void prv_handle_disconnected_evt(ble_service_t *svc,
135
150
if (mds -> subscriber .active && (mds -> subscriber .conn_idx == evt -> conn_idx )) {
136
151
mds -> subscriber .active = false;
137
152
mds -> subscriber .conn_idx = 0 ;
153
+ mds -> subscriber .seq_num = 0 ;
154
+ mds -> subscriber .mode = kMdsDataExportMode_StreamingDisabled ;
138
155
}
139
156
140
157
xTimerStop (mds -> timer , 0 );
@@ -147,29 +164,37 @@ static void prv_try_notify(md_service_t *mds, uint16_t conn_idx) {
147
164
return ;
148
165
}
149
166
167
+ if (mds -> subscriber .mode == kMdsDataExportMode_StreamingDisabled ) {
168
+ // client has subscribed but not yet enabled data export
169
+ return ;
170
+ }
171
+
150
172
uint16_t mtu_size = 0 ;
151
173
ble_error_t rv = ble_gattc_get_mtu (conn_idx , & mtu_size );
152
174
if (rv != BLE_STATUS_OK ) {
153
175
return ;
154
176
}
155
177
156
- if ((mds -> chunk .buf == NULL ) && memfault_packetizer_data_available ()) {
157
- mds -> chunk .buf = OS_MALLOC (mtu_size - MDS_ATT_HEADER_OVERHEAD );
158
- if (mds -> chunk .buf != NULL ) {
159
- mds -> chunk .buf_len = mtu_size - MDS_ATT_HEADER_OVERHEAD ;
160
- memfault_packetizer_get_chunk (mds -> chunk .buf , & mds -> chunk .buf_len );
178
+ if ((mds -> payload == NULL ) && memfault_packetizer_data_available ()) {
179
+ mds -> payload = OS_MALLOC (mtu_size - MDS_ATT_HEADER_OVERHEAD );
180
+ if (mds -> payload != NULL ) {
181
+ mds -> payload -> hdr = mds -> subscriber .seq_num & 0x1f ;
182
+ mds -> chunk_len = mtu_size - MDS_ATT_HEADER_OVERHEAD - sizeof (* mds -> payload );
183
+ memfault_packetizer_get_chunk (& mds -> payload -> chunk [0 ], & mds -> chunk_len );
161
184
}
162
185
}
163
186
164
- if (mds -> chunk .buf_len != 0 ) {
187
+ if (mds -> chunk_len != 0 ) {
188
+
165
189
rv = ble_gatts_send_event (conn_idx , mds -> chunk_val_h , GATT_EVENT_NOTIFICATION ,
166
- mds -> chunk . buf_len , mds -> chunk . buf );
190
+ mds -> chunk_len + sizeof ( * mds -> payload ) , mds -> payload );
167
191
if (rv == BLE_STATUS_OK ) {
192
+ mds -> subscriber .seq_num = (mds -> subscriber .seq_num + 1 ) % MDS_TOTAL_SEQ_NUMBERS ;
168
193
// Note: No need to schedule a retry since we will pump more data when the sent callback is
169
194
// invoked for the current notification
170
- OS_FREE (mds -> chunk . buf );
171
- mds -> chunk . buf = NULL ;
172
- mds -> chunk . buf_len = 0 ;
195
+ OS_FREE (mds -> payload );
196
+ mds -> payload = NULL ;
197
+ mds -> chunk_len = 0 ;
173
198
return ;
174
199
}
175
200
}
@@ -216,9 +241,9 @@ static void prv_handle_read_req(ble_service_t *svc,
216
241
char uri [MDS_MAX_DATA_URI_LENGTH ];
217
242
struct MemfaultDeviceInfo info ;
218
243
219
- if (evt -> handle == mds -> version_h ) {
220
- value = & s_mds_version ;
221
- length = sizeof (s_mds_version );
244
+ if (evt -> handle == mds -> supported_features_h ) {
245
+ value = & s_mds_supported_features ;
246
+ length = sizeof (s_mds_supported_features );
222
247
} else if (evt -> handle == mds -> data_uri_h ) {
223
248
memfault_platform_get_device_info (& info );
224
249
strncpy (uri , MDS_URI_BASE , sizeof (uri ) - 1 );
@@ -282,7 +307,7 @@ static att_error_t prv_handle_cccd_write(md_service_t *mds, uint16_t conn_idx,
282
307
const bool subscribe_for_notifs = ((cccd & GATT_CCC_NOTIFICATIONS ) != 0 );
283
308
if (!mds -> subscriber .active ) {
284
309
// NB: we expect caller to subscribe for notifications each time they connect
285
- // so don't persist the state across disconnects _and_ we only allow one
310
+ // so don't persist the mode across disconnects _and_ we only allow one
286
311
// active subscription at a time.
287
312
mds -> subscriber .active = subscribe_for_notifs ;
288
313
mds -> subscriber .conn_idx = conn_idx ;
@@ -298,6 +323,37 @@ static att_error_t prv_handle_cccd_write(md_service_t *mds, uint16_t conn_idx,
298
323
return ATT_ERROR_OK ;
299
324
}
300
325
326
+ static att_error_t prv_handle_data_export_write (md_service_t * mds , uint16_t conn_idx ,
327
+ uint16_t offset , uint16_t length ,
328
+ const uint8_t * value ) {
329
+ if (offset != 0 ) {
330
+ return ATT_ERROR_ATTRIBUTE_NOT_LONG ;
331
+ }
332
+
333
+ if (length != sizeof (uint8_t )) {
334
+ return kMdsAppError_InvalidLength ;
335
+ }
336
+
337
+ if ((!mds -> subscriber .active ) || (mds -> subscriber .conn_idx != conn_idx )) {
338
+ return kMdsAppError_ClientNotSubscribed ;
339
+ }
340
+
341
+ const eMdsDataExportMode cmd = (eMdsDataExportMode )get_u8 (value );
342
+
343
+ switch (cmd ) {
344
+ case kMdsDataExportMode_StreamingDisabled :
345
+ case kMdsDataExportMode_FullStreamingEnabled :
346
+ break ;
347
+ default :
348
+ return ATT_ERROR_REQUEST_NOT_SUPPORTED ;
349
+ }
350
+
351
+ mds -> subscriber .mode = cmd ;
352
+ prv_try_notify (mds , conn_idx );
353
+
354
+ return ATT_ERROR_OK ;
355
+ }
356
+
301
357
static void prv_handle_write_req (ble_service_t * svc ,
302
358
const ble_evt_gatts_write_req_t * evt ) {
303
359
if (!mds_access_enabled (evt -> conn_idx )) {
@@ -312,22 +368,18 @@ static void prv_handle_write_req(ble_service_t *svc,
312
368
if (evt -> handle == mds -> chunk_cccd_h ) {
313
369
status = prv_handle_cccd_write (mds , evt -> conn_idx , evt -> offset , evt -> length ,
314
370
evt -> value );
315
- if (status == ATT_ERROR_OK ) {
316
- // Empirically, sending a notification immediately will wind up getting flushed
317
- // before the response for the CCCD write so we defer starting to send chunk
318
- // notifications so they do not arrive out of order
319
- xTimerChangePeriod (mds -> timer , 10 , 0 );
320
- xTimerStart (mds -> timer , 0 );
321
- }
371
+ } else if (evt -> handle == mds -> chunk_val_h ) {
372
+ status = prv_handle_data_export_write (mds , evt -> conn_idx , evt -> offset , evt -> length ,
373
+ evt -> value );
322
374
}
323
375
324
376
ble_gatts_write_cfm (evt -> conn_idx , evt -> handle , status );
325
377
}
326
378
327
379
static void prv_cleanup_service (ble_service_t * svc ) {
328
380
md_service_t * mds = (md_service_t * )svc ;
329
- if (mds -> chunk . buf != NULL ) {
330
- OS_FREE (mds -> chunk . buf );
381
+ if (mds -> payload != NULL ) {
382
+ OS_FREE (mds -> payload );
331
383
}
332
384
if (mds -> timer != NULL ) {
333
385
xTimerDelete (mds -> timer , portMAX_DELAY );
@@ -365,7 +417,7 @@ void *mds_boot(void) {
365
417
366
418
ble_uuid_from_string ("54220001-f6a5-4007-a371-722f4ebd8436" , & uuid );
367
419
ble_gatts_add_characteristic (& uuid , GATT_PROP_READ , ATT_PERM_RW , sizeof (uint8_t ),
368
- GATTS_FLAG_CHAR_READ_REQ , NULL , & mds -> version_h );
420
+ GATTS_FLAG_CHAR_READ_REQ , NULL , & mds -> supported_features_h );
369
421
370
422
ble_uuid_from_string ("54220002-f6a5-4007-a371-722f4ebd8436" , & uuid );
371
423
ble_gatts_add_characteristic (& uuid , GATT_PROP_READ , ATT_PERM_RW , sizeof (uint8_t ),
@@ -380,13 +432,13 @@ void *mds_boot(void) {
380
432
GATTS_FLAG_CHAR_READ_REQ , NULL , & mds -> auth_h );
381
433
382
434
ble_uuid_from_string ("54220005-f6a5-4007-a371-722f4ebd8436" , & uuid );
383
- ble_gatts_add_characteristic (& uuid , GATT_PROP_NOTIFY , ATT_PERM_RW , sizeof (uint8_t ),
435
+ ble_gatts_add_characteristic (& uuid , GATT_PROP_NOTIFY | GATT_PROP_WRITE , ATT_PERM_RW , sizeof (uint8_t ),
384
436
GATTS_FLAG_CHAR_READ_REQ , NULL , & mds -> chunk_val_h );
385
437
386
438
ble_uuid_create16 (UUID_GATT_CLIENT_CHAR_CONFIGURATION , & uuid );
387
439
ble_gatts_add_descriptor (& uuid , ATT_PERM_RW , 2 , 0 , & mds -> chunk_cccd_h );
388
440
389
- ble_gatts_register_service (& mds -> svc .start_h , & mds -> version_h , & mds -> device_id_h ,
441
+ ble_gatts_register_service (& mds -> svc .start_h , & mds -> supported_features_h , & mds -> device_id_h ,
390
442
& mds -> data_uri_h , & mds -> auth_h , & mds -> chunk_val_h ,
391
443
& mds -> chunk_cccd_h , 0 );
392
444
0 commit comments