takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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