Merge pull request #7221 from pan-/cordio-port-test

Cordio: Add tests that validates a cordio port.
pull/7359/head
Cruz Monrreal 2018-06-21 23:30:39 -05:00 committed by GitHub
commit 34513f2a92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 379 additions and 0 deletions

View File

@ -0,0 +1,111 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include "events/mbed_events.h"
#include "platform/Callback.h"
#include "ble/BLE.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
#include "hci_api.h"
#include "hci_cmd.h"
#include "hci_core.h"
#include "dm_api.h"
#include "bstream.h"
using namespace utest::v1;
using mbed::callback;
#define INITIALIZATION_TIMEOUT (10 * 1000)
static EventQueue event_queue(/* event count */ 10 * EVENTS_EVENT_SIZE);
enum initialization_state_t {
WAITING_FOR_INITIALIZATION,
INITIALIZATION_FAILURE,
INITIALIZATION_SUCCESS
};
static initialization_state_t initialization_state = WAITING_FOR_INITIALIZATION;
static void process_ble_events(BLE::OnEventsToProcessCallbackContext* context) {
BLE &ble = BLE::Instance();
event_queue.call(callback(&ble, &BLE::processEvents));
}
static void on_initialization_complete(BLE::InitializationCompleteCallbackContext *params) {
if (params->error == BLE_ERROR_NONE) {
initialization_state = INITIALIZATION_SUCCESS;
} else {
initialization_state = INITIALIZATION_FAILURE;
}
event_queue.break_dispatch();
}
static void test_stack_initialization() {
BLE &ble = BLE::Instance();
ble.onEventsToProcess(process_ble_events);
ble.init(on_initialization_complete);
event_queue.dispatch(INITIALIZATION_TIMEOUT);
// At this point ble is suppose to be initialized; inspect the various state
// of the stack.
TEST_ASSERT_EQUAL(INITIALIZATION_SUCCESS, initialization_state);
// ensure that the size of ACL buffer of the controller has been filled in
// during the initialisation
TEST_ASSERT_NOT_EQUAL(0, hciCoreCb.bufSize);
// Ensure that the total number of buffer available in the controller has
// been filled in during the initialisation
TEST_ASSERT_NOT_EQUAL(0, hciCoreCb.numBufs);
// Ensure that at least one HCI buffer is available
TEST_ASSERT_NOT_EQUAL(0, hciCoreCb.availBufs);
// Ensure that allowed LE state has been filled in during initialisation
// Note: see BT command LE Read Supported States Command in BT specification
uint8_t invalid_le_states[HCI_LE_STATES_LEN] = { 0 };
TEST_ASSERT_NOT_EQUAL(0, memcmp(invalid_le_states, hciCoreCb.leStates, HCI_LE_STATES_LEN));
// Ensure that the size of the whitelist of the controller has been filled
// in during the initialisation
TEST_ASSERT_NOT_EQUAL(0, hciCoreCb.whiteListSize);
// Note: cannot test supported features are the list may be null
// Note: cannot test resolving list size as this may be null
}
Case cases[] = {
Case("Test cordio stack reset sequence", test_stack_initialization),
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
GREENTEA_SETUP(15, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
int main() {
return !Harness::run(specification);
}

View File

@ -0,0 +1,268 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <algorithm>
#include "driver/CordioHCITransportDriver.h"
#include "driver/CordioHCIDriver.h"
#include "hci_defs.h"
#include "rtos/EventFlags.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
using namespace utest::v1;
using ble::vendor::cordio::CordioHCIDriver;
using ble::vendor::cordio::CordioHCITransportDriver;
extern ble::vendor::cordio::CordioHCIDriver& ble_cordio_get_hci_driver();
namespace ble {
namespace vendor {
namespace cordio {
struct CordioHCIHook {
static CordioHCIDriver& get_driver() {
return ble_cordio_get_hci_driver();
}
static CordioHCITransportDriver& get_transport_driver() {
return get_driver()._transport_driver;
}
static void set_data_received_handler(void (*handler)(uint8_t*, uint8_t)) {
get_transport_driver().set_data_received_handler(handler);
}
};
} // namespace cordio
} // namespace vendor
} // namespace ble
using ble::vendor::cordio::CordioHCIHook;
//
// Handle signal mechanism
//
#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_EXPECTED_STATUS 0
#define HCI_OPCODE_RESET_LSB (HCI_OPCODE_RESET & 0xFF)
#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
static bool is_reset_event(const uint8_t* data, uint16_t len) {
if (len != RESET_PACKET_LENGTH) {
return false;
}
if (*data++ != HCI_EVT_TYPE) {
return false;
}
if (*data++ != HCI_CMD_CMPL_EVT) {
return false;
}
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[] = {
HCI_OPCODE_RESET_LSB, HCI_OPCODE_RESET_MSB, // reset opcode
0 // parameter length
};
void test_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);
transport_driver.write(HCI_CMD_TYPE, sizeof(reset_cmd), reset_cmd);
uint32_t events = wait_for_event();
TEST_ASSERT_EQUAL(RESET_RECEIVED_FLAG, events);
driver.terminate();
}
#define EXPECTED_CONSECUTIVE_RESET 10
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("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) {
GREENTEA_SETUP(15, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
int main() {
return !Harness::run(specification);
}