mirror of https://github.com/ARMmbed/mbed-os.git
Cordio BLE: Rewrite reset test strategy
The existing test was expecting that the acknowledgement of the reset command would be the first and only event receive. This assumption is false. As a consequence, the new code parse all incoming packets and raise a flag in the following circumstances: * a reset packet has been successfully received. * RX stream is not synchronized * The status of the reset command is an error. Another test has been added that send a serie of reset commands.pull/7221/head
parent
4f8a006f3b
commit
d2ecdb8900
|
|
@ -15,11 +15,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "driver/CordioHCITransportDriver.h"
|
#include "driver/CordioHCITransportDriver.h"
|
||||||
#include "driver/CordioHCIDriver.h"
|
#include "driver/CordioHCIDriver.h"
|
||||||
#include "hci_defs.h"
|
#include "hci_defs.h"
|
||||||
#include "rtos/Semaphore.h"
|
#include "rtos/EventFlags.h"
|
||||||
|
|
||||||
#include "greentea-client/test_env.h"
|
#include "greentea-client/test_env.h"
|
||||||
#include "utest/utest.h"
|
#include "utest/utest.h"
|
||||||
|
|
@ -56,107 +57,162 @@ struct CordioHCIHook {
|
||||||
|
|
||||||
using ble::vendor::cordio::CordioHCIHook;
|
using ble::vendor::cordio::CordioHCIHook;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle signal mechanism
|
||||||
|
//
|
||||||
#define RESET_COMMAND_TIMEOUT (10 * 1000)
|
#define RESET_COMMAND_TIMEOUT (10 * 1000)
|
||||||
|
|
||||||
|
static const uint32_t RESET_RECEIVED_FLAG = 1 << 0;
|
||||||
|
static const uint32_t RECEPTION_ERROR_FLAG = 1 << 1;
|
||||||
|
static const uint32_t RESET_STATUS_ERROR_FLAG = 1 << 2;
|
||||||
|
|
||||||
|
static const uint32_t WAITING_FLAGS =
|
||||||
|
RESET_RECEIVED_FLAG | RECEPTION_ERROR_FLAG | RESET_STATUS_ERROR_FLAG;
|
||||||
|
|
||||||
|
static rtos::EventFlags event_channel;
|
||||||
|
|
||||||
|
static void signal_flag(uint32_t flag) {
|
||||||
|
if (!(event_channel.get() & flag)) {
|
||||||
|
event_channel.set(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t wait_for_event() {
|
||||||
|
// clear reception flags
|
||||||
|
uint32_t flags = event_channel.get();
|
||||||
|
event_channel.clear(flags & ~RESET_RECEIVED_FLAG);
|
||||||
|
|
||||||
|
return event_channel.wait_any(
|
||||||
|
WAITING_FLAGS,
|
||||||
|
/* timeout */ RESET_COMMAND_TIMEOUT,
|
||||||
|
/* clear */ false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle reset command reception
|
||||||
|
//
|
||||||
|
|
||||||
#define RESET_PARAMETER_LENGTH 4
|
#define RESET_PARAMETER_LENGTH 4
|
||||||
#define RESET_EXPECTED_STATUS 0
|
#define RESET_EXPECTED_STATUS 0
|
||||||
#define HCI_OPCODE_RESET_LSB (HCI_OPCODE_RESET & 0xFF)
|
#define HCI_OPCODE_RESET_LSB (HCI_OPCODE_RESET & 0xFF)
|
||||||
#define HCI_OPCODE_RESET_MSB (HCI_OPCODE_RESET >> 8)
|
#define HCI_OPCODE_RESET_MSB (HCI_OPCODE_RESET >> 8)
|
||||||
|
#define RESET_PACKET_LENGTH (1 + HCI_EVT_HDR_LEN + RESET_PARAMETER_LENGTH)
|
||||||
|
#define RESET_STATUS_INDEX 6
|
||||||
|
|
||||||
enum test_result_t {
|
static bool is_reset_event(const uint8_t* data, uint16_t len) {
|
||||||
TEST_RESULT_TIMEOUT_FAILURE,
|
if (len != RESET_PACKET_LENGTH) {
|
||||||
TEST_RESULT_FAILURE,
|
return false;
|
||||||
TEST_RESULT_SUCCESS
|
}
|
||||||
};
|
|
||||||
|
|
||||||
enum state_t {
|
if (*data++ != HCI_EVT_TYPE) {
|
||||||
WAITING_EVENT_PACKET,
|
return false;
|
||||||
WAITING_EVENT_CODE_COMPLETE,
|
}
|
||||||
WAITING_PARAMETER_LENGTH,
|
|
||||||
WAITING_STATUS,
|
|
||||||
WAITING_NUM_HCI_EVT_PACKET,
|
|
||||||
WAITING_OPCODE_LSB,
|
|
||||||
WAITING_OPCODE_MSB,
|
|
||||||
DONE
|
|
||||||
};
|
|
||||||
|
|
||||||
static state_t state = WAITING_EVENT_PACKET;
|
if (*data++ != HCI_CMD_CMPL_EVT) {
|
||||||
static test_result_t test_result = TEST_RESULT_TIMEOUT_FAILURE;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static rtos::Semaphore sem;
|
if (*data++ != RESET_PARAMETER_LENGTH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note skip num of HCI packet as this is controller dependent
|
||||||
|
data++;
|
||||||
|
|
||||||
|
if (*data++ != HCI_OPCODE_RESET_LSB) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*data++ != HCI_OPCODE_RESET_MSB) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hci_driver_rx_reset_handler(uint8_t* data, uint8_t len) {
|
||||||
|
enum packet_state_t {
|
||||||
|
WAITING_FOR_PACKET_TYPE,
|
||||||
|
WAITING_FOR_HEADER_COMPLETE,
|
||||||
|
WAITING_FOR_DATA_COMPLETE,
|
||||||
|
SYNCHRONIZATION_ERROR,
|
||||||
|
STATUS_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t packet[256] = { 0 };
|
||||||
|
static uint16_t position = 0;
|
||||||
|
static uint16_t packet_length;
|
||||||
|
static packet_state_t reception_state = WAITING_FOR_PACKET_TYPE;
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
switch (reception_state) {
|
||||||
|
case WAITING_FOR_PACKET_TYPE:
|
||||||
|
if (*data != HCI_EVT_TYPE) {
|
||||||
|
reception_state = SYNCHRONIZATION_ERROR;
|
||||||
|
signal_flag(RECEPTION_ERROR_FLAG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet[position++] = *data++;
|
||||||
|
--len;
|
||||||
|
packet_length = 1 + HCI_EVT_HDR_LEN;
|
||||||
|
reception_state = WAITING_FOR_HEADER_COMPLETE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAITING_FOR_HEADER_COMPLETE:
|
||||||
|
case WAITING_FOR_DATA_COMPLETE: {
|
||||||
|
uint16_t step = std::min((uint16_t) len, (uint16_t) (packet_length - position));
|
||||||
|
memcpy(packet + position, data, step);
|
||||||
|
position+= step;
|
||||||
|
data += step;
|
||||||
|
len -= step;
|
||||||
|
|
||||||
|
if (reception_state == WAITING_FOR_HEADER_COMPLETE &&
|
||||||
|
position == packet_length
|
||||||
|
) {
|
||||||
|
reception_state = WAITING_FOR_DATA_COMPLETE;
|
||||||
|
packet_length += packet[HCI_EVT_HDR_LEN];
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
|
||||||
|
// dead end; we never exit from the error state; just asignal it again.
|
||||||
|
case SYNCHRONIZATION_ERROR:
|
||||||
|
signal_flag(RECEPTION_ERROR_FLAG);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case STATUS_ERROR:
|
||||||
|
signal_flag(RESET_STATUS_ERROR_FLAG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool packet_complete = (reception_state == WAITING_FOR_DATA_COMPLETE) &&
|
||||||
|
(position == packet_length);
|
||||||
|
|
||||||
|
if (packet_complete) {
|
||||||
|
if (is_reset_event(packet, packet_length)) {
|
||||||
|
if (packet[RESET_STATUS_INDEX] != RESET_EXPECTED_STATUS) {
|
||||||
|
reception_state = STATUS_ERROR;
|
||||||
|
signal_flag(RESET_STATUS_ERROR_FLAG);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
signal_flag(RESET_RECEIVED_FLAG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reception_state = WAITING_FOR_PACKET_TYPE;
|
||||||
|
position = 0;
|
||||||
|
packet_length = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t reset_cmd[] = {
|
static uint8_t reset_cmd[] = {
|
||||||
HCI_OPCODE_RESET_LSB, HCI_OPCODE_RESET_MSB, // reset opcode
|
HCI_OPCODE_RESET_LSB, HCI_OPCODE_RESET_MSB, // reset opcode
|
||||||
0 // parameter length
|
0 // parameter length
|
||||||
};
|
};
|
||||||
|
|
||||||
static void hci_driver_rx_dummy_handler(uint8_t* data, uint8_t len) { }
|
|
||||||
|
|
||||||
static void hci_driver_rx_reset_handler(uint8_t* data, uint8_t len) {
|
|
||||||
for (size_t i = 0; i < len; ++i) {
|
|
||||||
switch (state) {
|
|
||||||
case WAITING_EVENT_PACKET:
|
|
||||||
if (data[i] == HCI_EVT_TYPE) {
|
|
||||||
state = WAITING_EVENT_CODE_COMPLETE;
|
|
||||||
} else {
|
|
||||||
test_result = TEST_RESULT_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAITING_EVENT_CODE_COMPLETE:
|
|
||||||
if (data[i] == HCI_CMD_CMPL_EVT) {
|
|
||||||
state = WAITING_PARAMETER_LENGTH;
|
|
||||||
} else {
|
|
||||||
test_result = TEST_RESULT_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAITING_PARAMETER_LENGTH:
|
|
||||||
if (data[i] == RESET_PARAMETER_LENGTH) {
|
|
||||||
state = WAITING_NUM_HCI_EVT_PACKET;
|
|
||||||
} else {
|
|
||||||
test_result = TEST_RESULT_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAITING_NUM_HCI_EVT_PACKET:
|
|
||||||
// controler dependent; can be any value, pass on to the next token
|
|
||||||
state = WAITING_OPCODE_LSB;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAITING_OPCODE_LSB:
|
|
||||||
if (data[i] == HCI_OPCODE_RESET_LSB) {
|
|
||||||
state = WAITING_OPCODE_MSB;
|
|
||||||
} else {
|
|
||||||
test_result = TEST_RESULT_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAITING_OPCODE_MSB:
|
|
||||||
if (data[i] == HCI_OPCODE_RESET_MSB) {
|
|
||||||
state = WAITING_STATUS;
|
|
||||||
} else {
|
|
||||||
test_result = TEST_RESULT_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WAITING_STATUS:
|
|
||||||
if (data[i] == RESET_EXPECTED_STATUS) {
|
|
||||||
test_result = TEST_RESULT_SUCCESS;
|
|
||||||
state = DONE;
|
|
||||||
} else {
|
|
||||||
test_result = TEST_RESULT_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_result != TEST_RESULT_TIMEOUT_FAILURE) {
|
|
||||||
CordioHCIHook::set_data_received_handler(hci_driver_rx_dummy_handler);
|
|
||||||
sem.release();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_reset_command() {
|
void test_reset_command() {
|
||||||
CordioHCIDriver& driver = CordioHCIHook::get_driver();
|
CordioHCIDriver& driver = CordioHCIHook::get_driver();
|
||||||
CordioHCITransportDriver& transport_driver = CordioHCIHook::get_transport_driver();
|
CordioHCITransportDriver& transport_driver = CordioHCIHook::get_transport_driver();
|
||||||
|
|
@ -164,18 +220,40 @@ void test_reset_command() {
|
||||||
driver.initialize();
|
driver.initialize();
|
||||||
|
|
||||||
CordioHCIHook::set_data_received_handler(hci_driver_rx_reset_handler);
|
CordioHCIHook::set_data_received_handler(hci_driver_rx_reset_handler);
|
||||||
|
|
||||||
transport_driver.write(HCI_CMD_TYPE, sizeof(reset_cmd), reset_cmd);
|
transport_driver.write(HCI_CMD_TYPE, sizeof(reset_cmd), reset_cmd);
|
||||||
sem.wait(RESET_COMMAND_TIMEOUT);
|
uint32_t events = wait_for_event();
|
||||||
CordioHCIHook::set_data_received_handler(hci_driver_rx_dummy_handler);
|
|
||||||
|
TEST_ASSERT_EQUAL(RESET_RECEIVED_FLAG, events);
|
||||||
|
|
||||||
driver.terminate();
|
driver.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(TEST_RESULT_SUCCESS, test_result);
|
#define EXPECTED_CONSECUTIVE_RESET 10
|
||||||
TEST_ASSERT_EQUAL(DONE, state);
|
|
||||||
|
void test_multiple_reset_command() {
|
||||||
|
CordioHCIDriver& driver = CordioHCIHook::get_driver();
|
||||||
|
CordioHCITransportDriver& transport_driver = CordioHCIHook::get_transport_driver();
|
||||||
|
|
||||||
|
driver.initialize();
|
||||||
|
|
||||||
|
CordioHCIHook::set_data_received_handler(hci_driver_rx_reset_handler);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < EXPECTED_CONSECUTIVE_RESET; ++i) {
|
||||||
|
transport_driver.write(HCI_CMD_TYPE, sizeof(reset_cmd), reset_cmd);
|
||||||
|
uint32_t events = wait_for_event();
|
||||||
|
TEST_ASSERT_EQUAL(RESET_RECEIVED_FLAG, events);
|
||||||
|
if (events != RESET_RECEIVED_FLAG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
driver.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Case cases[] = {
|
Case cases[] = {
|
||||||
Case("Test reset command", test_reset_command),
|
Case("Test reset command", test_reset_command),
|
||||||
|
Case("Test multiple reset commands", test_multiple_reset_command)
|
||||||
};
|
};
|
||||||
|
|
||||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue