Merge pull request #13055 from paul-szczepanek-arm/cordio-rf-tester

BLE: RF tester commands (and Cordio unhandled command complete)
pull/13095/head
Martin Kojtal 2020-06-10 12:07:30 +02:00 committed by GitHub
commit 6a431ffc58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 230 additions and 1 deletions

View File

@ -42,3 +42,7 @@ A partial implementation is present in the file
delivering memory to the stack and a complete reset sequence. However, it does
not define any initialization for the Bluetooth controller, this part being
specific to the controller used.
The driver also provides an interface to perform RF testing on the BLE trasnmitter.
This is done using the LE Receiver/Transmitter Test command and LE Test End command
as described in the Bluetooth Core spec in Vol.2, Part E, 7.8.28-30.

View File

@ -286,6 +286,83 @@ uint16_t CordioHCIDriver::write(uint8_t type, uint16_t len, uint8_t *pData)
void CordioHCIDriver::on_host_stack_inactivity()
{
}
void CordioHCIDriver::handle_test_end(bool success, uint16_t packets) {
if (_test_end_handler) {
_test_end_handler(success, packets);
_test_end_handler = nullptr;
}
}
ble_error_t CordioHCIDriver::rf_test_start_le_receiver_test(
test_end_handler_t test_end_handler, uint8_t channel
)
{
if (_test_end_handler) {
return BLE_ERROR_INVALID_STATE;
}
if (!test_end_handler) {
return BLE_ERROR_INVALID_PARAM;
}
_test_end_handler = test_end_handler;
uint8_t *buf = hciCmdAlloc(HCI_OPCODE_LE_RECEIVER_TEST, HCI_LEN_LE_RECEIVER_TEST);
if (buf) {
uint8_t* p = buf + HCI_CMD_HDR_LEN;
UINT8_TO_BSTREAM(p, channel);
hciCmdSend(buf);
return BLE_ERROR_NONE;
}
return BLE_ERROR_NO_MEM;
}
ble_error_t CordioHCIDriver::rf_test_start_le_transmitter_test(
test_end_handler_t test_end_handler, uint8_t channel, uint8_t length, uint8_t type
)
{
if (_test_end_handler) {
return BLE_ERROR_INVALID_STATE;
}
if (!test_end_handler) {
return BLE_ERROR_INVALID_PARAM;
}
_test_end_handler = test_end_handler;
uint8_t *buf = hciCmdAlloc(HCI_OPCODE_LE_TRANSMITTER_TEST, HCI_LEN_LE_TRANSMITTER_TEST);
if (buf) {
uint8_t* p = buf + HCI_CMD_HDR_LEN;
UINT8_TO_BSTREAM(p, channel);
UINT8_TO_BSTREAM(p, length);
UINT8_TO_BSTREAM(p, type);
hciCmdSend(buf);
return BLE_ERROR_NONE;
}
return BLE_ERROR_NO_MEM;
}
ble_error_t CordioHCIDriver::rf_test_end()
{
if (!_test_end_handler) {
return BLE_ERROR_INVALID_STATE;
}
uint8_t *buf = hciCmdAlloc(HCI_OPCODE_LE_TEST_END, HCI_LEN_LE_TEST_END);
if (buf) {
hciCmdSend(buf);
return BLE_ERROR_NONE;
}
return BLE_ERROR_NO_MEM;
}
} // namespace cordio
} // namespace vendor

View File

@ -22,6 +22,8 @@
#include <BLETypes.h>
#include "wsf_buf.h"
#include "CordioHCITransportDriver.h"
#include "ble/blecommon.h"
#include "mbed.h"
namespace ble {
namespace vendor {
@ -144,6 +146,47 @@ public:
*/
virtual void on_host_stack_inactivity();
/* BLE Tester commands */
/**
* This will be called by host part of the stack to indicate the end of the test.
*
* @param success True if the TEST END command was a success.
* @param packets Number of packets received during the test.
* @return BLE_ERROR_NONE on success.
*/
void handle_test_end(bool success, uint16_t packets);
/** Callback to inform the caller of the result of the test, the parameters are success and the
* number of packets received.
*/
typedef mbed::Callback<void(bool, uint16_t)> test_end_handler_t;
/**
* Start BLE receiver test. Call rf_test_end when you want to stop.
* @param test_end_handler Handler that will be called with the number of packets received.
* @param channel Channel to use.
* @return BLE_ERROR_NONE on success.
*/
ble_error_t rf_test_start_le_receiver_test(test_end_handler_t test_end_handler, uint8_t channel);
/**
* Start BLE transmitter test. Call rf_test_end when you want to stop.
* @param test_end_handler Handler that will be called with status and the number of packets set to 0.
* @param channel Channel to use.
* @param length Size of payload.
* @param type Type of pattern to transmit @see BLE spec Volume 6 Part F, Section 4.1.5.
* @return BLE_ERROR_NONE on success.
*/
ble_error_t rf_test_start_le_transmitter_test(test_end_handler_t test_end_handler, uint8_t channel,
uint8_t length, uint8_t type);
/**
* Complete the test. This will trigger the end test event which will call handle_test_end
* @return BLE_ERROR_NONE on success.
*/
ble_error_t rf_test_end();
protected:
/**
* Return a default set of memory pool that the Cordio stack can use.
@ -170,6 +213,9 @@ private:
*/
virtual void do_terminate() = 0;
protected:
test_end_handler_t _test_end_handler;
private:
CordioHCITransportDriver& _transport_driver;
};

View File

@ -63,6 +63,10 @@
"help": "Where the CBC MAC calculatio is performed. Valid values are 0 (host) and 1 (controller through HCI).",
"value": 1,
"macro_name": "SEC_CCM_CFG"
},
"route_unhandled_command_complete_events": {
"help": "If enabled the stack will forward to the user all HCI events not handled by the stack.",
"value": 1
}
}
}

View File

@ -34,6 +34,7 @@
#include "hci_drv.h"
#include "CordioBLE.h"
#include "mbed_assert.h"
#include "bstream.h"
#include "CordioPalAttClient.h"
#include "CordioPalSecurityManager.h"
@ -311,6 +312,22 @@ void BLE::processEvents()
deviceInstance().initialization_status = INITIALIZED;
_init_callback.call(&context);
} break;
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
case DM_UNHANDLED_CMD_CMPL_EVT_IND: {
// upcast to unhandled command complete event to access the payload
hciUnhandledCmdCmplEvt_t* unhandled = (hciUnhandledCmdCmplEvt_t*)msg;
if (unhandled->hdr.status == HCI_SUCCESS && unhandled->hdr.param == HCI_OPCODE_LE_TEST_END) {
// unhandled events are not parsed so we need to parse the payload ourselves
uint8_t status;
uint16_t packet_number;
status = unhandled->param[0];
BYTES_TO_UINT16(packet_number, unhandled->param + 1);
_hci_driver->handle_test_end(status == 0, packet_number);
return;
}
}
#endif // MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
default:
impl::PalGapImpl::gap_handler(msg);

View File

@ -511,6 +511,9 @@ enum
DM_ERROR_IND, /*!< \brief General error */
DM_HW_ERROR_IND, /*!< \brief Hardware error */
DM_VENDOR_SPEC_IND /*!< \brief Vendor specific event */
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
, DM_UNHANDLED_CMD_CMPL_EVT_IND, /*!< \brief Unhandled command complete events */
#endif
};
#define DM_CBACK_END DM_VENDOR_SPEC_IND /*!< \brief DM callback event ending value */
@ -763,6 +766,9 @@ typedef union
hciLeConnCteRspEnableCmdCmplEvt_t connCteRspStart; /*!< \brief handles \ref DM_CONN_CTE_RSP_START_IND */
hciLeConnCteRspEnableCmdCmplEvt_t connCteRspStop; /*!< \brief handles \ref DM_CONN_CTE_RSP_STOP_IND */
hciLeReadAntennaInfoCmdCmplEvt_t readAntennaInfo; /*!< \brief handles \ref DM_READ_ANTENNA_INFO_IND */
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
hciUnhandledCmdCmplEvt_t unhandledCmdCmplEvt;
#endif
dmL2cCmdRejEvt_t l2cCmdRej; /*!< \brief handles \ref DM_L2C_CMD_REJ_IND */
/* common header used by DM_ERROR_IND */
hciHwErrorEvt_t hwError; /*!< \brief handles \ref DM_HW_ERROR_IND */

View File

@ -115,6 +115,9 @@ extern "C" {
#define HCI_CIS_EST_CBACK_EVT 68 /*!< \brief CIS established event */
#define HCI_CIS_REQ_CBACK_EVT 69 /*!< \brief CIS request event */
#define HCI_REQ_PEER_SCA_CBACK_EVT 70 /*!< \brief Request peer SCA complete */
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
#define HCI_UNHANDLED_CMD_CMPL_CBACK_EVT 71 /*!< \brief Unhandled event */
#endif
/**@}*/
/**************************************************************************************************
@ -679,6 +682,15 @@ typedef struct
uint8_t cteMaxLen; /*!< \brief Max CTE Length. */
} hciLeReadAntennaInfoCmdCmplEvt_t;
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
/*! \brief LE read antenna information command complete event */
typedef struct
{
wsfMsgHdr_t hdr; /*!< \brief Event header containing the opcode in hdr.param. */
uint8_t param[1]; /*!< \brief Unhandled event payload. */
} hciUnhandledCmdCmplEvt_t;
#endif // MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
/*! \brief Local version information */
typedef struct
{
@ -754,6 +766,9 @@ typedef union
hciLeConnCteReqEnableCmdCmplEvt_t leConnCteReqEnableCmdCmpl; /*!< \brief LE connection CTE request enable command complete. */
hciLeConnCteRspEnableCmdCmplEvt_t leConnCteRspEnableCmdCmpl; /*!< \brief LE connection CTE response enable command complete. */
hciLeReadAntennaInfoCmdCmplEvt_t leReadAntennaInfoCmdCmpl; /*!< \brief LE read antenna information command complete. */
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
hciUnhandledCmdCmplEvt_t unhandledCmdCmpl; /*!< \brief Unhandled events. */
#endif
} hciEvt_t;
/*! \} */ /* STACK_HCI_EVT_API */

View File

@ -2048,6 +2048,12 @@ void hciEvtProcessCmdCmpl(uint8_t *p, uint8_t len)
{
cbackEvt = hciCoreVsCmdCmplRcvd(opcode, p, len);
}
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
else
{
cbackEvt = HCI_UNHANDLED_CMD_CMPL_CBACK_EVT;
}
#endif // MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
break;
}
@ -2055,6 +2061,29 @@ void hciEvtProcessCmdCmpl(uint8_t *p, uint8_t len)
if (cbackEvt != 0)
{
/* allocate temp buffer */
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
if (cbackEvt == HCI_UNHANDLED_CMD_CMPL_CBACK_EVT) {
const uint8_t structSize = sizeof(hciUnhandledCmdCmplEvt_t) - 1 /* removing the fake 1-byte array */;
const uint8_t remainingLen = len - 3 /* we already read opcode and numPkts */;
const uint8_t msgSize = structSize + remainingLen;
pMsg = WsfBufAlloc(msgSize);
if (pMsg != NULL) {
pMsg->hdr.param = opcode;
pMsg->hdr.event = HCI_UNHANDLED_CMD_CMPL_CBACK_EVT;
pMsg->hdr.status = HCI_SUCCESS;
/* copy the payload */
memcpy(pMsg->unhandledCmdCmpl.param, p, remainingLen);
/* execute callback */
(*cback)(pMsg);
/* free buffer */
WsfBufFree(pMsg);
}
}
else
#endif // MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
if ((pMsg = WsfBufAlloc(hciEvtCbackLen[cbackEvt])) != NULL)
{
/* initialize message header */

View File

@ -128,6 +128,23 @@ static void dmDevHciEvtHwError(hciEvt_t *pEvent)
(*dmCb.cback)((dmEvt_t *) pEvent);
}
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
/*************************************************************************************************/
/*!
* \brief Handle unhandled command complete events from HCI.
*
* \param pEvent Pointer to HCI callback event structure.
*
* \return None.
*/
/*************************************************************************************************/
static void dmDevHciEvtUnhandledCmdCmpl(hciEvt_t *pEvent)
{
pEvent->hdr.event = DM_UNHANDLED_CMD_CMPL_EVT_IND;
(*dmCb.cback)((dmEvt_t *) pEvent);
}
#endif // MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
/*************************************************************************************************/
/*!
* \brief DM dev HCI event handler.
@ -153,6 +170,12 @@ void dmDevHciHandler(hciEvt_t *pEvent)
dmDevHciEvtHwError(pEvent);
break;
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
case HCI_UNHANDLED_CMD_CMPL_CBACK_EVT:
dmDevHciEvtUnhandledCmdCmpl(pEvent);
break;
#endif // MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
default:
/* ignore event */
break;

View File

@ -107,6 +107,14 @@ static const uint8_t dmHciToIdTbl[] =
DM_ID_CONN_CTE, /* HCI_LE_CONN_CTE_REQ_ENABLE_CMD_CMPL_CBACK_EVT */
DM_ID_CONN_CTE, /* HCI_LE_CONN_CTE_RSP_ENABLE_CMD_CMPL_CBACK_EVT */
DM_ID_CONN_CTE /* HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT */
#if MBED_CONF_CORDIO_ROUTE_UNHANDLED_COMMAND_COMPLETE_EVENTS
/* these 3 were inexplicably missing */
, DM_ID_DEV /* HCI_CIS_EST_CBACK_EVT */
, DM_ID_DEV /* HCI_CIS_REQ_CBACK_EVT */
, DM_ID_DEV /* HCI_REQ_PEER_SCA_CBACK_EVT */
, DM_ID_DEV /* HCI_UNHANDLED_CMD_COMPL_CBACK_EVT */
#endif
};
/* DM callback event length table */
@ -230,7 +238,7 @@ dmCb_t dmCb;
/*************************************************************************************************/
static void dmHciEvtCback(hciEvt_t *pEvent)
{
WSF_ASSERT(pEvent->hdr.event <= HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT);
WSF_ASSERT(pEvent->hdr.event < sizeof(dmHciToIdTbl));
/* if DM not resetting or resetting but incoming event is HCI reset sequence complete event */
if (!dmCb.resetting || (pEvent->hdr.event == HCI_RESET_SEQ_CMPL_CBACK_EVT))