diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/doc/HCIAbstraction.md b/features/FEATURE_BLE/targets/TARGET_CORDIO/doc/HCIAbstraction.md index 879fb01806..4ae44b9505 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/doc/HCIAbstraction.md +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/doc/HCIAbstraction.md @@ -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. diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.cpp b/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.cpp index bdca48f634..70e5616cc1 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.cpp +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.cpp @@ -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 diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.h b/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.h index e620748e49..e491e20335 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.h +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/driver/CordioHCIDriver.h @@ -22,6 +22,8 @@ #include #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 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; }; diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/mbed_lib.json b/features/FEATURE_BLE/targets/TARGET_CORDIO/mbed_lib.json index 165b63f15e..e4de776e10 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/mbed_lib.json +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/mbed_lib.json @@ -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 } } } \ No newline at end of file diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioBLE.cpp b/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioBLE.cpp index 5085bc29f0..89e9a66cab 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioBLE.cpp +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioBLE.cpp @@ -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); diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/dm_api.h b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/dm_api.h index b2ddb03d7e..0c1e4c9727 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/dm_api.h +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/dm_api.h @@ -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 */ diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/hci_api.h b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/hci_api.h index d0aa82fe83..1c90195be4 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/hci_api.h +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/include/hci_api.h @@ -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 */ diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/hci/dual_chip/hci_evt.c b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/hci/dual_chip/hci_evt.c index 3fe4ab8bfe..265465ad8a 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/hci/dual_chip/hci_evt.c +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/hci/dual_chip/hci_evt.c @@ -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 */ diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_dev.c b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_dev.c index e77caef01d..abc1a89dbb 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_dev.c +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_dev.c @@ -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; diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_main.c b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_main.c index 12047f1d0f..ede1811d87 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_main.c +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/stack/ble-host/sources/stack/dm/dm_main.c @@ -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))