diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index 588e0966..9df18eb1 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -249,6 +249,18 @@ #define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE 1 #endif +// Enable/Disable BLE Service in pairing mode: MicroBitUtilityService +// Set '1' to enable. +#ifndef MICROBIT_BLE_UTILITY_SERVICE_PAIRING +#define MICROBIT_BLE_UTILITY_SERVICE_PAIRING 1 +#endif + +// Enable/Disable BLE Service in application mode: MicroBitUtilityService +// Set '1' to enable. +#ifndef MICROBIT_BLE_UTILITY_SERVICE +#define MICROBIT_BLE_UTILITY_SERVICE 0 +#endif + // Enable/Disable Nordic Firmware style BLE based UART implimentation. // The default codal implimentation reverses the TX/RX ids // Set to '1' to enable diff --git a/inc/bluetooth/MicroBitUtilityService.h b/inc/bluetooth/MicroBitUtilityService.h new file mode 100644 index 00000000..91d528e3 --- /dev/null +++ b/inc/bluetooth/MicroBitUtilityService.h @@ -0,0 +1,177 @@ +/* +The MIT License (MIT) + +This class has been written for the Microbit Educational Foundation. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MICROBIT_UTILITY_SERVICE_H +#define MICROBIT_UTILITY_SERVICE_H + +#include "MicroBitConfig.h" + +#if CONFIG_ENABLED(DEVICE_BLE) + +#include "MicroBitBLEManager.h" +#include "MicroBitBLEService.h" +#include "MicroBitMemoryMap.h" + +#include "MicroBitFlash.h" +#include "MicroBitStorage.h" + +#include "MicroBitComponent.h" +#include "MicroBitEvent.h" +#include "EventModel.h" +#include "MicroBitLog.h" + + +class MicroBitUtilityWorkspace; + + +/** + * Class declration for the custom MicroBit Utility Service. + * Provides simple BLE request/response + * See MicroBitUtilityTypes.h + */ +class MicroBitUtilityService : public MicroBitBLEService +{ + static MicroBitUtilityService *shared; + + public: + /** + * Return a pointer to the shared service + * @return a pointer to the service, or NULL if it has not been created + */ + static MicroBitUtilityService *getShared() + { + return shared; + } + + /** + * Return a pointer to the shared service, creating it if necessary + * @param _ble an instance of BLEDevice + * @param _messageBus An instance of a MessageBus to interface with. + * @param _storage A persistent storage manager to use to hold non-volatile state. + * @param _log a log storage manager to read stored data. + * @return a pointer to the service, or NULL if it has not been created + */ + static MicroBitUtilityService *createShared( BLEDevice &_ble, EventModel &_messageBus, MicroBitStorage &_storage, MicroBitLog &_log); + + public: + /** + * Constructor. + * @param _ble an instance of BLEDevice + * @param _messageBus An instance of a MessageBus to interface with. + * @param _storage A persistent storage manager to use to hold non-volatile state. + * @param _log a log storage manager to read stored data. + */ + MicroBitUtilityService( BLEDevice &_ble, EventModel &_messageBus, MicroBitStorage &_storage, MicroBitLog &_log); + + private: + /** + * Invoked when BLE connects. + * Start listening for events + */ + void onConnect( const microbit_ble_evt_t *p_ble_evt); + + /** + * Invoked when BLE disconnects. + * Stop listening for events + */ + void onDisconnect( const microbit_ble_evt_t *p_ble_evt); + + /** + * Callback. Invoked when any of our attributes are written via BLE. + * Record the request, and trigger processing. + * Requests are not queued. If a request is actively being processed, ignore the new request, otherwise simply override the previous one. + */ + void onDataWritten(const microbit_ble_evt_write_t *params); + + /** + * Callback. Invoked when a registered event occurs. + */ + void onEvent(MicroBitEvent e); + + private: + // MessageBus we're using + EventModel &messageBus; + MicroBitStorage &storage; + MicroBitLog &log; + + uint8_t characteristicValue[ 20]; + + // Index for each charactersitic in arrays of handles and UUIDs + typedef enum mbbs_cIdx + { + mbbs_cIdxCTRL, + mbbs_cIdxCOUNT + } mbbs_cIdx; + + // UUIDs for our service and characteristics + static const uint16_t serviceUUID; + static const uint16_t charUUID[ mbbs_cIdxCOUNT]; + + // Data for each characteristic when they are held by Soft Device. + MicroBitBLEChar chars[ mbbs_cIdxCOUNT]; + + public: + int characteristicCount() { return mbbs_cIdxCOUNT; }; + MicroBitBLEChar *characteristicPtr( int idx) { return &chars[ idx]; }; + + private: + + MicroBitUtilityWorkspace *workspace; + + /** + * Listen for or ignore events for processing requests + * @param yes true to listen, otherwise ignore + */ + void listen( bool yes); + + /** + * Send a reply packet + * @param data Data to send + * @param length Length if the data + * @return DEVICE_OK if sent + */ + int sendReply( const void *data, uint16_t length); + + /** + * Process the current request. This may block. + * @return DEVICE_OK if finished + */ + int processRequest(); + + /** + * Process request typeLogLength + * @return DEVICE_OK if finished + */ + int processLogLength(); + + /** + * Process request typeLogRead + * @return DEVICE_OK if finished + */ + int processLogRead(); +}; + + +#endif +#endif diff --git a/inc/bluetooth/MicroBitUtilityTypes.h b/inc/bluetooth/MicroBitUtilityTypes.h new file mode 100644 index 00000000..04878bde --- /dev/null +++ b/inc/bluetooth/MicroBitUtilityTypes.h @@ -0,0 +1,115 @@ +/* +The MIT License (MIT) + +This header has been written for the Microbit Educational Foundation. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MICROBIT_UTILITY_TYPES_H +#define MICROBIT_UTILITY_TYPES_H + +#include + +/* Define types for the MicroBitUtilityService + * + * request_t - Request sent from client to service. + * job (1 byte) - see below + * type (1 byte) - specifies the request type + * data (up to 18 bytes) - depends on request type + * + * reply_t - Reply from service to client. + * job (1 byte) - see below + * data (up to 19 bytes) - depends on request type + * + * job - Synchronize requests and replies. + * Enables the client to ignore replies to a previous request, and detect missing reply packets. + * Client cycles high nibble i.e. { 0x00, 0x10, 0x20, ..., 0xF0, 0x00, ...} + * Service reply cycles low nibble, i.e client job + { 0x00, 0x01, 0x02, ..., 0x0E, 0x00, ... } + * low nibble == 0x0F (jobLowERR) in a reply indicates error (data = 4 bytes signed integer error) + * + * Request types and data contents: + * + * type 1 - Log file length + * request format (1 byte) - 0 = HTML header; 1 = HTML; 2 = CSV + * reply length (4 bytes) - unsigned integer log data length + * + * type 2 - Log file data + * request format (1 byte) - 0 = HTML header; 1 = HTML; 2 = CSV + * reserved (1 byte) - set to zero + * index (4 bytes) - unsigned integer index into file + * batchlen (4 bytes) - unsigned size in bytes to return + * length (4 bytes) - length of whole file, from request type 1 (Log file length) + * reply data (up to 19 bytes) - 1 or more reply packets, to total batchlen bytes + * + */ +namespace MicroBitUtility +{ + typedef enum requestType_t + { + requestTypeNone, + requestTypeLogLength, // reply data = 4 bytes log data length + requestTypeLogRead // reply data = up to 19 bytes of log data + } requestType_t; + + typedef struct request_t + { + uint8_t job; // Client cycles high nibble i.e. { 0x00, 0x10, 0x20, ..., 0xF0, 0x00, ...} + uint8_t type; // requestType_t + uint8_t data[18]; + } request_t; + + typedef struct reply_t + { + uint8_t job; // Service cycles low nibble i.e client job + { 0x00, 0x01, 0x02, ..., 0x0E, 0x00, ... } + // low nibble == 0x0F (jobLowERR) indicates error and data = 4 bytes signed integer error + uint8_t data[19]; + } reply_t; + + typedef enum requestLogFormat + { + requestLogHTMLHeader = 0, + requestLogHTML = 1, + requestLogCSV = 2 + } requestLogFormat; + + typedef struct requestLog_t + { + uint8_t job; + uint8_t type; // requestType_t + uint8_t format; // requestLogFormat + } requestLog_t; + + typedef struct requestLogRead_t + { + uint8_t job; + uint8_t type; // requestType_t + uint8_t format; // requestLogFormat + uint8_t reserved; // set to zero + uint32_t index; // index into data + uint32_t batchlen; // size in bytes to return + uint32_t length; // length of whole file, from requestTypeLogLength + } requestLogRead_t; + + const uint8_t jobLowMAX = 0x0E; + const uint8_t jobLowERR = 0x0F; // reply data = 4 bytes signed integer error +}; + + +#endif diff --git a/inc/compat/MicroBitCompat.h b/inc/compat/MicroBitCompat.h index 8923e040..a5f8feaa 100644 --- a/inc/compat/MicroBitCompat.h +++ b/inc/compat/MicroBitCompat.h @@ -372,6 +372,8 @@ const uint16_t MICROBIT_ID_MBED_TICKER __attribute__ ((deprecated)) = 83; #define MICROBIT_ID_LOG DEVICE_ID_LOG +#define MICROBIT_ID_UTILITY 45 + #define MICROBIT_MAXIMUM_HEAPS DEVICE_MAXIMUM_HEAPS #define MICROBIT_NESTED_HEAP_SIZE 0 #define MICROBIT_PANIC_HEAP_FULL DEVICE_PANIC_HEAP_FULL diff --git a/model/MicroBit.cpp b/model/MicroBit.cpp index ad9a2e53..be4f30e4 100644 --- a/model/MicroBit.cpp +++ b/model/MicroBit.cpp @@ -272,6 +272,11 @@ int MicroBit::init() // Start the BLE stack, if it isn't already running. bleManager.init( ManagedString( microbit_friendly_name()), getSerial(), messageBus, storage, true); + +#if CONFIG_ENABLED(MICROBIT_BLE_UTILITY_SERVICE_PAIRING) + MICROBIT_DEBUG_DMESG( "UTILITY_SERVICE"); + MicroBitUtilityService::createShared( *ble, messageBus, storage, log); +#endif // Enter pairing mode, using the LED matrix for any necessary pairing operations bleManager.pairingMode(display, buttonA); } @@ -281,6 +286,11 @@ int MicroBit::init() #if CONFIG_ENABLED(DEVICE_BLE) && CONFIG_ENABLED(MICROBIT_BLE_ENABLED) // Start the BLE stack, if it isn't already running. bleManager.init( ManagedString( microbit_friendly_name()), getSerial(), messageBus, storage, false); + +#if CONFIG_ENABLED(MICROBIT_BLE_UTILITY_SERVICE) + MICROBIT_DEBUG_DMESG( "UTILITY_SERVICE"); + MicroBitUtilityService::createShared( *ble, messageBus, storage, log); +#endif #endif // Bring up the 64MHz external oscillator. diff --git a/model/MicroBit.h b/model/MicroBit.h index 05e70443..1f421d41 100644 --- a/model/MicroBit.h +++ b/model/MicroBit.h @@ -84,6 +84,7 @@ DEALINGS IN THE SOFTWARE. #include "MicroBitTemperatureService.h" #include "MicroBitUARTService.h" #include "MicroBitPartialFlashingService.h" +#include "MicroBitUtilityService.h" #endif #include "MicroBitStorage.h" diff --git a/source/bluetooth/MicroBitBLEChar.cpp b/source/bluetooth/MicroBitBLEChar.cpp index b0b5cbe0..54d1b184 100644 --- a/source/bluetooth/MicroBitBLEChar.cpp +++ b/source/bluetooth/MicroBitBLEChar.cpp @@ -65,7 +65,7 @@ bool MicroBitBLEChar::notifyChrValue( microbit_gaphandle_t connection, const uin if ( connection == BLE_CONN_HANDLE_INVALID) return false; - MICROBIT_DEBUG_DMESGF( "MicroBitBLEChar::notifyChrValue %d", (int) handles.value); + MICROBIT_DEBUG_DMESG( "MicroBitBLEChar::notifyChrValue %d", (int) handles.value); bool set = false; @@ -78,7 +78,7 @@ bool MicroBitBLEChar::notifyChrValue( microbit_gaphandle_t connection, const uin hvx_params.p_len = &length; hvx_params.p_data = data; - MICROBIT_DEBUG_DMESGF( "calling sd_ble_gatts_hvx( %d, %x, %d, %d)", + MICROBIT_DEBUG_DMESG( "calling sd_ble_gatts_hvx( %d, %x, %d, %d)", (int) handles.value, (unsigned int) data, (int) *data, (int) length); if ( MICROBIT_BLE_ECHK( sd_ble_gatts_hvx( connection, &hvx_params)) == NRF_SUCCESS) @@ -100,7 +100,7 @@ bool MicroBitBLEChar::indicateChrValue( microbit_gaphandle_t connection, const u if ( connection == BLE_CONN_HANDLE_INVALID) return false; - MICROBIT_DEBUG_DMESGF( "MicroBitBLEChar::indicateChrValue %d", (int) handles.value); + MICROBIT_DEBUG_DMESG( "MicroBitBLEChar::indicateChrValue %d", (int) handles.value); bool set = false; @@ -113,7 +113,7 @@ bool MicroBitBLEChar::indicateChrValue( microbit_gaphandle_t connection, const u hvx_params.p_len = &length; hvx_params.p_data = data; - MICROBIT_DEBUG_DMESGF( "calling sd_ble_gatts_hvx( %d, %x, %d, %d)", + MICROBIT_DEBUG_DMESG( "calling sd_ble_gatts_hvx( %d, %x, %d, %d)", (int) handles.value, (unsigned int) data, (int) *data, (int) length); if ( MICROBIT_BLE_ECHK( sd_ble_gatts_hvx( connection, &hvx_params)) == NRF_SUCCESS) @@ -135,7 +135,7 @@ bool MicroBitBLEChar::writeChrValue( microbit_gaphandle_t connection, const uint if ( connection == BLE_CONN_HANDLE_INVALID) return false; - MICROBIT_DEBUG_DMESGF( "MicroBitBLEChar::writeChrValue %d", (int) handles.value); + MICROBIT_DEBUG_DMESG( "MicroBitBLEChar::writeChrValue %d", (int) handles.value); bool done = true; diff --git a/source/bluetooth/MicroBitUtilityService.cpp b/source/bluetooth/MicroBitUtilityService.cpp new file mode 100644 index 00000000..6644c4bf --- /dev/null +++ b/source/bluetooth/MicroBitUtilityService.cpp @@ -0,0 +1,385 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Class definition for the custom MicroBit Utility service. + */ +#include "MicroBitConfig.h" + +#if CONFIG_ENABLED(DEVICE_BLE) + +#include "MicroBit.h" +#include "MicroBitUtilityService.h" +#include "MicroBitUtilityTypes.h" + +using namespace codal; +using namespace MicroBitUtility; + +#define MICROBIT_ID_UTILITY_PROCESS 0 + +#define MicroBitUtilityService_SLEEP 10 +#define MicroBitUtilityService_TIMEOUT 10 + +namespace MicroBitUtility +{ + typedef enum replyState_t + { + replyStateClear, + replyStateReady, // reply has been prepared but not sent + replyStateError + } replyState_t; +}; + + +//TODO How to choose service and characteristic IDs? +const uint16_t MicroBitUtilityService::serviceUUID = 0x0001; +const uint16_t MicroBitUtilityService::charUUID[ mbbs_cIdxCOUNT] = { 0x0002 }; + + +MicroBitUtilityService *MicroBitUtilityService::shared = NULL; + + +/** + * Class definition for the MicroBit Utility Service workspace. + * Provides simple BLE request/response + * See MicroBitUtilityTypes.h + */ +class MicroBitUtilityWorkspace +{ + public: + + request_t request; + reply_t reply; + uint8_t replyState; + uint8_t replyLength; + uint8_t jobLow; + bool lock; + + /** + * Constructor. + */ + MicroBitUtilityWorkspace() + { + init(); + lock = false; + } + + /** + * Initialise workspace + */ + void init() + { + memset( &request, 0, sizeof( request)); + memset( &reply, 0, sizeof( reply)); + replyState = replyStateClear; + replyLength = 0; + jobLow = 0; + } + + /** + * Prepare workspace to process new request + */ + void setRequest( const void *data, int len) + { + init(); + memcpy( &request, data, len); + } + + /** + * Prepare reply to send + */ + void setReplyReady( uint32_t replyLen) + { + reply.job = request.job + jobLow; + replyLength = replyLen; + replyState = replyStateReady; + } + + /** + * Set the reply data to a uint32_t value + */ + void setReply( uint32_t value) + { + memcpy( reply.data, &value, sizeof(uint32_t)); + setReplyReady( sizeof(uint32_t)); + } + + /** + * Set the reply data to an error + */ + void setReplyError( uint32_t error) + { + setReply( error); + replyState = replyStateError; + reply.job = request.job + jobLowERR; + } + + /** + * When a non-error reply has been sent, cycle jobLow for next reply + */ + void onReplySent( const void *data) + { + reply_t *reply = (reply_t *) data; + if ( ( reply->job & 0x0F) != jobLowERR) + { + jobLow++; + if ( jobLow > jobLowMAX) + jobLow = 0; + } + } +}; + + +/** + * Return a pointer to the shared service, creating it if necessary + * @param _ble an instance of BLEDevice + * @param _messageBus An instance of a MessageBus to interface with. + * @param _storage A persistent storage manager to use to hold non-volatile state. + * @return a pointer to the service, or NULL if it has not been created + */ +MicroBitUtilityService *MicroBitUtilityService::createShared( BLEDevice &_ble, EventModel &_messageBus, MicroBitStorage &_storage, MicroBitLog &_log) +{ + if ( !shared) + shared = new MicroBitUtilityService( _ble, _messageBus, _storage, _log); + return shared; +} + +/** + * Constructor. + * Create a representation of the Utility Service + * @param _ble The instance of a BLE device that we're running on. + * @param _messageBus An instance of a MessageBus to interface with. + * @param _storage A persistent storage manager to use to hold non-volatile state. + * @param _log An instance of a MicroBitLog to interface with. + */ +MicroBitUtilityService::MicroBitUtilityService( BLEDevice &_ble, EventModel &_messageBus, MicroBitStorage &_storage, MicroBitLog &_log) : + messageBus(_messageBus), storage(_storage), log(_log), workspace(NULL) +{ + // Initialise data + memclr( characteristicValue, sizeof( characteristicValue)); + + // Register the base UUID and create the service. + RegisterBaseUUID( bs_base_uuid); + CreateService( serviceUUID); + + CreateCharacteristic( mbbs_cIdxCTRL, charUUID[ mbbs_cIdxCTRL], + characteristicValue, + 0, sizeof(characteristicValue), + microbit_propWRITE | microbit_propWRITE_WITHOUT | microbit_propNOTIFY); + + if ( getConnected()) + listen(true); +} + + +/** + * Invoked when BLE connects. + * Start listening for events + */ +void MicroBitUtilityService::onConnect( const microbit_ble_evt_t *p_ble_evt) +{ + listen(true); +} + + +/** + * Invoked when BLE disconnects. + * Stop listening for events + */ +void MicroBitUtilityService::onDisconnect( const microbit_ble_evt_t *p_ble_evt) +{ + listen(false); +} + + +/** + * Callback. Invoked when any of our attributes are written via BLE. + * Record the request, and trigger processing. + * Requests are not queued. If a request is actively being processed, ignore the new request, otherwise simply override the previous one. + */ +void MicroBitUtilityService::onDataWritten(const microbit_ble_evt_write_t *params) +{ + if ( params->handle == valueHandle( mbbs_cIdxCTRL) && params->len > 0 && params->len < sizeof(request_t)) + { + if ( workspace) + { + if ( workspace->lock) + return; + } + else + { + workspace = new MicroBitUtilityWorkspace(); + if ( !workspace) + return; + } + + workspace->setRequest( params->data, params->len); + MicroBitEvent evt( MICROBIT_ID_UTILITY, MICROBIT_ID_UTILITY_PROCESS); + } +} + + +/** + * Callback. Invoked when a registered event occurs. + */ +void MicroBitUtilityService::onEvent(MicroBitEvent e) +{ + switch (e.source) + { + case MICROBIT_ID_UTILITY: + switch(e.value) + { + case MICROBIT_ID_UTILITY_PROCESS: + processRequest(); + break; + } + break; + } +} + + +/** + * Listen for or ignore events for processing requests + * @param yes true to listen, otherwise ignore + */ +void MicroBitUtilityService::listen( bool yes) +{ + if ( yes) + messageBus.listen( MICROBIT_ID_UTILITY, MICROBIT_EVT_ANY, this, &MicroBitUtilityService::onEvent); + else + messageBus.ignore( MICROBIT_ID_UTILITY, MICROBIT_EVT_ANY, this, &MicroBitUtilityService::onEvent); +} + + +/** + * Send a reply packet + * @param data Data to send + * @param length Length if the data + * @return DEVICE_OK if sent + */ +int MicroBitUtilityService::sendReply( const void *data, uint16_t length) +{ + bool ok = notifyChrValue( mbbs_cIdxCTRL, (const uint8_t *) data, length); + if ( !ok) + return DEVICE_BUSY; + workspace->onReplySent( data); + return DEVICE_OK; +} + + +/** + * Process the current request. This may block. + * @return DEVICE_OK if finished + */ +int MicroBitUtilityService::processRequest() +{ + int result = DEVICE_OK; + + if ( workspace && !workspace->lock && workspace->request.type != requestTypeNone) + { + // Block changes to the request while processing it + workspace->lock = true; + + switch ( workspace->request.type) + { + case requestTypeLogLength: + result = processLogLength(); + break; + case requestTypeLogRead: + result = processLogRead(); + break; + default: + break; + } + + // Check if finished processing the current request + if ( result == DEVICE_OK) + workspace->init(); + + workspace->lock = false; + } + + if ( workspace && workspace->request.type != requestTypeNone) + MicroBitEvent evt( MICROBIT_ID_UTILITY, MICROBIT_ID_UTILITY_PROCESS); + return result; +} + + +/** + * Process request typeLogLength + * @return DEVICE_OK if finished + */ +int MicroBitUtilityService::processLogLength() +{ + if ( workspace->replyState == replyStateClear) + { + requestLog_t *request = (requestLog_t *) &workspace->request; + uint32_t length = log.getDataLength( (DataFormat) request->format); + workspace->setReply( length); + } + return sendReply( &workspace->reply, offsetof(reply_t, data) + workspace->replyLength); +} + + +/** + * Process request typeLogRead + * @return DEVICE_OK if finished + */ +int MicroBitUtilityService::processLogRead() +{ + requestLogRead_t *request = (requestLogRead_t *) &workspace->request; + + while ( request->batchlen) + { + if ( workspace->replyState == replyStateClear) + { + int block = min( request->batchlen, sizeof( reply_t) - offsetof( reply_t, data)); + int result = log.readData(workspace->reply.data, request->index, block, (DataFormat) request->format, request->length); + if ( result) + workspace->setReplyError( result); + else + workspace->setReplyReady( block); + } + + int r = sendReply( &workspace->reply, offsetof(reply_t, data) + workspace->replyLength); + if ( r != DEVICE_OK) + { + return r; + } + + if ( workspace->replyState == replyStateError) + { + return DEVICE_OK; + } + + workspace->replyState = replyStateClear; + request->index += workspace->replyLength; + request->batchlen -= workspace->replyLength; + } + + return DEVICE_OK; +} + +#endif