mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #7221 from pan-/cordio-port-test
Cordio: Add tests that validates a cordio port.pull/7359/head
commit
34513f2a92
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue