From 54dc16134dc8ea9139f88e991a69f648aac302d6 Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 8 Nov 2021 22:41:40 +0300 Subject: [PATCH] [FL-1922] BLE buffer overflow (#789) * rpc: increase RPC buffer size. Add get available buffer size API * bt: add flow control characteristic to serial service * ble: change updating flow control characteristic logic * rpc: add buffer is empty callback * bt: add notification about empty RPC buffer * ble: add more debug info * serial_service: add mutex guarding available buffer size * ble: remove debug logs in serial service --- applications/bt/bt_service/bt.c | 11 ++- applications/rpc/rpc.c | 27 +++++++- applications/rpc/rpc.h | 21 ++++++ core/furi/common_defines.h | 6 ++ firmware/targets/f6/ble-glue/serial_service.c | 67 +++++++++++++++++-- firmware/targets/f6/ble-glue/serial_service.h | 6 +- firmware/targets/f6/furi-hal/furi-hal-bt.c | 8 ++- firmware/targets/f7/ble-glue/serial_service.c | 67 +++++++++++++++++-- firmware/targets/f7/ble-glue/serial_service.h | 6 +- firmware/targets/f7/furi-hal/furi-hal-bt.c | 8 ++- .../targets/furi-hal-include/furi-hal-bt.h | 5 +- 11 files changed, 206 insertions(+), 26 deletions(-) mode change 100644 => 100755 applications/rpc/rpc.c mode change 100644 => 100755 applications/rpc/rpc.h mode change 100644 => 100755 core/furi/common_defines.h diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index aa999e36..3336ab9f 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -81,7 +81,7 @@ Bt* bt_alloc() { } // Called from GAP thread from Serial service -static void bt_on_data_received_callback(uint8_t* data, uint16_t size, void* context) { +static uint16_t bt_on_data_received_callback(uint8_t* data, uint16_t size, void* context) { furi_assert(context); Bt* bt = context; @@ -89,6 +89,7 @@ static void bt_on_data_received_callback(uint8_t* data, uint16_t size, void* con if(bytes_processed != size) { FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size); } + return rpc_session_get_available_size(bt->rpc_session); } // Called from GAP thread from Serial service @@ -118,6 +119,11 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt } } +static void bt_rpc_buffer_is_empty_callback(void* context) { + furi_assert(context); + furi_hal_bt_notify_buffer_is_empty(); +} + // Called from GAP thread static void bt_on_gap_event_callback(BleEvent event, void* context) { furi_assert(context); @@ -132,9 +138,10 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) { FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection"); bt->rpc_session = rpc_session_open(bt->rpc); rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); + rpc_session_set_buffer_is_empty_callback(bt->rpc_session, bt_rpc_buffer_is_empty_callback); rpc_session_set_context(bt->rpc_session, bt); furi_hal_bt_set_data_event_callbacks( - bt_on_data_received_callback, bt_on_data_sent_callback, bt); + RPC_BUFFER_SIZE, bt_on_data_received_callback, bt_on_data_sent_callback, bt); // Update battery level PowerInfo info; power_get_info(bt->power, &info); diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c old mode 100644 new mode 100755 index 80355e5e..3d4243c0 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -53,6 +53,7 @@ static RpcSystemCallbacks rpc_systems[] = { struct RpcSession { RpcSendBytesCallback send_bytes_callback; + RpcBufferIsEmptyCallback buffer_is_empty_callback; RpcSessionClosedCallback closed_callback; void* context; osMutexId_t callbacks_mutex; @@ -292,7 +293,7 @@ static Rpc* rpc_alloc(void) { rpc->busy_mutex = osMutexNew(NULL); rpc->busy = false; rpc->events = osEventFlagsNew(NULL); - rpc->stream = xStreamBufferCreate(256, 1); + rpc->stream = xStreamBufferCreate(RPC_BUFFER_SIZE, 1); rpc->decoded_message = furi_alloc(sizeof(PB_Main)); rpc->decoded_message->cb_content.funcs.decode = content_callback; @@ -365,6 +366,7 @@ static void rpc_free_session(RpcSession* session) { session->context = NULL; session->closed_callback = NULL; session->send_bytes_callback = NULL; + session->buffer_is_empty_callback = NULL; } void rpc_session_set_context(RpcSession* session, void* context) { @@ -397,6 +399,18 @@ void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallba osMutexRelease(session->callbacks_mutex); } +void rpc_session_set_buffer_is_empty_callback( + RpcSession* session, + RpcBufferIsEmptyCallback callback) { + furi_assert(session); + furi_assert(callback); + furi_assert(session->rpc->busy); + + osMutexAcquire(session->callbacks_mutex, osWaitForever); + session->buffer_is_empty_callback = callback; + osMutexRelease(session->callbacks_mutex); +} + /* Doesn't forbid using rpc_feed_bytes() after session close - it's safe. * Because any bytes received in buffer will be flushed before next session. * If bytes get into stream buffer before it's get epmtified and this @@ -415,6 +429,12 @@ size_t return bytes_sent; } +size_t rpc_session_get_available_size(RpcSession* session) { + furi_assert(session); + Rpc* rpc = session->rpc; + return xStreamBufferSpacesAvailable(rpc->stream); +} + bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { Rpc* rpc = istream->state; uint32_t flags = 0; @@ -425,6 +445,11 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { while(1) { bytes_received += xStreamBufferReceive(rpc->stream, buf + bytes_received, count - bytes_received, 0); + if(xStreamBufferIsEmpty(rpc->stream)) { + if(rpc->session.buffer_is_empty_callback) { + rpc->session.buffer_is_empty_callback(rpc->session.context); + } + } if(count == bytes_received) { break; } else { diff --git a/applications/rpc/rpc.h b/applications/rpc/rpc.h old mode 100644 new mode 100755 index 29e37773..3e58a885 --- a/applications/rpc/rpc.h +++ b/applications/rpc/rpc.h @@ -4,6 +4,8 @@ #include #include "cmsis_os.h" +#define RPC_BUFFER_SIZE (1024) + /** Rpc interface. Used for opening session only. */ typedef struct Rpc Rpc; /** Rpc session interface */ @@ -11,6 +13,8 @@ typedef struct RpcSession RpcSession; /** Callback to send to client any data (e.g. response to command) */ typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes_len); +/** Callback to notify client that buffer is empty */ +typedef void (*RpcBufferIsEmptyCallback)(void* context); /** Callback to notify transport layer that close_session command * is received. Any other actions lays on transport layer. * No destruction or session close preformed. */ @@ -59,6 +63,15 @@ void rpc_session_set_context(RpcSession* session, void* context); */ void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback); +/** Set callback to notify that buffer is empty + * + * @param session pointer to RpcSession descriptor + * @param callback callback to notify client that buffer is empty (can be NULL) + */ +void rpc_session_set_buffer_is_empty_callback( + RpcSession* session, + RpcBufferIsEmptyCallback callback); + /** Set callback to be called when RPC command to close session is received * WARN: It's forbidden to call RPC API within RpcSessionClosedCallback * @@ -77,3 +90,11 @@ void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallbac * @return actually consumed bytes */ size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, TickType_t timeout); + +/** Get available size of RPC buffer + * + * @param session pointer to RpcSession descriptor + * + * @return bytes available in buffer + */ +size_t rpc_session_get_available_size(RpcSession* session); diff --git a/core/furi/common_defines.h b/core/furi/common_defines.h old mode 100644 new mode 100755 index bb4aadfa..00a68529 --- a/core/furi/common_defines.h +++ b/core/furi/common_defines.h @@ -64,4 +64,10 @@ #ifndef TOSTRING #define TOSTRING(x) STRINGIFY(x) +#endif + +#ifndef REVERSE_BYTES_U32 +#define REVERSE_BYTES_U32(x) \ + ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ + (((x)&0xFF000000) >> 24)) #endif \ No newline at end of file diff --git a/firmware/targets/f6/ble-glue/serial_service.c b/firmware/targets/f6/ble-glue/serial_service.c index 7de3529c..43b07718 100644 --- a/firmware/targets/f6/ble-glue/serial_service.c +++ b/firmware/targets/f6/ble-glue/serial_service.c @@ -10,16 +10,21 @@ typedef struct { uint16_t svc_handle; uint16_t rx_char_handle; uint16_t tx_char_handle; + uint16_t flow_ctrl_char_handle; + osMutexId_t buff_size_mtx; + uint32_t buff_size; + uint16_t bytes_ready_to_receive; SerialSvcDataReceivedCallback on_received_cb; SerialSvcDataSentCallback on_sent_cb; void* context; } SerialSvc; -static SerialSvc* serial_svc; +static SerialSvc* serial_svc = NULL; static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; -static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t flow_ctrl_uuid[] = {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; @@ -36,7 +41,17 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); if(serial_svc->on_received_cb) { - serial_svc->on_received_cb(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length, serial_svc->context); + furi_check(osMutexAcquire(serial_svc->buff_size_mtx, osWaitForever) == osOK); + if(attribute_modified->Attr_Data_Length > serial_svc->bytes_ready_to_receive) { + FURI_LOG_W( + SERIAL_SERVICE_TAG, "Received %d, while was ready to receive %d bytes. Can lead to buffer overflow!", + attribute_modified->Attr_Data_Length, serial_svc->bytes_ready_to_receive); + } + serial_svc->bytes_ready_to_receive -= MIN(serial_svc->bytes_ready_to_receive, attribute_modified->Attr_Data_Length); + uint32_t buff_free_size = + serial_svc->on_received_cb(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length, serial_svc->context); + FURI_LOG_D(SERIAL_SERVICE_TAG, "Available buff size: %d", buff_free_size); + furi_check(osMutexRelease(serial_svc->buff_size_mtx) == osOK); } ret = SVCCTL_EvtAckFlowEnable; } @@ -58,7 +73,7 @@ void serial_svc_start() { SVCCTL_RegisterSvcHandler(serial_svc_event_handler); // Add service - status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle); + status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 10, &serial_svc->svc_handle); if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); } @@ -78,7 +93,7 @@ void serial_svc_start() { // Add TX characteristic status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid, - SERIAL_SVC_DATA_LEN_MAX, + SERIAL_SVC_DATA_LEN_MAX, CHAR_PROP_READ | CHAR_PROP_INDICATE, ATTR_PERMISSION_AUTHEN_READ, GATT_DONT_NOTIFY_EVENTS, @@ -88,12 +103,45 @@ void serial_svc_start() { if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); } + // Add Flow Control characteristic + status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)flow_ctrl_uuid, + sizeof(uint32_t), + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Flow Control characteristic: %d", status); + } + // Allocate buffer size mutex + serial_svc->buff_size_mtx = osMutexNew(NULL); } -void serial_svc_set_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { +void serial_svc_set_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { + furi_assert(serial_svc); serial_svc->on_received_cb = on_received_cb; serial_svc->on_sent_cb = on_sent_cb; serial_svc->context = context; + serial_svc->buff_size = buff_size; + serial_svc->bytes_ready_to_receive = buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); + aci_gatt_update_char_value(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle, 0, sizeof(uint32_t), (uint8_t*)&buff_size_reversed); +} + +void serial_svc_notify_buffer_is_empty() { + furi_assert(serial_svc); + furi_assert(serial_svc->buff_size_mtx); + + furi_check(osMutexAcquire(serial_svc->buff_size_mtx, osWaitForever) == osOK); + if(serial_svc->bytes_ready_to_receive == 0) { + FURI_LOG_D(SERIAL_SERVICE_TAG, "Buffer is empty. Notifying client"); + serial_svc->bytes_ready_to_receive = serial_svc->buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); + aci_gatt_update_char_value(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle, 0, sizeof(uint32_t), (uint8_t*)&buff_size_reversed); + } + furi_check(osMutexRelease(serial_svc->buff_size_mtx) == osOK); } void serial_svc_stop() { @@ -108,11 +156,17 @@ void serial_svc_stop() { if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status); } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Flow Control characteristic: %d", status); + } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status); } + // Delete buffer size mutex + osMutexDelete(serial_svc->buff_size_mtx); free(serial_svc); serial_svc = NULL; } @@ -122,7 +176,6 @@ bool serial_svc_update_tx(uint8_t* data, uint8_t data_len) { if(data_len > SERIAL_SVC_DATA_LEN_MAX) { return false; } - FURI_LOG_D(SERIAL_SERVICE_TAG, "Updating char %d len", data_len); tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, serial_svc->tx_char_handle, 0, diff --git a/firmware/targets/f6/ble-glue/serial_service.h b/firmware/targets/f6/ble-glue/serial_service.h index b5a1c078..0aa4c79f 100644 --- a/firmware/targets/f6/ble-glue/serial_service.h +++ b/firmware/targets/f6/ble-glue/serial_service.h @@ -9,12 +9,14 @@ extern "C" { #endif -typedef void(*SerialSvcDataReceivedCallback)(uint8_t* buff, uint16_t size, void* context); +typedef uint16_t(*SerialSvcDataReceivedCallback)(uint8_t* buff, uint16_t size, void* context); typedef void(*SerialSvcDataSentCallback)(void* context); void serial_svc_start(); -void serial_svc_set_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context); +void serial_svc_set_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context); + +void serial_svc_notify_buffer_is_empty(); void serial_svc_stop(); diff --git a/firmware/targets/f6/furi-hal/furi-hal-bt.c b/firmware/targets/f6/furi-hal/furi-hal-bt.c index a018085c..5b68cdd0 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-bt.c +++ b/firmware/targets/f6/furi-hal/furi-hal-bt.c @@ -59,8 +59,12 @@ void furi_hal_bt_stop_advertising() { } } -void furi_hal_bt_set_data_event_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { - serial_svc_set_callbacks(on_received_cb, on_sent_cb, context); +void furi_hal_bt_set_data_event_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { + serial_svc_set_callbacks(buff_size, on_received_cb, on_sent_cb, context); +} + +void furi_hal_bt_notify_buffer_is_empty() { + serial_svc_notify_buffer_is_empty(); } bool furi_hal_bt_tx(uint8_t* data, uint16_t size) { diff --git a/firmware/targets/f7/ble-glue/serial_service.c b/firmware/targets/f7/ble-glue/serial_service.c index 7de3529c..43b07718 100644 --- a/firmware/targets/f7/ble-glue/serial_service.c +++ b/firmware/targets/f7/ble-glue/serial_service.c @@ -10,16 +10,21 @@ typedef struct { uint16_t svc_handle; uint16_t rx_char_handle; uint16_t tx_char_handle; + uint16_t flow_ctrl_char_handle; + osMutexId_t buff_size_mtx; + uint32_t buff_size; + uint16_t bytes_ready_to_receive; SerialSvcDataReceivedCallback on_received_cb; SerialSvcDataSentCallback on_sent_cb; void* context; } SerialSvc; -static SerialSvc* serial_svc; +static SerialSvc* serial_svc = NULL; static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; -static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t flow_ctrl_uuid[] = {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; @@ -36,7 +41,17 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); if(serial_svc->on_received_cb) { - serial_svc->on_received_cb(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length, serial_svc->context); + furi_check(osMutexAcquire(serial_svc->buff_size_mtx, osWaitForever) == osOK); + if(attribute_modified->Attr_Data_Length > serial_svc->bytes_ready_to_receive) { + FURI_LOG_W( + SERIAL_SERVICE_TAG, "Received %d, while was ready to receive %d bytes. Can lead to buffer overflow!", + attribute_modified->Attr_Data_Length, serial_svc->bytes_ready_to_receive); + } + serial_svc->bytes_ready_to_receive -= MIN(serial_svc->bytes_ready_to_receive, attribute_modified->Attr_Data_Length); + uint32_t buff_free_size = + serial_svc->on_received_cb(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length, serial_svc->context); + FURI_LOG_D(SERIAL_SERVICE_TAG, "Available buff size: %d", buff_free_size); + furi_check(osMutexRelease(serial_svc->buff_size_mtx) == osOK); } ret = SVCCTL_EvtAckFlowEnable; } @@ -58,7 +73,7 @@ void serial_svc_start() { SVCCTL_RegisterSvcHandler(serial_svc_event_handler); // Add service - status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle); + status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 10, &serial_svc->svc_handle); if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); } @@ -78,7 +93,7 @@ void serial_svc_start() { // Add TX characteristic status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid, - SERIAL_SVC_DATA_LEN_MAX, + SERIAL_SVC_DATA_LEN_MAX, CHAR_PROP_READ | CHAR_PROP_INDICATE, ATTR_PERMISSION_AUTHEN_READ, GATT_DONT_NOTIFY_EVENTS, @@ -88,12 +103,45 @@ void serial_svc_start() { if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); } + // Add Flow Control characteristic + status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)flow_ctrl_uuid, + sizeof(uint32_t), + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Flow Control characteristic: %d", status); + } + // Allocate buffer size mutex + serial_svc->buff_size_mtx = osMutexNew(NULL); } -void serial_svc_set_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { +void serial_svc_set_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { + furi_assert(serial_svc); serial_svc->on_received_cb = on_received_cb; serial_svc->on_sent_cb = on_sent_cb; serial_svc->context = context; + serial_svc->buff_size = buff_size; + serial_svc->bytes_ready_to_receive = buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); + aci_gatt_update_char_value(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle, 0, sizeof(uint32_t), (uint8_t*)&buff_size_reversed); +} + +void serial_svc_notify_buffer_is_empty() { + furi_assert(serial_svc); + furi_assert(serial_svc->buff_size_mtx); + + furi_check(osMutexAcquire(serial_svc->buff_size_mtx, osWaitForever) == osOK); + if(serial_svc->bytes_ready_to_receive == 0) { + FURI_LOG_D(SERIAL_SERVICE_TAG, "Buffer is empty. Notifying client"); + serial_svc->bytes_ready_to_receive = serial_svc->buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); + aci_gatt_update_char_value(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle, 0, sizeof(uint32_t), (uint8_t*)&buff_size_reversed); + } + furi_check(osMutexRelease(serial_svc->buff_size_mtx) == osOK); } void serial_svc_stop() { @@ -108,11 +156,17 @@ void serial_svc_stop() { if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status); } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Flow Control characteristic: %d", status); + } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); if(status) { FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status); } + // Delete buffer size mutex + osMutexDelete(serial_svc->buff_size_mtx); free(serial_svc); serial_svc = NULL; } @@ -122,7 +176,6 @@ bool serial_svc_update_tx(uint8_t* data, uint8_t data_len) { if(data_len > SERIAL_SVC_DATA_LEN_MAX) { return false; } - FURI_LOG_D(SERIAL_SERVICE_TAG, "Updating char %d len", data_len); tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, serial_svc->tx_char_handle, 0, diff --git a/firmware/targets/f7/ble-glue/serial_service.h b/firmware/targets/f7/ble-glue/serial_service.h index b5a1c078..0aa4c79f 100644 --- a/firmware/targets/f7/ble-glue/serial_service.h +++ b/firmware/targets/f7/ble-glue/serial_service.h @@ -9,12 +9,14 @@ extern "C" { #endif -typedef void(*SerialSvcDataReceivedCallback)(uint8_t* buff, uint16_t size, void* context); +typedef uint16_t(*SerialSvcDataReceivedCallback)(uint8_t* buff, uint16_t size, void* context); typedef void(*SerialSvcDataSentCallback)(void* context); void serial_svc_start(); -void serial_svc_set_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context); +void serial_svc_set_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context); + +void serial_svc_notify_buffer_is_empty(); void serial_svc_stop(); diff --git a/firmware/targets/f7/furi-hal/furi-hal-bt.c b/firmware/targets/f7/furi-hal/furi-hal-bt.c index a018085c..5b68cdd0 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-bt.c +++ b/firmware/targets/f7/furi-hal/furi-hal-bt.c @@ -59,8 +59,12 @@ void furi_hal_bt_stop_advertising() { } } -void furi_hal_bt_set_data_event_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { - serial_svc_set_callbacks(on_received_cb, on_sent_cb, context); +void furi_hal_bt_set_data_event_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context) { + serial_svc_set_callbacks(buff_size, on_received_cb, on_sent_cb, context); +} + +void furi_hal_bt_notify_buffer_is_empty() { + serial_svc_notify_buffer_is_empty(); } bool furi_hal_bt_tx(uint8_t* data, uint16_t size) { diff --git a/firmware/targets/furi-hal-include/furi-hal-bt.h b/firmware/targets/furi-hal-include/furi-hal-bt.h index 461fc4ce..089ca9ae 100644 --- a/firmware/targets/furi-hal-include/furi-hal-bt.h +++ b/firmware/targets/furi-hal-include/furi-hal-bt.h @@ -92,7 +92,10 @@ void furi_hal_bt_set_key_storage_change_callback(BleGlueKeyStorageChangedCallbac * @param on_sent_cb - SerialSvcDataSentCallback instance * @param context - pointer to context */ -void furi_hal_bt_set_data_event_callbacks(SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context); +void furi_hal_bt_set_data_event_callbacks(uint16_t buff_size, SerialSvcDataReceivedCallback on_received_cb, SerialSvcDataSentCallback on_sent_cb, void* context); + +/** Notify that buffer is empty */ +void furi_hal_bt_notify_buffer_is_empty(); /** Send data through BLE * @param data - data buffer