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() :
|
||||
_dhcp(true),
|
||||
_ip_address(),
|
||||
_netmask(),
|
||||
_gateway(),
|
||||
_connection_status_cb(NULL),
|
||||
_connect_status(NSAPI_STATUS_DISCONNECTED)
|
||||
EthernetInterface::EthernetInterface(EMAC &emac, OnboardNetworkStack &stack) :
|
||||
_emac(emac),
|
||||
_stack(stack),
|
||||
_interface(NULL),
|
||||
_dhcp(true),
|
||||
_blocking(true),
|
||||
_ip_address(),
|
||||
_netmask(),
|
||||
_gateway()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsapi_error_t EthernetInterface::set_network(const char *ip_address, const char *netmask, const char *gateway)
|
||||
{
|
||||
_dhcp = false;
|
||||
|
@ -52,26 +51,39 @@ nsapi_error_t EthernetInterface::set_dhcp(bool dhcp)
|
|||
|
||||
nsapi_error_t EthernetInterface::connect()
|
||||
{
|
||||
return mbed_lwip_bringup_2(_dhcp, false,
|
||||
if (!_interface) {
|
||||
nsapi_error_t err = _stack.add_ethernet_interface(_emac, true, &_interface);
|
||||
if (err != NSAPI_ERROR_OK) {
|
||||
_interface = NULL;
|
||||
return err;
|
||||
}
|
||||
_interface->attach(_connection_status_cb);
|
||||
}
|
||||
|
||||
return _interface->bringup(_dhcp,
|
||||
_ip_address[0] ? _ip_address : 0,
|
||||
_netmask[0] ? _netmask : 0,
|
||||
_gateway[0] ? _gateway : 0,
|
||||
DEFAULT_STACK);
|
||||
DEFAULT_STACK,
|
||||
_blocking);
|
||||
}
|
||||
|
||||
nsapi_error_t EthernetInterface::disconnect()
|
||||
{
|
||||
return mbed_lwip_bringdown_2(false);
|
||||
return _interface->bringdown();
|
||||
}
|
||||
|
||||
const char *EthernetInterface::get_mac_address()
|
||||
{
|
||||
return mbed_lwip_get_mac_address();
|
||||
if (_interface->get_mac_address(_mac_address, sizeof(_mac_address))) {
|
||||
return _mac_address;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *EthernetInterface::get_ip_address()
|
||||
{
|
||||
if (mbed_lwip_get_ip_address(_ip_address, sizeof _ip_address)) {
|
||||
if (_interface->get_ip_address(_ip_address, sizeof(_ip_address))) {
|
||||
return _ip_address;
|
||||
}
|
||||
|
||||
|
@ -80,7 +92,7 @@ const char *EthernetInterface::get_ip_address()
|
|||
|
||||
const char *EthernetInterface::get_netmask()
|
||||
{
|
||||
if (mbed_lwip_get_netmask(_netmask, sizeof _netmask)) {
|
||||
if (_interface->get_netmask(_netmask, sizeof(_netmask))) {
|
||||
return _netmask;
|
||||
}
|
||||
|
||||
|
@ -89,7 +101,7 @@ const char *EthernetInterface::get_netmask()
|
|||
|
||||
const char *EthernetInterface::get_gateway()
|
||||
{
|
||||
if (mbed_lwip_get_gateway(_gateway, sizeof _gateway)) {
|
||||
if (_interface->get_gateway(_gateway, sizeof(_gateway))) {
|
||||
return _gateway;
|
||||
}
|
||||
|
||||
|
@ -98,34 +110,29 @@ const char *EthernetInterface::get_gateway()
|
|||
|
||||
NetworkStack *EthernetInterface::get_stack()
|
||||
{
|
||||
return nsapi_create_stack(&lwip_stack);
|
||||
return &_stack;
|
||||
}
|
||||
|
||||
void EthernetInterface::attach(
|
||||
Callback<void(nsapi_event_t, intptr_t)> status_cb)
|
||||
{
|
||||
_connection_status_cb = status_cb;
|
||||
mbed_lwip_attach(netif_status_cb, this);
|
||||
if (_interface) {
|
||||
_interface->attach(status_cb);
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_connection_status_t EthernetInterface::get_connection_status() const
|
||||
{
|
||||
return _connect_status;
|
||||
}
|
||||
|
||||
void EthernetInterface::netif_status_cb(void *ethernet_if_ptr,
|
||||
nsapi_event_t reason, intptr_t parameter)
|
||||
{
|
||||
EthernetInterface *eth_ptr = static_cast<EthernetInterface*>(ethernet_if_ptr);
|
||||
eth_ptr->_connect_status = (nsapi_connection_status_t)parameter;
|
||||
if (eth_ptr->_connection_status_cb)
|
||||
{
|
||||
eth_ptr->_connection_status_cb(reason, parameter);
|
||||
}
|
||||
if (_interface) {
|
||||
return _interface->get_connection_status();
|
||||
} else {
|
||||
return NSAPI_STATUS_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsapi_error_t EthernetInterface::set_blocking(bool blocking)
|
||||
{
|
||||
mbed_lwip_set_blocking(blocking);
|
||||
_blocking = blocking;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
void emac_stack_mem_ref(emac_stack_t* stack, emac_stack_mem_t *mem);
|
||||
* Set total length of the memory chain
|
||||
*
|
||||
* @param chain Memory chain
|
||||
* @param len Total chain length
|
||||
*/
|
||||
void emac_stack_mem_set_chain_len(emac_stack_mem_chain_t *chain, uint32_t len);
|
||||
|
||||
#endif /* DEVICE_EMAC */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EMAC_MBED_STACK_MEM_h */
|
||||
|
|
|
@ -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