|
15 | 15 | */
|
16 | 16 |
|
17 | 17 | #include <stdio.h>
|
| 18 | +#include <algorithm> |
18 | 19 |
|
19 | 20 | #include "driver/CordioHCITransportDriver.h"
|
20 | 21 | #include "driver/CordioHCIDriver.h"
|
21 | 22 | #include "hci_defs.h"
|
22 |
| -#include "rtos/Semaphore.h" |
| 23 | +#include "rtos/EventFlags.h" |
23 | 24 |
|
24 | 25 | #include "greentea-client/test_env.h"
|
25 | 26 | #include "utest/utest.h"
|
@@ -56,126 +57,203 @@ struct CordioHCIHook {
|
56 | 57 |
|
57 | 58 | using ble::vendor::cordio::CordioHCIHook;
|
58 | 59 |
|
| 60 | +// |
| 61 | +// Handle signal mechanism |
| 62 | +// |
59 | 63 | #define RESET_COMMAND_TIMEOUT (10 * 1000)
|
| 64 | + |
| 65 | +static const uint32_t RESET_RECEIVED_FLAG = 1 << 0; |
| 66 | +static const uint32_t RECEPTION_ERROR_FLAG = 1 << 1; |
| 67 | +static const uint32_t RESET_STATUS_ERROR_FLAG = 1 << 2; |
| 68 | + |
| 69 | +static const uint32_t WAITING_FLAGS = |
| 70 | + RESET_RECEIVED_FLAG | RECEPTION_ERROR_FLAG | RESET_STATUS_ERROR_FLAG; |
| 71 | + |
| 72 | +static rtos::EventFlags event_channel; |
| 73 | + |
| 74 | +static void signal_flag(uint32_t flag) { |
| 75 | + if (!(event_channel.get() & flag)) { |
| 76 | + event_channel.set(flag); |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +uint32_t wait_for_event() { |
| 81 | + // clear reception flags |
| 82 | + uint32_t flags = event_channel.get(); |
| 83 | + event_channel.clear(flags & ~RESET_RECEIVED_FLAG); |
| 84 | + |
| 85 | + return event_channel.wait_any( |
| 86 | + WAITING_FLAGS, |
| 87 | + /* timeout */ RESET_COMMAND_TIMEOUT, |
| 88 | + /* clear */ false |
| 89 | + ); |
| 90 | +} |
| 91 | + |
| 92 | +// |
| 93 | +// Handle reset command reception |
| 94 | +// |
| 95 | + |
60 | 96 | #define RESET_PARAMETER_LENGTH 4
|
61 | 97 | #define RESET_EXPECTED_STATUS 0
|
62 | 98 | #define HCI_OPCODE_RESET_LSB (HCI_OPCODE_RESET & 0xFF)
|
63 | 99 | #define HCI_OPCODE_RESET_MSB (HCI_OPCODE_RESET >> 8)
|
| 100 | +#define RESET_PACKET_LENGTH (1 + HCI_EVT_HDR_LEN + RESET_PARAMETER_LENGTH) |
| 101 | +#define RESET_STATUS_INDEX 6 |
64 | 102 |
|
65 |
| -enum test_result_t { |
66 |
| - TEST_RESULT_TIMEOUT_FAILURE, |
67 |
| - TEST_RESULT_FAILURE, |
68 |
| - TEST_RESULT_SUCCESS |
69 |
| -}; |
| 103 | +static bool is_reset_event(const uint8_t* data, uint16_t len) { |
| 104 | + if (len != RESET_PACKET_LENGTH) { |
| 105 | + return false; |
| 106 | + } |
70 | 107 |
|
71 |
| -enum state_t { |
72 |
| - WAITING_EVENT_PACKET, |
73 |
| - WAITING_EVENT_CODE_COMPLETE, |
74 |
| - WAITING_PARAMETER_LENGTH, |
75 |
| - WAITING_STATUS, |
76 |
| - WAITING_NUM_HCI_EVT_PACKET, |
77 |
| - WAITING_OPCODE_LSB, |
78 |
| - WAITING_OPCODE_MSB, |
79 |
| - DONE |
80 |
| -}; |
| 108 | + if (*data++ != HCI_EVT_TYPE) { |
| 109 | + return false; |
| 110 | + } |
| 111 | + |
| 112 | + if (*data++ != HCI_CMD_CMPL_EVT) { |
| 113 | + return false; |
| 114 | + } |
81 | 115 |
|
82 |
| -static state_t state = WAITING_EVENT_PACKET; |
83 |
| -static test_result_t test_result = TEST_RESULT_TIMEOUT_FAILURE; |
| 116 | + if (*data++ != RESET_PARAMETER_LENGTH) { |
| 117 | + return false; |
| 118 | + } |
84 | 119 |
|
85 |
| -static rtos::Semaphore sem; |
| 120 | + // Note skip num of HCI packet as this is controller dependent |
| 121 | + data++; |
86 | 122 |
|
87 |
| -static uint8_t reset_cmd[] = { |
88 |
| - HCI_OPCODE_RESET_LSB, HCI_OPCODE_RESET_MSB, // reset opcode |
89 |
| - 0 // parameter length |
90 |
| -}; |
| 123 | + if (*data++ != HCI_OPCODE_RESET_LSB) { |
| 124 | + return false; |
| 125 | + } |
| 126 | + |
| 127 | + if (*data++ != HCI_OPCODE_RESET_MSB) { |
| 128 | + return false; |
| 129 | + } |
91 | 130 |
|
92 |
| -static void hci_driver_rx_dummy_handler(uint8_t* data, uint8_t len) { } |
| 131 | + return true; |
| 132 | +} |
93 | 133 |
|
94 | 134 | static void hci_driver_rx_reset_handler(uint8_t* data, uint8_t len) {
|
95 |
| - for (size_t i = 0; i < len; ++i) { |
96 |
| - switch (state) { |
97 |
| - case WAITING_EVENT_PACKET: |
98 |
| - if (data[i] == HCI_EVT_TYPE) { |
99 |
| - state = WAITING_EVENT_CODE_COMPLETE; |
100 |
| - } else { |
101 |
| - test_result = TEST_RESULT_FAILURE; |
| 135 | + enum packet_state_t { |
| 136 | + WAITING_FOR_PACKET_TYPE, |
| 137 | + WAITING_FOR_HEADER_COMPLETE, |
| 138 | + WAITING_FOR_DATA_COMPLETE, |
| 139 | + SYNCHRONIZATION_ERROR, |
| 140 | + STATUS_ERROR |
| 141 | + }; |
| 142 | + |
| 143 | + static uint8_t packet[256] = { 0 }; |
| 144 | + static uint16_t position = 0; |
| 145 | + static uint16_t packet_length; |
| 146 | + static packet_state_t reception_state = WAITING_FOR_PACKET_TYPE; |
| 147 | + |
| 148 | + while (len) { |
| 149 | + switch (reception_state) { |
| 150 | + case WAITING_FOR_PACKET_TYPE: |
| 151 | + if (*data != HCI_EVT_TYPE) { |
| 152 | + reception_state = SYNCHRONIZATION_ERROR; |
| 153 | + signal_flag(RECEPTION_ERROR_FLAG); |
| 154 | + return; |
102 | 155 | }
|
103 |
| - break; |
104 | 156 |
|
105 |
| - case WAITING_EVENT_CODE_COMPLETE: |
106 |
| - if (data[i] == HCI_CMD_CMPL_EVT) { |
107 |
| - state = WAITING_PARAMETER_LENGTH; |
108 |
| - } else { |
109 |
| - test_result = TEST_RESULT_FAILURE; |
110 |
| - } |
| 157 | + packet[position++] = *data++; |
| 158 | + --len; |
| 159 | + packet_length = 1 + HCI_EVT_HDR_LEN; |
| 160 | + reception_state = WAITING_FOR_HEADER_COMPLETE; |
111 | 161 | break;
|
112 | 162 |
|
113 |
| - case WAITING_PARAMETER_LENGTH: |
114 |
| - if (data[i] == RESET_PARAMETER_LENGTH) { |
115 |
| - state = WAITING_NUM_HCI_EVT_PACKET; |
116 |
| - } else { |
117 |
| - test_result = TEST_RESULT_FAILURE; |
| 163 | + case WAITING_FOR_HEADER_COMPLETE: |
| 164 | + case WAITING_FOR_DATA_COMPLETE: { |
| 165 | + uint16_t step = std::min((uint16_t) len, (uint16_t) (packet_length - position)); |
| 166 | + memcpy(packet + position, data, step); |
| 167 | + position+= step; |
| 168 | + data += step; |
| 169 | + len -= step; |
| 170 | + |
| 171 | + if (reception_state == WAITING_FOR_HEADER_COMPLETE && |
| 172 | + position == packet_length |
| 173 | + ) { |
| 174 | + reception_state = WAITING_FOR_DATA_COMPLETE; |
| 175 | + packet_length += packet[HCI_EVT_HDR_LEN]; |
118 | 176 | }
|
119 |
| - break; |
| 177 | + } break; |
120 | 178 |
|
121 |
| - case WAITING_NUM_HCI_EVT_PACKET: |
122 |
| - // controler dependent; can be any value, pass on to the next token |
123 |
| - state = WAITING_OPCODE_LSB; |
124 |
| - break; |
125 | 179 |
|
126 |
| - case WAITING_OPCODE_LSB: |
127 |
| - if (data[i] == HCI_OPCODE_RESET_LSB) { |
128 |
| - state = WAITING_OPCODE_MSB; |
129 |
| - } else { |
130 |
| - test_result = TEST_RESULT_FAILURE; |
131 |
| - } |
132 |
| - break; |
| 180 | + // dead end; we never exit from the error state; just asignal it again. |
| 181 | + case SYNCHRONIZATION_ERROR: |
| 182 | + signal_flag(RECEPTION_ERROR_FLAG); |
| 183 | + return; |
133 | 184 |
|
134 |
| - case WAITING_OPCODE_MSB: |
135 |
| - if (data[i] == HCI_OPCODE_RESET_MSB) { |
136 |
| - state = WAITING_STATUS; |
137 |
| - } else { |
138 |
| - test_result = TEST_RESULT_FAILURE; |
139 |
| - } |
140 |
| - break; |
| 185 | + case STATUS_ERROR: |
| 186 | + signal_flag(RESET_STATUS_ERROR_FLAG); |
| 187 | + return; |
| 188 | + } |
141 | 189 |
|
142 |
| - case WAITING_STATUS: |
143 |
| - if (data[i] == RESET_EXPECTED_STATUS) { |
144 |
| - test_result = TEST_RESULT_SUCCESS; |
145 |
| - state = DONE; |
| 190 | + bool packet_complete = (reception_state == WAITING_FOR_DATA_COMPLETE) && |
| 191 | + (position == packet_length); |
| 192 | + |
| 193 | + if (packet_complete) { |
| 194 | + if (is_reset_event(packet, packet_length)) { |
| 195 | + if (packet[RESET_STATUS_INDEX] != RESET_EXPECTED_STATUS) { |
| 196 | + reception_state = STATUS_ERROR; |
| 197 | + signal_flag(RESET_STATUS_ERROR_FLAG); |
| 198 | + return; |
146 | 199 | } else {
|
147 |
| - test_result = TEST_RESULT_FAILURE; |
| 200 | + signal_flag(RESET_RECEIVED_FLAG); |
148 | 201 | }
|
149 |
| - break; |
150 |
| - } |
| 202 | + } |
151 | 203 |
|
152 |
| - if (test_result != TEST_RESULT_TIMEOUT_FAILURE) { |
153 |
| - CordioHCIHook::set_data_received_handler(hci_driver_rx_dummy_handler); |
154 |
| - sem.release(); |
155 |
| - return; |
| 204 | + reception_state = WAITING_FOR_PACKET_TYPE; |
| 205 | + position = 0; |
| 206 | + packet_length = 1; |
156 | 207 | }
|
157 | 208 | }
|
158 | 209 | }
|
159 | 210 |
|
| 211 | +static uint8_t reset_cmd[] = { |
| 212 | + HCI_OPCODE_RESET_LSB, HCI_OPCODE_RESET_MSB, // reset opcode |
| 213 | + 0 // parameter length |
| 214 | +}; |
| 215 | + |
160 | 216 | void test_reset_command() {
|
161 | 217 | CordioHCIDriver& driver = CordioHCIHook::get_driver();
|
162 | 218 | CordioHCITransportDriver& transport_driver = CordioHCIHook::get_transport_driver();
|
163 | 219 |
|
164 | 220 | driver.initialize();
|
165 | 221 |
|
166 | 222 | CordioHCIHook::set_data_received_handler(hci_driver_rx_reset_handler);
|
| 223 | + |
167 | 224 | transport_driver.write(HCI_CMD_TYPE, sizeof(reset_cmd), reset_cmd);
|
168 |
| - sem.wait(RESET_COMMAND_TIMEOUT); |
169 |
| - CordioHCIHook::set_data_received_handler(hci_driver_rx_dummy_handler); |
| 225 | + uint32_t events = wait_for_event(); |
| 226 | + |
| 227 | + TEST_ASSERT_EQUAL(RESET_RECEIVED_FLAG, events); |
170 | 228 |
|
171 | 229 | driver.terminate();
|
| 230 | +} |
| 231 | + |
| 232 | +#define EXPECTED_CONSECUTIVE_RESET 10 |
172 | 233 |
|
173 |
| - TEST_ASSERT_EQUAL(TEST_RESULT_SUCCESS, test_result); |
174 |
| - TEST_ASSERT_EQUAL(DONE, state); |
| 234 | +void test_multiple_reset_command() { |
| 235 | + CordioHCIDriver& driver = CordioHCIHook::get_driver(); |
| 236 | + CordioHCITransportDriver& transport_driver = CordioHCIHook::get_transport_driver(); |
| 237 | + |
| 238 | + driver.initialize(); |
| 239 | + |
| 240 | + CordioHCIHook::set_data_received_handler(hci_driver_rx_reset_handler); |
| 241 | + |
| 242 | + for (size_t i = 0; i < EXPECTED_CONSECUTIVE_RESET; ++i) { |
| 243 | + transport_driver.write(HCI_CMD_TYPE, sizeof(reset_cmd), reset_cmd); |
| 244 | + uint32_t events = wait_for_event(); |
| 245 | + TEST_ASSERT_EQUAL(RESET_RECEIVED_FLAG, events); |
| 246 | + if (events != RESET_RECEIVED_FLAG) { |
| 247 | + break; |
| 248 | + } |
| 249 | + } |
| 250 | + |
| 251 | + driver.terminate(); |
175 | 252 | }
|
176 | 253 |
|
177 | 254 | Case cases[] = {
|
178 | 255 | Case("Test reset command", test_reset_command),
|
| 256 | + Case("Test multiple reset commands", test_multiple_reset_command) |
179 | 257 | };
|
180 | 258 |
|
181 | 259 | utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
|
|
0 commit comments