Networking update: general refactoring, unifying EMAC

Initial work by Bartek Szatkowski in https://github.com/ARMmbed/mbed-os/pull/4079,
reworked following review of https://github.com/ARMmbed/mbed-os/pull/5202 to
transform the entire system into C++, retaining the basic functionality.

Bartek's summary:

* Porting ethernet to EMAC
* Updating EMAC to enable multiple interfaces
* Untangling networking classes, making the abstractions a bit clearer to follow, etc
* General refactoring
* Removal of DEVICE_EMAC flag and introducing DEVICE_ETH and DEVICE_WIFI

Revisions since initial branch:

* Remove lwip depencies
* Correct doxygen warnings
* Remove emac_api.h, replace with C++ EMAC abstract class.
* Create OnboardNetworkInterface, and LWIP implementation.
* Mappings since #4079
     lwip-interface/nsapi_stack_lwip.c -> LWIPStack.cpp
     lwip-interface/ipstack_lwip.c -> LWIPInterface.cpp
     netsocket/mbed_ipstack.h -> OnboardNetworkStack.h
     hal/emac_api.h -> EMAC.h
* Reinstate use of EthInterface abstraction
* Correct and clarify HW address EMAC ops
* Restore MBED_MAC_ADDR implementation
* Integrate PPP support with LWIP::Interface.
* Convert K64F lwIP driver to K64F_EMAC.

To do:

* Convert emac_stack_mem.h to follow this pattern.
* Figure out DEVICE_ETH/EMAC
* Update all drivers to use EMAC
pull/6847/head
Kevin Bracey 2017-08-15 15:55:30 +03:00
parent db73ed0751
commit 0386f73719
28 changed files with 3312 additions and 2732 deletions

View File

@ -2070,6 +2070,7 @@ PREDEFINED = DOXYGEN_ONLY \
DEVICE_CAN \
DEVICE_ETHERNET \
DEVICE_EMAC \
DEVICE_ETH \
DEVICE_FLASH \
DEVICE_I2C \
DEVICE_I2CSLAVE \

View File

@ -0,0 +1,613 @@
/* 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 "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->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);
}
netif_set_up(&netif);
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
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;
}

View File

@ -0,0 +1,182 @@
/* 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 "emac_stack_mem.h"
#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)
{
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_stack_mem_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_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));
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);
/* Interface capabilities */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
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

View File

@ -0,0 +1,670 @@
/* 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 "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;
}
sys_prot_t prot = sys_arch_protect();
LWIP &lwip = LWIP::get_instance();
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);
}
}
sys_arch_unprotect(prot);
}
#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();
}
/* 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::gethostbyname(const char *host, SocketAddress *address, nsapi_version_t version)
{
ip_addr_t lwip_addr;
#if LWIP_IPV4 && LWIP_IPV6
u8_t addr_type;
if (version == NSAPI_UNSPEC) {
const ip_addr_t *ip_addr = NULL;
if (default_interface) {
ip_addr = get_ip_addr(true, &default_interface->netif);
}
// Prefer IPv6
if (IP_IS_V6(ip_addr)) {
// If IPv4 is available use it as backup
if (get_ipv4_addr(&default_interface->netif)) {
addr_type = NETCONN_DNS_IPV6_IPV4;
} else {
addr_type = NETCONN_DNS_IPV6;
}
// Prefer IPv4
} else {
// If IPv6 is available use it as backup
if (get_ipv6_addr(&default_interface->netif)) {
addr_type = NETCONN_DNS_IPV4_IPV6;
} else {
addr_type = NETCONN_DNS_IPV4;
}
}
} else if (version == NSAPI_IPv4) {
addr_type = NETCONN_DNS_IPV4;
} else if (version == NSAPI_IPv6) {
addr_type = NETCONN_DNS_IPV6;
} else {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname_addrtype(host, &lwip_addr, addr_type);
#elif LWIP_IPV4
if (version != NSAPI_IPv4 && version != NSAPI_UNSPEC) {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname(host, &lwip_addr);
#elif LWIP_IPV6
if (version != NSAPI_IPv6 && version != NSAPI_UNSPEC) {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname(host, &lwip_addr);
#endif
if (err != ERR_OK) {
return NSAPI_ERROR_DNS_FAILURE;
}
nsapi_addr_t addr;
convert_lwip_addr_to_mbed(&addr, &lwip_addr);
address->set_addr(addr);
return 0;
}
nsapi_error_t LWIP::add_dns_server(const SocketAddress &address)
{
// Shift all dns servers down to give precedence to new server
for (int i = DNS_MAX_SERVERS-1; i > 0; i--) {
dns_setserver(i, dns_getserver(i-1));
}
nsapi_addr_t addr = address.get_addr();
ip_addr_t ip_addr;
if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) {
return NSAPI_ERROR_PARAMETER;
}
dns_setserver(0, &ip_addr);
return 0;
}
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);
sys_prot_t prot = sys_arch_protect();
#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
sys_arch_unprotect(prot);
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--;
sys_prot_t prot = sys_arch_protect();
#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
sys_arch_unprotect(prot);
}
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

View File

@ -0,0 +1,476 @@
/* 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 "emac_stack_mem.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"
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);
static err_t emac_low_level_output(struct netif *netif, struct pbuf *p);
void emac_input(emac_stack_mem_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);
union {
EMAC *emac; /**< HW specific emac implementation */
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;
};
/** 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);
/** 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:
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:
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;
};
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);
rtos::Semaphore tcpip_inited;
Interface *default_interface;
};
#endif /* LWIPSTACK_H_ */

View File

@ -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 */

View File

@ -14,12 +14,10 @@
* 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)
emac_stack_mem_t *emac_stack_mem_alloc(uint32_t size, uint32_t align)
{
struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, size + align, PBUF_RAM);
@ -42,34 +40,34 @@ emac_stack_mem_t *emac_stack_mem_alloc(emac_stack_t* stack, uint32_t size, uint3
return (emac_stack_mem_t*)pbuf;
}
void emac_stack_mem_free(emac_stack_t* stack, emac_stack_mem_t *mem)
void emac_stack_mem_free(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)
void emac_stack_mem_copy(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)
void *emac_stack_mem_ptr(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)
uint32_t emac_stack_mem_len(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)
void emac_stack_mem_set_len(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)
emac_stack_mem_t *emac_stack_mem_chain_dequeue(emac_stack_mem_chain_t **chain)
{
struct pbuf **list = (struct pbuf**)chain;
struct pbuf *head = *list;
@ -78,14 +76,18 @@ emac_stack_mem_t *emac_stack_mem_chain_dequeue(emac_stack_t* stack, emac_stack_m
return (emac_stack_mem_t *)head;
}
uint32_t emac_stack_mem_chain_len(emac_stack_t* stack, emac_stack_mem_chain_t *chain)
uint32_t emac_stack_mem_chain_len(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)
void emac_stack_mem_set_chain_len(emac_stack_mem_chain_t *chain, uint32_t len)
{
struct pbuf *pbuf = (struct pbuf*)chain;
pbuf->tot_len = len;
}
void emac_stack_mem_ref(emac_stack_mem_t *mem)
{
pbuf_ref((struct pbuf*)mem);
}
#endif /* DEVICE_EMAC */

View File

@ -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_

View File

@ -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 ------------------------------ */

View File

@ -0,0 +1,595 @@
/*
* Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
* Copyright (c) 2017 ARM Limited
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* o 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.
*
* o Neither the name of Freescale Semiconductor, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "cmsis_os.h"
#include "mbed_interface.h"
#include "emac_stack_mem.h"
#include "mbed_assert.h"
#include "netsocket/nsapi_types.h"
#include "mbed_shared_queues.h"
#if DEVICE_EMAC
#include "fsl_phy.h"
#include "k64f_emac_config.h"
#include "k64f_emac.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
emac_stack_mem_t *rx_buff[ENET_RX_RING_LEN];
// TX packet buffer pointers
emac_stack_mem_t *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 "C" void k64f_init_eth_hardware(void);
#endif
#if (defined(TARGET_K66F) && (defined(TARGET_FRDM)))
extern "C" void k66f_init_eth_hardware(void);
#endif
/* \brief Flags for worker thread */
#define FLAG_TX 1
#define FLAG_RX 2
/** \brief Driver thread priority */
#define THREAD_PRIORITY (osPriorityNormal)
#define PHY_TASK_PERIOD_MS 200
K64F_EMAC::K64F_EMAC() : xTXDCountSem(ENET_TX_RING_LEN, ENET_TX_RING_LEN), hwaddr()
{
}
static osThreadId_t create_new_thread(const char *threadName, void (*thread)(void *arg), void *arg, int stacksize, osPriority_t priority, os_thread_t *thread_cb)
{
osThreadAttr_t attr = {0};
attr.name = threadName;
attr.stack_mem = malloc(stacksize);
attr.cb_mem = thread_cb;
attr.stack_size = stacksize;
attr.cb_size = sizeof(os_thread_t);
attr.priority = priority;
return osThreadNew(thread, arg, &attr);
}
/********************************************************************************
* 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
*/
void K64F_EMAC::tx_reclaim()
{
/* Get exclusive access */
TXLockMutex.lock();
// Traverse all descriptors, looking for the ones modified by the uDMA
while((tx_consume_index != tx_produce_index) &&
(!(g_handle.txBdDirty->control & ENET_BUFFDESCRIPTOR_TX_READY_MASK))) {
emac_stack_mem_free(tx_buff[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++;
tx_consume_index += 1;
xTXDCountSem.release();
}
/* Restore access */
TXLockMutex.unlock();
}
/** \brief Ethernet receive interrupt handler
*
* This function handles the receive interrupt of K64F.
*/
void K64F_EMAC::rx_isr()
{
if (thread) {
osThreadFlagsSet(thread, FLAG_RX);
}
}
void K64F_EMAC::tx_isr()
{
osThreadFlagsSet(thread, FLAG_TX);
}
void K64F_EMAC::ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param)
{
K64F_EMAC *enet = static_cast<K64F_EMAC *>(param);
switch (event)
{
case kENET_RxEvent:
enet->rx_isr();
break;
case kENET_TxEvent:
enet->tx_isr();
break;
default:
break;
}
}
/** \brief Low level init of the MAC and PHY.
*/
bool K64F_EMAC::low_level_init_successful()
{
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 false;
// 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 false;
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] = emac_stack_mem_alloc(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT);
if (NULL == rx_buff[i])
return false;
rx_ptr[i] = (uint32_t*)emac_stack_mem_ptr(rx_buff[i]);
}
tx_consume_index = 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 = 0;
config.rxAccelerConfig = kENET_RxAccelMacCheckEnabled;
ENET_Init(ENET, &g_handle, &config, &buffCfg, 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, &K64F_EMAC::ethernet_callback, this);
ENET_ActiveRead(ENET);
return true;
}
/** \brief Allocates a emac_stack_mem_t and returns the data from the incoming packet.
*
* \param[in] idx index of packet to be read
* \return a emac_stack_mem_t filled with the received packet (including MAC header)
*/
emac_stack_mem_t *K64F_EMAC::low_level_input(int idx)
{
volatile enet_rx_bd_struct_t *bdPtr = g_handle.rxBdCurrent;
emac_stack_mem_t *p = NULL;
emac_stack_mem_t *temp_rxbuf = NULL;
uint32_t length = 0;
const uint16_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 */
TXLockMutex.lock();
#endif
/* Determine if a frame has been received */
if ((bdPtr->control & err_mask) != 0) {
/* 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];
emac_stack_mem_set_len(p, length);
/* Attempt to queue new buffer */
temp_rxbuf = emac_stack_mem_alloc(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT);
if (NULL == temp_rxbuf) {
/* Re-queue the same buffer */
update_read_buffer(NULL);
#ifdef LOCK_RX_THREAD
TXLockMutex.unlock();
#endif
return NULL;
}
rx_buff[idx] = temp_rxbuf;
rx_ptr[idx] = (uint32_t*)emac_stack_mem_ptr(rx_buff[idx]);
update_read_buffer((uint8_t*)rx_ptr[idx]);
/* Save size */
emac_stack_mem_set_chain_len(p, length);
}
#ifdef LOCK_RX_THREAD
osMutexRelease(TXLockMutex);
#endif
return p;
}
/** \brief Attempt to read a packet from the EMAC interface.
*
* \param[in] idx index of packet to be read
*/
void K64F_EMAC::input(int idx)
{
emac_stack_mem_t *p;
/* move received packet into a new buf */
p = low_level_input(idx);
if (p == NULL)
return;
emac_link_input_cb(p);
}
/** \brief Worker thread.
*
* Woken by thread flags to receive packets or clean up transmit
*
* \param[in] pvParameters pointer to the interface data
*/
void K64F_EMAC::thread_function(void* pvParameters)
{
struct K64F_EMAC *k64f_enet = static_cast<K64F_EMAC *>(pvParameters);
for (;;) {
uint32_t flags = osThreadFlagsWait(FLAG_RX|FLAG_TX, osFlagsWaitAny, osWaitForever);
MBED_ASSERT(!(flags & osFlagsError));
if (flags & FLAG_RX) {
k64f_enet->packet_rx();
}
if (flags & FLAG_TX) {
k64f_enet->packet_tx();
}
}
}
/** \brief Packet reception task
*
* This task is called when a packet is received. It will
* pass the packet to the LWIP core.
*/
void K64F_EMAC::packet_rx()
{
static int idx = 0;
while ((g_handle.rxBdCurrent->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK) == 0) {
input(idx);
idx = (idx + 1) % ENET_RX_RING_LEN;
}
}
/** \brief Transmit cleanup task
*
* This task is called when a transmit interrupt occurs and
* reclaims the buffer and descriptor used for the packet once
* the packet has been transferred.
*/
void K64F_EMAC::packet_tx()
{
tx_reclaim();
}
/** \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] buf 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
*/
bool K64F_EMAC::link_out(emac_stack_mem_chain_t *chain)
{
emac_stack_mem_t *q;
emac_stack_mem_t *temp_pbuf;
uint8_t *psend = NULL, *dst;
temp_pbuf = emac_stack_mem_alloc(emac_stack_mem_chain_len(chain), ENET_BUFF_ALIGNMENT);
if (NULL == temp_pbuf)
return false;
psend = (uint8_t*)emac_stack_mem_ptr(temp_pbuf);
for (q = emac_stack_mem_chain_dequeue(&chain), dst = psend; q != NULL; q = emac_stack_mem_chain_dequeue(&chain)) {
memcpy(dst, emac_stack_mem_ptr(q), emac_stack_mem_len(q));
dst += emac_stack_mem_len(q);
}
/* Check if a descriptor is available for the transfer. */
if (xTXDCountSem.wait(0) == 0)
return false;
/* Get exclusive access */
TXLockMutex.lock();
/* Save the buffer so that it can be freed when transmit is done */
tx_buff[tx_produce_index % ENET_TX_RING_LEN] = temp_pbuf;
tx_produce_index += 1;
/* Setup transfers */
g_handle.txBdCurrent->buffer = psend;
g_handle.txBdCurrent->length = emac_stack_mem_len(temp_pbuf);
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;
/* Restore access */
TXLockMutex.unlock();
return true;
}
/*******************************************************************************
* PHY task: monitor link
*******************************************************************************/
#define STATE_UNKNOWN (-1)
int phy_link_status(void) {
bool connection_status;
uint32_t phyAddr = 0;
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
return (int)connection_status;
}
void K64F_EMAC::phy_task()
{
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) {
emac_link_state_cb(crt_state.connected);
}
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;
}
bool K64F_EMAC::power_up()
{
/* Initialize the hardware */
if (!low_level_init_successful())
return false;
/* Worker thread */
thread = create_new_thread("k64f_emac_thread", &K64F_EMAC::thread_function, this, THREAD_STACKSIZE, THREAD_PRIORITY, &thread_cb);
/* Trigger thread to deal with any RX packets that arrived before thread was started */
rx_isr();
/* PHY monitoring task */
prev_state.connected = STATE_UNKNOWN;
prev_state.speed = (phy_speed_t)STATE_UNKNOWN;
prev_state.duplex = (phy_duplex_t)STATE_UNKNOWN;
phy_task_handle = mbed::mbed_event_queue()->call_every(PHY_TASK_PERIOD_MS, mbed::callback(this, &K64F_EMAC::phy_task));
/* Allow the PHY task to detect the initial link state and set up the proper flags */
osDelay(10);
return true;
}
uint32_t K64F_EMAC::get_mtu_size() const
{
return K64_ETH_MTU_SIZE;
}
void K64F_EMAC::get_ifname(char *name, uint8_t size) const
{
memcpy(name, K64_ETH_IF_NAME, (size < sizeof(K64_ETH_IF_NAME)) ? size : sizeof(K64_ETH_IF_NAME));
}
uint8_t K64F_EMAC::get_hwaddr_size() const
{
return K64F_HWADDR_SIZE;
}
bool K64F_EMAC::get_hwaddr(uint8_t *addr) const
{
return false;
}
void K64F_EMAC::set_hwaddr(const uint8_t *addr)
{
memcpy(hwaddr, addr, sizeof hwaddr);
ENET_SetMacAddr(ENET, const_cast<uint8_t*>(addr));
}
void K64F_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
{
emac_link_input_cb = input_cb;
}
void K64F_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
{
emac_link_state_cb = state_cb;
}
void K64F_EMAC::add_multicast_group(uint8_t *addr)
{
ENET_AddMulticastGroup(ENET, addr);
}
void K64F_EMAC::power_down()
{
/* No-op at this stage */
}
K64F_EMAC &K64F_EMAC::get_instance() {
static K64F_EMAC emac;
return emac;
}
// Weak so a module can override
MBED_WEAK EMAC &EMAC::get_default_instance() {
return K64F_EMAC::get_instance();
}
/**
* @}
*/
#endif // DEVICE_EMAC
/* --------------------------------- End Of File ------------------------------ */

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2017 ARM Limited. All rights reserved.
*/
#ifndef K64F_EMAC_H_
#define K64F_EMAC_H_
#include "EMAC.h"
#include "rtos/Semaphore.h"
#include "rtos/Mutex.h"
class K64F_EMAC : public EMAC {
public:
K64F_EMAC();
static K64F_EMAC &get_instance();
/**
* Return maximum transmission unit
*
* @return MTU in bytes
*/
virtual uint32_t get_mtu_size() const;
/**
* 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;
/**
* Returns size of the underlying interface HW address size.
*
* @return HW address size in bytes
*/
virtual uint8_t get_hwaddr_size() const;
/**
* 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;
/**
* 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);
/**
* 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_stack_mem_chain_t *buf);
/**
* Initializes the HW
*
* @return True on success, False in case of an error.
*/
virtual bool power_up();
/**
* Deinitializes the HW
*
*/
virtual void power_down();
/**
* 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);
/**
* 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);
/** Add device to a multicast group
*
* @param address A multicast group hardware address
*/
virtual void add_multicast_group(uint8_t *address);
private:
bool low_level_init_successful();
void rx_isr();
void tx_isr();
void packet_rx();
void packet_tx();
void tx_reclaim();
void input(int idx);
emac_stack_mem_t *low_level_input(int idx);
static void thread_function(void* pvParameters);
void phy_task();
static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param);
os_thread_t thread_cb;
osThreadId_t thread; /**< Processing thread */
rtos::Mutex TXLockMutex;/**< TX critical section mutex */
rtos::Semaphore xTXDCountSem; /**< TX free buffer counting semaphore */
uint8_t tx_consume_index, tx_produce_index; /**< TX buffers ring */
emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming data */
emac_link_state_change_cb_t emac_link_state_cb; /**< Link state change callback */
int phy_task_handle; /**< Handle for phy task event */
struct PHY_STATE {
int connected;
phy_speed_t speed;
phy_duplex_t duplex;
};
PHY_STATE prev_state;
uint8_t hwaddr[K64F_HWADDR_SIZE];
};
#endif /* K64F_EMAC_H_ */

View File

@ -37,15 +37,12 @@
#define ENET_ETH_MAX_FLEN (1522) // recommended size for a VLAN frame
#if defined(__cplusplus)
extern "C" {
#endif
#define K64F_HWADDR_SIZE (6)
int phy_link_status(void);
#define K64_ETH_MTU_SIZE 1500
#define K64_ETH_IF_NAME "en"
#if defined(__cplusplus)
}
#endif
#define THREAD_STACKSIZE 512
#endif // #define K64F_EMAC_CONFIG_H__

View File

@ -22,7 +22,6 @@
#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)

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1,191 @@
/* 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()
{
sys_prot_t prot = sys_arch_protect();
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;
sys_arch_unprotect(prot);
return s;
}
}
sys_arch_unprotect(prot);
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;
}

View File

@ -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;
}
}

View File

@ -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
}

156
features/netsocket/EMAC.h Normal file
View File

@ -0,0 +1,156 @@
/* 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 "emac_stack_mem.h"
/**
* 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_stack_mem_chain_t *buf);
typedef mbed::Callback<void (emac_stack_mem_chain_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;
/**
* 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_stack_mem_chain_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(uint8_t *address) = 0;
};
/** These need to be defined by targets wishing to provide an Ethernet driver using EMAC interface. It will
* be used by the EthernetInterface 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 */

View File

@ -15,21 +15,20 @@
*/
#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)
EthernetInterface::EthernetInterface(EMAC &emac, OnboardNetworkStack &stack) :
_emac(emac),
_stack(stack),
_interface(NULL),
_dhcp(true),
_blocking(true),
_ip_address(),
_netmask(),
_gateway()
{
}
nsapi_error_t EthernetInterface::set_network(const char *ip_address, const char *netmask, const char *gateway)
{
_dhcp = false;
@ -52,26 +51,39 @@ nsapi_error_t EthernetInterface::set_dhcp(bool dhcp)
nsapi_error_t EthernetInterface::connect()
{
return mbed_lwip_bringup_2(_dhcp, false,
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);
DEFAULT_STACK,
_blocking);
}
nsapi_error_t EthernetInterface::disconnect()
{
return mbed_lwip_bringdown_2(false);
return _interface->bringdown();
}
const char *EthernetInterface::get_mac_address()
{
return mbed_lwip_get_mac_address();
if (_interface->get_mac_address(_mac_address, sizeof(_mac_address))) {
return _mac_address;
}
return NULL;
}
const char *EthernetInterface::get_ip_address()
{
if (mbed_lwip_get_ip_address(_ip_address, sizeof _ip_address)) {
if (_interface->get_ip_address(_ip_address, sizeof(_ip_address))) {
return _ip_address;
}
@ -80,7 +92,7 @@ const char *EthernetInterface::get_ip_address()
const char *EthernetInterface::get_netmask()
{
if (mbed_lwip_get_netmask(_netmask, sizeof _netmask)) {
if (_interface->get_netmask(_netmask, sizeof(_netmask))) {
return _netmask;
}
@ -89,7 +101,7 @@ const char *EthernetInterface::get_netmask()
const char *EthernetInterface::get_gateway()
{
if (mbed_lwip_get_gateway(_gateway, sizeof _gateway)) {
if (_interface->get_gateway(_gateway, sizeof(_gateway))) {
return _gateway;
}
@ -98,34 +110,29 @@ const char *EthernetInterface::get_gateway()
NetworkStack *EthernetInterface::get_stack()
{
return nsapi_create_stack(&lwip_stack);
return &_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);
if (_interface) {
_interface->attach(status_cb);
}
}
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);
}
if (_interface) {
return _interface->get_connection_status();
} else {
return NSAPI_STATUS_DISCONNECTED;
}
}
nsapi_error_t EthernetInterface::set_blocking(bool blocking)
{
mbed_lwip_set_blocking(blocking);
_blocking = blocking;
return NSAPI_ERROR_OK;
}

View File

@ -19,21 +19,32 @@
#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
* Implementation of the NetworkStack for an EMAC-based driver
*/
class EthernetInterface : public EthInterface
{
public:
/** EthernetInterface lifetime
/** 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();
EthernetInterface(
EMAC &emac = EMAC::get_default_instance(),
OnboardNetworkStack &stack = OnboardNetworkStack::get_default_instance());
/** Set a static IP address
*
@ -41,10 +52,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);
@ -127,16 +138,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

View File

@ -20,8 +20,12 @@
#include "stddef.h"
#include <new>
// Default NetworkStack operations
const char *NetworkStack::get_ip_address()
{
return 0;
}
nsapi_error_t NetworkStack::gethostbyname(const char *name, SocketAddress *address, nsapi_version_t version)
{
// check for simple ip addresses

View File

@ -37,11 +37,14 @@ public:
virtual ~NetworkStack() {};
/** Get the local IP address
* @deprecated
*
* @return Null-terminated representation of the local IP address
* or null if not yet connected
*/
virtual const char *get_ip_address() = 0;
MBED_DEPRECATED_SINCE("mbed-os-5.7",
"Use NetworkInterface::get_ip_address()")
virtual const char *get_ip_address();
/** Translates a hostname to an IP address with specific version
*

View File

@ -0,0 +1,138 @@
/* mbed OS IP stack API
* Copyright (c) 2015-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 MBED_IPSTACK_H
#define MBED_IPSTACK_H
#include "nsapi.h"
#include "NetworkStack.h"
#include "EMAC.h"
/**
* mbed OS API for onboard IP stack abstraction
*
* This interface should be used by targets to initialize IP stack, create, bring up and bring down network interfaces.
*
* An onboard network stack has the potential ability to register interfaces
* such as through EMAC, and has its own interface identifiers.
*/
class OnboardNetworkStack : public NetworkStack {
public:
/** Return the default on-board network stack
*
* Returns the default on-board network stack, as configured by
* JSON option nsapi.default-stack.
*/
static OnboardNetworkStack &get_default_instance();
/** Representation of a stack's view of an interface.
*
* Provides facilities required by a driver to implement the application
* NetworkInterface API.
*/
class Interface {
public:
virtual ~Interface() {}
/** 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) = 0;
/** Disconnect interface from the network
*
* After this call the network interface is inactive, to use it again user needs to call @n bringup again.
*
* @return NSAPI_ERROR_OK on success, or error code
*/
virtual nsapi_error_t bringdown() = 0;
/** 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) = 0;
/** Get the connection status
*
* @return The connection status according to ConnectionStatusType
*/
virtual nsapi_connection_status_t get_connection_status() const = 0;
/** 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) = 0;
/** Copies IP address of the network interface to user supplied buffer
*
* @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) = 0;
/** 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) = 0;
/** 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) = 0;
};
/** 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, Interface **interface_out) = 0;
};
#endif /* MBED_IPSTACK_H */

View File

@ -16,10 +16,12 @@
#ifndef MBED_EMAC_STACK_MEM_H
#define MBED_EMAC_STACK_MEM_H
#if DEVICE_EMAC
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Stack memory module
*
@ -29,88 +31,81 @@
*/
typedef void emac_stack_mem_t;
typedef void emac_stack_mem_chain_t;
typedef void emac_stack_t;
/**
* Allocates stack memory
*
* @param stack Emac stack context
* @param size Size of memory to allocate
* @param align Memory alignment requirements
* @return Allocated memory struct, or NULL in case of error
*/
emac_stack_mem_t *emac_stack_mem_alloc(emac_stack_t* stack, uint32_t size, uint32_t align);
emac_stack_mem_t *emac_stack_mem_alloc(uint32_t size, uint32_t align);
/**
* Free memory allocated using @a stack_mem_alloc
*
* @param stack Emac stack context
* @param mem Memory to be freed
*/
void emac_stack_mem_free(emac_stack_t* stack, emac_stack_mem_t *mem);
void emac_stack_mem_free(emac_stack_mem_t *mem);
/**
* Copy memory
*
* @param stack Emac stack context
* @param to Memory to copy to
* @param from Memory to copy from
*/
void emac_stack_mem_copy(emac_stack_t* stack, emac_stack_mem_t *to, emac_stack_mem_t *from);
void emac_stack_mem_copy(emac_stack_mem_t *to, emac_stack_mem_t *from);
/**
* Return pointer to the payload
*
* @param stack Emac stack context
* @param mem Memory structure
* @return Pointer to the payload
*/
void *emac_stack_mem_ptr(emac_stack_t* stack, emac_stack_mem_t *mem);
void *emac_stack_mem_ptr(emac_stack_mem_t *mem);
/**
* Return actual payload size
*
* @param stack Emac stack context
* @param mem Memory structure
* @return Size in bytes
*/
uint32_t emac_stack_mem_len(emac_stack_t* stack, emac_stack_mem_t *mem);
uint32_t emac_stack_mem_len(emac_stack_mem_t *mem);
/**
* Sets the actual payload size (the allocated payload size will not change)
*
* @param stack Emac stack context
* @param mem Memory structure
* @param len Actual payload size
*/
void emac_stack_mem_set_len(emac_stack_t* stack, emac_stack_mem_t *mem, uint32_t len);
void emac_stack_mem_set_len(emac_stack_mem_t *mem, uint32_t len);
/**
* Returns first memory structure from the list and move the head to point to the next node
*
* @param stack Emac stack context
* @param chain Pointer to the list
* @return First memory structure from the list
*/
emac_stack_mem_t *emac_stack_mem_chain_dequeue(emac_stack_t* stack, emac_stack_mem_chain_t **chain);
emac_stack_mem_t *emac_stack_mem_chain_dequeue(emac_stack_mem_chain_t **chain);
/**
* Return total length of the memory chain
*
* @param stack Emac stack context
* @param chain Memory chain
* @return Chain length
*/
uint32_t emac_stack_mem_chain_len(emac_stack_t* stack, emac_stack_mem_chain_t *chain);
uint32_t emac_stack_mem_chain_len(emac_stack_mem_chain_t *chain);
/**
* Increases the reference counter for the memory
*
* @param stack Emac stack context
* @param mem Memory structure
*/
void emac_stack_mem_ref(emac_stack_t* stack, emac_stack_mem_t *mem);
* Set total length of the memory chain
*
* @param chain Memory chain
* @param len Total chain length
*/
void emac_stack_mem_set_chain_len(emac_stack_mem_chain_t *chain, uint32_t len);
#endif /* DEVICE_EMAC */
#ifdef __cplusplus
}
#endif
#endif /* EMAC_MBED_STACK_MEM_h */

View File

@ -1,6 +1,7 @@
{
"name": "nsapi",
"config": {
"present": 1
"present": 1,
"default-stack": "LWIP"
}
}

View File

@ -1,160 +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.
*/
#ifndef MBED_EMAC_API_H
#define MBED_EMAC_API_H
#if DEVICE_EMAC
#include <stdbool.h>
#include "emac_stack_mem.h"
typedef struct emac_interface emac_interface_t;
/**
* EmacInterface
*
* This interface should be used to abstract low level access to networking hardware
*/
/**
* Callback to be register with Emac interface and to be called fore received packets
*
* @param data Arbitrary user data (IP stack)
* @param buf Received data
*/
typedef void (*emac_link_input_fn)(void *data, emac_stack_mem_chain_t *buf);
/**
* Callback to be register with Emac interface and to be called for link status changes
*
* @param data Arbitrary user data (IP stack)
* @param up Link status
*/
typedef void (*emac_link_state_change_fn)(void *data, bool up);
/**
* Return maximum transmission unit
*
* @param emac Emac interface
* @return MTU in bytes
*/
typedef uint32_t (*emac_get_mtu_size_fn)(emac_interface_t *emac);
/**
* Return interface name
*
* @param emac Emac interface
* @param name Pointer to where the name should be written
* @param size Maximum number of character to copy
*/
typedef void (*emac_get_ifname_fn)(emac_interface_t *emac, char *name, uint8_t size);
/**
* Returns size of the underlying interface HW address size
*
* @param emac Emac interface
* @return HW address size in bytes
*/
typedef uint8_t (*emac_get_hwaddr_size_fn)(emac_interface_t *emac);
/**
* Return interface hw address
*
* Copies HW address to provided memory, @param addr has to be of correct size see @a get_hwaddr_size
*
* @param emac Emac interface
* @param addr HW address for underlying interface
*/
typedef void (*emac_get_hwaddr_fn)(emac_interface_t *emac, uint8_t *addr);
/**
* Set HW address for interface
*
* Provided address has to be of correct size, see @a get_hwaddr_size
*
* @param emac Emac interface
* @param addr Address to be set
*/
typedef void (*emac_set_hwaddr_fn)(emac_interface_t *emac, uint8_t *addr);
/**
* Sends the packet over the link
*
* That can not be called from an interrupt context.
*
* @param emac Emac interface
* @param buf Packet to be send
* @return True if the packet was send successfully, False otherwise
*/
typedef bool (*emac_link_out_fn)(emac_interface_t *emac, emac_stack_mem_t *buf);
/**
* Initializes the HW
*
* @return True on success, False in case of an error.
*/
typedef bool (*emac_power_up_fn)(emac_interface_t *emac);
/**
* Deinitializes the HW
*
* @param emac Emac interface
*/
typedef void (*emac_power_down_fn)(emac_interface_t *emac);
/**
* Sets a callback that needs to be called for packets received for that interface
*
* @param emac Emac interface
* @param input_cb Function to be register as a callback
* @param data Arbitrary user data to be passed to the callback
*/
typedef void (*emac_set_link_input_cb_fn)(emac_interface_t *emac, emac_link_input_fn input_cb, void *data);
/**
* Sets a callback that needs to be called on link status changes for given interface
*
* @param emac Emac interface
* @param state_cb Function to be register as a callback
* @param data Arbitrary user data to be passed to the callback
*/
typedef void (*emac_set_link_state_cb_fn)(emac_interface_t *emac, emac_link_state_change_fn state_cb, void *data);
typedef struct emac_interface_ops {
emac_get_mtu_size_fn get_mtu_size;
emac_get_ifname_fn get_ifname;
emac_get_hwaddr_size_fn get_hwaddr_size;
emac_get_hwaddr_fn get_hwaddr;
emac_set_hwaddr_fn set_hwaddr;
emac_link_out_fn link_out;
emac_power_up_fn power_up;
emac_power_down_fn power_down;
emac_set_link_input_cb_fn set_link_input_cb;
emac_set_link_state_cb_fn set_link_state_cb;
} emac_interface_ops_t;
typedef struct emac_interface {
const emac_interface_ops_t ops;
void *hw;
} emac_interface_t;
#else
typedef void *emac_interface_t;
#endif /* DEVICE_EMAC */
#endif /* MBED_EMAC_API_H */

View File

@ -615,7 +615,7 @@
"macros": ["CPU_MK64FN1M0VMD12", "FSL_RTOS_MBED"],
"inherits": ["Target"],
"detect_code": ["0240"],
"device_has": ["ANALOGIN", "ANALOGOUT", "I2C", "I2CSLAVE", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_FC", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE", "STDIO_MESSAGES", "STORAGE", "TRNG", "FLASH"],
"device_has": ["ANALOGIN", "ANALOGOUT", "EMAC", "I2C", "I2CSLAVE", "INTERRUPTIN", "LOWPOWERTIMER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_FC", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPI_ASYNCH", "SPISLAVE", "STDIO_MESSAGES", "STORAGE", "TRNG", "FLASH"],
"features": ["LWIP", "STORAGE"],
"release_versions": ["2", "5"],
"device_name": "MK64FN1M0xxx12",
@ -679,7 +679,7 @@
"macros": ["CPU_MK66FN2M0VMD18", "FSL_RTOS_MBED"],
"inherits": ["Target"],
"detect_code": ["0311"],
"device_has": ["ANALOGIN", "ANALOGOUT", "I2C", "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES", "TRNG", "FLASH"],
"device_has": ["ANALOGIN", "ANALOGOUT", "EMAC", "I2C", "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES", "TRNG", "FLASH"],
"features": ["LWIP"],
"release_versions": ["2", "5"],
"device_name": "MK66FN2M0xxx18",