[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:
gornekich 2021-11-08 22:41:40 +03:00 committed by GitHub
parent 4e9e9f32d7
commit 54dc16134d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 206 additions and 26 deletions

View file

@ -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
View 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
View 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
View file

@ -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

View file

@ -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,

View file

@ -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();

View file

@ -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) {

View file

@ -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,

View file

@ -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();

View file

@ -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) {

View file

@ -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