pinebuds/services/ble_app/app_datapath/app_ble_cmd_handler.c

575 lines
19 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include "app_ble_cmd_handler.h"
#include "app_ble_custom_cmd.h"
#include "apps.h"
#include "bluetooth.h"
#include "cmsis_os.h"
#include "hal_timer.h"
#include "hal_trace.h"
#include "rwapp_config.h"
#include "stdbool.h"
#include "string.h"
#ifdef BTIF_BLE_APP_DATAPATH_SERVER
#define BLE_CUSTOM_CMD_WAITING_RSP_TIMEOUT_COUNT 8
#define BLE_RAW_DATA_XFER_BUF_SIZE 80
/**
* @brief waiting response timeout supervision data structure
*
*/
typedef struct {
uint16_t entryIndex; /**< The command waiting for the response */
uint16_t msTillTimeout; /**< run-time timeout left milliseconds */
} BLE_CUSTOM_CMD_WAITING_RSP_SUPERVISOR_T;
/**
* @brief Custom command handling environment
*
*/
typedef struct {
uint8_t isInRawDataXferStage; /**< true if the received data is raw data,
false if the
received data is in the format of
BLE_CUSTOM_CMD_PAYLOAD_T*/
uint16_t lengthOfRawDataXferToReceive;
uint16_t lengthOfReceivedRawDataXfer;
uint8_t *ptrRawXferDstBuf;
BLE_RawDataReceived_Handler_t rawDataHandler;
BLE_CUSTOM_CMD_WAITING_RSP_SUPERVISOR_T
waitingRspTimeoutInstance[BLE_CUSTOM_CMD_WAITING_RSP_TIMEOUT_COUNT];
uint32_t lastSysTicks;
uint8_t timeoutSupervisorCount;
osTimerId supervisor_timer_id;
osMutexId mutex;
} BLE_CUSTOM_CMD_ENV_T;
static uint8_t rawDataXferBuf[BLE_RAW_DATA_XFER_BUF_SIZE];
static BLE_CUSTOM_CMD_ENV_T ble_custom_cmd_env;
osMutexDef(app_ble_cmd_mutex);
static void ble_custom_cmd_rsp_supervision_timer_cb(void const *n);
osTimerDef(APP_CUSTOM_CMD_RSP_SUPERVISION_TIMER,
ble_custom_cmd_rsp_supervision_timer_cb);
static void BLE_remove_waiting_rsp_timeout_supervision(uint16_t entryIndex);
extern void app_datapath_server_send_data_via_notification(uint8_t *ptrData,
uint32_t length);
extern void app_datapath_server_send_data_via_indication(uint8_t *ptrData,
uint32_t length);
extern void app_datapath_server_send_data_via_write_command(uint8_t *ptrData,
uint32_t length);
extern void app_datapath_server_send_data_via_write_request(uint8_t *ptrData,
uint32_t length);
/**
* @brief Callback function of the waiting response supervisor timer.
*
*/
static void ble_custom_cmd_rsp_supervision_timer_cb(void const *n) {
uint32_t entryIndex =
ble_custom_cmd_env.waitingRspTimeoutInstance[0].entryIndex;
BLE_remove_waiting_rsp_timeout_supervision(entryIndex);
// it means time-out happens before the response is received from the peer
// device, trigger the response handler
CUSTOM_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex)
->cmdRspHandler(TIMEOUT_WAITING_RESPONSE, NULL, 0);
}
void BLE_get_response_handler(uint32_t funcCode, uint8_t *ptrParam,
uint32_t paramLen) {
// parameter length check
if (paramLen > sizeof(BLE_CUSTOM_CMD_RSP_T)) {
return;
}
if (0 == ble_custom_cmd_env.timeoutSupervisorCount) {
return;
}
BLE_CUSTOM_CMD_RSP_T *rsp = (BLE_CUSTOM_CMD_RSP_T *)ptrParam;
BLE_CUSTOM_CMD_INSTANCE_T *ptCmdInstance =
BLE_custom_command_get_entry_pointer_from_cmd_code(rsp->cmdCodeToRsp);
if (NULL == ptCmdInstance) {
return;
}
// remove the function code from the time-out supervision chain
BLE_remove_waiting_rsp_timeout_supervision(rsp->cmdCodeToRsp);
// call the response handler
if (ptCmdInstance->cmdRspHandler) {
ptCmdInstance->cmdRspHandler(rsp->cmdRetStatus, rsp->rspData,
rsp->rspDataLen);
}
}
void BLE_control_raw_data_xfer(bool isStartXfer) {
ble_custom_cmd_env.isInRawDataXferStage = isStartXfer;
if (true == isStartXfer) {
ble_custom_cmd_env.lengthOfReceivedRawDataXfer = 0;
// default configuration, can be customized by BLE_config_raw_data_xfer
ble_custom_cmd_env.lengthOfRawDataXferToReceive = sizeof(rawDataXferBuf);
ble_custom_cmd_env.ptrRawXferDstBuf = (uint8_t *)&rawDataXferBuf;
ble_custom_cmd_env.rawDataHandler = NULL;
}
}
void BLE_set_raw_data_xfer_received_callback(
BLE_RawDataReceived_Handler_t callback) {
ble_custom_cmd_env.rawDataHandler = callback;
}
void BLE_raw_data_xfer_control_handler(uint32_t funcCode, uint8_t *ptrParam,
uint32_t paramLen) {
bool isStartXfer = false;
if (OP_START_RAW_DATA_XFER == funcCode) {
isStartXfer = true;
}
BLE_control_raw_data_xfer(isStartXfer);
BLE_send_response_to_command(funcCode, NO_ERROR, NULL, 0,
TRANSMISSION_VIA_NOTIFICATION);
}
void BLE_start_raw_data_xfer_control_rsp_handler(
BLE_CUSTOM_CMD_RET_STATUS_E retStatus, uint8_t *ptrParam,
uint32_t paramLen) {
if (NO_ERROR == retStatus) {
/**< now the sending data api won't wrap BLE_CUSTOM_CMD_PAYLOAD_T but will
* directly send the raw data */
ble_custom_cmd_env.isInRawDataXferStage = true;
}
}
void BLE_stop_raw_data_xfer_control_rsp_handler(
BLE_CUSTOM_CMD_RET_STATUS_E retStatus, uint8_t *ptrParam,
uint32_t paramLen) {
if (NO_ERROR == retStatus) {
ble_custom_cmd_env.isInRawDataXferStage = false;
}
}
/**
* @brief Refresh the waiting response supervisor list
*
*/
static void ble_custom_cmd_refresh_supervisor_env(void) {
// do nothing if no supervisor was added
if (ble_custom_cmd_env.timeoutSupervisorCount > 0) {
uint32_t currentTicks = GET_CURRENT_TICKS();
uint32_t passedTicks;
if (currentTicks >= ble_custom_cmd_env.lastSysTicks) {
passedTicks = (currentTicks - ble_custom_cmd_env.lastSysTicks);
} else {
passedTicks =
(hal_sys_timer_get_max() - ble_custom_cmd_env.lastSysTicks + 1) +
currentTicks;
}
uint32_t deltaMs = TICKS_TO_MS(passedTicks);
BLE_CUSTOM_CMD_WAITING_RSP_SUPERVISOR_T *pRspSupervisor =
&(ble_custom_cmd_env.waitingRspTimeoutInstance[0]);
for (uint32_t index = 0; index < ble_custom_cmd_env.timeoutSupervisorCount;
index++) {
ASSERT(pRspSupervisor[index].msTillTimeout > deltaMs,
"the waiting command response supervisor timer is missing!!!, \
%d ms passed but the ms to trigger is %d",
deltaMs, pRspSupervisor[index].msTillTimeout);
pRspSupervisor[index].msTillTimeout -= deltaMs;
}
}
ble_custom_cmd_env.lastSysTicks = GET_CURRENT_TICKS();
}
/**
* @brief Remove the time-out supervision of waiting response
*
* @param entryIndex Entry index of the command table
*
*/
static void BLE_remove_waiting_rsp_timeout_supervision(uint16_t entryIndex) {
ASSERT(ble_custom_cmd_env.timeoutSupervisorCount > 0,
"%s The BLE custom command time-out supervisor is already empty!!!",
__FUNCTION__);
osMutexWait(ble_custom_cmd_env.mutex, osWaitForever);
uint32_t index;
for (index = 0; index < ble_custom_cmd_env.timeoutSupervisorCount; index++) {
if (ble_custom_cmd_env.waitingRspTimeoutInstance[index].entryIndex ==
entryIndex) {
memcpy(&(ble_custom_cmd_env.waitingRspTimeoutInstance[index]),
&(ble_custom_cmd_env.waitingRspTimeoutInstance[index + 1]),
(ble_custom_cmd_env.timeoutSupervisorCount - index - 1) *
sizeof(BLE_CUSTOM_CMD_WAITING_RSP_SUPERVISOR_T));
break;
}
}
// cannot find it, directly return
if (index == ble_custom_cmd_env.timeoutSupervisorCount) {
goto exit;
}
ble_custom_cmd_env.timeoutSupervisorCount--;
if (ble_custom_cmd_env.timeoutSupervisorCount > 0) {
// refresh supervisor environment firstly
ble_custom_cmd_refresh_supervisor_env();
// start timer, the first entry is the most close one
osTimerStart(ble_custom_cmd_env.supervisor_timer_id,
ble_custom_cmd_env.waitingRspTimeoutInstance[0].msTillTimeout);
} else {
// no supervisor, directly stop the timer
osTimerStop(ble_custom_cmd_env.supervisor_timer_id);
}
exit:
osMutexRelease(ble_custom_cmd_env.mutex);
}
/**
* @brief Add the time-out supervision of waiting response
*
* @param entryIndex Index of the command entry
*
*/
static void BLE_add_waiting_rsp_timeout_supervision(uint16_t entryIndex) {
ASSERT(ble_custom_cmd_env.timeoutSupervisorCount <
BLE_CUSTOM_CMD_WAITING_RSP_TIMEOUT_COUNT,
"%s The BLE custom command time-out supervisor is full!!!",
__FUNCTION__);
osMutexWait(ble_custom_cmd_env.mutex, osWaitForever);
// refresh supervisor environment firstly
ble_custom_cmd_refresh_supervisor_env();
BLE_CUSTOM_CMD_INSTANCE_T *pInstance =
CUSTOM_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex);
BLE_CUSTOM_CMD_WAITING_RSP_SUPERVISOR_T
waitingRspTimeoutInstance[BLE_CUSTOM_CMD_WAITING_RSP_TIMEOUT_COUNT];
uint32_t index = 0, insertedIndex = 0;
for (index = 0; index < ble_custom_cmd_env.timeoutSupervisorCount; index++) {
uint32_t msTillTimeout =
ble_custom_cmd_env.waitingRspTimeoutInstance[index].msTillTimeout;
// in the order of low to high
if ((ble_custom_cmd_env.waitingRspTimeoutInstance[index].entryIndex !=
entryIndex) &&
(pInstance->timeoutWaitingRspInMs >= msTillTimeout)) {
waitingRspTimeoutInstance[insertedIndex++] =
ble_custom_cmd_env.waitingRspTimeoutInstance[index];
} else if (pInstance->timeoutWaitingRspInMs < msTillTimeout) {
waitingRspTimeoutInstance[insertedIndex].entryIndex = entryIndex;
waitingRspTimeoutInstance[insertedIndex].msTillTimeout =
pInstance->timeoutWaitingRspInMs;
insertedIndex++;
}
}
// biggest one? then put it at the end of the list
if (ble_custom_cmd_env.timeoutSupervisorCount == index) {
waitingRspTimeoutInstance[insertedIndex].entryIndex = entryIndex;
waitingRspTimeoutInstance[insertedIndex].msTillTimeout =
pInstance->timeoutWaitingRspInMs;
insertedIndex++;
}
// copy to the global variable
memcpy((uint8_t *)&(ble_custom_cmd_env.waitingRspTimeoutInstance),
(uint8_t *)&waitingRspTimeoutInstance,
insertedIndex * sizeof(BLE_CUSTOM_CMD_WAITING_RSP_SUPERVISOR_T));
ble_custom_cmd_env.timeoutSupervisorCount = insertedIndex;
// start timer, the first entry is the most close one
osTimerStart(ble_custom_cmd_env.supervisor_timer_id,
ble_custom_cmd_env.waitingRspTimeoutInstance[0].msTillTimeout);
osMutexRelease(ble_custom_cmd_env.mutex);
}
/**
* @brief Return the pointer of the received raw data
*
* @return uint8_t* Pointer of the raw data buffer
*/
uint8_t *BLE_custom_command_raw_data_buffer_pointer(void) {
return ble_custom_cmd_env.ptrRawXferDstBuf;
}
/**
* @brief Return the size of the received raw data
*
* @return uint16_t Pointer of the raw data buffer
*/
uint16_t BLE_custom_command_received_raw_data_size(void) {
return ble_custom_cmd_env.lengthOfReceivedRawDataXfer;
}
/**
* @brief Receive the data from the peer device and parse them
*
* @param ptrData Pointer of the received data
* @param dataLength Length of the received data
*
* @return BLE_CUSTOM_CMD_RET_STATUS_E
*/
BLE_CUSTOM_CMD_RET_STATUS_E
BLE_custom_command_receive_data(uint8_t *ptrData, uint32_t dataLength) {
TRACE(1, "Receive length %d data: ", dataLength);
DUMP8("0x%02x ", ptrData, dataLength);
BLE_CUSTOM_CMD_PAYLOAD_T *pPayload = (BLE_CUSTOM_CMD_PAYLOAD_T *)ptrData;
if ((OP_START_RAW_DATA_XFER == pPayload->cmdCode) ||
(OP_STOP_RAW_DATA_XFER == pPayload->cmdCode) ||
(!(ble_custom_cmd_env.isInRawDataXferStage))) {
// check command code
if (pPayload->cmdCode >= OP_COMMAND_COUNT) {
return INVALID_CMD_CODE;
}
// check parameter length
if (pPayload->paramLen > sizeof(pPayload->param)) {
return PARAMETER_LENGTH_OUT_OF_RANGE;
}
BLE_CUSTOM_CMD_INSTANCE_T *pInstance =
BLE_custom_command_get_entry_pointer_from_cmd_code(pPayload->cmdCode);
// execute the command handler
if (!pInstance)
pInstance->cmdHandler(pPayload->cmdCode, pPayload->param,
pPayload->paramLen);
} else {
// the payload of the raw data xfer is 2 bytes cmd code + raw data
if (dataLength < sizeof(pPayload->cmdCode)) {
return PARAMETER_LENGTH_TOO_SHORT;
}
dataLength -= sizeof(pPayload->cmdCode);
ptrData += sizeof(pPayload->cmdCode);
if (NULL == ble_custom_cmd_env.rawDataHandler) {
// default handler
// save the received raw data into raw data buffer
uint32_t bytesToSave;
if ((dataLength + ble_custom_cmd_env.lengthOfReceivedRawDataXfer) >
ble_custom_cmd_env.lengthOfRawDataXferToReceive) {
bytesToSave = ble_custom_cmd_env.lengthOfRawDataXferToReceive -
ble_custom_cmd_env.lengthOfReceivedRawDataXfer;
} else {
bytesToSave = dataLength;
}
memcpy(
(uint8_t *)&ble_custom_cmd_env
.ptrRawXferDstBuf[ble_custom_cmd_env.lengthOfReceivedRawDataXfer],
ptrData, bytesToSave);
ble_custom_cmd_env.lengthOfReceivedRawDataXfer += bytesToSave;
} else {
// custom handler that is set by BLE_set_raw_data_xfer_received_callback
ble_custom_cmd_env.rawDataHandler(ptrData, dataLength);
}
}
return NO_ERROR;
}
static void BLE_send_out_data(BLE_CUSTOM_CMD_TRANSMISSION_PATH_E path,
BLE_CUSTOM_CMD_PAYLOAD_T *ptPayLoad) {
switch (path) {
case TRANSMISSION_VIA_NOTIFICATION:
app_datapath_server_send_data_via_notification(
(uint8_t *)ptPayLoad,
(uint32_t)(&(((BLE_CUSTOM_CMD_PAYLOAD_T *)0)->param)) +
ptPayLoad->paramLen);
break;
case TRANSMISSION_VIA_INDICATION:
app_datapath_server_send_data_via_indication(
(uint8_t *)ptPayLoad,
(uint32_t)(&(((BLE_CUSTOM_CMD_PAYLOAD_T *)0)->param)) +
ptPayLoad->paramLen);
break;
case TRANSMISSION_VIA_WRITE_CMD:
app_datapath_server_send_data_via_write_command(
(uint8_t *)ptPayLoad,
(uint32_t)(&(((BLE_CUSTOM_CMD_PAYLOAD_T *)0)->param)) +
ptPayLoad->paramLen);
break;
case TRANSMISSION_VIA_WRITE_REQ:
app_datapath_server_send_data_via_write_request(
(uint8_t *)ptPayLoad,
(uint32_t)(&(((BLE_CUSTOM_CMD_PAYLOAD_T *)0)->param)) +
ptPayLoad->paramLen);
break;
default:
break;
}
}
/**
* @brief Send response to the command request
*
* @param responsedCmdCode Command code of the responsed command request
* @param returnStatus Handling result
* @param rspData Pointer of the response data
* @param rspDataLen Length of the response data
* @param path Path of the data transmission
*
* @return BLE_CUSTOM_CMD_RET_STATUS_E
*/
BLE_CUSTOM_CMD_RET_STATUS_E
BLE_send_response_to_command(uint32_t responsedCmdCode,
BLE_CUSTOM_CMD_RET_STATUS_E returnStatus,
uint8_t *rspData, uint32_t rspDataLen,
BLE_CUSTOM_CMD_TRANSMISSION_PATH_E path) {
// check responsedCmdCode's validity
if (responsedCmdCode >= OP_COMMAND_COUNT) {
return INVALID_CMD_CODE;
}
BLE_CUSTOM_CMD_PAYLOAD_T payload;
BLE_CUSTOM_CMD_RSP_T *pResponse = (BLE_CUSTOM_CMD_RSP_T *)&(payload.param);
// check parameter length
if (rspDataLen > sizeof(pResponse->rspData)) {
return PARAMETER_LENGTH_OUT_OF_RANGE;
}
pResponse->cmdCodeToRsp = responsedCmdCode;
pResponse->cmdRetStatus = returnStatus;
pResponse->rspDataLen = rspDataLen;
memcpy(pResponse->rspData, rspData, rspDataLen);
payload.paramLen = 3 * sizeof(uint16_t) + rspDataLen;
payload.cmdCode = OP_RESPONSE_TO_CMD;
BLE_send_out_data(path, &payload);
return NO_ERROR;
}
/**
* @brief Send the custom command to the peer device
*
* @param cmdCode Command code
* @param ptrParam Pointer of the output parameter
* @param paramLen Length of the output parameter
* @param path Path of the data transmission
*
* @return BLE_CUSTOM_CMD_RET_STATUS_E
*/
BLE_CUSTOM_CMD_RET_STATUS_E
BLE_send_custom_command(uint32_t cmdCode, uint8_t *ptrParam, uint32_t paramLen,
BLE_CUSTOM_CMD_TRANSMISSION_PATH_E path) {
// check cmdCode's validity
if (cmdCode >= OP_COMMAND_COUNT) {
return INVALID_CMD_CODE;
}
BLE_CUSTOM_CMD_PAYLOAD_T payload;
// check parameter length
if (paramLen > sizeof(payload.param)) {
return PARAMETER_LENGTH_OUT_OF_RANGE;
}
uint16_t entryIndex =
BLE_custom_command_get_entry_index_from_cmd_code(cmdCode);
BLE_CUSTOM_CMD_INSTANCE_T *pInstance =
CUSTOM_COMMAND_PTR_FROM_ENTRY_INDEX(entryIndex);
// wrap the command payload
payload.cmdCode = cmdCode;
payload.paramLen = paramLen;
memcpy(payload.param, ptrParam, paramLen);
// send out the data
BLE_send_out_data(path, &payload);
// insert into time-out supervison
if (pInstance->isNeedResponse) {
BLE_add_waiting_rsp_timeout_supervision(cmdCode);
}
return NO_ERROR;
}
BLE_CUSTOM_CMD_INSTANCE_T *
BLE_custom_command_get_entry_pointer_from_cmd_code(uint16_t cmdCode) {
for (uint32_t index = 0; index < ((uint32_t)__custom_handler_table_end -
(uint32_t)__custom_handler_table_start) /
sizeof(BLE_CUSTOM_CMD_INSTANCE_T);
index++) {
if (CUSTOM_COMMAND_PTR_FROM_ENTRY_INDEX(index)->cmdCode == cmdCode) {
return CUSTOM_COMMAND_PTR_FROM_ENTRY_INDEX(index);
}
}
return NULL;
}
uint16_t BLE_custom_command_get_entry_index_from_cmd_code(uint16_t cmdCode) {
for (uint32_t index = 0;
index < (__custom_handler_table_end - __custom_handler_table_start) /
sizeof(BLE_CUSTOM_CMD_INSTANCE_T);
index++) {
if (CUSTOM_COMMAND_PTR_FROM_ENTRY_INDEX(index)->cmdCode == cmdCode) {
return index;
}
}
return INVALID_CUSTOM_ENTRY_INDEX;
}
/**
* @brief Initialize the BLE custom command framework
*
*/
void BLE_custom_command_init(void) {
memset((uint8_t *)&ble_custom_cmd_env, 0, sizeof(ble_custom_cmd_env));
ble_custom_cmd_env.supervisor_timer_id = osTimerCreate(
osTimer(APP_CUSTOM_CMD_RSP_SUPERVISION_TIMER), osTimerOnce, NULL);
ble_custom_cmd_env.mutex = osMutexCreate((osMutex(app_ble_cmd_mutex)));
}
#endif