[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
This commit is contained in:
		
							parent
							
								
									4e9e9f32d7
								
							
						
					
					
						commit
						54dc16134d
					
				| @ -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); | ||||
|  | ||||
							
								
								
									
										27
									
								
								applications/rpc/rpc.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										27
									
								
								applications/rpc/rpc.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -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 { | ||||
|  | ||||
							
								
								
									
										21
									
								
								applications/rpc/rpc.h
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										21
									
								
								applications/rpc/rpc.h
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -4,6 +4,8 @@ | ||||
| #include <stdbool.h> | ||||
| #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); | ||||
|  | ||||
							
								
								
									
										6
									
								
								core/furi/common_defines.h
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										6
									
								
								core/furi/common_defines.h
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -65,3 +65,9 @@ | ||||
| #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 | ||||
| @ -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) { | ||||
|                     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); | ||||
|     } | ||||
| @ -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, | ||||
|  | ||||
| @ -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(); | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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) { | ||||
|                     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); | ||||
|     } | ||||
| @ -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, | ||||
|  | ||||
| @ -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(); | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich