mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 15:04:19 +00:00
[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
11 changed files with 206 additions and 26 deletions
|
@ -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
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
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,
|
||||
|
|
|
@ -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…
Reference in a new issue