From 9be3848491af54417eacc39324d5739e84092afd Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Tue, 31 May 2022 12:46:49 +0200 Subject: [PATCH] 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 --- 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(-) create mode 100644 drivers/net/w5500.c 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 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_PKT +# include +#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 - +#include #include #include #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 */