mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #6847 from ARMmbed/feature-emac
Merge feature-emac branch into masterpull/7011/head
commit
13dcef63e3
|
@ -27,10 +27,18 @@
|
|||
|
||||
using namespace utest::v1;
|
||||
|
||||
// Avoid creating the interface twice
|
||||
static NetworkInterface *get_interface()
|
||||
{
|
||||
static NetworkInterface *interface = MBED_CONF_APP_OBJECT_CONSTRUCTION;
|
||||
|
||||
return interface;
|
||||
}
|
||||
|
||||
// Bringing the network up and down
|
||||
template <int COUNT>
|
||||
void test_bring_up_down() {
|
||||
NetworkInterface* net = MBED_CONF_APP_OBJECT_CONSTRUCTION;
|
||||
NetworkInterface* net = get_interface();
|
||||
|
||||
for (int i = 0; i < COUNT; i++) {
|
||||
int err = MBED_CONF_APP_CONNECT_STATEMENT;
|
||||
|
|
|
@ -1,28 +1,163 @@
|
|||
# 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, you need to configure one device in the Ethernet segment 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 you configure a device to be a CTP echo server, it starts to forward CTP messages automatically when it is on and continues to do so until you switch it off.
|
||||
|
||||
# Test cases
|
||||
## Other configuration options
|
||||
|
||||
## EMAC interface initialise
|
||||
Default configuration files included with tests are configured for ethernet. For Wi-Fi, set `test-ethernet` to 0 and `test-wifi` to 1. You also need to configure Wi-Fi SSID and security options to the configuration file.
|
||||
|
||||
Initializes EMAC interface driver.
|
||||
## Example commands
|
||||
|
||||
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.
|
||||
### CTP echo server
|
||||
|
||||
## EMAC interface broadcast
|
||||
You can use the following command to a build CTP echo server:
|
||||
|
||||
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.
|
||||
`mbed test --compile -m TARGET -t GCC_ARM -v -n tests-network-emac --app-config TESTS/network/emac/template_mbed_app_echo_server.txt`
|
||||
|
||||
## EMAC interface unicast
|
||||
Replace TARGET with the target device. After building, flash the binary to the CTP echo server device.
|
||||
|
||||
Sends three CTP unicast messages to the CTP echo server. Verifies that all are replied.
|
||||
You can verify that the CTP echo server has started properly by making a terminal connection to the device, resetting it and verifying that `echo server started successfully` prints on the terminal. You can run host tests when the CTP echo server is running on the Ethernet segment.
|
||||
|
||||
## EMAC interface unicast frame length
|
||||
For Wi-Fi tests, the CTP echo server can also run on the ethernet side as long as the network configuration is such that Ethernet frames are routed between Wi-Fi and Ethernet.
|
||||
|
||||
The CTP echo server sends a 100-byte long broadcast CTP Ethernet frame every 60 seconds to inform the network of its presence.
|
||||
|
||||
### Running tests
|
||||
|
||||
You can use the following command to run tests:
|
||||
|
||||
`mbed test --compile --run -m TARGET -t GCC_ARM -v -n tests-network-emac --app-config TESTS/network/emac/template_mbed_app.txt`
|
||||
|
||||
Replace TARGET with the target device.
|
||||
|
||||
## Traces
|
||||
|
||||
Test cases have different trace levels based on how much tracing can be done without affecting the performance of the test case. Configure tracing using `SET\_TRACE\_LEVEL` macro.
|
||||
|
||||
For example, the `EMAC broadcast` test enables send, input CTP frame, success and failure traces:
|
||||
|
||||
`SET_TRACE_LEVEL(TRACE_SEND | TRACE_ETH_FRAMES | TRACE_SUCCESS | TRACE_FAILURE);`
|
||||
|
||||
This example trace is about a message sent to broadcast address `ff:ff:ff:ff:ff:ff` that an echo server answers:
|
||||
|
||||
```
|
||||
|
||||
message sent ff:ff:ff:ff:ff:ff
|
||||
|
||||
response: receipt number 24 LENGTH OK DATA OK BROADCAST
|
||||
|
||||
```
|
||||
|
||||
This example trace is about a message sent to broadcast address `ff:ff:ff:ff:ff:ff` that an echo server does not answer:
|
||||
|
||||
```
|
||||
|
||||
message sent ff:ff:ff:ff:ff:ff
|
||||
|
||||
NO RESPONSE: receipt number 25
|
||||
|
||||
```
|
||||
|
||||
This example is about input message trace. Message hex dump contains the first 32 bytes of the received Ethernet frame:
|
||||
|
||||
```
|
||||
|
||||
INP> LEN 100
|
||||
|
||||
INP> 000000 ff ff ff ff ff ff ba 42 ed 79 11 8a 90 00
|
||||
|
||||
INP> 00000e 00 00 02 00 ba 42 ed 79 11 8a 01 00 01 00
|
||||
|
||||
```
|
||||
|
||||
To verify whether the echo server is receiving CTP Ethernet frames, enable `echo-server-trace` option in the echo server configuration file. You can use this for debugging purposes. Do not run performance tests such as `EMAC unicast burst` with tracing enabled because tracing affects the echo server performance.
|
||||
|
||||
## Test cases
|
||||
|
||||
### EMAC initialize
|
||||
|
||||
The test case initializes the EMAC driver and the test network 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`.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
The `emac_if_init()` function of the test case configures and powers up the EMAC.
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -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_WIFI || MBED_CONF_APP_TEST_ETHERNET
|
||||
|
||||
#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
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* 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_WIFI || MBED_CONF_APP_TEST_ETHERNET
|
||||
|
||||
#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
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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_WIFI || MBED_CONF_APP_TEST_ETHERNET
|
||||
|
||||
#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;
|
||||
|
||||
EmacTestMemoryManager *memory_manager = &EmacTestMemoryManager::get_instance();
|
||||
emac.set_memory_manager(*memory_manager);
|
||||
|
||||
*interface_out = m_interface;
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
EmacTestNetworkStack::Interface::Interface()
|
||||
: m_emac(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void EmacTestNetworkStack::Interface::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
nsapi_connection_status_t EmacTestNetworkStack::Interface::get_connection_status() const
|
||||
{
|
||||
return NSAPI_STATUS_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
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, bool blocking)
|
||||
{
|
||||
if (!emac_if_init(m_emac)) {
|
||||
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
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* 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_WIFI || MBED_CONF_APP_TEST_ETHERNET
|
||||
|
||||
#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.
|
||||
* @param blocking Specify whether bringup blocks for connection completion.
|
||||
* @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 = DEFAULT_STACK,
|
||||
bool blocking = true
|
||||
);
|
||||
|
||||
/** 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();
|
||||
|
||||
/** Register callback for status reporting
|
||||
*
|
||||
* The specified status callback function will be called on status changes
|
||||
* on the network. The parameters on the callback are the event type and
|
||||
* event-type dependent reason parameter.
|
||||
*
|
||||
* @param status_cb The callback for status changes
|
||||
*/
|
||||
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
|
||||
|
||||
/** Get the connection status
|
||||
*
|
||||
* @return The connection status according to ConnectionStatusType
|
||||
*/
|
||||
virtual nsapi_connection_status_t get_connection_status() const;
|
||||
|
||||
/** 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
|
|
@ -23,10 +23,9 @@
|
|||
|
||||
#include "mbed.h"
|
||||
|
||||
#include "lwip/opt.h" /* ETH_PAD_SIZE */
|
||||
|
||||
#include "emac_stack_mem.h"
|
||||
#include "emac_api.h"
|
||||
#include "EMAC.h"
|
||||
#include "EMACMemoryManager.h"
|
||||
#include "emac_TestMemoryManager.h"
|
||||
|
||||
#include "emac_tests.h"
|
||||
#include "emac_ctp.h"
|
||||
|
@ -100,13 +99,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 +116,22 @@ 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);
|
||||
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);
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
SET_ERROR_FLAGS(NO_FREE_MEM_BUF);
|
||||
emac_if_free_outgoing_msg(outgoing_msg_index);
|
||||
return;
|
||||
|
@ -131,12 +145,11 @@ 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);
|
||||
emac_if_check_memory(true);
|
||||
emac_if_get()->link_out(buf);
|
||||
emac_if_check_memory(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
#ifndef EMAC_INITIALIZE_H
|
||||
#define EMAC_INITIALIZE_H
|
||||
|
||||
uint8_t *emac_if_get_hw_addr(void);
|
||||
emac_interface_t *emac_if_get(void);
|
||||
unsigned char *emac_if_get_hw_addr(void);
|
||||
bool emac_if_init(EMAC *emac);
|
||||
EMAC *emac_if_get(void);
|
||||
EmacTestMemoryManager *emac_m_mngr_get(void);
|
||||
|
||||
#endif /* EMAC_INITIALIZE_H */
|
||||
|
|
|
@ -24,23 +24,26 @@
|
|||
|
||||
#include "lwip/opt.h" /* ETH_PAD_SIZE */
|
||||
|
||||
#include "emac_api.h"
|
||||
#include "emac_stack_mem.h"
|
||||
#include "mbed.h"
|
||||
|
||||
#include "EMAC.h"
|
||||
#include "EMACMemoryManager.h"
|
||||
#include "emac_TestMemoryManager.h"
|
||||
|
||||
#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)
|
||||
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)) {
|
||||
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 (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 (; index < buf_payload_len; index++) {
|
||||
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 {
|
||||
|
@ -51,22 +54,20 @@ int emac_if_memory_buffer_read(emac_stack_mem_chain_t *mem_chain_p, unsigned cha
|
|||
}
|
||||
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)
|
||||
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)) {
|
||||
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 (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 (; index < buf_payload_len; index++) {
|
||||
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) {
|
||||
|
@ -76,7 +77,6 @@ void emac_if_memory_buffer_write(emac_stack_mem_chain_t *mem_chain_p, unsigned c
|
|||
}
|
||||
eth_frame_index++;
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -28,42 +28,73 @@
|
|||
|
||||
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
|
||||
printf("echo server started successfully\r\n\r\n");
|
||||
counter = 255;
|
||||
#else
|
||||
worker_loop_end();
|
||||
static bool echo_server_started = false;
|
||||
if (!echo_server_started) {
|
||||
#if MBED_CONF_APP_ECHO_SERVER_TRACE == 0
|
||||
SET_TRACE_LEVEL(TRACE_NONE);
|
||||
#endif
|
||||
printf("echo server started successfully\r\n\r\n");
|
||||
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");
|
||||
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);
|
||||
|
|
|
@ -15,55 +15,64 @@
|
|||
* 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
|
||||
|
||||
#include "inttypes.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include "EMAC.h"
|
||||
#include "EMACMemoryManager.h"
|
||||
#include "emac_TestMemoryManager.h"
|
||||
#include "emac_TestNetworkStack.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_initialize.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);
|
||||
EMAC *emac_handle = NULL;
|
||||
|
||||
void test_emac_initialize()
|
||||
{
|
||||
#if MBED_CONF_APP_TEST_WIFI
|
||||
static WiFiInterface *wifi;
|
||||
worker_loop_init();
|
||||
|
||||
#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
|
||||
wifi = new OdinWiFiInterface;
|
||||
#endif
|
||||
#ifdef TARGET_REALTEK_RTL8195AM
|
||||
wifi = new RTWInterface;
|
||||
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 = wifi->scan(ap, 30);
|
||||
int size = network_interface->scan(ap, 30);
|
||||
|
||||
for (int i=0; i<size; i++) {
|
||||
const char *ssid = ap[i].get_ssid();
|
||||
|
@ -79,14 +88,14 @@ void test_emac_initialize()
|
|||
}
|
||||
#endif
|
||||
|
||||
wifi->set_credentials(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, MBED_CONF_APP_WIFI_SECURITY);
|
||||
wifi->connect();
|
||||
network_interface->set_credentials(MBED_CONF_APP_WIFI_SSID, MBED_CONF_APP_WIFI_PASSWORD, MBED_CONF_APP_WIFI_SECURITY);
|
||||
|
||||
const char *ip_addr = wifi->get_ip_address();
|
||||
printf("connected IP %s\r\n\r\n", ip_addr);
|
||||
#endif
|
||||
|
||||
TEST_ASSERT(emac_if_init());
|
||||
// Power up the interface and emac driver
|
||||
network_interface->connect();
|
||||
|
||||
worker_loop_link_up_wait();
|
||||
}
|
||||
|
||||
unsigned char *emac_if_get_hw_addr(void)
|
||||
|
@ -94,46 +103,46 @@ unsigned char *emac_if_get_hw_addr(void)
|
|||
return ð_mac_addr[0];
|
||||
}
|
||||
|
||||
emac_interface_t *emac_if_get(void)
|
||||
EMAC *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
|
||||
return emac_handle;
|
||||
}
|
||||
|
||||
static bool emac_if_init(void)
|
||||
EmacTestMemoryManager *emac_m_mngr_get(void)
|
||||
{
|
||||
emac_interface_t *emac_if = emac_if_get();
|
||||
return &EmacTestMemoryManager::get_instance();
|
||||
}
|
||||
|
||||
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);
|
||||
bool emac_if_init(EMAC *emac)
|
||||
{
|
||||
emac_handle = emac;
|
||||
|
||||
int hwaddr_len = emac_if->ops.get_hwaddr_size(emac_if);
|
||||
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);
|
||||
|
||||
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]);
|
||||
}
|
||||
TEST_ASSERT_MESSAGE(hwaddr_len == 6, "invalid emac hwaddr length!");
|
||||
|
||||
int mtu = emac_if->ops.get_mtu_size(emac_if);
|
||||
printf("emac mtu %i\r\n\r\n", mtu);
|
||||
// If driver updates this, write it back, otherwise write default from mbed_mac_address
|
||||
mbed_mac_address(reinterpret_cast<char *>(ð_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_if->ops.get_ifname(emac_if, hw_name, 10);
|
||||
emac->get_ifname(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -22,12 +22,11 @@
|
|||
|
||||
#if MBED_CONF_APP_TEST_WIFI || MBED_CONF_APP_TEST_ETHERNET
|
||||
|
||||
extern "C" { // netif input
|
||||
#include "tcpip.h"
|
||||
}
|
||||
#include "mbed.h"
|
||||
|
||||
#include "emac_api.h"
|
||||
#include "emac_stack_mem.h"
|
||||
#include "EMAC.h"
|
||||
#include "EMACMemoryManager.h"
|
||||
#include "emac_TestMemoryManager.h"
|
||||
|
||||
#include "emac_tests.h"
|
||||
#include "emac_initialize.h"
|
||||
|
@ -58,23 +57,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 +107,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 +210,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 +291,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 +330,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 +352,48 @@ void emac_if_trace_to_ascii_hex_dump(const char *prefix, int len, unsigned char
|
|||
printf("\r\n\r\n");
|
||||
}
|
||||
|
||||
void emac_if_link_state_change_cb(void *data, bool up)
|
||||
void emac_if_set_all_multicast(bool all)
|
||||
{
|
||||
emac_if_get()->set_all_multicast(all);
|
||||
}
|
||||
|
||||
void emac_if_add_multicast_group(uint8_t *address)
|
||||
{
|
||||
emac_if_get()->add_multicast_group(address);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void emac_if_link_state_change_cb(bool up)
|
||||
{
|
||||
if (up) {
|
||||
worker_loop_event.post(LINK_UP);
|
||||
|
@ -333,21 +402,21 @@ 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)
|
||||
void emac_if_link_input_cb(void *buf)
|
||||
{
|
||||
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);
|
||||
int length = emac_m_mngr_get()->get_total_len(buf);
|
||||
|
||||
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 +425,114 @@ 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);
|
||||
emac_if_get()->link_out(buf);
|
||||
buf = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
emac_if_add_echo_server_addr(ð_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);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
if (buf) {
|
||||
emac_m_mngr_get()->free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void worker_loop_start(void (*test_step_cb_fnc)(void), int timeout)
|
||||
static unsigned char thread_stack[2048];
|
||||
|
||||
void worker_loop(void);
|
||||
|
||||
void worker_loop_init(void)
|
||||
{
|
||||
int test_step_cb_timer = worker_loop_event_queue.call_every(timeout, test_step_cb_fnc);
|
||||
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)(int opt), int timeout)
|
||||
{
|
||||
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) {
|
||||
printf("cable connected\r\n\r\n");
|
||||
link_up = true;
|
||||
link_status_semaphore.release();
|
||||
}
|
||||
|
||||
if (event == LINK_DOWN) {
|
||||
printf("cable disconnected\r\n\r\n");
|
||||
link_up = false;
|
||||
}
|
||||
}
|
||||
|
||||
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 +540,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
|
||||
|
|
|
@ -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,23 @@ 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);
|
||||
int emac_if_get_mtu_size();
|
||||
void emac_if_set_mtu_size(int mtu_size);
|
||||
|
||||
void worker_loop_start(void (*test_step_cb_fnc)(void), int timeout);
|
||||
extern void emac_if_link_input_cb(void *buf);
|
||||
extern void emac_if_link_state_change_cb(bool up);
|
||||
|
||||
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 */
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
* 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) || \
|
||||
#if !defined(MBED_CONF_APP_TEST_WIFI) || \
|
||||
!defined(MBED_CONF_APP_TEST_ETHERNET) || \
|
||||
!defined(MBED_CONF_APP_ECHO_SERVER) || \
|
||||
!defined(MBED_CONF_APP_ECHO_SERVER_TRACE) || \
|
||||
!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
|
||||
|
@ -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);
|
||||
|
|
|
@ -2,16 +2,20 @@
|
|||
"config": {
|
||||
"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",
|
||||
"value": 0
|
||||
},
|
||||
"echo-server-trace": {
|
||||
"help": "Trace incoming messages on echo server",
|
||||
"value": 0
|
||||
},
|
||||
"wifi-scan": {
|
||||
"help": "Scan and list access points",
|
||||
"value": 0
|
||||
|
@ -28,5 +32,10 @@
|
|||
"help": "WiFi Password",
|
||||
"value": "\"PASSWORD\""
|
||||
}
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"nsapi.default-stack": "TEST"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"config": {
|
||||
"test-ethernet": {
|
||||
"help": "Enable ethernet testing",
|
||||
"value": 1
|
||||
},
|
||||
"test-wifi": {
|
||||
"help": "Enable wifi testing",
|
||||
"value": 0
|
||||
},
|
||||
"echo-server": {
|
||||
"help": "Build test to be echo server",
|
||||
"value": 1
|
||||
},
|
||||
"echo-server-trace": {
|
||||
"help": "Trace incoming messages on 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\""
|
||||
}
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"nsapi.default-stack": "TEST"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2070,6 +2070,7 @@ PREDEFINED = DOXYGEN_ONLY \
|
|||
DEVICE_CAN \
|
||||
DEVICE_ETHERNET \
|
||||
DEVICE_EMAC \
|
||||
DEVICE_ETH \
|
||||
DEVICE_FLASH \
|
||||
DEVICE_I2C \
|
||||
DEVICE_I2CSLAVE \
|
||||
|
|
|
@ -4,3 +4,4 @@ lwip/src/apps/*
|
|||
lwip/src/netif/lwip_slipif.c
|
||||
lwip/src/include/lwip/apps/*
|
||||
lwip/src/include/posix/*
|
||||
lwip-eth/*
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
/* LWIP implementation of NetworkInterfaceAPI
|
||||
* Copyright (c) 2015 ARM Limited
|
||||
*
|
||||
* 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 "EthernetInterface.h"
|
||||
#include "lwip_stack.h"
|
||||
|
||||
|
||||
/* Interface implementation */
|
||||
EthernetInterface::EthernetInterface() :
|
||||
_dhcp(true),
|
||||
_ip_address(),
|
||||
_netmask(),
|
||||
_gateway(),
|
||||
_connection_status_cb(NULL),
|
||||
_connect_status(NSAPI_STATUS_DISCONNECTED)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsapi_error_t EthernetInterface::set_network(const char *ip_address, const char *netmask, const char *gateway)
|
||||
{
|
||||
_dhcp = false;
|
||||
|
||||
strncpy(_ip_address, ip_address ? ip_address : "", sizeof(_ip_address));
|
||||
_ip_address[sizeof(_ip_address) - 1] = '\0';
|
||||
strncpy(_netmask, netmask ? netmask : "", sizeof(_netmask));
|
||||
_netmask[sizeof(_netmask) - 1] = '\0';
|
||||
strncpy(_gateway, gateway ? gateway : "", sizeof(_gateway));
|
||||
_gateway[sizeof(_gateway) - 1] = '\0';
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t EthernetInterface::set_dhcp(bool dhcp)
|
||||
{
|
||||
_dhcp = dhcp;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t EthernetInterface::connect()
|
||||
{
|
||||
return mbed_lwip_bringup_2(_dhcp, false,
|
||||
_ip_address[0] ? _ip_address : 0,
|
||||
_netmask[0] ? _netmask : 0,
|
||||
_gateway[0] ? _gateway : 0,
|
||||
DEFAULT_STACK);
|
||||
}
|
||||
|
||||
nsapi_error_t EthernetInterface::disconnect()
|
||||
{
|
||||
return mbed_lwip_bringdown_2(false);
|
||||
}
|
||||
|
||||
const char *EthernetInterface::get_mac_address()
|
||||
{
|
||||
return mbed_lwip_get_mac_address();
|
||||
}
|
||||
|
||||
const char *EthernetInterface::get_ip_address()
|
||||
{
|
||||
if (mbed_lwip_get_ip_address(_ip_address, sizeof _ip_address)) {
|
||||
return _ip_address;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *EthernetInterface::get_netmask()
|
||||
{
|
||||
if (mbed_lwip_get_netmask(_netmask, sizeof _netmask)) {
|
||||
return _netmask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *EthernetInterface::get_gateway()
|
||||
{
|
||||
if (mbed_lwip_get_gateway(_gateway, sizeof _gateway)) {
|
||||
return _gateway;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetworkStack *EthernetInterface::get_stack()
|
||||
{
|
||||
return nsapi_create_stack(&lwip_stack);
|
||||
}
|
||||
|
||||
void EthernetInterface::attach(
|
||||
Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
_connection_status_cb = status_cb;
|
||||
mbed_lwip_attach(netif_status_cb, this);
|
||||
}
|
||||
|
||||
nsapi_connection_status_t EthernetInterface::get_connection_status() const
|
||||
{
|
||||
return _connect_status;
|
||||
}
|
||||
|
||||
void EthernetInterface::netif_status_cb(void *ethernet_if_ptr,
|
||||
nsapi_event_t reason, intptr_t parameter)
|
||||
{
|
||||
EthernetInterface *eth_ptr = static_cast<EthernetInterface*>(ethernet_if_ptr);
|
||||
eth_ptr->_connect_status = (nsapi_connection_status_t)parameter;
|
||||
if (eth_ptr->_connection_status_cb)
|
||||
{
|
||||
eth_ptr->_connection_status_cb(reason, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t EthernetInterface::set_blocking(bool blocking)
|
||||
{
|
||||
mbed_lwip_set_blocking(blocking);
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
|
@ -0,0 +1,619 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include "nsapi.h"
|
||||
#include "mbed_interface.h"
|
||||
#include "mbed_assert.h"
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/dhcp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/udp.h"
|
||||
|
||||
#include "ppp_lwip.h"
|
||||
|
||||
#include "LWIPStack.h"
|
||||
|
||||
static void add_dns_addr_to_dns_list_index(const u8_t addr_type, const u8_t index)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (addr_type == IPADDR_TYPE_V6) {
|
||||
/* 2001:4860:4860::8888 google */
|
||||
ip_addr_t ipv6_dns_addr = IPADDR6_INIT(
|
||||
PP_HTONL(0x20014860UL),
|
||||
PP_HTONL(0x48600000UL),
|
||||
PP_HTONL(0x00000000UL),
|
||||
PP_HTONL(0x00008888UL));
|
||||
dns_setserver(index, &ipv6_dns_addr);
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV4
|
||||
if (addr_type == IPADDR_TYPE_V4) {
|
||||
/* 8.8.8.8 google */
|
||||
ip_addr_t ipv4_dns_addr = IPADDR4_INIT(0x08080808);
|
||||
dns_setserver(index, &ipv4_dns_addr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int get_ip_addr_type(const ip_addr_t *ip_addr)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(ip_addr)) {
|
||||
return IPADDR_TYPE_V6;
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(ip_addr)) {
|
||||
return IPADDR_TYPE_V4;
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV6 && LWIP_IPV4
|
||||
return IPADDR_TYPE_ANY;
|
||||
#endif
|
||||
}
|
||||
|
||||
void LWIP::add_dns_addr(struct netif *lwip_netif)
|
||||
{
|
||||
// Check for existing dns address
|
||||
for (char numdns = 0; numdns < DNS_MAX_SERVERS; numdns++) {
|
||||
const ip_addr_t *dns_ip_addr = dns_getserver(numdns);
|
||||
if (!ip_addr_isany(dns_ip_addr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get preferred ip version
|
||||
const ip_addr_t *ip_addr = get_ip_addr(false, lwip_netif);
|
||||
u8_t addr_type = IPADDR_TYPE_ANY;
|
||||
|
||||
// Add preferred ip version dns address to index 0
|
||||
if (ip_addr) {
|
||||
addr_type = get_ip_addr_type(ip_addr);
|
||||
add_dns_addr_to_dns_list_index(addr_type, 0);
|
||||
}
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
if (!ip_addr) {
|
||||
// Get address for any ip version
|
||||
ip_addr = get_ip_addr(true, lwip_netif);
|
||||
if (!ip_addr) {
|
||||
return;
|
||||
}
|
||||
addr_type = get_ip_addr_type(ip_addr);
|
||||
// Add the dns address to index 0
|
||||
add_dns_addr_to_dns_list_index(addr_type, 0);
|
||||
}
|
||||
|
||||
if (addr_type == IPADDR_TYPE_V4) {
|
||||
// If ipv4 is preferred and ipv6 is available add ipv6 dns address to index 1
|
||||
ip_addr = get_ipv6_addr(lwip_netif);
|
||||
} else if (addr_type == IPADDR_TYPE_V6) {
|
||||
// If ipv6 is preferred and ipv4 is available add ipv4 dns address to index 1
|
||||
ip_addr = get_ipv4_addr(lwip_netif);
|
||||
} else {
|
||||
ip_addr = NULL;
|
||||
}
|
||||
|
||||
if (ip_addr) {
|
||||
addr_type = get_ip_addr_type(ip_addr);
|
||||
add_dns_addr_to_dns_list_index(addr_type, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::Interface::set_dhcp()
|
||||
{
|
||||
netif_set_up(&netif);
|
||||
|
||||
#if LWIP_DHCP
|
||||
if (dhcp_has_to_be_set) {
|
||||
err_t err = dhcp_start(&netif);
|
||||
dhcp_has_to_be_set = false;
|
||||
if (err) {
|
||||
connected = NSAPI_STATUS_DISCONNECTED;
|
||||
if (client_callback) {
|
||||
client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
|
||||
}
|
||||
return NSAPI_ERROR_DHCP_FAILURE;
|
||||
}
|
||||
dhcp_started = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
void LWIP::Interface::netif_link_irq(struct netif *netif)
|
||||
{
|
||||
LWIP::Interface *interface = static_cast<LWIP::Interface *>(netif->state);
|
||||
|
||||
if (netif_is_link_up(&interface->netif)) {
|
||||
nsapi_error_t dhcp_status = interface->set_dhcp();
|
||||
|
||||
if (interface->blocking && dhcp_status == NSAPI_ERROR_OK) {
|
||||
osSemaphoreRelease(interface->linked);
|
||||
} else if (dhcp_status != NSAPI_ERROR_OK) {
|
||||
netif_set_down(&interface->netif);
|
||||
}
|
||||
} else {
|
||||
osSemaphoreRelease(interface->unlinked);
|
||||
netif_set_down(&interface->netif);
|
||||
}
|
||||
}
|
||||
|
||||
void LWIP::Interface::netif_status_irq(struct netif *netif)
|
||||
{
|
||||
LWIP::Interface *interface = static_cast<LWIP::Interface *>(netif->state);
|
||||
|
||||
if (netif_is_up(&interface->netif)) {
|
||||
bool dns_addr_has_to_be_added = false;
|
||||
if (!(interface->has_addr_state & HAS_ANY_ADDR) && LWIP::get_ip_addr(true, netif)) {
|
||||
if (interface->blocking) {
|
||||
osSemaphoreRelease(interface->has_any_addr);
|
||||
}
|
||||
interface->has_addr_state |= HAS_ANY_ADDR;
|
||||
dns_addr_has_to_be_added = true;
|
||||
}
|
||||
#if PREF_ADDR_TIMEOUT
|
||||
if (!(interface->has_addr_state & HAS_PREF_ADDR) && LWIP::get_ip_addr(false, netif)) {
|
||||
if (interface->blocking) {
|
||||
osSemaphoreRelease(interface->has_pref_addr);
|
||||
}
|
||||
interface->has_addr_state |= HAS_PREF_ADDR;
|
||||
dns_addr_has_to_be_added = true;
|
||||
}
|
||||
#endif
|
||||
#if BOTH_ADDR_TIMEOUT
|
||||
if (!(interface->has_addr_state & HAS_BOTH_ADDR) && LWIP::get_ipv4_addr(netif) && LWIP::get_ipv6_addr(netif)) {
|
||||
if (interface->blocking) {
|
||||
osSemaphoreRelease(interface->has_both_addr);
|
||||
}
|
||||
interface->has_addr_state |= HAS_BOTH_ADDR;
|
||||
dns_addr_has_to_be_added = true;
|
||||
}
|
||||
#endif
|
||||
if (dns_addr_has_to_be_added && !interface->blocking) {
|
||||
add_dns_addr(&interface->netif);
|
||||
}
|
||||
|
||||
|
||||
if (interface->has_addr_state & HAS_ANY_ADDR) {
|
||||
interface->connected = NSAPI_STATUS_GLOBAL_UP;
|
||||
}
|
||||
} else {
|
||||
interface->connected = NSAPI_STATUS_DISCONNECTED;
|
||||
}
|
||||
|
||||
if (interface->client_callback) {
|
||||
interface->client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, interface->connected);
|
||||
}
|
||||
}
|
||||
|
||||
void LWIP::Interface::attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
client_callback = status_cb;
|
||||
}
|
||||
|
||||
nsapi_connection_status_t LWIP::Interface::get_connection_status() const
|
||||
{
|
||||
return connected;
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
static void mbed_lwip_clear_ipv6_addresses(struct netif *netif)
|
||||
{
|
||||
for (u8_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
|
||||
netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
char *LWIP::Interface::get_mac_address(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
(void) snprintf(buf, buflen, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
netif.hwaddr[0], netif.hwaddr[1], netif.hwaddr[2],
|
||||
netif.hwaddr[3], netif.hwaddr[4], netif.hwaddr[5]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *LWIP::Interface::get_ip_address(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
const ip_addr_t *addr = LWIP::get_ip_addr(true, &netif);
|
||||
if (!addr) {
|
||||
return NULL;
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(addr)) {
|
||||
return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen);
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(addr)) {
|
||||
return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen);
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV6 && LWIP_IPV4
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
char *LWIP::Interface::get_netmask(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
#if LWIP_IPV4
|
||||
const ip4_addr_t *addr = netif_ip4_netmask(&netif);
|
||||
if (!ip4_addr_isany(addr)) {
|
||||
return ip4addr_ntoa_r(addr, buf, buflen);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
char *LWIP::Interface::get_gateway(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
#if LWIP_IPV4
|
||||
const ip4_addr_t *addr = netif_ip4_gw(&netif);
|
||||
if (!ip4_addr_isany(addr)) {
|
||||
return ip4addr_ntoa_r(addr, buf, buflen);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
LWIP::Interface::Interface() :
|
||||
hw(NULL), has_addr_state(0),
|
||||
connected(NSAPI_STATUS_DISCONNECTED),
|
||||
dhcp_started(false), dhcp_has_to_be_set(false), blocking(true), ppp(false)
|
||||
{
|
||||
memset(&netif, 0, sizeof netif);
|
||||
|
||||
osSemaphoreAttr_t attr;
|
||||
attr.name = NULL;
|
||||
attr.attr_bits = 0;
|
||||
|
||||
attr.cb_mem = &linked_sem;
|
||||
attr.cb_size = sizeof linked_sem;
|
||||
linked = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
|
||||
attr.cb_mem = &unlinked_sem;
|
||||
attr.cb_size = sizeof unlinked_sem;
|
||||
unlinked = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
|
||||
attr.cb_mem = &has_any_addr_sem;
|
||||
attr.cb_size = sizeof has_any_addr_sem;
|
||||
has_any_addr = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
#if PREF_ADDR_TIMEOUT
|
||||
attr.cb_mem = &has_pref_addr_sem;
|
||||
attr.cb_size = sizeof has_pref_addr_sem;
|
||||
has_pref_addr = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
#endif
|
||||
#if BOTH_ADDR_TIMEOUT
|
||||
attr.cb_mem = &has_both_addr_sem;
|
||||
attr.cb_size = sizeof has_both_addr_sem;
|
||||
has_both_addr = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
#endif
|
||||
|
||||
netif.state = this;
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out)
|
||||
{
|
||||
#if LWIP_ETHERNET
|
||||
Interface *interface = new (std::nothrow) Interface();
|
||||
if (!interface) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
interface->emac = &emac;
|
||||
interface->memory_manager = &memory_manager;
|
||||
interface->ppp = false;
|
||||
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
netif->interface.hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->interface.hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->interface.hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->interface.hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->interface.hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->interface.hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *) interface->netif.hwaddr);
|
||||
#endif
|
||||
|
||||
interface->netif.hwaddr_len = 6;
|
||||
|
||||
if (!netif_add(&interface->netif,
|
||||
#if LWIP_IPV4
|
||||
0, 0, 0,
|
||||
#endif
|
||||
interface, &LWIP::Interface::emac_if_init, tcpip_input)) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (default_if) {
|
||||
netif_set_default(&interface->netif);
|
||||
default_interface = interface;
|
||||
}
|
||||
|
||||
netif_set_link_callback(&interface->netif, &LWIP::Interface::netif_link_irq);
|
||||
netif_set_status_callback(&interface->netif, &LWIP::Interface::netif_status_irq);
|
||||
|
||||
*interface_out = interface;
|
||||
|
||||
/* Use mac address as additional seed to random number generator */
|
||||
uint64_t seed = interface->netif.hwaddr[0];
|
||||
for (uint8_t i = 1; i < 8; i++) {
|
||||
seed <<= 8;
|
||||
seed |= interface->netif.hwaddr[i % 6];
|
||||
}
|
||||
lwip_add_random_seed(seed);
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
#else
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
#endif //LWIP_ETHERNET
|
||||
}
|
||||
|
||||
/* Internal API to preserve existing PPP functionality - revise to better match mbed_ipstak_add_ethernet_interface later */
|
||||
nsapi_error_t LWIP::_add_ppp_interface(void *hw, bool default_if, LWIP::Interface **interface_out)
|
||||
{
|
||||
#if LWIP_PPP
|
||||
Interface *interface = new (nothrow) Interface();
|
||||
if (!interface) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
interface->hw = hw;
|
||||
interface->ppp = true;
|
||||
|
||||
ret = ppp_lwip_if_init(hw, &interface->netif);
|
||||
if (ret != NSAPI_ERROR_OK) {
|
||||
free(interface);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (default_if)
|
||||
netif_set_default(&interface->netif);
|
||||
|
||||
netif_set_link_callback(&interface->netif, mbed_lwip_netif_link_irq);
|
||||
netif_set_status_callback(&interface->netif, mbed_lwip_netif_status_irq);
|
||||
|
||||
*interface_out = interface;
|
||||
|
||||
#else
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
#endif //LWIP_PPP
|
||||
}
|
||||
|
||||
|
||||
nsapi_error_t LWIP::Interface::bringup(bool dhcp, const char *ip, const char *netmask, const char *gw, const nsapi_ip_stack_t stack, bool block)
|
||||
{
|
||||
// Check if we've already connected
|
||||
if (connected == NSAPI_STATUS_GLOBAL_UP) {
|
||||
return NSAPI_ERROR_IS_CONNECTED;
|
||||
} else if (connected == NSAPI_STATUS_CONNECTING) {
|
||||
return NSAPI_ERROR_ALREADY;
|
||||
}
|
||||
|
||||
connected = NSAPI_STATUS_CONNECTING;
|
||||
blocking = block;
|
||||
|
||||
#if LWIP_DHCP
|
||||
if (stack != IPV6_STACK && dhcp) {
|
||||
dhcp_has_to_be_set = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV6
|
||||
if (stack != IPV4_STACK) {
|
||||
if (netif.hwaddr_len == 6) {
|
||||
netif_create_ip6_linklocal_address(&netif, 1/*from MAC*/);
|
||||
}
|
||||
#if LWIP_IPV6_MLD
|
||||
/*
|
||||
* For hardware/netifs that implement MAC filtering.
|
||||
* All-nodes link-local is handled by default, so we must let the hardware know
|
||||
* to allow multicast packets in.
|
||||
* Should set mld_mac_filter previously. */
|
||||
if (netif.mld_mac_filter != NULL) {
|
||||
ip6_addr_t ip6_allnodes_ll;
|
||||
ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
|
||||
netif.mld_mac_filter(&netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
|
||||
}
|
||||
#endif /* LWIP_IPV6_MLD */
|
||||
|
||||
#if LWIP_IPV6_AUTOCONFIG
|
||||
/* IPv6 address autoconfiguration not enabled by default */
|
||||
netif.ip6_autoconfig_enabled = 1;
|
||||
#endif /* LWIP_IPV6_AUTOCONFIG */
|
||||
} else {
|
||||
// Disable rourter solicitations
|
||||
netif.rs_count = 0;
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (stack != IPV6_STACK) {
|
||||
if (!dhcp && !ppp) {
|
||||
ip4_addr_t ip_addr;
|
||||
ip4_addr_t netmask_addr;
|
||||
ip4_addr_t gw_addr;
|
||||
|
||||
if (!inet_aton(ip, &ip_addr) ||
|
||||
!inet_aton(netmask, &netmask_addr) ||
|
||||
!inet_aton(gw, &gw_addr)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
netif_set_addr(&netif, &ip_addr, &netmask_addr, &gw_addr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (client_callback) {
|
||||
client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_CONNECTING);
|
||||
}
|
||||
|
||||
if (ppp) {
|
||||
err_t err = ppp_lwip_connect(hw);
|
||||
if (err) {
|
||||
connected = NSAPI_STATUS_DISCONNECTED;
|
||||
if (client_callback) {
|
||||
client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED);
|
||||
}
|
||||
return err_remap(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!netif_is_link_up(&netif)) {
|
||||
if (blocking) {
|
||||
if (osSemaphoreAcquire(linked, 15000) != osOK) {
|
||||
if (ppp) {
|
||||
(void) ppp_lwip_disconnect(hw);
|
||||
}
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsapi_error_t ret = set_dhcp();
|
||||
if (ret != NSAPI_ERROR_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!blocking) {
|
||||
// Done enough - as addresses are acquired, there will be
|
||||
// connected callbacks.
|
||||
// XXX shouldn't this be NSAPI_ERROR_IN_PROGRESS if in CONNECTING state?
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
// If doesn't have address
|
||||
if (!LWIP::get_ip_addr(true, &netif)) {
|
||||
if (osSemaphoreAcquire(has_any_addr, DHCP_TIMEOUT * 1000) != osOK) {
|
||||
if (ppp) {
|
||||
(void) ppp_lwip_disconnect(hw);
|
||||
}
|
||||
return NSAPI_ERROR_DHCP_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
#if PREF_ADDR_TIMEOUT
|
||||
if (stack != IPV4_STACK && stack != IPV6_STACK) {
|
||||
// If address is not for preferred stack waits a while to see
|
||||
// if preferred stack address is acquired
|
||||
if (!LWIP::get_ip_addr(false, &netif)) {
|
||||
osSemaphoreAcquire(has_pref_addr, PREF_ADDR_TIMEOUT * 1000);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BOTH_ADDR_TIMEOUT
|
||||
if (stack != IPV4_STACK && stack != IPV6_STACK) {
|
||||
// If addresses for both stacks are not available waits a while to
|
||||
// see if address for both stacks are acquired
|
||||
if (!(LWIP::get_ipv4_addr(&netif) && LWIP::get_ipv6_addr(&netif))) {
|
||||
osSemaphoreAcquire(has_both_addr, BOTH_ADDR_TIMEOUT * 1000);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
add_dns_addr(&netif);
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::Interface::bringdown()
|
||||
{
|
||||
// Check if we've connected
|
||||
if (connected == NSAPI_STATUS_DISCONNECTED) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
#if LWIP_DHCP
|
||||
// Disconnect from the network
|
||||
if (dhcp_started) {
|
||||
dhcp_release(&netif);
|
||||
dhcp_stop(&netif);
|
||||
dhcp_started = false;
|
||||
dhcp_has_to_be_set = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ppp) {
|
||||
/* this is a blocking call, returns when PPP is properly closed */
|
||||
err_t err = ppp_lwip_disconnect(hw);
|
||||
if (err) {
|
||||
return err_remap(err);
|
||||
}
|
||||
MBED_ASSERT(!netif_is_link_up(&netif));
|
||||
/*if (netif_is_link_up(&netif)) {
|
||||
if (sys_arch_sem_wait(&unlinked, 15000) == SYS_ARCH_TIMEOUT) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
}*/
|
||||
} else {
|
||||
netif_set_down(&netif);
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
mbed_lwip_clear_ipv6_addresses(&netif);
|
||||
#endif
|
||||
#if LWIP_IPV4
|
||||
ip_addr_set_zero(&(netif.ip_addr));
|
||||
ip_addr_set_zero(&(netif.netmask));
|
||||
ip_addr_set_zero(&(netif.gw));
|
||||
#endif
|
||||
|
||||
osSemaphoreDelete(has_any_addr);
|
||||
osSemaphoreAttr_t attr;
|
||||
attr.name = NULL;
|
||||
attr.attr_bits = 0;
|
||||
attr.cb_mem = &has_any_addr_sem;
|
||||
attr.cb_size = sizeof has_any_addr_sem;
|
||||
has_any_addr = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
#if PREF_ADDR_TIMEOUT
|
||||
osSemaphoreDelete(has_pref_addr);
|
||||
attr.cb_mem = &has_pref_addr_sem;
|
||||
attr.cb_size = sizeof has_pref_addr_sem;
|
||||
has_pref_addr = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
#endif
|
||||
#if BOTH_ADDR_TIMEOUT
|
||||
osSemaphoreDelete(has_both_addr);
|
||||
attr.cb_mem = &has_both_addr_sem;
|
||||
attr.cb_size = sizeof has_both_addr_sem;
|
||||
has_both_addr = osSemaphoreNew(UINT16_MAX, 0, &attr);
|
||||
#endif
|
||||
has_addr_state = 0;
|
||||
|
||||
connected = NSAPI_STATUS_DISCONNECTED;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "lwip/tcpip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "netsocket/nsapi_types.h"
|
||||
#include "netsocket/EMAC.h"
|
||||
|
||||
#include "LWIPStack.h"
|
||||
|
||||
#if LWIP_ETHERNET
|
||||
|
||||
err_t LWIP::Interface::emac_low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
/* Increase reference counter since lwip stores handle to pbuf and frees
|
||||
it after output */
|
||||
pbuf_ref(p);
|
||||
|
||||
LWIP::Interface *mbed_if = static_cast<LWIP::Interface *>(netif->state);
|
||||
bool ret = mbed_if->emac->link_out(p);
|
||||
return ret ? ERR_OK : ERR_IF;
|
||||
}
|
||||
|
||||
void LWIP::Interface::emac_input(emac_mem_buf_t *buf)
|
||||
{
|
||||
struct pbuf *p = static_cast<struct pbuf *>(buf);
|
||||
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif.input(p, &netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("Emac LWIP: IP input error\n"));
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
void LWIP::Interface::emac_state_change(bool up)
|
||||
{
|
||||
if (up) {
|
||||
tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_up, &netif, 1);
|
||||
} else {
|
||||
tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_down, &netif, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_IGMP
|
||||
|
||||
#include "lwip/igmp.h"
|
||||
/**
|
||||
* IPv4 address filtering setup.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure
|
||||
* \param[in] group IPv4 group to modify
|
||||
* \param[in] action
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
err_t LWIP::Interface::emac_igmp_mac_filter(struct netif *netif, const ip4_addr_t *group, enum netif_mac_filter_action action)
|
||||
{
|
||||
LWIP::Interface *mbed_if = static_cast<LWIP::Interface *>(netif->state);
|
||||
|
||||
switch (action) {
|
||||
case NETIF_ADD_MAC_FILTER:
|
||||
{
|
||||
uint32_t group23 = ntohl(group->addr) & 0x007FFFFF;
|
||||
uint8_t addr[6];
|
||||
addr[0] = LL_IP4_MULTICAST_ADDR_0;
|
||||
addr[1] = LL_IP4_MULTICAST_ADDR_1;
|
||||
addr[2] = LL_IP4_MULTICAST_ADDR_2;
|
||||
addr[3] = group23 >> 16;
|
||||
addr[4] = group23 >> 8;
|
||||
addr[5] = group23;
|
||||
mbed_if->emac->add_multicast_group(addr);
|
||||
return ERR_OK;
|
||||
}
|
||||
case NETIF_DEL_MAC_FILTER:
|
||||
/* As we don't reference count, silently ignore delete requests */
|
||||
return ERR_OK;
|
||||
default:
|
||||
return ERR_ARG;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
|
||||
#include "lwip/mld6.h"
|
||||
/**
|
||||
* IPv6 address filtering setup.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure
|
||||
* \param[in] group IPv6 group to modify
|
||||
* \param[in] action
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
err_t LWIP::Interface::emac_mld_mac_filter(struct netif *netif, const ip6_addr_t *group, enum netif_mac_filter_action action)
|
||||
{
|
||||
LWIP::Interface *mbed_if = static_cast<LWIP::Interface *>(netif->state);
|
||||
|
||||
switch (action) {
|
||||
case NETIF_ADD_MAC_FILTER:
|
||||
{
|
||||
uint32_t group32 = ntohl(group->addr[3]);
|
||||
uint8_t addr[6];
|
||||
addr[0] = LL_IP6_MULTICAST_ADDR_0;
|
||||
addr[1] = LL_IP6_MULTICAST_ADDR_1;
|
||||
addr[2] = group32 >> 24;
|
||||
addr[3] = group32 >> 16;
|
||||
addr[4] = group32 >> 8;
|
||||
addr[5] = group32;
|
||||
mbed_if->emac->add_multicast_group(addr);
|
||||
return ERR_OK;
|
||||
}
|
||||
case NETIF_DEL_MAC_FILTER:
|
||||
/* As we don't reference count, silently ignore delete requests */
|
||||
return ERR_OK;
|
||||
default:
|
||||
return ERR_ARG;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err_t LWIP::Interface::emac_if_init(struct netif *netif)
|
||||
{
|
||||
int err = ERR_OK;
|
||||
LWIP::Interface *mbed_if = static_cast<LWIP::Interface *>(netif->state);
|
||||
|
||||
mbed_if->emac->set_memory_manager(*mbed_if->memory_manager);
|
||||
mbed_if->emac->set_link_input_cb(mbed::callback(mbed_if, &LWIP::Interface::emac_input));
|
||||
mbed_if->emac->set_link_state_cb(mbed::callback(mbed_if, &LWIP::Interface::emac_state_change));
|
||||
|
||||
/* Interface capabilities */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
|
||||
if (!mbed_if->emac->power_up()) {
|
||||
err = ERR_IF;
|
||||
}
|
||||
|
||||
netif->mtu = mbed_if->emac->get_mtu_size();
|
||||
/* We have a default MAC address, so do don't force them to supply one */
|
||||
netif->hwaddr_len = mbed_if->emac->get_hwaddr_size();
|
||||
/* They may or may not update hwaddr with their address */
|
||||
mbed_if->emac->get_hwaddr(netif->hwaddr);
|
||||
/* Then we write back either what they gave us, or our default */
|
||||
mbed_if->emac->set_hwaddr(netif->hwaddr);
|
||||
|
||||
mbed_if->emac->get_ifname(netif->name, 2);
|
||||
|
||||
#if LWIP_IPV4
|
||||
netif->output = etharp_output;
|
||||
#if LWIP_IGMP
|
||||
netif->igmp_mac_filter = &LWIP::Interface::emac_igmp_mac_filter;
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
#endif /* LWIP_IGMP */
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = ethip6_output;
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->mld_mac_filter = &LWIP::Interface::emac_mld_mac_filter;
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#else
|
||||
// Would need to enable all multicasts here - no API in fsl_enet to do that
|
||||
#error "IPv6 multicasts won't be received if LWIP_IPV6_MLD is disabled, breaking the system"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
netif->linkoutput = &LWIP::Interface::emac_low_level_output;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "pbuf.h"
|
||||
#include "LWIPMemoryManager.h"
|
||||
|
||||
emac_mem_buf_t *LWIPMemoryManager::alloc_heap(uint32_t size, uint32_t align)
|
||||
{
|
||||
struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, size + align, PBUF_RAM);
|
||||
if (pbuf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
align_memory(pbuf, align);
|
||||
|
||||
return static_cast<emac_mem_buf_t *>(pbuf);
|
||||
}
|
||||
|
||||
emac_mem_buf_t *LWIPMemoryManager::alloc_pool(uint32_t size, uint32_t align)
|
||||
{
|
||||
uint32_t total_align = count_total_align(size, align);
|
||||
|
||||
struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, size + total_align, PBUF_POOL);
|
||||
if (pbuf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
align_memory(pbuf, align);
|
||||
|
||||
return static_cast<emac_mem_buf_t *>(pbuf);
|
||||
}
|
||||
|
||||
uint32_t LWIPMemoryManager::get_pool_alloc_unit(uint32_t align) const
|
||||
{
|
||||
uint32_t alloc_unit = LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) - align;
|
||||
return alloc_unit;
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::free(emac_mem_buf_t *buf)
|
||||
{
|
||||
pbuf_free(static_cast<struct pbuf *>(buf));
|
||||
}
|
||||
|
||||
uint32_t LWIPMemoryManager::get_total_len(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
return (static_cast<const struct pbuf *>(buf))->tot_len;
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::copy(emac_mem_buf_t *to_buf, const emac_mem_buf_t *from_buf)
|
||||
{
|
||||
pbuf_copy(static_cast<struct pbuf *>(to_buf), static_cast<const struct pbuf *>(from_buf));
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::copy_to_buf(emac_mem_buf_t *to_buf, const void *ptr, uint32_t len)
|
||||
{
|
||||
pbuf_take(static_cast<struct pbuf *>(to_buf), ptr, len);
|
||||
}
|
||||
|
||||
uint32_t LWIPMemoryManager::copy_from_buf(void *ptr, uint32_t len, const emac_mem_buf_t *from_buf) const
|
||||
{
|
||||
return pbuf_copy_partial(static_cast<const struct pbuf *>(from_buf), ptr, len, 0);
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::cat(emac_mem_buf_t *to_buf, emac_mem_buf_t *cat_buf)
|
||||
{
|
||||
pbuf_cat(static_cast<struct pbuf *>(to_buf), static_cast<struct pbuf *>(cat_buf));
|
||||
}
|
||||
|
||||
emac_mem_buf_t *LWIPMemoryManager::get_next(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
}
|
||||
struct pbuf *next = (static_cast<const struct pbuf *>(buf))->next;
|
||||
return static_cast<emac_mem_buf_t *>(next);
|
||||
}
|
||||
|
||||
void *LWIPMemoryManager::get_ptr(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
return (static_cast<const struct pbuf *>(buf))->payload;
|
||||
}
|
||||
|
||||
uint32_t LWIPMemoryManager::get_len(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
return (static_cast<const struct pbuf *>(buf))->len;
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::set_len(emac_mem_buf_t *buf, uint32_t len)
|
||||
{
|
||||
struct pbuf *pbuf = static_cast<struct pbuf *>(buf);
|
||||
pbuf->len = len;
|
||||
set_total_len(pbuf);
|
||||
}
|
||||
|
||||
uint32_t LWIPMemoryManager::count_total_align(uint32_t size, uint32_t align)
|
||||
{
|
||||
uint32_t buffers = size / get_pool_alloc_unit(align);
|
||||
if (size % get_pool_alloc_unit(align) != 0) {
|
||||
buffers++;
|
||||
}
|
||||
return buffers * align;
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::align_memory(struct pbuf *pbuf, uint32_t align)
|
||||
{
|
||||
if (!align) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct pbuf *pbuf_start = pbuf;
|
||||
|
||||
while (pbuf) {
|
||||
uint32_t remainder = reinterpret_cast<uint32_t>(pbuf->payload) % align;
|
||||
if (remainder) {
|
||||
uint32_t offset = align - remainder;
|
||||
if (offset >= align) {
|
||||
offset = align;
|
||||
}
|
||||
pbuf->payload = static_cast<char *>(pbuf->payload) + offset;
|
||||
}
|
||||
pbuf->len -= align;
|
||||
pbuf = pbuf->next;
|
||||
}
|
||||
|
||||
// Correct total lengths
|
||||
set_total_len(pbuf_start);
|
||||
}
|
||||
|
||||
void LWIPMemoryManager::set_total_len(struct pbuf *pbuf)
|
||||
{
|
||||
if (!pbuf->next) {
|
||||
pbuf->tot_len = pbuf->len;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t total_len;
|
||||
struct pbuf *pbuf_tailing;
|
||||
|
||||
while (pbuf) {
|
||||
total_len = pbuf->len;
|
||||
|
||||
pbuf_tailing = pbuf->next;
|
||||
while (pbuf_tailing) {
|
||||
total_len += pbuf_tailing->len;
|
||||
pbuf_tailing = pbuf_tailing->next;
|
||||
}
|
||||
|
||||
pbuf->tot_len = total_len;
|
||||
pbuf = pbuf->next;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 LWIP_MEMORY_MANAGER_H
|
||||
#define LWIP_MEMORY_MANAGER_H
|
||||
|
||||
#include "EMACMemoryManager.h"
|
||||
|
||||
class LWIPMemoryManager : public EMACMemoryManager {
|
||||
public:
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
/**
|
||||
* 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 buf 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 buf 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);
|
||||
|
||||
/**
|
||||
* Copy to a memory buffer chain
|
||||
*
|
||||
* Copies data to a buffer chain. Copy operation does not adjust the lengths
|
||||
* of the copied-to memory buffer chain, so chain total length must match the
|
||||
* copied length.
|
||||
*
|
||||
* @param to_buf Memory buffer chain to copy to
|
||||
* @param ptr Pointer to data
|
||||
* @param len Data length
|
||||
*/
|
||||
virtual void copy_to_buf(emac_mem_buf_t *to_buf, const void *ptr, uint32_t len);
|
||||
|
||||
/**
|
||||
* Copy from a memory buffer chain
|
||||
*
|
||||
* Copies data from a memory buffer chain.
|
||||
*
|
||||
* @param len Data length
|
||||
* @param ptr Pointer to data
|
||||
* @param from_buf Memory buffer chain to copy from
|
||||
* @return Length of the data that was copied
|
||||
*/
|
||||
virtual uint32_t copy_from_buf(void *ptr, uint32_t len, const emac_mem_buf_t *from_buf) const;
|
||||
|
||||
/**
|
||||
* 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 buf 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 buf 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 buf 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 buf 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);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Returns a total memory alignment size
|
||||
*
|
||||
* Calculates the total memory alignment size for a memory buffer chain.
|
||||
* Used internally on pool allocation.
|
||||
*
|
||||
* @param size Size of the memory to allocate in bytes
|
||||
* @param align Memory alignment requirement for each buffer in bytes
|
||||
* @return Total alignment needed in bytes
|
||||
*/
|
||||
uint32_t count_total_align(uint32_t size, uint32_t align);
|
||||
|
||||
/**
|
||||
* Aligns a memory buffer chain
|
||||
*
|
||||
* Aligns a memory buffer chain and updates lengths and total lengths
|
||||
* accordingly. There needs to be enough overhead to do the alignment
|
||||
* for all buffers.
|
||||
*
|
||||
* @param pbuf Memory buffer
|
||||
* @param align Memory alignment requirement for each buffer in bytes
|
||||
*/
|
||||
void align_memory(struct pbuf *pbuf, uint32_t align);
|
||||
|
||||
/**
|
||||
* Sets total lengths of a memory buffer chain
|
||||
*
|
||||
* Sets total length fields for a memory buffer chain based on buffer
|
||||
* length fields. All total lengths are calculated again.
|
||||
*
|
||||
* @param pbuf Memory buffer
|
||||
*/
|
||||
void set_total_len(struct pbuf *pbuf);
|
||||
};
|
||||
|
||||
#endif /* LWIP_MEMORY_MANAGER_H */
|
|
@ -0,0 +1,678 @@
|
|||
/* LWIP implementation of NSAPI NetworkStack
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "nsapi.h"
|
||||
#include "mbed_interface.h"
|
||||
#include "mbed_assert.h"
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/dhcp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/lwip_errno.h"
|
||||
#include "lwip-sys/arch/sys_arch.h"
|
||||
|
||||
#include "LWIPStack.h"
|
||||
|
||||
#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
|
||||
#define LWIP_SOCKET_MAX_MEMBERSHIPS 4
|
||||
#endif
|
||||
|
||||
void LWIP::socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len)
|
||||
{
|
||||
// Filter send minus events
|
||||
if (eh == NETCONN_EVT_SENDMINUS && nc->state == NETCONN_WRITE) {
|
||||
return;
|
||||
}
|
||||
|
||||
LWIP &lwip = LWIP::get_instance();
|
||||
|
||||
lwip.adaptation.lock();
|
||||
|
||||
for (int i = 0; i < MEMP_NUM_NETCONN; i++) {
|
||||
if (lwip.arena[i].in_use
|
||||
&& lwip.arena[i].conn == nc
|
||||
&& lwip.arena[i].cb) {
|
||||
lwip.arena[i].cb(lwip.arena[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
lwip.adaptation.unlock();
|
||||
}
|
||||
|
||||
#if !LWIP_IPV4 || !LWIP_IPV6
|
||||
static bool all_zeros(const uint8_t *p, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (p[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool convert_lwip_addr_to_mbed(nsapi_addr_t *out, const ip_addr_t *in)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(in)) {
|
||||
out->version = NSAPI_IPv6;
|
||||
SMEMCPY(out->bytes, ip_2_ip6(in), sizeof(ip6_addr_t));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(in)) {
|
||||
out->version = NSAPI_IPv4;
|
||||
SMEMCPY(out->bytes, ip_2_ip4(in), sizeof(ip4_addr_t));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV6 && LWIP_IPV4
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool convert_mbed_addr_to_lwip(ip_addr_t *out, const nsapi_addr_t *in)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (in->version == NSAPI_IPv6) {
|
||||
IP_SET_TYPE(out, IPADDR_TYPE_V6);
|
||||
SMEMCPY(ip_2_ip6(out), in->bytes, sizeof(ip6_addr_t));
|
||||
return true;
|
||||
}
|
||||
#if !LWIP_IPV4
|
||||
/* For bind() and other purposes, need to accept "null" of other type */
|
||||
/* (People use IPv4 0.0.0.0 as a general null) */
|
||||
if (in->version == NSAPI_UNSPEC ||
|
||||
(in->version == NSAPI_IPv4 && all_zeros(in->bytes, 4))) {
|
||||
ip_addr_set_zero_ip6(out);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (in->version == NSAPI_IPv4) {
|
||||
IP_SET_TYPE(out, IPADDR_TYPE_V4);
|
||||
SMEMCPY(ip_2_ip4(out), in->bytes, sizeof(ip4_addr_t));
|
||||
return true;
|
||||
}
|
||||
#if !LWIP_IPV6
|
||||
/* For symmetry with above, accept IPv6 :: as a general null */
|
||||
if (in->version == NSAPI_UNSPEC ||
|
||||
(in->version == NSAPI_IPv6 && all_zeros(in->bytes, 16))) {
|
||||
ip_addr_set_zero_ip4(out);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
if (in->version == NSAPI_UNSPEC) {
|
||||
#if IP_VERSION_PREF == PREF_IPV4
|
||||
ip_addr_set_zero_ip4(out);
|
||||
#else
|
||||
ip_addr_set_zero_ip6(out);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LWIP::tcpip_init_irq(void *eh)
|
||||
{
|
||||
LWIP *lwip = static_cast<LWIP *>(eh);
|
||||
lwip->tcpip_inited.release();
|
||||
sys_tcpip_thread_set();
|
||||
}
|
||||
|
||||
/* LWIP network stack implementation */
|
||||
LWIP::LWIP()
|
||||
{
|
||||
default_interface = NULL;
|
||||
|
||||
// Seed lwip random
|
||||
lwip_seed_random();
|
||||
|
||||
// Initialise TCP sequence number
|
||||
uint32_t tcp_isn_secret[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
tcp_isn_secret[i] = LWIP_RAND();
|
||||
}
|
||||
lwip_init_tcp_isn(0, (u8_t *) &tcp_isn_secret);
|
||||
|
||||
tcpip_init(&LWIP::tcpip_init_irq, this);
|
||||
tcpip_inited.wait(0);
|
||||
|
||||
// Zero out socket set
|
||||
arena_init();
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::get_dns_server(int index, SocketAddress *address)
|
||||
{
|
||||
int dns_entries = 0;
|
||||
|
||||
for (int i = 0; i < DNS_MAX_SERVERS; i++) {
|
||||
const ip_addr_t *ip_addr = dns_getserver(i);
|
||||
if (!ip_addr_isany(ip_addr)) {
|
||||
if (index == dns_entries) {
|
||||
nsapi_addr_t addr;
|
||||
convert_lwip_addr_to_mbed(&addr, ip_addr);
|
||||
address->set_addr(addr);
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
dns_entries++;
|
||||
}
|
||||
}
|
||||
return NSAPI_ERROR_NO_ADDRESS;
|
||||
}
|
||||
|
||||
void LWIP::tcpip_thread_callback(void *ptr)
|
||||
{
|
||||
lwip_callback *cb = static_cast<lwip_callback *>(ptr);
|
||||
|
||||
if (cb->delay) {
|
||||
sys_timeout(cb->delay, LWIP::tcpip_thread_callback, ptr);
|
||||
cb->delay = 0;
|
||||
} else {
|
||||
cb->callback();
|
||||
delete cb;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::call_in(int delay, mbed::Callback<void()> func)
|
||||
{
|
||||
lwip_callback *cb = new lwip_callback;
|
||||
if (!cb) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
cb->delay = delay;
|
||||
cb->callback = func;
|
||||
|
||||
if (tcpip_callback_with_block(LWIP::tcpip_thread_callback, cb, 1) != ERR_OK) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
LWIP::call_in_callback_cb_t LWIP::get_call_in_callback()
|
||||
{
|
||||
call_in_callback_cb_t cb(this, &LWIP::call_in);
|
||||
return cb;
|
||||
}
|
||||
|
||||
const char *LWIP::get_ip_address()
|
||||
{
|
||||
if (!default_interface) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const ip_addr_t *addr = get_ip_addr(true, &default_interface->netif);
|
||||
|
||||
if (!addr) {
|
||||
return NULL;
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(addr)) {
|
||||
return ip6addr_ntoa_r(ip_2_ip6(addr), ip_address, sizeof(ip_address));
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(addr)) {
|
||||
return ip4addr_ntoa_r(ip_2_ip4(addr), ip_address, sizeof(ip_address));
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV6 && LWIP_IPV4
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto)
|
||||
{
|
||||
// check if network is connected
|
||||
// if (lwip_connected == NSAPI_STATUS_DISCONNECTED) {
|
||||
// return NSAPI_ERROR_NO_CONNECTION;
|
||||
// }
|
||||
|
||||
// allocate a socket
|
||||
struct mbed_lwip_socket *s = arena_alloc();
|
||||
if (!s) {
|
||||
return NSAPI_ERROR_NO_SOCKET;
|
||||
}
|
||||
|
||||
enum netconn_type lwip_proto = proto == NSAPI_TCP ? NETCONN_TCP : NETCONN_UDP;
|
||||
|
||||
#if LWIP_IPV6
|
||||
// Enable IPv6 (or dual-stack)
|
||||
lwip_proto = (enum netconn_type) (lwip_proto | NETCONN_TYPE_IPV6);
|
||||
#endif
|
||||
|
||||
s->conn = netconn_new_with_callback(lwip_proto, &LWIP::socket_callback);
|
||||
|
||||
if (!s->conn) {
|
||||
arena_dealloc(s);
|
||||
return NSAPI_ERROR_NO_SOCKET;
|
||||
}
|
||||
|
||||
netconn_set_recvtimeout(s->conn, 1);
|
||||
*(struct mbed_lwip_socket **)handle = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::socket_close(nsapi_socket_t handle)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
|
||||
netbuf_delete(s->buf);
|
||||
err_t err = netconn_delete(s->conn);
|
||||
arena_dealloc(s);
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::socket_bind(nsapi_socket_t handle, const SocketAddress &address)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
ip_addr_t ip_addr;
|
||||
|
||||
if (
|
||||
#if LWIP_TCP
|
||||
(NETCONNTYPE_GROUP(s->conn->type) == NETCONN_TCP && s->conn->pcb.tcp->local_port != 0) ||
|
||||
#endif
|
||||
(NETCONNTYPE_GROUP(s->conn->type) == NETCONN_UDP && s->conn->pcb.udp->local_port != 0)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
nsapi_addr_t addr = address.get_addr();
|
||||
if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
if (!ip_addr_isany_val(ip_addr) && !is_local_addr(&ip_addr)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
err_t err = netconn_bind(s->conn, &ip_addr, address.get_port());
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::socket_listen(nsapi_socket_t handle, int backlog)
|
||||
{
|
||||
#if LWIP_TCP
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
|
||||
if (s->conn->pcb.tcp->local_port == 0) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
err_t err = netconn_listen_with_backlog(s->conn, backlog);
|
||||
return err_remap(err);
|
||||
#else
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::socket_connect(nsapi_socket_t handle, const SocketAddress &address)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
ip_addr_t ip_addr;
|
||||
|
||||
nsapi_addr_t addr = address.get_addr();
|
||||
if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
netconn_set_nonblocking(s->conn, false);
|
||||
err_t err = netconn_connect(s->conn, &ip_addr, address.get_port());
|
||||
netconn_set_nonblocking(s->conn, true);
|
||||
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address)
|
||||
{
|
||||
#if LWIP_TCP
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)server;
|
||||
struct mbed_lwip_socket *ns = arena_alloc();
|
||||
if (!ns) {
|
||||
return NSAPI_ERROR_NO_SOCKET;
|
||||
}
|
||||
|
||||
if (s->conn->pcb.tcp->state != LISTEN) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
err_t err = netconn_accept(s->conn, &ns->conn);
|
||||
if (err != ERR_OK) {
|
||||
arena_dealloc(ns);
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
netconn_set_recvtimeout(ns->conn, 1);
|
||||
*(struct mbed_lwip_socket **)handle = ns;
|
||||
|
||||
ip_addr_t peer_addr;
|
||||
nsapi_addr_t addr;
|
||||
u16_t port;
|
||||
(void) netconn_peer(ns->conn, &peer_addr, &port);
|
||||
convert_lwip_addr_to_mbed(&addr, &peer_addr);
|
||||
|
||||
if (address) {
|
||||
address->set_addr(addr);
|
||||
address->set_port(port);
|
||||
}
|
||||
|
||||
netconn_set_nonblocking(ns->conn, true);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t LWIP::socket_send(nsapi_socket_t handle, const void *data, nsapi_size_t size)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
size_t bytes_written = 0;
|
||||
|
||||
err_t err = netconn_write_partly(s->conn, data, size, NETCONN_COPY, &bytes_written);
|
||||
if (err != ERR_OK) {
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
return (nsapi_size_or_error_t)bytes_written;
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t LWIP::socket_recv(nsapi_socket_t handle, void *data, nsapi_size_t size)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
|
||||
if (!s->buf) {
|
||||
err_t err = netconn_recv(s->conn, &s->buf);
|
||||
s->offset = 0;
|
||||
|
||||
if (err != ERR_OK) {
|
||||
return err_remap(err);
|
||||
}
|
||||
}
|
||||
|
||||
u16_t recv = netbuf_copy_partial(s->buf, data, (u16_t)size, s->offset);
|
||||
s->offset += recv;
|
||||
|
||||
if (s->offset >= netbuf_len(s->buf)) {
|
||||
netbuf_delete(s->buf);
|
||||
s->buf = 0;
|
||||
}
|
||||
|
||||
return recv;
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t LWIP::socket_sendto(nsapi_socket_t handle, const SocketAddress &address, const void *data, nsapi_size_t size)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
ip_addr_t ip_addr;
|
||||
|
||||
nsapi_addr_t addr = address.get_addr();
|
||||
if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
struct netbuf *buf = netbuf_new();
|
||||
|
||||
err_t err = netbuf_ref(buf, data, (u16_t)size);
|
||||
if (err != ERR_OK) {
|
||||
netbuf_free(buf);
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
err = netconn_sendto(s->conn, buf, &ip_addr, address.get_port());
|
||||
netbuf_delete(buf);
|
||||
if (err != ERR_OK) {
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t LWIP::socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *data, nsapi_size_t size)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
struct netbuf *buf;
|
||||
|
||||
err_t err = netconn_recv(s->conn, &buf);
|
||||
if (err != ERR_OK) {
|
||||
return err_remap(err);
|
||||
}
|
||||
|
||||
if (address) {
|
||||
nsapi_addr_t addr;
|
||||
convert_lwip_addr_to_mbed(&addr, netbuf_fromaddr(buf));
|
||||
address->set_addr(addr);
|
||||
address->set_port(netbuf_fromport(buf));
|
||||
}
|
||||
|
||||
u16_t recv = netbuf_copy(buf, data, (u16_t)size);
|
||||
netbuf_delete(buf);
|
||||
|
||||
return recv;
|
||||
}
|
||||
|
||||
int32_t LWIP::find_multicast_member(const struct mbed_lwip_socket *s, const nsapi_ip_mreq_t *imr) {
|
||||
uint32_t count = 0;
|
||||
uint32_t index = 0;
|
||||
// Set upper limit on while loop, should break out when the membership pair is found
|
||||
while (count < s->multicast_memberships_count) {
|
||||
index = next_registered_multicast_member(s, index);
|
||||
|
||||
if (memcmp(&s->multicast_memberships[index].imr_multiaddr, &imr->imr_multiaddr, sizeof(nsapi_addr_t)) == 0 &&
|
||||
memcmp(&s->multicast_memberships[index].imr_interface, &imr->imr_interface, sizeof(nsapi_addr_t)) == 0) {
|
||||
return index;
|
||||
}
|
||||
count++;
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
|
||||
switch (optname) {
|
||||
#if LWIP_TCP
|
||||
case NSAPI_KEEPALIVE:
|
||||
if (optlen != sizeof(int) || NETCONNTYPE_GROUP(s->conn->type) != NETCONN_TCP) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
s->conn->pcb.tcp->so_options |= SOF_KEEPALIVE;
|
||||
return 0;
|
||||
|
||||
case NSAPI_KEEPIDLE:
|
||||
if (optlen != sizeof(int) || NETCONNTYPE_GROUP(s->conn->type) != NETCONN_TCP) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
s->conn->pcb.tcp->keep_idle = *(int*)optval;
|
||||
return 0;
|
||||
|
||||
case NSAPI_KEEPINTVL:
|
||||
if (optlen != sizeof(int) || NETCONNTYPE_GROUP(s->conn->type) != NETCONN_TCP) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
s->conn->pcb.tcp->keep_intvl = *(int*)optval;
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
case NSAPI_REUSEADDR:
|
||||
if (optlen != sizeof(int)) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (*(int *)optval) {
|
||||
ip_set_option(s->conn->pcb.ip, SOF_REUSEADDR);
|
||||
} else {
|
||||
ip_reset_option(s->conn->pcb.ip, SOF_REUSEADDR);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case NSAPI_ADD_MEMBERSHIP:
|
||||
case NSAPI_DROP_MEMBERSHIP: {
|
||||
if (optlen != sizeof(nsapi_ip_mreq_t)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
err_t igmp_err;
|
||||
const nsapi_ip_mreq_t *imr = static_cast<const nsapi_ip_mreq_t *>(optval);
|
||||
|
||||
/* Check interface address type matches group, or is unspecified */
|
||||
if (imr->imr_interface.version != NSAPI_UNSPEC && imr->imr_interface.version != imr->imr_multiaddr.version) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
ip_addr_t if_addr;
|
||||
ip_addr_t multi_addr;
|
||||
|
||||
/* Convert the group address */
|
||||
if (!convert_mbed_addr_to_lwip(&multi_addr, &imr->imr_multiaddr)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
/* Convert the interface address, or make sure it's the correct sort of "any" */
|
||||
if (imr->imr_interface.version != NSAPI_UNSPEC) {
|
||||
if (!convert_mbed_addr_to_lwip(&if_addr, &imr->imr_interface)) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
ip_addr_set_any(IP_IS_V6(&if_addr), &if_addr);
|
||||
}
|
||||
|
||||
igmp_err = ERR_USE; // Maps to NSAPI_ERROR_UNSUPPORTED
|
||||
int32_t member_pair_index = find_multicast_member(s, imr);
|
||||
|
||||
if (optname == NSAPI_ADD_MEMBERSHIP) {
|
||||
if (!s->multicast_memberships) {
|
||||
// First multicast join on this socket, allocate space for membership tracking
|
||||
s->multicast_memberships = (nsapi_ip_mreq_t*)malloc(sizeof(nsapi_ip_mreq_t) * LWIP_SOCKET_MAX_MEMBERSHIPS);
|
||||
if (!s->multicast_memberships) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
} else if(s->multicast_memberships_count == LWIP_SOCKET_MAX_MEMBERSHIPS) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (member_pair_index != -1) {
|
||||
return NSAPI_ERROR_ADDRESS_IN_USE;
|
||||
}
|
||||
|
||||
member_pair_index = next_free_multicast_member(s, 0);
|
||||
|
||||
adaptation.lock();
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(&if_addr)) {
|
||||
igmp_err = igmp_joingroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(&if_addr)) {
|
||||
igmp_err = mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
|
||||
}
|
||||
#endif
|
||||
|
||||
adaptation.unlock();
|
||||
|
||||
if (igmp_err == ERR_OK) {
|
||||
set_multicast_member_registry_bit(s, member_pair_index);
|
||||
s->multicast_memberships[member_pair_index] = *imr;
|
||||
s->multicast_memberships_count++;
|
||||
}
|
||||
} else {
|
||||
if (member_pair_index == -1) {
|
||||
return NSAPI_ERROR_NO_ADDRESS;
|
||||
}
|
||||
|
||||
clear_multicast_member_registry_bit(s, member_pair_index);
|
||||
s->multicast_memberships_count--;
|
||||
|
||||
adaptation.lock();
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(&if_addr)) {
|
||||
igmp_err = igmp_leavegroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
|
||||
}
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(&if_addr)) {
|
||||
igmp_err = mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
|
||||
}
|
||||
#endif
|
||||
|
||||
adaptation.unlock();
|
||||
}
|
||||
|
||||
return err_remap(igmp_err);
|
||||
}
|
||||
|
||||
default:
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t LWIP::getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen)
|
||||
{
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
void LWIP::socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data)
|
||||
{
|
||||
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
|
||||
|
||||
s->cb = callback;
|
||||
s->data = data;
|
||||
}
|
||||
|
||||
LWIP &LWIP::get_instance() {
|
||||
static LWIP lwip;
|
||||
return lwip;
|
||||
}
|
||||
|
||||
// This works as long as it's not ever set to something which corresponds to
|
||||
// a macro defined as a non-integer. Eg `#define Nanostack "Foo"`
|
||||
#define LWIP 0x11991199
|
||||
#if MBED_CONF_NSAPI_DEFAULT_STACK == LWIP
|
||||
#undef LWIP
|
||||
OnboardNetworkStack &OnboardNetworkStack::get_default_instance() {
|
||||
return LWIP::get_instance();
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,518 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 LWIPSTACK_H_
|
||||
#define LWIPSTACK_H_
|
||||
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/api.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "netsocket/nsapi_types.h"
|
||||
#include "netsocket/EMAC.h"
|
||||
#include "netsocket/OnboardNetworkStack.h"
|
||||
#include "LWIPMemoryManager.h"
|
||||
|
||||
|
||||
class LWIP : public OnboardNetworkStack, private mbed::NonCopyable<LWIP> {
|
||||
public:
|
||||
|
||||
static LWIP &get_instance();
|
||||
|
||||
class Interface : public OnboardNetworkStack::Interface {
|
||||
public:
|
||||
/** 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.
|
||||
* @param blocking Specify whether bringup blocks for connection completion.
|
||||
* @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 = DEFAULT_STACK,
|
||||
bool blocking = true
|
||||
);
|
||||
|
||||
/** 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();
|
||||
|
||||
/** Register callback for status reporting
|
||||
*
|
||||
* The specified status callback function will be called on status changes
|
||||
* on the network. The parameters on the callback are the event type and
|
||||
* event-type dependent reason parameter.
|
||||
*
|
||||
* @param status_cb The callback for status changes
|
||||
*/
|
||||
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
|
||||
|
||||
/** Get the connection status
|
||||
*
|
||||
* @return The connection status according to ConnectionStatusType
|
||||
*/
|
||||
virtual nsapi_connection_status_t get_connection_status() const;
|
||||
|
||||
/** 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 LWIP;
|
||||
|
||||
Interface();
|
||||
|
||||
nsapi_error_t set_dhcp();
|
||||
static void netif_link_irq(struct netif *netif);
|
||||
static void netif_status_irq(struct netif *netif);
|
||||
|
||||
#if LWIP_ETHERNET
|
||||
static err_t emac_low_level_output(struct netif *netif, struct pbuf *p);
|
||||
void emac_input(emac_mem_buf_t *buf);
|
||||
void emac_state_change(bool up);
|
||||
#if LWIP_IGMP
|
||||
static err_t emac_igmp_mac_filter(struct netif *netif, const ip4_addr_t *group, enum netif_mac_filter_action action);
|
||||
#endif
|
||||
#if LWIP_IPV6_MLD
|
||||
static err_t emac_mld_mac_filter(struct netif *netif, const ip6_addr_t *group, enum netif_mac_filter_action action);
|
||||
#endif
|
||||
|
||||
static err_t emac_if_init(struct netif *netif);
|
||||
#endif
|
||||
|
||||
union {
|
||||
#if LWIP_ETHERNET
|
||||
EMAC *emac; /**< HW specific emac implementation */
|
||||
#endif
|
||||
void *hw; /**< alternative implementation pointer - used for PPP */
|
||||
};
|
||||
|
||||
os_semaphore_t linked_sem;
|
||||
osSemaphoreId_t linked;
|
||||
os_semaphore_t unlinked_sem;
|
||||
osSemaphoreId_t unlinked;
|
||||
os_semaphore_t has_any_addr_sem;
|
||||
osSemaphoreId_t has_any_addr;
|
||||
#define HAS_ANY_ADDR 1
|
||||
#if PREF_ADDR_TIMEOUT
|
||||
os_semaphore_t has_pref_addr_sem;
|
||||
osSemaphoreId_t has_pref_addr;
|
||||
#define HAS_PREF_ADDR 2
|
||||
#endif
|
||||
#if BOTH_ADDR_TIMEOUT
|
||||
os_semaphore_t has_both_addr_sem;
|
||||
osSemaphoreId_t has_both_addr;
|
||||
#define HAS_BOTH_ADDR 4
|
||||
#endif
|
||||
char has_addr_state;
|
||||
nsapi_connection_status_t connected;
|
||||
bool dhcp_started;
|
||||
bool dhcp_has_to_be_set;
|
||||
bool blocking;
|
||||
bool ppp;
|
||||
mbed::Callback<void(nsapi_event_t, intptr_t)> client_callback;
|
||||
struct netif netif;
|
||||
LWIPMemoryManager *memory_manager;
|
||||
};
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Register a PPP interface with the IP stack
|
||||
*
|
||||
* Connects PPP layer with the IP stack and initializes all the required infrastructure.
|
||||
* This function should be called only once for each available interface.
|
||||
*
|
||||
* This is an internal function that links ppp_lwip.cpp to mbed_ipstack_lwip.cpp,
|
||||
* once a driver starts it via the nsapi_ppp.h API.
|
||||
*
|
||||
* Ultimately the nsapi_ppp.h API will be deprecated, and there will be a
|
||||
* mbed_ipstack_add_ppp_interface() replacing nsapi_ppp_connect().
|
||||
*
|
||||
* @param pcb PPP implementation specific user data; will be passed to PPP callbacks
|
||||
* @param default_if true if the interface should be treated as the default one
|
||||
* @param[out] interface_out set to interface handle that must be passed to subsequent mbed_stack calls
|
||||
* @return NSAPI_ERROR_OK on success, or error code
|
||||
*/
|
||||
nsapi_error_t _add_ppp_interface(void *pcb, bool default_if, LWIP::Interface **interface_out);
|
||||
|
||||
/** Get a domain name server from a list of servers to query
|
||||
*
|
||||
* Returns a DNS server address for a index. If returns error no more
|
||||
* DNS servers to read.
|
||||
*
|
||||
* @param index Index of the DNS server, starts from zero
|
||||
* @param address Destination for the host address
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t get_dns_server(int index, SocketAddress *address);
|
||||
|
||||
/** Get the local IP address
|
||||
*
|
||||
* @return Null-terminated representation of the local IP address
|
||||
* or null if not yet connected
|
||||
*/
|
||||
virtual const char *get_ip_address();
|
||||
|
||||
protected:
|
||||
LWIP();
|
||||
virtual ~LWIP() {}
|
||||
|
||||
/** 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:
|
||||
|
||||
/** Call in callback
|
||||
*
|
||||
* Callback is used to call the call in method of the network stack.
|
||||
*/
|
||||
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
|
||||
|
||||
/** Get a call in callback
|
||||
*
|
||||
* Get a call in callback from the network stack context.
|
||||
*
|
||||
* Callback should not take more than 10ms to execute, otherwise it might
|
||||
* prevent underlying thread processing. A portable user of the callback
|
||||
* should not make calls to network operations due to stack size limitations.
|
||||
* The callback should not perform expensive operations such as socket recv/send
|
||||
* calls or blocking operations.
|
||||
*
|
||||
* @return Call in callback
|
||||
*/
|
||||
virtual call_in_callback_cb_t get_call_in_callback();
|
||||
|
||||
/** Call a callback after a delay
|
||||
*
|
||||
* Call a callback from the network stack context after a delay. If function
|
||||
* returns error callback will not be called.
|
||||
*
|
||||
* @param delay Delay in milliseconds
|
||||
* @param func Callback to be called
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
|
||||
|
||||
struct mbed_lwip_socket {
|
||||
bool in_use;
|
||||
|
||||
struct netconn *conn;
|
||||
struct netbuf *buf;
|
||||
u16_t offset;
|
||||
|
||||
void (*cb)(void *);
|
||||
void *data;
|
||||
|
||||
// Track multicast addresses subscribed to by this socket
|
||||
nsapi_ip_mreq_t *multicast_memberships;
|
||||
uint32_t multicast_memberships_count;
|
||||
uint32_t multicast_memberships_registry;
|
||||
};
|
||||
|
||||
struct lwip_callback {
|
||||
unsigned int delay;
|
||||
mbed::Callback<void()> callback;
|
||||
};
|
||||
|
||||
static nsapi_error_t err_remap(err_t err);
|
||||
static bool is_local_addr(const ip_addr_t *ip_addr);
|
||||
static const ip_addr_t *get_ip_addr(bool any_addr, const struct netif *netif);
|
||||
static const ip_addr_t *get_ipv4_addr(const struct netif *netif);
|
||||
static const ip_addr_t *get_ipv6_addr(const struct netif *netif);
|
||||
|
||||
static void add_dns_addr(struct netif *lwip_netif);
|
||||
|
||||
/* Static arena of sockets */
|
||||
struct mbed_lwip_socket arena[MEMP_NUM_NETCONN];
|
||||
void arena_init(void);
|
||||
struct mbed_lwip_socket *arena_alloc();
|
||||
void arena_dealloc(struct mbed_lwip_socket *s);
|
||||
|
||||
static uint32_t next_registered_multicast_member(const struct mbed_lwip_socket *s, uint32_t index) {
|
||||
while (!(s->multicast_memberships_registry & (0x0001 << index))) { index++; }
|
||||
return index;
|
||||
}
|
||||
|
||||
static uint32_t next_free_multicast_member(const struct mbed_lwip_socket *s, uint32_t index) {
|
||||
while ((s->multicast_memberships_registry & (0x0001 << index))) { index++; }
|
||||
return index;
|
||||
}
|
||||
|
||||
static void set_multicast_member_registry_bit(struct mbed_lwip_socket *s, uint32_t index) {
|
||||
s->multicast_memberships_registry |= (0x0001 << index);
|
||||
}
|
||||
|
||||
static void clear_multicast_member_registry_bit(struct mbed_lwip_socket *s, uint32_t index) {
|
||||
s->multicast_memberships_registry &= ~(0x0001 << index);
|
||||
}
|
||||
static int32_t find_multicast_member(const struct mbed_lwip_socket *s, const nsapi_ip_mreq_t *imr);
|
||||
|
||||
static void socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len);
|
||||
|
||||
static void tcpip_init_irq(void *handle);
|
||||
static void tcpip_thread_callback(void *ptr);
|
||||
|
||||
char ip_address[40];
|
||||
rtos::Semaphore tcpip_inited;
|
||||
Interface *default_interface;
|
||||
LWIPMemoryManager memory_manager;
|
||||
osThreadId tcpip_thread_id;
|
||||
rtos::Mutex adaptation;
|
||||
};
|
||||
|
||||
#endif /* LWIPSTACK_H_ */
|
|
@ -1,92 +0,0 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 DEVICE_EMAC
|
||||
|
||||
#include "emac_api.h"
|
||||
#include "emac_stack_mem.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/ethip6.h"
|
||||
|
||||
static err_t emac_lwip_low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
emac_interface_t *mac = (emac_interface_t *)netif->state;
|
||||
bool ret = mac->ops.link_out(mac, (emac_stack_mem_t *)p);
|
||||
|
||||
return ret ? ERR_OK : ERR_IF;
|
||||
}
|
||||
|
||||
static void emac_lwip_input(void *data, emac_stack_t *buf)
|
||||
{
|
||||
struct pbuf *p = (struct pbuf *)buf;
|
||||
struct netif *netif = (struct netif *)data;
|
||||
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("Emac LWIP: IP input error\n"));
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void emac_lwip_state_change(void *data, bool up)
|
||||
{
|
||||
struct netif *netif = (struct netif *)data;
|
||||
|
||||
if (up) {
|
||||
tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_up, netif, 1);
|
||||
} else {
|
||||
tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_down, netif, 1);
|
||||
}
|
||||
}
|
||||
|
||||
err_t emac_lwip_if_init(struct netif *netif)
|
||||
{
|
||||
int err = ERR_OK;
|
||||
emac_interface_t *mac = (emac_interface_t *)netif->state;
|
||||
|
||||
mac->ops.set_link_input_cb(mac, emac_lwip_input, netif);
|
||||
mac->ops.set_link_state_cb(mac, emac_lwip_state_change, netif);
|
||||
|
||||
if (!mac->ops.power_up(mac)) {
|
||||
err = ERR_IF;
|
||||
}
|
||||
|
||||
netif->mtu = mac->ops.get_mtu_size(mac);
|
||||
netif->hwaddr_len = mac->ops.get_hwaddr_size(mac);
|
||||
mac->ops.get_hwaddr(mac, netif->hwaddr);
|
||||
|
||||
/* Interface capabilities */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP;
|
||||
|
||||
mac->ops.get_ifname(mac, netif->name, 2);
|
||||
|
||||
#if LWIP_IPV4
|
||||
netif->output = etharp_output;
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = ethip6_output;
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
netif->linkoutput = emac_lwip_low_level_output;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* DEVICE_EMAC */
|
|
@ -1,91 +0,0 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 DEVICE_EMAC
|
||||
|
||||
#include "emac_stack_mem.h"
|
||||
#include "pbuf.h"
|
||||
|
||||
emac_stack_mem_t *emac_stack_mem_alloc(emac_stack_t* stack, uint32_t size, uint32_t align)
|
||||
{
|
||||
|
||||
struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, size + align, PBUF_RAM);
|
||||
if (pbuf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (align) {
|
||||
uint32_t remainder = (uint32_t)pbuf->payload % align;
|
||||
uint32_t offset = align - remainder;
|
||||
if (offset >= align) {
|
||||
offset = align;
|
||||
}
|
||||
|
||||
pbuf->payload = (void*)((char*)pbuf->payload + offset);
|
||||
pbuf->tot_len -= offset;
|
||||
pbuf->len -= offset;
|
||||
}
|
||||
|
||||
return (emac_stack_mem_t*)pbuf;
|
||||
}
|
||||
|
||||
void emac_stack_mem_free(emac_stack_t* stack, emac_stack_mem_t *mem)
|
||||
{
|
||||
pbuf_free((struct pbuf*)mem);
|
||||
}
|
||||
|
||||
void emac_stack_mem_copy(emac_stack_t* stack, emac_stack_mem_t *to, emac_stack_mem_t *from)
|
||||
{
|
||||
pbuf_copy((struct pbuf*)to, (struct pbuf*)from);
|
||||
}
|
||||
|
||||
void *emac_stack_mem_ptr(emac_stack_t* stack, emac_stack_mem_t *mem)
|
||||
{
|
||||
return ((struct pbuf*)mem)->payload;
|
||||
}
|
||||
|
||||
uint32_t emac_stack_mem_len(emac_stack_t* stack, emac_stack_mem_t *mem)
|
||||
{
|
||||
return ((struct pbuf*)mem)->len;
|
||||
}
|
||||
|
||||
void emac_stack_mem_set_len(emac_stack_t* stack, emac_stack_mem_t *mem, uint32_t len)
|
||||
{
|
||||
struct pbuf *pbuf = (struct pbuf*)mem;
|
||||
|
||||
pbuf->len = len;
|
||||
}
|
||||
|
||||
emac_stack_mem_t *emac_stack_mem_chain_dequeue(emac_stack_t* stack, emac_stack_mem_chain_t **chain)
|
||||
{
|
||||
struct pbuf **list = (struct pbuf**)chain;
|
||||
struct pbuf *head = *list;
|
||||
*list = (*list)->next;
|
||||
|
||||
return (emac_stack_mem_t *)head;
|
||||
}
|
||||
|
||||
uint32_t emac_stack_mem_chain_len(emac_stack_t* stack, emac_stack_mem_chain_t *chain)
|
||||
{
|
||||
return ((struct pbuf*)chain)->tot_len;
|
||||
}
|
||||
|
||||
void emac_stack_mem_ref(emac_stack_t* stack, emac_stack_mem_t *mem)
|
||||
{
|
||||
pbuf_ref((struct pbuf*)mem);
|
||||
}
|
||||
|
||||
#endif /* DEVICE_EMAC */
|
|
@ -1,45 +0,0 @@
|
|||
/* EthernetInterface.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Architecture specific Ethernet interface
|
||||
// Must be implemented by each target
|
||||
|
||||
#ifndef ETHARCH_H_
|
||||
#define ETHARCH_H_
|
||||
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if DEVICE_EMAC
|
||||
err_t emac_lwip_if_init(struct netif *netif);
|
||||
|
||||
#else /* DEVICE_EMAC */
|
||||
void eth_arch_enable_interrupts(void);
|
||||
void eth_arch_disable_interrupts(void);
|
||||
err_t eth_arch_enetif_init(struct netif *netif);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #ifndef ETHARCHINTERFACE_H_
|
|
@ -0,0 +1,3 @@
|
|||
This directory contains lwIP drivers that are no longer built. Any drivers
|
||||
remaining here must be convered to use the EMAC interface, and moved to
|
||||
features/netsocket/emac-drivers.
|
|
@ -1,781 +0,0 @@
|
|||
#include "lwip/opt.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
|
||||
#include "eth_arch.h"
|
||||
#include "sys_arch.h"
|
||||
|
||||
#include "fsl_phy.h"
|
||||
#include "k64f_emac_config.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mbed_interface.h"
|
||||
|
||||
enet_handle_t g_handle;
|
||||
// TX Buffer descriptors
|
||||
uint8_t *tx_desc_start_addr;
|
||||
// RX Buffer descriptors
|
||||
uint8_t *rx_desc_start_addr;
|
||||
// RX packet buffer pointers
|
||||
struct pbuf *rx_buff[ENET_RX_RING_LEN];
|
||||
// TX packet buffer pointers
|
||||
struct pbuf *tx_buff[ENET_RX_RING_LEN];
|
||||
// RX packet payload pointers
|
||||
uint32_t *rx_ptr[ENET_RX_RING_LEN];
|
||||
|
||||
/********************************************************************************
|
||||
* Internal data
|
||||
********************************************************************************/
|
||||
#define ENET_BuffSizeAlign(n) ENET_ALIGN(n, ENET_BUFF_ALIGNMENT)
|
||||
#define ENET_ALIGN(x,align) ((unsigned int)((x) + ((align)-1)) & (unsigned int)(~(unsigned int)((align)- 1)))
|
||||
#if (defined(TARGET_K64F) && (defined(TARGET_FRDM)))
|
||||
extern void k64f_init_eth_hardware(void);
|
||||
#endif
|
||||
|
||||
#if (defined(TARGET_K66F) && (defined(TARGET_FRDM)))
|
||||
extern void k66f_init_eth_hardware(void);
|
||||
#endif
|
||||
|
||||
/* K64F EMAC driver data structure */
|
||||
struct k64f_enetdata {
|
||||
struct netif *netif; /**< Reference back to LWIP parent netif */
|
||||
osThreadId_t thread; /**< Processing thread */
|
||||
sys_mutex_t TXLockMutex; /**< TX critical section mutex */
|
||||
sys_sem_t xTXDCountSem; /**< TX free buffer counting semaphore */
|
||||
uint8_t tx_consume_index, tx_produce_index; /**< TX buffers ring */
|
||||
};
|
||||
|
||||
static struct k64f_enetdata k64f_enetdata;
|
||||
|
||||
/* \brief Flags for worker thread */
|
||||
#define FLAG_TX 1
|
||||
#define FLAG_RX 2
|
||||
|
||||
/** \brief Driver thread priority */
|
||||
#define THREAD_PRIORITY (osPriorityNormal)
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
#define THREAD_STACKSIZE (DEFAULT_THREAD_STACKSIZE * 5)
|
||||
#else
|
||||
#define THREAD_STACKSIZE DEFAULT_THREAD_STACKSIZE
|
||||
#endif
|
||||
|
||||
static void k64f_phy_task(void *data);
|
||||
static void packet_rx(struct k64f_enetdata *k64f_enet);
|
||||
static void packet_tx(struct k64f_enetdata *k64f_enet);
|
||||
|
||||
#define PHY_TASK_PERIOD_MS 200
|
||||
|
||||
/********************************************************************************
|
||||
* Buffer management
|
||||
********************************************************************************/
|
||||
/*
|
||||
* This function will queue a new receive buffer
|
||||
*/
|
||||
static void update_read_buffer(uint8_t *buf)
|
||||
{
|
||||
if (buf != NULL) {
|
||||
g_handle.rxBdCurrent->buffer = buf;
|
||||
}
|
||||
|
||||
/* Clears status. */
|
||||
g_handle.rxBdCurrent->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
|
||||
|
||||
/* Sets the receive buffer descriptor with the empty flag. */
|
||||
g_handle.rxBdCurrent->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;
|
||||
|
||||
/* Increases the buffer descriptor to the next one. */
|
||||
if (g_handle.rxBdCurrent->control & ENET_BUFFDESCRIPTOR_RX_WRAP_MASK) {
|
||||
g_handle.rxBdCurrent = g_handle.rxBdBase;
|
||||
} else {
|
||||
g_handle.rxBdCurrent++;
|
||||
}
|
||||
|
||||
/* Actives the receive buffer descriptor. */
|
||||
ENET->RDAR = ENET_RDAR_RDAR_MASK;
|
||||
}
|
||||
|
||||
/** \brief Free TX buffers that are complete
|
||||
*
|
||||
* \param[in] k64f_enet Pointer to driver data structure
|
||||
*/
|
||||
static void k64f_tx_reclaim(struct k64f_enetdata *k64f_enet)
|
||||
{
|
||||
/* Get exclusive access */
|
||||
sys_mutex_lock(&k64f_enet->TXLockMutex);
|
||||
|
||||
// Traverse all descriptors, looking for the ones modified by the uDMA
|
||||
while((k64f_enet->tx_consume_index != k64f_enet->tx_produce_index) &&
|
||||
(!(g_handle.txBdDirty->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK))) {
|
||||
pbuf_free(tx_buff[k64f_enet->tx_consume_index % ENET_TX_RING_LEN]);
|
||||
if (g_handle.txBdDirty->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK)
|
||||
g_handle.txBdDirty = g_handle.txBdBase;
|
||||
else
|
||||
g_handle.txBdDirty++;
|
||||
|
||||
k64f_enet->tx_consume_index += 1;
|
||||
osSemaphoreRelease(k64f_enet->xTXDCountSem.id);
|
||||
}
|
||||
|
||||
/* Restore access */
|
||||
sys_mutex_unlock(&k64f_enet->TXLockMutex);
|
||||
}
|
||||
|
||||
/** \brief Ethernet receive interrupt handler
|
||||
*
|
||||
* This function handles the receive interrupt of K64F.
|
||||
*/
|
||||
void enet_mac_rx_isr()
|
||||
{
|
||||
if (k64f_enetdata.thread) {
|
||||
osThreadFlagsSet(k64f_enetdata.thread, FLAG_RX);
|
||||
}
|
||||
}
|
||||
|
||||
void enet_mac_tx_isr()
|
||||
{
|
||||
osThreadFlagsSet(k64f_enetdata.thread, FLAG_TX);
|
||||
}
|
||||
|
||||
void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case kENET_RxEvent:
|
||||
enet_mac_rx_isr();
|
||||
break;
|
||||
case kENET_TxEvent:
|
||||
enet_mac_tx_isr();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Low level init of the MAC and PHY.
|
||||
*
|
||||
* \param[in] netif Pointer to LWIP netif structure
|
||||
*/
|
||||
static err_t low_level_init(struct netif *netif)
|
||||
{
|
||||
struct k64f_enetdata *k64f_enet = netif->state;
|
||||
uint8_t i;
|
||||
uint32_t sysClock;
|
||||
phy_speed_t phy_speed;
|
||||
phy_duplex_t phy_duplex;
|
||||
uint32_t phyAddr = 0;
|
||||
bool link = false;
|
||||
enet_config_t config;
|
||||
|
||||
// Allocate RX descriptors
|
||||
rx_desc_start_addr = (uint8_t *)calloc(1, sizeof(enet_rx_bd_struct_t) * ENET_RX_RING_LEN + ENET_BUFF_ALIGNMENT);
|
||||
if(!rx_desc_start_addr)
|
||||
return ERR_MEM;
|
||||
|
||||
// Allocate TX descriptors
|
||||
tx_desc_start_addr = (uint8_t *)calloc(1, sizeof(enet_tx_bd_struct_t) * ENET_TX_RING_LEN + ENET_BUFF_ALIGNMENT);
|
||||
if(!tx_desc_start_addr)
|
||||
return ERR_MEM;
|
||||
|
||||
rx_desc_start_addr = (uint8_t *)ENET_ALIGN(rx_desc_start_addr, ENET_BUFF_ALIGNMENT);
|
||||
tx_desc_start_addr = (uint8_t *)ENET_ALIGN(tx_desc_start_addr, ENET_BUFF_ALIGNMENT);
|
||||
|
||||
/* Create buffers for each receive BD */
|
||||
for (i = 0; i < ENET_RX_RING_LEN; i++) {
|
||||
rx_buff[i] = pbuf_alloc(PBUF_RAW, ENET_ETH_MAX_FLEN + ENET_BUFF_ALIGNMENT, PBUF_RAM);
|
||||
if (NULL == rx_buff[i])
|
||||
return ERR_MEM;
|
||||
|
||||
/* K64F note: the next line ensures that the RX buffer is properly aligned for the K64F
|
||||
RX descriptors (16 bytes alignment). However, by doing so, we're effectively changing
|
||||
a data structure which is internal to lwIP. This might not prove to be a good idea
|
||||
in the long run, but a better fix would probably involve modifying lwIP itself */
|
||||
rx_buff[i]->payload = (void*)ENET_ALIGN((uint32_t)rx_buff[i]->payload, ENET_BUFF_ALIGNMENT);
|
||||
rx_ptr[i] = rx_buff[i]->payload;
|
||||
}
|
||||
|
||||
k64f_enet->tx_consume_index = k64f_enet->tx_produce_index = 0;
|
||||
|
||||
/* prepare the buffer configuration. */
|
||||
enet_buffer_config_t buffCfg = {
|
||||
ENET_RX_RING_LEN,
|
||||
ENET_TX_RING_LEN,
|
||||
ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT),
|
||||
0,
|
||||
(volatile enet_rx_bd_struct_t *)rx_desc_start_addr,
|
||||
(volatile enet_tx_bd_struct_t *)tx_desc_start_addr,
|
||||
(uint8_t *)&rx_ptr,
|
||||
NULL,
|
||||
};
|
||||
#if (defined(TARGET_K64F) && (defined(TARGET_FRDM)))
|
||||
k64f_init_eth_hardware();
|
||||
#endif
|
||||
|
||||
#if (defined(TARGET_K66F) && (defined(TARGET_FRDM)))
|
||||
k66f_init_eth_hardware();
|
||||
#endif
|
||||
|
||||
sysClock = CLOCK_GetFreq(kCLOCK_CoreSysClk);
|
||||
|
||||
ENET_GetDefaultConfig(&config);
|
||||
|
||||
PHY_Init(ENET, 0, sysClock);
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &link);
|
||||
if (link)
|
||||
{
|
||||
/* Get link information from PHY */
|
||||
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &phy_speed, &phy_duplex);
|
||||
/* Change the MII speed and duplex for actual link status. */
|
||||
config.miiSpeed = (enet_mii_speed_t)phy_speed;
|
||||
config.miiDuplex = (enet_mii_duplex_t)phy_duplex;
|
||||
config.interrupt = kENET_RxFrameInterrupt | kENET_TxFrameInterrupt;
|
||||
}
|
||||
config.rxMaxFrameLen = ENET_ETH_MAX_FLEN;
|
||||
config.macSpecialConfig = kENET_ControlFlowControlEnable;
|
||||
config.txAccelerConfig = kENET_TxAccelIsShift16Enabled;
|
||||
config.rxAccelerConfig = kENET_RxAccelisShift16Enabled | kENET_RxAccelMacCheckEnabled;
|
||||
ENET_Init(ENET, &g_handle, &config, &buffCfg, netif->hwaddr, sysClock);
|
||||
|
||||
#if defined(TOOLCHAIN_ARM)
|
||||
#if defined(__OPTIMISE_TIME) && (__ARMCC_VERSION < 5060750)
|
||||
/* Add multicast groups
|
||||
work around for https://github.com/ARMmbed/mbed-os/issues/4372 */
|
||||
ENET->GAUR = 0xFFFFFFFFu;
|
||||
ENET->GALR = 0xFFFFFFFFu;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ENET_SetCallback(&g_handle, ethernet_callback, netif);
|
||||
ENET_ActiveRead(ENET);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is the ipv4 ethernet packet send function. It calls
|
||||
* etharp_output after checking link status.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this enetif
|
||||
* \param[in] q Pointer to pbug to send
|
||||
* \param[in] ipaddr IP address
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
#if LWIP_IPV4
|
||||
err_t k64f_etharp_output_ipv4(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
|
||||
{
|
||||
/* Only send packet is link is up */
|
||||
if (netif->flags & NETIF_FLAG_LINK_UP) {
|
||||
return etharp_output(netif, q, ipaddr);
|
||||
}
|
||||
|
||||
return ERR_CONN;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function is the ipv6 ethernet packet send function. It calls
|
||||
* ethip6_output after checking link status.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this enetif
|
||||
* \param[in] q Pointer to pbug to send
|
||||
* \param[in] ipaddr IP address
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
#if LWIP_IPV6
|
||||
err_t k64f_etharp_output_ipv6(struct netif *netif, struct pbuf *q, const ip6_addr_t *ipaddr)
|
||||
{
|
||||
/* Only send packet is link is up */
|
||||
if (netif->flags & NETIF_FLAG_LINK_UP) {
|
||||
return ethip6_output(netif, q, ipaddr);
|
||||
}
|
||||
|
||||
return ERR_CONN;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_IGMP
|
||||
/**
|
||||
* IPv4 address filtering setup.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this enetif
|
||||
* \param[in] group IPv4 group to modify
|
||||
* \param[in] action
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
err_t igmp_mac_filter(struct netif *netif, const ip4_addr_t *group, enum netif_mac_filter_action action)
|
||||
{
|
||||
switch (action) {
|
||||
case NETIF_ADD_MAC_FILTER:
|
||||
{
|
||||
uint32_t group23 = ntohl(group->addr) & 0x007FFFFF;
|
||||
uint8_t addr[6];
|
||||
addr[0] = LL_IP4_MULTICAST_ADDR_0;
|
||||
addr[1] = LL_IP4_MULTICAST_ADDR_1;
|
||||
addr[2] = LL_IP4_MULTICAST_ADDR_2;
|
||||
addr[3] = group23 >> 16;
|
||||
addr[4] = group23 >> 8;
|
||||
addr[5] = group23;
|
||||
ENET_AddMulticastGroup(ENET, addr);
|
||||
return ERR_OK;
|
||||
}
|
||||
case NETIF_DEL_MAC_FILTER:
|
||||
/* As we don't reference count, silently ignore delete requests */
|
||||
return ERR_OK;
|
||||
default:
|
||||
return ERR_ARG;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
/**
|
||||
* IPv6 address filtering setup.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this enetif
|
||||
* \param[in] group IPv6 group to modify
|
||||
* \param[in] action
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
err_t mld_mac_filter(struct netif *netif, const ip6_addr_t *group, enum netif_mac_filter_action action)
|
||||
{
|
||||
switch (action) {
|
||||
case NETIF_ADD_MAC_FILTER:
|
||||
{
|
||||
uint32_t group32 = ntohl(group->addr[3]);
|
||||
uint8_t addr[6];
|
||||
addr[0] = LL_IP6_MULTICAST_ADDR_0;
|
||||
addr[1] = LL_IP6_MULTICAST_ADDR_1;
|
||||
addr[2] = group32 >> 24;
|
||||
addr[3] = group32 >> 16;
|
||||
addr[4] = group32 >> 8;
|
||||
addr[5] = group32;
|
||||
ENET_AddMulticastGroup(ENET, addr);
|
||||
return ERR_OK;
|
||||
}
|
||||
case NETIF_DEL_MAC_FILTER:
|
||||
/* As we don't reference count, silently ignore delete requests */
|
||||
return ERR_OK;
|
||||
default:
|
||||
return ERR_ARG;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \brief Allocates a pbuf and returns the data from the incoming packet.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure
|
||||
* \param[in] idx index of packet to be read
|
||||
* \return a pbuf filled with the received packet (including MAC header)
|
||||
*/
|
||||
static struct pbuf *k64f_low_level_input(struct netif *netif, int idx)
|
||||
{
|
||||
volatile enet_rx_bd_struct_t *bdPtr = g_handle.rxBdCurrent;
|
||||
struct pbuf *p = NULL;
|
||||
struct pbuf *temp_rxbuf = NULL;
|
||||
u32_t length = 0;
|
||||
const u16_t err_mask = ENET_BUFFDESCRIPTOR_RX_TRUNC_MASK | ENET_BUFFDESCRIPTOR_RX_CRC_MASK |
|
||||
ENET_BUFFDESCRIPTOR_RX_NOOCTET_MASK | ENET_BUFFDESCRIPTOR_RX_LENVLIOLATE_MASK;
|
||||
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
/* Get exclusive access */
|
||||
sys_mutex_lock(&k64f_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
/* Determine if a frame has been received */
|
||||
if ((bdPtr->control & err_mask) != 0) {
|
||||
#if LINK_STATS
|
||||
if ((bdPtr->control & ENET_BUFFDESCRIPTOR_RX_LENVLIOLATE_MASK) != 0)
|
||||
LINK_STATS_INC(link.lenerr);
|
||||
else
|
||||
LINK_STATS_INC(link.chkerr);
|
||||
#endif
|
||||
LINK_STATS_INC(link.drop);
|
||||
/* Re-use the same buffer in case of error */
|
||||
update_read_buffer(NULL);
|
||||
} else {
|
||||
/* A packet is waiting, get length */
|
||||
length = bdPtr->length;
|
||||
|
||||
/* Zero-copy */
|
||||
p = rx_buff[idx];
|
||||
p->len = length;
|
||||
|
||||
/* Attempt to queue new buffer */
|
||||
temp_rxbuf = pbuf_alloc(PBUF_RAW, ENET_ETH_MAX_FLEN + ENET_BUFF_ALIGNMENT, PBUF_RAM);
|
||||
if (NULL == temp_rxbuf) {
|
||||
/* Drop frame (out of memory) */
|
||||
LINK_STATS_INC(link.drop);
|
||||
|
||||
/* Re-queue the same buffer */
|
||||
update_read_buffer(NULL);
|
||||
|
||||
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
|
||||
("k64f_low_level_input: Packet index %d dropped for OOM\n",
|
||||
idx));
|
||||
#ifdef LOCK_RX_THREAD
|
||||
sys_mutex_unlock(&k64f_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rx_buff[idx] = temp_rxbuf;
|
||||
/* K64F note: the next line ensures that the RX buffer is properly aligned for the K64F
|
||||
RX descriptors (16 bytes alignment). However, by doing so, we're effectively changing
|
||||
a data structure which is internal to lwIP. This might not prove to be a good idea
|
||||
in the long run, but a better fix would probably involve modifying lwIP itself */
|
||||
rx_buff[idx]->payload = (void*)ENET_ALIGN((uint32_t)rx_buff[idx]->payload, ENET_BUFF_ALIGNMENT);
|
||||
rx_ptr[idx] = rx_buff[idx]->payload;
|
||||
|
||||
update_read_buffer(rx_buff[idx]->payload);
|
||||
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
|
||||
("k64f_low_level_input: Packet received: %p, size %"PRIu32" (index=%d)\n",
|
||||
p, length, idx));
|
||||
|
||||
/* Save size */
|
||||
p->tot_len = (u16_t) length;
|
||||
LINK_STATS_INC(link.recv);
|
||||
}
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
sys_mutex_unlock(&k64f_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/** \brief Attempt to read a packet from the EMAC interface.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure
|
||||
* \param[in] idx index of packet to be read
|
||||
*/
|
||||
void k64f_enetif_input(struct netif *netif, int idx)
|
||||
{
|
||||
struct pbuf *p;
|
||||
|
||||
/* move received packet into a new pbuf */
|
||||
p = k64f_low_level_input(netif, idx);
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("k64f_enetif_input: input error\n"));
|
||||
/* Free buffer */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Worker thread.
|
||||
*
|
||||
* Woken by thread flags to receive packets or clean up transmit
|
||||
*
|
||||
* \param[in] pvParameters pointer to the interface data
|
||||
*/
|
||||
static void emac_thread(void* pvParameters)
|
||||
{
|
||||
struct k64f_enetdata *k64f_enet = pvParameters;
|
||||
|
||||
for (;;) {
|
||||
uint32_t flags = osThreadFlagsWait(FLAG_RX|FLAG_TX, osFlagsWaitAny, PHY_TASK_PERIOD_MS);
|
||||
if (flags == osFlagsErrorTimeout) {
|
||||
// Rather than calling strictly every period, we call when idle
|
||||
// for that period - hopefully good enough. We run this task
|
||||
// from lwIP's thread rather than our RX/TX thread, as PHY reads can
|
||||
// be slow, and we don't want them to interfere with data pumping.
|
||||
// This is analogous to the way the PHY polling works in the Nanostack
|
||||
// version of the driver
|
||||
tcpip_callback_with_block(k64f_phy_task, k64f_enet->netif, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
LWIP_ASSERT("osThreadFlagsWait error", !(flags & osFlagsError));
|
||||
|
||||
if (flags & FLAG_RX) {
|
||||
packet_rx(k64f_enet);
|
||||
}
|
||||
|
||||
if (flags & FLAG_TX) {
|
||||
packet_tx(k64f_enet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Packet reception task
|
||||
*
|
||||
* This task is called when a packet is received. It will
|
||||
* pass the packet to the LWIP core.
|
||||
*
|
||||
* \param[in] k64f_enet pointer to the interface data
|
||||
*/
|
||||
static void packet_rx(struct k64f_enetdata *k64f_enet)
|
||||
{
|
||||
static int idx = 0;
|
||||
|
||||
while ((g_handle.rxBdCurrent->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK) == 0) {
|
||||
k64f_enetif_input(k64f_enet->netif, idx);
|
||||
idx = (idx + 1) % ENET_RX_RING_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Transmit cleanup task
|
||||
*
|
||||
* This task is called when a transmit interrupt occurs and
|
||||
* reclaims the pbuf and descriptor used for the packet once
|
||||
* the packet has been transferred.
|
||||
*
|
||||
* \param[in] k64f_enet pointer to the interface data
|
||||
*/
|
||||
static void packet_tx(struct k64f_enetdata *k64f_enet)
|
||||
{
|
||||
k64f_tx_reclaim(k64f_enet);
|
||||
}
|
||||
|
||||
/** \brief Low level output of a packet. Never call this from an
|
||||
* interrupt context, as it may block until TX descriptors
|
||||
* become available.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this netif
|
||||
* \param[in] p the MAC packet to send (e.g. IP packet including MAC addresses and type)
|
||||
* \return ERR_OK if the packet could be sent or an err_t value if the packet couldn't be sent
|
||||
*/
|
||||
static err_t k64f_low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
struct k64f_enetdata *k64f_enet = netif->state;
|
||||
struct pbuf *q;
|
||||
struct pbuf *temp_pbuf;
|
||||
uint8_t *psend = NULL, *dst;
|
||||
|
||||
temp_pbuf = pbuf_alloc(PBUF_RAW, p->tot_len + ENET_BUFF_ALIGNMENT, PBUF_RAM);
|
||||
if (NULL == temp_pbuf)
|
||||
return ERR_MEM;
|
||||
|
||||
/* K64F note: the next line ensures that the RX buffer is properly aligned for the K64F
|
||||
RX descriptors (16 bytes alignment). However, by doing so, we're effectively changing
|
||||
a data structure which is internal to lwIP. This might not prove to be a good idea
|
||||
in the long run, but a better fix would probably involve modifying lwIP itself */
|
||||
psend = (uint8_t *)ENET_ALIGN((uint32_t)temp_pbuf->payload, ENET_BUFF_ALIGNMENT);
|
||||
|
||||
for (q = p, dst = psend; q != NULL; q = q->next) {
|
||||
MEMCPY(dst, q->payload, q->len);
|
||||
dst += q->len;
|
||||
}
|
||||
|
||||
/* Check if a descriptor is available for the transfer. */
|
||||
osStatus_t stat = osSemaphoreAcquire(k64f_enet->xTXDCountSem.id, 0);
|
||||
if (stat != osOK)
|
||||
return ERR_BUF;
|
||||
|
||||
/* Get exclusive access */
|
||||
sys_mutex_lock(&k64f_enet->TXLockMutex);
|
||||
|
||||
/* Save the buffer so that it can be freed when transmit is done */
|
||||
tx_buff[k64f_enet->tx_produce_index % ENET_TX_RING_LEN] = temp_pbuf;
|
||||
k64f_enet->tx_produce_index += 1;
|
||||
|
||||
/* Setup transfers */
|
||||
g_handle.txBdCurrent->buffer = psend;
|
||||
g_handle.txBdCurrent->length = p->tot_len;
|
||||
g_handle.txBdCurrent->control |= (ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK);
|
||||
|
||||
/* Increase the buffer descriptor address. */
|
||||
if (g_handle.txBdCurrent->control & ENET_BUFFDESCRIPTOR_TX_WRAP_MASK)
|
||||
g_handle.txBdCurrent = g_handle.txBdBase;
|
||||
else
|
||||
g_handle.txBdCurrent++;
|
||||
|
||||
/* Active the transmit buffer descriptor. */
|
||||
ENET->TDAR = ENET_TDAR_TDAR_MASK;
|
||||
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
/* Restore access */
|
||||
sys_mutex_unlock(&k64f_enet->TXLockMutex);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* PHY task: monitor link
|
||||
*******************************************************************************/
|
||||
|
||||
#define STATE_UNKNOWN (-1)
|
||||
|
||||
typedef struct {
|
||||
int connected;
|
||||
phy_speed_t speed;
|
||||
phy_duplex_t duplex;
|
||||
} PHY_STATE;
|
||||
|
||||
int phy_link_status(void) {
|
||||
bool connection_status;
|
||||
uint32_t phyAddr = 0;
|
||||
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
|
||||
return (int)connection_status;
|
||||
}
|
||||
|
||||
static void k64f_phy_task(void *data)
|
||||
{
|
||||
struct netif *netif = data;
|
||||
|
||||
static PHY_STATE prev_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN};
|
||||
|
||||
uint32_t phyAddr = 0;
|
||||
|
||||
// Get current status
|
||||
PHY_STATE crt_state;
|
||||
bool connection_status;
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
|
||||
crt_state.connected = connection_status;
|
||||
// Get the actual PHY link speed
|
||||
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &crt_state.speed, &crt_state.duplex);
|
||||
|
||||
// Compare with previous state
|
||||
if (crt_state.connected != prev_state.connected) {
|
||||
// We're called from lwIP's tcpip thread, so can call link functions directly
|
||||
if (crt_state.connected) {
|
||||
netif_set_link_up(netif);
|
||||
} else {
|
||||
netif_set_link_down(netif);
|
||||
}
|
||||
}
|
||||
|
||||
if (crt_state.speed != prev_state.speed) {
|
||||
uint32_t rcr = ENET->RCR;
|
||||
rcr &= ~ENET_RCR_RMII_10T_MASK;
|
||||
rcr |= ENET_RCR_RMII_10T(!crt_state.speed);
|
||||
ENET->RCR = rcr;
|
||||
}
|
||||
|
||||
prev_state = crt_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called at the beginning of the program to set up the
|
||||
* network interface.
|
||||
*
|
||||
* This function should be passed as a parameter to netif_add().
|
||||
*
|
||||
* @param[in] netif the lwip network interface structure for this netif
|
||||
* @return ERR_OK if the loopif is initialized
|
||||
* ERR_MEM if private data couldn't be allocated
|
||||
* any other err_t on error
|
||||
*/
|
||||
err_t eth_arch_enetif_init(struct netif *netif)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
||||
|
||||
k64f_enetdata.netif = netif;
|
||||
|
||||
/* set MAC hardware address */
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
netif->hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *)netif->hwaddr);
|
||||
#endif
|
||||
|
||||
/* Ethernet address length */
|
||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||
|
||||
/* maximum transfer unit */
|
||||
netif->mtu = 1500;
|
||||
|
||||
/* device capabilities */
|
||||
// TODOETH: check if the flags are correct below
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
|
||||
/* Initialize the hardware */
|
||||
netif->state = &k64f_enetdata;
|
||||
err = low_level_init(netif);
|
||||
if (err != ERR_OK)
|
||||
return err;
|
||||
|
||||
#if LWIP_NETIF_HOSTNAME
|
||||
/* Initialize interface hostname */
|
||||
netif->hostname = "lwipk64f";
|
||||
#endif /* LWIP_NETIF_HOSTNAME */
|
||||
|
||||
netif->name[0] = 'e';
|
||||
netif->name[1] = 'n';
|
||||
|
||||
#if LWIP_IPV4
|
||||
netif->output = k64f_etharp_output_ipv4;
|
||||
#if LWIP_IGMP
|
||||
netif->igmp_mac_filter = igmp_mac_filter;
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
#endif
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = k64f_etharp_output_ipv6;
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->mld_mac_filter = mld_mac_filter;
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#else
|
||||
// Would need to enable all multicasts here - no API in fsl_enet to do that
|
||||
#error "IPv6 multicasts won't be received if LWIP_IPV6_MLD is disabled, breaking the system"
|
||||
#endif
|
||||
#endif
|
||||
netif->linkoutput = k64f_low_level_output;
|
||||
|
||||
/* CMSIS-RTOS, start tasks */
|
||||
memset(&k64f_enetdata.xTXDCountSem.data, 0, sizeof(k64f_enetdata.xTXDCountSem.data));
|
||||
k64f_enetdata.xTXDCountSem.attr.cb_mem = &k64f_enetdata.xTXDCountSem.data;
|
||||
k64f_enetdata.xTXDCountSem.attr.cb_size = sizeof(k64f_enetdata.xTXDCountSem.data);
|
||||
k64f_enetdata.xTXDCountSem.id = osSemaphoreNew(ENET_TX_RING_LEN, ENET_TX_RING_LEN, &k64f_enetdata.xTXDCountSem.attr);
|
||||
|
||||
LWIP_ASSERT("xTXDCountSem creation error", (k64f_enetdata.xTXDCountSem.id != NULL));
|
||||
|
||||
err = sys_mutex_new(&k64f_enetdata.TXLockMutex);
|
||||
LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK));
|
||||
|
||||
/* Allow the PHY task to detect the initial link state and set up the proper flags */
|
||||
tcpip_callback_with_block(k64f_phy_task, netif, 1);
|
||||
osDelay(10);
|
||||
|
||||
/* Worker thread */
|
||||
k64f_enetdata.thread = sys_thread_new("k64f_emac_thread", emac_thread, netif->state, THREAD_STACKSIZE, THREAD_PRIORITY)->id;
|
||||
|
||||
/* Trigger thread to deal with any RX packets that arrived before thread was started */
|
||||
enet_mac_rx_isr();
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void eth_arch_enable_interrupts(void) {
|
||||
//NVIC_SetPriority(ENET_Receive_IRQn, 6U);
|
||||
//NVIC_SetPriority(ENET_Transmit_IRQn, 6U);
|
||||
}
|
||||
|
||||
void eth_arch_disable_interrupts(void) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* --------------------------------- End Of File ------------------------------ */
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LWIPOPTS_CONF_H
|
||||
#define LWIPOPTS_CONF_H
|
||||
|
||||
#include "k64f_emac_config.h"
|
||||
|
||||
#define LWIP_TRANSPORT_ETHERNET 1
|
||||
#define ETH_PAD_SIZE 2
|
||||
|
||||
#define MEM_SIZE (ENET_RX_RING_LEN * (ENET_ETH_MAX_FLEN + ENET_BUFF_ALIGNMENT) + ENET_TX_RING_LEN * ENET_ETH_MAX_FLEN)
|
||||
|
||||
#endif
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2015, 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 LWIPOPTS_CONF_H
|
||||
#define LWIPOPTS_CONF_H
|
||||
|
||||
#define LWIP_TRANSPORT_ETHERNET 1
|
||||
#define ETH_PAD_SIZE 2
|
||||
|
||||
#define MEM_SIZE (16*1024)//(8*1024)//(16*1024)
|
||||
|
||||
#endif
|
|
@ -1,530 +0,0 @@
|
|||
/**
|
||||
* @file
|
||||
* Ethernet Interface Skeleton
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is a skeleton for developing Ethernet network interface
|
||||
* drivers for lwIP. Add code to the low_level functions and do a
|
||||
* search-and-replace for the word "ethernetif" to replace it with
|
||||
* something that better describes your network interface.
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <lwip/stats.h>
|
||||
#include <lwip/snmp.h>
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
#include "m480_eth.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "eth_arch.h"
|
||||
#include "sys_arch.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mbed_interface.h"
|
||||
#include "cmsis.h"
|
||||
|
||||
|
||||
/* Define those to better describe your network interface. */
|
||||
#define IFNAME0 'e'
|
||||
#define IFNAME1 'n'
|
||||
|
||||
// Fow now, all interrupt handling happens inside one single handler, so we need to figure
|
||||
// out what actually triggered the interrupt.
|
||||
static volatile uint8_t emac_timer_fired;
|
||||
volatile uint8_t allow_net_callbacks;
|
||||
|
||||
struct netif *_netif;
|
||||
|
||||
unsigned char my_mac_addr[6] = {0x02, 0x00, 0xac, 0x55, 0x66, 0x88};
|
||||
extern u8_t my_mac_addr[6];
|
||||
extern int ETH_link_ok(void);
|
||||
extern void EMAC_RX_Action(void);
|
||||
|
||||
sys_sem_t RxReadySem; /**< RX packet ready semaphore */
|
||||
|
||||
static void __phy_task(void *data);
|
||||
static void __packet_rx_task(void *data);
|
||||
/**
|
||||
* Helper struct to hold private data used to operate your ethernet interface.
|
||||
* Keeping the ethernet address of the MAC in this struct is not necessary
|
||||
* as it is already kept in the struct netif.
|
||||
* But this is only an example, anyway...
|
||||
*/
|
||||
struct ethernetif {
|
||||
struct eth_addr *ethaddr;
|
||||
/* Add whatever per-interface state that is needed here. */
|
||||
};
|
||||
|
||||
// Override mbed_mac_address of mbed_interface.c to provide ethernet devices with a semi-unique MAC address
|
||||
void mbed_mac_address(char *mac)
|
||||
{
|
||||
uint32_t uID1;
|
||||
// Fetch word 0
|
||||
uint32_t word0 = *(uint32_t *)0x7F804; // 2KB Data Flash at 0x7F800
|
||||
// Fetch word 1
|
||||
// we only want bottom 16 bits of word1 (MAC bits 32-47)
|
||||
// and bit 9 forced to 1, bit 8 forced to 0
|
||||
// Locally administered MAC, reduced conflicts
|
||||
// http://en.wikipedia.org/wiki/MAC_address
|
||||
uint32_t word1 = *(uint32_t *)0x7F800; // 2KB Data Flash at 0x7F800
|
||||
|
||||
if( word0 == 0xFFFFFFFF ) // Not burn any mac address at 1st 2 words of Data Flash
|
||||
{
|
||||
// with a semi-unique MAC address from the UUID
|
||||
/* Enable FMC ISP function */
|
||||
SYS_UnlockReg();
|
||||
FMC_Open();
|
||||
// = FMC_ReadUID(0);
|
||||
uID1 = FMC_ReadUID(1);
|
||||
word1 = (uID1 & 0x003FFFFF) | ((uID1 & 0x030000) << 6) >> 8;
|
||||
word0 = ((FMC_ReadUID(0) >> 4) << 20) | ((uID1 & 0xFF)<<12) | (FMC_ReadUID(2) & 0xFFF);
|
||||
/* Disable FMC ISP function */
|
||||
FMC_Close();
|
||||
/* Lock protected registers */
|
||||
SYS_LockReg();
|
||||
}
|
||||
|
||||
word1 |= 0x00000200;
|
||||
word1 &= 0x0000FEFF;
|
||||
|
||||
mac[0] = (word1 & 0x0000ff00) >> 8;
|
||||
mac[1] = (word1 & 0x000000ff);
|
||||
mac[2] = (word0 & 0xff000000) >> 24;
|
||||
mac[3] = (word0 & 0x00ff0000) >> 16;
|
||||
mac[4] = (word0 & 0x0000ff00) >> 8;
|
||||
mac[5] = (word0 & 0x000000ff);
|
||||
// printf("mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* In this function, the hardware should be initialized.
|
||||
* Called from ethernetif_init().
|
||||
*
|
||||
* @param netif the already initialized lwip network interface structure
|
||||
* for this ethernetif
|
||||
*/
|
||||
static void
|
||||
low_level_init(struct netif *netif)
|
||||
{
|
||||
|
||||
/* set MAC hardware address length */
|
||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||
|
||||
/* set MAC hardware address */
|
||||
#if 1 // set MAC HW address
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
netif->hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *)netif->hwaddr);
|
||||
#endif /* set MAC HW address */
|
||||
|
||||
#else
|
||||
netif->hwaddr[0] = my_mac_addr[0];
|
||||
netif->hwaddr[1] = my_mac_addr[1];
|
||||
netif->hwaddr[2] = my_mac_addr[2];
|
||||
netif->hwaddr[3] = my_mac_addr[3];
|
||||
netif->hwaddr[4] = my_mac_addr[4];
|
||||
netif->hwaddr[5] = my_mac_addr[5];
|
||||
#endif // endif
|
||||
|
||||
/* maximum transfer unit */
|
||||
netif->mtu = 1500;
|
||||
|
||||
/* device capabilities */
|
||||
/* NETIF_FLAG_LINK_UP should be enabled by netif_set_link_up() */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
#ifdef LWIP_IGMP
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
#endif
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#endif
|
||||
// TODO: enable clock & configure GPIO function
|
||||
ETH_init(netif->hwaddr);
|
||||
|
||||
#if LWIP_IGMP
|
||||
EMAC_ENABLE_RECV_BCASTPKT();
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
EMAC_ENABLE_RECV_MCASTPKT();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should do the actual transmission of the packet. The packet is
|
||||
* contained in the pbuf that is passed to the function. This pbuf
|
||||
* might be chained.
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
|
||||
* @return ERR_OK if the packet could be sent
|
||||
* an err_t value if the packet couldn't be sent
|
||||
*
|
||||
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
|
||||
* strange results. You might consider waiting for space in the DMA queue
|
||||
* to become availale since the stack doesn't retry to send a packet
|
||||
* dropped because of memory failure (except for the TCP timers).
|
||||
*/
|
||||
|
||||
static err_t
|
||||
low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
struct pbuf *q;
|
||||
u8_t *buf = NULL;
|
||||
u16_t len = 0;
|
||||
|
||||
|
||||
buf = ETH_get_tx_buf();
|
||||
if(buf == NULL)
|
||||
return ERR_MEM;
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
||||
#endif
|
||||
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
memcpy((u8_t*)&buf[len], q->payload, q->len);
|
||||
len = len + q->len;
|
||||
}
|
||||
#ifdef TIME_STAMPING
|
||||
ETH_trigger_tx(len, p->flags & PBUF_FLAG_GET_TXTS ? p : NULL);
|
||||
#else
|
||||
ETH_trigger_tx(len, NULL);
|
||||
#endif
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
|
||||
#endif
|
||||
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should allocate a pbuf and transfer the bytes of the incoming
|
||||
* packet from the interface into the pbuf.
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
* @return a pbuf filled with the received packet (including MAC header)
|
||||
* NULL on memory error
|
||||
*/
|
||||
static struct pbuf *
|
||||
low_level_input(struct netif *netif, u16_t len, u8_t *buf)
|
||||
{
|
||||
struct pbuf *p, *q;
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
|
||||
#endif
|
||||
|
||||
/* We allocate a pbuf chain of pbufs from the pool. */
|
||||
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
||||
|
||||
if (p != NULL) {
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
||||
#endif
|
||||
|
||||
|
||||
len = 0;
|
||||
/* We iterate over the pbuf chain until we have read the entire
|
||||
* packet into the pbuf. */
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
memcpy((u8_t*)q->payload, (u8_t*)&buf[len], q->len);
|
||||
len = len + q->len;
|
||||
}
|
||||
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
|
||||
#endif
|
||||
|
||||
LINK_STATS_INC(link.recv);
|
||||
} else {
|
||||
// do nothing. drop the packet
|
||||
LINK_STATS_INC(link.memerr);
|
||||
LINK_STATS_INC(link.drop);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should be called when a packet is ready to be read
|
||||
* from the interface. It uses the function low_level_input() that
|
||||
* should handle the actual reception of bytes from the network
|
||||
* interface. Then the type of the received packet is determined and
|
||||
* the appropriate input function is called.
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
*/
|
||||
void
|
||||
ethernetif_input(u16_t len, u8_t *buf, u32_t s, u32_t ns)
|
||||
{
|
||||
struct eth_hdr *ethhdr;
|
||||
struct pbuf *p;
|
||||
|
||||
|
||||
/* move received packet into a new pbuf */
|
||||
p = low_level_input(_netif, len, buf);
|
||||
/* no packet could be read, silently ignore this */
|
||||
if (p == NULL) return;
|
||||
#ifdef TIME_STAMPING
|
||||
p->ts_sec = s;
|
||||
p->ts_nsec = ns;
|
||||
#endif
|
||||
|
||||
/* points to packet payload, which starts with an Ethernet header */
|
||||
ethhdr = p->payload;
|
||||
|
||||
switch (htons(ethhdr->type)) {
|
||||
/* IP or ARP packet? */
|
||||
case ETHTYPE_IP:
|
||||
case ETHTYPE_ARP:
|
||||
#if PPPOE_SUPPORT
|
||||
/* PPPoE packet? */
|
||||
case ETHTYPE_PPPOEDISC:
|
||||
case ETHTYPE_PPPOE:
|
||||
#endif /* PPPOE_SUPPORT */
|
||||
/* full packet send to tcpip_thread to process */
|
||||
if (_netif->input(p, _netif)!=ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
|
||||
pbuf_free(p);
|
||||
p = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pbuf_free(p);
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TIME_STAMPING
|
||||
void
|
||||
ethernetif_loopback_input(struct pbuf *p) // TODO: make sure packet not drop in input()
|
||||
{
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("k64f_enetif_input: input error\n"));
|
||||
/* Free buffer */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Should be called at the beginning of the program to set up the
|
||||
* network interface. It calls the function low_level_init() to do the
|
||||
* actual setup of the hardware.
|
||||
*
|
||||
* This function should be passed as a parameter to netif_add().
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
* @return ERR_OK if the loopif is initialized
|
||||
* ERR_MEM if private data couldn't be allocated
|
||||
* any other err_t on error
|
||||
*/
|
||||
err_t
|
||||
eth_arch_enetif_init(struct netif *netif)
|
||||
{
|
||||
err_t err;
|
||||
struct ethernetif *ethernetif;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
||||
|
||||
_netif = netif;
|
||||
ethernetif = mem_malloc(sizeof(struct ethernetif));
|
||||
if (ethernetif == NULL) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, (" eth_arch_enetif_init: out of memory\n"));
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
// Chris: The initialization code uses osDelay, so timers neet to run
|
||||
// SysTick_Init();
|
||||
|
||||
#if LWIP_NETIF_HOSTNAME
|
||||
/* Initialize interface hostname */
|
||||
netif->hostname = "m480";
|
||||
#endif /* LWIP_NETIF_HOSTNAME */
|
||||
|
||||
/*
|
||||
* Initialize the snmp variables and counters inside the struct netif.
|
||||
* The last argument should be replaced with your link speed, in units
|
||||
* of bits per second.
|
||||
*/
|
||||
NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
|
||||
|
||||
netif->state = ethernetif;
|
||||
netif->name[0] = IFNAME0;
|
||||
netif->name[1] = IFNAME1;
|
||||
/* We directly use etharp_output() here to save a function call.
|
||||
* You can instead declare your own function an call etharp_output()
|
||||
* from it if you have to do some checks before sending (e.g. if link
|
||||
* is available...) */
|
||||
#if LWIP_IPV4
|
||||
netif->output = etharp_output;
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = ethip6_output;
|
||||
#endif
|
||||
netif->linkoutput = low_level_output;
|
||||
|
||||
ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
|
||||
|
||||
/* initialize the hardware */
|
||||
low_level_init(netif);
|
||||
|
||||
/* Packet receive task */
|
||||
|
||||
err = sys_sem_new(&RxReadySem, 0);
|
||||
if(err != ERR_OK) {
|
||||
LWIP_ASSERT("RxReadySem creation error", (err == ERR_OK));
|
||||
}
|
||||
// In GCC code, DEFAULT_THREAD_STACKSIZE 512 bytes is not enough for rx_task
|
||||
#if defined (__GNUC__)
|
||||
// mbed OS 2.0, DEFAULT_THREAD_STACKSIZE*3
|
||||
// mbed OS 5.0, DEFAULT_THREAD_STACKSIZE*5
|
||||
sys_thread_new("receive_thread", __packet_rx_task, &RxReadySem, DEFAULT_THREAD_STACKSIZE*5, osPriorityNormal);
|
||||
#else
|
||||
sys_thread_new("receive_thread", __packet_rx_task, &RxReadySem, DEFAULT_THREAD_STACKSIZE, osPriorityNormal);
|
||||
#endif
|
||||
/* PHY monitoring task */
|
||||
#if defined (__GNUC__)
|
||||
// mbed OS 2.0, DEFAULT_THREAD_STACKSIZE
|
||||
// mbed OS 5.0, DEFAULT_THREAD_STACKSIZE*2
|
||||
sys_thread_new("phy_thread", __phy_task, netif, DEFAULT_THREAD_STACKSIZE*2, osPriorityNormal);
|
||||
#else
|
||||
sys_thread_new("phy_thread", __phy_task, netif, DEFAULT_THREAD_STACKSIZE, osPriorityNormal);
|
||||
#endif
|
||||
/* Allow the PHY task to detect the initial link state and set up the proper flags */
|
||||
osDelay(10);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void eth_arch_enable_interrupts(void) {
|
||||
// enet_hal_config_interrupt(BOARD_DEBUG_ENET_INSTANCE_ADDR, (kEnetTxFrameInterrupt | kEnetRxFrameInterrupt), true);
|
||||
EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk |
|
||||
EMAC_INTEN_TXIEN_Msk ;
|
||||
NVIC_EnableIRQ(EMAC_RX_IRQn);
|
||||
NVIC_EnableIRQ(EMAC_TX_IRQn);
|
||||
}
|
||||
|
||||
void eth_arch_disable_interrupts(void) {
|
||||
NVIC_DisableIRQ(EMAC_RX_IRQn);
|
||||
NVIC_DisableIRQ(EMAC_TX_IRQn);
|
||||
}
|
||||
|
||||
|
||||
/* Defines the PHY link speed */
|
||||
typedef enum _phy_speed
|
||||
{
|
||||
kPHY_Speed10M = 0U, /* ENET PHY 10M speed. */
|
||||
kPHY_Speed100M /* ENET PHY 100M speed. */
|
||||
} phy_speed_t;
|
||||
|
||||
/* Defines the PHY link duplex. */
|
||||
typedef enum _phy_duplex
|
||||
{
|
||||
kPHY_HalfDuplex = 0U, /* ENET PHY half duplex. */
|
||||
kPHY_FullDuplex /* ENET PHY full duplex. */
|
||||
} phy_duplex_t;
|
||||
|
||||
typedef struct {
|
||||
int connected;
|
||||
phy_speed_t speed;
|
||||
phy_duplex_t duplex;
|
||||
} PHY_STATE;
|
||||
|
||||
#define STATE_UNKNOWN (-1)
|
||||
|
||||
static void __phy_task(void *data) {
|
||||
struct netif *netif = (struct netif*)data;
|
||||
// PHY_STATE crt_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN};
|
||||
// PHY_STATE prev_state;
|
||||
|
||||
// prev_state = crt_state;
|
||||
while (1) {
|
||||
// Get current status
|
||||
// Get the actual PHY link speed
|
||||
// Compare with previous state
|
||||
|
||||
if( !(ETH_link_ok()) && (netif->flags & NETIF_FLAG_LINK_UP) ) {
|
||||
/* tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_down, (void*) netif, 1); */
|
||||
netif_set_link_down(netif);
|
||||
LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING|LWIP_DBG_ON, ("Link Down\r\n"));
|
||||
}else if ( ETH_link_ok() && !(netif->flags & NETIF_FLAG_LINK_UP) ) {
|
||||
/* tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_up, (void*) netif, 1); */
|
||||
netif_set_link_up(netif);
|
||||
LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING|LWIP_DBG_ON, ("Link Up\r\n"));
|
||||
}
|
||||
|
||||
osDelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
void ack_emac_rx_isr()
|
||||
{
|
||||
sys_sem_signal(&RxReadySem);
|
||||
}
|
||||
|
||||
static void __packet_rx_task(void *data) {
|
||||
|
||||
while (1) {
|
||||
/* Wait for receive task to wakeup */
|
||||
sys_arch_sem_wait(&RxReadySem, 0);
|
||||
EMAC_RX_Action();
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Nuvoton Technology Corp.
|
||||
*
|
||||
* See file CREDITS for list of people who contributed to this
|
||||
* project.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
* Description: EMAC driver header file
|
||||
*/
|
||||
#ifndef __ETHERNETIF_H__
|
||||
#define __ETHERNETIF_H__
|
||||
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//extern sys_sem_t tx_sem;
|
||||
extern sys_sem_t rx_sem;
|
||||
|
||||
//err_t ethernetif_init(struct netif *netif);
|
||||
//err_t ethernetif_input(struct netif *netif);
|
||||
//struct netif *ethernetif_register(void);
|
||||
//int ethernetif_poll(void);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SERVER
|
||||
|
||||
#define MAC_ADDR0 0x00
|
||||
#define MAC_ADDR1 0x00
|
||||
#define MAC_ADDR2 0x00
|
||||
#define MAC_ADDR3 0x00
|
||||
#define MAC_ADDR4 0x00
|
||||
#define MAC_ADDR5 0x01
|
||||
|
||||
#else
|
||||
|
||||
#define MAC_ADDR0 0x00
|
||||
#define MAC_ADDR1 0x00
|
||||
#define MAC_ADDR2 0x00
|
||||
#define MAC_ADDR3 0x00
|
||||
#define MAC_ADDR4 0x00
|
||||
//#define MAC_ADDR5 0x02
|
||||
#define MAC_ADDR5 0x03
|
||||
//#define MAC_ADDR5 0x04
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2015, 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 LWIPOPTS_CONF_H
|
||||
#define LWIPOPTS_CONF_H
|
||||
|
||||
#define LWIP_TRANSPORT_ETHERNET 1
|
||||
#define ETH_PAD_SIZE 2
|
||||
|
||||
#define MEM_SIZE (16*1024)//(8*1024)//(16*1024)
|
||||
|
||||
#endif
|
|
@ -1,529 +0,0 @@
|
|||
/**
|
||||
* @file
|
||||
* Ethernet Interface Skeleton
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is a skeleton for developing Ethernet network interface
|
||||
* drivers for lwIP. Add code to the low_level functions and do a
|
||||
* search-and-replace for the word "ethernetif" to replace it with
|
||||
* something that better describes your network interface.
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <lwip/stats.h>
|
||||
#include <lwip/snmp.h>
|
||||
#include "netif/etharp.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
#include "nuc472_eth.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "eth_arch.h"
|
||||
#include "sys_arch.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mbed_interface.h"
|
||||
#include "cmsis.h"
|
||||
|
||||
|
||||
/* Define those to better describe your network interface. */
|
||||
#define IFNAME0 'e'
|
||||
#define IFNAME1 'n'
|
||||
|
||||
// Fow now, all interrupt handling happens inside one single handler, so we need to figure
|
||||
// out what actually triggered the interrupt.
|
||||
static volatile uint8_t emac_timer_fired;
|
||||
volatile uint8_t allow_net_callbacks;
|
||||
|
||||
struct netif *_netif;
|
||||
|
||||
unsigned char my_mac_addr[6] = {0x02, 0x00, 0xac, 0x55, 0x66, 0x77};
|
||||
extern u8_t my_mac_addr[6];
|
||||
extern int ETH_link_ok(void);
|
||||
extern void EMAC_RX_Action(void);
|
||||
|
||||
sys_sem_t RxReadySem; /**< RX packet ready semaphore */
|
||||
|
||||
static void __phy_task(void *data);
|
||||
static void __packet_rx_task(void *data);
|
||||
/**
|
||||
* Helper struct to hold private data used to operate your ethernet interface.
|
||||
* Keeping the ethernet address of the MAC in this struct is not necessary
|
||||
* as it is already kept in the struct netif.
|
||||
* But this is only an example, anyway...
|
||||
*/
|
||||
struct ethernetif {
|
||||
struct eth_addr *ethaddr;
|
||||
/* Add whatever per-interface state that is needed here. */
|
||||
};
|
||||
|
||||
// Override mbed_mac_address of mbed_interface.c to provide ethernet devices with a semi-unique MAC address
|
||||
void mbed_mac_address(char *mac)
|
||||
{
|
||||
uint32_t uID1;
|
||||
// Fetch word 0
|
||||
uint32_t word0 = *(uint32_t *)0x7F804; // 2KB Data Flash at 0x7F800
|
||||
// Fetch word 1
|
||||
// we only want bottom 16 bits of word1 (MAC bits 32-47)
|
||||
// and bit 9 forced to 1, bit 8 forced to 0
|
||||
// Locally administered MAC, reduced conflicts
|
||||
// http://en.wikipedia.org/wiki/MAC_address
|
||||
uint32_t word1 = *(uint32_t *)0x7F800; // 2KB Data Flash at 0x7F800
|
||||
|
||||
if( word0 == 0xFFFFFFFF ) // Not burn any mac address at 1st 2 words of Data Flash
|
||||
{
|
||||
// with a semi-unique MAC address from the UUID
|
||||
/* Enable FMC ISP function */
|
||||
SYS_UnlockReg();
|
||||
FMC_Open();
|
||||
// = FMC_ReadUID(0);
|
||||
uID1 = FMC_ReadUID(1);
|
||||
word1 = (uID1 & 0x003FFFFF) | ((uID1 & 0x030000) << 6) >> 8;
|
||||
word0 = ((FMC_ReadUID(0) >> 4) << 20) | ((uID1 & 0xFF)<<12) | (FMC_ReadUID(2) & 0xFFF);
|
||||
/* Disable FMC ISP function */
|
||||
FMC_Close();
|
||||
/* Lock protected registers */
|
||||
SYS_LockReg();
|
||||
}
|
||||
|
||||
word1 |= 0x00000200;
|
||||
word1 &= 0x0000FEFF;
|
||||
|
||||
mac[0] = (word1 & 0x0000ff00) >> 8;
|
||||
mac[1] = (word1 & 0x000000ff);
|
||||
mac[2] = (word0 & 0xff000000) >> 24;
|
||||
mac[3] = (word0 & 0x00ff0000) >> 16;
|
||||
mac[4] = (word0 & 0x0000ff00) >> 8;
|
||||
mac[5] = (word0 & 0x000000ff);
|
||||
|
||||
LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING|LWIP_DBG_ON, ("mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]));
|
||||
}
|
||||
|
||||
/**
|
||||
* In this function, the hardware should be initialized.
|
||||
* Called from ethernetif_init().
|
||||
*
|
||||
* @param netif the already initialized lwip network interface structure
|
||||
* for this ethernetif
|
||||
*/
|
||||
static void
|
||||
low_level_init(struct netif *netif)
|
||||
{
|
||||
|
||||
/* set MAC hardware address length */
|
||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||
|
||||
/* set MAC hardware address */
|
||||
#if 1 // set MAC HW address
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
netif->hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *)netif->hwaddr);
|
||||
#endif /* set MAC HW address */
|
||||
|
||||
#else
|
||||
netif->hwaddr[0] = my_mac_addr[0];
|
||||
netif->hwaddr[1] = my_mac_addr[1];
|
||||
netif->hwaddr[2] = my_mac_addr[2];
|
||||
netif->hwaddr[3] = my_mac_addr[3];
|
||||
netif->hwaddr[4] = my_mac_addr[4];
|
||||
netif->hwaddr[5] = my_mac_addr[5];
|
||||
#endif // endif
|
||||
|
||||
/* maximum transfer unit */
|
||||
netif->mtu = 1500;
|
||||
|
||||
/* device capabilities */
|
||||
/* NETIF_FLAG_LINK_UP should be enabled by netif_set_link_up() */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
#ifdef LWIP_IGMP
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
#endif
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#endif
|
||||
// TODO: enable clock & configure GPIO function
|
||||
ETH_init(netif->hwaddr);
|
||||
|
||||
#if LWIP_IGMP
|
||||
EMAC_ENABLE_RECV_BCASTPKT();
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
EMAC_ENABLE_RECV_MCASTPKT();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should do the actual transmission of the packet. The packet is
|
||||
* contained in the pbuf that is passed to the function. This pbuf
|
||||
* might be chained.
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
|
||||
* @return ERR_OK if the packet could be sent
|
||||
* an err_t value if the packet couldn't be sent
|
||||
*
|
||||
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
|
||||
* strange results. You might consider waiting for space in the DMA queue
|
||||
* to become availale since the stack doesn't retry to send a packet
|
||||
* dropped because of memory failure (except for the TCP timers).
|
||||
*/
|
||||
|
||||
static err_t
|
||||
low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
struct pbuf *q;
|
||||
u8_t *buf = NULL;
|
||||
u16_t len = 0;
|
||||
|
||||
|
||||
buf = ETH_get_tx_buf();
|
||||
if(buf == NULL)
|
||||
return ERR_MEM;
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
||||
#endif
|
||||
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
memcpy((u8_t*)&buf[len], q->payload, q->len);
|
||||
len = len + q->len;
|
||||
}
|
||||
#ifdef TIME_STAMPING
|
||||
ETH_trigger_tx(len, p->flags & PBUF_FLAG_GET_TXTS ? p : NULL);
|
||||
#else
|
||||
ETH_trigger_tx(len, NULL);
|
||||
#endif
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
|
||||
#endif
|
||||
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should allocate a pbuf and transfer the bytes of the incoming
|
||||
* packet from the interface into the pbuf.
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
* @return a pbuf filled with the received packet (including MAC header)
|
||||
* NULL on memory error
|
||||
*/
|
||||
static struct pbuf *
|
||||
low_level_input(struct netif *netif, u16_t len, u8_t *buf)
|
||||
{
|
||||
struct pbuf *p, *q;
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
|
||||
#endif
|
||||
|
||||
/* We allocate a pbuf chain of pbufs from the pool. */
|
||||
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
||||
|
||||
if (p != NULL) {
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
||||
#endif
|
||||
|
||||
|
||||
len = 0;
|
||||
/* We iterate over the pbuf chain until we have read the entire
|
||||
* packet into the pbuf. */
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
memcpy((u8_t*)q->payload, (u8_t*)&buf[len], q->len);
|
||||
len = len + q->len;
|
||||
}
|
||||
|
||||
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
|
||||
#endif
|
||||
|
||||
LINK_STATS_INC(link.recv);
|
||||
} else {
|
||||
// do nothing. drop the packet
|
||||
LINK_STATS_INC(link.memerr);
|
||||
LINK_STATS_INC(link.drop);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should be called when a packet is ready to be read
|
||||
* from the interface. It uses the function low_level_input() that
|
||||
* should handle the actual reception of bytes from the network
|
||||
* interface. Then the type of the received packet is determined and
|
||||
* the appropriate input function is called.
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
*/
|
||||
void
|
||||
ethernetif_input(u16_t len, u8_t *buf, u32_t s, u32_t ns)
|
||||
{
|
||||
struct eth_hdr *ethhdr;
|
||||
struct pbuf *p;
|
||||
|
||||
|
||||
/* move received packet into a new pbuf */
|
||||
p = low_level_input(_netif, len, buf);
|
||||
/* no packet could be read, silently ignore this */
|
||||
if (p == NULL) return;
|
||||
#ifdef TIME_STAMPING
|
||||
p->ts_sec = s;
|
||||
p->ts_nsec = ns;
|
||||
#endif
|
||||
|
||||
/* points to packet payload, which starts with an Ethernet header */
|
||||
ethhdr = p->payload;
|
||||
|
||||
switch (htons(ethhdr->type)) {
|
||||
/* IP or ARP packet? */
|
||||
case ETHTYPE_IP:
|
||||
case ETHTYPE_ARP:
|
||||
#if PPPOE_SUPPORT
|
||||
/* PPPoE packet? */
|
||||
case ETHTYPE_PPPOEDISC:
|
||||
case ETHTYPE_PPPOE:
|
||||
#endif /* PPPOE_SUPPORT */
|
||||
/* full packet send to tcpip_thread to process */
|
||||
if (_netif->input(p, _netif)!=ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
|
||||
pbuf_free(p);
|
||||
p = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pbuf_free(p);
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TIME_STAMPING
|
||||
void
|
||||
ethernetif_loopback_input(struct pbuf *p) // TODO: make sure packet not drop in input()
|
||||
{
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("netif_input: input error\n"));
|
||||
/* Free buffer */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Should be called at the beginning of the program to set up the
|
||||
* network interface. It calls the function low_level_init() to do the
|
||||
* actual setup of the hardware.
|
||||
*
|
||||
* This function should be passed as a parameter to netif_add().
|
||||
*
|
||||
* @param netif the lwip network interface structure for this ethernetif
|
||||
* @return ERR_OK if the loopif is initialized
|
||||
* ERR_MEM if private data couldn't be allocated
|
||||
* any other err_t on error
|
||||
*/
|
||||
err_t
|
||||
eth_arch_enetif_init(struct netif *netif)
|
||||
{
|
||||
err_t err;
|
||||
struct ethernetif *ethernetif;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
||||
|
||||
_netif = netif;
|
||||
ethernetif = mem_malloc(sizeof(struct ethernetif));
|
||||
if (ethernetif == NULL) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, (" eth_arch_enetif_init: out of memory\n"));
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
// Chris: The initialization code uses osDelay, so timers neet to run
|
||||
// SysTick_Init();
|
||||
|
||||
#if LWIP_NETIF_HOSTNAME
|
||||
/* Initialize interface hostname */
|
||||
netif->hostname = "nuc472";
|
||||
#endif /* LWIP_NETIF_HOSTNAME */
|
||||
|
||||
/*
|
||||
* Initialize the snmp variables and counters inside the struct netif.
|
||||
* The last argument should be replaced with your link speed, in units
|
||||
* of bits per second.
|
||||
*/
|
||||
NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
|
||||
|
||||
netif->state = ethernetif;
|
||||
netif->name[0] = IFNAME0;
|
||||
netif->name[1] = IFNAME1;
|
||||
/* We directly use etharp_output() here to save a function call.
|
||||
* You can instead declare your own function an call etharp_output()
|
||||
* from it if you have to do some checks before sending (e.g. if link
|
||||
* is available...) */
|
||||
#if LWIP_IPV4
|
||||
netif->output = etharp_output;
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = ethip6_output;
|
||||
#endif
|
||||
netif->linkoutput = low_level_output;
|
||||
|
||||
ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
|
||||
|
||||
/* initialize the hardware */
|
||||
low_level_init(netif);
|
||||
|
||||
/* Packet receive task */
|
||||
|
||||
err = sys_sem_new(&RxReadySem, 0);
|
||||
if(err != ERR_OK) LWIP_ASSERT("RxReadySem creation error", (err == ERR_OK));
|
||||
// In GCC code, DEFAULT_THREAD_STACKSIZE 512 bytes is not enough for rx_task
|
||||
#if defined (__GNUC__)
|
||||
// mbed OS 2.0, DEFAULT_THREAD_STACKSIZE*3
|
||||
// mbed OS 5.0, DEFAULT_THREAD_STACKSIZE*5
|
||||
sys_thread_new("nuc472_emac_rx_thread", __packet_rx_task, &RxReadySem, DEFAULT_THREAD_STACKSIZE*5, osPriorityNormal);
|
||||
#else
|
||||
sys_thread_new("nuc472_emac_rx_thread", __packet_rx_task, &RxReadySem, DEFAULT_THREAD_STACKSIZE, osPriorityNormal);
|
||||
#endif
|
||||
/* PHY monitoring task */
|
||||
#if defined (__GNUC__)
|
||||
// mbed OS 2.0, DEFAULT_THREAD_STACKSIZE
|
||||
// mbed OS 5.0, DEFAULT_THREAD_STACKSIZE*2
|
||||
sys_thread_new("nuc472_emac_phy_thread", __phy_task, netif, DEFAULT_THREAD_STACKSIZE*2, osPriorityNormal);
|
||||
#else
|
||||
sys_thread_new("nuc472_emac_phy_thread", __phy_task, netif, DEFAULT_THREAD_STACKSIZE, osPriorityNormal);
|
||||
#endif
|
||||
/* Allow the PHY task to detect the initial link state and set up the proper flags */
|
||||
osDelay(10);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void eth_arch_enable_interrupts(void) {
|
||||
// enet_hal_config_interrupt(BOARD_DEBUG_ENET_INSTANCE_ADDR, (kEnetTxFrameInterrupt | kEnetRxFrameInterrupt), true);
|
||||
EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk |
|
||||
EMAC_INTEN_TXIEN_Msk ;
|
||||
NVIC_EnableIRQ(EMAC_RX_IRQn);
|
||||
NVIC_EnableIRQ(EMAC_TX_IRQn);
|
||||
}
|
||||
|
||||
void eth_arch_disable_interrupts(void) {
|
||||
NVIC_DisableIRQ(EMAC_RX_IRQn);
|
||||
NVIC_DisableIRQ(EMAC_TX_IRQn);
|
||||
}
|
||||
|
||||
|
||||
/* Defines the PHY link speed */
|
||||
typedef enum _phy_speed
|
||||
{
|
||||
kPHY_Speed10M = 0U, /* ENET PHY 10M speed. */
|
||||
kPHY_Speed100M /* ENET PHY 100M speed. */
|
||||
} phy_speed_t;
|
||||
|
||||
/* Defines the PHY link duplex. */
|
||||
typedef enum _phy_duplex
|
||||
{
|
||||
kPHY_HalfDuplex = 0U, /* ENET PHY half duplex. */
|
||||
kPHY_FullDuplex /* ENET PHY full duplex. */
|
||||
} phy_duplex_t;
|
||||
|
||||
typedef struct {
|
||||
int connected;
|
||||
phy_speed_t speed;
|
||||
phy_duplex_t duplex;
|
||||
} PHY_STATE;
|
||||
|
||||
#define STATE_UNKNOWN (-1)
|
||||
|
||||
static void __phy_task(void *data) {
|
||||
struct netif *netif = (struct netif*)data;
|
||||
// PHY_STATE crt_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN};
|
||||
// PHY_STATE prev_state;
|
||||
|
||||
// prev_state = crt_state;
|
||||
while (1) {
|
||||
// Get current status
|
||||
// Get the actual PHY link speed
|
||||
// Compare with previous state
|
||||
|
||||
if( !(ETH_link_ok()) && (netif->flags & NETIF_FLAG_LINK_UP) ) {
|
||||
/* tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_down, (void*) netif, 1); */
|
||||
netif_set_link_down(netif);
|
||||
LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING|LWIP_DBG_ON, ("Link Down\r\n"));
|
||||
}else if ( ETH_link_ok() && !(netif->flags & NETIF_FLAG_LINK_UP) ) {
|
||||
/* tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_up, (void*) netif, 1); */
|
||||
netif_set_link_up(netif);
|
||||
LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING|LWIP_DBG_ON, ("Link Up\r\n"));
|
||||
}
|
||||
|
||||
osDelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
void ack_emac_rx_isr()
|
||||
{
|
||||
sys_sem_signal(&RxReadySem);
|
||||
}
|
||||
|
||||
static void __packet_rx_task(void *data) {
|
||||
|
||||
while (1) {
|
||||
/* Wait for receive task to wakeup */
|
||||
sys_arch_sem_wait(&RxReadySem, 0);
|
||||
EMAC_RX_Action();
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Nuvoton Technology Corp.
|
||||
*
|
||||
* See file CREDITS for list of people who contributed to this
|
||||
* project.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
* Description: EMAC driver header file
|
||||
*/
|
||||
#ifndef __ETHERNETIF_H__
|
||||
#define __ETHERNETIF_H__
|
||||
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//extern sys_sem_t tx_sem;
|
||||
extern sys_sem_t rx_sem;
|
||||
|
||||
//err_t ethernetif_init(struct netif *netif);
|
||||
//err_t ethernetif_input(struct netif *netif);
|
||||
//struct netif *ethernetif_register(void);
|
||||
//int ethernetif_poll(void);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SERVER
|
||||
|
||||
#define MAC_ADDR0 0x00
|
||||
#define MAC_ADDR1 0x00
|
||||
#define MAC_ADDR2 0x00
|
||||
#define MAC_ADDR3 0x00
|
||||
#define MAC_ADDR4 0x00
|
||||
#define MAC_ADDR5 0x01
|
||||
|
||||
#else
|
||||
|
||||
#define MAC_ADDR0 0x00
|
||||
#define MAC_ADDR1 0x00
|
||||
#define MAC_ADDR2 0x00
|
||||
#define MAC_ADDR3 0x00
|
||||
#define MAC_ADDR4 0x00
|
||||
//#define MAC_ADDR5 0x02
|
||||
#define MAC_ADDR5 0x03
|
||||
//#define MAC_ADDR5 0x04
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,692 +0,0 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "lwip/opt.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
|
||||
#include "eth_arch.h"
|
||||
#include "sys_arch.h"
|
||||
|
||||
#include "fsl_phy.h"
|
||||
#include "lpc546xx_emac_config.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "mbed_interface.h"
|
||||
|
||||
#if LWIP_ARP || LWIP_ETHERNET
|
||||
|
||||
enet_handle_t g_handle;
|
||||
// RX packet buffer pointers
|
||||
struct pbuf *rx_buff[ENET_RX_RING_LEN];
|
||||
// TX packet buffer pointers
|
||||
uint8_t *tx_buff[ENET_TX_RING_LEN];
|
||||
// RX packet payload pointers
|
||||
uint32_t rx_ptr[ENET_RX_RING_LEN];
|
||||
|
||||
/** \brief Debug output formatter lock define
|
||||
*
|
||||
* When using FreeRTOS and with LWIP_DEBUG enabled, enabling this
|
||||
* define will allow RX debug messages to not interleave with the
|
||||
* TX messages (so they are actually readable). Not enabling this
|
||||
* define when the system is under load will cause the output to
|
||||
* be unreadable. There is a small tradeoff in performance for this
|
||||
* so use it only for debug. */
|
||||
//#define LOCK_RX_THREAD
|
||||
|
||||
/* LPC546XX EMAC driver data structure */
|
||||
struct lpc_enetdata {
|
||||
struct netif *netif; /**< Reference back to LWIP parent netif */
|
||||
osThreadId_t thread; /**< Processing thread */
|
||||
sys_mutex_t TXLockMutex; /**< TX critical section mutex */
|
||||
sys_sem_t xTXDCountSem; /**< TX free buffer counting semaphore */
|
||||
};
|
||||
|
||||
static struct lpc_enetdata lpc_enetdata;
|
||||
|
||||
/* \brief Flags for worker thread */
|
||||
#define FLAG_TX 1
|
||||
#define FLAG_RX 2
|
||||
|
||||
/** \brief Driver thread priority */
|
||||
#define THREAD_PRIORITY (osPriorityNormal)
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
#define THREAD_STACKSIZE (DEFAULT_THREAD_STACKSIZE * 5)
|
||||
#else
|
||||
#define THREAD_STACKSIZE DEFAULT_THREAD_STACKSIZE
|
||||
#endif
|
||||
|
||||
static void lpc546xx_phy_task(void *data);
|
||||
static void lpc546xx_packet_rx(struct lpc_enetdata *lpc_enet);
|
||||
static void lpc546xx_packet_tx(struct lpc_enetdata *lpc_enet);
|
||||
|
||||
#define PHY_TASK_PERIOD_MS 200
|
||||
|
||||
/********************************************************************************
|
||||
* Buffer management
|
||||
********************************************************************************/
|
||||
/*
|
||||
* This function will queue a new receive buffer
|
||||
*/
|
||||
static void update_read_buffer(enet_rx_bd_struct_t *rxDesc, uint8_t *buf)
|
||||
{
|
||||
enet_rx_bd_ring_t *rxBdRing = (enet_rx_bd_ring_t *)&g_handle.rxBdRing[0];
|
||||
uint32_t control = ENET_RXDESCRIP_RD_OWN_MASK | ENET_RXDESCRIP_RD_BUFF1VALID_MASK | ENET_RXDESCRIP_RD_IOC_MASK;
|
||||
uint32_t index = rxBdRing->rxGenIdx;
|
||||
/* Increase the index. */
|
||||
index++;
|
||||
if (index >= ENET_RX_RING_LEN) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
rxBdRing->rxGenIdx = index;
|
||||
if (buf != NULL) {
|
||||
rxDesc->buff1Addr = (uint32_t)buf;
|
||||
}
|
||||
|
||||
rxDesc->buff2Addr = 0;
|
||||
rxDesc->reserved = 0;
|
||||
rxDesc->control = control;
|
||||
}
|
||||
|
||||
/** \brief Free TX buffers that are complete
|
||||
*
|
||||
* \param[in] lpc_enet Pointer to driver data structure
|
||||
*/
|
||||
static void lpc546xx_tx_reclaim(struct lpc_enetdata *lpc_enet)
|
||||
{
|
||||
static uint8_t consume_index = 0;
|
||||
enet_tx_bd_ring_t *txBdRing = (enet_tx_bd_ring_t *)&g_handle.txBdRing[0];
|
||||
|
||||
while (consume_index != txBdRing->txConsumIdx) {
|
||||
/* Free the transmit buffer */
|
||||
free(tx_buff[consume_index]);
|
||||
tx_buff[consume_index] = NULL;
|
||||
|
||||
osSemaphoreRelease(lpc_enetdata.xTXDCountSem.id);
|
||||
|
||||
consume_index++;
|
||||
if (consume_index >= ENET_TX_RING_LEN) {
|
||||
consume_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Internal data
|
||||
********************************************************************************/
|
||||
#define ENET_BuffSizeAlign(n) ENET_ALIGN(n, ENET_BUFF_ALIGNMENT)
|
||||
#define ENET_ALIGN(x,align) ((unsigned int)((x) + ((align)-1)) & (unsigned int)(~(unsigned int)((align)- 1)))
|
||||
extern void lpc546xx_init_eth_hardware(void);
|
||||
|
||||
/** \brief Allocates a pbuf and returns the data from the incoming packet.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \return a pbuf filled with the received packet (including MAC header)
|
||||
* NULL on memory error
|
||||
*/
|
||||
static struct pbuf *lpc546xx_low_level_input(struct netif *netif)
|
||||
{
|
||||
enet_rx_bd_ring_t *rxBdRing = (enet_rx_bd_ring_t *)&g_handle.rxBdRing[0];
|
||||
enet_rx_bd_struct_t *bdPtr = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
|
||||
#ifdef LOCK_RX_THREAD
|
||||
struct lpc_enetdata *lpc_enet = netif->state;
|
||||
#endif
|
||||
struct pbuf *p = NULL;
|
||||
struct pbuf *temp_rxbuf = NULL;
|
||||
u32_t length = 0;
|
||||
|
||||
/* Determine if a frame has been received */
|
||||
if ((bdPtr->control & ENET_RXDESCRIP_WR_OWN_MASK) != 0) {
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
/* Get exclusive access */
|
||||
sys_mutex_lock(&lpc_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
/* Determine if the received frame has an error */
|
||||
if ((bdPtr->control & ENET_RXDESCRIP_WR_ERRSUM_MASK) != 0) {
|
||||
LINK_STATS_INC(link.err);
|
||||
LINK_STATS_INC(link.drop);
|
||||
/* Re-use the same buffer in case of error */
|
||||
update_read_buffer(bdPtr, NULL);
|
||||
} else {
|
||||
if (bdPtr->control & ENET_RXDESCRIP_WR_LD_MASK) {
|
||||
length = (bdPtr->control & ENET_RXDESCRIP_WR_PACKETLEN_MASK);
|
||||
} else {
|
||||
length = rxBdRing->rxBuffSizeAlign;
|
||||
}
|
||||
|
||||
/* Zero-copy */
|
||||
p = rx_buff[rxBdRing->rxGenIdx];
|
||||
p->len = length;
|
||||
|
||||
/* Attempt to queue new buffer */
|
||||
temp_rxbuf = pbuf_alloc(PBUF_RAW, ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT), PBUF_RAM);
|
||||
if (NULL == temp_rxbuf) {
|
||||
/* Drop frame (out of memory) */
|
||||
LINK_STATS_INC(link.drop);
|
||||
|
||||
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
|
||||
("lpc546xx_low_level_input: Packet index %d dropped for OOM\n", rxBdRing->rxGenIdx));
|
||||
|
||||
/* Re-queue the same buffer */
|
||||
update_read_buffer(bdPtr, NULL);
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
sys_mutex_unlock(&lpc_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rx_buff[rxBdRing->rxGenIdx] = temp_rxbuf;
|
||||
rx_ptr[rxBdRing->rxGenIdx] = (uint32_t)rx_buff[rxBdRing->rxGenIdx]->payload;
|
||||
|
||||
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
|
||||
("lpc_low_level_input: Packet received: %p, size %"PRIu32" (index=%d)\n", p, length, rxBdRing->rxGenIdx));
|
||||
|
||||
update_read_buffer(bdPtr, rx_buff[rxBdRing->rxGenIdx]->payload);
|
||||
|
||||
/* Save size */
|
||||
p->tot_len = (u16_t)length;
|
||||
LINK_STATS_INC(link.recv);
|
||||
}
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
sys_mutex_unlock(&lpc_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/** \brief Attempt to read a packet from the EMAC interface.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure
|
||||
*/
|
||||
void lpc546xx_enetif_input(struct netif *netif)
|
||||
{
|
||||
struct pbuf *p;
|
||||
|
||||
/* move received packet into a new pbuf */
|
||||
p = lpc546xx_low_level_input(netif);
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("lpc546xx_enetif_input: input error\n"));
|
||||
/* Free buffer */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Worker thread.
|
||||
*
|
||||
* Woken by thread flags to receive packets or clean up transmit
|
||||
*
|
||||
* \param[in] pvParameters pointer to the interface data
|
||||
*/
|
||||
static void lpc546xx_emac_thread(void* pvParameters)
|
||||
{
|
||||
struct lpc_enetdata *lpc_enet = pvParameters;
|
||||
|
||||
for (;;) {
|
||||
uint32_t flags = osThreadFlagsWait(FLAG_RX | FLAG_TX, osFlagsWaitAny, PHY_TASK_PERIOD_MS);
|
||||
if (flags == osFlagsErrorTimeout) {
|
||||
// Rather than calling strictly every period, we call when idle
|
||||
// for that period - hopefully good enough. We run this task
|
||||
// from lwIP's thread rather than our RX/TX thread, as PHY reads can
|
||||
// be slow, and we don't want them to interfere with data pumping.
|
||||
// This is analogous to the way the PHY polling works in the Nanostack
|
||||
// version of the driver
|
||||
tcpip_callback_with_block(lpc546xx_phy_task, lpc_enet->netif, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
LWIP_ASSERT("osThreadFlagsWait error", !(flags & osFlagsError));
|
||||
if (flags & FLAG_RX) {
|
||||
lpc546xx_packet_rx(lpc_enet);
|
||||
}
|
||||
|
||||
if (flags & FLAG_TX) {
|
||||
lpc546xx_packet_tx(lpc_enet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Low level output of a packet. Never call this from an
|
||||
* interrupt context, as it may block until TX descriptors
|
||||
* become available.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \param[in] p the MAC packet to send (e.g. IP packet including MAC addresses and type)
|
||||
* \return ERR_OK if the packet could be sent or an err_t value if the packet couldn't be sent
|
||||
*/
|
||||
static err_t lpc546xx_low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
struct lpc_enetdata *lpc_enet = netif->state;
|
||||
struct pbuf *q;
|
||||
uint8_t *psend = NULL, *dst;
|
||||
enet_tx_bd_ring_t *txBdRing = (enet_tx_bd_ring_t *)&g_handle.txBdRing[0];
|
||||
uint32_t index = txBdRing->txGenIdx;
|
||||
|
||||
psend = (uint8_t *)calloc(1, ENET_ALIGN(p->tot_len, ENET_BUFF_ALIGNMENT));
|
||||
if (NULL == psend)
|
||||
return ERR_MEM;
|
||||
|
||||
if (p->len == p->tot_len) {
|
||||
MEMCPY(psend, p->payload, p->len);
|
||||
} else {
|
||||
for (q = p, dst = psend; q != NULL; q = q->next) {
|
||||
MEMCPY(dst, q->payload, q->len);
|
||||
dst += q->len;
|
||||
}
|
||||
}
|
||||
/* Check if a descriptor is available for the transfer. */
|
||||
osStatus_t stat = osSemaphoreAcquire(lpc_enet->xTXDCountSem.id, 0);
|
||||
if (stat != osOK)
|
||||
return ERR_BUF;
|
||||
|
||||
/* Save the buffer so that it can be freed when transmit is done */
|
||||
tx_buff[index] = psend;
|
||||
|
||||
if (ENET_SendFrame(ENET, &g_handle, psend, p->tot_len) != kStatus_Success) {
|
||||
free(psend);
|
||||
tx_buff[index] = NULL;
|
||||
return ERR_IF;
|
||||
}
|
||||
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** \brief Ethernet receive interrupt handler
|
||||
*
|
||||
* This function handles the receive interrupt of LPC546XX.
|
||||
*/
|
||||
void enet_mac_rx_isr()
|
||||
{
|
||||
osThreadFlagsSet(lpc_enetdata.thread, FLAG_RX);
|
||||
}
|
||||
|
||||
void enet_mac_tx_isr()
|
||||
{
|
||||
osThreadFlagsSet(lpc_enetdata.thread, FLAG_TX);
|
||||
}
|
||||
|
||||
void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, uint8_t channel, void *param)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case kENET_RxIntEvent:
|
||||
enet_mac_rx_isr();
|
||||
break;
|
||||
case kENET_TxIntEvent:
|
||||
enet_mac_tx_isr();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if NO_SYS == 0
|
||||
|
||||
/** \brief Packet reception task
|
||||
*
|
||||
* This task is called when a packet is received. It will
|
||||
* pass the packet to the LWIP core.
|
||||
*
|
||||
* \param[in] lpc_enet pointer to the interface data
|
||||
*/
|
||||
static void lpc546xx_packet_rx(struct lpc_enetdata *lpc_enet)
|
||||
{
|
||||
enet_rx_bd_ring_t *rxBdRing = (enet_rx_bd_ring_t *)&g_handle.rxBdRing[0];
|
||||
enet_rx_bd_struct_t *bdPtr = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
|
||||
bool suspend = false;
|
||||
|
||||
/* Suspend and command for rx. */
|
||||
if (ENET->DMA_CH[0].DMA_CHX_STAT & ENET_DMA_CH_DMA_CHX_STAT_RBU_MASK) {
|
||||
suspend = true;
|
||||
}
|
||||
|
||||
while ((bdPtr->control & ENET_RXDESCRIP_WR_OWN_MASK) == 0) {
|
||||
lpc546xx_enetif_input(lpc_enet->netif);
|
||||
/* rxGenIdx gets updated, gets the next receive buffer descriptor */
|
||||
bdPtr = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
|
||||
}
|
||||
|
||||
/* Set command for rx when it is suspend. */
|
||||
if (suspend)
|
||||
{
|
||||
ENET->DMA_CH[0].DMA_CHX_RXDESC_TAIL_PTR = ENET->DMA_CH[0].DMA_CHX_RXDESC_TAIL_PTR;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Transmit cleanup task
|
||||
*
|
||||
* This task is called when a transmit interrupt occurs and
|
||||
* reclaims the pbuf and descriptor used for the packet once
|
||||
* the packet has been transferred.
|
||||
*
|
||||
* \param[in] lpc_enet pointer to the interface data
|
||||
*/
|
||||
static void lpc546xx_packet_tx(struct lpc_enetdata *lpc_enet)
|
||||
{
|
||||
lpc546xx_tx_reclaim(lpc_enet);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \brief Low level init of the MAC and PHY.
|
||||
*
|
||||
* \param[in] netif Pointer to LWIP netif structure
|
||||
*/
|
||||
static err_t low_level_init(struct netif *netif)
|
||||
{
|
||||
status_t status;
|
||||
uint8_t i;
|
||||
uint32_t refClock;
|
||||
phy_speed_t phy_speed;
|
||||
phy_duplex_t phy_duplex;
|
||||
uint32_t phyAddr = 0;
|
||||
bool link = false;
|
||||
enet_config_t config;
|
||||
uint32_t timeout = 0xFFFU;
|
||||
|
||||
lpc546xx_init_eth_hardware();
|
||||
|
||||
refClock = CLOCK_GetFreq(kCLOCK_CoreSysClk);
|
||||
|
||||
status = PHY_Init(ENET, phyAddr, refClock);
|
||||
if (status != kStatus_Success) {
|
||||
return ERR_IF;
|
||||
}
|
||||
|
||||
ENET_GetDefaultConfig(&config);
|
||||
config.multiqueueCfg = NULL;
|
||||
|
||||
while ((!link) && timeout) {
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &link);
|
||||
if (link) {
|
||||
/* Get link information from PHY */
|
||||
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &phy_speed, &phy_duplex);
|
||||
|
||||
/* Change the MII speed and duplex for actual link status. */
|
||||
config.miiSpeed = (enet_mii_speed_t)phy_speed;
|
||||
config.miiDuplex = (enet_mii_duplex_t)phy_duplex;
|
||||
}
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (!link) {
|
||||
return ERR_CONN;
|
||||
}
|
||||
|
||||
AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rx_desc_start_addr[ENET_RX_RING_LEN], ENET_BUFF_ALIGNMENT);
|
||||
AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t tx_desc_start_addr[ENET_TX_RING_LEN], ENET_BUFF_ALIGNMENT);
|
||||
|
||||
/* Create buffers for each receive BD */
|
||||
for (i = 0; i < ENET_RX_RING_LEN; i++) {
|
||||
rx_buff[i] = pbuf_alloc(PBUF_RAW, ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT), PBUF_RAM);
|
||||
if (NULL == rx_buff[i])
|
||||
return ERR_MEM;
|
||||
|
||||
rx_ptr[i] = (uint32_t)rx_buff[i]->payload;
|
||||
}
|
||||
|
||||
/* prepare the buffer configuration. */
|
||||
enet_buffer_config_t buffCfg = {
|
||||
ENET_RX_RING_LEN,
|
||||
ENET_TX_RING_LEN,
|
||||
&tx_desc_start_addr[0],
|
||||
&tx_desc_start_addr[0],
|
||||
&rx_desc_start_addr[0],
|
||||
&rx_desc_start_addr[ENET_RX_RING_LEN],
|
||||
rx_ptr,
|
||||
ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT),
|
||||
};
|
||||
|
||||
ENET_Init(ENET, &config, netif->hwaddr, refClock);
|
||||
|
||||
/* Enable the tx & rx interrupt. */
|
||||
ENET_EnableInterrupts(ENET, kENET_DmaTx | kENET_DmaRx);
|
||||
|
||||
/* Create the handler. */
|
||||
ENET_CreateHandler(ENET, &g_handle, &config, &buffCfg, ethernet_callback, netif);
|
||||
|
||||
/* Initialize Descriptor. */
|
||||
ENET_DescriptorInit(ENET, &config, &buffCfg);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the ethernet IPv4 packet send function. It calls
|
||||
* etharp_output after checking link status.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \param[in] q Pointer to pbug to send
|
||||
* \param[in] ipaddr IP address
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
#if LWIP_IPV4
|
||||
err_t lpc546xx_etharp_output_ipv4(struct netif *netif, struct pbuf *q,
|
||||
const ip4_addr_t *ipaddr)
|
||||
{
|
||||
/* Only send packet is link is up */
|
||||
if (netif->flags & NETIF_FLAG_LINK_UP)
|
||||
return etharp_output(netif, q, ipaddr);
|
||||
|
||||
return ERR_CONN;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function is the ethernet IPv6 packet send function. It calls
|
||||
* etharp_output after checking link status.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \param[in] q Pointer to pbug to send
|
||||
* \param[in] ipaddr IP address
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
#if LWIP_IPV6
|
||||
err_t lpc546xx_etharp_output_ipv6(struct netif *netif, struct pbuf *q,
|
||||
const ip6_addr_t *ipaddr)
|
||||
{
|
||||
/* Only send packet is link is up */s
|
||||
if (netif->flags & NETIF_FLAG_LINK_UP)
|
||||
return ethip6_output(netif, q, ipaddr);
|
||||
|
||||
return ERR_CONN;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* PHY task: monitor link
|
||||
*******************************************************************************/
|
||||
|
||||
#define STATE_UNKNOWN (-1)
|
||||
|
||||
typedef struct {
|
||||
int connected;
|
||||
phy_speed_t speed;
|
||||
phy_duplex_t duplex;
|
||||
} PHY_STATE;
|
||||
|
||||
int phy_link_status(void) {
|
||||
bool connection_status;
|
||||
uint32_t phyAddr = 0;
|
||||
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
|
||||
return (int)connection_status;
|
||||
}
|
||||
|
||||
static void lpc546xx_phy_task(void *data) {
|
||||
struct netif *netif = (struct netif*)data;
|
||||
static PHY_STATE prev_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN};
|
||||
uint32_t phyAddr = 0;
|
||||
// Get current status
|
||||
PHY_STATE crt_state;
|
||||
bool connection_status;
|
||||
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
|
||||
crt_state.connected = connection_status;
|
||||
// Get the actual PHY link speed
|
||||
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &crt_state.speed, &crt_state.duplex);
|
||||
|
||||
// Compare with previous state
|
||||
if (crt_state.connected != prev_state.connected) {
|
||||
// We're called from lwIP's tcpip thread, so can call link functions directly
|
||||
if (crt_state.connected) {
|
||||
netif_set_link_up(netif);
|
||||
} else {
|
||||
netif_set_link_down(netif);
|
||||
}
|
||||
}
|
||||
|
||||
if (crt_state.speed != prev_state.speed) {
|
||||
uint32_t fes = ENET->MAC_CONFIG;
|
||||
fes &= ~ENET_MAC_CONFIG_FES_MASK;
|
||||
if (prev_state.speed != (phy_speed_t)STATE_UNKNOWN) {
|
||||
fes |= ENET_MAC_CONFIG_FES(!crt_state.speed);
|
||||
} else {
|
||||
fes |= ENET_MAC_CONFIG_FES(crt_state.speed);
|
||||
}
|
||||
|
||||
ENET->MAC_CONFIG = fes;
|
||||
}
|
||||
|
||||
prev_state = crt_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called at the beginning of the program to set up the
|
||||
* network interface.
|
||||
*
|
||||
* This function should be passed as a parameter to netif_add().
|
||||
*
|
||||
* @param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* @return ERR_OK if the loopif is initialized
|
||||
* ERR_MEM if private data couldn't be allocated
|
||||
* any other err_t on error
|
||||
*/
|
||||
err_t eth_arch_enetif_init(struct netif *netif)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
||||
|
||||
lpc_enetdata.netif = netif;
|
||||
|
||||
/* set MAC hardware address */
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
netif->hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *)netif->hwaddr);
|
||||
#endif
|
||||
|
||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||
|
||||
/* maximum transfer unit */
|
||||
netif->mtu = 1500;
|
||||
|
||||
/* device capabilities */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
#ifdef LWIP_IGMP
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
#endif
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#endif
|
||||
|
||||
/* Initialize the hardware */
|
||||
netif->state = &lpc_enetdata;
|
||||
err = low_level_init(netif);
|
||||
if (err != ERR_OK)
|
||||
return err;
|
||||
|
||||
#if LWIP_NETIF_HOSTNAME
|
||||
/* Initialize interface hostname */
|
||||
netif->hostname = "lwiplpc546xx";
|
||||
#endif /* LWIP_NETIF_HOSTNAME */
|
||||
|
||||
netif->name[0] = 'e';
|
||||
netif->name[1] = 'n';
|
||||
|
||||
#if LWIP_IPV4
|
||||
netif->output = lpc546xx_etharp_output_ipv4;
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = lpc546xx_etharp_output_ipv6;
|
||||
#endif
|
||||
|
||||
netif->linkoutput = lpc546xx_low_level_output;
|
||||
|
||||
/* CMSIS-RTOS, start tasks */
|
||||
#if NO_SYS == 0
|
||||
memset(&lpc_enetdata.xTXDCountSem.data, 0, sizeof(lpc_enetdata.xTXDCountSem.data));
|
||||
lpc_enetdata.xTXDCountSem.attr.cb_mem = &lpc_enetdata.xTXDCountSem.data;
|
||||
lpc_enetdata.xTXDCountSem.attr.cb_size = sizeof(lpc_enetdata.xTXDCountSem.data);
|
||||
lpc_enetdata.xTXDCountSem.id = osSemaphoreNew(ENET_TX_RING_LEN, ENET_TX_RING_LEN, &lpc_enetdata.xTXDCountSem.attr);
|
||||
|
||||
LWIP_ASSERT("xTXDCountSem creation error", (lpc_enetdata.xTXDCountSem.id != NULL));
|
||||
|
||||
err = sys_mutex_new(&lpc_enetdata.TXLockMutex);
|
||||
LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK));
|
||||
|
||||
/* Allow the PHY task to detect the initial link state and set up the proper flags */
|
||||
tcpip_callback_with_block(lpc546xx_phy_task, netif, 1);
|
||||
osDelay(10);
|
||||
|
||||
/* Worker thread */
|
||||
lpc_enetdata.thread = sys_thread_new("lpc546xx_emac_thread", lpc546xx_emac_thread, netif->state, THREAD_STACKSIZE, THREAD_PRIORITY)->id;
|
||||
#endif
|
||||
|
||||
/* Active TX/RX. */
|
||||
ENET_StartRxTx(ENET, 1, 1);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void eth_arch_enable_interrupts(void) {
|
||||
|
||||
}
|
||||
|
||||
void eth_arch_disable_interrupts(void) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* LWIP_ARP || LWIP_ETHERNET */
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LWIPOPTS_CONF_H
|
||||
#define LWIPOPTS_CONF_H
|
||||
|
||||
#include "lpc546xx_emac_config.h"
|
||||
|
||||
#define LWIP_TRANSPORT_ETHERNET 1
|
||||
#define ETH_PAD_SIZE 0
|
||||
|
||||
#define MEM_SIZE (ENET_RX_RING_LEN * (ENET_ETH_MAX_FLEN + ENET_BUFF_ALIGNMENT) + ENET_TX_RING_LEN * ENET_ETH_MAX_FLEN)
|
||||
|
||||
#endif
|
|
@ -1,26 +0,0 @@
|
|||
/* Copyright (C) 2015 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LWIPOPTS_CONF_H
|
||||
#define LWIPOPTS_CONF_H
|
||||
|
||||
#define LWIP_TRANSPORT_ETHERNET 1
|
||||
|
||||
#define MEM_SIZE (1600 * 16)
|
||||
|
||||
#endif
|
|
@ -480,6 +480,18 @@ void sys_msleep(u32_t ms) {
|
|||
osDelay(ms);
|
||||
}
|
||||
|
||||
osThreadId_t lwip_tcpip_thread_id = 0;
|
||||
|
||||
void sys_tcpip_thread_set(void)
|
||||
{
|
||||
lwip_tcpip_thread_id = osThreadGetId();
|
||||
}
|
||||
|
||||
bool sys_tcpip_thread_check(void)
|
||||
{
|
||||
return osThreadGetId() == lwip_tcpip_thread_id;
|
||||
}
|
||||
|
||||
// Keep a pool of thread structures
|
||||
static int thread_pool_index = 0;
|
||||
static sys_thread_data_t thread_pool[SYS_THREAD_POOL_N];
|
||||
|
|
|
@ -85,6 +85,9 @@ typedef sys_thread_data_t* sys_thread_t;
|
|||
// === PROTECTION ===
|
||||
typedef int sys_prot_t;
|
||||
|
||||
void sys_tcpip_thread_set(void);
|
||||
bool sys_tcpip_thread_check(void);
|
||||
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
#include "lwip/debug.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip_random.h"
|
||||
|
||||
#include "randLIB.h"
|
||||
|
||||
void lwip_seed_random(void)
|
||||
{
|
||||
randLIB_seed_random();
|
||||
|
||||
}
|
||||
|
||||
void lwip_add_random_seed(uint64_t seed)
|
||||
|
|
|
@ -102,11 +102,18 @@ netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
|
|||
apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
|
||||
if (err == ERR_OK) {
|
||||
return apimsg->err;
|
||||
if (sys_tcpip_thread_check()) {
|
||||
fn(apimsg);
|
||||
return ERR_OK;
|
||||
} else {
|
||||
err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
|
||||
|
||||
if (err == ERR_OK) {
|
||||
return apimsg->err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -903,6 +910,8 @@ netconn_join_leave_group(struct netconn *conn,
|
|||
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
|
||||
|
||||
#if LWIP_DNS
|
||||
#if LWIP_FULL_DNS
|
||||
|
||||
/**
|
||||
* @ingroup netconn_common
|
||||
* Execute a DNS query, only one IP address is returned
|
||||
|
@ -982,6 +991,8 @@ netconn_gethostbyname(const char *name, ip_addr_t *addr)
|
|||
API_VAR_FREE(MEMP_DNS_API_MSG, msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* LWIP_FULL_DNS */
|
||||
#endif /* LWIP_DNS*/
|
||||
|
||||
#if LWIP_NETCONN_SEM_PER_THREAD
|
||||
|
|
|
@ -1892,6 +1892,7 @@ lwip_netconn_do_join_leave_group(void *m)
|
|||
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
|
||||
|
||||
#if LWIP_DNS
|
||||
#if LWIP_FULL_DNS
|
||||
/**
|
||||
* Callback function that is called when DNS name is resolved
|
||||
* (or on timeout). A waiting application thread is waked up by
|
||||
|
@ -1943,5 +1944,6 @@ lwip_netconn_do_gethostbyname(void *arg)
|
|||
}
|
||||
}
|
||||
#endif /* LWIP_DNS */
|
||||
#endif /* LWIP_FULL_DNS */
|
||||
|
||||
#endif /* LWIP_NETCONN */
|
||||
|
|
|
@ -283,16 +283,18 @@ static void dns_init_local(void);
|
|||
static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
|
||||
#endif /* DNS_LOCAL_HOSTLIST */
|
||||
|
||||
|
||||
#if LWIP_FULL_DNS
|
||||
/* forward declarations */
|
||||
static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
|
||||
static void dns_check_entries(void);
|
||||
static void dns_call_found(u8_t idx, ip_addr_t* addr);
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Globals
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#if LWIP_FULL_DNS
|
||||
/* DNS variables */
|
||||
static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
|
||||
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
|
||||
|
@ -301,6 +303,7 @@ static u8_t dns_last_pcb_idx;
|
|||
static u8_t dns_seqno;
|
||||
static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
|
||||
static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
|
||||
#endif
|
||||
static ip_addr_t dns_servers[DNS_MAX_SERVERS];
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
@ -324,6 +327,7 @@ dns_init(void)
|
|||
dns_setserver(0, &dnsserver);
|
||||
#endif /* DNS_SERVER_ADDRESS */
|
||||
|
||||
#if LWIP_FULL_DNS
|
||||
LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
|
||||
sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
|
||||
LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
|
||||
|
@ -351,6 +355,7 @@ dns_init(void)
|
|||
#if DNS_LOCAL_HOSTLIST
|
||||
dns_init_local();
|
||||
#endif
|
||||
#endif /* LWIP_FULL_DNS */
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -397,10 +402,14 @@ dns_getserver(u8_t numdns)
|
|||
void
|
||||
dns_tmr(void)
|
||||
{
|
||||
#if LWIP_FULL_DNS
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
|
||||
dns_check_entries();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LWIP_FULL_DNS
|
||||
|
||||
#if DNS_LOCAL_HOSTLIST
|
||||
static void
|
||||
dns_init_local(void)
|
||||
|
@ -1570,4 +1579,6 @@ dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_call
|
|||
LWIP_DNS_ISMDNS_ARG(is_mdns));
|
||||
}
|
||||
|
||||
#endif /* LWIP_FULL_DNS */
|
||||
|
||||
#endif /* LWIP_DNS */
|
||||
|
|
|
@ -290,8 +290,8 @@ struct netconn {
|
|||
/** @ingroup netconn_common
|
||||
* Create new netconn connection
|
||||
* @param t @ref netconn_type */
|
||||
#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
|
||||
#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
|
||||
#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
|
||||
#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
|
||||
struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
|
||||
netconn_callback callback);
|
||||
err_t netconn_delete(struct netconn *conn);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,52 +0,0 @@
|
|||
/* LWIP implementation of NetworkInterfaceAPI
|
||||
* Copyright (c) 2015 ARM Limited
|
||||
*
|
||||
* 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 LWIP_STACK_H
|
||||
#define LWIP_STACK_H
|
||||
|
||||
#include "nsapi.h"
|
||||
#include "emac_api.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "netif.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Access to lwip through the nsapi - be wary of API changes as external 1st-generation EMAC
|
||||
// drivers attach through these.
|
||||
nsapi_error_t mbed_lwip_init(emac_interface_t *emac);
|
||||
nsapi_error_t mbed_lwip_emac_init(emac_interface_t *emac);
|
||||
nsapi_connection_status_t mbed_lwip_netif_status_check(void);
|
||||
nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask, const char *gw);
|
||||
nsapi_error_t mbed_lwip_bringup_2(bool dhcp, bool ppp, const char *ip, const char *netmask, const char *gw,
|
||||
const nsapi_ip_stack_t stack);
|
||||
typedef void (*mbed_lwip_client_callback)(void *ethernet_if_ptr, nsapi_event_t reason, intptr_t parameter);
|
||||
void mbed_lwip_attach(mbed_lwip_client_callback status_cb, void *status_cb_handle);
|
||||
void mbed_lwip_set_blocking(bool blocking);
|
||||
nsapi_error_t mbed_lwip_bringdown(void);
|
||||
nsapi_error_t mbed_lwip_bringdown_2(bool ppp);
|
||||
const char *mbed_lwip_get_mac_address(void);
|
||||
char *mbed_lwip_get_ip_address(char *buf, nsapi_size_t buflen);
|
||||
char *mbed_lwip_get_netmask(char *buf, nsapi_size_t buflen);
|
||||
char *mbed_lwip_get_gateway(char *buf, nsapi_size_t buflen);
|
||||
|
||||
extern nsapi_stack_t lwip_stack;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,194 @@
|
|||
/* LWIP common helpers
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/api.h"
|
||||
|
||||
#include "LWIPStack.h"
|
||||
|
||||
#include "netsocket/nsapi_types.h"
|
||||
|
||||
/* LWIP error remapping */
|
||||
nsapi_error_t LWIP::err_remap(err_t err) {
|
||||
switch (err) {
|
||||
case ERR_OK:
|
||||
case ERR_CLSD:
|
||||
return 0;
|
||||
case ERR_MEM:
|
||||
case ERR_BUF:
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
case ERR_CONN:
|
||||
case ERR_RST:
|
||||
case ERR_ABRT:
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
case ERR_TIMEOUT:
|
||||
case ERR_RTE:
|
||||
case ERR_WOULDBLOCK:
|
||||
return NSAPI_ERROR_WOULD_BLOCK;
|
||||
case ERR_VAL:
|
||||
case ERR_USE:
|
||||
case ERR_ARG:
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
case ERR_INPROGRESS:
|
||||
return NSAPI_ERROR_IN_PROGRESS;
|
||||
case ERR_ALREADY:
|
||||
return NSAPI_ERROR_ALREADY;
|
||||
case ERR_ISCONN:
|
||||
return NSAPI_ERROR_IS_CONNECTED;
|
||||
default:
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
const ip_addr_t *LWIP::get_ipv4_addr(const struct netif *netif)
|
||||
{
|
||||
if (!netif_is_up(netif)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!ip4_addr_isany(netif_ip4_addr(netif))) {
|
||||
return netif_ip_addr4(netif);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV6
|
||||
const ip_addr_t *LWIP::get_ipv6_addr(const struct netif *netif)
|
||||
{
|
||||
if (!netif_is_up(netif)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
|
||||
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
|
||||
!ip6_addr_islinklocal(netif_ip6_addr(netif, i))) {
|
||||
return netif_ip_addr6(netif, i);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool LWIP::is_local_addr(const ip_addr_t *ip_addr)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
for (netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
if (!netif_is_up(netif)) {
|
||||
continue;
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(ip_addr)) {
|
||||
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
|
||||
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
|
||||
ip6_addr_cmp(netif_ip6_addr(netif, i), ip_2_ip6(ip_addr))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (IP_IS_V4(ip_addr)) {
|
||||
if (!ip4_addr_isany(netif_ip4_addr(netif)) &&
|
||||
ip4_addr_cmp(netif_ip4_addr(netif), ip_2_ip4(ip_addr))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const ip_addr_t *LWIP::get_ip_addr(bool any_addr, const struct netif *netif)
|
||||
{
|
||||
const ip_addr_t *pref_ip_addr = 0;
|
||||
const ip_addr_t *npref_ip_addr = 0;
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
#if IP_VERSION_PREF == PREF_IPV4
|
||||
pref_ip_addr = get_ipv4_addr(netif);
|
||||
npref_ip_addr = get_ipv6_addr(netif);
|
||||
#else
|
||||
pref_ip_addr = get_ipv6_addr(netif);
|
||||
npref_ip_addr = get_ipv4_addr(netif);
|
||||
#endif
|
||||
#elif LWIP_IPV6
|
||||
pref_ip_addr = get_ipv6_addr(netif);
|
||||
#elif LWIP_IPV4
|
||||
pref_ip_addr = get_ipv4_addr(netif);
|
||||
#endif
|
||||
|
||||
if (pref_ip_addr) {
|
||||
return pref_ip_addr;
|
||||
} else if (npref_ip_addr && any_addr) {
|
||||
return npref_ip_addr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void LWIP::arena_init(void)
|
||||
{
|
||||
memset(arena, 0, sizeof(arena));
|
||||
}
|
||||
|
||||
struct LWIP::mbed_lwip_socket *LWIP::arena_alloc()
|
||||
{
|
||||
LWIP &lwip = LWIP::get_instance();
|
||||
|
||||
lwip.adaptation.lock();
|
||||
|
||||
for (int i = 0; i < MEMP_NUM_NETCONN; i++) {
|
||||
if (!arena[i].in_use) {
|
||||
struct mbed_lwip_socket *s = &arena[i];
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->in_use = true;
|
||||
lwip.adaptation.unlock();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
lwip.adaptation.unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LWIP::arena_dealloc(struct mbed_lwip_socket *s)
|
||||
{
|
||||
s->in_use = false;
|
||||
|
||||
while (s->multicast_memberships_count > 0) {
|
||||
uint32_t index = 0;
|
||||
index = next_registered_multicast_member(s, index);
|
||||
|
||||
setsockopt(s, NSAPI_SOCKET, NSAPI_DROP_MEMBERSHIP, &s->multicast_memberships[index],
|
||||
sizeof(s->multicast_memberships[index]));
|
||||
index++;
|
||||
}
|
||||
|
||||
free(s->multicast_memberships);
|
||||
s->multicast_memberships = NULL;
|
||||
}
|
|
@ -19,10 +19,6 @@
|
|||
#ifndef LWIPOPTS_H
|
||||
#define LWIPOPTS_H
|
||||
|
||||
#if MBED_CONF_LWIP_ETHERNET_ENABLED
|
||||
#include "lwipopts_conf.h"
|
||||
#endif
|
||||
|
||||
// Workaround for Linux timeval
|
||||
#if defined (TOOLCHAIN_GCC)
|
||||
#define LWIP_TIMEVAL_PRIVATE 0
|
||||
|
@ -149,7 +145,7 @@
|
|||
|
||||
#ifdef MBED_CONF_LWIP_PBUF_POOL_BUFSIZE
|
||||
#undef PBUF_POOL_BUFSIZE
|
||||
#define PBUF_POOL_BUFSIZE MBED_CONF_LWIP_PBUF_POOL_BUFSIZE
|
||||
#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(MBED_CONF_LWIP_PBUF_POOL_BUFSIZE)
|
||||
#else
|
||||
#ifndef PBUF_POOL_BUFSIZE
|
||||
#if LWIP_IPV6
|
||||
|
@ -217,6 +213,8 @@
|
|||
#endif
|
||||
|
||||
#define LWIP_DNS 1
|
||||
// Only DNS address storage is enabled
|
||||
#define LWIP_FULL_DNS 0
|
||||
#define LWIP_SOCKET 0
|
||||
|
||||
#define SO_REUSE 1
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
"addr-timeout": {
|
||||
"help": "On dual-stack system how long to additionally wait for other stack's address in seconds",
|
||||
"value": 5
|
||||
},
|
||||
},
|
||||
"addr-timeout-mode": {
|
||||
"help": "Address timeout mode; true: wait both stack's addresses; false: wait for preferred stack's address",
|
||||
"value": true
|
||||
},
|
||||
},
|
||||
"ethernet-enabled": {
|
||||
"help": "Enable support for Ethernet interfaces",
|
||||
"value": true
|
||||
|
@ -100,6 +100,15 @@
|
|||
"target_overrides": {
|
||||
"REALTEK_RTL8195AM": {
|
||||
"tcpip-thread-stacksize": 1600
|
||||
},
|
||||
"STM": {
|
||||
"mem-size": 25600
|
||||
},
|
||||
"Freescale": {
|
||||
"mem-size": 36560
|
||||
},
|
||||
"NXP": {
|
||||
"mem-size": 36496
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ extern "C" { // "pppos.h" is missing extern C
|
|||
|
||||
#include "nsapi_ppp.h"
|
||||
#include "ppp_lwip.h"
|
||||
#include "lwip_stack.h"
|
||||
#include "LWIPStack.h"
|
||||
|
||||
namespace mbed {
|
||||
|
||||
|
@ -48,8 +48,10 @@ static nsapi_error_t connect_error_code;
|
|||
|
||||
// Just one interface for now
|
||||
static FileHandle *my_stream;
|
||||
static LWIP::Interface *my_interface;
|
||||
static ppp_pcb *my_ppp_pcb;
|
||||
static bool ppp_active = false;
|
||||
static bool blocking_connect = true;
|
||||
static const char *login;
|
||||
static const char *pwd;
|
||||
static sys_sem_t ppp_close_sem;
|
||||
|
@ -275,7 +277,7 @@ static void stream_cb() {
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" err_t ppp_lwip_connect()
|
||||
extern "C" err_t ppp_lwip_connect(void *pcb)
|
||||
{
|
||||
#if PPP_AUTH_SUPPORT
|
||||
ppp_set_auth(my_ppp_pcb, PPPAUTHTYPE_ANY, login, pwd);
|
||||
|
@ -291,7 +293,7 @@ extern "C" err_t ppp_lwip_connect()
|
|||
return ret;
|
||||
}
|
||||
|
||||
extern "C" err_t ppp_lwip_disconnect()
|
||||
extern "C" err_t ppp_lwip_disconnect(void *pcb)
|
||||
{
|
||||
err_t ret = ppp_close(my_ppp_pcb, 0);
|
||||
if (ret != ERR_OK) {
|
||||
|
@ -309,7 +311,7 @@ extern "C" err_t ppp_lwip_disconnect()
|
|||
return ret;
|
||||
}
|
||||
|
||||
extern "C" nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack)
|
||||
extern "C" nsapi_error_t ppp_lwip_if_init(void *pcb, struct netif *netif, const nsapi_ip_stack_t stack)
|
||||
{
|
||||
if (!prepare_event_queue()) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
|
@ -349,7 +351,7 @@ nsapi_error_t nsapi_ppp_error_code()
|
|||
|
||||
nsapi_error_t nsapi_ppp_set_blocking(bool blocking)
|
||||
{
|
||||
mbed_lwip_set_blocking(blocking);
|
||||
blocking_connect = blocking;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -364,9 +366,20 @@ nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_event_t,
|
|||
login = uname;
|
||||
pwd = password;
|
||||
|
||||
nsapi_error_t retcode;
|
||||
|
||||
if (!my_interface) {
|
||||
LWIP &lwip = LWIP::get_instance();
|
||||
retcode = lwip._add_ppp_interface(stream, true, &my_interface);
|
||||
if (retcode != NSAPI_ERROR_OK) {
|
||||
my_interface = NULL;
|
||||
return retcode;
|
||||
}
|
||||
}
|
||||
|
||||
// mustn't start calling input until after connect -
|
||||
// attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup
|
||||
nsapi_error_t retcode = mbed_lwip_bringup_2(false, true, NULL, NULL, NULL, stack);
|
||||
retcode = my_interface->bringup(false, NULL, NULL, NULL, stack, blocking_connect);
|
||||
|
||||
if (retcode != NSAPI_ERROR_OK && connect_error_code != NSAPI_ERROR_OK) {
|
||||
return connect_error_code;
|
||||
|
@ -377,12 +390,12 @@ nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_event_t,
|
|||
|
||||
nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream)
|
||||
{
|
||||
return mbed_lwip_bringdown_2(true);
|
||||
return my_interface->bringdown();
|
||||
}
|
||||
|
||||
NetworkStack *nsapi_ppp_get_stack()
|
||||
{
|
||||
return nsapi_create_stack(&lwip_stack);
|
||||
return &LWIP::get_instance();
|
||||
}
|
||||
|
||||
const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
|
||||
|
@ -391,7 +404,7 @@ const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
|
|||
|
||||
if (stream == my_stream) {
|
||||
|
||||
if (mbed_lwip_get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
|
||||
if (my_interface->get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
|
||||
return ip_addr;
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +419,7 @@ const char *nsapi_ppp_get_netmask(FileHandle *stream)
|
|||
|
||||
static char netmask[IPADDR_STRLEN_MAX];
|
||||
if (stream == my_stream) {
|
||||
if (mbed_lwip_get_netmask(netmask, IPADDR_STRLEN_MAX)) {
|
||||
if (my_interface->get_netmask(netmask, IPADDR_STRLEN_MAX)) {
|
||||
return netmask;
|
||||
}
|
||||
}
|
||||
|
@ -421,7 +434,7 @@ const char *nsapi_ppp_get_gw_addr(FileHandle *stream)
|
|||
|
||||
static char gwaddr[IPADDR_STRLEN_MAX];
|
||||
if (stream == my_stream) {
|
||||
if (mbed_lwip_get_gateway(gwaddr, IPADDR_STRLEN_MAX)) {
|
||||
if (my_interface->get_netmask(gwaddr, IPADDR_STRLEN_MAX)) {
|
||||
return gwaddr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ extern "C" {
|
|||
*
|
||||
* @return 0 for success and negative error codes for failure
|
||||
*/
|
||||
nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack);
|
||||
nsapi_error_t ppp_lwip_if_init(void *pcb, struct netif *netif, nsapi_ip_stack_t stack);
|
||||
|
||||
/** Connects to a PPP pipe
|
||||
*
|
||||
|
@ -38,7 +38,7 @@ nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack
|
|||
*
|
||||
* @return 0 for success and negative error codes for failure
|
||||
*/
|
||||
err_t ppp_lwip_connect(void);
|
||||
err_t ppp_lwip_connect(void *pcb);
|
||||
|
||||
/** Disconnects from a PPP pipe
|
||||
*
|
||||
|
@ -48,14 +48,14 @@ err_t ppp_lwip_connect(void);
|
|||
*
|
||||
* @return 0 for success and negative error codes for failure
|
||||
*/
|
||||
err_t ppp_lwip_disconnect(void);
|
||||
err_t ppp_lwip_disconnect(void *pcb);
|
||||
#else
|
||||
/**
|
||||
* Stubs in case LWIP PPP is not enabled
|
||||
*/
|
||||
#define ppp_lwip_if_init(netif, stack) NSAPI_ERROR_UNSUPPORTED
|
||||
#define ppp_lwip_connect() ERR_IF
|
||||
#define ppp_lwip_disconnect() ERR_IF
|
||||
#define ppp_lwip_if_init(pcb, netif, stack) NSAPI_ERROR_UNSUPPORTED
|
||||
#define ppp_lwip_connect(pcb) ERR_IF
|
||||
#define ppp_lwip_disconnect(pcb) ERR_IF
|
||||
#endif //LWIP_PPP_API
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -30,22 +30,16 @@ public:
|
|||
*
|
||||
* Must initialize to initialize the mesh on a phy.
|
||||
*/
|
||||
LoWPANNDInterface() : MeshInterfaceNanostack() { }
|
||||
LoWPANNDInterface() { }
|
||||
|
||||
/** Create an initialized MeshInterface
|
||||
/** Create an initialized LoWPANNDInterface
|
||||
*
|
||||
*/
|
||||
LoWPANNDInterface(NanostackRfPhy *phy) : MeshInterfaceNanostack(phy) { }
|
||||
|
||||
nsapi_error_t initialize(NanostackRfPhy *phy);
|
||||
virtual int connect();
|
||||
virtual int disconnect();
|
||||
virtual bool getOwnIpAddress(char *address, int8_t len);
|
||||
bool getRouterIpAddress(char *address, int8_t len);
|
||||
private:
|
||||
mesh_error_t init();
|
||||
mesh_error_t mesh_connect();
|
||||
mesh_error_t mesh_disconnect();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,36 +20,53 @@
|
|||
|
||||
#include "MeshInterface.h"
|
||||
#include "NanostackRfPhy.h"
|
||||
#include "Nanostack.h"
|
||||
#include "mesh_interface_types.h"
|
||||
|
||||
/** Nanostack's network interface class.
|
||||
*
|
||||
* Common class that is shared between mesh interface classes
|
||||
*/
|
||||
class MeshInterfaceNanostack : public MeshInterface {
|
||||
class Nanostack::Interface : public OnboardNetworkStack::Interface, private mbed::NonCopyable<Nanostack::Interface> {
|
||||
public:
|
||||
virtual char *get_ip_address(char *buf, nsapi_size_t buflen);
|
||||
virtual char *get_mac_address(char *buf, nsapi_size_t buflen);
|
||||
virtual char *get_netmask(char *buf, nsapi_size_t buflen);
|
||||
virtual char *get_gateway(char *buf, nsapi_size_t buflen);
|
||||
virtual void attach(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb);
|
||||
virtual nsapi_connection_status_t get_connection_status() const;
|
||||
|
||||
/** Attach phy and initialize the mesh
|
||||
*
|
||||
* Initializes a mesh interface on the given phy. Not needed if
|
||||
* the phy is passed to the mesh's constructor.
|
||||
*
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
nsapi_error_t initialize(NanostackPhy *phy);
|
||||
void get_mac_address(uint8_t *buf) const { interface_phy.get_mac_address(buf); }
|
||||
|
||||
/** Start the interface
|
||||
*
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect() = 0;
|
||||
/**
|
||||
* \brief Callback from C-layer
|
||||
* \param status state of the network
|
||||
* */
|
||||
void network_handler(mesh_connection_status_t status);
|
||||
|
||||
/** Stop the interface
|
||||
*
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
virtual nsapi_error_t disconnect() = 0;
|
||||
int8_t get_interface_id() const { return interface_id; }
|
||||
int8_t get_driver_id() const { return _device_id; }
|
||||
|
||||
private:
|
||||
NanostackPhy &interface_phy;
|
||||
protected:
|
||||
Interface(NanostackPhy &phy);
|
||||
virtual nsapi_error_t register_phy();
|
||||
NanostackPhy &get_phy() const { return interface_phy; }
|
||||
int8_t interface_id;
|
||||
int8_t _device_id;
|
||||
Semaphore connect_semaphore;
|
||||
|
||||
Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb;
|
||||
nsapi_connection_status_t _connect_status;
|
||||
bool _blocking;
|
||||
};
|
||||
|
||||
class Nanostack::MeshInterface : public Nanostack::Interface {
|
||||
protected:
|
||||
MeshInterface(NanostackRfPhy &phy) : Interface(phy) { }
|
||||
NanostackRfPhy &get_phy() const { return static_cast<NanostackRfPhy &>(Interface::get_phy()); }
|
||||
};
|
||||
|
||||
|
||||
class InterfaceNanostack : public virtual NetworkInterface {
|
||||
public:
|
||||
/** Get the internally stored IP address
|
||||
/return IP address of the interface or null if not yet connected
|
||||
*/
|
||||
|
@ -60,18 +77,11 @@ public:
|
|||
*/
|
||||
virtual const char *get_mac_address();
|
||||
|
||||
/** Get the interface ID
|
||||
/return Interface identifier
|
||||
*/
|
||||
int8_t get_interface_id() const { return _network_interface_id; }
|
||||
|
||||
/**
|
||||
* \brief Callback from C-layer
|
||||
* \param status state of the network
|
||||
* */
|
||||
void mesh_network_handler(mesh_connection_status_t status);
|
||||
|
||||
/** Register callback for status reporting
|
||||
*
|
||||
* The specified status callback function will be called on status changes
|
||||
* on the network. The parameters on the callback are the event type and
|
||||
* event-type dependent reason parameter.
|
||||
*
|
||||
* @param status_cb The callback for status changes
|
||||
*/
|
||||
|
@ -90,34 +100,41 @@ public:
|
|||
*/
|
||||
virtual nsapi_error_t set_blocking(bool blocking);
|
||||
|
||||
/** Get the interface ID
|
||||
/return Interface identifier
|
||||
*/
|
||||
int8_t get_interface_id() const { return _interface->get_interface_id(); }
|
||||
|
||||
protected:
|
||||
MeshInterfaceNanostack();
|
||||
MeshInterfaceNanostack(NanostackPhy *phy);
|
||||
nsapi_error_t register_phy();
|
||||
virtual NetworkStack * get_stack(void);
|
||||
InterfaceNanostack();
|
||||
virtual Nanostack *get_stack(void);
|
||||
Nanostack::Interface *get_interface() const { return _interface; }
|
||||
|
||||
/**
|
||||
* \brief Read own global IP address
|
||||
*
|
||||
* \param address is where the IP address will be copied
|
||||
* \param len is the length of the address buffer, must be at least 40 bytes
|
||||
* \return true if address is read successfully, false otherwise
|
||||
*/
|
||||
virtual bool getOwnIpAddress(char *address, int8_t len) = 0;
|
||||
Nanostack::Interface *_interface;
|
||||
|
||||
NanostackPhy *phy;
|
||||
/** Network interface ID */
|
||||
int8_t _network_interface_id;
|
||||
/** Registered device ID */
|
||||
int8_t _device_id;
|
||||
uint8_t _eui64[8];
|
||||
char ip_addr_str[40];
|
||||
char mac_addr_str[24];
|
||||
Semaphore connect_semaphore;
|
||||
|
||||
Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb;
|
||||
nsapi_connection_status_t _connect_status;
|
||||
mbed::Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb;
|
||||
bool _blocking;
|
||||
};
|
||||
|
||||
class MeshInterfaceNanostack : public InterfaceNanostack, public MeshInterface, private mbed::NonCopyable<MeshInterfaceNanostack> {
|
||||
public:
|
||||
|
||||
/** Attach phy and initialize the mesh
|
||||
*
|
||||
* Initializes a mesh interface on the given phy. Not needed if
|
||||
* the phy is passed to the mesh's constructor.
|
||||
*
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
nsapi_error_t initialize(NanostackRfPhy *phy);
|
||||
|
||||
protected:
|
||||
MeshInterfaceNanostack() : _phy(NULL) { }
|
||||
MeshInterfaceNanostack(NanostackRfPhy *phy) : _phy(phy) { }
|
||||
Nanostack::MeshInterface *get_interface() const { return static_cast<Nanostack::MeshInterface *>(_interface); }
|
||||
NanostackRfPhy *_phy;
|
||||
};
|
||||
|
||||
#endif /* MESHINTERFACENANOSTACK_H */
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 NANOSTACKEMACINTERFACE_H
|
||||
#define NANOSTACKEMACINTERFACE_H
|
||||
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
#include "NanostackEthernetPhy.h"
|
||||
|
||||
class NanostackEMACInterface : public Nanostack::Interface {
|
||||
public:
|
||||
|
||||
NanostackEMACInterface(EMAC &emac);
|
||||
};
|
||||
|
||||
#endif // NANOSTACKEMACINTERFACE_H
|
|
@ -17,24 +17,53 @@
|
|||
#ifndef NANOSTACKETHERNETINTERFACE_H
|
||||
#define NANOSTACKETHERNETINTERFACE_H
|
||||
|
||||
#include "EthInterface.h"
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
#include "NanostackEthernetPhy.h"
|
||||
|
||||
class Nanostack::EthernetInterface : public Nanostack::Interface {
|
||||
public:
|
||||
virtual nsapi_error_t bringup(bool dhcp, const char *ip,
|
||||
const char *netmask, const char *gw,
|
||||
nsapi_ip_stack_t stack = DEFAULT_STACK,
|
||||
bool blocking = true);
|
||||
virtual nsapi_error_t bringdown();
|
||||
|
||||
private:
|
||||
friend Nanostack;
|
||||
friend class NanostackEthernetInterface;
|
||||
EthernetInterface(NanostackEthernetPhy &phy) : Interface(phy) {}
|
||||
nsapi_error_t initialize();
|
||||
protected:
|
||||
NanostackEthernetPhy &get_phy() const { return static_cast<NanostackEthernetPhy &>(Interface::get_phy()); }
|
||||
};
|
||||
|
||||
/** Ethernet interface for Nanostack.
|
||||
*
|
||||
* Configure Nanostack to use Ethernet connectivity.
|
||||
*/
|
||||
class NanostackEthernetInterface : public MeshInterfaceNanostack {
|
||||
class NanostackEthernetInterface : public InterfaceNanostack, public EthInterface, private mbed::NonCopyable<NanostackEthernetInterface> {
|
||||
public:
|
||||
|
||||
NanostackEthernetInterface() : MeshInterfaceNanostack() { }
|
||||
NanostackEthernetInterface(NanostackEthernetPhy *phy) : MeshInterfaceNanostack(phy) { }
|
||||
NanostackEthernetInterface() { }
|
||||
//NanostackEthernetInterface(NanostackEthernetPhy *phy);
|
||||
|
||||
nsapi_error_t initialize(NanostackEthernetPhy *phy);
|
||||
virtual int connect();
|
||||
virtual int disconnect();
|
||||
virtual bool getOwnIpAddress(char *address, int8_t len);
|
||||
bool getRouterIpAddress(char *address, int8_t len);
|
||||
|
||||
/** Start the interface
|
||||
*
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect();
|
||||
|
||||
/** Stop the interface
|
||||
*
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
virtual nsapi_error_t disconnect();
|
||||
|
||||
protected:
|
||||
Nanostack::EthernetInterface *get_interface() const { return static_cast<Nanostack::EthernetInterface *>(_interface); }
|
||||
|
||||
};
|
||||
|
||||
#endif // NANOSTACKETHERNETINTERFACE_H
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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 NANOSTACKRFINTERFACE_H
|
||||
#define NANOSTACKRFINTERFACE_H
|
||||
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
#include "NanostackRfPhy.h"
|
||||
|
||||
class NanostackRfInterface : public Nanostack::Interface {
|
||||
public:
|
||||
|
||||
NanostackRfInterface(NanostackRfPhy &phy);
|
||||
};
|
||||
|
||||
#endif // NANOSTACKRFINTERFACE_H
|
|
@ -26,18 +26,16 @@
|
|||
class ThreadInterface : public MeshInterfaceNanostack {
|
||||
public:
|
||||
|
||||
/** Create an uninitialized LoWPANNDInterface
|
||||
/** Create an uninitialized ThreadInterface
|
||||
*
|
||||
* Must initialize to initialize the mesh on a phy.
|
||||
*/
|
||||
ThreadInterface() : MeshInterfaceNanostack() { }
|
||||
ThreadInterface() : user_set_eui64(false) { }
|
||||
|
||||
/** Create an initialized MeshInterface
|
||||
/** Create an initialized ThreadInterface
|
||||
*
|
||||
*/
|
||||
ThreadInterface(NanostackRfPhy *phy) : MeshInterfaceNanostack(phy) { }
|
||||
|
||||
nsapi_error_t initialize(NanostackRfPhy *phy);
|
||||
ThreadInterface(NanostackRfPhy *phy) : MeshInterfaceNanostack(phy), user_set_eui64(false) { }
|
||||
|
||||
/**
|
||||
* \brief Sets the eui64 for the device configuration.
|
||||
|
@ -59,40 +57,11 @@ public:
|
|||
|
||||
virtual int connect();
|
||||
virtual int disconnect();
|
||||
protected:
|
||||
Nanostack::ThreadInterface *get_interface() const;
|
||||
|
||||
private:
|
||||
/*
|
||||
* \brief Initialization of the interface.
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_PARAM when input parameters are illegal (also in case when RF device is already associated to other interface)
|
||||
* \return MESH_ERROR_MEMORY in case of memory error
|
||||
* \return MESH_ERROR_UNKNOWN in other error cases
|
||||
*/
|
||||
mesh_error_t init();
|
||||
/**
|
||||
* \brief Connect interface to the mesh network
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_PARAM in case of illegal parameters.
|
||||
* \return MESH_ERROR_MEMORY in case of memory error.
|
||||
* \return MESH_ERROR_STATE if interface is already connected to network.
|
||||
* \return MESH_ERROR_UNKNOWN in case of unspecified error.
|
||||
* */
|
||||
mesh_error_t mesh_connect();
|
||||
|
||||
/**
|
||||
* \brief Disconnect interface from the mesh network
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_UNKNOWN in case of error.
|
||||
* */
|
||||
mesh_error_t mesh_disconnect();
|
||||
|
||||
/**
|
||||
* \brief Read own global IP address
|
||||
*
|
||||
* \param address is where the IP address will be copied
|
||||
* \param len is the length of the address buffer, must be at least 40 bytes
|
||||
* \return true if address is read successfully, false otherwise
|
||||
*/
|
||||
virtual bool getOwnIpAddress(char *address, int8_t len);
|
||||
bool user_set_eui64;
|
||||
};
|
||||
|
||||
#endif // THREADINTERFACE_H
|
||||
|
|
|
@ -14,20 +14,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
|
||||
#include "include/callback_handler.h"
|
||||
|
||||
|
||||
static MeshInterfaceNanostack *_handler = NULL;
|
||||
static Nanostack::Interface *_handler = NULL;
|
||||
|
||||
void __mesh_handler_c_callback(mesh_connection_status_t state)
|
||||
{
|
||||
|
||||
if (_handler) {
|
||||
_handler->mesh_network_handler(state);
|
||||
_handler->network_handler(state);
|
||||
}
|
||||
}
|
||||
|
||||
void __mesh_handler_set_callback(MeshInterfaceNanostack *handler)
|
||||
void __mesh_handler_set_callback(Nanostack::Interface *handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,49 @@
|
|||
#include "LoWPANNDInterface.h"
|
||||
#include "include/nd_tasklet.h"
|
||||
#include "callback_handler.h"
|
||||
#include "NanostackLockGuard.h"
|
||||
#include "mesh_system.h"
|
||||
#include "randLIB.h"
|
||||
|
||||
#include "ns_trace.h"
|
||||
#define TRACE_GROUP "nslp"
|
||||
|
||||
nsapi_error_t LoWPANNDInterface::initialize(NanostackRfPhy *phy)
|
||||
class Nanostack::LoWPANNDInterface : public Nanostack::MeshInterface
|
||||
{
|
||||
return MeshInterfaceNanostack::initialize(phy);
|
||||
}
|
||||
public:
|
||||
virtual nsapi_error_t bringup(bool dhcp, const char *ip,
|
||||
const char *netmask, const char *gw,
|
||||
nsapi_ip_stack_t stack = IPV6_STACK,
|
||||
bool blocking = true);
|
||||
virtual nsapi_error_t bringdown();
|
||||
virtual char *get_gateway(char *buf, nsapi_size_t buflen);
|
||||
|
||||
friend Nanostack;
|
||||
friend class ::LoWPANNDInterface;
|
||||
private:
|
||||
LoWPANNDInterface(NanostackRfPhy &phy) : MeshInterface(phy) { }
|
||||
mesh_error_t init();
|
||||
mesh_error_t mesh_connect();
|
||||
mesh_error_t mesh_disconnect();
|
||||
};
|
||||
|
||||
int LoWPANNDInterface::connect()
|
||||
{
|
||||
if (!_interface) {
|
||||
_interface = new (nothrow) Nanostack::LoWPANNDInterface(*_phy);
|
||||
if (!_interface) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
_interface->attach(_connection_status_cb);
|
||||
}
|
||||
|
||||
return _interface->bringup(false, NULL, NULL, NULL, IPV6_STACK, _blocking);
|
||||
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::LoWPANNDInterface::bringup(bool dhcp, const char *ip,
|
||||
const char *netmask, const char *gw,
|
||||
nsapi_ip_stack_t stack, bool blocking)
|
||||
{
|
||||
nanostack_lock();
|
||||
|
||||
|
@ -21,6 +52,8 @@ int LoWPANNDInterface::connect()
|
|||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
_blocking = blocking;
|
||||
|
||||
// After the RF is up, we can seed the random from it.
|
||||
randLIB_seed_random();
|
||||
|
||||
|
@ -39,11 +72,13 @@ int LoWPANNDInterface::connect()
|
|||
// Release mutex before blocking
|
||||
nanostack_unlock();
|
||||
|
||||
// wait connection for ever
|
||||
int32_t count = connect_semaphore.wait(osWaitForever);
|
||||
if (blocking) {
|
||||
// wait connection for ever
|
||||
int32_t count = connect_semaphore.wait(osWaitForever);
|
||||
|
||||
if (count <= 0) {
|
||||
return NSAPI_ERROR_DHCP_FAILURE; // sort of...
|
||||
if (count <= 0) {
|
||||
return NSAPI_ERROR_DHCP_FAILURE; // sort of...
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
@ -51,37 +86,40 @@ int LoWPANNDInterface::connect()
|
|||
|
||||
int LoWPANNDInterface::disconnect()
|
||||
{
|
||||
nanostack_lock();
|
||||
return _interface->bringdown();
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::LoWPANNDInterface::bringdown()
|
||||
{
|
||||
NanostackLockGuard lock;
|
||||
|
||||
mesh_error_t status = mesh_disconnect();
|
||||
|
||||
nanostack_unlock();
|
||||
|
||||
return map_mesh_error(status);
|
||||
}
|
||||
|
||||
mesh_error_t LoWPANNDInterface::init()
|
||||
mesh_error_t Nanostack::LoWPANNDInterface::init()
|
||||
{
|
||||
nd_tasklet_init();
|
||||
__mesh_handler_set_callback(this);
|
||||
_network_interface_id = nd_tasklet_network_init(_device_id);
|
||||
interface_id = nd_tasklet_network_init(_device_id);
|
||||
|
||||
if (_network_interface_id == -2) {
|
||||
if (interface_id == -2) {
|
||||
return MESH_ERROR_PARAM;
|
||||
} else if (_network_interface_id == -3) {
|
||||
} else if (interface_id == -3) {
|
||||
return MESH_ERROR_MEMORY;
|
||||
} else if (_network_interface_id < 0) {
|
||||
} else if (interface_id < 0) {
|
||||
return MESH_ERROR_UNKNOWN;
|
||||
}
|
||||
return MESH_ERROR_NONE;
|
||||
}
|
||||
|
||||
mesh_error_t LoWPANNDInterface::mesh_connect()
|
||||
mesh_error_t Nanostack::LoWPANNDInterface::mesh_connect()
|
||||
{
|
||||
int8_t status = -9; // init to unknown error
|
||||
tr_debug("connect()");
|
||||
|
||||
status = nd_tasklet_connect(&__mesh_handler_c_callback, _network_interface_id);
|
||||
status = nd_tasklet_connect(&__mesh_handler_c_callback, interface_id);
|
||||
|
||||
if (status >= 0) {
|
||||
return MESH_ERROR_NONE;
|
||||
|
@ -96,7 +134,7 @@ mesh_error_t LoWPANNDInterface::mesh_connect()
|
|||
}
|
||||
}
|
||||
|
||||
mesh_error_t LoWPANNDInterface::mesh_disconnect()
|
||||
mesh_error_t Nanostack::LoWPANNDInterface::mesh_disconnect()
|
||||
{
|
||||
int8_t status = -1;
|
||||
|
||||
|
@ -109,20 +147,26 @@ mesh_error_t LoWPANNDInterface::mesh_disconnect()
|
|||
return MESH_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bool LoWPANNDInterface::getOwnIpAddress(char *address, int8_t len)
|
||||
char *Nanostack::LoWPANNDInterface::get_gateway(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
tr_debug("getOwnIpAddress()");
|
||||
if (nd_tasklet_get_ip_address(address, len) == 0) {
|
||||
return true;
|
||||
NanostackLockGuard lock;
|
||||
if (nd_tasklet_get_router_ip_address(buf, buflen) == 0) {
|
||||
return buf;
|
||||
}
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool LoWPANNDInterface::getRouterIpAddress(char *address, int8_t len)
|
||||
{
|
||||
tr_debug("getRouterIpAddress()");
|
||||
if (nd_tasklet_get_router_ip_address(address, len) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return _interface->get_gateway(address, len);
|
||||
}
|
||||
|
||||
#define LOWPAN 0x2345
|
||||
#if MBED_CONF_NSAPI_DEFAULT_MESH_TYPE == LOWPAN && DEVICE_802_15_4_PHY
|
||||
MBED_WEAK MeshInterface *MeshInterface::get_default_instance()
|
||||
{
|
||||
static LoWPANNDInterface lowpan(NanostackRfPhy::get_default_instance());
|
||||
|
||||
return lowpan;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -15,56 +15,99 @@
|
|||
*/
|
||||
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
#include "NanostackInterface.h"
|
||||
#include "Nanostack.h"
|
||||
#include "NanostackLockGuard.h"
|
||||
#include "mesh_system.h"
|
||||
#include "net_interface.h"
|
||||
#include "nanostack/net_interface.h"
|
||||
#include "thread_management_if.h"
|
||||
#include "ip6string.h"
|
||||
|
||||
|
||||
MeshInterfaceNanostack::MeshInterfaceNanostack()
|
||||
: phy(NULL), _network_interface_id(-1), _device_id(-1), _eui64(),
|
||||
ip_addr_str(), mac_addr_str(), connect_semaphore(0),
|
||||
_connection_status_cb(NULL), _connect_status(NSAPI_STATUS_DISCONNECTED),
|
||||
_blocking(true)
|
||||
char *Nanostack::Interface::get_ip_address(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
// Nothing to do
|
||||
NanostackLockGuard lock;
|
||||
uint8_t binary_ipv6[16];
|
||||
|
||||
if (buflen >= 40 && arm_net_address_get(interface_id, ADDR_IPV6_GP, binary_ipv6) == 0) {
|
||||
ip6tos(binary_ipv6, buf);
|
||||
return buf;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MeshInterfaceNanostack::MeshInterfaceNanostack(NanostackPhy *phy)
|
||||
: phy(phy), _network_interface_id(-1), _device_id(-1), connect_semaphore(0)
|
||||
char *Nanostack::Interface::get_mac_address(char *buf, nsapi_size_t buflen)
|
||||
{
|
||||
// Nothing to do
|
||||
NanostackLockGuard lock;
|
||||
link_layer_address_s addr;
|
||||
if (buflen >= 24 && arm_nwk_mac_address_read(interface_id, &addr) == 0) {
|
||||
snprintf(buf, buflen, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", addr.mac_long[0], addr.mac_long[1], addr.mac_long[2], addr.mac_long[3], addr.mac_long[4], addr.mac_long[5], addr.mac_long[6], addr.mac_long[7]);
|
||||
return buf;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t MeshInterfaceNanostack::initialize(NanostackPhy *phy)
|
||||
char *Nanostack::Interface::get_netmask(char *, nsapi_size_t)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *Nanostack::Interface::get_gateway(char *, nsapi_size_t)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nsapi_connection_status_t Nanostack::Interface::get_connection_status() const
|
||||
{
|
||||
return _connect_status;
|
||||
}
|
||||
|
||||
void Nanostack::Interface::attach(
|
||||
mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
_connection_status_cb = status_cb;
|
||||
}
|
||||
|
||||
Nanostack::Interface::Interface(NanostackPhy &phy) : interface_phy(phy), interface_id(-1), _device_id(-1),
|
||||
_connect_status(NSAPI_STATUS_DISCONNECTED), _blocking(true)
|
||||
{
|
||||
mesh_system_init();
|
||||
if (this->phy != NULL) {
|
||||
error("Phy already set");
|
||||
}
|
||||
this->phy = phy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MeshInterfaceNanostack::mesh_network_handler(mesh_connection_status_t status)
|
||||
{
|
||||
nanostack_lock();
|
||||
|
||||
InterfaceNanostack::InterfaceNanostack()
|
||||
: _interface(NULL),
|
||||
ip_addr_str(), mac_addr_str(), _blocking(true)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
nsapi_error_t MeshInterfaceNanostack::initialize(NanostackRfPhy *phy)
|
||||
{
|
||||
if (_phy) {
|
||||
error("Phy already set");
|
||||
return NSAPI_ERROR_IS_CONNECTED;
|
||||
}
|
||||
_phy = phy;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
void Nanostack::Interface::network_handler(mesh_connection_status_t status)
|
||||
{
|
||||
if ((status == MESH_CONNECTED || status == MESH_CONNECTED_LOCAL ||
|
||||
status == MESH_CONNECTED_GLOBAL) && _blocking) {
|
||||
connect_semaphore.release();
|
||||
}
|
||||
|
||||
nanostack_unlock();
|
||||
|
||||
|
||||
if (status == MESH_CONNECTED) {
|
||||
uint8_t temp_ipv6_global[16];
|
||||
uint8_t temp_ipv6_local[16];
|
||||
if (arm_net_address_get(_network_interface_id, ADDR_IPV6_LL, temp_ipv6_local) == 0) {
|
||||
if (arm_net_address_get(interface_id, ADDR_IPV6_LL, temp_ipv6_local) == 0) {
|
||||
_connect_status = NSAPI_STATUS_LOCAL_UP;
|
||||
}
|
||||
if (arm_net_address_get(_network_interface_id, ADDR_IPV6_GP, temp_ipv6_global) == 0
|
||||
if (arm_net_address_get(interface_id, ADDR_IPV6_GP, temp_ipv6_global) == 0
|
||||
&& (memcmp(temp_ipv6_global, temp_ipv6_local, 16) != 0)) {
|
||||
_connect_status = NSAPI_STATUS_GLOBAL_UP;
|
||||
}
|
||||
|
@ -83,66 +126,66 @@ void MeshInterfaceNanostack::mesh_network_handler(mesh_connection_status_t statu
|
|||
}
|
||||
}
|
||||
|
||||
nsapi_error_t MeshInterfaceNanostack::register_phy()
|
||||
nsapi_error_t Nanostack::Interface::register_phy()
|
||||
{
|
||||
nanostack_lock();
|
||||
NanostackLockGuard lock;
|
||||
|
||||
_device_id = phy->phy_register();
|
||||
_device_id = interface_phy.phy_register();
|
||||
if (_device_id < 0) {
|
||||
nanostack_unlock();
|
||||
return -1;
|
||||
}
|
||||
// Read mac address after registering the device.
|
||||
const uint8_t empty_eui64[8] = {0,0,0,0,0,0,0,0};
|
||||
// if not set by application then read from rf driver
|
||||
if(!memcmp(_eui64, empty_eui64,8)) {
|
||||
phy->get_mac_address(_eui64);
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
sprintf(mac_addr_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", _eui64[0], _eui64[1], _eui64[2], _eui64[3], _eui64[4], _eui64[5], _eui64[6], _eui64[7]);
|
||||
|
||||
nanostack_unlock();
|
||||
|
||||
return 0;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
NetworkStack * MeshInterfaceNanostack::get_stack()
|
||||
Nanostack *InterfaceNanostack::get_stack()
|
||||
{
|
||||
return NanostackInterface::get_stack();
|
||||
return &Nanostack::get_instance();
|
||||
}
|
||||
|
||||
const char *MeshInterfaceNanostack::get_ip_address()
|
||||
const char *InterfaceNanostack::get_ip_address()
|
||||
{
|
||||
nanostack_lock();
|
||||
|
||||
const char *ret = NULL;
|
||||
if (getOwnIpAddress(ip_addr_str, sizeof ip_addr_str)) {
|
||||
ret = ip_addr_str;
|
||||
if (_interface->get_ip_address(ip_addr_str, sizeof(ip_addr_str))) {
|
||||
return ip_addr_str;
|
||||
}
|
||||
|
||||
nanostack_unlock();
|
||||
|
||||
return ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *MeshInterfaceNanostack::get_mac_address()
|
||||
const char *InterfaceNanostack::get_mac_address()
|
||||
{
|
||||
return mac_addr_str;
|
||||
if (_interface->get_mac_address(mac_addr_str, sizeof(mac_addr_str))) {
|
||||
return mac_addr_str;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nsapi_connection_status_t MeshInterfaceNanostack::get_connection_status() const
|
||||
nsapi_connection_status_t InterfaceNanostack::get_connection_status() const
|
||||
{
|
||||
return _connect_status;
|
||||
if (_interface) {
|
||||
return _interface->get_connection_status();
|
||||
} else {
|
||||
return NSAPI_STATUS_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshInterfaceNanostack::attach(
|
||||
Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
void InterfaceNanostack::attach(
|
||||
mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
_connection_status_cb = status_cb;
|
||||
if (_interface) {
|
||||
_interface->attach(status_cb);
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t MeshInterfaceNanostack::set_blocking(bool blocking)
|
||||
nsapi_error_t InterfaceNanostack::set_blocking(bool blocking)
|
||||
{
|
||||
_blocking = blocking;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
#if !DEVICE_802_15_4_PHY
|
||||
MBED_WEAK MeshInterface *MeshInterface::get_target_default_instance()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2017 ARM Limited. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "Nanostack.h"
|
||||
#include "NanostackEthernetInterface.h"
|
||||
#include "NanostackEthernetPhy.h"
|
||||
#include "EMAC.h"
|
||||
#include "nsdynmemLIB.h"
|
||||
#include "arm_hal_phy.h"
|
||||
|
||||
class EMACPhy : public NanostackEthernetPhy
|
||||
{
|
||||
public:
|
||||
EMACPhy(NanostackMemoryManager &mem, EMAC &m);
|
||||
virtual int8_t phy_register();
|
||||
virtual void get_mac_address(uint8_t *mac);
|
||||
virtual void set_mac_address(uint8_t *mac);
|
||||
|
||||
int8_t address_write(phy_address_type_e , uint8_t *);
|
||||
int8_t tx(uint8_t *data_ptr, uint16_t data_len, uint8_t tx_handle,data_protocol_e data_flow);
|
||||
|
||||
void emac_phy_rx(emac_mem_buf_t *mem);
|
||||
|
||||
private:
|
||||
NanostackMemoryManager &memory_manager;
|
||||
EMAC &emac;
|
||||
uint8_t mac_addr[6];
|
||||
int8_t device_id;
|
||||
phy_device_driver_s phy;
|
||||
};
|
||||
|
||||
// GAH! no handles on the callback. Force a single interface
|
||||
static EMACPhy *single_phy;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
static int8_t emac_phy_address_write(phy_address_type_e address_type, uint8_t *address_ptr)
|
||||
{
|
||||
return single_phy->address_write(address_type, address_ptr);
|
||||
}
|
||||
|
||||
static int8_t emac_phy_interface_state_control(phy_interface_state_e, uint8_t)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int8_t emac_phy_tx(uint8_t *data_ptr, uint16_t data_len, uint8_t tx_handle,data_protocol_e data_flow)
|
||||
{
|
||||
return single_phy->tx(data_ptr, data_len, tx_handle, data_flow);
|
||||
}
|
||||
|
||||
EMACPhy::EMACPhy(NanostackMemoryManager &mem, EMAC &m) : memory_manager(mem), emac(m), device_id(-1)
|
||||
{
|
||||
/* Same default address logic as lwIP glue uses */
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
mac_addr[0] = MBED_MAC_ADDR_0;
|
||||
mac_addr[1] = MBED_MAC_ADDR_1;
|
||||
mac_addr[2] = MBED_MAC_ADDR_2;
|
||||
mac_addr[3] = MBED_MAC_ADDR_3;
|
||||
mac_addr[4] = MBED_MAC_ADDR_4;
|
||||
mac_addr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *) mac_addr);
|
||||
#endif
|
||||
/* We have a default MAC address, so do don't force them to supply one */
|
||||
/* They may or may not update hwaddr with their address */
|
||||
emac.get_hwaddr(mac_addr);
|
||||
}
|
||||
|
||||
|
||||
void EMACPhy::emac_phy_rx(emac_mem_buf_t *mem)
|
||||
{
|
||||
const uint8_t *ptr = NULL;
|
||||
uint8_t *tmpbuf = NULL;
|
||||
uint32_t total_len;
|
||||
|
||||
if (memory_manager.get_next(mem) == NULL) {
|
||||
// Easy contiguous case
|
||||
ptr = static_cast<const uint8_t*>(memory_manager.get_ptr(mem));
|
||||
total_len = memory_manager.get_len(mem);
|
||||
} else {
|
||||
// Nanostack can't accept chunked data - make temporary contiguous copy
|
||||
total_len = memory_manager.get_total_len(mem);
|
||||
ptr = tmpbuf = static_cast<uint8_t *>(ns_dyn_mem_temporary_alloc(total_len));
|
||||
if (tmpbuf) {
|
||||
memory_manager.copy_from_buf(tmpbuf, total_len, mem);
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr) {
|
||||
phy.phy_rx_cb(ptr, total_len, 0xff, 0, device_id);
|
||||
}
|
||||
ns_dyn_mem_free(tmpbuf);
|
||||
memory_manager.free(mem);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
int8_t EMACPhy::address_write(phy_address_type_e address_type, uint8_t *address_ptr)
|
||||
{
|
||||
if (address_type != PHY_MAC_48BIT) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(mac_addr, address_ptr, 6);
|
||||
emac.set_hwaddr(address_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t EMACPhy::tx(uint8_t *data_ptr, uint16_t data_len, uint8_t tx_handle,data_protocol_e data_flow)
|
||||
{
|
||||
emac_mem_buf_t *mem = memory_manager.alloc_pool(data_len, 0);
|
||||
if (!mem) {
|
||||
return -1;
|
||||
}
|
||||
memory_manager.copy_to_buf(mem, data_ptr, data_len);
|
||||
|
||||
// They take ownership - their responsibility to free
|
||||
emac.link_out(mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t EMACPhy::phy_register()
|
||||
{
|
||||
if (device_id < 0) {
|
||||
|
||||
emac.set_memory_manager(memory_manager);
|
||||
emac.set_link_input_cb(callback(this, &EMACPhy::emac_phy_rx));
|
||||
|
||||
if (!emac.power_up()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
phy.phy_MTU = emac.get_mtu_size();
|
||||
/* Set the address - this could be either board default, what they
|
||||
* told us with EMAC::get_mac_address, or something manually specified
|
||||
* with EMACPhy::set_mac_address
|
||||
*/
|
||||
emac.set_hwaddr(mac_addr);
|
||||
|
||||
emac.set_all_multicast(true);
|
||||
|
||||
phy.PHY_MAC = mac_addr;
|
||||
phy.address_write = emac_phy_address_write;
|
||||
phy.driver_description = const_cast<char*>("ETH");
|
||||
phy.link_type = PHY_LINK_ETHERNET_TYPE;
|
||||
phy.phy_MTU = 0;
|
||||
phy.phy_header_length = 0;
|
||||
phy.phy_tail_length = 0;
|
||||
phy.state_control = emac_phy_interface_state_control;
|
||||
phy.tx = emac_phy_tx;
|
||||
phy.phy_rx_cb = NULL;
|
||||
phy.phy_tx_done_cb = NULL;
|
||||
device_id = arm_net_phy_register(&phy);
|
||||
// driver_readiness_status_callback = driver_status_cb;
|
||||
|
||||
if (device_id < 0){
|
||||
//tr_error("Ethernet Driver failed to register with Stack. RetCode=%i", eth_driver_enabled);
|
||||
//driver_readiness_status_callback(0, eth_interface_id);
|
||||
emac.power_down();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return device_id;
|
||||
}
|
||||
|
||||
void EMACPhy::get_mac_address(uint8_t *mac)
|
||||
{
|
||||
memcpy(mac, mac_addr, sizeof mac_addr);
|
||||
}
|
||||
|
||||
void EMACPhy::set_mac_address(uint8_t *mac)
|
||||
{
|
||||
memcpy(mac_addr, mac, sizeof mac_addr);
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr)
|
||||
{
|
||||
if (single_phy) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
single_phy = new (nothrow) EMACPhy(this->memory_manager, emac);
|
||||
if (!single_phy) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (mac_addr) {
|
||||
single_phy->set_mac_address(const_cast<uint8_t*>(mac_addr));
|
||||
}
|
||||
|
||||
Nanostack::EthernetInterface *interface;
|
||||
|
||||
interface = new (nothrow) Nanostack::EthernetInterface(*single_phy);
|
||||
if (!interface) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
nsapi_error_t err = interface->initialize();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*interface_out = interface;
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out)
|
||||
{
|
||||
Nanostack::EthernetInterface *interface;
|
||||
nsapi_error_t err = add_ethernet_interface(emac, default_if, &interface);
|
||||
*interface_out = interface;
|
||||
return err;
|
||||
}
|
|
@ -18,12 +18,8 @@
|
|||
#include "callback_handler.h"
|
||||
#include "enet_tasklet.h"
|
||||
|
||||
nsapi_error_t NanostackEthernetInterface::initialize(NanostackEthernetPhy *phy)
|
||||
nsapi_error_t Nanostack::EthernetInterface::initialize()
|
||||
{
|
||||
nsapi_error_t err = MeshInterfaceNanostack::initialize(phy);
|
||||
if (err != NSAPI_ERROR_OK)
|
||||
return err;
|
||||
|
||||
nanostack_lock();
|
||||
|
||||
if (register_phy() < 0) {
|
||||
|
@ -31,54 +27,85 @@ nsapi_error_t NanostackEthernetInterface::initialize(NanostackEthernetPhy *phy)
|
|||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
enet_tasklet_init();
|
||||
__mesh_handler_set_callback(this);
|
||||
_network_interface_id = enet_tasklet_network_init(_device_id);
|
||||
|
||||
nanostack_unlock();
|
||||
|
||||
if (_network_interface_id < 0)
|
||||
return MESH_ERROR_UNKNOWN;
|
||||
else
|
||||
return MESH_ERROR_NONE;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
int NanostackEthernetInterface::connect()
|
||||
nsapi_error_t NanostackEthernetInterface::initialize(NanostackEthernetPhy *phy)
|
||||
{
|
||||
if (_interface) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
_interface = new (nothrow) Nanostack::EthernetInterface(*phy);
|
||||
if (!_interface) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
return get_interface()->initialize();
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::EthernetInterface::bringup(bool dhcp, const char *ip,
|
||||
const char *netmask, const char *gw,
|
||||
nsapi_ip_stack_t stack, bool blocking)
|
||||
{
|
||||
if (stack == IPV4_STACK) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
nanostack_lock();
|
||||
int8_t status = enet_tasklet_connect(&__mesh_handler_c_callback, _network_interface_id);
|
||||
_blocking = blocking;
|
||||
if (interface_id < 0) {
|
||||
enet_tasklet_init();
|
||||
__mesh_handler_set_callback(this);
|
||||
interface_id = enet_tasklet_network_init(_device_id);
|
||||
}
|
||||
int8_t status = -1;
|
||||
if (interface_id >= 0) {
|
||||
status = enet_tasklet_connect(&__mesh_handler_c_callback, interface_id);
|
||||
}
|
||||
nanostack_unlock();
|
||||
|
||||
if (status == -1) {
|
||||
return MESH_ERROR_PARAM;
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
} else if (status == -2) {
|
||||
return MESH_ERROR_MEMORY;
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
} else if (status == -3) {
|
||||
return MESH_ERROR_STATE;
|
||||
return NSAPI_ERROR_ALREADY;
|
||||
} else if (status != 0) {
|
||||
return MESH_ERROR_UNKNOWN;
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int32_t count = connect_semaphore.wait(30000);
|
||||
if (blocking) {
|
||||
int32_t count = connect_semaphore.wait(30000);
|
||||
|
||||
if (count <= 0) {
|
||||
return NSAPI_ERROR_DHCP_FAILURE; // sort of...
|
||||
if (count <= 0) {
|
||||
return NSAPI_ERROR_DHCP_FAILURE; // sort of...
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NanostackEthernetInterface::disconnect()
|
||||
int NanostackEthernetInterface::connect()
|
||||
{
|
||||
if (!_interface) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
return _interface->bringup(false, NULL, NULL, NULL, IPV6_STACK, _blocking);
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::EthernetInterface::bringdown()
|
||||
{
|
||||
enet_tasklet_disconnect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool NanostackEthernetInterface::getOwnIpAddress(char *address, int8_t len)
|
||||
{
|
||||
return enet_tasklet_get_ip_address(address, len)?false:true;
|
||||
}
|
||||
|
||||
bool NanostackEthernetInterface::getRouterIpAddress(char *address, int8_t len)
|
||||
int NanostackEthernetInterface::disconnect()
|
||||
{
|
||||
return false;
|
||||
if (!_interface) {
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
return _interface->bringdown();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "nsdynmemLIB.h"
|
||||
#include <string.h>
|
||||
#include "mbed_assert.h"
|
||||
#include "NanostackMemoryManager.h"
|
||||
|
||||
struct ns_stack_mem_t
|
||||
{
|
||||
ns_stack_mem_t *next;
|
||||
void *payload;
|
||||
uint32_t len;
|
||||
uint8_t mem[];
|
||||
};
|
||||
|
||||
emac_mem_buf_t *NanostackMemoryManager::alloc_heap(uint32_t size, uint32_t align)
|
||||
{
|
||||
ns_stack_mem_t *buf = static_cast<ns_stack_mem_t *>(ns_dyn_mem_temporary_alloc(sizeof(ns_stack_mem_t) + size + align));
|
||||
if (buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf->next = NULL;
|
||||
buf->payload = buf->mem;
|
||||
buf->len = size;
|
||||
|
||||
if (align) {
|
||||
uint32_t remainder = reinterpret_cast<uint32_t>(buf->payload) % align;
|
||||
if (remainder) {
|
||||
uint32_t offset = align - remainder;
|
||||
if (offset >= align) {
|
||||
offset = align;
|
||||
}
|
||||
|
||||
buf->payload = static_cast<char *>(buf->payload) + offset;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<emac_mem_buf_t *>(buf);
|
||||
}
|
||||
|
||||
emac_mem_buf_t *NanostackMemoryManager::alloc_pool(uint32_t size, uint32_t align)
|
||||
{
|
||||
return alloc_heap(size, align);
|
||||
}
|
||||
|
||||
uint32_t NanostackMemoryManager::get_pool_alloc_unit(uint32_t align) const
|
||||
{
|
||||
return 1536; // arbitrary nicely-aligned number big enough for Ethernet
|
||||
}
|
||||
|
||||
void NanostackMemoryManager::free(emac_mem_buf_t *mem)
|
||||
{
|
||||
ns_dyn_mem_free(mem);
|
||||
}
|
||||
|
||||
uint32_t NanostackMemoryManager::get_total_len(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
const ns_stack_mem_t *mem = static_cast<const ns_stack_mem_t *>(buf);
|
||||
uint32_t total = 0;
|
||||
|
||||
while (mem) {
|
||||
total += mem->len;
|
||||
mem = mem->next;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void NanostackMemoryManager::copy(emac_mem_buf_t *to, const emac_mem_buf_t *from)
|
||||
{
|
||||
ns_stack_mem_t *to_mem = static_cast<ns_stack_mem_t *>(to);
|
||||
const ns_stack_mem_t *from_mem = static_cast<const ns_stack_mem_t *>(from);
|
||||
MBED_ASSERT(get_total_len(to) >= get_total_len(from));
|
||||
|
||||
uint32_t to_offset = 0;
|
||||
uint32_t from_offset = 0;
|
||||
while (from_mem) {
|
||||
uint32_t to_avail = to_mem->len - to_offset;
|
||||
uint32_t from_avail = from_mem->len - from_offset;
|
||||
uint32_t chunk = to_avail < from_avail ? to_avail : from_avail;
|
||||
uint8_t *to_ptr = static_cast<uint8_t *>(to_mem->payload) + to_offset;
|
||||
const uint8_t *from_ptr = static_cast<const uint8_t *>(from_mem->payload) + from_offset;
|
||||
memcpy(to_ptr, from_ptr, chunk);
|
||||
to_offset += chunk;
|
||||
if (to_offset == to_mem->len) {
|
||||
to_mem = to_mem->next;
|
||||
to_offset = 0;
|
||||
}
|
||||
from_offset += chunk;
|
||||
if (from_offset == from_mem->len) {
|
||||
from_mem = from_mem->next;
|
||||
from_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NanostackMemoryManager::cat(emac_mem_buf_t *to_buf, emac_mem_buf_t *cat_buf)
|
||||
{
|
||||
ns_stack_mem_t *to_mem = static_cast<ns_stack_mem_t *>(to_buf);
|
||||
ns_stack_mem_t *cat_mem = static_cast<ns_stack_mem_t *>(cat_buf);
|
||||
|
||||
while (to_mem->next) {
|
||||
to_mem = to_mem->next;
|
||||
}
|
||||
|
||||
to_mem->next = cat_mem;
|
||||
}
|
||||
|
||||
emac_mem_buf_t *NanostackMemoryManager::get_next(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
return static_cast<const ns_stack_mem_t *>(buf)->next;
|
||||
}
|
||||
|
||||
void *NanostackMemoryManager::get_ptr(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
return static_cast<const ns_stack_mem_t *>(buf)->payload;
|
||||
}
|
||||
|
||||
uint32_t NanostackMemoryManager::get_len(const emac_mem_buf_t *buf) const
|
||||
{
|
||||
return static_cast<const ns_stack_mem_t *>(buf)->len;
|
||||
}
|
||||
|
||||
void NanostackMemoryManager::set_len(emac_mem_buf_t *buf, uint32_t len)
|
||||
{
|
||||
ns_stack_mem_t *mem = static_cast<ns_stack_mem_t *>(buf);
|
||||
|
||||
mem->len = len;
|
||||
}
|
|
@ -7,12 +7,86 @@
|
|||
#include "ns_trace.h"
|
||||
#define TRACE_GROUP "nsth"
|
||||
|
||||
nsapi_error_t ThreadInterface::initialize(NanostackRfPhy *phy)
|
||||
class Nanostack::ThreadInterface : public Nanostack::MeshInterface
|
||||
{
|
||||
return MeshInterfaceNanostack::initialize(phy);
|
||||
public:
|
||||
virtual nsapi_error_t bringup(bool dhcp, const char *ip,
|
||||
const char *netmask, const char *gw,
|
||||
nsapi_ip_stack_t stack = IPV6_STACK,
|
||||
bool blocking = true);
|
||||
virtual nsapi_error_t bringdown();
|
||||
friend Nanostack;
|
||||
friend class ::ThreadInterface;
|
||||
private:
|
||||
ThreadInterface(NanostackRfPhy &phy) : MeshInterface(phy), user_eui64_set(false) { }
|
||||
|
||||
/*
|
||||
* \brief Initialization of the interface.
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_PARAM when input parameters are illegal (also in case when RF device is already associated to other interface)
|
||||
* \return MESH_ERROR_MEMORY in case of memory error
|
||||
* \return MESH_ERROR_UNKNOWN in other error cases
|
||||
*/
|
||||
mesh_error_t init();
|
||||
/**
|
||||
* \brief Connect interface to the mesh network
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_PARAM in case of illegal parameters.
|
||||
* \return MESH_ERROR_MEMORY in case of memory error.
|
||||
* \return MESH_ERROR_STATE if interface is already connected to network.
|
||||
* \return MESH_ERROR_UNKNOWN in case of unspecified error.
|
||||
* */
|
||||
mesh_error_t mesh_connect();
|
||||
|
||||
/**
|
||||
* \brief Disconnect interface from the mesh network
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_UNKNOWN in case of error.
|
||||
* */
|
||||
mesh_error_t mesh_disconnect();
|
||||
|
||||
/**
|
||||
* \brief Sets the eui64 for the device configuration.
|
||||
* By default this value is read from the radio driver.
|
||||
* The value must be set before calling the connect function.
|
||||
* */
|
||||
void device_eui64_set(const uint8_t *eui64);
|
||||
|
||||
/**
|
||||
* \brief sets the PSKd for the device configuration.
|
||||
* The default value is overwritten, which is defined in the mbed_lib.json file in the mesh-api
|
||||
* The value must be set before calling the connect function.
|
||||
* \return MESH_ERROR_NONE on success.
|
||||
* \return MESH_ERROR_PARAM in case of illegal parameters.
|
||||
* \return MESH_ERROR_MEMORY in case of memory error.
|
||||
* */
|
||||
|
||||
mesh_error_t device_pskd_set(const char *pskd);
|
||||
|
||||
bool user_eui64_set;
|
||||
};
|
||||
|
||||
Nanostack::ThreadInterface *ThreadInterface::get_interface() const
|
||||
{
|
||||
return static_cast<Nanostack::ThreadInterface*>(_interface);
|
||||
}
|
||||
|
||||
int ThreadInterface::connect()
|
||||
{
|
||||
if (!_interface) {
|
||||
_interface = new (nothrow) Nanostack::ThreadInterface(*_phy);
|
||||
if (!_interface) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
_interface->attach(_connection_status_cb);
|
||||
}
|
||||
|
||||
return _interface->bringup(false, NULL, NULL, NULL, IPV6_STACK, _blocking);
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::ThreadInterface::bringup(bool dhcp, const char *ip,
|
||||
const char *netmask, const char *gw,
|
||||
nsapi_ip_stack_t stack, bool blocking)
|
||||
{
|
||||
if (_connect_status == NSAPI_STATUS_GLOBAL_UP || _connect_status == NSAPI_STATUS_LOCAL_UP) {
|
||||
return NSAPI_ERROR_IS_CONNECTED;
|
||||
|
@ -20,12 +94,17 @@ int ThreadInterface::connect()
|
|||
return NSAPI_ERROR_ALREADY;
|
||||
}
|
||||
|
||||
nanostack_lock();
|
||||
if (stack == IPV4_STACK) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (register_phy() < 0) {
|
||||
nanostack_unlock();
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
nanostack_lock();
|
||||
|
||||
_blocking = blocking;
|
||||
|
||||
// After the RF is up, we can seed the random from it.
|
||||
randLIB_seed_random();
|
||||
|
||||
|
@ -63,6 +142,11 @@ int ThreadInterface::connect()
|
|||
}
|
||||
|
||||
int ThreadInterface::disconnect()
|
||||
{
|
||||
return _interface->bringdown();
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::ThreadInterface::bringdown()
|
||||
{
|
||||
nanostack_lock();
|
||||
|
||||
|
@ -73,38 +157,33 @@ int ThreadInterface::disconnect()
|
|||
return map_mesh_error(status);
|
||||
}
|
||||
|
||||
mesh_error_t ThreadInterface::init()
|
||||
mesh_error_t Nanostack::ThreadInterface::init()
|
||||
{
|
||||
thread_tasklet_init();
|
||||
__mesh_handler_set_callback(this);
|
||||
thread_tasklet_device_eui64_set(_eui64);
|
||||
_network_interface_id = thread_tasklet_network_init(_device_id);
|
||||
if (!user_eui64_set) {
|
||||
uint8_t eui64[8];
|
||||
get_phy().get_mac_address(eui64);
|
||||
thread_tasklet_device_eui64_set(eui64);
|
||||
}
|
||||
interface_id = thread_tasklet_network_init(_device_id);
|
||||
|
||||
if (_network_interface_id == -2) {
|
||||
if (interface_id == -2) {
|
||||
return MESH_ERROR_PARAM;
|
||||
} else if (_network_interface_id == -3) {
|
||||
} else if (interface_id == -3) {
|
||||
return MESH_ERROR_MEMORY;
|
||||
} else if (_network_interface_id < 0) {
|
||||
} else if (interface_id < 0) {
|
||||
return MESH_ERROR_UNKNOWN;
|
||||
}
|
||||
return MESH_ERROR_NONE;
|
||||
}
|
||||
|
||||
bool ThreadInterface::getOwnIpAddress(char *address, int8_t len)
|
||||
{
|
||||
tr_debug("getOwnIpAddress()");
|
||||
if (thread_tasklet_get_ip_address(address, len) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh_error_t ThreadInterface::mesh_connect()
|
||||
mesh_error_t Nanostack::ThreadInterface::mesh_connect()
|
||||
{
|
||||
int8_t status;
|
||||
tr_debug("connect()");
|
||||
|
||||
status = thread_tasklet_connect(&__mesh_handler_c_callback, _network_interface_id);
|
||||
status = thread_tasklet_connect(&__mesh_handler_c_callback, interface_id);
|
||||
|
||||
if (status >= 0) {
|
||||
return MESH_ERROR_NONE;
|
||||
|
@ -119,17 +198,7 @@ mesh_error_t ThreadInterface::mesh_connect()
|
|||
}
|
||||
}
|
||||
|
||||
void ThreadInterface::device_eui64_set(const uint8_t *eui64)
|
||||
{
|
||||
memcpy(_eui64, eui64, 8);
|
||||
}
|
||||
|
||||
mesh_error_t ThreadInterface::device_pskd_set(const char *pskd)
|
||||
{
|
||||
return (mesh_error_t)thread_tasklet_device_pskd_set(pskd);
|
||||
}
|
||||
|
||||
mesh_error_t ThreadInterface::mesh_disconnect()
|
||||
mesh_error_t Nanostack::ThreadInterface::mesh_disconnect()
|
||||
{
|
||||
int8_t status;
|
||||
|
||||
|
@ -141,3 +210,34 @@ mesh_error_t ThreadInterface::mesh_disconnect()
|
|||
|
||||
return MESH_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
void ThreadInterface::device_eui64_set(const uint8_t *eui64)
|
||||
{
|
||||
get_interface()->device_eui64_set(eui64);
|
||||
}
|
||||
|
||||
void Nanostack::ThreadInterface::device_eui64_set(const uint8_t *eui64)
|
||||
{
|
||||
user_eui64_set = true;
|
||||
thread_tasklet_device_eui64_set(eui64);
|
||||
}
|
||||
|
||||
mesh_error_t ThreadInterface::device_pskd_set(const char *pskd)
|
||||
{
|
||||
return get_interface()->device_pskd_set(pskd);
|
||||
}
|
||||
|
||||
mesh_error_t Nanostack::ThreadInterface::device_pskd_set(const char *pskd)
|
||||
{
|
||||
return (mesh_error_t)thread_tasklet_device_pskd_set(pskd);
|
||||
}
|
||||
|
||||
#define THREAD 0x2345
|
||||
#if MBED_CONF_NSAPI_DEFAULT_MESH_TYPE == THREAD && DEVICE_802_15_4_PHY
|
||||
MBED_WEAK MeshInterface *MeshInterface::get_target_default_instance()
|
||||
{
|
||||
static ThreadInterface thread(NanostackRfPhy::get_default_instance());
|
||||
|
||||
return thread;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -224,20 +224,6 @@ void enet_tasklet_network_state_changed(mesh_connection_status_t status)
|
|||
}
|
||||
|
||||
/* Public functions */
|
||||
int8_t enet_tasklet_get_ip_address(char *address, int8_t len)
|
||||
{
|
||||
uint8_t binary_ipv6[16];
|
||||
|
||||
if ((len >= 40) && (0 == arm_net_address_get(
|
||||
tasklet_data_ptr->network_interface_id, ADDR_IPV6_GP, binary_ipv6))) {
|
||||
ip6tos(binary_ipv6, address);
|
||||
//tr_debug("IP address: %s", address);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t enet_tasklet_connect(mesh_interface_cb callback, int8_t nwk_interface_id)
|
||||
{
|
||||
int8_t re_connecting = true;
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 NANOSTACK_MEMORY_MANAGER_H
|
||||
#define NANOSTACK_MEMORY_MANAGER_H
|
||||
|
||||
#include "EMACMemoryManager.h"
|
||||
|
||||
class NanostackMemoryManager : public EMACMemoryManager {
|
||||
public:
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
/**
|
||||
* 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 buf 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 buf Memory buffer chain
|
||||
* @return Total length in bytes
|
||||
*/
|
||||
virtual uint32_t get_total_len(const emac_mem_buf_t *buf) const;
|
||||
|
||||
virtual void set_align_preference(uint32_t align) { }
|
||||
|
||||
/**
|
||||
* 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 buf 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 buf 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 buf 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 buf 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);
|
||||
};
|
||||
|
||||
#endif /* NANOSTACK_MEMORY_MANAGER_H */
|
|
@ -21,6 +21,7 @@
|
|||
#include "mesh_interface_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
@ -31,8 +32,7 @@ void __mesh_handler_c_callback(mesh_connection_status_t state);
|
|||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "NanostackInterface.h"
|
||||
void __mesh_handler_set_callback(MeshInterfaceNanostack *handler);
|
||||
void __mesh_handler_set_callback(Nanostack::Interface *handler);
|
||||
#endif
|
||||
|
||||
#endif /* __INCLUDE_CALLBACK_HANDLER_H__ */
|
||||
|
|
|
@ -26,7 +26,6 @@ void enet_tasklet_init(void);
|
|||
uint8_t enet_tasklet_network_init(int8_t);
|
||||
int8_t enet_tasklet_connect(void (*)(mesh_connection_status_t mesh_status), int8_t nwk_interface_id);
|
||||
void enet_tasklet_disconnect();
|
||||
int8_t enet_tasklet_get_ip_address(char *address, int8_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -29,17 +29,6 @@ extern "C" {
|
|||
*/
|
||||
typedef void (*mesh_interface_cb)(mesh_connection_status_t mesh_status);
|
||||
|
||||
/*
|
||||
* \brief Read own global IP address
|
||||
*
|
||||
* \param address where own IP address will be written
|
||||
* \param len length of provided address buffer
|
||||
*
|
||||
* \return 0 on success
|
||||
* \return -1 if address reading fails
|
||||
*/
|
||||
int8_t nd_tasklet_get_ip_address(char *address, int8_t len);
|
||||
|
||||
/*
|
||||
* \brief Read border router IP address
|
||||
*
|
||||
|
|
|
@ -29,17 +29,6 @@ extern "C" {
|
|||
*/
|
||||
typedef void (*mesh_interface_cb)(mesh_connection_status_t mesh_status);
|
||||
|
||||
/*
|
||||
* \brief Read own global IP address
|
||||
*
|
||||
* \param address where own IP address will be written
|
||||
* \param len length of provided address buffer
|
||||
*
|
||||
* \return 0 on success
|
||||
* \return -1 if address reading fails
|
||||
*/
|
||||
int8_t thread_tasklet_get_ip_address(char *address, int8_t len);
|
||||
|
||||
/*
|
||||
* \brief Connect to mesh network
|
||||
*
|
||||
|
|
|
@ -340,20 +340,6 @@ void nd_tasklet_trace_bootstrap_info()
|
|||
#endif /* #define TRACE_ND_TASKLET */
|
||||
|
||||
/* Public functions */
|
||||
int8_t nd_tasklet_get_ip_address(char *address, int8_t len)
|
||||
{
|
||||
uint8_t binary_ipv6[16];
|
||||
|
||||
if ((len >= 40) && (0 == arm_net_address_get(
|
||||
tasklet_data_ptr->network_interface_id, ADDR_IPV6_GP, binary_ipv6))) {
|
||||
ip6tos(binary_ipv6, address);
|
||||
//tr_debug("IP address: %s", address);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t nd_tasklet_get_router_ip_address(char *address, int8_t len)
|
||||
{
|
||||
network_layer_address_s nd_address;
|
||||
|
|
|
@ -390,20 +390,6 @@ void thread_tasklet_trace_bootstrap_info()
|
|||
}
|
||||
#endif /* #define TRACE_THREAD_TASKLET */
|
||||
|
||||
int8_t thread_tasklet_get_ip_address(char *address, int8_t len)
|
||||
{
|
||||
uint8_t binary_ipv6[16];
|
||||
|
||||
if ((len >= 40) && (0 == arm_net_address_get(
|
||||
thread_tasklet_data_ptr->nwk_if_id, ADDR_IPV6_GP, binary_ipv6))) {
|
||||
ip6tos(binary_ipv6, address);
|
||||
//tr_debug("IP address: %s", address);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t thread_tasklet_connect(mesh_interface_cb callback, int8_t nwk_interface_id)
|
||||
{
|
||||
int8_t re_connecting = true;
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
|
||||
#include "mbed.h"
|
||||
#include "rtos.h"
|
||||
#include "NanostackInterface.h"
|
||||
#include "Nanostack.h"
|
||||
#include "NanostackLockGuard.h"
|
||||
|
||||
#include "ns_address.h"
|
||||
#include "nsdynmemLIB.h"
|
||||
#include "eventOS_scheduler.h"
|
||||
#include "eventOS_event_timer.h"
|
||||
#include "randLIB.h"
|
||||
#include "ip6string.h"
|
||||
|
||||
|
@ -57,6 +58,7 @@ enum socket_mode_t {
|
|||
SOCKET_MODE_LISTENING, // Socket is listening for connections
|
||||
};
|
||||
|
||||
#define CALL_EVENT 0x12
|
||||
|
||||
class NanostackSocket {
|
||||
public:
|
||||
|
@ -444,20 +446,69 @@ void NanostackSocket::event_connection_reset(socket_callback_t *sock_cb)
|
|||
close();
|
||||
}
|
||||
|
||||
NanostackInterface *NanostackInterface::_ns_interface;
|
||||
|
||||
NanostackInterface *NanostackInterface::get_stack()
|
||||
Nanostack::Nanostack()
|
||||
: call_event_tasklet(-1)
|
||||
{
|
||||
NanostackLockGuard lock;
|
||||
|
||||
if (NULL == _ns_interface) {
|
||||
_ns_interface = new NanostackInterface();
|
||||
}
|
||||
|
||||
return _ns_interface;
|
||||
}
|
||||
|
||||
const char * NanostackInterface::get_ip_address()
|
||||
void Nanostack::call_event_tasklet_main(arm_event_s *event)
|
||||
{
|
||||
if (event->event_id == CALL_EVENT) {
|
||||
nanostack_callback *cb = static_cast<nanostack_callback *>(event->data_ptr);
|
||||
cb->callback();
|
||||
delete cb;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t Nanostack::call_in(int delay, mbed::Callback<void()> func)
|
||||
{
|
||||
if (call_event_tasklet < 0) {
|
||||
call_event_tasklet = eventOS_event_handler_create(&call_event_tasklet_main, 0);
|
||||
if (call_event_tasklet < 0) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
nanostack_callback *cb = new nanostack_callback;
|
||||
if (!cb) {
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
cb->callback = func;
|
||||
|
||||
arm_event_s event;
|
||||
|
||||
event.sender = call_event_tasklet,
|
||||
event.event_id = CALL_EVENT,
|
||||
event.receiver = call_event_tasklet,
|
||||
event.data_ptr = cb;
|
||||
event.event_type = APPLICATION_EVENT;
|
||||
event.priority = ARM_LIB_LOW_PRIORITY_EVENT;
|
||||
|
||||
if (delay) {
|
||||
uint32_t ticks = eventOS_event_timer_ms_to_ticks(delay);
|
||||
if (!eventOS_event_send_in(&event, ticks)) {
|
||||
delete cb;
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
} else {
|
||||
if (eventOS_event_send(&event) < 0) {
|
||||
delete cb;
|
||||
return NSAPI_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
Nanostack::call_in_callback_cb_t Nanostack::get_call_in_callback()
|
||||
{
|
||||
call_in_callback_cb_t cb(this, &Nanostack::call_in);
|
||||
return cb;
|
||||
}
|
||||
|
||||
const char * Nanostack::get_ip_address()
|
||||
{
|
||||
NanostackLockGuard lock;
|
||||
|
||||
|
@ -474,7 +525,7 @@ const char * NanostackInterface::get_ip_address()
|
|||
return "::";
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::socket_open(void **handle, nsapi_protocol_t protocol)
|
||||
nsapi_error_t Nanostack::socket_open(void **handle, nsapi_protocol_t protocol)
|
||||
{
|
||||
// Validate parameters
|
||||
if (NULL == handle) {
|
||||
|
@ -511,7 +562,7 @@ nsapi_error_t NanostackInterface::socket_open(void **handle, nsapi_protocol_t pr
|
|||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::socket_close(void *handle)
|
||||
nsapi_error_t Nanostack::socket_close(void *handle)
|
||||
{
|
||||
NanostackLockGuard lock;
|
||||
// Validate parameters
|
||||
|
@ -528,7 +579,7 @@ nsapi_error_t NanostackInterface::socket_close(void *handle)
|
|||
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t NanostackInterface::do_sendto(void *handle, const ns_address_t *address, const void *data, nsapi_size_t size)
|
||||
nsapi_size_or_error_t Nanostack::do_sendto(void *handle, const ns_address_t *address, const void *data, nsapi_size_t size)
|
||||
{
|
||||
// Validate parameters
|
||||
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
|
||||
|
@ -601,7 +652,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t NanostackInterface::socket_sendto(void *handle, const SocketAddress &address, const void *data, nsapi_size_t size)
|
||||
nsapi_size_or_error_t Nanostack::socket_sendto(void *handle, const SocketAddress &address, const void *data, nsapi_size_t size)
|
||||
{
|
||||
if (address.get_ip_version() != NSAPI_IPv6) {
|
||||
return NSAPI_ERROR_UNSUPPORTED;
|
||||
|
@ -613,7 +664,7 @@ nsapi_size_or_error_t NanostackInterface::socket_sendto(void *handle, const Sock
|
|||
return do_sendto(handle, &ns_address, data, size);
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t NanostackInterface::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, nsapi_size_t size)
|
||||
nsapi_size_or_error_t Nanostack::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, nsapi_size_t size)
|
||||
{
|
||||
// Validate parameters
|
||||
NanostackSocket *socket = static_cast<NanostackSocket *>(handle);
|
||||
|
@ -658,7 +709,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::socket_bind(void *handle, const SocketAddress &address)
|
||||
nsapi_error_t Nanostack::socket_bind(void *handle, const SocketAddress &address)
|
||||
{
|
||||
// Validate parameters
|
||||
NanostackSocket *socket = static_cast<NanostackSocket *>(handle);
|
||||
|
@ -699,7 +750,7 @@ nsapi_error_t NanostackInterface::socket_bind(void *handle, const SocketAddress
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen)
|
||||
nsapi_error_t Nanostack::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen)
|
||||
{
|
||||
NanostackSocket *socket = static_cast<NanostackSocket *>(handle);
|
||||
if (handle == NULL) {
|
||||
|
@ -753,7 +804,7 @@ nsapi_error_t NanostackInterface::setsockopt(void *handle, int level, int optnam
|
|||
}
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen)
|
||||
nsapi_error_t Nanostack::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen)
|
||||
{
|
||||
NanostackSocket *socket = static_cast<NanostackSocket *>(handle);
|
||||
if (handle == NULL) {
|
||||
|
@ -776,7 +827,7 @@ nsapi_error_t NanostackInterface::getsockopt(void *handle, int level, int optnam
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::socket_listen(void *handle, int backlog)
|
||||
nsapi_error_t Nanostack::socket_listen(void *handle, int backlog)
|
||||
{
|
||||
//Check if socket exists
|
||||
NanostackSocket *socket = static_cast<NanostackSocket *>(handle);
|
||||
|
@ -798,7 +849,7 @@ nsapi_error_t NanostackInterface::socket_listen(void *handle, int backlog)
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::socket_connect(void *handle, const SocketAddress &addr)
|
||||
nsapi_error_t Nanostack::socket_connect(void *handle, const SocketAddress &addr)
|
||||
{
|
||||
// Validate parameters
|
||||
NanostackSocket *socket = static_cast<NanostackSocket *>(handle);
|
||||
|
@ -850,7 +901,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_error_t NanostackInterface::socket_accept(void *server, void **handle, SocketAddress *address)
|
||||
nsapi_error_t Nanostack::socket_accept(void *server, void **handle, SocketAddress *address)
|
||||
{
|
||||
NanostackSocket * socket = static_cast<NanostackSocket *>(server);
|
||||
NanostackSocket *accepted_sock = NULL;
|
||||
|
@ -900,17 +951,17 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t NanostackInterface::socket_send(void *handle, const void *data, nsapi_size_t size)
|
||||
nsapi_size_or_error_t Nanostack::socket_send(void *handle, const void *data, nsapi_size_t size)
|
||||
{
|
||||
return do_sendto(handle, NULL, data, size);
|
||||
}
|
||||
|
||||
nsapi_size_or_error_t NanostackInterface::socket_recv(void *handle, void *data, nsapi_size_t size)
|
||||
nsapi_size_or_error_t Nanostack::socket_recv(void *handle, void *data, nsapi_size_t size)
|
||||
{
|
||||
return socket_recvfrom(handle, NULL, data, size);
|
||||
}
|
||||
|
||||
void NanostackInterface::socket_attach(void *handle, void (*callback)(void *), void *id)
|
||||
void Nanostack::socket_attach(void *handle, void (*callback)(void *), void *id)
|
||||
{
|
||||
// Validate parameters
|
||||
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
|
||||
|
@ -926,3 +977,18 @@ void NanostackInterface::socket_attach(void *handle, void (*callback)(void *), v
|
|||
|
||||
tr_debug("socket_attach(socket=%p) sock_id=%d", socket, socket->socket_id);
|
||||
}
|
||||
|
||||
Nanostack &Nanostack::get_instance() {
|
||||
static Nanostack nanostack;
|
||||
return nanostack;
|
||||
}
|
||||
|
||||
// This works as long as it's not ever set to something which corresponds to
|
||||
// a macro defined as a non-integer. Eg `#define Nanostack "Foo"`
|
||||
#define NANOSTACK 0x99119911
|
||||
#if MBED_CONF_NSAPI_DEFAULT_STACK == NANOSTACK
|
||||
#undef NANOSTACK
|
||||
OnboardNetworkStack &OnboardNetworkStack::get_default_instance() {
|
||||
return Nanostack::get_instance();
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2017, Arm Limited and affiliates.
|
||||
* 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 NANOSTACK_H_
|
||||
#define NANOSTACK_H_
|
||||
|
||||
#include "mbed.h"
|
||||
#include "OnboardNetworkStack.h"
|
||||
#include "NanostackMemoryManager.h"
|
||||
#include "MeshInterface.h"
|
||||
#include "mesh_interface_types.h"
|
||||
#include "eventOS_event.h"
|
||||
|
||||
struct ns_address;
|
||||
|
||||
class Nanostack : public OnboardNetworkStack, private mbed::NonCopyable<Nanostack> {
|
||||
public:
|
||||
static Nanostack &get_instance();
|
||||
|
||||
// Our Nanostack::Interface etc are defined by mbed_mesh_api
|
||||
class Interface;
|
||||
class EthernetInterface;
|
||||
class MeshInterface;
|
||||
class LoWPANNDInterface;
|
||||
class ThreadInterface;
|
||||
|
||||
/* Implement OnboardNetworkStack method */
|
||||
virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out);
|
||||
|
||||
/* Local variant with stronger typing and manual address specification */
|
||||
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr = NULL);
|
||||
|
||||
protected:
|
||||
|
||||
Nanostack();
|
||||
|
||||
/** Get the local IP address
|
||||
*
|
||||
* @return Null-terminated representation of the local IP address
|
||||
* or null if not yet connected
|
||||
*/
|
||||
virtual const char *get_ip_address();
|
||||
|
||||
/** 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(void **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(void *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(void *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(void *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(void *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(void *handle, void **server, SocketAddress *address);
|
||||
|
||||
/** 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(void *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(void *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(void *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(void *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(void *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(void *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(void *handle, int level, int optname, void *optval, unsigned *optlen);
|
||||
|
||||
private:
|
||||
|
||||
/** Call in callback
|
||||
*
|
||||
* Callback is used to call the call in method of the network stack.
|
||||
*/
|
||||
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;
|
||||
|
||||
/** Get a call in callback
|
||||
*
|
||||
* Get a call in callback from the network stack context.
|
||||
*
|
||||
* Callback should not take more than 10ms to execute, otherwise it might
|
||||
* prevent underlying thread processing. A portable user of the callback
|
||||
* should not make calls to network operations due to stack size limitations.
|
||||
* The callback should not perform expensive operations such as socket recv/send
|
||||
* calls or blocking operations.
|
||||
*
|
||||
* @return Call in callback
|
||||
*/
|
||||
virtual call_in_callback_cb_t get_call_in_callback();
|
||||
|
||||
/** Call a callback after a delay
|
||||
*
|
||||
* Call a callback from the network stack context after a delay. If function
|
||||
* returns error callback will not be called.
|
||||
*
|
||||
* @param delay Delay in milliseconds
|
||||
* @param func Callback to be called
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
nsapi_error_t call_in(int delay, mbed::Callback<void()> func);
|
||||
|
||||
struct nanostack_callback {
|
||||
mbed::Callback<void()> callback;
|
||||
};
|
||||
|
||||
nsapi_size_or_error_t do_sendto(void *handle, const struct ns_address *address, const void *data, nsapi_size_t size);
|
||||
static void call_event_tasklet_main(arm_event_s *event);
|
||||
char text_ip_address[40];
|
||||
NanostackMemoryManager memory_manager;
|
||||
int8_t call_event_tasklet;
|
||||
};
|
||||
|
||||
nsapi_error_t map_mesh_error(mesh_error_t err);
|
||||
|
||||
#endif /* NANOSTACK_H_ */
|
|
@ -19,7 +19,6 @@
|
|||
#define NANOSTACK_INTERFACE_H_
|
||||
|
||||
#include "mbed.h"
|
||||
#include "NetworkStack.h"
|
||||
#include "MeshInterface.h"
|
||||
// Include here for backward compatibility
|
||||
#include "LoWPANNDInterface.h"
|
||||
|
@ -27,217 +26,4 @@
|
|||
#include "NanostackEthernetInterface.h"
|
||||
#include "MeshInterfaceNanostack.h"
|
||||
|
||||
struct ns_address;
|
||||
|
||||
/** Network interface class for Nanostack */
|
||||
class NanostackInterface : public NetworkStack {
|
||||
public:
|
||||
static NanostackInterface *get_stack();
|
||||
|
||||
protected:
|
||||
|
||||
/** Get the local IP address
|
||||
*
|
||||
* @return Null-terminated representation of the local IP address
|
||||
* or null if not yet connected
|
||||
*/
|
||||
virtual const char *get_ip_address();
|
||||
|
||||
/** 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(void **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(void *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(void *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(void *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(void *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(void *handle, void **server, SocketAddress *address);
|
||||
|
||||
/** 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(void *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(void *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(void *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(void *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(void *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(void *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(void *handle, int level, int optname, void *optval, unsigned *optlen);
|
||||
|
||||
private:
|
||||
nsapi_size_or_error_t do_sendto(void *handle, const struct ns_address *address, const void *data, nsapi_size_t size);
|
||||
char text_ip_address[40];
|
||||
static NanostackInterface * _ns_interface;
|
||||
};
|
||||
|
||||
nsapi_error_t map_mesh_error(mesh_error_t err);
|
||||
|
||||
#endif /* NANOSTACK_INTERFACE_H_ */
|
||||
|
|
|
@ -24,6 +24,13 @@
|
|||
class NanostackRfPhy : public NanostackPhy {
|
||||
public:
|
||||
|
||||
/** Return the default on-board NanostackRfPhy
|
||||
*
|
||||
* Returns the default on-board NanostackRfPhy - this will be target-specific, and
|
||||
* may not be available on all targets.
|
||||
*/
|
||||
static NanostackRfPhy &get_default_instance();
|
||||
|
||||
/** Register this physical interface with Nanostack
|
||||
*
|
||||
* @return Device driver ID or a negative error
|
||||
|
|
|
@ -26,6 +26,17 @@ class CellularBase: public NetworkInterface {
|
|||
|
||||
public:
|
||||
|
||||
/** Get the default Cellular interface.
|
||||
*
|
||||
* This is provided as a weak method so applications can override.
|
||||
* Default behaviour is to get the target's default interface, if
|
||||
* any.
|
||||
*
|
||||
* @return pointer to interface, if any
|
||||
*/
|
||||
|
||||
static CellularBase *get_default_instance();
|
||||
|
||||
/** Set the Cellular network credentials
|
||||
*
|
||||
* Please check documentation of connect() for default behaviour of APN settings.
|
||||
|
@ -101,6 +112,20 @@ public:
|
|||
*/
|
||||
virtual const char *get_gateway() = 0;
|
||||
|
||||
virtual CellularBase *cellularBase() {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Get the target's default Cellular interface.
|
||||
*
|
||||
* This is provided as a weak method so targets can override. The
|
||||
* default implementation configures and returns the OnBoardModemInterface
|
||||
* if available.
|
||||
*
|
||||
* @return pointer to interface, if any
|
||||
*/
|
||||
static CellularBase *get_target_default_instance();
|
||||
};
|
||||
|
||||
#endif //CELLULAR_BASE_H
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/* CellularInterface
|
||||
* Copyright (c) 2015 ARM Limited
|
||||
*
|
||||
* 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 CELLULAR_INTERFACE_H
|
||||
#define CELLULAR_INTERFACE_H
|
||||
|
||||
#include "netsocket/NetworkInterface.h"
|
||||
|
||||
/** CellularInterface class
|
||||
*
|
||||
* @deprecated This API will be deprecated in mbed-os-5.9. Use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.
|
||||
*
|
||||
* Common interface that is shared between ethernet hardware
|
||||
* @addtogroup netsocket
|
||||
*/
|
||||
class CellularInterface : public NetworkInterface
|
||||
{
|
||||
public:
|
||||
/** CellularInterface lifetime
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.9", "This API will be deprecated, use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.")
|
||||
virtual ~CellularInterface() {};
|
||||
|
||||
/** Set the cellular network APN and credentials
|
||||
*
|
||||
* @deprecated This API will be deprecated in mbed-os-5.9. Use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.
|
||||
*
|
||||
* @param apn Optional name of the network to connect to
|
||||
* @param username Optional username for the APN
|
||||
* @param password Optional password fot the APN
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.9", "This API will be deprecated, use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.")
|
||||
virtual nsapi_error_t set_credentials(const char *apn,
|
||||
const char *username = 0, const char *password = 0) = 0;
|
||||
|
||||
/** Start the interface
|
||||
*
|
||||
* @deprecated This API will be deprecated in mbed-os-5.9. Use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.
|
||||
*
|
||||
* @param apn Optional name of the network to connect to
|
||||
* @param username Optional username for your APN
|
||||
* @param password Optional password for your APN
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.9", "This API will be deprecated, use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.")
|
||||
virtual nsapi_error_t connect(const char *apn,
|
||||
const char *username = 0, const char *password = 0) = 0;
|
||||
|
||||
/** Start the interface
|
||||
*
|
||||
* @deprecated This API will be deprecated in mbed-os-5.9. Use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.
|
||||
*
|
||||
* Attempts to connect to a cellular network based on supplied credentials
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.9", "This API will be deprecated, use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.")
|
||||
virtual nsapi_error_t connect() = 0;
|
||||
|
||||
/** Stop the interface
|
||||
*
|
||||
* @deprecated This API will be deprecated in mbed-os-5.9. Use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
MBED_DEPRECATED_SINCE("mbed-os-5.9", "This API will be deprecated, use mbed-os/features/cellular/framework/API/CellularNetwork.h instead.")
|
||||
virtual nsapi_error_t disconnect() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* 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 DNS_H
|
||||
#define DNS_H
|
||||
|
||||
class DNS {
|
||||
public:
|
||||
|
||||
/** 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) = 0;
|
||||
|
||||
/** Hostname translation callback (asynchronous)
|
||||
*
|
||||
* Callback will be called after DNS resolution completes or a failure occurs.
|
||||
*
|
||||
* Callback should not take more than 10ms to execute, otherwise it might
|
||||
* prevent underlying thread processing. A portable user of the callback
|
||||
* should not make calls to network operations due to stack size limitations.
|
||||
* The callback should not perform expensive operations such as socket recv/send
|
||||
* calls or blocking operations.
|
||||
*
|
||||
* @param status 0 on success, negative error code on failure
|
||||
* @param address On success, destination for the host SocketAddress
|
||||
*/
|
||||
typedef mbed::Callback<void (nsapi_error_t result, SocketAddress *address)> hostbyname_cb_t;
|
||||
|
||||
/** Translates a hostname to an IP address (asynchronous)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Call is non-blocking. Result of the DNS operation is returned by the callback.
|
||||
* If this function returns failure, callback will not be called. In case result
|
||||
* is success (IP address was found from DNS cache), callback will be called
|
||||
* before function returns.
|
||||
*
|
||||
* @param host Hostname to resolve
|
||||
* @param callback Callback that is called for result
|
||||
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
|
||||
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
|
||||
* @return 0 on immediate success,
|
||||
* negative error code on immediate failure or
|
||||
* a positive unique id that represents the hostname translation operation
|
||||
* and can be passed to cancel
|
||||
*/
|
||||
virtual nsapi_value_or_error_t gethostbyname_async(const char *host, hostbyname_cb_t callback,
|
||||
nsapi_version_t version = NSAPI_UNSPEC) = 0;
|
||||
|
||||
/** Cancels asynchronous hostname translation
|
||||
*
|
||||
* When translation is cancelled, callback will not be called.
|
||||
*
|
||||
* @param id Unique id of the hostname translation operation
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t gethostbyname_async_cancel(int id) = 0;
|
||||
|
||||
/** 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) = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,187 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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_H
|
||||
#define EMAC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "Callback.h"
|
||||
#include "EMACMemoryManager.h"
|
||||
|
||||
// Nuvoton platform headers define EMAC - avoid the collision
|
||||
#undef EMAC
|
||||
|
||||
/**
|
||||
* This interface should be used to abstract low level access to networking hardware
|
||||
* All operations receive a `void *` hw pointer which an emac device provides when
|
||||
* it is registered with a stack.
|
||||
*/
|
||||
class EMAC {
|
||||
public:
|
||||
|
||||
/** Return the default on-board EMAC
|
||||
*
|
||||
* Returns the default on-board EMAC - this will be target-specific, and
|
||||
* may not be available on all targets.
|
||||
*/
|
||||
static EMAC &get_default_instance();
|
||||
|
||||
/**
|
||||
* Callback to be register with Emac interface and to be called for received packets
|
||||
*
|
||||
* @param buf Received data
|
||||
*/
|
||||
//typedef void (*emac_link_input_fn)(void *data, emac_mem_buf_t *buf);
|
||||
typedef mbed::Callback<void (emac_mem_buf_t *buf)> emac_link_input_cb_t;
|
||||
|
||||
/**
|
||||
* Callback to be register with Emac interface and to be called for link status changes
|
||||
*
|
||||
* @param up Link status
|
||||
*/
|
||||
//typedef void (*emac_link_state_change_fn)(void *data, bool up);
|
||||
typedef mbed::Callback<void (bool up)> emac_link_state_change_cb_t;
|
||||
|
||||
/**
|
||||
* Return maximum transmission unit
|
||||
*
|
||||
* @return MTU in bytes
|
||||
*/
|
||||
virtual uint32_t get_mtu_size() const = 0;
|
||||
|
||||
/**
|
||||
* Gets memory buffer alignment preference
|
||||
*
|
||||
* Gets preferred memory buffer alignment of the Emac device. IP stack may or may not
|
||||
* align link out memory buffer chains using the alignment.
|
||||
*
|
||||
* @return Memory alignment requirement in bytes
|
||||
*/
|
||||
virtual uint32_t get_align_preference() const = 0;
|
||||
|
||||
/**
|
||||
* Return interface name
|
||||
*
|
||||
* @param name Pointer to where the name should be written
|
||||
* @param size Maximum number of character to copy
|
||||
*/
|
||||
virtual void get_ifname(char *name, uint8_t size) const = 0;
|
||||
|
||||
/**
|
||||
* Returns size of the underlying interface HW address size.
|
||||
*
|
||||
* @return HW address size in bytes
|
||||
*/
|
||||
virtual uint8_t get_hwaddr_size() const = 0;
|
||||
|
||||
/**
|
||||
* Return interface-supplied HW address
|
||||
*
|
||||
* Copies HW address to provided memory, @param addr has to be of correct size see @a get_hwaddr_size
|
||||
*
|
||||
* HW address need not be provided if this interface does not have its own HW
|
||||
* address configuration; stack will choose address from central system
|
||||
* configuration if the function returns false and does not write to addr.
|
||||
*
|
||||
* @param addr HW address for underlying interface
|
||||
* @return true if HW address is available
|
||||
*/
|
||||
virtual bool get_hwaddr(uint8_t *addr) const = 0;
|
||||
|
||||
/**
|
||||
* Set HW address for interface
|
||||
*
|
||||
* Provided address has to be of correct size, see @a get_hwaddr_size
|
||||
*
|
||||
* Called to set the MAC address to actually use - if @a get_hwaddr is provided
|
||||
* the stack would normally use that, but it could be overridden, eg for test
|
||||
* purposes.
|
||||
*
|
||||
* @param addr Address to be set
|
||||
*/
|
||||
virtual void set_hwaddr(const uint8_t *addr) = 0;
|
||||
|
||||
/**
|
||||
* Sends the packet over the link
|
||||
*
|
||||
* That can not be called from an interrupt context.
|
||||
*
|
||||
* @param buf Packet to be send
|
||||
* @return True if the packet was send successfully, False otherwise
|
||||
*/
|
||||
virtual bool link_out(emac_mem_buf_t *buf) = 0;
|
||||
|
||||
/**
|
||||
* Initializes the HW
|
||||
*
|
||||
* @return True on success, False in case of an error.
|
||||
*/
|
||||
virtual bool power_up() = 0;
|
||||
|
||||
/**
|
||||
* Deinitializes the HW
|
||||
*
|
||||
*/
|
||||
virtual void power_down() = 0;
|
||||
|
||||
/**
|
||||
* Sets a callback that needs to be called for packets received for that interface
|
||||
*
|
||||
* @param input_cb Function to be register as a callback
|
||||
*/
|
||||
virtual void set_link_input_cb(emac_link_input_cb_t input_cb) = 0;
|
||||
|
||||
/**
|
||||
* Sets a callback that needs to be called on link status changes for given interface
|
||||
*
|
||||
* @param state_cb Function to be register as a callback
|
||||
*/
|
||||
virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb) = 0;
|
||||
|
||||
/** Add device to a multicast group
|
||||
*
|
||||
* @param address A multicast group hardware address
|
||||
*/
|
||||
virtual void add_multicast_group(const uint8_t *address) = 0;
|
||||
|
||||
/** Remove device from a multicast group
|
||||
*
|
||||
* @param address A multicast group hardware address
|
||||
*/
|
||||
virtual void remove_multicast_group(const uint8_t *address) = 0;
|
||||
|
||||
/** Request reception of all multicast packets
|
||||
*
|
||||
* @param all True to receive all multicasts
|
||||
* False to receive only multicasts addressed to specified groups
|
||||
*/
|
||||
virtual void set_all_multicast(bool all) = 0;
|
||||
|
||||
/** Sets memory manager that is used to handle memory buffers
|
||||
*
|
||||
* @param mem_mngr Pointer to memory manager
|
||||
*/
|
||||
virtual void set_memory_manager(EMACMemoryManager &mem_mngr) = 0;
|
||||
};
|
||||
|
||||
|
||||
/** These need to be defined by targets wishing to provide an Ethernet driver using EMAC interface. It will
|
||||
* be used by the EMACInterface class's default constructor to initialise the networking subsystem.
|
||||
*/
|
||||
//extern const emac_interface_ops_t mbed_emac_eth_ops_default;
|
||||
//extern void *mbed_emac_eth_hw_default;
|
||||
|
||||
#endif /* EMAC_H */
|
|
@ -0,0 +1,138 @@
|
|||
/* LWIP implementation of NetworkInterfaceAPI
|
||||
* Copyright (c) 2015 ARM Limited
|
||||
*
|
||||
* 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 "EMACInterface.h"
|
||||
|
||||
/* Interface implementation */
|
||||
EMACInterface::EMACInterface(EMAC &emac, OnboardNetworkStack &stack) :
|
||||
_emac(emac),
|
||||
_stack(stack),
|
||||
_interface(NULL),
|
||||
_dhcp(true),
|
||||
_blocking(true),
|
||||
_ip_address(),
|
||||
_netmask(),
|
||||
_gateway()
|
||||
{
|
||||
}
|
||||
|
||||
nsapi_error_t EMACInterface::set_network(const char *ip_address, const char *netmask, const char *gateway)
|
||||
{
|
||||
_dhcp = false;
|
||||
|
||||
strncpy(_ip_address, ip_address ? ip_address : "", sizeof(_ip_address));
|
||||
_ip_address[sizeof(_ip_address) - 1] = '\0';
|
||||
strncpy(_netmask, netmask ? netmask : "", sizeof(_netmask));
|
||||
_netmask[sizeof(_netmask) - 1] = '\0';
|
||||
strncpy(_gateway, gateway ? gateway : "", sizeof(_gateway));
|
||||
_gateway[sizeof(_gateway) - 1] = '\0';
|
||||
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t EMACInterface::set_dhcp(bool dhcp)
|
||||
{
|
||||
_dhcp = dhcp;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
nsapi_error_t EMACInterface::connect()
|
||||
{
|
||||
if (!_interface) {
|
||||
nsapi_error_t err = _stack.add_ethernet_interface(_emac, true, &_interface);
|
||||
if (err != NSAPI_ERROR_OK) {
|
||||
_interface = NULL;
|
||||
return err;
|
||||
}
|
||||
_interface->attach(_connection_status_cb);
|
||||
}
|
||||
|
||||
return _interface->bringup(_dhcp,
|
||||
_ip_address[0] ? _ip_address : 0,
|
||||
_netmask[0] ? _netmask : 0,
|
||||
_gateway[0] ? _gateway : 0,
|
||||
DEFAULT_STACK,
|
||||
_blocking);
|
||||
}
|
||||
|
||||
nsapi_error_t EMACInterface::disconnect()
|
||||
{
|
||||
return _interface->bringdown();
|
||||
}
|
||||
|
||||
const char *EMACInterface::get_mac_address()
|
||||
{
|
||||
if (_interface->get_mac_address(_mac_address, sizeof(_mac_address))) {
|
||||
return _mac_address;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *EMACInterface::get_ip_address()
|
||||
{
|
||||
if (_interface->get_ip_address(_ip_address, sizeof(_ip_address))) {
|
||||
return _ip_address;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *EMACInterface::get_netmask()
|
||||
{
|
||||
if (_interface->get_netmask(_netmask, sizeof(_netmask))) {
|
||||
return _netmask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *EMACInterface::get_gateway()
|
||||
{
|
||||
if (_interface->get_gateway(_gateway, sizeof(_gateway))) {
|
||||
return _gateway;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetworkStack *EMACInterface::get_stack()
|
||||
{
|
||||
return &_stack;
|
||||
}
|
||||
|
||||
void EMACInterface::attach(
|
||||
Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
_connection_status_cb = status_cb;
|
||||
if (_interface) {
|
||||
_interface->attach(status_cb);
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_connection_status_t EMACInterface::get_connection_status() const
|
||||
{
|
||||
if (_interface) {
|
||||
return _interface->get_connection_status();
|
||||
} else {
|
||||
return NSAPI_STATUS_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t EMACInterface::set_blocking(bool blocking)
|
||||
{
|
||||
_blocking = blocking;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
|
@ -14,26 +14,47 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ETHERNET_INTERFACE_H
|
||||
#define ETHERNET_INTERFACE_H
|
||||
#ifndef EMAC_INTERFACE_H
|
||||
#define EMAC_INTERFACE_H
|
||||
|
||||
#include "nsapi.h"
|
||||
#include "rtos.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
// Forward declaration
|
||||
class NetworkStack;
|
||||
#include "EMAC.h"
|
||||
#include "OnboardNetworkStack.h"
|
||||
|
||||
|
||||
/** EthernetInterface class
|
||||
* Implementation of the NetworkStack for LWIP
|
||||
/** EMACInterface class
|
||||
* Implementation of the NetworkInterface for an EMAC-based driver
|
||||
*
|
||||
* This class provides the necessary glue logic to create a NetworkInterface
|
||||
* based on an EMAC and an OnboardNetworkStack. EthernetInterface and
|
||||
* EMAC-based Wi-Fi drivers derive from it.
|
||||
*
|
||||
* Drivers derived from EMACInterface should be constructed so that their
|
||||
* EMAC is functional without the need to call `connect()`. For example
|
||||
* a Wi-Fi driver should permit `WiFi::get_emac().power_up()` as soon as
|
||||
* the credentials have been set. This is necessary to support specialised
|
||||
* applications such as 6LoWPAN mesh border routers.
|
||||
*/
|
||||
class EthernetInterface : public EthInterface
|
||||
class EMACInterface : public virtual NetworkInterface
|
||||
{
|
||||
public:
|
||||
/** EthernetInterface lifetime
|
||||
/** Create an EMAC-based network interface.
|
||||
*
|
||||
* The default arguments obtain the default EMAC, which will be target-
|
||||
* dependent (and the target may have some JSON option to choose which
|
||||
* is the default, if there are multiple). The default stack is configured
|
||||
* by JSON option nsapi.default-stack.
|
||||
*
|
||||
* Due to inability to return errors from the constructor, no real
|
||||
* work is done until the first call to connect().
|
||||
*
|
||||
* @param emac Reference to EMAC to use
|
||||
* @param stack Reference to onboard-network stack to use
|
||||
*/
|
||||
EthernetInterface();
|
||||
EMACInterface(
|
||||
EMAC &emac = EMAC::get_default_instance(),
|
||||
OnboardNetworkStack &stack = OnboardNetworkStack::get_default_instance());
|
||||
|
||||
/** Set a static IP address
|
||||
*
|
||||
|
@ -41,10 +62,10 @@ public:
|
|||
* Implicitly disables DHCP, which can be enabled in set_dhcp.
|
||||
* Requires that the network is disconnected.
|
||||
*
|
||||
* @param address Null-terminated representation of the local IP address
|
||||
* @param netmask Null-terminated representation of the local network mask
|
||||
* @param gateway Null-terminated representation of the local gateway
|
||||
* @return 0 on success, negative error code on failure
|
||||
* @param ip_address Null-terminated representation of the local IP address
|
||||
* @param netmask Null-terminated representation of the local network mask
|
||||
* @param gateway Null-terminated representation of the local gateway
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t set_network(
|
||||
const char *ip_address, const char *netmask, const char *gateway);
|
||||
|
@ -119,6 +140,19 @@ public:
|
|||
*/
|
||||
virtual nsapi_error_t set_blocking(bool blocking);
|
||||
|
||||
/** Provide access to the EMAC
|
||||
*
|
||||
* This should be used with care - normally the network stack would
|
||||
* control the EMAC, so manipulating the EMAC while the stack
|
||||
* is also using it (ie after connect) will likely cause problems.
|
||||
*
|
||||
* @return Reference to the EMAC in use
|
||||
*/
|
||||
EMAC &get_emac() const { return _emac; }
|
||||
|
||||
virtual EMACInterface *emacInterface() {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Provide access to the underlying stack
|
||||
|
@ -127,16 +161,16 @@ protected:
|
|||
*/
|
||||
virtual NetworkStack *get_stack();
|
||||
|
||||
EMAC &_emac;
|
||||
OnboardNetworkStack &_stack;
|
||||
OnboardNetworkStack::Interface *_interface;
|
||||
bool _dhcp;
|
||||
char _ip_address[IPADDR_STRLEN_MAX];
|
||||
bool _blocking;
|
||||
char _mac_address[NSAPI_MAC_SIZE];
|
||||
char _ip_address[NSAPI_IPv6_SIZE];
|
||||
char _netmask[NSAPI_IPv4_SIZE];
|
||||
char _gateway[NSAPI_IPv4_SIZE];
|
||||
|
||||
|
||||
Callback<void(nsapi_event_t, intptr_t)> _connection_status_cb;
|
||||
nsapi_connection_status_t _connect_status;
|
||||
static void netif_status_cb(void *, nsapi_event_t, intptr_t);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
/* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* 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 "EMACMemoryManager.h"
|
||||
|
||||
void EMACMemoryManager::copy_to_buf(emac_mem_buf_t *to_buf, const void *ptr, uint32_t len)
|
||||
{
|
||||
while (to_buf && len) {
|
||||
void *copy_to_ptr = get_ptr(to_buf);
|
||||
uint32_t copy_to_len = get_len(to_buf);
|
||||
|
||||
if (copy_to_len > len) {
|
||||
copy_to_len = len;
|
||||
len = 0;
|
||||
} else {
|
||||
len -= copy_to_len;
|
||||
}
|
||||
|
||||
memcpy(copy_to_ptr, ptr, copy_to_len);
|
||||
ptr = static_cast<const uint8_t *>(ptr) + copy_to_len;
|
||||
|
||||
to_buf = get_next(to_buf);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EMACMemoryManager::copy_from_buf(void *ptr, uint32_t len, const emac_mem_buf_t *from_buf) const
|
||||
{
|
||||
uint32_t copied_len = 0;
|
||||
|
||||
while (from_buf && len) {
|
||||
void *copy_from_ptr = get_ptr(from_buf);
|
||||
uint32_t copy_from_len = get_len(from_buf);
|
||||
|
||||
if (copy_from_len > len) {
|
||||
copy_from_len = len;
|
||||
len = 0;
|
||||
} else {
|
||||
len -= copy_from_len;
|
||||
}
|
||||
|
||||
memcpy(ptr, copy_from_ptr, copy_from_len);
|
||||
ptr = static_cast<uint8_t *>(ptr) + copy_from_len;
|
||||
copied_len += copy_from_len;
|
||||
|
||||
from_buf = get_next(from_buf);
|
||||
}
|
||||
|
||||
return copied_len;
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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_MEMORY_MANAGER_H
|
||||
#define EMAC_MEMORY_MANAGER_H
|
||||
|
||||
/**
|
||||
* Emac interface memory manager
|
||||
*
|
||||
* This interface provides abstraction for memory modules used in different IP stacks (often to accommodate zero
|
||||
* copy). Emac interface is required to accept output packets and provide received data using this stack-
|
||||
* independent API. This header should be implemented for each IP stack, so that we keep emacs module independent.
|
||||
*
|
||||
* Emac memory interface uses memory buffer chains to store data. Data passed in either direction
|
||||
* may be either contiguous (a single-buffer chain), or may consist of multiple buffers.
|
||||
* Chaining of the buffers is made using singly-linked list. The Emac data-passing APIs do not specify
|
||||
* alignment or structure of the chain in either direction.
|
||||
*
|
||||
* Memory buffers can be allocated either from heap or from memory pools. Heap buffers are always contiguous.
|
||||
* Memory pool buffers may be either contiguous or chained depending on allocation size.
|
||||
*
|
||||
* On Emac interface buffer chain ownership is transferred. Emac must free buffer chain that it is given for
|
||||
* link output and the stack must free the buffer chain that it is given for link input.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nsapi.h"
|
||||
|
||||
typedef void emac_mem_buf_t; // Memory buffer
|
||||
|
||||
class EMACMemoryManager {
|
||||
public:
|
||||
|
||||
/**
|
||||
* 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) = 0;
|
||||
|
||||
/**
|
||||
* 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) = 0;
|
||||
|
||||
/**
|
||||
* 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 = 0;
|
||||
|
||||
/**
|
||||
* 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 buf Memory buffer chain to be freed.
|
||||
*/
|
||||
virtual void free(emac_mem_buf_t *buf) = 0;
|
||||
|
||||
/**
|
||||
* Return total length of a memory buffer chain
|
||||
*
|
||||
* Returns a total length of this buffer and any following buffers in the chain.
|
||||
*
|
||||
* @param buf Memory buffer chain
|
||||
* @return Total length in bytes
|
||||
*/
|
||||
virtual uint32_t get_total_len(const emac_mem_buf_t *buf) const = 0;
|
||||
|
||||
/**
|
||||
* 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) = 0;
|
||||
|
||||
/**
|
||||
* Copy to a memory buffer chain
|
||||
*
|
||||
* Copies data to a buffer chain. Copy operation does not adjust the lengths
|
||||
* of the copied-to memory buffer chain, so chain total length must match the
|
||||
* copied length.
|
||||
*
|
||||
* @param to_buf Memory buffer chain to copy to
|
||||
* @param ptr Pointer to data
|
||||
* @param len Data length
|
||||
*/
|
||||
virtual void copy_to_buf(emac_mem_buf_t *to_buf, const void *ptr, uint32_t len);
|
||||
|
||||
/**
|
||||
* Copy from a memory buffer chain
|
||||
*
|
||||
* Copies data from a memory buffer chain.
|
||||
*
|
||||
* @param len Data length
|
||||
* @param ptr Pointer to data
|
||||
* @param from_buf Memory buffer chain to copy from
|
||||
* @return Length of the data that was copied
|
||||
*/
|
||||
virtual uint32_t copy_from_buf(void *ptr, uint32_t len, const emac_mem_buf_t *from_buf) const;
|
||||
|
||||
/**
|
||||
* 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) = 0;
|
||||
|
||||
/**
|
||||
* Returns the next buffer
|
||||
*
|
||||
* Returns the next buffer from the memory buffer chain.
|
||||
*
|
||||
* @param buf 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 = 0;
|
||||
|
||||
/**
|
||||
* Return pointer to the payload of the buffer
|
||||
*
|
||||
* @param buf Memory buffer
|
||||
* @return Pointer to the payload
|
||||
*/
|
||||
virtual void *get_ptr(const emac_mem_buf_t *buf) const = 0;
|
||||
|
||||
/**
|
||||
* Return payload size of the buffer
|
||||
*
|
||||
* @param buf Memory buffer
|
||||
* @return Size in bytes
|
||||
*/
|
||||
virtual uint32_t get_len(const emac_mem_buf_t *buf) const = 0;
|
||||
|
||||
/**
|
||||
* 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 buf 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) = 0;
|
||||
};
|
||||
|
||||
#endif /* EMAC_MEMORY_MANAGER_H */
|
|
@ -27,8 +27,35 @@
|
|||
*
|
||||
* Common interface that is shared between ethernet hardware.
|
||||
*/
|
||||
class EthInterface : public NetworkInterface
|
||||
class EthInterface : public virtual NetworkInterface
|
||||
{
|
||||
public:
|
||||
|
||||
virtual EthInterface *ethInterface() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Get the default Ethernet interface.
|
||||
*
|
||||
* This is provided as a weak method so applications can override.
|
||||
* Default behaviour is to get the target's default interface, if
|
||||
* any.
|
||||
*
|
||||
* @return pointer to interface, if any
|
||||
*/
|
||||
static EthInterface *get_default_instance();
|
||||
|
||||
protected:
|
||||
|
||||
/** Get the target's default Ethernet interface.
|
||||
*
|
||||
* This is provided as a weak method so targets can override. The
|
||||
* default implementation will invoke EthernetInterface with the
|
||||
* default EMAC and default network stack, if DEVICE_EMAC is set.
|
||||
*
|
||||
* @return pointer to interface, if any
|
||||
*/
|
||||
static EthInterface *get_target_default_instance();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* LWIP implementation of NetworkInterfaceAPI
|
||||
* Copyright (c) 2015 ARM Limited
|
||||
*
|
||||
* 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 "EthernetInterface.h"
|
||||
|
||||
/* No actual interface implementation here, as EthernetInterface is
|
||||
* just an EMACInterface. But we can be the default EthInterface - step up
|
||||
* if the target has a default EMAC.
|
||||
*/
|
||||
#if DEVICE_EMAC
|
||||
MBED_WEAK EthInterface *EthInterface::get_target_default_instance()
|
||||
{
|
||||
static EthernetInterface ethernet;
|
||||
return ðernet;
|
||||
}
|
||||
#else
|
||||
MBED_WEAK EthInterface *EthInterface::get_target_default_instance()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,49 @@
|
|||
/* LWIP implementation of NetworkInterfaceAPI
|
||||
* Copyright (c) 2015 ARM Limited
|
||||
*
|
||||
* 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 ETHERNET_INTERFACE_H
|
||||
#define ETHERNET_INTERFACE_H
|
||||
|
||||
#include "nsapi.h"
|
||||
#include "rtos.h"
|
||||
#include "EMACInterface.h"
|
||||
|
||||
|
||||
/** EthernetInterface class
|
||||
* Implementation of the NetworkStack for an EMAC-based Ethernet driver
|
||||
*/
|
||||
class EthernetInterface : public EMACInterface, public EthInterface
|
||||
{
|
||||
public:
|
||||
/** Create an EMAC-based ethernet interface.
|
||||
*
|
||||
* The default arguments obtain the default EMAC, which will be target-
|
||||
* dependent (and the target may have some JSON option to choose which
|
||||
* is the default, if there are multiple). The default stack is configured
|
||||
* by JSON option nsapi.default-stack.
|
||||
*
|
||||
* Due to inability to return errors from the constructor, no real
|
||||
* work is done until the first call to connect().
|
||||
*
|
||||
* @param emac Reference to EMAC to use
|
||||
* @param stack Reference to onboard-network stack to use
|
||||
*/
|
||||
EthernetInterface(
|
||||
EMAC &emac = EMAC::get_default_instance(),
|
||||
OnboardNetworkStack &stack = OnboardNetworkStack::get_default_instance()) : EMACInterface(emac, stack) { }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -27,8 +27,35 @@
|
|||
*
|
||||
* Common interface that is shared between mesh hardware
|
||||
*/
|
||||
class MeshInterface : public NetworkInterface
|
||||
class MeshInterface : public virtual NetworkInterface
|
||||
{
|
||||
public:
|
||||
|
||||
virtual MeshInterface *meshInterface() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Get the default Mesh interface.
|
||||
*
|
||||
* This is provided as a weak method so applications can override.
|
||||
* Default behaviour is to get the target's default interface, if
|
||||
* any.
|
||||
*
|
||||
* @return pointer to interface, if any
|
||||
*/
|
||||
static MeshInterface *get_default_instance();
|
||||
|
||||
protected:
|
||||
|
||||
/** Get the target's default Mesh interface.
|
||||
*
|
||||
* This is provided as a weak method so targets can override. The
|
||||
* default implementation will invoke LoWPANNDInterface or ThreadInterface
|
||||
* with the default NanostackRfPhy.
|
||||
*
|
||||
* @return pointer to interface, if any
|
||||
*/
|
||||
static MeshInterface *get_target_default_instance();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -60,6 +60,16 @@ nsapi_error_t NetworkInterface::gethostbyname(const char *name, SocketAddress *a
|
|||
return get_stack()->gethostbyname(name, address, version);
|
||||
}
|
||||
|
||||
nsapi_value_or_error_t NetworkInterface::gethostbyname_async(const char *host, hostbyname_cb_t callback, nsapi_version_t version)
|
||||
{
|
||||
return get_stack()->gethostbyname_async(host, callback, version);
|
||||
}
|
||||
|
||||
nsapi_error_t NetworkInterface::gethostbyname_async_cancel(int id)
|
||||
{
|
||||
return get_stack()->gethostbyname_async_cancel(id);
|
||||
}
|
||||
|
||||
nsapi_error_t NetworkInterface::add_dns_server(const SocketAddress &address)
|
||||
{
|
||||
return get_stack()->add_dns_server(address);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue