@@ -75,18 +75,32 @@ MEMFAULT_STATIC_ASSERT(configTIMER_TASK_STACK_DEPTH >= 256,
7575#define MDS_DYNAMIC_ACCESS_CONTROL 0
7676#endif
7777
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+ };
7983
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 ;
8391
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 ;
8599
86100typedef struct {
87101 ble_service_t svc ;
88102
89- uint16_t version_h ;
103+ uint16_t supported_features_h ;
90104 uint16_t data_uri_h ;
91105 uint16_t auth_h ;
92106 uint16_t device_id_h ;
@@ -100,13 +114,13 @@ typedef struct {
100114 // second request will be ignored.
101115 struct {
102116 bool active ;
117+ eMdsDataExportMode mode ;
118+ uint8_t seq_num ; // current sequence number to use
103119 uint16_t conn_idx ;
104120 } subscriber ;
105121
106- struct {
107- void * buf ;
108- size_t buf_len ;
109- } chunk ;
122+ sMdsDataExportPayload * payload ;
123+ size_t chunk_len ;
110124
111125 TimerHandle_t timer ;
112126} md_service_t ;
@@ -115,6 +129,7 @@ typedef enum {
115129 kMdsAppError_InvalidLength = ATT_ERROR_APPLICATION_ERROR ,
116130 kMdsAppError_ClientAlreadySubscribed ,
117131 kMdsAppError_InsufficientLength ,
132+ kMdsAppError_ClientNotSubscribed ,
118133} eMdsAppError ;
119134
120135static md_service_t * s_mds ;
@@ -135,6 +150,8 @@ static void prv_handle_disconnected_evt(ble_service_t *svc,
135150 if (mds -> subscriber .active && (mds -> subscriber .conn_idx == evt -> conn_idx )) {
136151 mds -> subscriber .active = false;
137152 mds -> subscriber .conn_idx = 0 ;
153+ mds -> subscriber .seq_num = 0 ;
154+ mds -> subscriber .mode = kMdsDataExportMode_StreamingDisabled ;
138155 }
139156
140157 xTimerStop (mds -> timer , 0 );
@@ -147,29 +164,37 @@ static void prv_try_notify(md_service_t *mds, uint16_t conn_idx) {
147164 return ;
148165 }
149166
167+ if (mds -> subscriber .mode == kMdsDataExportMode_StreamingDisabled ) {
168+ // client has subscribed but not yet enabled data export
169+ return ;
170+ }
171+
150172 uint16_t mtu_size = 0 ;
151173 ble_error_t rv = ble_gattc_get_mtu (conn_idx , & mtu_size );
152174 if (rv != BLE_STATUS_OK ) {
153175 return ;
154176 }
155177
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 );
161184 }
162185 }
163186
164- if (mds -> chunk .buf_len != 0 ) {
187+ if (mds -> chunk_len != 0 ) {
188+
165189 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 );
167191 if (rv == BLE_STATUS_OK ) {
192+ mds -> subscriber .seq_num = (mds -> subscriber .seq_num + 1 ) % MDS_TOTAL_SEQ_NUMBERS ;
168193 // Note: No need to schedule a retry since we will pump more data when the sent callback is
169194 // 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 ;
173198 return ;
174199 }
175200 }
@@ -216,9 +241,9 @@ static void prv_handle_read_req(ble_service_t *svc,
216241 char uri [MDS_MAX_DATA_URI_LENGTH ];
217242 struct MemfaultDeviceInfo info ;
218243
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 );
222247 } else if (evt -> handle == mds -> data_uri_h ) {
223248 memfault_platform_get_device_info (& info );
224249 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,
282307 const bool subscribe_for_notifs = ((cccd & GATT_CCC_NOTIFICATIONS ) != 0 );
283308 if (!mds -> subscriber .active ) {
284309 // 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
286311 // active subscription at a time.
287312 mds -> subscriber .active = subscribe_for_notifs ;
288313 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,
298323 return ATT_ERROR_OK ;
299324}
300325
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+
301357static void prv_handle_write_req (ble_service_t * svc ,
302358 const ble_evt_gatts_write_req_t * evt ) {
303359 if (!mds_access_enabled (evt -> conn_idx )) {
@@ -312,22 +368,18 @@ static void prv_handle_write_req(ble_service_t *svc,
312368 if (evt -> handle == mds -> chunk_cccd_h ) {
313369 status = prv_handle_cccd_write (mds , evt -> conn_idx , evt -> offset , evt -> length ,
314370 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 );
322374 }
323375
324376 ble_gatts_write_cfm (evt -> conn_idx , evt -> handle , status );
325377}
326378
327379static void prv_cleanup_service (ble_service_t * svc ) {
328380 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 );
331383 }
332384 if (mds -> timer != NULL ) {
333385 xTimerDelete (mds -> timer , portMAX_DELAY );
@@ -365,7 +417,7 @@ void *mds_boot(void) {
365417
366418 ble_uuid_from_string ("54220001-f6a5-4007-a371-722f4ebd8436" , & uuid );
367419 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 );
369421
370422 ble_uuid_from_string ("54220002-f6a5-4007-a371-722f4ebd8436" , & uuid );
371423 ble_gatts_add_characteristic (& uuid , GATT_PROP_READ , ATT_PERM_RW , sizeof (uint8_t ),
@@ -380,13 +432,13 @@ void *mds_boot(void) {
380432 GATTS_FLAG_CHAR_READ_REQ , NULL , & mds -> auth_h );
381433
382434 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 ),
384436 GATTS_FLAG_CHAR_READ_REQ , NULL , & mds -> chunk_val_h );
385437
386438 ble_uuid_create16 (UUID_GATT_CLIENT_CHAR_CONFIGURATION , & uuid );
387439 ble_gatts_add_descriptor (& uuid , ATT_PERM_RW , 2 , 0 , & mds -> chunk_cccd_h );
388440
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 ,
390442 & mds -> data_uri_h , & mds -> auth_h , & mds -> chunk_val_h ,
391443 & mds -> chunk_cccd_h , 0 );
392444
0 commit comments