1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 03:32:49 +01:00
RIOT/pkg/lwip/contrib/netdev/lwip_netdev.c
Erik Ekman 04a2070faa pkg/lwip: Set netdev callback before driver init
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 change 95196fb7e4 can be reverted since even early events are
consumed properly.
2022-09-03 14:07:02 +02:00

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