mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			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 EMACpull/6847/head
							parent
							
								
									db73ed0751
								
							
						
					
					
						commit
						0386f73719
					
				| 
						 | 
				
			
			@ -2070,6 +2070,7 @@ PREDEFINED             = DOXYGEN_ONLY            \
 | 
			
		|||
                         DEVICE_CAN              \
 | 
			
		||||
                         DEVICE_ETHERNET         \
 | 
			
		||||
                         DEVICE_EMAC             \
 | 
			
		||||
                         DEVICE_ETH              \
 | 
			
		||||
                         DEVICE_FLASH            \
 | 
			
		||||
                         DEVICE_I2C              \
 | 
			
		||||
                         DEVICE_I2CSLAVE         \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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_
 | 
			
		||||
| 
						 | 
				
			
			@ -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 ------------------------------ */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 ------------------------------ */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -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__
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
											
										
									
								
							| 
						 | 
				
			
			@ -1,52 +0,0 @@
 | 
			
		|||
/* LWIP implementation of NetworkInterfaceAPI
 | 
			
		||||
 * Copyright (c) 2015 ARM Limited
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef LWIP_STACK_H
 | 
			
		||||
#define LWIP_STACK_H
 | 
			
		||||
 | 
			
		||||
#include "nsapi.h"
 | 
			
		||||
#include "emac_api.h"
 | 
			
		||||
#include "lwip/opt.h"
 | 
			
		||||
#include "netif.h"
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Access to lwip through the nsapi - be wary of API changes as external 1st-generation EMAC
 | 
			
		||||
// drivers attach through these.
 | 
			
		||||
nsapi_error_t mbed_lwip_init(emac_interface_t *emac);
 | 
			
		||||
nsapi_error_t mbed_lwip_emac_init(emac_interface_t *emac);
 | 
			
		||||
nsapi_connection_status_t mbed_lwip_netif_status_check(void);
 | 
			
		||||
nsapi_error_t mbed_lwip_bringup(bool dhcp, const char *ip, const char *netmask, const char *gw);
 | 
			
		||||
nsapi_error_t mbed_lwip_bringup_2(bool dhcp, bool ppp, const char *ip, const char *netmask, const char *gw, 
 | 
			
		||||
    const nsapi_ip_stack_t stack);
 | 
			
		||||
typedef void (*mbed_lwip_client_callback)(void *ethernet_if_ptr, nsapi_event_t reason, intptr_t parameter);
 | 
			
		||||
void mbed_lwip_attach(mbed_lwip_client_callback status_cb, void *status_cb_handle);
 | 
			
		||||
void mbed_lwip_set_blocking(bool blocking);
 | 
			
		||||
nsapi_error_t mbed_lwip_bringdown(void);
 | 
			
		||||
nsapi_error_t mbed_lwip_bringdown_2(bool ppp);
 | 
			
		||||
const char *mbed_lwip_get_mac_address(void);
 | 
			
		||||
char *mbed_lwip_get_ip_address(char *buf, nsapi_size_t buflen);
 | 
			
		||||
char *mbed_lwip_get_netmask(char *buf, nsapi_size_t buflen);
 | 
			
		||||
char *mbed_lwip_get_gateway(char *buf, nsapi_size_t buflen);
 | 
			
		||||
 | 
			
		||||
extern nsapi_stack_t lwip_stack;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ extern "C" { // "pppos.h" is missing extern C
 | 
			
		|||
 | 
			
		||||
#include "nsapi_ppp.h"
 | 
			
		||||
#include "ppp_lwip.h"
 | 
			
		||||
#include "lwip_stack.h"
 | 
			
		||||
#include "LWIPStack.h"
 | 
			
		||||
 | 
			
		||||
namespace mbed {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,8 +48,10 @@ static nsapi_error_t connect_error_code;
 | 
			
		|||
 | 
			
		||||
// Just one interface for now
 | 
			
		||||
static FileHandle *my_stream;
 | 
			
		||||
static LWIP::Interface *my_interface;
 | 
			
		||||
static ppp_pcb *my_ppp_pcb;
 | 
			
		||||
static bool ppp_active = false;
 | 
			
		||||
static bool blocking_connect = true;
 | 
			
		||||
static const char *login;
 | 
			
		||||
static const char *pwd;
 | 
			
		||||
static sys_sem_t ppp_close_sem;
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +277,7 @@ static void stream_cb() {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" err_t ppp_lwip_connect()
 | 
			
		||||
extern "C" err_t ppp_lwip_connect(void *pcb)
 | 
			
		||||
{
 | 
			
		||||
#if PPP_AUTH_SUPPORT
 | 
			
		||||
   ppp_set_auth(my_ppp_pcb, PPPAUTHTYPE_ANY, login, pwd);
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +293,7 @@ extern "C" err_t ppp_lwip_connect()
 | 
			
		|||
   return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" err_t ppp_lwip_disconnect()
 | 
			
		||||
extern "C" err_t ppp_lwip_disconnect(void *pcb)
 | 
			
		||||
{
 | 
			
		||||
    err_t ret = ppp_close(my_ppp_pcb, 0);
 | 
			
		||||
    if (ret != ERR_OK) {
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +311,7 @@ extern "C" err_t ppp_lwip_disconnect()
 | 
			
		|||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack)
 | 
			
		||||
extern "C" nsapi_error_t ppp_lwip_if_init(void *pcb, struct netif *netif, const nsapi_ip_stack_t stack)
 | 
			
		||||
{
 | 
			
		||||
    if (!prepare_event_queue()) {
 | 
			
		||||
        return NSAPI_ERROR_NO_MEMORY;
 | 
			
		||||
| 
						 | 
				
			
			@ -349,7 +351,7 @@ nsapi_error_t nsapi_ppp_error_code()
 | 
			
		|||
 | 
			
		||||
nsapi_error_t nsapi_ppp_set_blocking(bool blocking)
 | 
			
		||||
{
 | 
			
		||||
    mbed_lwip_set_blocking(blocking);
 | 
			
		||||
    blocking_connect = blocking;
 | 
			
		||||
    return NSAPI_ERROR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -364,9 +366,20 @@ nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_event_t,
 | 
			
		|||
    login = uname;
 | 
			
		||||
    pwd = password;
 | 
			
		||||
 | 
			
		||||
    nsapi_error_t retcode;
 | 
			
		||||
 | 
			
		||||
    if (!my_interface) {
 | 
			
		||||
        LWIP &lwip = LWIP::get_instance();
 | 
			
		||||
        retcode = lwip._add_ppp_interface(stream, true, &my_interface);
 | 
			
		||||
        if (retcode != NSAPI_ERROR_OK) {
 | 
			
		||||
            my_interface = NULL;
 | 
			
		||||
            return retcode;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // mustn't start calling input until after connect -
 | 
			
		||||
    // attach deferred until ppp_lwip_connect, called from mbed_lwip_bringup
 | 
			
		||||
    nsapi_error_t retcode = mbed_lwip_bringup_2(false, true, NULL, NULL, NULL, stack);
 | 
			
		||||
    retcode = my_interface->bringup(false, NULL, NULL, NULL, stack, blocking_connect);
 | 
			
		||||
 | 
			
		||||
    if (retcode != NSAPI_ERROR_OK && connect_error_code != NSAPI_ERROR_OK) {
 | 
			
		||||
        return connect_error_code;
 | 
			
		||||
| 
						 | 
				
			
			@ -377,12 +390,12 @@ nsapi_error_t nsapi_ppp_connect(FileHandle *stream, Callback<void(nsapi_event_t,
 | 
			
		|||
 | 
			
		||||
nsapi_error_t nsapi_ppp_disconnect(FileHandle *stream)
 | 
			
		||||
{
 | 
			
		||||
    return mbed_lwip_bringdown_2(true);
 | 
			
		||||
    return my_interface->bringdown();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NetworkStack *nsapi_ppp_get_stack()
 | 
			
		||||
{
 | 
			
		||||
    return nsapi_create_stack(&lwip_stack);
 | 
			
		||||
    return &LWIP::get_instance();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
 | 
			
		||||
| 
						 | 
				
			
			@ -391,7 +404,7 @@ const char *nsapi_ppp_get_ip_addr(FileHandle *stream)
 | 
			
		|||
 | 
			
		||||
    if (stream == my_stream) {
 | 
			
		||||
 | 
			
		||||
        if (mbed_lwip_get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
 | 
			
		||||
        if (my_interface->get_ip_address(ip_addr, IPADDR_STRLEN_MAX)) {
 | 
			
		||||
            return ip_addr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +419,7 @@ const char *nsapi_ppp_get_netmask(FileHandle *stream)
 | 
			
		|||
 | 
			
		||||
    static char netmask[IPADDR_STRLEN_MAX];
 | 
			
		||||
    if (stream == my_stream) {
 | 
			
		||||
        if (mbed_lwip_get_netmask(netmask, IPADDR_STRLEN_MAX)) {
 | 
			
		||||
        if (my_interface->get_netmask(netmask, IPADDR_STRLEN_MAX)) {
 | 
			
		||||
            return netmask;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -421,7 +434,7 @@ const char *nsapi_ppp_get_gw_addr(FileHandle *stream)
 | 
			
		|||
 | 
			
		||||
    static char gwaddr[IPADDR_STRLEN_MAX];
 | 
			
		||||
    if (stream == my_stream) {
 | 
			
		||||
        if (mbed_lwip_get_gateway(gwaddr, IPADDR_STRLEN_MAX)) {
 | 
			
		||||
        if (my_interface->get_netmask(gwaddr, IPADDR_STRLEN_MAX)) {
 | 
			
		||||
            return gwaddr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ extern "C" {
 | 
			
		|||
 *
 | 
			
		||||
 *  @return 0 for success and negative error codes for failure
 | 
			
		||||
 */
 | 
			
		||||
nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack);
 | 
			
		||||
nsapi_error_t ppp_lwip_if_init(void *pcb, struct netif *netif,  nsapi_ip_stack_t stack);
 | 
			
		||||
 | 
			
		||||
/** Connects to a PPP pipe
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ nsapi_error_t ppp_lwip_if_init(struct netif *netif, const nsapi_ip_stack_t stack
 | 
			
		|||
 *
 | 
			
		||||
 *  @return 0 for success and negative error codes for failure
 | 
			
		||||
 */
 | 
			
		||||
err_t ppp_lwip_connect(void);
 | 
			
		||||
err_t ppp_lwip_connect(void *pcb);
 | 
			
		||||
 | 
			
		||||
/** Disconnects from a PPP pipe
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -48,14 +48,14 @@ err_t ppp_lwip_connect(void);
 | 
			
		|||
 *
 | 
			
		||||
 *  @return 0 for success and negative error codes for failure
 | 
			
		||||
 */
 | 
			
		||||
err_t ppp_lwip_disconnect(void);
 | 
			
		||||
err_t ppp_lwip_disconnect(void *pcb);
 | 
			
		||||
#else
 | 
			
		||||
/**
 | 
			
		||||
 * Stubs in case LWIP PPP is not enabled
 | 
			
		||||
 */
 | 
			
		||||
#define ppp_lwip_if_init(netif, stack)     NSAPI_ERROR_UNSUPPORTED
 | 
			
		||||
#define ppp_lwip_connect()                 ERR_IF
 | 
			
		||||
#define ppp_lwip_disconnect()              ERR_IF
 | 
			
		||||
#define ppp_lwip_if_init(pcb, netif, stack) NSAPI_ERROR_UNSUPPORTED
 | 
			
		||||
#define ppp_lwip_connect(pcb)               ERR_IF
 | 
			
		||||
#define ppp_lwip_disconnect(pcb)            ERR_IF
 | 
			
		||||
#endif //LWIP_PPP_API
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -15,21 +15,20 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
#include "EthernetInterface.h"
 | 
			
		||||
#include "lwip_stack.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Interface implementation */
 | 
			
		||||
EthernetInterface::EthernetInterface() : 
 | 
			
		||||
EthernetInterface::EthernetInterface(EMAC &emac, OnboardNetworkStack &stack) :
 | 
			
		||||
    _emac(emac),
 | 
			
		||||
    _stack(stack),
 | 
			
		||||
    _interface(NULL),
 | 
			
		||||
    _dhcp(true),
 | 
			
		||||
    _blocking(true),
 | 
			
		||||
    _ip_address(),
 | 
			
		||||
    _netmask(),
 | 
			
		||||
    _gateway(),
 | 
			
		||||
    _connection_status_cb(NULL),
 | 
			
		||||
    _connect_status(NSAPI_STATUS_DISCONNECTED)
 | 
			
		||||
    _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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
* Set total length of the memory chain 
 | 
			
		||||
* 
 | 
			
		||||
* @param  chain Memory chain 
 | 
			
		||||
* @param  len   Total chain length 
 | 
			
		||||
 */ 
 | 
			
		||||
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); 
 | 
			
		||||
 | 
			
		||||
#endif /* DEVICE_EMAC */
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* EMAC_MBED_STACK_MEM_h */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
    "name": "nsapi",
 | 
			
		||||
    "config": {
 | 
			
		||||
        "present": 1
 | 
			
		||||
        "present": 1,
 | 
			
		||||
        "default-stack": "LWIP"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										160
									
								
								hal/emac_api.h
								
								
								
								
							
							
						
						
									
										160
									
								
								hal/emac_api.h
								
								
								
								
							| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue