Tests for wifi emac interface

pull/5435/head
Mika Leppänen 2017-11-03 15:54:22 +02:00
parent a519b8449b
commit 5c15819130
15 changed files with 1343 additions and 0 deletions

View File

@ -0,0 +1,28 @@
# Description
This document describes how to run EMAC tests. The EMAC test cases are made using Ethernet Configuration Testing Protocol (CTP). To run the tests, one device in the Ethernet segment needs to be configured to be a CTP echo server. The devices running the test cases, use the echo server to forward the CTP Ethernet frames back.
# Configuring CTP echo server
A device can be configured to be a CTP echo server by enabling `echo-server` setting in the test environment's application `json` file. When device is configured to be a CTP echo server, it starts to forward CTP messages automatically after power up and will continue forwarding until power down.
# Test cases
## EMAC interface initialise
Initializes EMAC interface driver.
For WLAN installs test case so that it can intercept incoming Ethernet messages from the WLAN driver. Incoming CTP frames are handed by the test case and other frames are forwarded to the LWIP stack.
## EMAC interface broadcast
Sends three 100 byte CTP broadcast messages, waits for three seconds and sends three 50 byte CTP broadcast messages. Listens for the CTP echo server responses and stores the addresses of the echo servers if replies are received. The test case will pass if there are no responses from echo server, but further test cases will be skipped.
## EMAC interface unicast
Sends three CTP unicast messages to the CTP echo server. Verifies that all are replied.
## EMAC interface unicast frame length
Sends CTP unicast messages with Ethernet message length from 100 bytes to maximum. Verifies that all are replied.

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "greentea-client/test_env.h"
#include "unity/unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "mbed.h"
#include "lwip/opt.h" /* ETH_PAD_SIZE */
#include "emac_stack_mem.h"
#include "emac_api.h"
#include "emac_tests.h"
#include "emac_ctp.h"
#include "emac_initialize.h"
#include "emac_util.h"
#include "emac_membuf.h"
using namespace utest::v1;
// Unique identifier for message
static int receipt_number = 0;
static int emac_if_ctp_header_build(unsigned char *eth_frame, const unsigned char *dest_addr, const unsigned char *origin_addr, const unsigned char *forward_addr)
{
memcpy(&eth_frame[0], dest_addr, 6);
memcpy(&eth_frame[6], origin_addr, 6);
eth_frame[12] = 0x90; /* loop back */
eth_frame[13] = 0x00;
eth_frame[14] = 0x00; /* skip count */
eth_frame[15] = 0x00;
eth_frame[16] = 0x02; /* function, forward */
eth_frame[17] = 0x00;
memcpy(&eth_frame[18], forward_addr, 6);
eth_frame[24] = 0x01; /* function, reply */
eth_frame[25] = 0x00;
receipt_number++;
eth_frame[26] = receipt_number; /* receipt number */
eth_frame[27] = receipt_number >> 8;
return receipt_number;
}
ctp_function emac_if_ctp_header_handle(unsigned char *eth_input_frame, unsigned char *eth_output_frame, unsigned char *origin_addr, int *receipt_number)
{
if (eth_input_frame[12] != 0x90 || eth_input_frame[13] != 0x00) {
return CTP_NONE;
}
int skip_count = eth_input_frame[15] << 8 | eth_input_frame[14];
unsigned char *ethernet_ptr = &eth_input_frame[16] + skip_count;
int function = ethernet_ptr[1] << 8 | ethernet_ptr[0];
ethernet_ptr += 2;
// Forward
if (function == 0x0002) {
memcpy(eth_output_frame, eth_input_frame, ETH_FRAME_HEADER_LEN);
// Update skip count
skip_count += 8;
eth_output_frame[14] = skip_count;
eth_output_frame[15] = skip_count >> 8;
// Set forward address to destination address
memcpy(&eth_output_frame[0], ethernet_ptr, 6);
// Copy own address to origin
memcpy(&eth_output_frame[6], origin_addr, 6);
return CTP_FORWARD;
// reply
} else if (function == 0x0001) {
*receipt_number = ethernet_ptr[1] << 8 | ethernet_ptr[0];
return CTP_REPLY;
}
return CTP_NONE;
}
void emac_if_ctp_msg_build(int eth_frame_len, const unsigned char *dest_addr, const unsigned char *origin_addr, const unsigned char *forward_addr)
{
if (eth_frame_len < ETH_FRAME_HEADER_LEN) {
eth_frame_len = ETH_FRAME_HEADER_LEN;
}
printf("message sent %x:%x:%x:%x:%x:%x\r\n\r\n", dest_addr[0], dest_addr[1], dest_addr[2], dest_addr[3], dest_addr[4], dest_addr[5]);
int outgoing_msg_index = emac_if_add_outgoing_msg(eth_frame_len);
if (outgoing_msg_index < 0) {
SET_ERROR_FLAGS(OUT_OF_MSG_DATA);
return;
}
emac_stack_mem_chain_t *mem_chain_p = emac_stack_mem_alloc(0, eth_frame_len + ETH_PAD_SIZE, 0);
if (!mem_chain_p) {
SET_ERROR_FLAGS(NO_FREE_MEM_BUF);
emac_if_free_outgoing_msg(outgoing_msg_index);
return;
}
if (memcmp(dest_addr, eth_mac_broadcast_addr, 6) == 0) {
emac_if_set_outgoing_msg_flags(outgoing_msg_index, BROADCAST);
}
unsigned char eth_output_frame_data[ETH_FRAME_HEADER_LEN];
int receipt_number = emac_if_ctp_header_build(eth_output_frame_data, dest_addr, origin_addr, forward_addr);
emac_if_set_outgoing_msg_receipt_num(outgoing_msg_index, receipt_number);
emac_if_memory_buffer_write(mem_chain_p, eth_output_frame_data, true);
//emac_if->ops.link_out(hw_driver, mem_chain_p);
emac_if_get()->ops.link_out(emac_if_get(), mem_chain_p);
emac_stack_mem_free(0, mem_chain_p);
}
#endif

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef EMAC_CTP_H
#define EMAC_CTP_H
enum ctp_function {
CTP_NONE,
CTP_FORWARD,
CTP_REPLY
};
ctp_function emac_if_ctp_header_handle(unsigned char *eth_input_frame, unsigned char *eth_output_frame, unsigned char *origin_addr, int *receipt_number);
void emac_if_ctp_msg_build(int eth_frame_len, const unsigned char *dest_addr, const unsigned char *origin_addr, const unsigned char *forward_addr);
void emac_if_ctp_reply_handle(int lenght, int invalid_data_index);
#endif /* EMAC_CTP_H */

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef EMAC_INITIALIZE_H
#define EMAC_INITIALIZE_H
uint8_t *emac_if_get_hw_addr(void);
emac_interface_t *emac_if_get(void);
#endif /* EMAC_INITIALIZE_H */

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "lwip/opt.h" /* ETH_PAD_SIZE */
#include "emac_api.h"
#include "emac_stack_mem.h"
#include "emac_membuf.h"
#include "emac_util.h"
int emac_if_memory_buffer_read(emac_stack_mem_chain_t *mem_chain_p, unsigned char *eth_frame)
{
int eth_frame_index = 0;
int invalid_data_index = 0;
int index = ETH_PAD_SIZE;
for (emac_stack_mem_t *mem_p = emac_stack_mem_chain_dequeue(0, &mem_chain_p); mem_p != NULL; mem_p = emac_stack_mem_chain_dequeue(0, &mem_chain_p)) {
unsigned char *buf_payload = (unsigned char *) emac_stack_mem_ptr(0, mem_p);
int buf_payload_len = emac_stack_mem_len(0, mem_p);
for (; index < buf_payload_len; index++) {
if (eth_frame_index < ETH_FRAME_HEADER_LEN) {
eth_frame[eth_frame_index] = buf_payload[index];
} else {
if (buf_payload[index] != (uint8_t) eth_frame_index) {
invalid_data_index = eth_frame_index;
break;
}
}
eth_frame_index++;
}
index = 0;
}
return invalid_data_index;
}
void emac_if_memory_buffer_write(emac_stack_mem_chain_t *mem_chain_p, unsigned char *eth_frame, bool write_data)
{
int eth_frame_index = 0;
int index = ETH_PAD_SIZE;
for (emac_stack_mem_t *mem_p = emac_stack_mem_chain_dequeue(0, &mem_chain_p); mem_p != NULL; mem_p = emac_stack_mem_chain_dequeue(0, &mem_chain_p)) {
unsigned char *buf_payload = (unsigned char *) emac_stack_mem_ptr(0, mem_p);
int buf_payload_len = emac_stack_mem_len(0, mem_p);
for (; index < buf_payload_len; index++) {
if (eth_frame_index < ETH_FRAME_HEADER_LEN) {
buf_payload[index] = eth_frame[eth_frame_index];
} else if (write_data) {
buf_payload[index] = (char) eth_frame_index;
} else {
break;
}
eth_frame_index++;
}
index = 0;
}
}
#endif

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef EMAC_MEMBUF_H
#define EMAC_MEMBUF_H
int emac_if_memory_buffer_read(emac_stack_mem_chain_t *mem_chain_p, unsigned char *eth_frame);
void emac_if_memory_buffer_write(emac_stack_mem_chain_t *mem_chain_p, unsigned char *eth_frame, bool write_data);
#endif /* EMAC_MEMBUF_H */

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "emac_tests.h"
#include "emac_util.h"
#include "emac_ctp.h"
using namespace utest::v1;
void test_emac_broadcast_cb(void)
{
emac_if_validate_outgoing_msg();
static int counter = 0;
// Send three broadcast
if (counter < 3) {
emac_if_ctp_msg_build(100, eth_mac_broadcast_addr, emac_if_get_own_addr(), emac_if_get_own_addr());
counter++;
} else if (counter < 6) {
counter++;
} else if (counter < 9) {
emac_if_ctp_msg_build(50, eth_mac_broadcast_addr, emac_if_get_own_addr(), emac_if_get_own_addr());
counter++;
} else if (counter < 12) {
counter++;
} else if (counter == 12) {
emac_if_reset_outgoing_msg();
// ignore errors since just probing
RESET_ERROR_FLAGS;
#if MBED_CONF_APP_ECHO_SERVER
printf("echo server started successfully\r\n\r\n");
counter = 255;
#else
worker_loop_end();
#endif
}
}
void test_emac_broadcast(void)
{
RESET_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);
worker_loop_start(test_emac_broadcast_cb, 10 * SECOND_TO_MS);
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);
RESET_OUTGOING_MSG_DATA;
}
#endif

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "inttypes.h"
#if MBED_CONF_APP_TEST_WIFI
#ifdef TARGET_UBLOX_EVK_ODIN_W2
#include "wifi_emac_api.h"
#include "OdinWiFiInterface.h"
#endif
#ifdef TARGET_REALTEK_RTL8195AM
#include "rtw_emac.h"
#include "RTWInterface.h"
#endif
#endif
#include "emac_api.h"
#include "emac_tests.h"
#include "emac_util.h"
using namespace utest::v1;
static unsigned char eth_mac_addr[ETH_MAC_ADDR_LEN];
static char emac_if_link_state_change_cb_data[] = "link_state_change_cb_data";
static char emac_if_link_input_cb_data[] = "link_input_cb_data";
static bool emac_if_init(void);
void test_emac_initialize()
{
#if MBED_CONF_APP_TEST_WIFI
static WiFiInterface *wifi;
#ifdef TARGET_UBLOX_EVK_ODIN_W2
wifi = new OdinWiFiInterface;
#endif
#ifdef TARGET_REALTEK_RTL8195AM
wifi = new RTWInterface;
#endif
#if MBED_CONF_APP_WIFI_SCAN
WiFiAccessPoint ap[30];
int size = wifi->scan(ap, 30);
for (int i=0; i<size; i++) {
const char *ssid = ap[i].get_ssid();
nsapi_security_t security = ap[i].get_security();
int8_t rssi = ap[i].get_rssi();
char ch = ap[i].get_channel();
printf("BS %i\r\n", i);
printf("ssid %s\r\n", ssid);
printf("security %i\r\n", security);
printf("rssi %i\r\n", rssi);
printf("ch %i\r\n\r\n", ch);
}
#endif
wifi->set_credentials(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, MBED_CONF_APP_WIFI_SECURITY);
wifi->connect();
const char *ip_addr = wifi->get_ip_address();
printf("connected IP %s\r\n\r\n", ip_addr);
#endif
TEST_ASSERT(emac_if_init());
}
unsigned char *emac_if_get_hw_addr(void)
{
return &eth_mac_addr[0];
}
emac_interface_t *emac_if_get(void)
{
#if MBED_CONF_APP_TEST_WIFI
#ifdef TARGET_UBLOX_EVK_ODIN_W2
return wifi_emac_get_interface();
#endif
#ifdef TARGET_REALTEK_RTL8195AM
return wlan_emac_init_interface();
#endif
#else
return 0;
#endif
}
static bool emac_if_init(void)
{
emac_interface_t *emac_if = emac_if_get();
emac_if->ops.set_link_input_cb(emac_if, emac_if_link_input_cb, emac_if_link_input_cb_data);
emac_if->ops.set_link_state_cb(emac_if, emac_if_link_state_change_cb, emac_if_link_state_change_cb_data);
int hwaddr_len = emac_if->ops.get_hwaddr_size(emac_if);
printf("emac hwaddr length %i\r\n\r\n", hwaddr_len);
if (hwaddr_len == 6) {
emac_if->ops.get_hwaddr(emac_if, eth_mac_addr);
printf("emac hwaddr %x:%x:%x:%x:%x:%x\r\n\r\n", eth_mac_addr[0],eth_mac_addr[1],eth_mac_addr[2],eth_mac_addr[3],eth_mac_addr[4],eth_mac_addr[5]);
}
int mtu = emac_if->ops.get_mtu_size(emac_if);
printf("emac mtu %i\r\n\r\n", mtu);
char hw_name[11];
emac_if->ops.get_ifname(emac_if, hw_name, 10);
printf("emac if name %s\r\n\r\n", hw_name);
if (!emac_if->ops.power_up(emac_if)) {
return false;
}
return true;
}
#endif

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "emac_tests.h"
#include "emac_util.h"
#include "emac_ctp.h"
using namespace utest::v1;
void test_emac_unicast_cb(void)
{
emac_if_validate_outgoing_msg();
static uint8_t counter = 0;
// Send three unicast
if (counter < 3) {
emac_if_ctp_msg_build(100, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr());
}
// End test
if (counter > 10) {
worker_loop_end();
if (emac_if_count_outgoing_msg() != 0) {
SET_ERROR_FLAGS(TEST_FAILED);
}
}
counter++;
}
void test_emac_unicast()
{
RESET_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);
if (emac_if_count_echo_server_addr()) {
worker_loop_start(test_emac_unicast_cb, 1 * SECOND_TO_MS);
}
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);
RESET_OUTGOING_MSG_DATA;
}
#endif

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "emac_tests.h"
#include "emac_util.h"
#include "emac_ctp.h"
using namespace utest::v1;
void test_emac_unicast_frame_len_cb(void)
{
emac_if_validate_outgoing_msg();
static uint32_t counter = 0;
// Send unicast to echo server
if (counter < 16) {
static uint32_t msg_len = 0;
emac_if_ctp_msg_build(msg_len, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr());
msg_len += 100;
if (msg_len > 1514) {
msg_len = 1514;
}
}
if (counter > 18) {
if (emac_if_count_outgoing_msg() == 0) {
worker_loop_end();
}
}
counter++;
}
void test_emac_unicast_frame_len()
{
RESET_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SUCCESS | TRACE_FAILURE);
if (emac_if_count_echo_server_addr()) {
worker_loop_start(test_emac_unicast_frame_len_cb, 1 * SECOND_TO_MS);
}
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);
RESET_OUTGOING_MSG_DATA;
}
#endif

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef EMAC_TESTS_H
#define EMAC_TESTS_H
void test_emac_initialize();
void test_emac_broadcast();
void test_emac_unicast();
void test_emac_unicast_frame_len();
#endif /* EMAC_TESTS_H */

View File

@ -0,0 +1,436 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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 "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
extern "C" { // netif input
#include "tcpip.h"
}
#include "emac_api.h"
#include "emac_stack_mem.h"
#include "emac_tests.h"
#include "emac_initialize.h"
#include "emac_util.h"
#include "emac_membuf.h"
#include "emac_ctp.h"
using namespace utest::v1;
typedef struct {
int length;
int receipt_number;
unsigned short flags;
unsigned short lifetime;
} outgoing_msg_t;
#define ECHO_SERVER_COUNT 5
#define OUTGOING_MSG_COUNT 100
// Event flags
#define LINK_UP 0x01
#define LINK_DOWN 0x02
// Hook to lwip input function
extern struct netif *netif_list;
// Broadcast address
const unsigned char eth_mac_broadcast_addr[ETH_MAC_ADDR_LEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
// Event queue
static EventQueue worker_loop_event_queue;
static void worker_loop_event_cb(int event);
static Event<void(int)> worker_loop_event(&worker_loop_event_queue, worker_loop_event_cb);
static void link_input_event_cb(emac_stack_mem_chain_t *mem_chain_p);
static Event<void(emac_stack_mem_chain_t *)> link_input_event(&worker_loop_event_queue, link_input_event_cb);
// Found echo server addresses
static unsigned char eth_mac_echo_server_addr[ECHO_SERVER_COUNT][ETH_MAC_ADDR_LEN];
static int etc_mac_echo_server_free_index = 0;
// Outgoing messages
static outgoing_msg_t outgoing_msgs[OUTGOING_MSG_COUNT];
static unsigned int trace_level = 0;
static unsigned int error_flags = 0;
static unsigned int no_response_cnt = 0;
int emac_if_find_outgoing_msg(int receipt_number)
{
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (outgoing_msgs[i].length && outgoing_msgs[i].receipt_number == receipt_number) {
return i;
}
}
return -1;
}
void emac_if_free_outgoing_msg(int index)
{
outgoing_msgs[index].length = 0;
}
int emac_if_count_outgoing_msg(void)
{
int count = 0;
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (outgoing_msgs[i].length) {
count++;
}
}
return count;
}
void emac_if_reset_outgoing_msg(void)
{
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (outgoing_msgs[i].length) {
outgoing_msgs[i].length = 0;
}
}
}
int emac_if_add_outgoing_msg(int length)
{
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (!outgoing_msgs[i].length) {
outgoing_msgs[i].receipt_number = 0;
outgoing_msgs[i].length = length;
outgoing_msgs[i].flags = 0;
outgoing_msgs[i].lifetime = 10;
return i;
}
}
return -1;
}
void emac_if_set_outgoing_msg_receipt_num(int index, int receipt_number)
{
outgoing_msgs[index].receipt_number = receipt_number;
}
void emac_if_set_outgoing_msg_flags(int index, int flags)
{
outgoing_msgs[index].flags |= flags;
}
void emac_if_timeout_outgoing_msg(void)
{
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (outgoing_msgs[i].length) {
if (outgoing_msgs[i].lifetime) {
outgoing_msgs[i].lifetime--;
if (outgoing_msgs[i].lifetime == 0) {
SET_ERROR_FLAGS(NO_RESPONSE);
}
}
}
}
}
void emac_if_validate_outgoing_msg(void)
{
static char broadcast_resp_count = 0;
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (outgoing_msgs[i].length) {
if (outgoing_msgs[i].flags & RESPONSE_RECEIVED) {
int failure = outgoing_msgs[i].flags & (INVALID_LENGHT | INVALID_DATA);
if (failure) {
SET_ERROR_FLAGS(MSG_VALID_ERROR);
}
if (!(outgoing_msgs[i].flags & PRINTED)) {
if ((trace_level & TRACE_SUCCESS) || ((trace_level & TRACE_FAILURE) && failure)) {
printf("response: receipt number %i %s %s %s\r\n\r\n", outgoing_msgs[i].receipt_number,
outgoing_msgs[i].flags & INVALID_LENGHT ? "LENGTH INVALID" : "LENGTH OK",
outgoing_msgs[i].flags & INVALID_DATA ? "DATA INVALID" : "DATA OK",
outgoing_msgs[i].flags & BROADCAST ? "BROADCAST" : "UNICAST");
outgoing_msgs[i].flags |= PRINTED;
}
}
if (outgoing_msgs[i].flags & BROADCAST) {
outgoing_msgs[i].lifetime = 2;
broadcast_resp_count++;
if (broadcast_resp_count > 5) {
emac_if_free_outgoing_msg(i);
}
} else {
emac_if_free_outgoing_msg(i);
}
}
if (!outgoing_msgs[i].lifetime) {
if (!(outgoing_msgs[i].flags & RESPONSE_RECEIVED) && (trace_level & TRACE_FAILURE)) {
printf("NO RESPONSE: receipt number %i\r\n\r\n", outgoing_msgs[i].receipt_number);
}
emac_if_free_outgoing_msg(i);
}
}
}
}
void emac_if_update_reply_to_outgoing_msg(int receipt_number, int lenght, int invalid_data_index)
{
int32_t outgoing_msg_index = emac_if_find_outgoing_msg(receipt_number);
if (outgoing_msg_index >= 0) {
outgoing_msgs[outgoing_msg_index].flags |= RESPONSE_RECEIVED;
#if MBED_CONF_APP_TEST_ETHERNET
if (outgoing_msgs[outgoing_msg_index].length < ETH_FRAME_MIN_LEN) {
if (lenght != ETH_FRAME_MIN_LEN) {
outgoing_msgs[outgoing_msg_index].flags |= INVALID_LENGHT;
}
} else {
#endif
if (outgoing_msgs[outgoing_msg_index].length != lenght) {
outgoing_msgs[outgoing_msg_index].flags |= INVALID_LENGHT;
}
#if MBED_CONF_APP_TEST_ETHERNET
}
#endif
if (invalid_data_index && invalid_data_index < outgoing_msgs[outgoing_msg_index].length) {
outgoing_msgs[outgoing_msg_index].flags |= INVALID_DATA;
}
}
}
void emac_if_add_echo_server_addr(unsigned char *addr)
{
if (etc_mac_echo_server_free_index == ECHO_SERVER_COUNT) {
return;
}
for (int i = 0; i < etc_mac_echo_server_free_index; i++) {
if (memcmp(&eth_mac_echo_server_addr[i][0], addr, ETH_MAC_ADDR_LEN) == 0) {
return;
}
}
memcpy(&eth_mac_echo_server_addr[etc_mac_echo_server_free_index][0], addr, ETH_MAC_ADDR_LEN);
etc_mac_echo_server_free_index++;
}
int emac_if_count_echo_server_addr(void)
{
return etc_mac_echo_server_free_index;
}
unsigned char *emac_if_get_echo_server_addr(int index)
{
if (index < etc_mac_echo_server_free_index) {
return &eth_mac_echo_server_addr[index][0];
}
return 0;
}
void emac_if_set_error_flags(unsigned int error_flags_value)
{
error_flags |= error_flags_value;
if (error_flags_value & NO_RESPONSE) {
no_response_cnt++;
}
}
unsigned int emac_if_get_error_flags(void)
{
int error_flags_value = error_flags;
// Indicate no response error only if more than three messages are lost
if (error_flags_value & NO_RESPONSE) {
if (no_response_cnt < 3) {
error_flags_value &= ~NO_RESPONSE;
}
}
return error_flags_value;
}
void emac_if_reset_error_flags(void)
{
error_flags = 0;
no_response_cnt = 0;
}
void emac_if_print_error_flags(void)
{
int error_flags_value = emac_if_get_error_flags();
char no_resp_message[50];
if (error_flags_value & NO_RESPONSE) {
snprintf(no_resp_message, 50, "no response from echo server, counter: %i", no_response_cnt);
} else if (no_response_cnt > 0) {
printf("no response from echo server, counter: %i\r\n\r\n", no_response_cnt);
}
printf("test result: %s%s%s%s%s%s\r\n\r\n",
error_flags_value ? "Test FAILED, reason: ": "PASS",
error_flags_value & TEST_FAILED ? "test failed ": "",
error_flags_value & MSG_VALID_ERROR ? "message content validation error ": "",
error_flags_value & OUT_OF_MSG_DATA ? "out of message validation data storage ": "",
error_flags_value & NO_FREE_MEM_BUF ? "no free memory buffers ": "",
error_flags_value & NO_RESPONSE ? no_resp_message: "");
}
void emac_if_set_trace_level(char trace_level_value)
{
trace_level = trace_level_value;
}
void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, unsigned char *data)
{
int line_len = 0;
for (int i = 0; i < len; i++) {
if ((line_len % 14) == 0) {
if (line_len != 0) {
printf("\r\n");
}
printf("%s %06x", prefix, line_len);
}
line_len++;
printf(" %02x", data[i]);
}
printf("\r\n\r\n");
}
void emac_if_link_state_change_cb(void *data, bool up)
{
if (up) {
worker_loop_event.post(LINK_UP);
} else {
worker_loop_event.post(LINK_DOWN);
}
}
void emac_if_link_input_cb(void *data, emac_stack_mem_chain_t *mem_chain_p)
{
link_input_event.post(mem_chain_p);
}
static void link_input_event_cb(emac_stack_mem_chain_t *mem_chain_p)
{
int lenght = emac_stack_mem_len(0, mem_chain_p);
if (lenght >= ETH_FRAME_HEADER_LEN) {
// Ethernet input frame
unsigned char eth_input_frame_data[ETH_FRAME_HEADER_LEN];
memset(eth_input_frame_data, 0, ETH_FRAME_HEADER_LEN);
int invalid_data_index = emac_if_memory_buffer_read(mem_chain_p, eth_input_frame_data);
if (eth_input_frame_data[12] == 0x90 && eth_input_frame_data[13] == 0x00) {
unsigned char eth_output_frame_data[ETH_FRAME_HEADER_LEN];
int receipt_number;
ctp_function function = emac_if_ctp_header_handle(eth_input_frame_data, eth_output_frame_data, emac_if_get_hw_addr(), &receipt_number);
if (function == CTP_REPLY) {
emac_if_update_reply_to_outgoing_msg(receipt_number, lenght, invalid_data_index);
#if MBED_CONF_APP_ECHO_SERVER
// Echoes only if configured as echo server
} else if (function == CTP_FORWARD) {
emac_if_memory_buffer_write(mem_chain_p, eth_output_frame_data, false);
emac_if_get()->ops.link_out(emac_if_get(), mem_chain_p);
#endif
}
emac_if_add_echo_server_addr(&eth_input_frame_data[6]);
emac_stack_mem_free(0, mem_chain_p);
if (trace_level & TRACE_ETH_FRAMES) {
printf("LEN %i\r\n\r\n", lenght);
const char trace_type[] = "INP>";
emac_if_trace_to_ascii_hex_dump(trace_type, ETH_FRAME_HEADER_LEN, eth_input_frame_data);
}
return;
}
}
// Forward other than CTP frames to lwip
struct netif *netif;
/* loop through netif's */
netif = netif_list;
if (netif != NULL) {
struct pbuf *p = (struct pbuf *)mem_chain_p;
/* pass all packets to ethernet_input, which decides what packets it supports */
if (netif->input(p, netif) != ERR_OK) {
emac_stack_mem_free(0, mem_chain_p);
}
} else {
emac_stack_mem_free(0, mem_chain_p);
}
}
void worker_loop_start(void (*test_step_cb_fnc)(void), int timeout)
{
int test_step_cb_timer = worker_loop_event_queue.call_every(timeout, test_step_cb_fnc);
int timeout_outgoing_msg_timer = worker_loop_event_queue.call_every(1000, emac_if_timeout_outgoing_msg);
#if MBED_CONF_APP_ECHO_SERVER
worker_loop_event_queue.dispatch_forever();
#else
worker_loop_event_queue.dispatch(600 * SECOND_TO_MS);
#endif
worker_loop_event_queue.cancel(test_step_cb_timer);
worker_loop_event_queue.cancel(timeout_outgoing_msg_timer);
worker_loop_event_queue.dispatch(5);
}
static void worker_loop_event_cb(int event)
{
if (event == LINK_UP) {
printf("cable connected\r\n\r\n");
}
if (event == LINK_DOWN) {
printf("cable disconnected\r\n\r\n");
}
}
void worker_loop_end(void)
{
worker_loop_event_queue.break_dispatch();
}
unsigned char *emac_if_get_own_addr(void)
{
return (emac_if_get_hw_addr());
}
#endif

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifndef EMAC_UTIL_H
#define EMAC_UTIL_H
#define SECOND_TO_US 1000000
#define SECOND_TO_MS 1000
#define MS_TO_US 1000
extern const unsigned char eth_mac_broadcast_addr[];
// Trace flags
#define TRACE_ETH_FRAMES 0x01
#define TRACE_SUCCESS 0x02
#define TRACE_FAILURE 0x04
#define SET_TRACE_LEVEL(level) emac_if_set_trace_level(level)
// Message validation flags
#define BROADCAST 0x01
#define RESPONSE_RECEIVED 0x02
#define INVALID_LENGHT 0x04
#define INVALID_DATA 0x08
#define PRINTED 0x10
#define RESET_OUTGOING_MSG_DATA emac_if_reset_outgoing_msg()
// General error flags
#define TEST_FAILED 0x01
#define MSG_VALID_ERROR 0x02
#define OUT_OF_MSG_DATA 0x04
#define NO_FREE_MEM_BUF 0x08
#define NO_RESPONSE 0x10
#define ERROR_FLAGS emac_if_get_error_flags()
#define RESET_ERROR_FLAGS emac_if_reset_error_flags()
#define PRINT_ERROR_FLAGS emac_if_print_error_flags()
#define SET_ERROR_FLAGS(flags) emac_if_set_error_flags(flags)
#define ETH_FRAME_HEADER_LEN 28
#define ETH_FRAME_MIN_LEN 60
#define ETH_MAC_ADDR_LEN 6
int emac_if_find_outgoing_msg(int receipt_number);
void emac_if_free_outgoing_msg(int index);
int emac_if_count_outgoing_msg(void);
void emac_if_reset_outgoing_msg(void);
int emac_if_add_outgoing_msg(int length);
void emac_if_timeout_outgoing_msg(void);
void emac_if_validate_outgoing_msg(void);
void emac_if_set_outgoing_msg_receipt_num(int index, int receipt_number);
void emac_if_set_outgoing_msg_flags(int index, int flags);
void emac_if_add_echo_server_addr(unsigned char *addr);
int emac_if_count_echo_server_addr(void);
unsigned char *emac_if_get_echo_server_addr(int index);
void emac_if_set_error_flags(unsigned int error_flags_value);
unsigned int emac_if_get_error_flags(void);
void emac_if_reset_error_flags(void);
void emac_if_print_error_flags(void);
void emac_if_set_trace_level(char trace_level_value);
void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, char *data);
void emac_if_link_state_change_cb(void *data, bool up);
unsigned char *emac_if_get_own_addr(void);
extern void emac_if_link_input_cb(void *data, void *mem_chain_p);
extern void emac_if_link_state_change_cb(void *data, bool up);
void worker_loop_start(void (*test_step_cb_fnc)(void), int timeout);
void worker_loop_end(void);
void emac_if_init_main_thread(void);
#endif /* EMAC_UTIL_H */

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#if !defined(MBED_CONF_APP_TEST_WIFI) || \
!defined(MBED_CONF_APP_TEST_ETHERNET) || \
!defined(MBED_CONF_APP_ECHO_SERVER) || \
!defined(MBED_CONF_APP_WIFI_SCAN) || \
!defined(MBED_CONF_APP_WIFI_SSID ) || \
!defined(MBED_CONF_APP_WIFI_SECURITY) || \
!defined(MBED_CONF_APP_WIFI_PASSWORD)
#error [NOT_SUPPORTED] Requires parameters from mbed_app.json
#endif
#if !MBED_CONF_APP_TEST_WIFI && !MBED_CONF_APP_TEST_ETHERNET
#error [NOT_SUPPORTED] Either wifi or ethernet testing need to be enabled
#endif
#if MBED_CONF_APP_TEST_WIFI
#if !defined(TARGET_UBLOX_EVK_ODIN_W2) && !defined(TARGET_REALTEK_RTL8195AM)
#error [NOT_SUPPORTED] Tests are valid only for UBLOX_EVK_ODIN_W2 and REALTEK_RTL8195AM
#endif
#endif
#if MBED_CONF_APP_TEST_ETHERNET
#error [NOT_SUPPORTED] Ethernet testing not supported
#endif
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#include "utest.h"
#include "emac_tests.h"
#include "emac_util.h"
using namespace utest::v1;
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
#if !MBED_CONF_APP_ECHO_SERVER
GREENTEA_SETUP(600, "default_auto");
#endif
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("EMAC interface initialize", test_emac_initialize),
Case("EMAC interface broadcast", test_emac_broadcast),
Case("EMAC interface unicast", test_emac_unicast),
Case("EMAC interface unicast frame length", test_emac_unicast_frame_len),
Case("EMAC interface broadcast (run again)", test_emac_broadcast)
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@ -0,0 +1,32 @@
{
"config": {
"test-ethernet": {
"help": "Enable ethernet testing",
"value": 0
},
"test-wifi": {
"help": "Enable wifi testing",
"value": 1
},
"echo-server": {
"help": "Build test to be echo server",
"value": 0
},
"wifi-scan": {
"help": "Scan and list access points",
"value": 0
},
"wifi-ssid": {
"help": "WiFi SSID for network",
"value": "\"SSID\""
},
"wifi-security": {
"help": "WiFi Security",
"value": "NSAPI_SECURITY_WPA_WPA2"
},
"wifi-password": {
"help": "WiFi Password",
"value": "\"PASSWORD\""
}
}
}