2015-05-21 02:06:55 +00:00
|
|
|
/* LWIP interface to the ESP WLAN MAC layer driver.
|
|
|
|
|
|
|
|
Based on the sample driver in ethernetif.c. Tweaks based on
|
|
|
|
examining esp-lwip eagle_if.c and observed behaviour of the ESP
|
|
|
|
RTOS liblwip.a.
|
|
|
|
|
|
|
|
libnet80211.a calls into ethernetif_init & ethernetif_input.
|
|
|
|
|
|
|
|
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
* are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
|
|
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
|
|
* OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* This file is part of the lwIP TCP/IP stack.
|
|
|
|
*
|
|
|
|
* Original Author: Simon Goldschmidt
|
|
|
|
* Modified by Angus Gratton based on work by @kadamski/Espressif via esp-lwip project.
|
|
|
|
*/
|
2017-06-06 02:47:21 +00:00
|
|
|
#include <string.h>
|
2015-05-13 06:40:54 +00:00
|
|
|
#include "lwip/opt.h"
|
2015-05-12 00:12:50 +00:00
|
|
|
|
2015-05-13 06:40:54 +00:00
|
|
|
#include "lwip/def.h"
|
|
|
|
#include "lwip/mem.h"
|
|
|
|
#include "lwip/pbuf.h"
|
|
|
|
#include <lwip/stats.h>
|
|
|
|
#include <lwip/snmp.h>
|
2017-06-06 02:47:21 +00:00
|
|
|
#include "lwip/ip.h"
|
|
|
|
#include "lwip/ethip6.h"
|
2017-09-08 00:48:44 +00:00
|
|
|
#include "lwip/priv/tcp_priv.h"
|
2015-05-13 06:40:54 +00:00
|
|
|
#include "netif/etharp.h"
|
2017-06-06 02:47:21 +00:00
|
|
|
#include "sysparam.h"
|
|
|
|
#include "netif/ppp/pppoe.h"
|
2017-09-03 10:10:22 +00:00
|
|
|
#include "FreeRTOS.h"
|
|
|
|
#include "task.h"
|
2015-05-13 06:40:54 +00:00
|
|
|
|
|
|
|
/* declared in libnet80211.a */
|
2015-05-30 09:11:04 +00:00
|
|
|
int8_t sdk_ieee80211_output_pbuf(struct netif *ifp, struct pbuf* pb);
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/* Define those to better describe your network interface. */
|
|
|
|
#define IFNAME0 'e'
|
|
|
|
#define IFNAME1 'n'
|
|
|
|
|
|
|
|
/**
|
|
|
|
* In this function, the hardware should be initialized.
|
|
|
|
* Called from ethernetif_init().
|
|
|
|
*
|
|
|
|
* @param netif the already initialized lwip network interface structure
|
|
|
|
* for this ethernetif
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
low_level_init(struct netif *netif)
|
2015-05-13 06:40:54 +00:00
|
|
|
{
|
2017-06-06 02:47:21 +00:00
|
|
|
/* set MAC hardware address length */
|
|
|
|
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/* maximum transfer unit */
|
|
|
|
netif->mtu = 1500;
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/* device capabilities */
|
|
|
|
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
|
|
|
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/* Do whatever else is needed to initialize interface. */
|
2015-05-13 06:40:54 +00:00
|
|
|
}
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/**
|
|
|
|
* This function should do the actual transmission of the packet. The packet is
|
|
|
|
* contained in the pbuf that is passed to the function. This pbuf
|
|
|
|
* might be chained.
|
|
|
|
*
|
|
|
|
* @param netif the lwip network interface structure for this ethernetif
|
|
|
|
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
|
|
|
|
* @return ERR_OK if the packet could be sent
|
|
|
|
* an err_t value if the packet couldn't be sent
|
|
|
|
*
|
|
|
|
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
|
|
|
|
* strange results. You might consider waiting for space in the DMA queue
|
|
|
|
* to become available since the stack doesn't retry to send a packet
|
|
|
|
* dropped because of memory failure (except for the TCP timers).
|
|
|
|
*/
|
|
|
|
#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
|
|
|
|
static err_t
|
|
|
|
low_level_output(struct netif *netif, struct pbuf *p)
|
2015-05-12 00:12:50 +00:00
|
|
|
{
|
2017-06-06 02:47:21 +00:00
|
|
|
/*
|
|
|
|
* Note the pbuf reference count is generally one here, but not always. For
|
|
|
|
* example a buffer that had been queued by etharp_query() would have had
|
|
|
|
* its reference count increased to two, and the caller will free it on that
|
|
|
|
* return path.
|
|
|
|
*/
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/* If the pbuf does not have contiguous data, or there is not enough room
|
|
|
|
* for the link layer header, or there are multiple pbufs in the chain then
|
|
|
|
* clone a pbuf to output. */
|
|
|
|
if ((p->type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) == 0 ||
|
|
|
|
(u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF + PBUF_LINK_ENCAPSULATION_HLEN ||
|
|
|
|
p->next) {
|
|
|
|
struct pbuf *q = pbuf_clone(PBUF_RAW_TX, PBUF_RAM, p);
|
|
|
|
if (q == NULL) {
|
|
|
|
return ERR_MEM;
|
|
|
|
}
|
|
|
|
sdk_ieee80211_output_pbuf(netif, q);
|
|
|
|
/* The sdk will pbuf_ref the pbuf before returning and free it later
|
|
|
|
* when it has been sent so free the link to it here. */
|
|
|
|
pbuf_free(q);
|
|
|
|
} else {
|
|
|
|
/* The SDK modifies the eth_hdr, well the first 12 bytes of it at least,
|
|
|
|
* but otherwise leaves the payload unmodified so it can be reused by
|
|
|
|
* the caller. The only paths that appear to reuse the pbuf are in
|
|
|
|
* tcp_out for re-transmission of TCP segments, and these paths check
|
|
|
|
* that the number of references has returned to one before reusing the
|
|
|
|
* pbuf.
|
|
|
|
*/
|
|
|
|
sdk_ieee80211_output_pbuf(netif, p);
|
|
|
|
}
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
LINK_STATS_INC(link.xmit);
|
|
|
|
return ERR_OK;
|
2015-05-12 00:12:50 +00:00
|
|
|
}
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
|
2017-09-03 10:10:22 +00:00
|
|
|
/**
|
|
|
|
* Keep account of the number the PP RX pool buffers being used in lwip,
|
|
|
|
* to help make decision about the number of OOSEQ buffers to maintain etc.
|
|
|
|
*/
|
2017-09-08 00:48:44 +00:00
|
|
|
volatile uint32_t pp_rx_pool_usage;
|
2017-09-03 10:10:22 +00:00
|
|
|
|
|
|
|
/* Support for recycling a pbuf from the sdk rx pool, and accounting for the
|
|
|
|
* number of these used in lwip. */
|
|
|
|
void pp_recycle_rx_pbuf(struct pbuf *p)
|
|
|
|
{
|
|
|
|
LWIP_ASSERT("expected esf_buf", p->esf_buf);
|
|
|
|
sdk_system_pp_recycle_rx_pkt(p->esf_buf);
|
|
|
|
taskENTER_CRITICAL();
|
2017-09-04 07:18:52 +00:00
|
|
|
LWIP_ASSERT("pp_rx_pool_usage underflow", pp_rx_pool_usage > 0);
|
2017-09-03 10:10:22 +00:00
|
|
|
pp_rx_pool_usage--;
|
|
|
|
taskEXIT_CRITICAL();
|
|
|
|
}
|
|
|
|
|
2017-09-04 07:18:52 +00:00
|
|
|
/* Set to true to copy the rx pbufs on input and free them. The pp rx buffer
|
|
|
|
* pool is limited so this allows a large number at the expense of memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define COPY_PP_RX_PBUFS 0
|
|
|
|
|
2017-10-01 10:26:15 +00:00
|
|
|
#if TCP_QUEUE_OOSEQ
|
|
|
|
|
2017-09-03 10:10:22 +00:00
|
|
|
/* Return the number of ooseq bytes that can be retained given the current
|
|
|
|
* size 'n'. */
|
2017-09-21 10:49:23 +00:00
|
|
|
size_t ooseq_bytes_limit(struct tcp_pcb *pcb)
|
2017-09-03 10:10:22 +00:00
|
|
|
{
|
2017-09-04 07:18:52 +00:00
|
|
|
#if COPY_PP_RX_PBUFS
|
2017-09-21 10:49:23 +00:00
|
|
|
struct tcp_seg *ooseq = pcb->ooseq;
|
2017-09-08 00:48:44 +00:00
|
|
|
size_t ooseq_blen = 0;
|
|
|
|
for (; ooseq != NULL; ooseq = ooseq->next) {
|
|
|
|
struct pbuf *p = ooseq->p;
|
|
|
|
ooseq_blen += p->tot_len;
|
|
|
|
}
|
|
|
|
|
2017-09-03 10:10:22 +00:00
|
|
|
size_t free = xPortGetFreeHeapSize();
|
2017-09-08 00:48:44 +00:00
|
|
|
ssize_t target = ((ssize_t)free - 8000) + ooseq_blen;
|
2017-09-03 10:10:22 +00:00
|
|
|
|
|
|
|
if (target < 0) {
|
|
|
|
target = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return target;
|
2017-09-04 07:18:52 +00:00
|
|
|
#else
|
|
|
|
/* The pool is pre-allocated so there is no need to look at the dynamic
|
|
|
|
* memory usage, just consider the number of them below. */
|
|
|
|
return 8000;
|
|
|
|
#endif
|
2017-09-03 10:10:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the number of ooseq pbufs that can be retained given the current
|
|
|
|
* size 'n'. */
|
2017-09-21 10:49:23 +00:00
|
|
|
size_t ooseq_pbufs_limit(struct tcp_pcb *pcb)
|
2017-09-03 10:10:22 +00:00
|
|
|
{
|
2017-09-21 10:49:23 +00:00
|
|
|
struct tcp_seg *ooseq = pcb->ooseq;
|
2017-09-08 00:48:44 +00:00
|
|
|
size_t ooseq_qlen = 0;
|
|
|
|
for (; ooseq != NULL; ooseq = ooseq->next) {
|
|
|
|
struct pbuf *p = ooseq->p;
|
|
|
|
ooseq_qlen += pbuf_clen(p);
|
|
|
|
}
|
|
|
|
|
2017-09-04 07:18:52 +00:00
|
|
|
#if COPY_PP_RX_PBUFS
|
|
|
|
/* More likely memory limited, but set some limit. */
|
|
|
|
ssize_t limit = 10;
|
|
|
|
#else
|
|
|
|
/* Set a small limit if using the pp rx pool, to avoid exhausting it. */
|
|
|
|
ssize_t limit = 2;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
size_t usage = pp_rx_pool_usage;
|
2017-09-08 00:48:44 +00:00
|
|
|
ssize_t target = limit - ((ssize_t)usage - ooseq_qlen);
|
2017-09-03 10:10:22 +00:00
|
|
|
|
|
|
|
if (target < 0) {
|
|
|
|
target = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2017-10-01 10:26:15 +00:00
|
|
|
#endif /* TCP_QUEUE_OOSEQ */
|
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
/**
|
|
|
|
* This function should be called when a packet is ready to be read
|
|
|
|
* from the interface. It uses the function low_level_input() that
|
|
|
|
* should handle the actual reception of bytes from the network
|
|
|
|
* interface. Then the type of the received packet is determined and
|
|
|
|
* the appropriate input function is called.
|
|
|
|
*
|
|
|
|
* @param netif the lwip network interface structure for this ethernetif
|
|
|
|
*/
|
2015-05-12 00:12:50 +00:00
|
|
|
/* called from ieee80211_deliver_data with new IP frames */
|
2015-05-13 06:40:54 +00:00
|
|
|
void ethernetif_input(struct netif *netif, struct pbuf *p)
|
2015-05-12 00:12:50 +00:00
|
|
|
{
|
2017-06-06 02:47:21 +00:00
|
|
|
struct eth_hdr *ethhdr;
|
|
|
|
|
|
|
|
if (p == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->payload == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netif == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-13 06:40:54 +00:00
|
|
|
|
2017-06-06 02:47:21 +00:00
|
|
|
ethhdr = p->payload;
|
2015-05-21 02:06:55 +00:00
|
|
|
|
2017-09-03 10:10:22 +00:00
|
|
|
/* Account for the number of rx pool buffers being used. */
|
|
|
|
taskENTER_CRITICAL();
|
|
|
|
uint32_t usage = pp_rx_pool_usage + 1;
|
|
|
|
pp_rx_pool_usage = usage;
|
|
|
|
taskEXIT_CRITICAL();
|
|
|
|
|
2015-05-21 02:06:55 +00:00
|
|
|
switch(htons(ethhdr->type)) {
|
|
|
|
/* IP or ARP packet? */
|
|
|
|
case ETHTYPE_IP:
|
2017-06-06 02:47:21 +00:00
|
|
|
case ETHTYPE_IPV6:
|
2017-09-03 10:10:22 +00:00
|
|
|
#if 0
|
|
|
|
/* Simulate IP packet loss. */
|
|
|
|
if ((random() & 0xff) < 0x10) {
|
|
|
|
pbuf_free(p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-05-21 02:06:55 +00:00
|
|
|
case ETHTYPE_ARP:
|
2017-06-06 02:47:21 +00:00
|
|
|
#if PPPOE_SUPPORT
|
|
|
|
/* PPPoE packet? */
|
|
|
|
case ETHTYPE_PPPOEDISC:
|
|
|
|
case ETHTYPE_PPPOE:
|
|
|
|
#endif /* PPPOE_SUPPORT */
|
2017-09-03 10:10:22 +00:00
|
|
|
{
|
2015-05-21 02:06:55 +00:00
|
|
|
/* full packet send to tcpip_thread to process */
|
2017-09-03 10:10:22 +00:00
|
|
|
|
2017-09-04 07:18:52 +00:00
|
|
|
#if COPY_PP_RX_PBUFS
|
2017-09-03 10:10:22 +00:00
|
|
|
/* Optionally copy the rx pool buffer and free it immediately. This
|
|
|
|
* helps avoid exhausting the limited rx buffer pool but uses more
|
|
|
|
* memory. */
|
|
|
|
struct pbuf *q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
|
|
|
|
pbuf_free(p);
|
|
|
|
if (q == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = q;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (netif->input(p, netif) != ERR_OK) {
|
2015-05-21 02:06:55 +00:00
|
|
|
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
|
|
|
|
pbuf_free(p);
|
|
|
|
p = NULL;
|
|
|
|
}
|
|
|
|
break;
|
2017-09-03 10:10:22 +00:00
|
|
|
}
|
2015-05-21 02:06:55 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
pbuf_free(p);
|
|
|
|
p = NULL;
|
|
|
|
break;
|
|
|
|
}
|
2015-05-12 00:12:50 +00:00
|
|
|
}
|
2017-06-06 02:47:21 +00:00
|
|
|
|
|
|
|
/* Since the pbuf_type definition has changed in lwip v2 and it is used by the
|
|
|
|
* sdk when calling pbuf_alloc, the SDK libraries have been modified to rename
|
|
|
|
* their references to pbuf_alloc to _pbufalloc allowing the pbuf_type to be
|
|
|
|
* rewritten here. Doing this here keeps this hack out of the lwip code, and
|
|
|
|
* ensures that this re-writing is only applied to the sdk calls to pbuf_alloc.
|
|
|
|
*
|
|
|
|
* The only pbuf types used by the SDK are type 0 for PBUF_RAM when writing
|
|
|
|
* data, and type 2 for the received data. The receive data path references
|
|
|
|
* internal buffer objects that need to be freed with custom code so a custom
|
|
|
|
* pbuf allocation type is used for these.
|
|
|
|
*
|
|
|
|
* The pbuf_layer is now also the header offset, but the sdk calls only call
|
|
|
|
* with a value of 3 which was PBUF_RAW and is now translated to a header
|
|
|
|
* offset of zero.
|
|
|
|
*/
|
|
|
|
struct pbuf *sdk_pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) {
|
|
|
|
if (type == 0) {
|
|
|
|
LWIP_ASSERT("Unexpected sdk_pbuf_alloc layer", layer == 3 || layer == 4);
|
|
|
|
return pbuf_alloc(PBUF_RAW_TX, length, PBUF_RAM);
|
|
|
|
} else if (type == 2) {
|
|
|
|
LWIP_ASSERT("Unexpected sdk_pbuf_alloc layer", layer == 3);
|
|
|
|
return pbuf_alloc_reference(NULL, length, PBUF_ALLOC_FLAG_RX | PBUF_TYPE_ALLOC_SRC_MASK_ESP_RX);
|
|
|
|
} else {
|
|
|
|
LWIP_ASSERT("Unexpected pbuf_alloc type", 0);
|
|
|
|
for (;;);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Should be called at the beginning of the program to set up the
|
|
|
|
* network interface. It calls the function low_level_init() to do the
|
|
|
|
* actual setup of the hardware.
|
|
|
|
*
|
|
|
|
* This function should be passed as a parameter to netif_add().
|
|
|
|
*
|
|
|
|
* @param netif the lwip network interface structure for this ethernetif
|
|
|
|
* @return ERR_OK if the loopif is initialized
|
|
|
|
* ERR_MEM if private data couldn't be allocated
|
|
|
|
* any other err_t on error
|
|
|
|
*/
|
|
|
|
err_t
|
|
|
|
ethernetif_init(struct netif *netif)
|
|
|
|
{
|
|
|
|
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
|
|
|
|
|
|
|
/* The hwaddr is currently set by sdk_wifi_station_start or
|
|
|
|
* sdk_wifi_softap_start. */
|
|
|
|
|
|
|
|
#if LWIP_IPV6
|
|
|
|
// Where to do this???
|
|
|
|
netif_create_ip6_linklocal_address(netif, 1);
|
|
|
|
netif->ip6_autoconfig_enabled = 1;
|
|
|
|
printf("ip6 link local address %s\n", ip6addr_ntoa(netif_ip6_addr(netif, 0)));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if LWIP_NETIF_HOSTNAME
|
|
|
|
/* Initialize interface hostname */
|
|
|
|
char *hostname = NULL;
|
|
|
|
/* Disabled for now as there were reports of crashes here, sysparam issues */
|
|
|
|
/* sysparam_get_string("hostname", &hostname); */
|
|
|
|
if (hostname && strlen(hostname) == 0) {
|
|
|
|
free(hostname);
|
|
|
|
hostname = NULL;
|
|
|
|
}
|
|
|
|
netif->hostname = hostname;
|
|
|
|
#endif /* LWIP_NETIF_HOSTNAME */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the snmp variables and counters inside the struct netif.
|
|
|
|
* The last argument should be replaced with your link speed, in units
|
|
|
|
* of bits per second.
|
|
|
|
*/
|
|
|
|
NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
|
|
|
|
|
|
|
|
// don't touch netif->state here, the field is used internally in the ESP SDK layers
|
|
|
|
netif->name[0] = IFNAME0;
|
|
|
|
netif->name[1] = IFNAME1;
|
|
|
|
/* We directly use etharp_output() here to save a function call.
|
|
|
|
* You can instead declare your own function an call etharp_output()
|
|
|
|
* from it if you have to do some checks before sending (e.g. if link
|
|
|
|
* is available...) */
|
|
|
|
#if LWIP_IPV4
|
|
|
|
netif->output = etharp_output;
|
|
|
|
#endif /* LWIP_IPV4 */
|
|
|
|
#if LWIP_IPV6
|
|
|
|
netif->output_ip6 = ethip6_output;
|
|
|
|
#endif /* LWIP_IPV6 */
|
|
|
|
netif->linkoutput = low_level_output;
|
|
|
|
|
|
|
|
/* initialize the hardware */
|
|
|
|
low_level_init(netif);
|
|
|
|
|
|
|
|
return ERR_OK;
|
|
|
|
}
|