Files
microchip-application-bmd38…/le_elite_srv.c
T

341 lines
11 KiB
C

#ifdef __cplusplus
extern "C"
{
#endif
#include <string.h>
#include "app_config.h"
#include "app_error.h"
#include "ble_srv_common.h"
#include "nrf_log.h"
#include "nrf_sdh_ble.h"
#include "FreeRTOS.h"
#include "message_buffer.h"
#include "task.h"
#include "elite.h"
#ifdef __cplusplus
}
#endif
#if NRF_MODULE_ENABLED(BLE_ELITE_SRV)
#define BLE_UUID_ELITE_SERVICE 0xFFF0
#define BLE_UUID_ELITE_DATA_CHAR 0xFFF1
#define BLE_UUID_ELITE_INST_CHAR 0xFFF2
#define BLE_UUID_ELITE_EVENT_CHAR 0xFFF3
#define BLE_UUID_ELITE_BEGIN_CHAR BLE_UUID_ELITE_DATA_CHAR
#define BLE_UUID_ELITE_END_CHAR (BLE_UUID_ELITE_EVENT_CHAR + 1)
typedef struct
{
uint16_t conn_handle;
uint16_t service_handle;
ble_gatts_char_handles_t data_char_handle;
ble_gatts_char_handles_t inst_char_handle;
ble_gatts_char_handles_t event_char_handle;
bool data_notify_enable;
bool inst_notify_enable;
bool event_notify_enable;
} elite_context_t;
static elite_context_t elite_context = {
.conn_handle = BLE_CONN_HANDLE_INVALID,
.data_notify_enable = false,
.inst_notify_enable = false,
.event_notify_enable = false,
};
static MessageBufferHandle_t async_notify_msg = NULL;
static void on_connect(elite_context_t *p_context, ble_evt_t const *p_ble_evt)
{
taskENTER_CRITICAL();
p_context->conn_handle = p_ble_evt->evt.common_evt.conn_handle;
taskEXIT_CRITICAL();
}
static void on_disconnect(elite_context_t *p_context, ble_evt_t const *p_ble_evt)
{
taskENTER_CRITICAL();
p_context->conn_handle = BLE_CONN_HANDLE_INVALID;
p_context->data_notify_enable = false;
p_context->inst_notify_enable = false;
p_context->event_notify_enable = false;
taskEXIT_CRITICAL();
}
static void elite_srv_ccc_update(elite_context_t *p_context, ble_evt_t const *p_ble_evt)
{
ble_gatts_evt_write_t const *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_context->data_char_handle.cccd_handle)
{
p_context->data_notify_enable = ble_srv_is_notification_enabled(p_evt_write->data);
NRF_LOG_INFO("data_notify:%s", p_context->data_notify_enable ? "enable" : "disable");
return;
}
if (p_evt_write->handle == p_context->inst_char_handle.cccd_handle)
{
p_context->inst_notify_enable = ble_srv_is_notification_enabled(p_evt_write->data);
NRF_LOG_INFO("inst_notify_notify:%s", p_context->inst_notify_enable ? "enable" : "disable");
return;
}
if (p_evt_write->handle == p_context->event_char_handle.cccd_handle)
{
p_context->event_notify_enable = ble_srv_is_notification_enabled(p_evt_write->data);
NRF_LOG_INFO("event_notify:%s", p_context->event_notify_enable ? "enable" : "disable");
return;
}
}
static void on_write(elite_context_t *p_context, ble_evt_t const *p_ble_evt)
{
ble_uuid_t uuid = p_ble_evt->evt.gatts_evt.params.write.uuid;
uint16_t handle = p_ble_evt->evt.gatts_evt.params.write.handle;
ble_gatts_evt_write_t const *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
switch (uuid.uuid)
{
case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG:
elite_srv_ccc_update(p_context, p_ble_evt);
break;
case BLE_UUID_ELITE_INST_CHAR:
NRF_LOG_INFO("");
NRF_LOG_HEXDUMP_INFO(p_evt_write->data, p_evt_write->len);
elite_instr_send((void *)p_evt_write->data, p_evt_write->len);
break;
default:
break;
}
}
static void on_hvx_tx_complete(elite_context_t *p_context, ble_evt_t const *p_ble_evt)
{
}
static void on_ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context)
{
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_context, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_context, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_context, p_ble_evt);
break;
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
on_hvx_tx_complete(p_context, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
static ret_code_t add_characteristic(uint16_t uuid, uint8_t uuid_type, uint32_t val_max_len, void *p_init_val, uint32_t init_len, ble_gatts_char_handles_t *p_handles, bool enable_cccd)
{
ble_add_char_params_t add_char_params;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = uuid;
add_char_params.uuid_type = uuid_type;
add_char_params.max_len = val_max_len;
add_char_params.init_len = init_len;
add_char_params.p_init_value = p_init_val;
add_char_params.is_value_user = false;
add_char_params.is_var_len = true;
add_char_params.write_access = SEC_OPEN;
add_char_params.char_props.write = 1;
add_char_params.read_access = SEC_OPEN;
add_char_params.char_props.read = 1;
add_char_params.cccd_write_access = SEC_OPEN;
add_char_params.char_props.notify = enable_cccd;
ret_code_t err_code = characteristic_add(elite_context.service_handle, &add_char_params, p_handles);
return err_code;
}
static ret_code_t add_characteristic_ro(uint16_t uuid, uint8_t uuid_type, uint32_t val_max_len, void *p_init_val, uint32_t init_len, ble_gatts_char_handles_t *p_handles, bool enable_cccd)
{
ble_add_char_params_t add_char_params;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = uuid;
add_char_params.uuid_type = uuid_type;
add_char_params.max_len = val_max_len;
add_char_params.init_len = init_len;
add_char_params.p_init_value = p_init_val;
add_char_params.is_value_user = true;
add_char_params.is_var_len = true;
add_char_params.read_access = SEC_OPEN;
add_char_params.char_props.read = 1;
add_char_params.cccd_write_access = SEC_OPEN;
add_char_params.char_props.notify = enable_cccd;
ret_code_t err_code = characteristic_add(elite_context.service_handle, &add_char_params, p_handles);
return err_code;
}
static uint32_t elite_srv_init(void)
{
ret_code_t err_code;
ble_uuid_t ble_uuid = {
.type = BLE_UUID_TYPE_BLE,
.uuid = BLE_UUID_ELITE_SERVICE,
};
// Adding proprietary service to the SoftDevice
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &elite_context.service_handle);
APP_ERROR_CHECK(err_code);
uint8_t init_val[NRF_SDH_BLE_GATT_MAX_MTU_SIZE - 3];
memset(init_val, 0x00, sizeof(init_val));
// Adding data characteristic to the SoftDevice
err_code = add_characteristic_ro(BLE_UUID_ELITE_DATA_CHAR, ble_uuid.type, sizeof(init_val), init_val, sizeof(init_val), &elite_context.data_char_handle, true);
APP_ERROR_CHECK(err_code);
// Adding instruction characteristic to the SoftDevice
err_code = add_characteristic(BLE_UUID_ELITE_INST_CHAR, ble_uuid.type, sizeof(init_val), init_val, sizeof(init_val), &elite_context.inst_char_handle, true);
APP_ERROR_CHECK(err_code);
// Adding event characteristic to the SoftDevice
err_code = add_characteristic_ro(BLE_UUID_ELITE_EVENT_CHAR, ble_uuid.type, sizeof(init_val), init_val, sizeof(init_val), &elite_context.event_char_handle, true);
APP_ERROR_CHECK(err_code);
// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_elite_observer, BLE_ELITE_OBSERVER_PRIO, on_ble_evt_handler, &elite_context);
return NRF_SUCCESS;
}
static void le_elite_srv_async_notify_task(void *p_arg)
{
static uint8_t buf[256];
for (;;)
{
uint32_t size = xMessageBufferReceive(async_notify_msg, buf, sizeof(buf), portMAX_DELAY);
if (size)
{
for (uint32_t i = 0; i < 10; i++)
{
ret_code_t ret = le_event_notify(buf, size);
if (ret == NRF_ERROR_BUSY)
{
vTaskDelay(pdMS_TO_TICKS(5));
}
else
{
break;
}
}
}
}
}
void le_elite_srv_init(void)
{
async_notify_msg = xMessageBufferCreate(10 * 1024);
if (xTaskCreate(le_elite_srv_async_notify_task, "async_notify", 512, NULL, 4, NULL) == pdFALSE)
{
APP_ERROR_CHECK(NRF_ERROR_RESOURCES);
}
ret_code_t err_code = elite_srv_init();
APP_ERROR_CHECK(err_code);
elite_init();
}
ret_code_t le_data_notify(uint8_t *p_value, uint16_t len)
{
ble_gatts_hvx_params_t hvx_params = {
.handle = elite_context.data_char_handle.value_handle,
.type = BLE_GATT_HVX_NOTIFICATION,
.offset = 0,
.p_len = &len,
.p_data = p_value,
};
return sd_ble_gatts_hvx(elite_context.conn_handle, &hvx_params);
}
ret_code_t le_data_update(uint8_t *p_value, uint16_t len)
{
static uint8_t values[1 + NRF_SDH_BLE_GATT_MAX_MTU_SIZE - 3];
values[0] = len < 20 ? 20 : len;
memcpy(&values[1], p_value, len);
// update database.
ble_gatts_value_t gatts_value = {
.offset = 0,
.len = values[0] + 1,
.p_value = values,
};
uint32_t err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID, elite_context.data_char_handle.value_handle, &gatts_value);
memset(&values[1], 0x00, len);
return err_code;
}
ret_code_t le_event_notify(uint8_t *p_value, uint16_t len)
{
ble_gatts_hvx_params_t hvx_params = {
.handle = elite_context.event_char_handle.value_handle,
.type = BLE_GATT_HVX_NOTIFICATION,
.offset = 0,
.p_len = &len,
.p_data = p_value,
};
return sd_ble_gatts_hvx(elite_context.conn_handle, &hvx_params);
}
ret_code_t le_event_update(uint8_t *p_value, uint16_t len)
{
// update database.
ble_gatts_value_t gatts_value = {
.offset = 0,
.len = len,
.p_value = p_value,
};
return sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID, elite_context.event_char_handle.value_handle, &gatts_value);
}
ret_code_t le_event_async_notify(uint8_t *p_value, uint16_t len, uint32_t ms_to_wait)
{
ret_code_t ret = NRF_SUCCESS;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0)
{
// not inside ISR (Thread mode)
if (ms_to_wait)
{
ret = xMessageBufferSend(async_notify_msg, p_value, len, pdMS_TO_TICKS(ms_to_wait)) == len ? NRF_SUCCESS : NRF_ERROR_BUSY;
}
else
{
taskENTER_CRITICAL();
ret = xMessageBufferSend(async_notify_msg, p_value, len, 0) == len ? NRF_SUCCESS : NRF_ERROR_BUSY;
taskEXIT_CRITICAL();
}
}
else
{
// inside ISR
ret = xMessageBufferSendFromISR(async_notify_msg, p_value, len, &xHigherPriorityTaskWoken) == len ? NRF_SUCCESS : NRF_ERROR_BUSY;
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
return ret;
}
#endif // NRF_MODULE_ENABLED(BLE_ELITE)