-
Notifications
You must be signed in to change notification settings - Fork 3k
Cordio: Add tests that validates a cordio port. #7221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
14e229e
Cordio: Add tests that validates a cordio port.
pan- 5b7f7aa
Cordio BLE: Fix test failure when controller initialization failed.
pan- 4f8a006
Cordio BLE: Document in test what fields are expected to be set after…
pan- d2ecdb8
Cordio BLE: Rewrite reset test strategy
pan- File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
features/FEATURE_BLE/targets/TARGET_CORDIO/TESTS/cordio_hci/driver/main.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
268 changes: 268 additions & 0 deletions
268
features/FEATURE_BLE/targets/TARGET_CORDIO/TESTS/cordio_hci/transport/main.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it actually acceptable for the initialisation process to take this long?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, at first I was thinking of setting it to 30 seconds; if the bitrate is low and the controller require a large service pack then initialization can takes a loooooot of time.