[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
 | // 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); |     furi_assert(context); | ||||||
|     Bt* bt = 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) { |     if(bytes_processed != size) { | ||||||
|         FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", 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
 | // 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
 | // Called from GAP thread
 | ||||||
| static void bt_on_gap_event_callback(BleEvent event, void* context) { | static void bt_on_gap_event_callback(BleEvent event, void* context) { | ||||||
|     furi_assert(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"); |         FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection"); | ||||||
|         bt->rpc_session = rpc_session_open(bt->rpc); |         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_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); |         rpc_session_set_context(bt->rpc_session, bt); | ||||||
|         furi_hal_bt_set_data_event_callbacks( |         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
 |         // Update battery level
 | ||||||
|         PowerInfo info; |         PowerInfo info; | ||||||
|         power_get_info(bt->power, &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 { | struct RpcSession { | ||||||
|     RpcSendBytesCallback send_bytes_callback; |     RpcSendBytesCallback send_bytes_callback; | ||||||
|  |     RpcBufferIsEmptyCallback buffer_is_empty_callback; | ||||||
|     RpcSessionClosedCallback closed_callback; |     RpcSessionClosedCallback closed_callback; | ||||||
|     void* context; |     void* context; | ||||||
|     osMutexId_t callbacks_mutex; |     osMutexId_t callbacks_mutex; | ||||||
| @ -292,7 +293,7 @@ static Rpc* rpc_alloc(void) { | |||||||
|     rpc->busy_mutex = osMutexNew(NULL); |     rpc->busy_mutex = osMutexNew(NULL); | ||||||
|     rpc->busy = false; |     rpc->busy = false; | ||||||
|     rpc->events = osEventFlagsNew(NULL); |     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 = furi_alloc(sizeof(PB_Main)); | ||||||
|     rpc->decoded_message->cb_content.funcs.decode = content_callback; |     rpc->decoded_message->cb_content.funcs.decode = content_callback; | ||||||
| @ -365,6 +366,7 @@ static void rpc_free_session(RpcSession* session) { | |||||||
|     session->context = NULL; |     session->context = NULL; | ||||||
|     session->closed_callback = NULL; |     session->closed_callback = NULL; | ||||||
|     session->send_bytes_callback = NULL; |     session->send_bytes_callback = NULL; | ||||||
|  |     session->buffer_is_empty_callback = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void rpc_session_set_context(RpcSession* session, void* context) { | 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); |     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.
 | /* 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. |  * 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 |  * If bytes get into stream buffer before it's get epmtified and this | ||||||
| @ -415,6 +429,12 @@ size_t | |||||||
|     return bytes_sent; |     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) { | bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { | ||||||
|     Rpc* rpc = istream->state; |     Rpc* rpc = istream->state; | ||||||
|     uint32_t flags = 0; |     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) { |     while(1) { | ||||||
|         bytes_received += |         bytes_received += | ||||||
|             xStreamBufferReceive(rpc->stream, buf + bytes_received, count - bytes_received, 0); |             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) { |         if(count == bytes_received) { | ||||||
|             break; |             break; | ||||||
|         } else { |         } 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 <stdbool.h> | ||||||
| #include "cmsis_os.h" | #include "cmsis_os.h" | ||||||
| 
 | 
 | ||||||
|  | #define RPC_BUFFER_SIZE (1024) | ||||||
|  | 
 | ||||||
| /** Rpc interface. Used for opening session only. */ | /** Rpc interface. Used for opening session only. */ | ||||||
| typedef struct Rpc Rpc; | typedef struct Rpc Rpc; | ||||||
| /** Rpc session interface */ | /** Rpc session interface */ | ||||||
| @ -11,6 +13,8 @@ typedef struct RpcSession RpcSession; | |||||||
| 
 | 
 | ||||||
| /** Callback to send to client any data (e.g. response to command) */ | /** Callback to send to client any data (e.g. response to command) */ | ||||||
| typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes_len); | 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
 | /** Callback to notify transport layer that close_session command
 | ||||||
|  * is received. Any other actions lays on transport layer. |  * is received. Any other actions lays on transport layer. | ||||||
|  * No destruction or session close preformed. */ |  * 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); | 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
 | /** Set callback to be called when RPC command to close session is received
 | ||||||
|  *  WARN: It's forbidden to call RPC API within RpcSessionClosedCallback |  *  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 |  * @return              actually consumed bytes | ||||||
|  */ |  */ | ||||||
| size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, TickType_t timeout); | 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 | #ifndef TOSTRING | ||||||
| #define TOSTRING(x) STRINGIFY(x) | #define TOSTRING(x) STRINGIFY(x) | ||||||
| #endif | #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 svc_handle; | ||||||
|     uint16_t rx_char_handle; |     uint16_t rx_char_handle; | ||||||
|     uint16_t tx_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; |     SerialSvcDataReceivedCallback on_received_cb; | ||||||
|     SerialSvcDataSentCallback on_sent_cb; |     SerialSvcDataSentCallback on_sent_cb; | ||||||
|     void* context; |     void* context; | ||||||
| } SerialSvc; | } 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 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_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) { | static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | ||||||
|     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; |     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) { |             } 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); |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||||
|                 if(serial_svc->on_received_cb) { |                 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; |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|             } |             } | ||||||
| @ -58,7 +73,7 @@ void serial_svc_start() { | |||||||
|     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); |     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||||
| 
 | 
 | ||||||
|     // Add service
 |     // 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) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||||
|     } |     } | ||||||
| @ -88,12 +103,45 @@ void serial_svc_start() { | |||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", 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_received_cb = on_received_cb; | ||||||
|     serial_svc->on_sent_cb = on_sent_cb; |     serial_svc->on_sent_cb = on_sent_cb; | ||||||
|     serial_svc->context = context; |     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() { | void serial_svc_stop() { | ||||||
| @ -108,11 +156,17 @@ void serial_svc_stop() { | |||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", 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
 |         // Delete service
 | ||||||
|         status = aci_gatt_del_service(serial_svc->svc_handle); |         status = aci_gatt_del_service(serial_svc->svc_handle); | ||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", 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); |         free(serial_svc); | ||||||
|         serial_svc = NULL; |         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) { |     if(data_len > SERIAL_SVC_DATA_LEN_MAX) { | ||||||
|         return false; |         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, |     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||||
|                                         serial_svc->tx_char_handle, |                                         serial_svc->tx_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|  | |||||||
| @ -9,12 +9,14 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #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); | typedef void(*SerialSvcDataSentCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| void serial_svc_start(); | 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(); | 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) { | 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(on_received_cb, on_sent_cb, 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) { | bool furi_hal_bt_tx(uint8_t* data, uint16_t size) { | ||||||
|  | |||||||
| @ -10,16 +10,21 @@ typedef struct { | |||||||
|     uint16_t svc_handle; |     uint16_t svc_handle; | ||||||
|     uint16_t rx_char_handle; |     uint16_t rx_char_handle; | ||||||
|     uint16_t tx_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; |     SerialSvcDataReceivedCallback on_received_cb; | ||||||
|     SerialSvcDataSentCallback on_sent_cb; |     SerialSvcDataSentCallback on_sent_cb; | ||||||
|     void* context; |     void* context; | ||||||
| } SerialSvc; | } 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 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_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) { | static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | ||||||
|     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; |     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) { |             } 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); |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||||
|                 if(serial_svc->on_received_cb) { |                 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; |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|             } |             } | ||||||
| @ -58,7 +73,7 @@ void serial_svc_start() { | |||||||
|     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); |     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||||
| 
 | 
 | ||||||
|     // Add service
 |     // 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) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||||
|     } |     } | ||||||
| @ -88,12 +103,45 @@ void serial_svc_start() { | |||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", 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_received_cb = on_received_cb; | ||||||
|     serial_svc->on_sent_cb = on_sent_cb; |     serial_svc->on_sent_cb = on_sent_cb; | ||||||
|     serial_svc->context = context; |     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() { | void serial_svc_stop() { | ||||||
| @ -108,11 +156,17 @@ void serial_svc_stop() { | |||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", 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
 |         // Delete service
 | ||||||
|         status = aci_gatt_del_service(serial_svc->svc_handle); |         status = aci_gatt_del_service(serial_svc->svc_handle); | ||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", 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); |         free(serial_svc); | ||||||
|         serial_svc = NULL; |         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) { |     if(data_len > SERIAL_SVC_DATA_LEN_MAX) { | ||||||
|         return false; |         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, |     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||||
|                                         serial_svc->tx_char_handle, |                                         serial_svc->tx_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|  | |||||||
| @ -9,12 +9,14 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #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); | typedef void(*SerialSvcDataSentCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| void serial_svc_start(); | 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(); | 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) { | 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(on_received_cb, on_sent_cb, 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) { | 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 on_sent_cb - SerialSvcDataSentCallback instance | ||||||
|  * @param context - pointer to context |  * @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
 | /** Send data through BLE
 | ||||||
|  * @param data - data buffer |  * @param data - data buffer | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich