You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/06/03 05:01:28 UTC
[incubator-nuttx] branch master updated: Add driver for WIZnet W5500 Ethernet controller
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 9be3848491 Add driver for WIZnet W5500 Ethernet controller
9be3848491 is described below
commit 9be3848491af54417eacc39324d5739e84092afd
Author: Michael Jung <mi...@secore.ly>
AuthorDate: Tue May 31 12:46:49 2022 +0200
Add driver for WIZnet W5500 Ethernet controller
A device driver based on drivers/net/skeleton.c, which uses the W5500 in
MACRAW mode (i.e. bypassing the integrated protocol stack).
Signed-off-by: Michael Jung <mi...@secore.ly>
---
drivers/net/Kconfig | 21 +
drivers/net/Make.defs | 4 +
drivers/net/w5500.c | 2232 +++++++++++++++++++++++++++++++++++++++++++++
include/nuttx/net/w5500.h | 251 +----
4 files changed, 2267 insertions(+), 241 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 26899f2522..2d9d1e54e3 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -351,6 +351,27 @@ menuconfig NET_LAN91C111
if NET_LAN91C111
endif # NET_LAN91C111
+menuconfig NET_W5500
+ bool "WIZnet W5500 Support"
+ default n
+ select SPI
+ select ARCH_HAVE_NETDEV_STATISTICS
+ ---help---
+ References:
+ W5500 Datasheet, Version 1.0.9, 2013 WIZnet Co., Ltd.
+
+if NET_W5500
+
+config NET_W5500_NINTERFACES
+ int "Number of physical W5500 devices"
+ default 1
+ range 1 1
+ ---help---
+ Specifies the number of physical WIZnet W5500
+ devices that will be supported.
+
+endif # W5500
+
if ARCH_HAVE_PHY
comment "External Ethernet PHY Device Support"
diff --git a/drivers/net/Make.defs b/drivers/net/Make.defs
index 1735c8e21d..708100ac97 100644
--- a/drivers/net/Make.defs
+++ b/drivers/net/Make.defs
@@ -64,6 +64,10 @@ ifeq ($(CONFIG_NET_LAN91C111),y)
CSRCS += lan91c111.c
endif
+ifeq ($(CONFIG_NET_W5500),y)
+ CSRCS += w5500.c
+endif
+
ifeq ($(CONFIG_ARCH_PHY_INTERRUPT),y)
CSRCS += phy_notify.c
endif
diff --git a/drivers/net/w5500.c b/drivers/net/w5500.c
new file mode 100644
index 0000000000..18ae561b9b
--- /dev/null
+++ b/drivers/net/w5500.c
@@ -0,0 +1,2232 @@
+/****************************************************************************
+ * drivers/net/w5500.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * References:
+ * [W5500] W5500 Datasheet, Version 1.0.9, May 2019, WIZnet Co., Ltd.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/w5500.h>
+#include <nuttx/signal.h>
+
+#ifdef CONFIG_NET_PKT
+# include <nuttx/net/pkt.h>
+#endif
+
+#ifdef CONFIG_NET_W5500
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+# error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE)
+#else
+
+/* The low priority work queue is preferred. If it is not enabled, LPWORK
+ * will be the same as HPWORK.
+ *
+ * NOTE: However, the network should NEVER run on the high priority work
+ * queue! That queue is intended only to service short back end interrupt
+ * processing that never suspends. Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+#define ETHWORK LPWORK
+
+/* CONFIG_NET_W5500_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_NET_W5500_NINTERFACES
+# define CONFIG_NET_W5500_NINTERFACES 1
+#endif
+
+/* TX timeout = 1 minute
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define W5500_TXTIMEOUT (60 * CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of Ethernet header */
+
+#define ETH_HDR ((FAR struct eth_hdr_s *)self->sk_dev.d_buf)
+
+/* Number of Ethernet frame transmission buffers maintained in W5500's 16 KiB
+ * Tx RAM. A maximum size is conservatively assumed per Ethernet frame in
+ * order to simplify buffer management logic.
+ */
+
+#define NUM_TXBUFS ((16 * 1024) / (CONFIG_NET_ETH_PKTSIZE))
+
+/* W5500 SPI Host Interface *************************************************/
+
+#define W5500_BSB_COMMON_REGS 0x00u
+#define W5500_BSB_SOCKET_REGS(n) (((n & 0x7u) << 5) | 0x08u)
+#define W5500_BSB_SOCKET_TX_BUFFER(n) (((n & 0x7u) << 5) | 0x10u)
+#define W5500_BSB_SOCKET_RX_BUFFER(n) (((n & 0x7u) << 5) | 0x18u)
+
+#define W5500_RWB_READ 0x00u
+#define W5500_RWB_WRITE 0x04u
+
+#define W5500_OM_DATA_LEN_VAR 0x00u
+#define W5500_OM_DATA_LEN_1 0x01u
+#define W5500_OM_DATA_LEN_2 0x02u
+#define W5500_OM_DATA_LEN_4 0x03u
+
+/* W5500 Register Addresses *************************************************/
+
+/* Common Register Block */
+
+#define W5500_MR 0x0000 /* Mode */
+#define W5500_GAR0 0x0001 /* Gateway Address */
+#define W5500_GAR1 0x0002
+#define W5500_GAR2 0x0003
+#define W5500_GAR3 0x0004
+#define W5500_SUBR0 0x0005 /* Subnet Mask Address */
+#define W5500_SUBR1 0x0006
+#define W5500_SUBR2 0x0007
+#define W5500_SUBR3 0x0008
+#define W5500_SHAR0 0x0009 /* Source Hardware Address */
+#define W5500_SHAR1 0x000a
+#define W5500_SHAR2 0x000b
+#define W5500_SHAR3 0x000c
+#define W5500_SHAR4 0x000d
+#define W5500_SHAR5 0x000e
+#define W5500_SIPR0 0x000f /* Source IP Address */
+#define W5500_SIPR1 0x0010
+#define W5500_SIPR2 0x0011
+#define W5500_SIPR3 0x0012
+#define W5500_INTLEVEL0 0x0013 /* Interrupt Low Level Timer */
+#define W5500_INTLEVEL1 0x0014
+#define W5500_IR 0x0015 /* Interrupt */
+#define W5500_IMR 0x0016 /* Interrupt Mask */
+#define W5500_SIR 0x0017 /* Socket Interrupt */
+#define W5500_SIMR 0x0018 /* Socket Interrupt Mask */
+#define W5500_RTR0 0x0019 /* Retry Time */
+#define W5500_RTR1 0x001a
+#define W5500_RCR 0x001b /* Retry Count */
+#define W5500_PTIMER 0x001c /* PPP LCP Request Timer */
+#define W5500_PMAGIC 0x001d /* PPP LCP Magic number */
+#define W5500_PHAR0 0x001e /* PPP Destination MAC Address */
+#define W5500_PHAR1 0x001f
+#define W5500_PHAR2 0x0020
+#define W5500_PHAR3 0x0021
+#define W5500_PHAR4 0x0022
+#define W5500_PHAR5 0x0023
+#define W5500_PSID0 0x0024 /* PPP Session Identification */
+#define W5500_PSID1 0x0025
+#define W5500_PMRU0 0x0026 /* PPP Maximum Segment Size */
+#define W5500_PMRU1 0x0027
+#define W5500_UIPR0 0x0028 /* Unreachable IP address */
+#define W5500_UIPR1 0x0029
+#define W5500_UIPR2 0x002a
+#define W5500_UIPR3 0x002b
+#define W5500_UPORTR0 0x002c /* Unreachable Port */
+#define W5500_UPORTR1 0x002d
+#define W5500_PHYCFGR 0x002e /* PHY Configuration */
+ /* 0x002f-0x0038: Reserved */
+#define W5500_VERSIONR 0x0039 /* Chip version */
+ /* 0x003a-0xffff: Reserved */
+
+/* Socket Register Block */
+
+#define W5500_SN_MR 0x0000 /* Socket n Mode */
+#define W5500_SN_CR 0x0001 /* Socket n Command */
+#define W5500_SN_IR 0x0002 /* Socket n Interrupt */
+#define W5500_SN_SR 0x0003 /* Socket n Status */
+#define W5500_SN_PORT0 0x0004 /* Socket n Source Port */
+#define W5500_SN_PORT1 0x0005
+#define W5500_SN_DHAR0 0x0006 /* Socket n Destination Hardware Address */
+#define W5500_SN_DHAR1 0x0007
+#define W5500_SN_DHAR2 0x0008
+#define W5500_SN_DHAR3 0x0009
+#define W5500_SN_DHAR4 0x000a
+#define W5500_SN_DHAR5 0x000b
+#define W5500_SN_DIPR0 0x000c /* Socket n Destination IP Address */
+#define W5500_SN_DIPR1 0x000d
+#define W5500_SN_DIPR2 0x000e
+#define W5500_SN_DIPR3 0x000f
+#define W5500_SN_DPORT0 0x0010 /* Socket n Destination Port */
+#define W5500_SN_DPORT1 0x0011
+#define W5500_SN_MSSR0 0x0012 /* Socket n Maximum Segment Size */
+#define W5500_SN_MSSR1 0x0013
+ /* 0x0014: Reserved */
+#define W5500_SN_TOS 0x0015 /* Socket n IP TOS */
+#define W5500_SN_TTL 0x0016 /* Socket n IP TTL */
+ /* 0x0017-0x001d: Reserved */
+#define W5500_SN_RXBUF_SIZE 0x001e /* Socket n Receive Buffer Size */
+#define W5500_SN_TXBUF_SIZE 0x001f /* Socket n Transmit Buffer Size */
+#define W5500_SN_TX_FSR0 0x0020 /* Socket n TX Free Size */
+#define W5500_SN_TX_FSR1 0x0021
+#define W5500_SN_TX_RD0 0x0022 /* Socket n TX Read Pointer */
+#define W5500_SN_TX_RD1 0x0023
+#define W5500_SN_TX_WR0 0x0024 /* Socket n TX Write Pointer */
+#define W5500_SN_TX_WR1 0x0025
+#define W5500_SN_RX_RSR0 0x0026 /* Socket n RX Received Size */
+#define W5500_SN_RX_RSR1 0x0027
+#define W5500_SN_RX_RD0 0x0028 /* Socket n RX Read Pointer */
+#define W5500_SN_RX_RD1 0x0029
+#define W5500_SN_RX_WR0 0x002a /* Socket n RX Write Pointer */
+#define W5500_SN_RX_WR1 0x002b
+#define W5500_SN_IMR 0x002c /* Socket n Interrupt Mask */
+#define W5500_SN_FRAG0 0x002d /* Socket n Fragment Offset in IP header */
+#define W5500_SN_FRAG1 0x002e
+#define W5500_SN_KPALVTR 0x002f /* Keep alive timer */
+ /* 0x0030-0xffff: Reserved */
+
+/* W5500 Register Bitfield Definitions **************************************/
+
+/* Common Register Block */
+
+/* Mode Register (MR) */
+
+#define MR_FARP (1 << 1) /* Bit 1: Force ARP */
+#define MR_PPPOE (1 << 3) /* Bit 3: PPPoE Mode */
+#define MR_PB (1 << 4) /* Bit 4: Ping Block Mode */
+#define MR_WOL (1 << 5) /* Bit 5: Wake on LAN */
+#define MR_RST (1 << 7) /* Bit 7: Reset registers */
+
+/* Interrupt Register (IR), Interrupt Mask Register (IMR) */
+
+#define INT_MP (1 << 4) /* Bit 4: Magic Packet */
+#define INT_PPPOE (1 << 5) /* Bit 5: PPPoE Connection Close */
+#define INT_UNREACH (1 << 6) /* Bit 6: Destination unreachable */
+#define INT_CONFLICT (1 << 7) /* Bit 7: IP Conflict */
+
+/* Socket Interrupt Register (SIR) */
+
+#define SIR(n) (1 << (n))
+
+/* Socket Interrupt Mask Register (SIMR)) */
+
+#define SIMR(n) (1 << (n))
+
+/* PHY Configuration Register (PHYCFGR) */
+
+#define PHYCFGR_LNK (1 << 0) /* Bit 0: Link Status */
+#define PHYCFGR_SPD (1 << 1) /* Bit 1: Speed Status */
+#define PHYCFGR_DPX (1 << 2) /* Bit 2: Duplex Status */
+#define PHYCFGR_OPMDC_SHIFT (3) /* Bits 3-5: Operation Mode Configuration */
+#define PHYCFGR_OPMDC_MASK (7 << PHYCFGR_OPMDC_SHIFT)
+# define PHYCFGR_OPMDC_10BT_HD_NAN (0 << PHYCFGR_OPMDC_SHIFT) /* 10BT Half-duplex */
+# define PHYCFGR_OPMDC_10BT_HFD_NAN (1 << PHYCFGR_OPMDC_SHIFT) /* 10BT Full-duplex */
+# define PHYCFGR_OPMDC_100BT_HD_NAN (2 << PHYCFGR_OPMDC_SHIFT) /* 100BT Half-duplex */
+# define PHYCFGR_OPMDC_10BT_FD_NAN (3 << PHYCFGR_OPMDC_SHIFT) /* 100BT Full-duplex,
+ * Auto-negotiation */
+# define PHYCFGR_OPMDC_100BT_HD_AN (4 << PHYCFGR_OPMDC_SHIFT) /* 100BT Half-duplex,
+ * Auto-negotiation */
+# define PHYCFGR_OPMDC_POWER_DOWN (6 << PHYCFGR_OPMDC_SHIFT) /* Power Down mode */
+# define PHYCFGR_OPMDC_ALLCAP_AN (7 << PHYCFGR_OPMDC_SHIFT) /* All capable,
+ * Auto-negotiation */
+
+#define PHYCFGR_OPMD (1 << 6) /* Bit 6: Configure PHY Operation Mode */
+#define PHYCFGR_RST (1 << 7) /* Bit 7: Reset */
+
+/* Socket Register Block */
+
+/* Socket n Mode Register (SN_MR) */
+
+#define SN_MR_PROTOCOL_SHIFT (0) /* Bits 0-3: Protocol */
+#define SN_MR_PROTOCOL_MASK (15 << SN_MR_PROTOCOL_SHIFT)
+# define SN_MR_P0 (1 << (SN_MR_PROTOCOL_SHIFT + 0))
+# define SN_MR_P1 (1 << (SN_MR_PROTOCOL_SHIFT + 1))
+# define SN_MR_P2 (1 << (SN_MR_PROTOCOL_SHIFT + 2))
+# define SN_MR_P3 (1 << (SN_MR_PROTOCOL_SHIFT + 3))
+# define SM_MR_CLOSED 0
+# define SM_MR_TCP SN_MR_P0
+# define SM_MR_UDP SN_MR_P1
+# define SM_MR_MACRAW SN_MR_P2
+#define SN_MR_UCASTB (1 << 4) /* Bit 4: UNICAST Blocking in UDP mode */
+#define SN_MR_MIP6B (1 << 4) /* Bit 4: IPv6 packet Blocking in MACRAW mode */
+#define SN_MR_ND (1 << 5) /* Bit 5: Use No Delayed ACK */
+#define SN_MR_MC (1 << 5) /* Bit 5: Multicast */
+#define SN_MR_MMB (1 << 5) /* Bit 5: Multicast Blocking in MACRAW mode */
+#define SN_MR_BCASTB (1 << 6) /* Bit 6: Broadcast Blocking in MACRAW and
+ * UDP mode */
+#define SN_MR_MULTI (1 << 7) /* Bit 7: Multicasting in UDP mode */
+#define SN_MR_MFEN (1 << 7) /* Bit 7: MAC Filter Enable in MACRAW mode */
+
+/* Socket n Command Register (SN_CR) */
+
+#define SN_CR_OPEN 0x01 /* Socket n is initialized and opened according
+ * to the protocol selected in SN_MR */
+#define SN_CR_LISTEN 0x02 /* Socket n operates as a 'TCP server' and waits
+ * for connection request from any 'TCP client' */
+#define SN_CR_CONNECT 0x04 /* 'TCP client' connection request */
+#define SN_CR_DISCON 0x08 /* TCP disconnection request */
+#define SN_CR_CLOSE 0x10 /* Close socket n */
+#define SN_CR_SEND 0x20 /* Transmit all data in Socket n TX buffer */
+#define SN_CR_SEND_MAC 0x21 /* Transmit all UDP data (no ARP) */
+#define SN_CR_SEND_KEEP 0x22 /* Send TCP keep-alive packet */
+#define SN_CR_RECV 0x40 /* Complete received data in Socket n RX buffer */
+
+/* Socket n Interrupt Register (SN_IR) and
+ * Socket n Interrupt Mask Register (SN_IMR)
+ */
+
+#define SN_INT_CON (1 << 0) /* Bit 0: Connection with peer successful */
+#define SN_INT_DISCON (1 << 1) /* Bit 1: FIN or FIN/ACK received from peer */
+#define SN_INT_RECV (1 << 2) /* Bit 2: Data received from peer */
+#define SN_INT_TIMEOUT (1 << 3) /* Bit 3: ARP or TCP timeout */
+#define SN_INT_SEND_OK (1 << 4) /* Bit 4: SEND command completed */
+
+/* Socket n Status Register (SN_SR) */
+
+#define SN_SR_SOCK_CLOSED 0x00
+#define SN_SR_SOCK_INIT 0x13
+#define SN_SR_SOCK_LISTEN 0x14
+#define SN_SR_SOCK_ESTABLISHED 0x17
+#define SN_SR_SOCK_CLOSE_WAIT 0x1c
+#define SN_SR_SOCK_UDP 0x22
+#define SN_SR_SOCK_MACRAW 0x42
+
+#define SN_SR_SOCK_SYNSENT 0x15 /* Transitional status */
+#define SN_SR_SOCK_SYNRECV 0x16
+#define SN_SR_SOCK_FIN_WAIT 0x18
+#define SN_SR_SOCK_CLOSING 0x1a
+#define SN_SR_SOCK_TIME_WAIT 0x1b
+#define SN_SR_SOCK_LAST_ACK 0x1d
+
+/* Socket n RX Buffer Size Register (SN_RXBUF) */
+
+#define SN_RXBUF_0KB 0
+#define SN_RXBUF_1KB 1
+#define SN_RXBUF_2KB 2
+#define SN_RXBUF_4KB 4
+#define SN_RXBUF_8KB 5
+#define SN_RXBUF_16KB 16
+
+/* Socket n TX Buffer Size Register (SN_TXBUF) */
+
+#define SN_TXBUF_0KB 0
+#define SN_TXBUF_1KB 1
+#define SN_TXBUF_2KB 2
+#define SN_TXBUF_4KB 4
+#define SN_TXBUF_8KB 5
+#define SN_TXBUF_16KB 16
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The w5500_driver_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct w5500_driver_s
+{
+ bool sk_bifup; /* true:ifup false:ifdown */
+ struct wdog_s sk_txtimeout; /* TX timeout timer */
+ struct work_s sk_irqwork; /* For deferring interrupt work to the work queue */
+ struct work_s sk_pollwork; /* For deferring poll work to the work queue */
+
+ /* Ethernet frame transmission buffer management */
+
+ uint16_t txbuf_offset[NUM_TXBUFS + 1];
+ uint8_t txbuf_rdptr;
+ uint8_t txbuf_wrptr;
+
+ /* The hardware interconnect to the W5500 chip */
+
+ FAR struct spi_dev_s *spi_dev; /* SPI hardware access */
+ FAR const struct w5500_lower_s *lower; /* Low-level MCU specific */
+
+ /* This holds the information visible to the NuttX network */
+
+ struct net_driver_s sk_dev; /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* These statically allocated structures would mean that only a single
+ * instance of the device could be supported. In order to support multiple
+ * devices instances, this data would have to be allocated dynamically.
+ */
+
+/* A single packet buffer per device is used in this example. There might
+ * be multiple packet buffers in a more complex, pipelined design. Many
+ * contemporary Ethernet interfaces, for example, use multiple, linked DMA
+ * descriptors in rings to implement such a pipeline. This example assumes
+ * much simpler hardware that simply handles one packet at a time.
+ *
+ * NOTE that if CONFIG_NET_W5500_NINTERFACES were greater than 1, you would
+ * need a minimum on one packet buffer per instance. Much better to be
+ * allocated dynamically in cases where more than one are needed.
+ */
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+/* Driver state structure */
+
+static struct w5500_driver_s g_w5500[CONFIG_NET_W5500_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static void w5500_transmit(FAR struct w5500_driver_s *priv);
+static int w5500_txpoll(FAR struct net_driver_s *dev);
+
+/* Interrupt handling */
+
+static void w5500_reply(struct w5500_driver_s *priv);
+static void w5500_receive(FAR struct w5500_driver_s *priv);
+static void w5500_txdone(FAR struct w5500_driver_s *priv);
+
+static void w5500_interrupt_work(FAR void *arg);
+static int w5500_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* Watchdog timer expirations */
+
+static void w5500_txtimeout_work(FAR void *arg);
+static void w5500_txtimeout_expiry(wdparm_t arg);
+
+/* NuttX callback functions */
+
+static int w5500_ifup(FAR struct net_driver_s *dev);
+static int w5500_ifdown(FAR struct net_driver_s *dev);
+
+static void w5500_txavail_work(FAR void *arg);
+static int w5500_txavail(FAR struct net_driver_s *dev);
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int w5500_addmac(FAR struct net_driver_s *dev,
+ FAR const uint8_t *mac);
+#ifdef CONFIG_NET_MCASTGROUP
+static int w5500_rmmac(FAR struct net_driver_s *dev,
+ FAR const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_ICMPv6
+static void w5500_ipv6multicast(FAR struct w5500_driver_s *priv);
+#endif
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+static int w5500_ioctl(FAR struct net_driver_s *dev, int cmd,
+ unsigned long arg);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* Hardware interface to W5500 **********************************************/
+
+/****************************************************************************
+ * Name: w5500_reset
+ *
+ * Description:
+ * Apply the hardware reset sequence to the W5500 (See [W5500], section
+ * 5.5.1 Reset Timing). Optinonally, keep hardware reset asserted.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * keep - Whether the hardware reset shall be left asserted.
+ *
+ * Assumptions:
+ * This function must only be called in interrupt context, if argument
+ * keep is set to false (otherwise it sleeps, which is not allowed in
+ * interrupt context).
+ *
+ ****************************************************************************/
+
+static void w5500_reset(FAR struct w5500_driver_s *self, bool keep)
+{
+ self->lower->reset(self->lower, true);
+
+ if (!keep)
+ {
+ nxsig_usleep(500); /* [W5500]: T_RC (Reset Cycle Time) min 500 us */
+
+ self->lower->reset(self->lower, false);
+
+ nxsig_usleep(1000); /* [W5500]: T_PL (RSTn to internal PLL lock) 1ms */
+ }
+}
+
+/****************************************************************************
+ * Name: w5500_lock
+ *
+ * Description:
+ * Acquire exclusive access to the W5500's SPI bus and configure it
+ * accordingly.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ ****************************************************************************/
+
+static void w5500_lock(FAR struct w5500_driver_s *self)
+{
+ int ret;
+
+ ret = SPI_LOCK(self->spi_dev, true);
+ DEBUGASSERT(ret == OK);
+
+ SPI_SETMODE(self->spi_dev, self->lower->mode);
+ SPI_SETBITS(self->spi_dev, 8);
+ SPI_HWFEATURES(self->spi_dev, 0);
+ SPI_SETFREQUENCY(self->spi_dev, self->lower->frequency);
+ SPI_SELECT(self->spi_dev, SPIDEV_ETHERNET(self->lower->spidevid), true);
+}
+
+/****************************************************************************
+ * Name: w5500_unlock
+ *
+ * Description:
+ * Relinquish exclusive access to the W5500's SPI bus
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ ****************************************************************************/
+
+static void w5500_unlock(FAR struct w5500_driver_s *self)
+{
+ int ret;
+
+ SPI_SELECT(self->spi_dev, SPIDEV_ETHERNET(self->lower->spidevid), false);
+ ret = SPI_LOCK(self->spi_dev, false);
+ DEBUGASSERT(ret == OK);
+}
+
+/****************************************************************************
+ * Name: w5500_read
+ *
+ * Description:
+ * Read a number of bytes from one of the W5500's register or buffer
+ * blocks.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register or buffer block to read from
+ * offset - The offset address within the block
+ * buffer - Buffer to copy read data into
+ * len - Number of bytes to read from block
+ *
+ ****************************************************************************/
+
+static void w5500_read(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ uint16_t offset,
+ void *buffer,
+ uint16_t len)
+{
+ uint8_t addr_cntl[3];
+
+ addr_cntl[0] = (uint8_t)(offset >> 8);
+ addr_cntl[1] = (uint8_t)offset;
+ addr_cntl[2] = block_select_bits | W5500_RWB_READ | W5500_OM_DATA_LEN_VAR;
+
+ w5500_lock(self);
+ SPI_SNDBLOCK(self->spi_dev, addr_cntl, sizeof(addr_cntl));
+ SPI_RECVBLOCK(self->spi_dev, buffer, len);
+ w5500_unlock(self);
+}
+
+/****************************************************************************
+ * Name: w5500_write
+ *
+ * Description:
+ * Write a number of bytes to one of the W5500's register or buffer
+ * blocks.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register or buffer block to read from
+ * offset - The offset address within the block
+ * Data - Data to write to block
+ * len - Number of bytes to write to block
+ *
+ ****************************************************************************/
+
+static void w5500_write(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ off_t offset,
+ const void *data,
+ size_t len)
+{
+ uint8_t addr_cntl[3];
+
+ addr_cntl[0] = (uint8_t)(offset >> 8);
+ addr_cntl[1] = (uint8_t)offset;
+ addr_cntl[2] = block_select_bits | W5500_RWB_WRITE | W5500_OM_DATA_LEN_VAR;
+
+ w5500_lock(self);
+ SPI_SNDBLOCK(self->spi_dev, addr_cntl, sizeof(addr_cntl));
+ SPI_SNDBLOCK(self->spi_dev, data, len);
+ w5500_unlock(self);
+}
+
+/****************************************************************************
+ * Name: w5500_read8
+ *
+ * Description:
+ * Read a single byte from one of the W5500's register or buffer blocks.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register or buffer block to read from
+ * offset - The offset address within the block
+ *
+ * Returned Value:
+ * The byte read
+ *
+ ****************************************************************************/
+
+static uint8_t w5500_read8(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ uint16_t offset)
+{
+ uint8_t value;
+
+ w5500_read(self,
+ block_select_bits,
+ offset,
+ &value,
+ sizeof(value));
+
+ return value;
+}
+
+/****************************************************************************
+ * Name: w5500_write8
+ *
+ * Description:
+ * Write a single byte to one of the W5500's register or buffer blocks.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register or buffer block to read from
+ * offset - The offset address within the block
+ * value - The byte value to write
+ *
+ ****************************************************************************/
+
+static void w5500_write8(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ uint16_t offset,
+ uint8_t value)
+{
+ w5500_write(self,
+ block_select_bits,
+ offset,
+ &value,
+ sizeof(value));
+}
+
+/****************************************************************************
+ * Name: w5500_read16
+ *
+ * Description:
+ * Read a two byte value from one of the W5500's register or buffer blocks.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register or buffer block to read from
+ * offset - The offset address within the block
+ *
+ * Returned Value:
+ * The two byte value read in host byte order.
+ *
+ ****************************************************************************/
+
+static uint16_t w5500_read16(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ uint16_t offset)
+{
+ uint16_t value;
+
+ w5500_read(self,
+ block_select_bits,
+ offset,
+ &value,
+ sizeof(value));
+
+ return NTOHS(value);
+}
+
+/****************************************************************************
+ * Name: w5500_write16
+ *
+ * Description:
+ * Write a two byte value to one of the W5500's register or buffer blocks.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register or buffer block to read from
+ * offset - The offset address within the block
+ * value - The two byte value to write in host byte order.
+ *
+ ****************************************************************************/
+
+static void w5500_write16(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ uint16_t offset,
+ uint16_t value)
+{
+ value = HTONS(value);
+
+ w5500_write(self,
+ block_select_bits,
+ offset,
+ &value,
+ sizeof(value));
+}
+
+/****************************************************************************
+ * Name: w5500_read16_atomic
+ *
+ * Description:
+ * Read a two-byte value that is concurrently updated by the W5500 hardware
+ * in a safe fashion. In [W5500] it is recommended to read the 16 bits
+ * multiple times until one gets the same value twice in a row.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ * block_select_bits - Register block to read from (See [W5500], section
+ * 2.2.2 Control Phase)
+ * offset - Offset address of the register to read.
+ * value - The 16-bit value read in host byte-order.
+ *
+ * Returned Value:
+ * OK in case of success. A negated errno value in case of failure.
+ *
+ ****************************************************************************/
+
+static int w5500_read16_atomic(FAR struct w5500_driver_s *self,
+ uint8_t block_select_bits,
+ uint16_t offset,
+ FAR uint16_t *value)
+{
+ int i;
+
+ *value = w5500_read16(self, block_select_bits, offset);
+
+ for (i = 0; i < 100; i++)
+ {
+ uint16_t temp;
+
+ temp = w5500_read16(self, block_select_bits, offset);
+
+ if (*value == temp)
+ {
+ return OK;
+ }
+
+ *value = temp;
+ }
+
+ nerr("Failed to get consistent value.\n");
+
+ return -EIO;
+}
+
+/* Ethernet frame tranmission buffer management *****************************/
+
+/****************************************************************************
+ * Name: w5500_txbuf_reset
+ *
+ * Description:
+ * Reset state that manages the storage of multiple outgoing Ethernet
+ * frames in W5500's 16KiB Tx RAM.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ ****************************************************************************/
+
+static void w5500_txbuf_reset(FAR struct w5500_driver_s *self)
+{
+ memset(self->txbuf_offset, 0, sizeof(self->txbuf_offset));
+ self->txbuf_rdptr = 0;
+ self->txbuf_wrptr = 0;
+}
+
+/****************************************************************************
+ * Name: w5500_txbuf_numfree
+ *
+ * Description:
+ * Return the number of Ethernet frames that can still be stored in W5500's
+ * 16KiB Tx RAM.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ * Returned Value:
+ * The number of Ethernet frames that can still be stored.
+ *
+ ****************************************************************************/
+
+static int w5500_txbuf_numfree(FAR struct w5500_driver_s *self)
+{
+ if (self->txbuf_wrptr >= self->txbuf_rdptr)
+ {
+ return NUM_TXBUFS - (self->txbuf_wrptr - self->txbuf_rdptr);
+ }
+ else
+ {
+ return self->txbuf_rdptr - self->txbuf_wrptr - 1;
+ }
+}
+
+/****************************************************************************
+ * Name: w5500_txbuf_numpending
+ *
+ * Description:
+ * Return the number of Ethernet frames that are still pending for trans-
+ * mission from W5500's 16KiB Tx RAM.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ * Returned Value:
+ * The number of Ethernet frames that are pending for transmission.
+ *
+ ****************************************************************************/
+
+static int w5500_txbuf_numpending(FAR struct w5500_driver_s *self)
+{
+ return NUM_TXBUFS - w5500_txbuf_numfree(self);
+}
+
+/****************************************************************************
+ * Name: w5500_txbuf_copy
+ *
+ * Description:
+ * Copy an Ethernet frame from the socket device's buffer to the W5500's
+ * 16KiB Tx RAM.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ * Returned Value:
+ * The byte offset of the first byte after the frame that was copied into
+ * W5500's 16KiB Tx RAM.
+ *
+ ****************************************************************************/
+
+static uint16_t w5500_txbuf_copy(FAR struct w5500_driver_s *self)
+{
+ uint16_t offset;
+
+ DEBUGASSERT(w5500_txbuf_numfree(self) > 0);
+
+ offset = self->txbuf_offset[self->txbuf_wrptr];
+
+ w5500_write(self,
+ W5500_BSB_SOCKET_TX_BUFFER(0),
+ offset,
+ self->sk_dev.d_buf,
+ self->sk_dev.d_len);
+
+ self->txbuf_wrptr = (self->txbuf_wrptr + 1) % (NUM_TXBUFS + 1);
+ self->txbuf_offset[self->txbuf_wrptr] = offset + self->sk_dev.d_len;
+
+ return self->txbuf_offset[self->txbuf_wrptr];
+}
+
+/****************************************************************************
+ * Name: w5500_txbuf_next
+ *
+ * Description:
+ * Release storage for an Ethernet frame that has been successfully
+ * transmitted. Also triggers transmission of the next Ethernet frame,
+ * if applicable.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ * Returned Value:
+ * Whether transmission of another Ethernet frame was triggered.
+ *
+ ****************************************************************************/
+
+static bool w5500_txbuf_next(FAR struct w5500_driver_s *self)
+{
+ uint16_t offset;
+
+ DEBUGASSERT(w5500_txbuf_numpending(self));
+
+ self->txbuf_rdptr = (self->txbuf_rdptr + 1) % (NUM_TXBUFS + 1);
+
+ offset = w5500_read16(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_TX_RD0);
+
+ DEBUGASSERT(self->txbuf_offset[self->txbuf_rdptr] == offset);
+
+ if (!w5500_txbuf_numpending(self))
+ {
+ return false;
+ }
+
+ offset = self->txbuf_offset[(self->txbuf_rdptr + 1) % (NUM_TXBUFS + 1)];
+
+ w5500_write16(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_TX_WR0,
+ offset);
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_CR,
+ SN_CR_SEND);
+
+ /* (Re-)start the TX timeout watchdog timer */
+
+ wd_start(&self->sk_txtimeout,
+ W5500_TXTIMEOUT,
+ w5500_txtimeout_expiry,
+ (wdparm_t)self);
+
+ return true;
+}
+
+/****************************************************************************
+ * Name: w5500_fence
+ *
+ * Description:
+ * Put the W5500 into reset and disable respective interrupt handling.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ ****************************************************************************/
+
+static void w5500_fence(FAR struct w5500_driver_s *self)
+{
+ self->lower->enable(self->lower, false);
+ w5500_reset(self, true); /* Reset and keep reset asserted */
+ self->sk_bifup = false;
+}
+
+/****************************************************************************
+ * Name: w5500_unfence
+ *
+ * Description:
+ * Release W5500 from reset, initialize it and wait up to ten seconds
+ * for link up.
+ *
+ * Input Parameters:
+ * self - The respective w5500 device
+ *
+ * Returned Value:
+ * OK in case of success. A negated errno value in case of failure, in
+ * which case the W5500 is fenced again.
+ *
+ ****************************************************************************/
+
+static int w5500_unfence(FAR struct w5500_driver_s *self)
+{
+ uint8_t value;
+ int i;
+
+ /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */
+
+ w5500_reset(self, false); /* Reset sequence and keep reset de-asserted */
+
+ /* Set the Ethernet interface's MAC address */
+
+ w5500_write(self,
+ W5500_BSB_COMMON_REGS,
+ W5500_SHAR0, /* Source Hardware Address Register */
+ self->sk_dev.d_mac.ether.ether_addr_octet,
+ sizeof(self->sk_dev.d_mac.ether.ether_addr_octet));
+
+ /* Configure socket 0 for raw MAC access with MAC filtering enabled. */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_MR,
+ SM_MR_MACRAW | SN_MR_MFEN);
+
+ /* Allocate all TX and RX buffer space to socket 0 ... */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_RXBUF_SIZE,
+ SN_RXBUF_16KB);
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_TXBUF_SIZE,
+ SN_TXBUF_16KB);
+
+ /* ... and none to sockets 1 to 7. */
+
+ for (i = 1; i < 8; i++)
+ {
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(i),
+ W5500_SN_RXBUF_SIZE,
+ SN_RXBUF_0KB);
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(i),
+ W5500_SN_TXBUF_SIZE,
+ SN_TXBUF_0KB);
+ }
+
+ /* Enable RECV interrupts on socket 0 (SEND_OK interrupts will only be
+ * enabled as long as a transmission is in progress).
+ */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_IMR,
+ SN_INT_RECV);
+
+ /* Enable interrupts on socket 0 */
+
+ w5500_write8(self,
+ W5500_BSB_COMMON_REGS,
+ W5500_SIMR, /* Socket Interrupt Mask Register */
+ SIMR(0));
+
+ /* Open socket 0 */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_CR, /* Control Register */
+ SN_CR_OPEN);
+
+ /* Check whether socket 0 is open in MACRAW mode. */
+
+ value = w5500_read8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_SR);
+
+ if (value != SN_SR_SOCK_MACRAW)
+ {
+ nerr("Unexpected status: %02" PRIx8 "\n", value);
+ goto error;
+ }
+
+ /* Reset Tx buffer management state. */
+
+ w5500_txbuf_reset(self);
+
+ /* Wait up to 10 seconds for link-up */
+
+ value = w5500_read8(self,
+ W5500_BSB_COMMON_REGS,
+ W5500_PHYCFGR);
+
+ for (i = 0; (i < 100) && !(value & PHYCFGR_LNK); i++)
+ {
+ value = w5500_read8(self,
+ W5500_BSB_COMMON_REGS,
+ W5500_PHYCFGR);
+
+ nxsig_usleep(100000); /* 100 ms x 100 = 10 sec */
+ }
+
+ if (value & PHYCFGR_LNK)
+ {
+ ninfo("Link up (%d Mbps / %s duplex)\n",
+ (value & PHYCFGR_SPD) ? 100 : 10,
+ (value & PHYCFGR_DPX) ? "full" : "half");
+ }
+ else
+ {
+ nwarn("Link still down. Cable plugged?\n");
+ goto error;
+ }
+
+ return OK;
+
+error:
+ w5500_fence(self);
+ return -EIO;
+}
+
+/****************************************************************************
+ * Name: w5500_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void w5500_transmit(FAR struct w5500_driver_s *self)
+{
+ uint16_t offset;
+
+ /* Verify that the hardware is ready to send another packet. If we get
+ * here, then we are committed to sending a packet; Higher level logic
+ * must have assured that there is no transmission in progress.
+ */
+
+ if (!w5500_txbuf_numfree(self))
+ {
+ ninfo("Dropping Tx packet due to no buffer available.\n");
+ NETDEV_TXERRORS(self->sk_dev);
+ return;
+ }
+
+ /* Increment statistics */
+
+ NETDEV_TXPACKETS(self->sk_dev);
+
+ /* Copy packet data to TX buffer */
+
+ offset = w5500_txbuf_copy(self);
+
+ /* If there have not been any Tx buffers in use this means we need to start
+ * transmission. Otherwise, this is done either in w5500_txdone or in
+ * w5500_txtimeout_work.
+ */
+
+ if (w5500_txbuf_numpending(self) == 1)
+ {
+ /* Set TX Write Pointer to indicate packet length */
+
+ w5500_write16(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_TX_WR0,
+ offset);
+
+ /* Enable Tx interrupts (Rx ones are always enabled). */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_IMR,
+ SN_INT_RECV | SN_INT_SEND_OK);
+
+ /* Send the packet */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_CR, /* Control Register */
+ SN_CR_SEND);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ wd_start(&self->sk_txtimeout, W5500_TXTIMEOUT,
+ w5500_txtimeout_expiry, (wdparm_t)self);
+ }
+
+#ifdef CONFIG_DEBUG_NET_INFO
+ ninfodumpbuffer("Transmitted:", self->sk_dev.d_buf, self->sk_dev.d_len);
+#endif
+}
+
+/****************************************************************************
+ * Name: w5500_txpoll
+ *
+ * Description:
+ * The transmitter is available, check if the network has any outgoing
+ * packets ready to send. This is a callback from devif_poll().
+ * devif_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static int w5500_txpoll(FAR struct net_driver_s *dev)
+{
+ FAR struct w5500_driver_s *self =
+ (FAR struct w5500_driver_s *)dev->d_private;
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (self->sk_dev.d_len > 0)
+ {
+ /* Look up the destination MAC address and add it to the Ethernet
+ * header.
+ */
+
+#ifdef CONFIG_NET_IPv4
+ if (IFF_IS_IPv4(self->sk_dev.d_flags))
+ {
+ arp_out(&self->sk_dev);
+ }
+#endif /* CONFIG_NET_IPv4 */
+
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv6(self->sk_dev.d_flags))
+ {
+ neighbor_out(&self->sk_dev);
+ }
+#endif /* CONFIG_NET_IPv6 */
+
+ /* Check if the network is sending this packet to the IP address of
+ * this device. If so, just loop the packet back into the network but
+ * don't attempt to put it on the wire.
+ */
+
+ if (!devif_loopback(&self->sk_dev))
+ {
+ /* Send the packet */
+
+ w5500_transmit(self);
+
+ /* Check if there is room in the device to hold another packet.
+ * If not, return a non-zero value to terminate the poll.
+ */
+
+ if (!w5500_txbuf_numfree(self))
+ {
+ return -EBUSY;
+ }
+ }
+ }
+
+ /* If zero is returned, the polling will continue until all connections
+ * have been examined.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: w5500_reply
+ *
+ * Description:
+ * After a packet has been received and dispatched to the network, it
+ * may return with an outgoing packet. This function checks for that case
+ * and performs the transmission if necessary.
+ *
+ * Input Parameters:
+ * self - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void w5500_reply(struct w5500_driver_s *self)
+{
+ /* If the packet dispatch resulted in data that should be sent out on the
+ * network, the field d_len will set to a value > 0.
+ */
+
+ if (self->sk_dev.d_len > 0)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv4
+ if (IFF_IS_IPv4(self->sk_dev.d_flags))
+ {
+ arp_out(&self->sk_dev);
+ }
+#endif
+
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv6(self->sk_dev.d_flags))
+ {
+ neighbor_out(&self->sk_dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ w5500_transmit(self);
+ }
+}
+
+/****************************************************************************
+ * Name: w5500_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void w5500_receive(FAR struct w5500_driver_s *self)
+{
+ do
+ {
+ uint16_t s0_rx_rd;
+ uint16_t s0_rx_rsr;
+ uint16_t pktlen;
+ int ret;
+
+ /* Check if the packet is a valid size for the network buffer
+ * configuration.
+ */
+
+ ret = w5500_read16_atomic(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_RX_RSR0,
+ &s0_rx_rsr);
+ if (ret != OK)
+ {
+ goto error;
+ }
+
+ if (s0_rx_rsr == 0)
+ {
+ ninfo("No data left to read. We are done.\n");
+ break;
+ }
+
+ /* The W5500 prepends each packet with a 2-byte length field in
+ * network byte order. The length value includes the length field
+ * itself. At least this 2-byte packet length must be available.
+ */
+
+ if (s0_rx_rsr < sizeof(pktlen))
+ {
+ nerr("Received size too small. S0_RX_RSR %"PRIu16"\n", s0_rx_rsr);
+ goto error;
+ }
+
+ /* Get the Socket 0 RX Read Pointer. */
+
+ s0_rx_rd = w5500_read16(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_RX_RD0);
+
+ /* Read 16-bit length field. */
+
+ pktlen = w5500_read16(self,
+ W5500_BSB_SOCKET_RX_BUFFER(0),
+ s0_rx_rd);
+
+ if (pktlen > s0_rx_rsr)
+ {
+ nerr("Incomplete packet: pktlen %"PRIu16", S0_RX_RSR %"PRIu16"\n",
+ pktlen,
+ s0_rx_rd);
+
+ goto error;
+ }
+
+ if (pktlen < s0_rx_rsr)
+ {
+ ninfo("More than one packet in RX buffer. "
+ "pktlen %"PRIu16", S0_RX_RSR %"PRIu16"\n",
+ pktlen,
+ s0_rx_rsr);
+ }
+
+ self->sk_dev.d_len = pktlen - sizeof(pktlen);
+
+ /* Copy the data data from the hardware to priv->sk_dev.d_buf. Set
+ * amount of data in priv->sk_dev.d_len
+ */
+
+ if (self->sk_dev.d_len <= CONFIG_NET_ETH_PKTSIZE)
+ {
+ w5500_read(self,
+ W5500_BSB_SOCKET_RX_BUFFER(0),
+ s0_rx_rd + sizeof(pktlen),
+ self->sk_dev.d_buf,
+ self->sk_dev.d_len);
+ }
+
+ /* Acknowledge data receiption to W5500 */
+
+ w5500_write16(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_RX_RD0,
+ s0_rx_rd + pktlen);
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_CR,
+ SN_CR_RECV);
+
+ /* Check for errors and update statistics */
+
+ if (self->sk_dev.d_len > CONFIG_NET_ETH_PKTSIZE ||
+ self->sk_dev.d_len < ETH_HDRLEN)
+ {
+ nerr("Bad packet size dropped (%"PRIu16")\n", self->sk_dev.d_len);
+ self->sk_dev.d_len = 0;
+ NETDEV_RXERRORS(&priv->dev);
+ continue;
+ }
+
+#ifdef CONFIG_DEBUG_NET_INFO
+ ninfodumpbuffer("Received Packet:",
+ self->sk_dev.d_buf,
+ self->sk_dev.d_len);
+#endif
+
+#ifdef CONFIG_NET_PKT
+ /* When packet sockets are enabled, feed the frame into the tap */
+
+ pkt_input(&self->sk_dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+ /* Check for an IPv4 packet */
+
+ if (ETH_HDR->type == HTONS(ETHTYPE_IP))
+ {
+ ninfo("IPv4 frame\n");
+ NETDEV_RXIPV4(&self->sk_dev);
+
+ /* Handle ARP on input, then dispatch IPv4 packet to the network
+ * layer.
+ */
+
+ arp_ipin(&self->sk_dev);
+ ipv4_input(&self->sk_dev);
+
+ /* Check for a reply to the IPv4 packet */
+
+ w5500_reply(self);
+ }
+ else
+#endif
+#ifdef CONFIG_NET_IPv6
+ /* Check for an IPv6 packet */
+
+ if (ETH_HDR->type == HTONS(ETHTYPE_IP6))
+ {
+ ninfo("IPv6 frame\n");
+ NETDEV_RXIPV6(&self->sk_dev);
+
+ /* Dispatch IPv6 packet to the network layer */
+
+ ipv6_input(&self->sk_dev);
+
+ /* Check for a reply to the IPv6 packet */
+
+ w5500_reply(self);
+ }
+ else
+#endif
+#ifdef CONFIG_NET_ARP
+ /* Check for an ARP packet */
+
+ if (ETH_HDR->type == HTONS(ETHTYPE_ARP))
+ {
+ ninfo("ARP frame\n");
+
+ /* Dispatch ARP packet to the network layer */
+
+ arp_arpin(&self->sk_dev);
+ NETDEV_RXARP(&self->sk_dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value
+ * > 0.
+ */
+
+ if (self->sk_dev.d_len > 0)
+ {
+ w5500_transmit(self);
+ }
+ }
+ else
+#endif
+ {
+ ninfo("Dropped frame\n");
+
+ NETDEV_RXDROPPED(&self->sk_dev);
+ }
+ }
+ while (true); /* While there are more packets to be processed */
+
+ return;
+
+error:
+ w5500_fence(self);
+}
+
+/****************************************************************************
+ * Name: w5500_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void w5500_txdone(FAR struct w5500_driver_s *self)
+{
+ /* Check for errors and update statistics */
+
+ NETDEV_TXDONE(self->sk_dev);
+
+ /* Check if there are pending transmissions. */
+
+ if (!w5500_txbuf_next(self))
+ {
+ ninfo("No further transmissions pending.\n");
+
+ /* If no further transmissions are pending, then cancel the TX timeout
+ * and disable further Tx interrupts.
+ */
+
+ wd_cancel(&self->sk_txtimeout);
+
+ /* And disable further TX interrupts. */
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_IMR, SN_INT_RECV);
+ }
+
+ /* In any event, poll the network for new TX data */
+
+ devif_poll(&self->sk_dev, w5500_txpoll);
+}
+
+/****************************************************************************
+ * Name: w5500_interrupt_work
+ *
+ * Description:
+ * Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Runs on a worker thread.
+ *
+ ****************************************************************************/
+
+static void w5500_interrupt_work(FAR void *arg)
+{
+ FAR struct w5500_driver_s *self = (FAR struct w5500_driver_s *)arg;
+ uint8_t ir[3];
+
+ /* Lock the network and serialize driver operations if necessary.
+ * NOTE: Serialization is only required in the case where the driver work
+ * is performed on an LP worker thread and where more than one LP worker
+ * thread has been configured.
+ */
+
+ net_lock();
+
+ /* Process pending Ethernet interrupts. Read IR, MIR and SIR in one shot
+ * to optimize latency, although MIR is not actually used.
+ */
+
+ w5500_read(self,
+ W5500_BSB_COMMON_REGS,
+ W5500_IR,
+ ir,
+ sizeof(ir));
+
+ /* We expect none of the common (integrated network stack related)
+ * interrupts and only interrupts from socket 0.
+ */
+
+ if (ir[0] != 0)
+ {
+ nwarn("Ignoring unexpected interrupts. IR: 0x%02"PRIx8"\n", ir[0]);
+
+ w5500_write8(self,
+ W5500_BSB_COMMON_REGS,
+ W5500_IR,
+ ir[0]);
+ }
+
+ if (ir[2] & ~SIR(0))
+ {
+ nwarn("Interrupt pending for unused socket. SIR: 0x%02"PRIx8"\n",
+ ir[2]);
+
+ goto error;
+ }
+
+ if (ir[2] == 0)
+ {
+ nwarn("Overinitiative interrupt work.\n");
+
+ goto done;
+ }
+
+ /* Get and clear interrupt status bits */
+
+ ir[0] = w5500_read8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_IR);
+
+ if ((ir[0] == 0) || ir[0] & ~(SN_INT_RECV | SN_INT_SEND_OK))
+ {
+ nerr("Unsupported socket interrupts: %02"PRIx8"\n", ir[0]);
+
+ goto error;
+ }
+
+ w5500_write8(self,
+ W5500_BSB_SOCKET_REGS(0),
+ W5500_SN_IR,
+ ir[0]);
+
+ /* Handle interrupts according to status bit settings */
+
+ /* Check if a packet transmission just completed. If so, call
+ * w5500_txdone. This may disable further Tx interrupts if there are no
+ * pending transmissions.
+ */
+
+ if (ir[0] & SN_INT_SEND_OK)
+ {
+ w5500_txdone(self);
+ }
+
+ /* Check if we received an incoming packet, if so, call w5500_receive() */
+
+ if (ir[0] & SN_INT_RECV)
+ {
+ w5500_receive(self);
+ }
+
+done:
+ net_unlock();
+
+ /* Re-enable Ethernet interrupts */
+
+ self->lower->enable(self->lower, true);
+
+ return;
+
+error:
+ w5500_fence(self);
+ net_unlock();
+}
+
+/****************************************************************************
+ * Name: w5500_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Input Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Runs in the context of a the Ethernet interrupt handler. Local
+ * interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static int w5500_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+ FAR struct w5500_driver_s *self = (FAR struct w5500_driver_s *)arg;
+
+ DEBUGASSERT(self != NULL);
+
+ /* Disable further Ethernet interrupts. Because Ethernet interrupts are
+ * also disabled if the TX timeout event occurs, there can be no race
+ * condition here.
+ */
+
+ self->lower->enable(self->lower, false);
+
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(ETHWORK, &self->sk_irqwork, w5500_interrupt_work, self, 0);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: w5500_txtimeout_work
+ *
+ * Description:
+ * Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ ****************************************************************************/
+
+static void w5500_txtimeout_work(FAR void *arg)
+{
+ FAR struct w5500_driver_s *self = (FAR struct w5500_driver_s *)arg;
+
+ /* Lock the network and serialize driver operations if necessary.
+ * NOTE: Serialization is only required in the case where the driver work
+ * is performed on an LP worker thread and where more than one LP worker
+ * thread has been configured.
+ */
+
+ net_lock();
+
+ /* Increment statistics and dump debug info */
+
+ NETDEV_TXTIMEOUTS(self->sk_dev);
+
+ /* Then reset the hardware */
+
+ if (w5500_unfence(self) == OK)
+ {
+ self->lower->enable(self->lower, true);
+
+ /* Then poll the network for new XMIT data */
+
+ devif_poll(&self->sk_dev, w5500_txpoll);
+ }
+
+ net_unlock();
+}
+
+/****************************************************************************
+ * Name: w5500_txtimeout_expiry
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Input Parameters:
+ * arg - The argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Runs in the context of a the timer interrupt handler. Local
+ * interrupts are disabled by the interrupt logic.
+ *
+ ****************************************************************************/
+
+static void w5500_txtimeout_expiry(wdparm_t arg)
+{
+ FAR struct w5500_driver_s *self = (FAR struct w5500_driver_s *)arg;
+
+ /* Disable further Ethernet interrupts. This will prevent some race
+ * conditions with interrupt work. There is still a potential race
+ * condition with interrupt work that is already queued and in progress.
+ */
+
+ w5500_fence(self);
+
+ /* Schedule to perform the TX timeout processing on the worker thread. */
+
+ work_queue(ETHWORK, &self->sk_irqwork, w5500_txtimeout_work, self, 0);
+}
+
+/****************************************************************************
+ * Name: w5500_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static int w5500_ifup(FAR struct net_driver_s *dev)
+{
+ FAR struct w5500_driver_s *self =
+ (FAR struct w5500_driver_s *)dev->d_private;
+ int ret;
+
+#ifdef CONFIG_NET_IPv4
+ ninfo("Bringing up: %d.%d.%d.%d\n",
+ (int)dev->d_ipaddr & 0xff,
+ (int)(dev->d_ipaddr >> 8) & 0xff,
+ (int)(dev->d_ipaddr >> 16) & 0xff,
+ (int)dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+ ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+ dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+ dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+ /* Initialize PHYs, Ethernet interface, and setup up Ethernet interrupts */
+
+ ret = w5500_unfence(self);
+
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+#ifdef CONFIG_NET_ICMPv6
+ /* Set up IPv6 multicast address filtering */
+
+ w5500_ipv6multicast(self);
+#endif
+
+ /* Enable the Ethernet interrupt */
+
+ self->sk_bifup = true;
+ self->lower->enable(self->lower, true);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: w5500_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static int w5500_ifdown(FAR struct net_driver_s *dev)
+{
+ FAR struct w5500_driver_s *self =
+ (FAR struct w5500_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = enter_critical_section();
+ self->lower->enable(self->lower, false);
+
+ /* Cancel the TX timeout timer */
+
+ wd_cancel(&self->sk_txtimeout);
+
+ /* Put the EMAC in its reset, non-operational state. This should be
+ * a known configuration that will guarantee the w5500_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ w5500_fence(self);
+
+ /* Mark the device "down" */
+
+ self->sk_bifup = false;
+ leave_critical_section(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: w5500_txavail_work
+ *
+ * Description:
+ * Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ * arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void w5500_txavail_work(FAR void *arg)
+{
+ FAR struct w5500_driver_s *priv = (FAR struct w5500_driver_s *)arg;
+
+ /* Lock the network and serialize driver operations if necessary.
+ * NOTE: Serialization is only required in the case where the driver work
+ * is performed on an LP worker thread and where more than one LP worker
+ * thread has been configured.
+ */
+
+ net_lock();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->sk_bifup)
+ {
+ /* Check if there is room in the hardware to hold another packet. */
+
+ /* If so, then poll the network for new XMIT data */
+
+ devif_poll(&priv->sk_dev, w5500_txpoll);
+ }
+
+ net_unlock();
+}
+
+/****************************************************************************
+ * Name: w5500_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static int w5500_txavail(FAR struct net_driver_s *dev)
+{
+ FAR struct w5500_driver_s *priv =
+ (FAR struct w5500_driver_s *)dev->d_private;
+
+ /* Is our single work structure available? It may not be if there are
+ * pending interrupt actions and we will have to ignore the Tx
+ * availability action.
+ */
+
+ if (work_available(&priv->sk_pollwork))
+ {
+ /* Schedule to serialize the poll on the worker thread. */
+
+ work_queue(ETHWORK, &priv->sk_pollwork, w5500_txavail_work, priv, 0);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: w5500_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static int w5500_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct w5500_driver_s *priv =
+ (FAR struct w5500_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: w5500_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware
+ * multicast address filtering
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int w5500_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct w5500_driver_s *priv =
+ (FAR struct w5500_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: w5500_ipv6multicast
+ *
+ * Description:
+ * Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void w5500_ipv6multicast(FAR struct w5500_driver_s *priv)
+{
+ FAR struct net_driver_s *dev;
+ uint16_t tmp16;
+ uint8_t mac[6];
+
+ /* For ICMPv6, we need to add the IPv6 multicast address
+ *
+ * For IPv6 multicast addresses, the Ethernet MAC is derived by
+ * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+ * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+ * to the Ethernet MAC address 33:33:00:01:00:03.
+ *
+ * NOTES: This appears correct for the ICMPv6 Router Solicitation
+ * Message, but the ICMPv6 Neighbor Solicitation message seems to
+ * use 33:33:ff:01:00:03.
+ */
+
+ mac[0] = 0x33;
+ mac[1] = 0x33;
+
+ dev = &priv->dev;
+ tmp16 = dev->d_ipv6addr[6];
+ mac[2] = 0xff;
+ mac[3] = tmp16 >> 8;
+
+ tmp16 = dev->d_ipv6addr[7];
+ mac[4] = tmp16 & 0xff;
+ mac[5] = tmp16 >> 8;
+
+ ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ w5500_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+ /* Add the IPv6 all link-local nodes Ethernet address. This is the
+ * address that we expect to receive ICMPv6 Router Advertisement
+ * packets.
+ */
+
+ w5500_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+ /* Add the IPv6 all link-local routers Ethernet address. This is the
+ * address that we expect to receive ICMPv6 Router Solicitation
+ * packets.
+ */
+
+ w5500_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Name: w5500_ioctl
+ *
+ * Description:
+ * Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * cmd - The IOCTL command
+ * arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int w5500_ioctl(FAR struct net_driver_s *dev, int cmd,
+ unsigned long arg)
+{
+ FAR struct w5500_driver_s *priv =
+ (FAR struct w5500_driver_s *)dev->d_private;
+ int ret;
+
+ /* Decode and dispatch the driver-specific IOCTL command */
+
+ switch (cmd)
+ {
+ /* Add cases here to support the IOCTL commands */
+
+ default:
+ nerr("ERROR: Unrecognized IOCTL command: %d\n", command);
+ return -ENOTTY; /* Special return value for this case */
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: w5500_initialize
+ *
+ * Description:
+ * Initialize the Ethernet controller and driver
+ *
+ * Parameters:
+ * spi - A reference to the platform's SPI driver for the W5500.
+ * lower - The lower half driver instance for this W5500 chip.
+ * devno - If more than one W5500 is supported, then this is the
+ * zero based number that identifies the W5500.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int w5500_initialize(FAR struct spi_dev_s *spi_dev,
+ FAR const struct w5500_lower_s *lower,
+ unsigned int devno)
+{
+ FAR struct w5500_driver_s *self;
+
+ /* Get the interface structure associated with this interface number. */
+
+ DEBUGASSERT(devno < CONFIG_NET_W5500_NINTERFACES);
+ self = &g_w5500[devno];
+
+ /* Check if a Ethernet chip is recognized at its I/O base */
+
+ /* Attach the IRQ to the driver */
+
+ if (lower->attach(lower, w5500_interrupt, self))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ memset(self, 0, sizeof(struct w5500_driver_s));
+ self->sk_dev.d_buf = g_pktbuf; /* Single packet buffer */
+ self->sk_dev.d_ifup = w5500_ifup; /* I/F up (new IP address) callback */
+ self->sk_dev.d_ifdown = w5500_ifdown; /* I/F down callback */
+ self->sk_dev.d_txavail = w5500_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+ self->sk_dev.d_addmac = w5500_addmac; /* Add multicast MAC address */
+ self->sk_dev.d_rmmac = w5500_rmmac; /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+ self->sk_dev.d_ioctl = w5500_ioctl; /* Handle network IOCTL commands */
+#endif
+ self->sk_dev.d_private = g_w5500; /* Used to recover private state from dev */
+ self->spi_dev = spi_dev; /* SPI hardware interconnect */
+ self->lower = lower; /* Low-level MCU specific support */
+
+ /* Put the interface in the down state. This usually amounts to resetting
+ * the device and/or calling w5500_ifdown().
+ */
+
+ w5500_ifdown(&self->sk_dev);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ netdev_register(&self->sk_dev, NET_LL_ETHERNET);
+ return OK;
+}
+
+#endif /* !defined(CONFIG_SCHED_WORKQUEUE) */
+
+#endif /* CONFIG_NET_W5500 */
diff --git a/include/nuttx/net/w5500.h b/include/nuttx/net/w5500.h
index 965d62eb74..30d4c4f379 100644
--- a/include/nuttx/net/w5500.h
+++ b/include/nuttx/net/w5500.h
@@ -27,248 +27,12 @@
****************************************************************************/
#include <nuttx/config.h>
-
+#include <nuttx/spi/spi.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef CONFIG_NET_W5500
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-/* W5500 Register Addresses *************************************************/
-
-/* Common Register Block */
-
-#define W5500_MR 0x0000 /* Mode */
-#define W5500_GAR0 0x0001 /* Gateway Address */
-#define W5500_GAR1 0x0002
-#define W5500_GAR2 0x0003
-#define W5500_GAR3 0x0004
-#define W5500_SUBR0 0x0005 /* Subnet Mask Address */
-#define W5500_SUBR1 0x0006
-#define W5500_SUBR2 0x0007
-#define W5500_SUBR3 0x0008
-#define W5500_SHAR0 0x0009 /* Source Hardware Address */
-#define W5500_SHAR1 0x000a
-#define W5500_SHAR2 0x000b
-#define W5500_SHAR3 0x000c
-#define W5500_SHAR4 0x000d
-#define W5500_SHAR5 0x000e
-#define W5500_SIPR0 0x000f /* Source IP Address */
-#define W5500_SIPR1 0x0010
-#define W5500_SIPR2 0x0011
-#define W5500_SIPR3 0x0012
-#define W5500_INTLEVEL0 0x0013 /* Interrupt Low Level Timer */
-#define W5500_INTLEVEL1 0x0014
-#define W5500_IR 0x0015 /* Interrupt */
-#define W5500_IMR 0x0016 /* Interrupt Mask */
-#define W5500_SIR 0x0017 /* Socket Interrupt */
-#define W5500_SIMR 0x0018 /* Socket Interrupt Mask */
-#define W5500_RTR0 0x0019 /* Retry Time */
-#define W5500_RTR1 0x001a
-#define W5500_RCR 0x001b /* Retry Count */
-#define W5500_PTIMER 0x001c /* PPP LCP Request Timer */
-#define W5500_PMAGIC 0x001d /* PPP LCP Magic number */
-#define W5500_PHAR0 0x001e /* PPP Destination MAC Address */
-#define W5500_PHAR1 0x001f
-#define W5500_PHAR2 0x0020
-#define W5500_PHAR3 0x0021
-#define W5500_PHAR4 0x0022
-#define W5500_PHAR5 0x0023
-#define W5500_PSID0 0x0024 /* PPP Session Identification */
-#define W5500_PSID1 0x0025
-#define W5500_PMRU0 0x0026 /* PPP Maximum Segment Size */
-#define W5500_PMRU1 0x0027
-#define W5500_UIPR0 0x0028 /* Unreachable IP address */
-#define W5500_UIPR1 0x0029
-#define W5500_UIPR2 0x002a
-#define W5500_UIPR3 0x002b
-#define W5500_UPORTR0 0x002c /* Unreachable Port */
-#define W5500_UPORTR1 0x002d
-#define W5500_PHYCFGR 0x002e /* PHY Configuration */
- /* 0x002f-0x0038: Reserved */
-#define W5500_VERSIONR 0x0039 /* Chip version */
- /* 0x003a-0xffff: Reserved */
-
-/* Socket Register Block */
-
-#define W5500_SN_MR 0x0000 /* Socket n Mode */
-#define W5500_SN_CR 0x0001 /* Socket n Command */
-#define W5500_SN_IR 0x0002 /* Socket n Interrupt */
-#define W5500_SN_SR 0x0003 /* Socket n Status */
-#define W5500_SN_PORT0 0x0004 /* Socket n Source Port */
-#define W5500_SN_PORT1 0x0005
-#define W5500_SN_DHAR0 0x0006 /* Socket n Destination Hardware Address */
-#define W5500_SN_DHAR1 0x0007
-#define W5500_SN_DHAR2 0x0008
-#define W5500_SN_DHAR3 0x0009
-#define W5500_SN_DHAR4 0x000a
-#define W5500_SN_DHAR5 0x000b
-#define W5500_SN_DIPR0 0x000c /* Socket n Destination IP Address */
-#define W5500_SN_DIPR1 0x000d
-#define W5500_SN_DIPR2 0x000e
-#define W5500_SN_DIPR3 0x000f
-#define W5500_SN_DPORT0 0x0010 /* Socket n Destination Port */
-#define W5500_SN_DPORT1 0x0011
-#define W5500_SN_MSSR0 0x0012 /* Socket n Maximum Segment Size */
-#define W5500_SN_MSSR1 0x0013
- /* 0x0014: Reserved */
-#define W5500_SN_TOS 0x0015 /* Socket n IP TOS */
-#define W5500_SN_TTL 0x0016 /* Socket n IP TTL */
- /* 0x0017-0x001d: Reserved */
-#define W5500_SN_RXBUF_SIZE 0x001e /* Socket n Receive Buffer Size */
-#define W5500_SN_TXBUF_SIZE 0x001f /* Socket n Transmit Buffer Size */
-#define W5500_SN_TX_FSR0 0x0020 /* Socket n TX Free Size */
-#define W5500_SN_TX_FSR1 0x0021
-#define W5500_SN_TX_RD0 0x0022 /* Socket n TX Read Pointer */
-#define W5500_SN_TX_RD1 0x0023
-#define W5500_SN_TX_WR0 0x0024 /* Socket n TX Write Pointer */
-#define W5500_SN_TX_WR1 0x0025
-#define W5500_SN_RX_RSR0 0x0026 /* Socket n RX Received Size */
-#define W5500_SN_RX_RSR1 0x0027
-#define W5500_SN_RX_RD0 0x0028 /* Socket n RX Read Pointer */
-#define W5500_SN_RX_RD1 0x0029
-#define W5500_SN_RX_WR0 0x002a /* Socket n RX Write Pointer */
-#define W5500_SN_RX_WR1 0x002b
-#define W5500_SN_IMR 0x002c /* Socket n Interrupt Mask */
-#define W5500_SN_FRAG0 0x002d /* Socket n Fragment Offset in IP header */
-#define W5500_SN_FRAG1 0x002e
-#define W5500_SN_KPALVTR 0x002f /* Keep alive timer */
- /* 0x0030-0xffff: Reserved */
-
-/* W5500 Register Bitfield Definitions **************************************/
-
-/* Common Register Block */
-
-/* Mode Register (MR) */
-
-#define MR_FARP (1 << 1) /* Bit 1: Force ARP */
-#define MR_PPPOE (1 << 3) /* Bit 3: PPPoE Mode */
-#define MR_PB (1 << 4) /* Bit 4: Ping Block Mode */
-#define MR_WOL (1 << 5) /* Bit 5: Wake on LAN */
-#define MR_RST (1 << 7) /* Bit 7: Reset registers */
-
-/* Interrupt Register (IR), Interrupt Mask Register (IMR) */
-
-#define INT_MP (1 << 4) /* Bit 4: Magic Packet */
-#define INT_PPPOE (1 << 5) /* Bit 5: PPPoE Connection Close */
-#define INT_UNREACH (1 << 6) /* Bit 6: Destination unreachable */
-#define INT_CONFLICT (1 << 7) /* Bit 7: IP Conflict */
-
-/* Socket Interrupt Register (SIR) */
-
-#define SIR(n) (1 << (n))
-
-/* Socket Interrupt Mask Register (SIMR)) */
-
-#define SIMR(n) (1 << (n))
-
-/* PHY Configuration Register (PHYCFGR) */
-
-#define PHYCFGR_LNK (1 << 0) /* Bit 0: Link Status */
-#define PHYCFGR_SPI (1 << 1 /* Bit 2: Speed Status */
-#define PHYCFGR_DPX (1 << 2) /* Bit 3: Duplex Status */
-#define PHYCFGR_OPMDC_SHIFT (3) /* Bits 3-5: Operation Mode Configuration */
-#define PHYCFGR_OPMDC_MASK (7 << PHYCFGR_OPMDC_SHIFT)
-# define PHYCFGR_OPMDC_10BT_HD_NAN (0 << PHYCFGR_OPMDC_SHIFT) /* 10BT Half-duplex */
-# define PHYCFGR_OPMDC_10BT_HFD_NAN (1 << PHYCFGR_OPMDC_SHIFT) /* 10BT Full-duplex */
-# define PHYCFGR_OPMDC_100BT_HD_NAN (2 << PHYCFGR_OPMDC_SHIFT) /* 100BT Half-duplex */
-# define PHYCFGR_OPMDC_10BT_FD_NAN (3 << PHYCFGR_OPMDC_SHIFT) /* 100BT Full-duplex,
- * Auto-negotiation */
-# define PHYCFGR_OPMDC_100BT_HD_AN (4 << PHYCFGR_OPMDC_SHIFT) /* 100BT Half-duplex,
- * Auto-negotiation */
-# define PHYCFGR_OPMDC_POWER_DOWN (6 << PHYCFGR_OPMDC_SHIFT) /* Power Down mode */
-# define PHYCFGR_OPMDC_ALLCAP_AN (7 << PHYCFGR_OPMDC_SHIFT) /* All capable,
- * Auto-negotiation */
-
-#define PHYCFGR_OPMD (1 << 6) /* Bit 6: Configure PHY Operation Mode */
-#define PHYCFGR_RST (1 << 7) /* Bit 7: Reset */
-
-/* Socket Register Block */
-
-/* Socket n Mode Register (SN_MR) */
-
-#define SN_MR_PROTOCOL_SHIFT (0) /* Bits 0-3: Protocol */
-#define SN_MR_PROTOCOL_MASK (15 << SN_MR_PROTOCOL_SHIFT)
-# define SN_MR_P0 (1 << (SN_MR_PROTOCOL_SHIFT + 0))
-# define SN_MR_P1 (1 << (SN_MR_PROTOCOL_SHIFT + 1))
-# define SN_MR_P2 (1 << (SN_MR_PROTOCOL_SHIFT + 2))
-# define SN_MR_P3 (1 << (SN_MR_PROTOCOL_SHIFT + 3))
-# define SM_MR_CLOSED 0
-# define SM_MR_TCP SN_MR_P0
-# define SM_MR_UDP SN_MR_P1
-# define SM_MR_MACRAW SN_MR_P2
-#define SN_MR_UCASTB (1 << 4) /* Bit 4: UNICAST Blocking in UDP mode */
-#define SN_MR_MIP6B (1 << 4) /* Bit 4: IPv6 packet Blocking in MACRAW mode */
-#define SN_MR_ND (1 << 5) /* Bit 5: Use No Delayed ACK */
-#define SN_MR_MC (1 << 5) /* Bit 5: Multicast */
-#define SN_MR_MMB (1 << 5) /* Bit 5: Multicast Blocking in MACRAW mode */
-#define SN_MR_BCASTB (1 << 6) /* Bit 6: Broadcast Blocking in MACRAW and
- * UDP mode */
-#define SN_MR_MULTI (1 << 7) /* Bit 7: Multicasting in UDP mode */
-#define SN_MR_MFEN (1 << 7) /* Bit 7: MAC Filter Enable in MACRAW mode */
-
-/* Socket n Command Register (SN_CR) */
-
-#define SN_CR_OPEN 0x01 /* Socket n is initialized and opened according
- * to the protocol selected in SN_MR */
-#define SN_CR_LISTEN 0x02 /* Socket n operates as a 'TCP server' and waits
- * for connection request from any 'TCP client' */
-#define SN_CR_CONNECT 0x04 /* 'TCP client' connection request */
-#define SN_CR_DISCON 0x08 /* TCP disconnection request */
-#define SN_CR_CLOSE 0x10 /* Close socket n */
-#define SN_CR_SEND 0x20 /* Transmit all data in Socket n TX buffer */
-#define SN_CR_SEND_MAC 0x21 /* Transmit all UDP data (no ARP) */
-#define SN_CR_SEND_KEEP 0x22 /* Send TCP keep-alive packet */
-#define SN_CR_RECV 0x40 /* Complete received data in Socket n RX buffer */
-
-/* Socket n Interrupt Register (SN_IR) and
- * Socket n Interrupt Mask Register (SN_IMR)
- */
-
-#define SN_INT_CON (1 << 0) /* Bit 0: Connection with peer successful */
-#define SN_INT_DISCON (1 << 1) /* Bit 1: FIN or FIN/ACK received from peer */
-#define SN_INT_RECV (1 << 2) /* Bit 2: Data received from peer */
-#define SN_INT_TIMEOUT (1 << 3) /* Bit 3: ARP or TCP timeout */
-#define SN_INT_SEND_OK (1 << 4) /* Bit 4: SEND command completed */
-
-/* Socket n Status Register (SN_SR) */
-
-#define SN_SR_SOCK_CLOSED 0x00
-#define SN_SR_SOCK_INIT 0x13
-#define SN_SR_SOCK_LISTEN 0x14
-#define SN_SR_SOCK_ESTABLISHED 0x17
-#define SN_SR_SOCK_CLOSE_WAIT 0x1c
-#define SN_SR_SOCK_UDP 0x22
-#define SN_SR_SOCK_MACRAW 0x42
-
-#define SN_SR_SOCK_SYNSENT 0x15 /* Transitional status */
-#define SN_SR_SOCK_SYNRECV 0x16
-#define SN_SR_SOCK_FIN_WAIT 0x18
-#define SN_SR_SOCK_CLOSING 0x1a
-#define SN_SR_SOCK_TIME_WAIT 0x1b
-#define SN_SR_SOCK_LAST_ACK 0x1d
-
-/* Socket n RX Buffer Size Register (SN_RXBUF) */
-
-#define SN_RXBUF_0KB 0
-#define SN_RXBUF_1KB 1
-#define SN_RXBUF_2KB 2
-#define SN_RXBUF_4KB 4
-#define SN_RXBUF_8KB 5
-#define SN_RXBUF_16KB 16
-
-/* Socket n TX Buffer Size Register (SN_TXBUF) */
-
-#define SN_TXBUF_0KB 0
-#define SN_TXBUF_1KB 1
-#define SN_TXBUF_2KB 2
-#define SN_TXBUF_4KB 4
-#define SN_TXBUF_8KB 5
-#define SN_TXBUF_16KB 16
-
/****************************************************************************
* Public Types
****************************************************************************/
@@ -279,9 +43,9 @@
struct w5500_lower_s
{
- uint32_t frequency; /* Frequency to use with SPI_SETFREQUENCY() */
- uint16_t spidevid; /* Index used with SPIDEV_ETHERNET() macro */
- enum spi_mode_e mode mode; /* SPI more for use with SPI_SETMODE() */
+ uint32_t frequency; /* Frequency to use with SPI_SETFREQUENCY() */
+ uint16_t spidevid; /* Index used with SPIDEV_ETHERNET() macro */
+ enum spi_mode_e mode; /* SPI more for use with SPI_SETMODE() */
/* Lower-half callbacks:
*
@@ -307,7 +71,10 @@ struct w5500_lower_s
* Initialize the Ethernet controller and driver
*
* Parameters:
+ * spi - A reference to the platform's SPI driver for the W5500.
* lower - The lower half driver instance for this W5500 chip.
+ * devno - If more than one W5500 is supported, then this is the
+ * zero based number that identifies the W5500.
*
* Returned Value:
* OK on success; Negated errno on failure.
@@ -316,7 +83,9 @@ struct w5500_lower_s
*
****************************************************************************/
-int w5500_initialize(FAR struct w5500_lower_s *lower);
+int w5500_initialize(FAR struct spi_dev_s *spi_dev,
+ FAR const struct w5500_lower_s *lower,
+ unsigned int devno);
#endif /* CONFIG_NET_W5500 */
#endif /* __INCLUDE_NUTTX_NET_W5500_H */