mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 03:32:49 +01:00
04a2070faa
Otherwise if the cable is connected at boot the immediate NETDEV_EVENT_ISR event signaling connection will not trigger the isr, and the netdev will not clear its pending event. This explains why the connect/rx event clash seen in "esp32/eth: Don't overwrite queued event with RX packet" (95196fb7e4
) only happened with lwIP. Now on my ESP32 board with Ethernet the issue was the opposite (since IDF upgrade), the stuck connected event blocked receive from working. After this change95196fb7e4
can be reverted since even early events are consumed properly.
348 lines
10 KiB
C
348 lines
10 KiB
C
/*
|
|
* Copyright (C) 2015 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @{
|
|
*
|
|
* @file
|
|
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <sys/uio.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "lwip/err.h"
|
|
#include "lwip/ethip6.h"
|
|
#include "lwip/netif.h"
|
|
#include "lwip/netifapi.h"
|
|
#include "lwip/netif/compat.h"
|
|
#include "lwip/netif/netdev.h"
|
|
#include "lwip/opt.h"
|
|
#include "lwip/pbuf.h"
|
|
#include "netif/etharp.h"
|
|
#include "netif/lowpan6.h"
|
|
|
|
#include "net/eui64.h"
|
|
#include "net/ieee802154.h"
|
|
#include "net/ipv6/addr.h"
|
|
#include "net/netdev.h"
|
|
#include "net/netopt.h"
|
|
#include "utlist.h"
|
|
#include "thread.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define LWIP_NETDEV_NAME "lwip_netdev_mux"
|
|
#define LWIP_NETDEV_PRIO (THREAD_PRIORITY_MAIN - 4)
|
|
#define LWIP_NETDEV_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
|
|
#define LWIP_NETDEV_QUEUE_LEN (8)
|
|
#define LWIP_NETDEV_MSG_TYPE_EVENT 0x1235
|
|
|
|
#define ETHERNET_IFNAME1 'E'
|
|
#define ETHERNET_IFNAME2 'T'
|
|
|
|
#define WPAN_IFNAME1 'W'
|
|
#define WPAN_IFNAME2 'P'
|
|
|
|
static kernel_pid_t _pid = KERNEL_PID_UNDEF;
|
|
static char _stack[LWIP_NETDEV_STACKSIZE];
|
|
static msg_t _queue[LWIP_NETDEV_QUEUE_LEN];
|
|
static char _tmp_buf[LWIP_NETDEV_BUFLEN];
|
|
|
|
#ifdef MODULE_NETDEV_ETH
|
|
static err_t _eth_link_output(struct netif *netif, struct pbuf *p);
|
|
#endif
|
|
#ifdef MODULE_LWIP_SIXLOWPAN
|
|
static err_t _ieee802154_link_output(struct netif *netif, struct pbuf *p);
|
|
#endif
|
|
static void _event_cb(netdev_t *dev, netdev_event_t event);
|
|
static void *_event_loop(void *arg);
|
|
|
|
err_t lwip_netdev_init(struct netif *netif)
|
|
{
|
|
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
|
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
|
|
netdev_t *netdev;
|
|
netopt_enable_t enabled = 0;
|
|
uint16_t dev_type;
|
|
err_t res = ERR_OK;
|
|
|
|
/* Init device lock */
|
|
lwip_netif_dev_lock_init(netif);
|
|
|
|
/* start multiplexing thread (only one needed) */
|
|
if (_pid <= KERNEL_PID_UNDEF) {
|
|
_pid = thread_create(_stack, LWIP_NETDEV_STACKSIZE, LWIP_NETDEV_PRIO,
|
|
THREAD_CREATE_STACKTEST, _event_loop, netif,
|
|
LWIP_NETDEV_NAME);
|
|
if (_pid <= 0) {
|
|
return ERR_IF;
|
|
}
|
|
}
|
|
|
|
/* initialize Bottom Half Processor, netdev and netif */
|
|
if (IS_USED(MODULE_BHP_MSG)) {
|
|
bhp_msg_claim_thread(lwip_netif_get_bhp(netif), _pid);
|
|
}
|
|
|
|
netdev = netif->state;
|
|
lwip_netif_dev_acquire(netif);
|
|
netdev->event_callback = _event_cb;
|
|
netdev->driver->init(netdev);
|
|
if (netdev->driver->get(netdev, NETOPT_DEVICE_TYPE, &dev_type,
|
|
sizeof(dev_type)) < 0) {
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
#if LWIP_NETIF_HOSTNAME
|
|
netif->hostname = "riot";
|
|
#endif /* LWIP_NETIF_HOSTNAME */
|
|
|
|
switch (dev_type) {
|
|
#ifdef MODULE_NETDEV_ETH
|
|
case NETDEV_TYPE_ETHERNET:
|
|
netif->name[0] = ETHERNET_IFNAME1;
|
|
netif->name[1] = ETHERNET_IFNAME2;
|
|
netif->hwaddr_len = (u8_t)netdev->driver->get(netdev, NETOPT_ADDRESS, netif->hwaddr,
|
|
sizeof(netif->hwaddr));
|
|
if (netif->hwaddr_len > sizeof(netif->hwaddr)) {
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
/* TODO: get from driver (currently not in netdev_eth) */
|
|
netif->mtu = ETHERNET_DATA_LEN;
|
|
netif->linkoutput = _eth_link_output;
|
|
#if LWIP_IPV4
|
|
netif->output = etharp_output;
|
|
#endif
|
|
#if LWIP_IPV6
|
|
netif->output_ip6 = ethip6_output;
|
|
netif_create_ip6_linklocal_address(netif, 1); /* 1: hwaddr is 48-bit MAC addr */
|
|
#endif
|
|
netif->flags |= NETIF_FLAG_BROADCAST;
|
|
netif->flags |= NETIF_FLAG_ETHARP;
|
|
netif->flags |= NETIF_FLAG_ETHERNET;
|
|
break;
|
|
#endif
|
|
#ifdef MODULE_LWIP_SIXLOWPAN
|
|
case NETDEV_TYPE_IEEE802154:
|
|
{
|
|
u16_t val;
|
|
ip6_addr_t *addr;
|
|
netif->name[0] = WPAN_IFNAME1;
|
|
netif->name[1] = WPAN_IFNAME2;
|
|
if (netdev->driver->get(netdev, NETOPT_NID, &val,
|
|
sizeof(val)) < 0) {
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
lowpan6_set_pan_id(val);
|
|
netif->hwaddr_len = (u8_t)netdev->driver->get(netdev, NETOPT_ADDRESS_LONG,
|
|
netif->hwaddr, sizeof(netif->hwaddr));
|
|
if (netif->hwaddr_len > sizeof(netif->hwaddr)) {
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
netif->linkoutput = _ieee802154_link_output;
|
|
res = lowpan6_if_init(netif);
|
|
if (res != ERR_OK) {
|
|
return res;
|
|
}
|
|
/* assure usage of long address as source address */
|
|
val = netif->hwaddr_len;
|
|
if (netdev->driver->set(netdev, NETOPT_SRC_LEN, &val, sizeof(val)) < 0) {
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
/* netif_create_ip6_linklocal_address() does weird byte-swapping
|
|
* with full IIDs, so let's do it ourselves */
|
|
addr = ip_2_ip6(&(netif->ip6_addr[0]));
|
|
/* addr->addr is a uint32_t array */
|
|
if (l2util_ipv6_iid_from_addr(dev_type,
|
|
netif->hwaddr, netif->hwaddr_len,
|
|
(eui64_t *)&addr->addr[2]) < 0) {
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
ipv6_addr_set_link_local_prefix((ipv6_addr_t *)&addr->addr[0]);
|
|
ip6_addr_assign_zone(addr, IP6_UNICAST, netif);
|
|
/* Set address state. */
|
|
#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
|
|
/* Will perform duplicate address detection (DAD). */
|
|
netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE;
|
|
#else
|
|
/* Consider address valid. */
|
|
netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED;
|
|
#endif /* LWIP_IPV6_AUTOCONFIG */
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
res = ERR_IF;
|
|
goto free;
|
|
}
|
|
netif->flags |= NETIF_FLAG_UP;
|
|
/* Set link state up if link state is unsupported, or if it is up */
|
|
if (netdev->driver->get(netdev, NETOPT_LINK, &enabled, sizeof(enabled)) <= 0 ||
|
|
enabled) {
|
|
netif->flags |= NETIF_FLAG_LINK_UP;
|
|
}
|
|
netif->flags |= NETIF_FLAG_IGMP;
|
|
netif->flags |= NETIF_FLAG_MLD6;
|
|
#if LWIP_IPV6_AUTOCONFIG
|
|
netif->ip6_autoconfig_enabled = 1;
|
|
#endif
|
|
free:
|
|
lwip_netif_dev_release(netif);
|
|
|
|
return res;
|
|
}
|
|
|
|
#ifdef MODULE_NETDEV_ETH
|
|
static err_t _eth_link_output(struct netif *netif, struct pbuf *p)
|
|
{
|
|
netdev_t *netdev = netif->state;
|
|
struct pbuf *q;
|
|
unsigned int count = 0;
|
|
|
|
#if ETH_PAD_SIZE
|
|
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
|
|
#endif
|
|
LL_COUNT(p, q, count);
|
|
iolist_t iolist[count];
|
|
|
|
/* make last point to the last entry of iolist[] */
|
|
iolist_t *last = &iolist[count];
|
|
last--;
|
|
|
|
for (q = p, count = 0; q != NULL; q = q->next, count++) {
|
|
iolist_t *iol = &iolist[count];
|
|
|
|
iol->iol_next = (iol == last) ? NULL : &iolist[count + 1];
|
|
|
|
iol->iol_base = q->payload;
|
|
iol->iol_len = (size_t)q->len;
|
|
}
|
|
#if ETH_PAD_SIZE
|
|
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
|
|
#endif
|
|
lwip_netif_dev_acquire(netif);
|
|
err_t res = (netdev->driver->send(netdev, iolist) >= 0) ? ERR_OK : ERR_BUF;
|
|
lwip_netif_dev_release(netif);
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MODULE_LWIP_SIXLOWPAN
|
|
static err_t _ieee802154_link_output(struct netif *netif, struct pbuf *p)
|
|
{
|
|
LWIP_ASSERT("p->next == NULL", p->next == NULL);
|
|
netdev_t *netdev = netif->state;
|
|
iolist_t pkt = {
|
|
.iol_base = p->payload,
|
|
.iol_len = (p->len - IEEE802154_FCS_LEN), /* FCS is written by driver */
|
|
};
|
|
|
|
lwip_netif_dev_acquire(netif);
|
|
err_t res = (netdev->driver->send(netdev, &pkt) >= 0) ? ERR_OK : ERR_BUF;
|
|
lwip_netif_dev_release(netif);
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
static struct pbuf *_get_recv_pkt(netdev_t *dev)
|
|
{
|
|
lwip_netif_t *compat_netif = dev->context;
|
|
struct netif *netif = &compat_netif->lwip_netif;
|
|
lwip_netif_dev_acquire(netif);
|
|
int len = dev->driver->recv(dev, _tmp_buf, sizeof(_tmp_buf), NULL);
|
|
lwip_netif_dev_release(netif);
|
|
|
|
if (len < 0) {
|
|
DEBUG("lwip_netdev: an error occurred while reading the packet\n");
|
|
return NULL;
|
|
}
|
|
assert(((unsigned)len) <= UINT16_MAX);
|
|
struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
|
|
|
|
if (p == NULL) {
|
|
DEBUG("lwip_netdev: can not allocate in pbuf\n");
|
|
return NULL;
|
|
}
|
|
pbuf_take(p, _tmp_buf, len);
|
|
return p;
|
|
}
|
|
|
|
static void _event_cb(netdev_t *dev, netdev_event_t event)
|
|
{
|
|
if (event == NETDEV_EVENT_ISR) {
|
|
assert(_pid != KERNEL_PID_UNDEF);
|
|
msg_t msg;
|
|
|
|
msg.type = LWIP_NETDEV_MSG_TYPE_EVENT;
|
|
msg.content.ptr = dev;
|
|
|
|
if (msg_send(&msg, _pid) <= 0) {
|
|
DEBUG("lwip_netdev: possibly lost interrupt.\n");
|
|
}
|
|
}
|
|
else {
|
|
lwip_netif_t *compat_netif = dev->context;
|
|
struct netif *netif = &compat_netif->lwip_netif;
|
|
switch (event) {
|
|
case NETDEV_EVENT_RX_COMPLETE: {
|
|
struct pbuf *p = _get_recv_pkt(dev);
|
|
if (p == NULL) {
|
|
DEBUG("lwip_netdev: error receiving packet\n");
|
|
return;
|
|
}
|
|
if (netif->input(p, netif) != ERR_OK) {
|
|
DEBUG("lwip_netdev: error inputing packet\n");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case NETDEV_EVENT_LINK_UP: {
|
|
/* Will wake up DHCP state machine */
|
|
netifapi_netif_set_link_up(netif);
|
|
break;
|
|
}
|
|
case NETDEV_EVENT_LINK_DOWN: {
|
|
netifapi_netif_set_link_down(netif);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void *_event_loop(void *arg)
|
|
{
|
|
struct netif *netif = arg;
|
|
msg_init_queue(_queue, LWIP_NETDEV_QUEUE_LEN);
|
|
while (1) {
|
|
msg_t msg;
|
|
msg_receive(&msg);
|
|
if (msg.type == LWIP_NETDEV_MSG_TYPE_EVENT) {
|
|
netdev_t *dev = msg.content.ptr;
|
|
lwip_netif_dev_acquire(netif);
|
|
dev->driver->isr(dev);
|
|
lwip_netif_dev_release(netif);
|
|
}
|
|
else if (IS_USED(MODULE_BHP_MSG) && msg.type == BHP_MSG_BH_REQUEST) {
|
|
bhp_msg_handler(&msg);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/** @} */
|