Knight KE / Mbed OS Game_Master
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers emac_util.cpp Source File

emac_util.cpp

00001 /*
00002  * Copyright (c) 2017, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include "mbed.h"
00019 #include "greentea-client/test_env.h"
00020 #include "unity.h"
00021 #include "utest.h"
00022 
00023 #if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
00024 
00025 #include "mbed.h"
00026 
00027 #include "EMAC.h"
00028 #include "EMACMemoryManager.h"
00029 #include "emac_TestMemoryManager.h"
00030 
00031 #include "emac_tests.h"
00032 #include "emac_initialize.h"
00033 #include "emac_util.h"
00034 #include "emac_membuf.h"
00035 #include "emac_ctp.h"
00036 
00037 using namespace utest::v1;
00038 
00039 /* For LPC boards define the memory bank ourselves to give us section placement
00040    control */
00041 #ifndef ETHMEM_SECTION
00042 #if defined(TARGET_LPC4088) || defined(TARGET_LPC4088_DM)
00043 #  if defined (__ICCARM__)
00044 #     define ETHMEM_SECTION
00045 #  elif defined(TOOLCHAIN_GCC_CR)
00046 #     define ETHMEM_SECTION __attribute__((section(".data.$RamPeriph32")))
00047 #  else
00048 #     define ETHMEM_SECTION __attribute__((section("AHBSRAM0"),aligned))
00049 #  endif
00050 #elif defined(TARGET_LPC1768) || defined(TARGET_LPC1769)
00051 #  if defined (__ICCARM__)
00052 #     define ETHMEM_SECTION
00053 #  elif defined(TOOLCHAIN_GCC_CR)
00054 #     define ETHMEM_SECTION __attribute__((section(".data.$RamPeriph32")))
00055 #  else
00056 #     define ETHMEM_SECTION __attribute__((section("AHBSRAM1"),aligned))
00057 #  endif
00058 #endif
00059 #endif
00060 
00061 #ifndef ETHMEM_SECTION
00062 #define ETHMEM_SECTION
00063 #endif
00064 
00065 typedef struct {
00066     int length;
00067     int receipt_number;
00068     unsigned short flags;
00069     unsigned short lifetime;
00070 } outgoing_msg_t;
00071 
00072 #define ECHO_SERVER_COUNT       5
00073 
00074 #define OUTGOING_MSG_COUNT      100
00075 
00076 // Event flags
00077 #define LINK_UP                 0x01
00078 #define LINK_DOWN               0x02
00079 
00080 // Hook to lwip input function
00081 extern struct netif *netif_list;
00082 
00083 // Broadcast address
00084 const unsigned char eth_mac_broadcast_addr[ETH_MAC_ADDR_LEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
00085 
00086 // MTU size
00087 static int eth_mtu_size = 0;
00088 
00089 // Event queue
00090 static rtos::Semaphore worker_loop_semaphore;
00091 static rtos::Semaphore link_status_semaphore;
00092 
00093 #if defined (__ICCARM__)
00094 #pragma location = ".ethusbram"
00095 #endif
00096 ETHMEM_SECTION static EventQueue worker_loop_event_queue(20 * EVENTS_EVENT_SIZE);
00097 
00098 static void worker_loop_event_cb(int event);
00099 static Event<void(int)> worker_loop_event(&worker_loop_event_queue, worker_loop_event_cb);
00100 static void link_input_event_cb(void *buf);
00101 static Event<void(void *)> link_input_event(&worker_loop_event_queue, link_input_event_cb);
00102 
00103 // Found echo server addresses
00104 static unsigned char eth_mac_echo_server_addr[ECHO_SERVER_COUNT][ETH_MAC_ADDR_LEN];
00105 static int etc_mac_echo_server_free_index = 0;
00106 
00107 static bool output_memory = true;
00108 static bool input_memory = true;
00109 
00110 static void (*current_test_step_cb_fnc)(int opt);
00111 
00112 // Outgoing messages
00113 static outgoing_msg_t outgoing_msgs[OUTGOING_MSG_COUNT];
00114 
00115 static unsigned int trace_level = 0;
00116 static unsigned int error_flags = 0;
00117 static unsigned int no_response_cnt = 0;
00118 static bool link_up = false;
00119 
00120 int emac_if_find_outgoing_msg(int receipt_number)
00121 {
00122     for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
00123         if (outgoing_msgs[i].length && outgoing_msgs[i].receipt_number == receipt_number) {
00124             return i;
00125         }
00126     }
00127     return -1;
00128 }
00129 
00130 void emac_if_free_outgoing_msg(int index)
00131 {
00132     outgoing_msgs[index].length = 0;
00133 }
00134 
00135 int emac_if_count_outgoing_msg(void)
00136 {
00137     int count = 0;
00138 
00139     for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
00140         if (outgoing_msgs[i].length) {
00141 
00142             if (!(outgoing_msgs[i].flags & RESPONSE_RECEIVED)) {
00143                 count++;
00144             }
00145         }
00146     }
00147 
00148     return count;
00149 }
00150 
00151 void emac_if_reset_outgoing_msg(void)
00152 {
00153     for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
00154         if (outgoing_msgs[i].length) {
00155             outgoing_msgs[i].length = 0;
00156         }
00157     }
00158 }
00159 
00160 int emac_if_add_outgoing_msg(int length)
00161 {
00162     for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
00163         if (!outgoing_msgs[i].length) {
00164             outgoing_msgs[i].receipt_number = 0;
00165             outgoing_msgs[i].length = length;
00166             outgoing_msgs[i].flags = 0;
00167             outgoing_msgs[i].lifetime = 10;
00168             return i;
00169         }
00170     }
00171     return -1;
00172 }
00173 
00174 void emac_if_set_outgoing_msg_receipt_num(int index, int receipt_number)
00175 {
00176     outgoing_msgs[index].receipt_number = receipt_number;
00177 }
00178 
00179 void emac_if_set_outgoing_msg_flags(int index, int flags)
00180 {
00181     outgoing_msgs[index].flags |= flags;
00182 }
00183 
00184 void emac_if_timeout_outgoing_msg(void)
00185 {
00186     for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
00187         if (outgoing_msgs[i].length) {
00188             if (outgoing_msgs[i].lifetime) {
00189                 outgoing_msgs[i].lifetime--;
00190                 if (outgoing_msgs[i].lifetime == 0) {
00191                     SET_ERROR_FLAGS(NO_RESPONSE);
00192                 }
00193             }
00194         }
00195     }
00196 }
00197 
00198 void emac_if_validate_outgoing_msg(void)
00199 {
00200     static char broadcast_resp_count = 0;
00201 
00202     for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
00203         if (outgoing_msgs[i].length) {
00204 
00205             if (outgoing_msgs[i].flags & RESPONSE_RECEIVED) {
00206 
00207                 int failure = outgoing_msgs[i].flags & (INVALID_LENGHT | INVALID_DATA);
00208 
00209                 if (failure) {
00210                     SET_ERROR_FLAGS(MSG_VALID_ERROR);
00211                 }
00212 
00213                 if (!(outgoing_msgs[i].flags & PRINTED)) {
00214                     if ((trace_level & TRACE_SUCCESS) || ((trace_level & TRACE_FAILURE) && failure)) {
00215                         printf("response: receipt number %i %s %s %s\r\n\r\n", outgoing_msgs[i].receipt_number,
00216                             outgoing_msgs[i].flags & INVALID_LENGHT ? "LENGTH INVALID" : "LENGTH OK",
00217                             outgoing_msgs[i].flags & INVALID_DATA ? "DATA INVALID" : "DATA OK",
00218                             outgoing_msgs[i].flags & BROADCAST ? "BROADCAST" : "UNICAST");
00219                         outgoing_msgs[i].flags |= PRINTED;
00220                     }
00221                 }
00222 
00223                 if (outgoing_msgs[i].flags & BROADCAST) {
00224                     outgoing_msgs[i].lifetime = 2;
00225                     broadcast_resp_count++;
00226                     if (broadcast_resp_count > 5) {
00227                         emac_if_free_outgoing_msg(i);
00228                     }
00229                 } else {
00230                     emac_if_free_outgoing_msg(i);
00231                 }
00232             }
00233 
00234             if (!outgoing_msgs[i].lifetime) {
00235                 if (!(outgoing_msgs[i].flags & RESPONSE_RECEIVED) && (trace_level & TRACE_FAILURE)) {
00236                     printf("NO RESPONSE: receipt number %i\r\n\r\n", outgoing_msgs[i].receipt_number);
00237                 }
00238                 emac_if_free_outgoing_msg(i);
00239             }
00240         }
00241     }
00242 }
00243 
00244 bool emac_if_update_reply_to_outgoing_msg(int receipt_number, int length, int invalid_data_index)
00245 {
00246     int32_t outgoing_msg_index = emac_if_find_outgoing_msg(receipt_number);
00247 
00248     if (outgoing_msg_index >= 0) {
00249         outgoing_msgs[outgoing_msg_index].flags |= RESPONSE_RECEIVED;
00250 
00251         if (outgoing_msgs[outgoing_msg_index].length < ETH_FRAME_MIN_LEN) {
00252             /* If length of the sent message is smaller than Ethernet minimum frame length, validates against
00253                minimum frame length or sent length (in case frame has been converted to be longer than minimum
00254                length does not validate length)  */
00255             if (length != ETH_FRAME_MIN_LEN && length != ETH_FRAME_PADD_LEN && outgoing_msgs[outgoing_msg_index].length != length && length < ETH_FRAME_MIN_LEN) {
00256                 outgoing_msgs[outgoing_msg_index].flags |= INVALID_LENGHT;
00257             }
00258         } else {
00259             if (outgoing_msgs[outgoing_msg_index].length != length) {
00260                 outgoing_msgs[outgoing_msg_index].flags |= INVALID_LENGHT;
00261             }
00262         }
00263         if (invalid_data_index && invalid_data_index < outgoing_msgs[outgoing_msg_index].length) {
00264             outgoing_msgs[outgoing_msg_index].flags |= INVALID_DATA;
00265         }
00266         return true;
00267     } else {
00268         return false;
00269     }
00270 }
00271 
00272 void emac_if_add_echo_server_addr(unsigned char *addr)
00273 {
00274     if (etc_mac_echo_server_free_index == ECHO_SERVER_COUNT) {
00275         return;
00276     }
00277 
00278     for (int i = 0; i < etc_mac_echo_server_free_index; i++) {
00279         if (memcmp(&eth_mac_echo_server_addr[i][0], addr, ETH_MAC_ADDR_LEN) == 0) {
00280             return;
00281         }
00282     }
00283 
00284     memcpy(&eth_mac_echo_server_addr[etc_mac_echo_server_free_index][0], addr, ETH_MAC_ADDR_LEN);
00285     etc_mac_echo_server_free_index++;
00286 }
00287 
00288 int emac_if_count_echo_server_addr(void)
00289 {
00290     return etc_mac_echo_server_free_index;
00291 }
00292 
00293 unsigned char *emac_if_get_echo_server_addr(int index)
00294 {
00295     if (index < etc_mac_echo_server_free_index) {
00296         return &eth_mac_echo_server_addr[index][0];
00297     }
00298 
00299     return 0;
00300 }
00301 
00302 void emac_if_set_error_flags(unsigned int error_flags_value)
00303 {
00304     error_flags |= error_flags_value;
00305 
00306     if (error_flags_value & NO_RESPONSE) {
00307         no_response_cnt++;
00308     }
00309 }
00310 
00311 unsigned int emac_if_get_error_flags(void)
00312 {
00313     int error_flags_value = error_flags;
00314 
00315     // Indicate no response error only if more than three messages are lost
00316     if (error_flags_value & NO_RESPONSE) {
00317         if (no_response_cnt < 3) {
00318             error_flags_value &= ~NO_RESPONSE;
00319         }
00320     }
00321 
00322     return error_flags_value;
00323 }
00324 
00325 void emac_if_reset_error_flags(unsigned int error_flags_value)
00326 {
00327     error_flags &= ~error_flags_value;
00328     if (error_flags_value & NO_RESPONSE) {
00329         no_response_cnt = 0;
00330     }
00331 }
00332 
00333 void emac_if_reset_all_error_flags(void)
00334 {
00335     error_flags = 0;
00336     no_response_cnt = 0;
00337 }
00338 
00339 void emac_if_print_error_flags(void)
00340 {
00341    int error_flags_value = emac_if_get_error_flags();
00342 
00343    char no_resp_message[50];
00344    if (error_flags_value & NO_RESPONSE) {
00345        snprintf(no_resp_message, 50, "no response from echo server, counter: %i", no_response_cnt);
00346    } else if (no_response_cnt > 0) {
00347        printf("no response from echo server, counter: %i\r\n\r\n", no_response_cnt);
00348    }
00349 
00350    printf("test result: %s%s%s%s%s%s\r\n\r\n",
00351        error_flags_value ? "Test FAILED, reason: ": "PASS",
00352        error_flags_value & TEST_FAILED ? "test failed ": "",
00353        error_flags_value & MSG_VALID_ERROR ? "message content validation error ": "",
00354        error_flags_value & OUT_OF_MSG_DATA ? "out of message validation data storage ": "",
00355        error_flags_value & NO_FREE_MEM_BUF ? "no free memory buffers ": "",
00356        error_flags_value & NO_RESPONSE ? no_resp_message: "");
00357 }
00358 
00359 void emac_if_set_trace_level(char trace_level_value)
00360 {
00361     trace_level = trace_level_value;
00362 }
00363 
00364 char emac_if_get_trace_level(void)
00365 {
00366     return trace_level;
00367 }
00368 
00369 void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, unsigned char *data)
00370 {
00371     int line_len = 0;
00372 
00373     for (int i = 0; i < len; i++) {
00374        if ((line_len % 14) == 0) {
00375            if (line_len != 0) {
00376                printf("\r\n");
00377            }
00378            printf("%s %06x", prefix, line_len);
00379        }
00380        line_len++;
00381        printf(" %02x", data[i]);
00382     }
00383     printf("\r\n\r\n");
00384 }
00385 
00386 void emac_if_set_all_multicast(bool all)
00387 {
00388     emac_if_get()->set_all_multicast(all);
00389 }
00390 
00391 void emac_if_add_multicast_group(uint8_t *address)
00392 {
00393     emac_if_get()->add_multicast_group(address);
00394 }
00395 
00396 void emac_if_set_output_memory(bool memory)
00397 {
00398     output_memory = memory;
00399 }
00400 
00401 void emac_if_set_input_memory(bool memory)
00402 {
00403     input_memory = memory;
00404 
00405     emac_if_set_memory(memory);
00406 }
00407 
00408 void emac_if_check_memory(bool output)
00409 {
00410     if (output) {
00411         emac_if_set_memory(output_memory);
00412     } else {
00413         emac_if_set_memory(input_memory);
00414     }
00415 }
00416 
00417 void emac_if_set_memory(bool memory)
00418 {
00419     static bool memory_value = true;
00420     if (memory_value != memory ) {
00421         memory_value = memory;
00422         EmacTestMemoryManager *mem_mngr = emac_m_mngr_get();
00423         mem_mngr->set_memory_available(memory);
00424     }
00425 }
00426 
00427 void emac_if_link_state_change_cb(bool up)
00428 {
00429     if (up) {
00430         worker_loop_event.post(LINK_UP);
00431     } else {
00432         worker_loop_event.post(LINK_DOWN);
00433     }
00434 }
00435 
00436 void emac_if_link_input_cb(void *buf)
00437 {
00438     link_input_event.post(buf);
00439 }
00440 
00441 static void link_input_event_cb(void *buf)
00442 {
00443     int length = emac_m_mngr_get()->get_total_len(buf);
00444 
00445     if (length >= ETH_FRAME_HEADER_LEN) {
00446         // Ethernet input frame
00447         unsigned char eth_input_frame_data[ETH_FRAME_HEADER_LEN];
00448         memset(eth_input_frame_data, 0, ETH_FRAME_HEADER_LEN);
00449 
00450         int invalid_data_index = emac_if_memory_buffer_read(buf, eth_input_frame_data);
00451 
00452         if (eth_input_frame_data[12] == 0x90 && eth_input_frame_data[13] == 0x00) {
00453             unsigned char eth_output_frame_data[ETH_FRAME_HEADER_LEN];
00454             int receipt_number;
00455 
00456             ctp_function function = emac_if_ctp_header_handle(eth_input_frame_data, eth_output_frame_data, emac_if_get_hw_addr(), &receipt_number);
00457 
00458             if (function == CTP_REPLY) {
00459                 // If reply has valid receipt number
00460                 if (emac_if_update_reply_to_outgoing_msg(receipt_number, length, invalid_data_index)) {
00461                     // Checks received messages for errors
00462                     emac_if_validate_outgoing_msg();
00463                     // Removes not replied retry entries if any
00464                     emac_if_reset_outgoing_msg();
00465                     // Removes retry entries no response flags
00466                     emac_if_reset_error_flags(NO_RESPONSE);
00467                     // Calls test loop
00468                     worker_loop_event_queue.call(current_test_step_cb_fnc, INPUT);
00469                 }
00470 #if MBED_CONF_APP_ECHO_SERVER
00471             // Echoes only if configured as echo server
00472             } else if (function == CTP_FORWARD) {
00473                 emac_if_memory_buffer_write(buf, eth_output_frame_data, false);
00474                 emac_if_get()->link_out(buf);
00475                 buf = 0;
00476 #endif
00477             }
00478 
00479             emac_if_add_echo_server_addr(&eth_input_frame_data[6]);
00480 
00481             if (trace_level & TRACE_ETH_FRAMES) {
00482                  printf("INP> LEN %i\r\n\r\n", length);
00483                  const char trace_type[] = "INP>";
00484                  emac_if_trace_to_ascii_hex_dump(trace_type, ETH_FRAME_HEADER_LEN, eth_input_frame_data);
00485             }
00486         }
00487     }
00488 
00489     if (buf) {
00490         emac_m_mngr_get()->free(buf);
00491     }
00492 }
00493 
00494 
00495 #if defined (__ICCARM__)
00496 #pragma location = ".ethusbram"
00497 #endif
00498 ETHMEM_SECTION static unsigned char thread_stack[2048];
00499 
00500 void worker_loop(void);
00501 
00502 void worker_loop_init(void)
00503 {
00504     static rtos::Thread worker_loop_thread(osPriorityNormal, 2048, thread_stack);
00505 
00506     static bool init_done = false;
00507 
00508     if (!init_done) {
00509         if (worker_loop_thread.get_state() == Thread::Deleted) {
00510             worker_loop_thread.start(mbed::callback(&worker_loop));
00511         }
00512         init_done = true;
00513     }
00514 }
00515 
00516 void worker_loop_start(void (*test_step_cb_fnc)(int opt), int timeout)
00517 {
00518     current_test_step_cb_fnc = test_step_cb_fnc;
00519 
00520     int test_step_cb_timer = worker_loop_event_queue.call_every(timeout, test_step_cb_fnc, TIMEOUT);
00521     int timeout_outgoing_msg_timer = worker_loop_event_queue.call_every(1000, emac_if_timeout_outgoing_msg);
00522 
00523     int validate_outgoing_msg_timer = 0;
00524     if (timeout > 500) {
00525         // For long test step callback timeouts validates messages also between callback timeouts
00526         validate_outgoing_msg_timer = worker_loop_event_queue.call_every(200, emac_if_validate_outgoing_msg);
00527     }
00528 
00529 #if MBED_CONF_APP_ECHO_SERVER
00530     worker_loop_semaphore.wait();
00531 #else
00532     worker_loop_semaphore.wait(600 * SECOND_TO_MS);
00533 #endif
00534 
00535     worker_loop_event_queue.cancel(test_step_cb_timer);
00536     worker_loop_event_queue.cancel(timeout_outgoing_msg_timer);
00537     if (validate_outgoing_msg_timer) {
00538         worker_loop_event_queue.cancel(validate_outgoing_msg_timer);
00539     }
00540 
00541     osDelay(1000);
00542 }
00543 
00544 static void worker_loop_event_cb(int event)
00545 {
00546     if (event == LINK_UP) {
00547         link_up = true;
00548         link_status_semaphore.release();
00549     }
00550 
00551     if (event == LINK_DOWN) {
00552         link_up = false;
00553     }
00554 }
00555 
00556 void worker_loop_link_up_wait(void)
00557 {
00558     if (!link_up) {
00559         link_status_semaphore.wait();
00560     }
00561 }
00562 
00563 void worker_loop_end(void)
00564 {
00565     worker_loop_semaphore.release();
00566 }
00567 
00568 void worker_loop(void)
00569 {
00570     worker_loop_event_queue.dispatch_forever();
00571 }
00572 
00573 unsigned char *emac_if_get_own_addr(void)
00574 {
00575     return (emac_if_get_hw_addr());
00576 }
00577 
00578 int emac_if_get_mtu_size()
00579 {
00580     return eth_mtu_size;
00581 }
00582 
00583 void emac_if_set_mtu_size(int mtu_size)
00584 {
00585     eth_mtu_size = mtu_size;
00586 }
00587 
00588 #endif