Emac greentea tests for feature-emac

pull/6847/head
Mika Leppänen 2018-02-16 13:26:45 +02:00 committed by Kevin Bracey
parent 01b4d97cb4
commit 7aebda7a0f
22 changed files with 2435 additions and 158 deletions

View File

@ -1,28 +1,93 @@
# Description
# Introduction
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.
This document describes how to run EMAC tests. The EMAC test cases are made using the 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
## Configuring the 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.
To configure a device to be a CTP echo server, you need to enable the `echo-server` setting in the `json` file of the test environment application. When a device is configured to be a CTP echo server, it starts to forward CTP messages automatically when it is switched on and continues to do so until it is switched off.
# Test cases
## Test cases
## EMAC interface initialise
### EMAC initialize
Initializes EMAC interface driver.
The test case initializes the EMAC driver and the test network stack.
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.
The EMAC test environment uses the test network stack as the default stack. To enable the stack, set the `nsapi.default-stack` option in the `json` file of the test environment application to value `TEST`.
## EMAC interface broadcast
The test network stack is a bare minimum implementation and has the functionality needed to set up the network interface. The test network stack is defined in the `emac_TestNetworkStack.h` and `emac_TestNetworkStack.cpp` files. The stack uses the test memory manager for the EMAC. The test memory manager is defined in the `emac_TestMemoryManager.h` and `emac_TestMemoryManager.cpp` files. Message buffers sent to the EMAC in `link_out()` are allocated from the buffer pool of the test memory manager. The test memory manager pool allocation unit (buffer size) is 610 bytes.
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.
The initialization test constructs the network interface and connects to it. The test network stack and the EMAC are bound to the network interface using `get_default_instance()` calls to the stack and to the EMAC.
## EMAC interface unicast
After the construction, the network interface is connected. A connect call triggers a set up call to the test network stack. The set up call triggers a call to `emac_if_init()` function in the EMAC initialization test case.
Sends three CTP unicast messages to the CTP echo server. Verifies that all are replied.
The `emac_if_init()` function of the test case configures and powers up the EMAC.
## EMAC interface unicast frame length
The configuration steps are:
* Setting the test memory manager for the EMAC.
* Setting the EMAC link input and state callbacks to call the test environment input and state callback handlers.
* Reading and setting the Ethernet MAC address.
### EMAC broadcast
1. Sends three CTP broadcast messages (100 bytes each)
2. Waits for three seconds
3. Sends three CTP broadcast messages (60 bytes each).
4. Listens for the CTP echo server responses.
5. Stores the addresses of the echo servers if replies are received.
The test case passes if there are no responses from the echo server, but further test cases are skipped.
### EMAC unicast
1. Sends three CTP unicast messages (100 bytes each) to the CTP echo server.
2. Verifies that all are replied.
### EMAC unicast frame length
Sends CTP unicast messages with Ethernet message length from 100 bytes to maximum. Verifies that all are replied.
1. Sends CTP unicast messages with Ethernet message length from 100 bytes to the maximum defined by the MTU of the EMAC with 50 bytes increments.
2. Verifies that all are replied.
### EMAC unicast burst
1. Sends CTP unicast messages with Ethernet message length from 100 bytes to the maximum defined by the MTU of the EMAC with 50 bytes increments.
2. Repeats the sending 10 times.
3. Verifies that all are replied.
### EMAC multicast filter
Tests multicast filtering. Multicast filtering is an optional feature for the EMAC. The test does not fail if filtering is not implemented.
The multicast testing requests the CTP echo server to forward the CTP messages to a specified multicast address as the destination address.
Test steps:
1. Using unicast, verify that the echo server responses are received.
2. Set the IPv6 multicast filter address and the echo server reply (forward) address to different values. Check if the echo response is filtered.
3. Set the IPv6 multicast filter address and the echo server reply address to same value. Check that the response is not filtered.
4. Set the IPv4 multicast filter address and the echo server reply address to different values. Check if the response is filtered.
5. Set the IPv4 multicast filter address and the echo server reply address to same value. Check that the response is not filtered.
6. Enable the receiving of all multicasts. Check that the response is not filtered.
### EMAC memory
Tests memory manager out-of-memory situations. The test case configures the test memory manager to reject memory buffer allocations made by the EMAC. Memory buffer allocations are divided into output and input memory allocations:
* The output memory allocations are the ones made by the EMAC in the `link_out()` function called by the network stack (test case).
* The input memory allocations are other memory allocations made by the EMAC.
Depending on the EMAC implementation, it may or may not allocate memory manager buffers in the link output function. If the memory manager buffers are not allocated, disabling the link output memory allocations in the test does not affect the functionality.
In each test step, the test case sends CTP unicast messages with Ethernet message length from 100 bytes to the maximum defined by the MTU of the EMAC with 50 bytes increments. Memory buffers sent to the EMAC in the `link_out()` function are forced to be non-aligned in this test case.
Test steps:
1. Memory buffer allocations are allowed. Verify that echo server responses are received.
2. Disable input memory buffer allocations. The echo server responses should not be received.
3. Allow memory buffer allocations. Verify that the echo server responses are received.
4. Disable output memory buffer allocations. The echo server responses may or may not be received depending on the EMAC link out implementation.
5. Allow memory buffer allocations. Verify that the echo server responses are received.
6. Disable input and output memory buffer allocations. The echo server responses should not be received.
7. Allow memory buffer allocations. Verify that the echo server responses are received.
8. Allocate memory buffers that are sent to the EMAC in link out from the heap (contiguous memory). Verify that the echo server responses are received.

View File

@ -0,0 +1,499 @@
/*
* Copyright (c) 2018, 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 MBED_CONF_APP_TEST_EMAC
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <list>
#include <stdlib.h>
#include "unity.h"
#include "rtos/Mutex.h"
#include "EMACMemoryManager.h"
#include "emac_TestMemoryManager.h"
#define BUF_HEAD "headheadheadhead"
#define BUF_HEAD_SIZE 16
#define BUF_TAIL "tailtailtailtail"
#define BUF_TAIL_SIZE 16
#define CHECK_ASSERT(value, fmt, ...) check_value(value, fmt, ##__VA_ARGS__)
#define BUF_POOL_SIZE (14 + 40 + 20 + 536) /* Ethernet + IP + TCP + payload */
#define MEM_MNGR_TRACE "test mem mngr: "
char s_trace_buffer[100] = MEM_MNGR_TRACE;
EmacTestMemoryManager::EmacTestMemoryManager()
: m_mem_mutex(),
m_mem_buffers(),
m_alloc_unit(BUF_POOL_SIZE),
m_memory_available(true)
{
}
emac_mem_buf_t *EmacTestMemoryManager::alloc_heap(uint32_t size, uint32_t align)
{
return alloc_heap(size, align, MEM_CHECK);
}
emac_mem_buf_t *EmacTestMemoryManager::alloc_heap(uint32_t size, uint32_t align, uint8_t opt)
{
validate_list();
CHECK_ASSERT(size, "alloc_heap() invalid parameter size");
check_align(align);
if ((opt & MEM_CHECK) && !m_memory_available) {
return NULL;
}
m_mem_mutex.lock();
emac_memory_t *buf = new emac_memory_t;
CHECK_ASSERT(buf, "alloc_heap() no memory");
buf->buffer = std::malloc(BUF_HEAD_SIZE + size + align + BUF_TAIL_SIZE);
CHECK_ASSERT(buf->buffer, "alloc_heap() no memory");
buf->next = 0;
buf->ptr = static_cast<char *>(buf->buffer) + BUF_HEAD_SIZE;
buf->orig_len = size;
buf->len = size;
buf->first = true;
if (opt & MEM_NO_ALIGN) {
if (reinterpret_cast<uint32_t>(buf->ptr) % sizeof(uint16_t) == 0) {
buf->ptr = static_cast<char *>(buf->ptr) + 1;
}
} else if (align) {
uint32_t remainder = reinterpret_cast<uint32_t>(buf->ptr) % align;
if (remainder) {
uint32_t offset = align - remainder;
if (offset >= align) {
offset = align;
}
buf->ptr = static_cast<char *>(buf->ptr) + offset;
}
}
char *buffer_head = static_cast<char *>(buf->ptr) - BUF_HEAD_SIZE;
memcpy(buffer_head, BUF_HEAD, BUF_HEAD_SIZE);
char *buffer_tail = static_cast<char *>(buf->ptr) + buf->len;
memcpy(buffer_tail, BUF_TAIL, BUF_TAIL_SIZE);
m_mem_buffers.push_front(buf);
m_mem_mutex.unlock();
return buf;
}
emac_mem_buf_t *EmacTestMemoryManager::alloc_pool(uint32_t size, uint32_t align)
{
return alloc_pool(size, align, MEM_CHECK);
}
emac_mem_buf_t *EmacTestMemoryManager::alloc_pool(uint32_t size, uint32_t align, uint8_t opt)
{
validate_list();
CHECK_ASSERT(size, "alloc_pool() invalid parameter size");
check_align(align);
if ((opt & MEM_CHECK) && !m_memory_available) {
return NULL;
}
// Contiguous allocation
if (size + align <= m_alloc_unit) {
return alloc_heap(size, align, opt);
}
unsigned int pool_buffer_max_size = m_alloc_unit - align;
CHECK_ASSERT(pool_buffer_max_size > 0, "alloc_pool() invalid align");
emac_memory_t *first_buf = 0;
emac_memory_t *prev_buf = 0;
unsigned int size_left = size;
while (size_left != 0) {
unsigned int alloc_size;
// New alloc size buffer needed
if (size_left > pool_buffer_max_size) {
size_left = size_left - pool_buffer_max_size;
alloc_size = pool_buffer_max_size;
// New smaller than alloc size buffer needed
} else {
alloc_size = size_left;
size_left = 0;
}
emac_memory_t *new_buf = static_cast<emac_memory_t *>(alloc_heap(alloc_size, align, opt));
if (prev_buf) {
new_buf->first = false;
prev_buf->next = new_buf;
} else {
first_buf = new_buf;
}
prev_buf = new_buf;
}
return first_buf;
}
uint32_t EmacTestMemoryManager::get_pool_alloc_unit(uint32_t align) const
{
validate_list();
check_align(align);
return m_alloc_unit - align;
}
void EmacTestMemoryManager::free(emac_mem_buf_t *buf)
{
validate_list();
CHECK_ASSERT(buf, "free(): buffer parameter is null");
emac_memory_t *mem_buf = static_cast<emac_memory_t *>(buf);
CHECK_ASSERT(mem_buf->first, "free() not first in chain");
std::list<emac_memory_t *>::iterator mem_buf_entry;
m_mem_mutex.lock();
while (mem_buf) {
for (mem_buf_entry = m_mem_buffers.begin(); mem_buf_entry != m_mem_buffers.end(); ++mem_buf_entry) {
if (*mem_buf_entry == mem_buf) {
break;
}
}
if (mem_buf_entry == m_mem_buffers.end()) {
CHECK_ASSERT(0, "free(): %p buffer already freed", mem_buf);
m_mem_mutex.unlock();
return;
}
char *buffer_head = static_cast<char *>(mem_buf->ptr) - BUF_HEAD_SIZE;
if (memcmp(buffer_head, BUF_HEAD, BUF_HEAD_SIZE) != 0) {
CHECK_ASSERT(0, "free(): %p head overwrite", mem_buf);
}
char *buffer_tail = static_cast<char *>(mem_buf->ptr) + mem_buf->orig_len;
if (memcmp(buffer_tail, BUF_TAIL, BUF_TAIL_SIZE) != 0) {
CHECK_ASSERT(0, "free(): %p tail overwrite", mem_buf);
}
emac_memory_t *next = mem_buf->next;
m_mem_buffers.erase(mem_buf_entry);
std::free(mem_buf->buffer);
delete mem_buf;
mem_buf = next;
}
m_mem_mutex.unlock();
}
uint32_t EmacTestMemoryManager::get_total_len(const emac_mem_buf_t *buf) const
{
validate_list();
if (!validate_ptr(buf)) {
CHECK_ASSERT(0, "get_total_len(): %p invalid buffer", buf);
return 0;
}
uint32_t total_len = 0;
for (emac_memory_t *mem_buf = (emac_memory_t *) buf; mem_buf != NULL; mem_buf = mem_buf->next) {
total_len += mem_buf->len;
}
return total_len;
}
void EmacTestMemoryManager::copy(emac_mem_buf_t *to_buf, const emac_mem_buf_t *from_buf)
{
validate_list();
if (!validate_ptr(to_buf)) {
CHECK_ASSERT(0, "copy(): %p invalid to buffer", to_buf);
return;
}
if (!validate_ptr(from_buf)) {
CHECK_ASSERT(0, "copy(): %p invalid from buffer", from_buf);
return;
}
if (get_total_len(to_buf) != get_total_len(from_buf)) {
CHECK_ASSERT(0, "copy(): %p to and %p from buffer total lengths not same", to_buf, from_buf);
return;
}
unsigned int to_buf_offset = 0;
unsigned int from_buf_offset = 0;
emac_memory_t *to_mem_buf = static_cast<emac_memory_t *>(to_buf);
const emac_memory_t *from_mem_buf = static_cast<const emac_memory_t *>(from_buf);
while (to_mem_buf && from_mem_buf) {
unsigned int buf_copy_len;
// Is there data in from buffer
buf_copy_len = from_mem_buf->len - from_buf_offset;
if (buf_copy_len == 0) {
from_mem_buf = from_mem_buf->next;
from_buf_offset = 0;
continue;
}
// Is there space left in to buffer
if (buf_copy_len > to_mem_buf->len - to_buf_offset) {
buf_copy_len = to_mem_buf->len - to_buf_offset;
}
if (buf_copy_len == 0) {
to_mem_buf = to_mem_buf->next;
to_buf_offset = 0;
continue;
}
// Copy data
memcpy(static_cast<char *>(to_mem_buf->ptr) + to_buf_offset, static_cast<const char *>(from_mem_buf->ptr) + from_buf_offset, buf_copy_len);
from_buf_offset += buf_copy_len;
to_buf_offset += buf_copy_len;
}
}
void EmacTestMemoryManager::cat(emac_mem_buf_t *to_buf, emac_mem_buf_t *cat_buf)
{
validate_list();
if (!validate_ptr(to_buf)) {
CHECK_ASSERT(0, "cat(): %p invalid to buffer", to_buf);
return;
}
if (!validate_ptr(cat_buf)) {
CHECK_ASSERT(0, "cat(): %p invalid cat buffer", cat_buf);
return;
}
emac_memory_t *cat_mem_buf = static_cast<emac_memory_t *>(cat_buf);
if (!cat_mem_buf->first) {
CHECK_ASSERT(0, "cat(): %p cat buffer does not point to head of chain", cat_buf);
return;
}
emac_memory_t *to_mem_buf = static_cast<emac_memory_t *>(to_buf);
while (to_mem_buf->next != 0) {
to_mem_buf = to_mem_buf->next;
}
to_mem_buf->next = cat_mem_buf;
cat_mem_buf->first = false;
}
emac_mem_buf_t *EmacTestMemoryManager::get_next(const emac_mem_buf_t *buf) const
{
validate_list();
if (!validate_ptr(buf)) {
CHECK_ASSERT(0, "get_next(): %p invalid buffer", buf);
return NULL;
}
const emac_memory_t *mem_buf = static_cast<const emac_memory_t *>(buf);
return mem_buf->next;
}
void *EmacTestMemoryManager::get_ptr(const emac_mem_buf_t *buf) const
{
validate_list();
if (!validate_ptr(buf)) {
CHECK_ASSERT(0, "get_ptr(): %p invalid buffer", buf);
return NULL;
}
const emac_memory_t *mem_buf = static_cast<const emac_memory_t *>(buf);
return mem_buf->ptr;
}
uint32_t EmacTestMemoryManager::get_len(const emac_mem_buf_t *buf) const
{
validate_list();
if (!validate_ptr(buf)) {
CHECK_ASSERT(0, "get_len(): %p invalid buffer", buf);
return 0;
}
const emac_memory_t *mem_buf = static_cast<const emac_memory_t *>(buf);
return mem_buf->len;
}
void EmacTestMemoryManager::set_len(emac_mem_buf_t *buf, uint32_t len)
{
validate_list();
if (!validate_ptr(buf)) {
CHECK_ASSERT(0, "set_len(): %p invalid buffer", buf);
return;
}
emac_memory_t *mem_buf = static_cast<emac_memory_t *>(buf);
if (len > mem_buf->orig_len) {
CHECK_ASSERT(0, "set_len(): %p new length %i must be less or equal allocated size %i", buf, len, mem_buf->orig_len);
return;
}
if (!mem_buf->first) {
CHECK_ASSERT(0, "set_len(): %p buffer does not point to head of chain", buf);
return;
}
mem_buf->len = len;
}
void EmacTestMemoryManager::set_alloc_unit(uint32_t alloc_unit)
{
validate_list();
m_alloc_unit = alloc_unit;
}
void EmacTestMemoryManager::set_memory_available(bool memory)
{
m_memory_available = memory;
}
void EmacTestMemoryManager::get_memory_statistics(int *buffers, int *memory)
{
if (!buffers || !memory) {
return;
}
*buffers = 0;
*memory = 0;
m_mem_mutex.lock();
const emac_memory_t *mem_buf;
for (std::list<emac_memory_t *>::const_iterator it = m_mem_buffers.begin(); it != m_mem_buffers.end(); ++it) {
mem_buf = static_cast<const emac_memory_t *>(*it);
++(*buffers);
*memory += mem_buf->orig_len;
}
m_mem_mutex.unlock();
}
template <typename TYPE> void EmacTestMemoryManager::check_value(TYPE value, const char *fmt, ...) const
{
if (!value) {
va_list ap;
va_start(ap, fmt);
snprintf(s_trace_buffer + sizeof(MEM_MNGR_TRACE) - 1, sizeof(s_trace_buffer) - sizeof(MEM_MNGR_TRACE) - 1, fmt, ap);
va_end(ap);
TEST_ASSERT_MESSAGE(0, s_trace_buffer);
}
}
bool EmacTestMemoryManager::validate_ptr(const emac_mem_buf_t *buf) const
{
if (!buf) {
return false;
}
m_mem_mutex.lock();
const emac_memory_t *mem_buf = static_cast<const emac_memory_t *>(buf);
for (std::list<emac_memory_t *>::const_iterator it = m_mem_buffers.begin(); it != m_mem_buffers.end(); ++it) {
emac_memory_t *list_buf = static_cast<emac_memory_t *>(*it);
if (list_buf == mem_buf) {
m_mem_mutex.unlock();
return true;
}
}
m_mem_mutex.unlock();
return false;
}
void EmacTestMemoryManager::check_align(uint32_t align) const
{
if (align > 64) {
CHECK_ASSERT(0, "check_align(): invalid alignment value");
}
}
void EmacTestMemoryManager::validate_list() const
{
m_mem_mutex.lock();
const emac_memory_t *mem_buf;
for (std::list<emac_memory_t *>::const_iterator it = m_mem_buffers.begin(); it != m_mem_buffers.end(); ++it) {
mem_buf = static_cast<const emac_memory_t *>(*it);
char *buffer_head = static_cast<char *>(mem_buf->ptr) - BUF_HEAD_SIZE;
if (memcmp(buffer_head, BUF_HEAD, BUF_HEAD_SIZE) != 0) {
CHECK_ASSERT(0, "validate_list(): %p head overwrite", mem_buf);
}
char *buffer_tail = static_cast<char *>(mem_buf->ptr) + mem_buf->orig_len;
if (memcmp(buffer_tail, BUF_TAIL, BUF_TAIL_SIZE) != 0) {
CHECK_ASSERT(0, "validate_list(): %p tail overwrite", mem_buf);
}
}
m_mem_mutex.unlock();
}
EmacTestMemoryManager &EmacTestMemoryManager::get_instance() {
static EmacTestMemoryManager test_memory_manager;
return test_memory_manager;
}
#endif

View File

@ -0,0 +1,231 @@
/*
* Copyright (c) 2018, 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 MBED_CONF_APP_TEST_EMAC
#ifndef EMAC_TEST_MEMORY_MANAGER_H
#define EMAC_TEST_MEMORY_MANAGER_H
#include <list>
#include "EMACMemoryManager.h"
#define MEM_CHECK 0x01
#define MEM_NO_ALIGN 0x02
typedef struct emac_memory {
struct emac_memory *next;
void *buffer; /**< Pointer to allocated buffer */
unsigned int orig_len; /**< Original buffer length (set_len() does not change) */
unsigned int len; /**< Buffer length */
void *ptr; /**< Aligned pointer */
bool first;
} emac_memory_t;
class EmacTestMemoryManager : public EMACMemoryManager {
public:
/**
* Creates emac test memory manager
*/
EmacTestMemoryManager();
/*
* Gets static instance
*/
static EmacTestMemoryManager &get_instance();
/**
* Allocates memory buffer from the heap
*
* Memory buffer allocated from heap is always contiguous and can be arbitrary size.
*
* @param size Size of the memory to allocate in bytes
* @param align Memory alignment requirement in bytes
* @return Allocated memory buffer, or NULL in case of error
*/
virtual emac_mem_buf_t *alloc_heap(uint32_t size, uint32_t align);
/**
* Allocates memory buffer from the heap
*
* Memory buffer allocated from heap is always contiguous and can be arbitrary size.
*
* @param size Size of the memory to allocate in bytes
* @param align Memory alignment requirement in bytes
* @param opt Options
* @return Allocated memory buffer, or NULL in case of error
*/
emac_mem_buf_t *alloc_heap(uint32_t size, uint32_t align, uint8_t opt);
/**
* Allocates memory buffer chain from a pool
*
* Memory allocated from pool is contiguous if size is equal or less than
* (aligned) allocation unit, otherwise may be chained. Will typically come from
* fixed-size packet pool memory.
*
* @param size Total size of the memory to allocate in bytes
* @param align Memory alignment requirement for each buffer in bytes
* @return Allocated memory buffer chain, or NULL in case of error
*/
virtual emac_mem_buf_t *alloc_pool(uint32_t size, uint32_t align);
/**
* Allocates memory buffer chain from a pool
*
* Memory allocated from pool is contiguous if size is equal or less than
* (aligned) allocation unit, otherwise may be chained. Will typically come from
* fixed-size packet pool memory.
*
* @param size Total size of the memory to allocate in bytes
* @param align Memory alignment requirement for each buffer in bytes
* @param opt Options
* @return Allocated memory buffer chain, or NULL in case of error
*/
emac_mem_buf_t *alloc_pool(uint32_t size, uint32_t align, uint8_t opt);
/**
* Get memory buffer pool allocation unit
*
* Returns the maximum size of contiguous memory that can be allocated from a pool.
*
* @param align Memory alignment requirement in bytes
* @return Contiguous memory size
*/
virtual uint32_t get_pool_alloc_unit(uint32_t align) const;
/**
* Free memory buffer chain
*
* If memory buffer is chained must point to the start of the chain. Frees all buffers
* from the chained list.
*
* @param mem Memory buffer chain to be freed.
*/
virtual void free(emac_mem_buf_t *buf);
/**
* Return total length of a memory buffer chain
*
* Returns a total length of this buffer and any following buffers in the chain.
*
* @param mem Memory buffer chain
* @return Total length in bytes
*/
virtual uint32_t get_total_len(const emac_mem_buf_t *buf) const;
/**
* Copy a memory buffer chain
*
* Copies data from one buffer chain to another. Copy operation does not adjust the lengths
* of the copied-to memory buffer chain, so chain total lengths must be the same.
*
* @param to_buf Memory buffer chain to copy to
* @param from_buf Memory buffer chain to copy from
*/
virtual void copy(emac_mem_buf_t *to_buf, const emac_mem_buf_t *from_buf);
/**
* Concatenate two memory buffer chains
*
* Concatenates buffer chain to end of the other buffer chain. Concatenated-to buffer total length
* is adjusted accordingly. cat_buf must point to the start of a the chain. After concatenation
* to_buf's chain now owns those buffers, and they will be freed when the to_buf chain is freed.
*
* @param to_buf Memory buffer chain to concatenate to
* @param cat_buf Memory buffer chain to concatenate
*/
virtual void cat(emac_mem_buf_t *to_buf, emac_mem_buf_t *cat_buf);
/**
* Returns the next buffer
*
* Returns the next buffer from the memory buffer chain.
*
* @param mem Memory buffer
* @return The next memory buffer, or NULL if last
*/
virtual emac_mem_buf_t *get_next(const emac_mem_buf_t *buf) const;
/**
* Return pointer to the payload of the buffer
*
* @param mem Memory buffer
* @return Pointer to the payload
*/
virtual void *get_ptr(const emac_mem_buf_t *buf) const;
/**
* Return payload size of the buffer
*
* @param mem Memory buffer
* @return Size in bytes
*/
virtual uint32_t get_len(const emac_mem_buf_t *buf) const;
/**
* Sets the payload size of the buffer
*
* The allocated payload size will not change. It is not permitted
* to change the length of a buffer that is not the first (or only) in a chain.
*
* @param mem Memory buffer
* @param len Payload size, must be less or equal allocated size
*/
virtual void set_len(emac_mem_buf_t *buf, uint32_t len);
/**
* Sets memory buffer pool allocation unit
*
* Sets the maximum size of contiguous memory that can be allocated from a pool.
*
* @param alloc_unit Contiguous memory size
*/
virtual void set_alloc_unit(uint32_t alloc_unit);
/**
* Sets whether memory is available
*
* Can be used to disable memory allocation request from emac.
*
* @param memory True if memory is available
*/
void set_memory_available(bool memory);
/**
* Gets memory statistics
*
* Gets memory usage statistics
*
* @param buffers Number of buffers that are reserved
* @param memory Reserved memory in bytes
*/
void get_memory_statistics(int *buffers, int *memory);
private:
void validate_list() const;
template <typename TYPE> void check_value(TYPE value, const char *fmt, ...) const;
bool validate_ptr(const emac_mem_buf_t *buf) const;
void check_align(uint32_t align) const;
mutable rtos::Mutex m_mem_mutex;
std::list<emac_memory_t *> m_mem_buffers;
unsigned int m_alloc_unit;
bool m_memory_available;
};
#endif /* EMAC_TEST_MEMORY_MANAGER_H */
#endif

View File

@ -0,0 +1,183 @@
/*
* Copyright (c) 2018, 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 MBED_CONF_APP_TEST_EMAC
#include "unity.h"
#include "EMACMemoryManager.h"
#include "emac_TestNetworkStack.h"
#include "emac_initialize.h"
EmacTestNetworkStack::EmacTestNetworkStack()
: m_interface(NULL)
{
}
nsapi_error_t EmacTestNetworkStack::gethostbyname(const char *host, SocketAddress *address, nsapi_version_t version)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::add_dns_server(const SocketAddress &address)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::socket_close(nsapi_socket_t handle)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::socket_bind(nsapi_socket_t handle, const SocketAddress &address)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::socket_listen(nsapi_socket_t handle, int backlog)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::socket_connect(nsapi_socket_t handle, const SocketAddress &address)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address)
{
return NSAPI_ERROR_OK;
}
nsapi_size_or_error_t EmacTestNetworkStack::socket_send(nsapi_socket_t handle, const void *data, nsapi_size_t size)
{
return NSAPI_ERROR_OK;
}
nsapi_size_or_error_t EmacTestNetworkStack::socket_recv(nsapi_socket_t handle, void *data, nsapi_size_t size)
{
return NSAPI_ERROR_OK;
}
nsapi_size_or_error_t EmacTestNetworkStack::socket_sendto(nsapi_socket_t handle, const SocketAddress &address, const void *data, nsapi_size_t size)
{
return size;
}
nsapi_size_or_error_t EmacTestNetworkStack::socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *data, nsapi_size_t size)
{
return NSAPI_ERROR_DEVICE_ERROR;
}
nsapi_error_t EmacTestNetworkStack::setsockopt(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen)
{
return NSAPI_ERROR_OK;
}
void EmacTestNetworkStack::socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data)
{
}
nsapi_error_t EmacTestNetworkStack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out)
{
// Test network stack supports only one interface
TEST_ASSERT_MESSAGE(!m_interface, "Only one interface supported!");
m_interface = &EmacTestNetworkStack::Interface::get_instance();
TEST_ASSERT_MESSAGE(m_interface, "Invalid interface!");
m_interface->m_emac = &emac;
*interface_out = m_interface;
return NSAPI_ERROR_OK;
}
EmacTestNetworkStack::Interface::Interface()
: m_emac(NULL)
{
}
char *EmacTestNetworkStack::Interface::get_mac_address(char *buf, nsapi_size_t buflen)
{
return NULL;
}
char *EmacTestNetworkStack::Interface::get_ip_address(char *buf, nsapi_size_t buflen)
{
return NULL;
}
char *EmacTestNetworkStack::Interface::get_netmask(char *buf, nsapi_size_t buflen)
{
return NULL;
}
char *EmacTestNetworkStack::Interface::get_gateway(char *buf, nsapi_size_t buflen)
{
return NULL;
}
nsapi_error_t EmacTestNetworkStack::Interface::bringup(bool dhcp, const char *ip, const char *netmask, const char *gw, const nsapi_ip_stack_t stack)
{
if (!emac_if_init()) {
TEST_ASSERT_MESSAGE(0, "emac initialization failed!");
}
return NSAPI_ERROR_OK;
}
nsapi_error_t EmacTestNetworkStack::Interface::bringdown()
{
return NSAPI_ERROR_OK;
}
EmacTestNetworkStack::Interface &EmacTestNetworkStack::Interface::get_instance()
{
static EmacTestNetworkStack::Interface test_interface;
return test_interface;
}
EmacTestNetworkStack &EmacTestNetworkStack::get_instance()
{
static EmacTestNetworkStack test_stack;
return test_stack;
}
#define TEST 0x33254234
#if MBED_CONF_NSAPI_DEFAULT_STACK == TEST
#undef TEST
OnboardNetworkStack &OnboardNetworkStack::get_default_instance()
{
return EmacTestNetworkStack::get_instance();
}
#endif
#endif

View File

@ -0,0 +1,349 @@
/*
* Copyright (c) 2018, 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 MBED_CONF_APP_TEST_EMAC
#ifndef EMAC_TEST_NETWORK_STACK_H
#define EMAC_TEST_NETWORK_STACK_H
#include "netsocket/nsapi_types.h"
#include "netsocket/EMAC.h"
#include "netsocket/OnboardNetworkStack.h"
#include "emac_TestMemoryManager.h"
class EmacTestNetworkStack : public OnboardNetworkStack, private mbed::NonCopyable<EmacTestNetworkStack> {
public:
static EmacTestNetworkStack &get_instance();
EmacTestNetworkStack();
virtual ~EmacTestNetworkStack() {}
class Interface : public OnboardNetworkStack::Interface {
public:
static Interface &get_instance();
/** Connect the interface to the network
*
* Sets up a connection on specified network interface, using DHCP or provided network details. If the @a dhcp is set to
* true all the remaining parameters are ignored.
*
* @param dhcp true if the network details should be acquired using DHCP
* @param ip IP address to be used for the interface as "W:X:Y:Z" or NULL
* @param netmask Net mask to be used for the interface as "W:X:Y:Z" or NULL
* @param gw Gateway address to be used for the interface as "W:X:Y:Z" or NULL
* @param stack Allow manual selection of IPv4 and/or IPv6.
* @return NSAPI_ERROR_OK on success, or error code
*/
virtual nsapi_error_t bringup(bool dhcp, const char *ip,
const char *netmask, const char *gw,
nsapi_ip_stack_t stack
#ifdef __cplusplus
= DEFAULT_STACK
#endif
);
/** Disconnect interface from the network
*
* After this call the network interface is inactive, to use it again user needs to call @a mbed_ipstack_bringup again.
*
* @return NSAPI_ERROR_OK on success, or error code
*/
virtual nsapi_error_t bringdown();
/** Return MAC address of the network interface
*
* @return MAC address as "V:W:X:Y:Z"
*/
virtual char *get_mac_address(char *buf, nsapi_size_t buflen);
/** Copies IP address of the network interface to user supplied buffer
*
* @param emac EMAC HAL implementation for this network interface
* @param buf buffer to which IP address will be copied as "W:X:Y:Z"
* @param buflen size of supplied buffer
* @return Pointer to a buffer, or NULL if the buffer is too small
*/
virtual char *get_ip_address(char *buf, nsapi_size_t buflen);
/** Copies netmask of the network interface to user supplied buffer
*
* @param buf buffer to which netmask will be copied as "W:X:Y:Z"
* @param buflen size of supplied buffer
* @return Pointer to a buffer, or NULL if the buffer is too small
*/
virtual char *get_netmask(char *buf, nsapi_size_t buflen);
/** Copies gateway address of the network interface to user supplied buffer
*
* @param buf buffer to which gateway address will be copied as "W:X:Y:Z"
* @param buflen size of supplied buffer
* @return Pointer to a buffer, or NULL if the buffer is too small
*/
virtual char *get_gateway(char *buf, nsapi_size_t buflen);
private:
friend EmacTestNetworkStack;
Interface();
EMAC *m_emac;
};
/** Register a network interface with the IP stack
*
* Connects EMAC layer with the IP stack and initializes all the required infrastructure.
* This function should be called only once for each available interface.
*
* @param emac EMAC HAL implementation for this network interface
* @param default_if true if the interface should be treated as the default one
* @param[out] interface_out pointer to stack interface object controlling the EMAC
* @return NSAPI_ERROR_OK on success, or error code
*/
virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out);
/** Translates a hostname to an IP address with specific version
*
* The hostname may be either a domain name or an IP address. If the
* hostname is an IP address, no network transactions will be performed.
*
* If no stack-specific DNS resolution is provided, the hostname
* will be resolve using a UDP socket on the stack.
*
* @param host Hostname to resolve
* @param address Destination for the host SocketAddress
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t gethostbyname(const char *host,
SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC);
/** Add a domain name server to list of servers to query
*
* @param address Destination for the host address
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t add_dns_server(const SocketAddress &address);
protected:
/** Opens a socket
*
* Creates a network socket and stores it in the specified handle.
* The handle must be passed to following calls on the socket.
*
* A stack may have a finite number of sockets, in this case
* NSAPI_ERROR_NO_SOCKET is returned if no socket is available.
*
* @param handle Destination for the handle to a newly created socket
* @param proto Protocol of socket to open, NSAPI_TCP or NSAPI_UDP
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto);
/** Close the socket
*
* Closes any open connection and deallocates any memory associated
* with the socket.
*
* @param handle Socket handle
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t socket_close(nsapi_socket_t handle);
/** Bind a specific address to a socket
*
* Binding a socket specifies the address and port on which to recieve
* data. If the IP address is zeroed, only the port is bound.
*
* @param handle Socket handle
* @param address Local address to bind
* @return 0 on success, negative error code on failure.
*/
virtual nsapi_error_t socket_bind(nsapi_socket_t handle, const SocketAddress &address);
/** Listen for connections on a TCP socket
*
* Marks the socket as a passive socket that can be used to accept
* incoming connections.
*
* @param handle Socket handle
* @param backlog Number of pending connections that can be queued
* simultaneously
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t socket_listen(nsapi_socket_t handle, int backlog);
/** Connects TCP socket to a remote host
*
* Initiates a connection to a remote server specified by the
* indicated address.
*
* @param handle Socket handle
* @param address The SocketAddress of the remote host
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t socket_connect(nsapi_socket_t handle, const SocketAddress &address);
/** Accepts a connection on a TCP socket
*
* The server socket must be bound and set to listen for connections.
* On a new connection, creates a network socket and stores it in the
* specified handle. The handle must be passed to following calls on
* the socket.
*
* A stack may have a finite number of sockets, in this case
* NSAPI_ERROR_NO_SOCKET is returned if no socket is available.
*
* This call is non-blocking. If accept would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @param server Socket handle to server to accept from
* @param handle Destination for a handle to the newly created socket
* @param address Destination for the remote address or NULL
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t socket_accept(nsapi_socket_t server,
nsapi_socket_t *handle, SocketAddress *address=0);
/** Send data over a TCP socket
*
* The socket must be connected to a remote host. Returns the number of
* bytes sent from the buffer.
*
* This call is non-blocking. If send would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @param handle Socket handle
* @param data Buffer of data to send to the host
* @param size Size of the buffer in bytes
* @return Number of sent bytes on success, negative error
* code on failure
*/
virtual nsapi_size_or_error_t socket_send(nsapi_socket_t handle,
const void *data, nsapi_size_t size);
/** Receive data over a TCP socket
*
* The socket must be connected to a remote host. Returns the number of
* bytes received into the buffer.
*
* This call is non-blocking. If recv would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @param handle Socket handle
* @param data Destination buffer for data received from the host
* @param size Size of the buffer in bytes
* @return Number of received bytes on success, negative error
* code on failure
*/
virtual nsapi_size_or_error_t socket_recv(nsapi_socket_t handle,
void *data, nsapi_size_t size);
/** Send a packet over a UDP socket
*
* Sends data to the specified address. Returns the number of bytes
* sent from the buffer.
*
* This call is non-blocking. If sendto would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @param handle Socket handle
* @param address The SocketAddress of the remote host
* @param data Buffer of data to send to the host
* @param size Size of the buffer in bytes
* @return Number of sent bytes on success, negative error
* code on failure
*/
virtual nsapi_size_or_error_t socket_sendto(nsapi_socket_t handle, const SocketAddress &address,
const void *data, nsapi_size_t size);
/** Receive a packet over a UDP socket
*
* Receives data and stores the source address in address if address
* is not NULL. Returns the number of bytes received into the buffer.
*
* This call is non-blocking. If recvfrom would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @param handle Socket handle
* @param address Destination for the source address or NULL
* @param buffer Destination buffer for data received from the host
* @param size Size of the buffer in bytes
* @return Number of received bytes on success, negative error
* code on failure
*/
virtual nsapi_size_or_error_t socket_recvfrom(nsapi_socket_t handle, SocketAddress *address,
void *buffer, nsapi_size_t size);
/** Register a callback on state change of the socket
*
* The specified callback will be called on state changes such as when
* the socket can recv/send/accept successfully and on when an error
* occurs. The callback may also be called spuriously without reason.
*
* The callback may be called in an interrupt context and should not
* perform expensive operations such as recv/send calls.
*
* @param handle Socket handle
* @param callback Function to call on state change
* @param data Argument to pass to callback
*/
virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data);
/* Set stack-specific socket options
*
* The setsockopt allow an application to pass stack-specific hints
* to the underlying stack. For unsupported options,
* NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified.
*
* @param handle Socket handle
* @param level Stack-specific protocol level
* @param optname Stack-specific option identifier
* @param optval Option value
* @param optlen Length of the option value
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level,
int optname, const void *optval, unsigned optlen);
/* Get stack-specific socket options
*
* The getstackopt allow an application to retrieve stack-specific hints
* from the underlying stack. For unsupported options,
* NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified.
*
* @param handle Socket handle
* @param level Stack-specific protocol level
* @param optname Stack-specific option identifier
* @param optval Destination for option value
* @param optlen Length of the option value
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level,
int optname, void *optval, unsigned *optlen);
private:
Interface *m_interface;
};
#endif /* EMAC_TEST_NETWORK_STACK_H */
#endif

View File

@ -23,11 +23,20 @@
#include "mbed.h"
#include "lwip/opt.h" /* ETH_PAD_SIZE */
#if MBED_CONF_APP_TEST_EMAC
#include "EMAC.h"
#include "EMACMemoryManager.h"
#include "emac_TestMemoryManager.h"
#else
#include "lwip/opt.h" /* ETH_PAD_SIZE */
#include "emac_stack_mem.h"
#include "emac_api.h"
#endif
#include "emac_tests.h"
#include "emac_ctp.h"
@ -100,13 +109,15 @@ ctp_function emac_if_ctp_header_handle(unsigned char *eth_input_frame, unsigned
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)
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, int options)
{
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]);
if (emac_if_get_trace_level() & TRACE_SEND) {
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);
@ -115,9 +126,26 @@ void emac_if_ctp_msg_build(int eth_frame_len, const unsigned char *dest_addr, co
return;
}
emac_stack_mem_chain_t *mem_chain_p = emac_stack_mem_alloc(0, eth_frame_len + ETH_PAD_SIZE, 0);
#if MBED_CONF_APP_TEST_EMAC
int alloc_opt = 0;
int align = 0;
if (options & CTP_OPT_NON_ALIGNED) {
alloc_opt |= MEM_NO_ALIGN; // Force align to odd address
align = 1; // Reserve memory overhead to align to odd address
}
if (!mem_chain_p) {
emac_mem_buf_t *buf;
if (options & CTP_OPT_HEAP) {
buf = emac_m_mngr_get()->alloc_heap(eth_frame_len, align, alloc_opt);
} else {
// Default allocation is from pool
buf = emac_m_mngr_get()->alloc_pool(eth_frame_len, align, alloc_opt);
}
#else
emac_stack_mem_chain_t *buf = emac_stack_mem_alloc(0, eth_frame_len + ETH_PAD_SIZE, 0);
#endif
if (!buf) {
SET_ERROR_FLAGS(NO_FREE_MEM_BUF);
emac_if_free_outgoing_msg(outgoing_msg_index);
return;
@ -131,12 +159,16 @@ void emac_if_ctp_msg_build(int eth_frame_len, const unsigned char *dest_addr, co
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_memory_buffer_write(buf, 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);
#if MBED_CONF_APP_TEST_EMAC
emac_if_check_memory(true);
emac_if_get()->link_out(buf);
emac_if_check_memory(false);
#else
emac_if_get()->ops.link_out(emac_if_get(), buf);
emac_stack_mem_free(0, buf);
#endif
}
#endif

View File

@ -24,8 +24,18 @@ enum ctp_function {
CTP_REPLY
};
// Test memory manager options
#define CTP_OPT_HEAP 0x01 // Allocate link_out() frame from heap
#define CTP_OPT_NON_ALIGNED 0x02 // Force memory buffers to be non-aligned
/* Builds and sends CTP message. Forward to address is the address where echo server sends the reply.
Default is own Ethernet MAC address. Options can be used to specify test memory manager options.
*/
#define CTP_MSG_SEND(length, send_to_address, own_address, forward_to_address, mem_mngr_options) \
emac_if_ctp_msg_build(length, send_to_address, own_address, forward_to_address, mem_mngr_options)
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_msg_build(int eth_frame_len, const unsigned char *dest_addr, const unsigned char *origin_addr, const unsigned char *forward_addr, int options);
void emac_if_ctp_reply_handle(int lenght, int invalid_data_index);
#endif /* EMAC_CTP_H */

View File

@ -18,7 +18,14 @@
#ifndef EMAC_INITIALIZE_H
#define EMAC_INITIALIZE_H
uint8_t *emac_if_get_hw_addr(void);
unsigned char *emac_if_get_hw_addr(void);
bool emac_if_init(void);
#if MBED_CONF_APP_TEST_EMAC
EMAC *emac_if_get(void);
EmacTestMemoryManager *emac_m_mngr_get(void);
#else
emac_interface_t *emac_if_get(void);
#endif
#endif /* EMAC_INITIALIZE_H */

View File

@ -24,19 +24,82 @@
#include "lwip/opt.h" /* ETH_PAD_SIZE */
#include "mbed.h"
#if MBED_CONF_APP_TEST_EMAC
#include "EMAC.h"
#include "EMACMemoryManager.h"
#include "emac_TestMemoryManager.h"
#else
#include "emac_api.h"
#include "emac_stack_mem.h"
#endif
#include "emac_initialize.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)
#if MBED_CONF_APP_TEST_EMAC
int emac_if_memory_buffer_read(void *buf, unsigned char *eth_frame)
{
int eth_frame_index = 0;
int invalid_data_index = 0;
for (emac_mem_buf_t *mem_buf = buf; mem_buf != NULL; mem_buf = emac_m_mngr_get()->get_next(mem_buf)) {
unsigned char *buf_payload = (unsigned char *) emac_m_mngr_get()->get_ptr(mem_buf);
int buf_payload_len = emac_m_mngr_get()->get_len(mem_buf);
for (int index = 0; 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++;
}
}
return invalid_data_index;
}
void emac_if_memory_buffer_write(void *buf, unsigned char *eth_frame, bool write_data)
{
int eth_frame_index = 0;
for (emac_mem_buf_t *mem_buf = buf; mem_buf != NULL; mem_buf = emac_m_mngr_get()->get_next(mem_buf)) {
unsigned char *buf_payload = (unsigned char *) emac_m_mngr_get()->get_ptr(mem_buf);
int buf_payload_len = emac_m_mngr_get()->get_len(mem_buf);
for (int index = 0; 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++;
}
}
}
#else
int emac_if_memory_buffer_read(void *buf, 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)) {
for (emac_stack_mem_t *mem_p = emac_stack_mem_chain_dequeue(0, &buf); mem_p != NULL; mem_p = emac_stack_mem_chain_dequeue(0, &buf)) {
unsigned char *buf_payload = (unsigned char *) emac_stack_mem_ptr(0, mem_p);
int buf_payload_len = emac_stack_mem_len(0, mem_p);
@ -57,12 +120,12 @@ int emac_if_memory_buffer_read(emac_stack_mem_chain_t *mem_chain_p, unsigned cha
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)
void emac_if_memory_buffer_write(void *buf, 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)) {
for (emac_stack_mem_t *mem_p = emac_stack_mem_chain_dequeue(0, &buf); mem_p != NULL; mem_p = emac_stack_mem_chain_dequeue(0, &buf)) {
unsigned char *buf_payload = (unsigned char *) emac_stack_mem_ptr(0, mem_p);
int buf_payload_len = emac_stack_mem_len(0, mem_p);
@ -81,3 +144,5 @@ void emac_if_memory_buffer_write(emac_stack_mem_chain_t *mem_chain_p, unsigned c
}
#endif
#endif

View File

@ -18,7 +18,7 @@
#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);
int emac_if_memory_buffer_read(void *buf, unsigned char *eth_frame);
void emac_if_memory_buffer_write(void *buf, unsigned char *eth_frame, bool write_data);
#endif /* EMAC_MEMBUF_H */

View File

@ -28,42 +28,72 @@
using namespace utest::v1;
void test_emac_broadcast_cb(void)
void test_emac_broadcast_cb(int opt)
{
emac_if_validate_outgoing_msg();
static bool send_request = true;
static int no_response_cnt = 0;
static int retries = 0;
static int test_step = 0;
static int msg_len = 100;
static int wait = 0;
static int counter = 0;
if (wait) {
--wait;
return;
}
// 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
static bool echo_server_started = false;
if (!echo_server_started) {
SET_TRACE_LEVEL(TRACE_NONE);
printf("echo server started successfully\r\n\r\n");
counter = 255;
#else
worker_loop_end();
echo_server_started = true;
} else {
// Sends broadcast every 60 seconds
CTP_MSG_SEND(msg_len, eth_mac_broadcast_addr, emac_if_get_own_addr(), emac_if_get_own_addr(), 0);
wait = 60;
}
return;
#endif
// Timeout
if (opt == TIMEOUT && send_request) {
CTP_MSG_SEND(msg_len, eth_mac_broadcast_addr, emac_if_get_own_addr(), emac_if_get_own_addr(), 0);
send_request = false;
no_response_cnt = 0;
} else if (opt == TIMEOUT) {
if (++no_response_cnt > 3) {
if (++retries > 6) {
printf("too many retries\r\n\r\n");
RESET_ERROR_FLAGS(NO_RESPONSE);
END_TEST_LOOP;
} else {
printf("retry count %i\r\n\r\n", retries);
send_request = true;
}
}
}
// Echo response received
if (opt == INPUT) {
++test_step;
if (test_step == 3) {
msg_len = 60;
wait = 3;
} else if (test_step == 6) {
END_TEST_LOOP;
}
retries = 0;
send_request = true;
}
}
void test_emac_broadcast(void)
{
RESET_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);
RESET_ALL_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SEND | TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);
worker_loop_start(test_emac_broadcast_cb, 10 * SECOND_TO_MS);
START_TEST_LOOP(test_emac_broadcast_cb, 1 * SECOND_TO_MS);
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);

View File

@ -15,13 +15,25 @@
* limitations under the License.
*/
#include <inttypes.h>
#include "mbed.h"
#include "mbed_stats.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#if MBED_CONF_APP_TEST_EMAC
#include "EthernetInterface.h"
#include "EMAC.h"
#include "EMACMemoryManager.h"
#include "emac_TestMemoryManager.h"
#include "emac_TestNetworkStack.h"
#else
#include "inttypes.h"
#if MBED_CONF_APP_TEST_WIFI
@ -36,6 +48,10 @@
#endif
#include "emac_api.h"
#endif
#include "emac_initialize.h"
#include "emac_tests.h"
#include "emac_util.h"
@ -43,13 +59,61 @@ using namespace utest::v1;
static unsigned char eth_mac_addr[ETH_MAC_ADDR_LEN];
#if !MBED_CONF_APP_TEST_EMAC
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);
#endif
void test_emac_initialize()
{
worker_loop_init();
#if MBED_CONF_APP_TEST_EMAC
#if MBED_CONF_APP_TEST_ETHERNET
static EthernetInterface *network_interface = new EthernetInterface;
#elif MBED_CONF_APP_TEST_WIFI
// Add wifi classes here
#ifdef TARGET_UBLOX_EVK_ODIN_W2
static WiFiInterface *network_interface = new OdinWiFiInterface;
#elif TARGET_REALTEK_RTL8195AM
static WiFiInterface *network_interface = new RTWInterface;
#else
static WiFiInterface *network_interface = new WiFiInterface;
#endif
#if MBED_CONF_APP_WIFI_SCAN
WiFiAccessPoint ap[30];
int size = network_interface->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
network_interface->set_credentials(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, MBED_CONF_APP_WIFI_SECURITY);
#endif
// Power up the interface and emac driver
network_interface->connect();
worker_loop_link_up_wait();
#else
#if MBED_CONF_APP_TEST_WIFI
static WiFiInterface *wifi;
@ -84,9 +148,10 @@ void test_emac_initialize()
const char *ip_addr = wifi->get_ip_address();
printf("connected IP %s\r\n\r\n", ip_addr);
#endif
TEST_ASSERT(emac_if_init());
#endif
#endif
}
unsigned char *emac_if_get_hw_addr(void)
@ -94,6 +159,19 @@ unsigned char *emac_if_get_hw_addr(void)
return &eth_mac_addr[0];
}
#if MBED_CONF_APP_TEST_EMAC
EMAC *emac_if_get(void)
{
return &EMAC::get_default_instance();
}
EmacTestMemoryManager *emac_m_mngr_get(void)
{
return &EmacTestMemoryManager::get_instance();
}
#else
emac_interface_t *emac_if_get(void)
{
#if MBED_CONF_APP_TEST_WIFI
@ -107,9 +185,43 @@ emac_interface_t *emac_if_get(void)
return 0;
#endif
}
#endif
static bool emac_if_init(void)
bool emac_if_init(void)
{
#if MBED_CONF_APP_TEST_EMAC
static EMAC *emac = &EMAC::get_default_instance();
static EmacTestMemoryManager *memory_manager = &EmacTestMemoryManager::get_instance();
emac->set_memory_manager(*memory_manager);
emac->set_link_input_cb(emac_if_link_input_cb);
emac->set_link_state_cb(emac_if_link_state_change_cb);
if (!emac->power_up()) {
TEST_ASSERT_MESSAGE(0, "emac power up failed!");
}
int hwaddr_len = emac->get_hwaddr_size();
printf("emac hwaddr length %i\r\n\r\n", hwaddr_len);
TEST_ASSERT_MESSAGE(hwaddr_len == 6, "invalid emac hwaddr length!");
// If driver updates this, write it back, otherwise write default from mbed_mac_address
mbed_mac_address(reinterpret_cast<char *>(&eth_mac_addr[0]));
emac->get_hwaddr(eth_mac_addr);
emac->set_hwaddr(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_size = emac->get_mtu_size();
printf("emac mtu %i\r\n\r\n", mtu_size);
emac_if_set_mtu_size(mtu_size);
char hw_name[11];
emac->get_ifname(hw_name, 10);
printf("emac if name %s\r\n\r\n", hw_name);
return true;
#else
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);
@ -123,8 +235,9 @@ static bool emac_if_init(void)
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);
int mtu_size = emac_if->ops.get_mtu_size(emac_if);
printf("emac mtu %i\r\n\r\n", mtu_size);
emac_if_set_mtu_size(mtu_size);
char hw_name[11];
emac_if->ops.get_ifname(emac_if, hw_name, 10);
@ -135,6 +248,7 @@ static bool emac_if_init(void)
}
return true;
#endif
}
#endif

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2018, 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_memory_cb(int opt)
{
static bool send_request = true;
static bool memory = true;
static int no_response_cnt = 0;
static int retries = 0;
static int msg_len = 0;
static int test_step = 0;
static bool next_step = true;
static int options = CTP_OPT_NON_ALIGNED;
// Defines test step
if (next_step) {
next_step = false;
switch (test_step) {
case 0:
printf("STEP 0: memory available\r\n\r\n");
emac_if_set_output_memory(true);
emac_if_set_input_memory(true);
memory = true;
break;
case 1:
printf("STEP 1: no input memory buffer memory available\r\n\r\n");
emac_if_set_output_memory(true);
emac_if_set_input_memory(false);
memory = false;
break;
case 2:
printf("STEP 2: memory available\r\n\r\n");
emac_if_set_output_memory(true);
emac_if_set_input_memory(true);
memory = true;
break;
case 3:
printf("STEP 3: no output memory buffer memory available\r\n\r\n");
emac_if_set_output_memory(false);
emac_if_set_input_memory(true);
memory = false;
break;
case 4:
printf("STEP 4: memory available\r\n\r\n");
emac_if_set_output_memory(true);
emac_if_set_input_memory(true);
memory = true;
break;
case 5:
printf("STEP 5: no output or input memory buffer memory available\r\n\r\n");
emac_if_set_output_memory(false);
emac_if_set_input_memory(false);
memory = false;
break;
case 6:
printf("STEP 6: memory available\r\n\r\n");
emac_if_set_output_memory(true);
emac_if_set_input_memory(true);
memory = true;
break;
case 7:
printf("STEP 7: memory available, alloc from heap\r\n\r\n");
emac_if_set_output_memory(true);
emac_if_set_input_memory(true);
options |= CTP_OPT_HEAP;
memory = true;
break;
case 8:
// Test ended
END_TEST_LOOP;
}
}
bool next_len = false;
// Timeout
if (opt == TIMEOUT && send_request) {
CTP_MSG_SEND(msg_len, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr(), options);
send_request = false;
no_response_cnt = 0;
} else if (opt == TIMEOUT) {
if (++no_response_cnt > 5) {
if (++retries > 3) {
// If echo replies should be received fails the test case
if (memory) {
printf("too many retries\r\n\r\n");
SET_ERROR_FLAGS(TEST_FAILED);
END_TEST_LOOP;
// Otherwise continues test
} else {
RESET_OUTGOING_MSG_DATA;
next_len = true;
}
} else {
printf("retry count %i\r\n\r\n", retries);
send_request = true;
}
}
}
// Echo response received or retry count exceeded for memory buffers are not available
if (opt == INPUT || next_len) {
if (msg_len == ETH_MAX_LEN) {
msg_len = 0;
test_step++;
next_step = true;
} else if (msg_len + 50 >= ETH_MAX_LEN) {
msg_len = ETH_MAX_LEN;
} else {
msg_len += 50;
}
printf("message length %i\r\n\r\n", msg_len);
retries = 0;
send_request = true;
}
}
void test_emac_memory()
{
RESET_ALL_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SEND | TRACE_SUCCESS | TRACE_FAILURE);
if (ECHO_SERVER_ADDRESS_KNOWN) {
START_TEST_LOOP(test_emac_memory_cb, 100);
}
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);
RESET_OUTGOING_MSG_DATA;
}
#endif

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2018, 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_multicast_filter_cb(int opt)
{
static bool multicasts_are_filtered = true;
unsigned char forward_addr[ETH_MAC_ADDR_LEN];
static bool send_request = true;
static bool receive = true;
static int no_response_cnt = 0;
static int retries = 0;
static int test_step = 0;
static bool next_step = true;
// Defines test step
if (next_step) {
next_step = false;
switch (test_step) {
case 0:
printf("STEP 0: check unicast message functionality\r\n\r\n");
{
unsigned char *own_addr = emac_if_get_own_addr();
memcpy(forward_addr, own_addr, 6);
}
receive = true;
break;
case 1:
printf("STEP 1: set ipv6 multicast filter, test if input message is filtered\r\n\r\n");
{
unsigned char filter[] = {0x33, 0x33, 0x1b, 0x1c, 0x1d, 0x1e};
char forw_addr[] = {0x33, 0x33, 0x11, 0x22, 0x33, 0x44};
emac_if_add_multicast_group(filter);
memcpy(forward_addr, forw_addr, 6);
}
receive = false;
break;
case 2:
printf("STEP 2: set ipv6 multicast filter, test that input message is not filtered\r\n\r\n");
{
unsigned char filter[] = {0x33, 0x33, 0xaa, 0xbb, 0xcc, 0xdd};
emac_if_add_multicast_group(filter);
memcpy(forward_addr, filter, 6);
}
receive = true;
break;
case 3:
printf("STEP 3: set ipv4 multicast filter, test if input message is filtered\r\n\r\n");
{
unsigned char filter[] = {0x01, 0x00, 0x5e, 0xa1, 0xa2, 0xa3};
char forw_addr[] = {0x01, 0x00, 0x5e, 0x11, 0x22, 0x33};
emac_if_add_multicast_group(filter);
memcpy(forward_addr, forw_addr, 6);
}
receive = false;
break;
case 4:
printf("STEP 4: set ipv4 multicast filter, test that input message is not filtered\r\n\r\n");
{
unsigned char filter[] = {0x01, 0x00, 0x5e, 0xa5, 0xa6, 0xa7};
emac_if_add_multicast_group(filter);
memcpy(forward_addr, filter, 6);
}
receive = true;
break;
case 5:
printf("STEP 5: set receive all multicast, verify that input messages are not filtered\r\n\r\n");
{
emac_if_set_all_multicast(true);
char forw_addr[] = {0x33, 0x33, 0x11, 0x12, 0x33, 0x44};
memcpy(forward_addr, forw_addr, 6);
}
receive = true;
break;
case 6:
// Test ended
if (!multicasts_are_filtered) {
printf("multicast filtering was not enabled!!!\r\n\r\n");
}
END_TEST_LOOP;
}
}
// Timeout
if (opt == TIMEOUT && send_request) {
CTP_MSG_SEND(100, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), forward_addr, 0);
send_request = false;
no_response_cnt = 0;
} else if (opt == TIMEOUT) {
if (++no_response_cnt > 5) {
if (++retries > 3) {
if (receive) {
printf("too many retries\r\n\r\n");
SET_ERROR_FLAGS(TEST_FAILED);
END_TEST_LOOP;
} else {
next_step = true;
}
} else {
printf("retry count %i\r\n\r\n", retries);
send_request = true;
}
}
}
// Echo response received
if (opt == INPUT) {
if (receive == false) {
printf("multicast was not filtered\r\n\r\n");
multicasts_are_filtered = false;
}
next_step = true;
}
if (next_step) {
RESET_OUTGOING_MSG_DATA;
test_step++;
retries = 0;
send_request = true;
}
}
void test_emac_multicast_filter()
{
RESET_ALL_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SEND | TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);
if (ECHO_SERVER_ADDRESS_KNOWN) {
START_TEST_LOOP(test_emac_multicast_filter_cb, 1 * SECOND_TO_MS);
}
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);
RESET_OUTGOING_MSG_DATA;
}
#endif

View File

@ -28,35 +28,49 @@
using namespace utest::v1;
void test_emac_unicast_cb(void)
void test_emac_unicast_cb(int opt)
{
emac_if_validate_outgoing_msg();
static bool send_request = true;
static int no_response_cnt = 0;
static int retries = 0;
static int test_step = 0;
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);
// Timeout
if (opt == TIMEOUT && send_request) {
CTP_MSG_SEND(100, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr(), 0);
send_request = false;
no_response_cnt = 0;
} else if (opt == TIMEOUT) {
if (++no_response_cnt > 5) {
if (++retries > 3) {
printf("too many retries\r\n\r\n");
SET_ERROR_FLAGS(TEST_FAILED);
END_TEST_LOOP;
} else {
printf("retry count %i\r\n\r\n", retries);
send_request = true;
}
}
}
// Echo response received
if (opt == INPUT) {
if (++test_step == 3) {
END_TEST_LOOP;
} else {
retries = 0;
send_request = true;
}
}
counter++;
}
void test_emac_unicast()
{
RESET_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);
RESET_ALL_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SEND | 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);
if (ECHO_SERVER_ADDRESS_KNOWN) {
START_TEST_LOOP(test_emac_unicast_cb, 1 * SECOND_TO_MS);
}
PRINT_ERROR_FLAGS;

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2018, 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_burst_cb(int opt)
{
static bool send_request = true;
static int no_response_cnt = 0;
static int retries = 0;
static int msg_len = 0;
static int test_step = 0;
// Timeout
if (opt == TIMEOUT && send_request) {
CTP_MSG_SEND(msg_len, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr(), 0);
send_request = false;
no_response_cnt = 0;
} else if (opt == TIMEOUT) {
if (++no_response_cnt > 50) {
if (++retries > 5) {
printf("too many retries\r\n\r\n");
SET_ERROR_FLAGS(TEST_FAILED);
END_TEST_LOOP;
} else {
printf("retry count %i\r\n\r\n", retries);
send_request = true;
}
}
}
// Echo response received
if (opt == INPUT) {
if (msg_len == ETH_MAX_LEN) {
if (++test_step > 10) {
END_TEST_LOOP;
} else {
msg_len = 0;
}
} else if (msg_len + 50 >= ETH_MAX_LEN) {
msg_len = ETH_MAX_LEN;
} else {
msg_len += 50;
}
printf("message length %i\r\n\r\n", msg_len);
retries = 0;
send_request = true;
}
}
void test_emac_unicast_burst()
{
RESET_ALL_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SEND | TRACE_SUCCESS | TRACE_FAILURE);
if (ECHO_SERVER_ADDRESS_KNOWN) {
START_TEST_LOOP(test_emac_unicast_burst_cb, 100);
}
PRINT_ERROR_FLAGS;
TEST_ASSERT_FALSE(ERROR_FLAGS);
RESET_OUTGOING_MSG_DATA;
}
#endif

View File

@ -28,40 +28,53 @@
using namespace utest::v1;
void test_emac_unicast_frame_len_cb(void)
void test_emac_unicast_frame_len_cb(int opt)
{
emac_if_validate_outgoing_msg();
static bool send_request = true;
static int no_response_cnt = 0;
static int retries = 0;
static int msg_len = 0;
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;
// Timeout
if (opt == TIMEOUT && send_request) {
CTP_MSG_SEND(msg_len, emac_if_get_echo_server_addr(0), emac_if_get_own_addr(), emac_if_get_own_addr(), 0);
send_request = false;
no_response_cnt = 0;
} else if (opt == TIMEOUT) {
if (++no_response_cnt > 5) {
if (++retries > 5) {
printf("too many retries\r\n\r\n");
SET_ERROR_FLAGS(TEST_FAILED);
END_TEST_LOOP;
} else {
printf("retry count %i\r\n\r\n", retries);
send_request = true;
}
}
}
if (counter > 18) {
if (emac_if_count_outgoing_msg() == 0) {
worker_loop_end();
// Echo response received
if (opt == INPUT) {
if (msg_len == ETH_MAX_LEN) {
END_TEST_LOOP;
} else if (msg_len + 50 >= ETH_MAX_LEN) {
msg_len = ETH_MAX_LEN;
} else {
msg_len += 50;
}
printf("message length %i\r\n\r\n", msg_len);
retries = 0;
send_request = true;
}
counter++;
}
void test_emac_unicast_frame_len()
{
RESET_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SUCCESS | TRACE_FAILURE);
RESET_ALL_ERROR_FLAGS;
SET_TRACE_LEVEL(TRACE_SEND | TRACE_SUCCESS | TRACE_FAILURE);
if (emac_if_count_echo_server_addr()) {
worker_loop_start(test_emac_unicast_frame_len_cb, 1 * SECOND_TO_MS);
if (ECHO_SERVER_ADDRESS_KNOWN) {
START_TEST_LOOP(test_emac_unicast_frame_len_cb, 100);
}
PRINT_ERROR_FLAGS;

View File

@ -22,5 +22,8 @@ void test_emac_initialize();
void test_emac_broadcast();
void test_emac_unicast();
void test_emac_unicast_frame_len();
void test_emac_unicast_burst();
void test_emac_multicast_filter();
void test_emac_memory();
#endif /* EMAC_TESTS_H */

View File

@ -22,6 +22,16 @@
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
#include "mbed.h"
#if MBED_CONF_APP_TEST_EMAC
#include "EMAC.h"
#include "EMACMemoryManager.h"
#include "emac_TestMemoryManager.h"
#else
extern "C" { // netif input
#include "tcpip.h"
}
@ -29,6 +39,8 @@ extern "C" { // netif input
#include "emac_api.h"
#include "emac_stack_mem.h"
#endif
#include "emac_tests.h"
#include "emac_initialize.h"
#include "emac_util.h"
@ -37,6 +49,7 @@ extern "C" { // netif input
using namespace utest::v1;
typedef struct {
int length;
int receipt_number;
@ -58,23 +71,34 @@ extern struct netif *netif_list;
// Broadcast address
const unsigned char eth_mac_broadcast_addr[ETH_MAC_ADDR_LEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
// MTU size
static int eth_mtu_size = 0;
// Event queue
static EventQueue worker_loop_event_queue;
static rtos::Semaphore worker_loop_semaphore;
static rtos::Semaphore link_status_semaphore;
static EventQueue worker_loop_event_queue(20 * EVENTS_EVENT_SIZE);
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);
static void link_input_event_cb(void *buf);
static Event<void(void *)> 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;
static bool output_memory = true;
static bool input_memory = true;
static void (*current_test_step_cb_fnc)(int opt);
// 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;
static bool link_up = false;
int emac_if_find_outgoing_msg(int receipt_number)
{
@ -97,7 +121,10 @@ int emac_if_count_outgoing_msg(void)
for (int i = 0; i < OUTGOING_MSG_COUNT; i++) {
if (outgoing_msgs[i].length) {
count++;
if (!(outgoing_msgs[i].flags & RESPONSE_RECEIVED)) {
count++;
}
}
}
@ -197,29 +224,31 @@ void emac_if_validate_outgoing_msg(void)
}
}
void emac_if_update_reply_to_outgoing_msg(int receipt_number, int lenght, int invalid_data_index)
bool emac_if_update_reply_to_outgoing_msg(int receipt_number, int length, 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) {
/* If length of the sent message is smaller than Ethernet minimum frame length, validates against
minimum frame length or sent length (in case frame has been converted to be longer than minimum
length does not validate length) */
if (length != ETH_FRAME_MIN_LEN && outgoing_msgs[outgoing_msg_index].length != length && length < ETH_FRAME_MIN_LEN ) {
outgoing_msgs[outgoing_msg_index].flags |= INVALID_LENGHT;
}
} else {
#endif
if (outgoing_msgs[outgoing_msg_index].length != lenght) {
if (outgoing_msgs[outgoing_msg_index].length != length) {
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;
}
return true;
} else {
return false;
}
}
@ -276,7 +305,15 @@ unsigned int emac_if_get_error_flags(void)
return error_flags_value;
}
void emac_if_reset_error_flags(void)
void emac_if_reset_error_flags(unsigned int error_flags_value)
{
error_flags &= ~error_flags_value;
if (error_flags_value & NO_RESPONSE) {
no_response_cnt = 0;
}
}
void emac_if_reset_all_error_flags(void)
{
error_flags = 0;
no_response_cnt = 0;
@ -307,6 +344,11 @@ void emac_if_set_trace_level(char trace_level_value)
trace_level = trace_level_value;
}
char emac_if_get_trace_level(void)
{
return trace_level;
}
void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, unsigned char *data)
{
int line_len = 0;
@ -324,7 +366,58 @@ void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, unsigned char
printf("\r\n\r\n");
}
void emac_if_set_all_multicast(bool all)
{
#if MBED_CONF_APP_TEST_EMAC
emac_if_get()->set_all_multicast(all);
#endif
}
void emac_if_add_multicast_group(uint8_t *address)
{
#if MBED_CONF_APP_TEST_EMAC
emac_if_get()->add_multicast_group(address);
#endif
}
void emac_if_set_output_memory(bool memory)
{
output_memory = memory;
}
void emac_if_set_input_memory(bool memory)
{
input_memory = memory;
emac_if_set_memory(memory);
}
void emac_if_check_memory(bool output)
{
if (output) {
emac_if_set_memory(output_memory);
} else {
emac_if_set_memory(input_memory);
}
}
void emac_if_set_memory(bool memory)
{
#if MBED_CONF_APP_TEST_EMAC
static bool memory_value = true;
if (memory_value != memory ) {
memory_value = memory;
EmacTestMemoryManager *mem_mngr = emac_m_mngr_get();
mem_mngr->set_memory_available(memory);
}
#endif
}
#if MBED_CONF_APP_TEST_EMAC
void emac_if_link_state_change_cb(bool up)
#else
void emac_if_link_state_change_cb(void *data, bool up)
#endif
{
if (up) {
worker_loop_event.post(LINK_UP);
@ -333,21 +426,29 @@ void emac_if_link_state_change_cb(void *data, bool up)
}
}
void emac_if_link_input_cb(void *data, emac_stack_mem_chain_t *mem_chain_p)
#if MBED_CONF_APP_TEST_EMAC
void emac_if_link_input_cb(void *buf)
#else
void emac_if_link_input_cb(void *data, void *buf)
#endif
{
link_input_event.post(mem_chain_p);
link_input_event.post(buf);
}
static void link_input_event_cb(emac_stack_mem_chain_t *mem_chain_p)
static void link_input_event_cb(void *buf)
{
int lenght = emac_stack_mem_len(0, mem_chain_p);
#if MBED_CONF_APP_TEST_EMAC
int length = emac_m_mngr_get()->get_total_len(buf);
#else
int length = emac_stack_mem_len(0, buf);
#endif
if (lenght >= ETH_FRAME_HEADER_LEN) {
if (length >= 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);
int invalid_data_index = emac_if_memory_buffer_read(buf, 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];
@ -356,76 +457,145 @@ static void link_input_event_cb(emac_stack_mem_chain_t *mem_chain_p)
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 reply has valid receipt number
if (emac_if_update_reply_to_outgoing_msg(receipt_number, length, invalid_data_index)) {
// Checks received messages for errors
emac_if_validate_outgoing_msg();
// Removes not replied retry entries if any
emac_if_reset_outgoing_msg();
// Removes retry entries no response flags
emac_if_reset_error_flags(NO_RESPONSE);
// Calls test loop
worker_loop_event_queue.call(current_test_step_cb_fnc, INPUT);
}
#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);
emac_if_memory_buffer_write(buf, eth_output_frame_data, false);
#if MBED_CONF_APP_TEST_EMAC
emac_if_get()->link_out(buf);
buf = 0;
#else
emac_if_get()->ops.link_out(emac_if_get(), buf);
#endif
#endif
}
emac_if_add_echo_server_addr(&eth_input_frame_data[6]);
emac_stack_mem_free(0, mem_chain_p);
#if !MBED_CONF_APP_TEST_EMAC
emac_stack_mem_free(0, buf);
#endif
if (trace_level & TRACE_ETH_FRAMES) {
printf("LEN %i\r\n\r\n", lenght);
printf("INP> LEN %i\r\n\r\n", length);
const char trace_type[] = "INP>";
emac_if_trace_to_ascii_hex_dump(trace_type, ETH_FRAME_HEADER_LEN, eth_input_frame_data);
}
#if !MBED_CONF_APP_TEST_EMAC
return;
#endif
}
}
#if MBED_CONF_APP_TEST_EMAC
if (buf) {
emac_m_mngr_get()->free(buf);
}
#else
// 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;
struct pbuf *p = (struct pbuf *)buf;
/* 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);
emac_stack_mem_free(0, buf);
}
} else {
emac_stack_mem_free(0, mem_chain_p);
emac_stack_mem_free(0, buf);
}
#endif
}
static unsigned char thread_stack[2048];
void worker_loop(void);
void worker_loop_init(void)
{
static rtos::Thread worker_loop_thread(osPriorityNormal, 2048, thread_stack);
static bool init_done = false;
if (!init_done) {
if (worker_loop_thread.get_state() == Thread::Deleted) {
worker_loop_thread.start(mbed::callback(&worker_loop));
}
init_done = true;
}
}
void worker_loop_start(void (*test_step_cb_fnc)(void), int timeout)
void worker_loop_start(void (*test_step_cb_fnc)(int opt), int timeout)
{
int test_step_cb_timer = worker_loop_event_queue.call_every(timeout, test_step_cb_fnc);
current_test_step_cb_fnc = test_step_cb_fnc;
int test_step_cb_timer = worker_loop_event_queue.call_every(timeout, test_step_cb_fnc, TIMEOUT);
int timeout_outgoing_msg_timer = worker_loop_event_queue.call_every(1000, emac_if_timeout_outgoing_msg);
int validate_outgoing_msg_timer = 0;
if (timeout > 500) {
// For long test step callback timeouts validates messages also between callback timeouts
validate_outgoing_msg_timer = worker_loop_event_queue.call_every(200, emac_if_validate_outgoing_msg);
}
#if MBED_CONF_APP_ECHO_SERVER
worker_loop_event_queue.dispatch_forever();
worker_loop_semaphore.wait();
#else
worker_loop_event_queue.dispatch(600 * SECOND_TO_MS);
worker_loop_semaphore.wait(600 * SECOND_TO_MS);
#endif
worker_loop_event_queue.cancel(test_step_cb_timer);
worker_loop_event_queue.cancel(timeout_outgoing_msg_timer);
if (validate_outgoing_msg_timer) {
worker_loop_event_queue.cancel(validate_outgoing_msg_timer);
}
worker_loop_event_queue.dispatch(5);
osDelay(1000);
}
static void worker_loop_event_cb(int event)
{
if (event == LINK_UP) {
link_up = true;
printf("cable connected\r\n\r\n");
link_status_semaphore.release();
}
if (event == LINK_DOWN) {
link_up = false;
printf("cable disconnected\r\n\r\n");
}
}
void worker_loop_link_up_wait(void)
{
if (!link_up) {
link_status_semaphore.wait();
}
}
void worker_loop_end(void)
{
worker_loop_event_queue.break_dispatch();
worker_loop_semaphore.release();
}
void worker_loop(void)
{
worker_loop_event_queue.dispatch_forever();
}
unsigned char *emac_if_get_own_addr(void)
@ -433,4 +603,14 @@ unsigned char *emac_if_get_own_addr(void)
return (emac_if_get_hw_addr());
}
int emac_if_get_mtu_size()
{
return eth_mtu_size;
}
void emac_if_set_mtu_size(int mtu_size)
{
eth_mtu_size = mtu_size;
}
#endif

View File

@ -25,9 +25,12 @@
extern const unsigned char eth_mac_broadcast_addr[];
// Trace flags
#define TRACE_NONE 0x00
#define TRACE_ETH_FRAMES 0x01
#define TRACE_SUCCESS 0x02
#define TRACE_FAILURE 0x04
#define TRACE_SEND 0x08
// Sets trace level
#define SET_TRACE_LEVEL(level) emac_if_set_trace_level(level)
// Message validation flags
@ -36,8 +39,10 @@ extern const unsigned char eth_mac_broadcast_addr[];
#define INVALID_LENGHT 0x04
#define INVALID_DATA 0x08
#define PRINTED 0x10
#define RESET_OUTGOING_MSG_DATA emac_if_reset_outgoing_msg()
// Validates outgoing messages for replies, sets error flags on error
#define VALIDATE_OUTGOING_MESSAGES emac_if_validate_outgoing_msg();
// Resets message data
#define RESET_OUTGOING_MSG_DATA emac_if_reset_outgoing_msg()
// General error flags
#define TEST_FAILED 0x01
@ -46,15 +51,31 @@ extern const unsigned char eth_mac_broadcast_addr[];
#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 ERROR_FLAGS emac_if_get_error_flags()
#define RESET_ALL_ERROR_FLAGS emac_if_reset_all_error_flags()
#define PRINT_ERROR_FLAGS emac_if_print_error_flags()
#define SET_ERROR_FLAGS(flags) emac_if_set_error_flags(flags)
#define RESET_ERROR_FLAGS(flags) emac_if_reset_error_flags(flags)
#define ETH_FRAME_HEADER_LEN 28
#define ETH_FRAME_MIN_LEN 60
#define ETH_MAC_ADDR_LEN 6
#define TIMEOUT 1
#define INPUT 2
#define ETH_MAX_LEN (14 + emac_if_get_mtu_size())
// Starts test loop. Calls callback function on interval defined by the timeout
// and when input message is received.
#define START_TEST_LOOP(cb_function, timeout) worker_loop_start(cb_function, timeout)
// Ends test loop
#define END_TEST_LOOP { worker_loop_end(); return; }
// Checks if echo server MAC address is known
#define ECHO_SERVER_ADDRESS_KNOWN emac_if_count_echo_server_addr()
int emac_if_find_outgoing_msg(int receipt_number);
void emac_if_free_outgoing_msg(int index);
int emac_if_count_outgoing_msg(void);
@ -71,10 +92,12 @@ 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_reset_all_error_flags(void);
void emac_if_reset_error_flags(unsigned int error_flags_value);
void emac_if_print_error_flags(void);
void emac_if_set_trace_level(char trace_level_value);
char emac_if_get_trace_level();
void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, char *data);
@ -82,12 +105,28 @@ 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);
int emac_if_get_mtu_size();
void emac_if_set_mtu_size(int mtu_size);
#if MBED_CONF_APP_TEST_EMAC
extern void emac_if_link_input_cb(void *buf);
extern void emac_if_link_state_change_cb(bool up);
#else
extern void emac_if_link_input_cb(void *data, void *buf);
extern void emac_if_link_state_change_cb(void *data, bool up);
#endif
void worker_loop_start(void (*test_step_cb_fnc)(void), int timeout);
void emac_if_set_all_multicast(bool all);
void emac_if_add_multicast_group(uint8_t *address);
void emac_if_set_output_memory(bool memory);
void emac_if_set_input_memory(bool memory);
void emac_if_check_memory(bool output);
void emac_if_set_memory(bool memory);
void worker_loop_init(void);
void worker_loop_start(void (*test_step_cb_fnc)(int opt), int timeout);
void worker_loop_end(void);
void emac_if_init_main_thread(void);
void worker_loop_link_up_wait(void);
#endif /* EMAC_UTIL_H */

View File

@ -15,7 +15,8 @@
* limitations under the License.
*/
#if !defined(MBED_CONF_APP_TEST_WIFI) || \
#if !defined(MBED_CONF_APP_TEST_EMAC) || \
!defined(MBED_CONF_APP_TEST_WIFI) || \
!defined(MBED_CONF_APP_TEST_ETHERNET) || \
!defined(MBED_CONF_APP_ECHO_SERVER) || \
!defined(MBED_CONF_APP_WIFI_SCAN) || \
@ -28,17 +29,21 @@
#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 && MBED_CONF_APP_TEST_ETHERNET
#error [NOT_SUPPORTED] Both wifi and ethernet testing cannot 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
#ifndef DEVICE_EMAC
#error [NOT_SUPPORTED] Device EMAC has to be enabled for the target
#endif
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#include "unity.h"
#include "utest.h"
#include "emac_tests.h"
@ -55,11 +60,13 @@ utest::v1::status_t test_setup(const size_t 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)
Case("EMAC initialize", test_emac_initialize),
Case("EMAC broadcast", test_emac_broadcast),
Case("EMAC unicast", test_emac_unicast),
Case("EMAC unicast frame length", test_emac_unicast_frame_len),
Case("EMAC unicast burst", test_emac_unicast_burst),
Case("EMAC multicast filter", test_emac_multicast_filter),
Case("EMAC memory", test_emac_memory)
};
Specification specification(test_setup, cases);

View File

@ -1,12 +1,16 @@
{
"config": {
"test-emac": {
"help": "Test feature-emac version of the emac",
"value": 1
},
"test-ethernet": {
"help": "Enable ethernet testing",
"value": 0
"value": 1
},
"test-wifi": {
"help": "Enable wifi testing",
"value": 1
"value": 0
},
"echo-server": {
"help": "Build test to be echo server",
@ -28,5 +32,10 @@
"help": "WiFi Password",
"value": "\"PASSWORD\""
}
},
"target_overrides": {
"*": {
"nsapi.default-stack": "TEST"
}
}
}