You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/06/02 20:08:03 UTC
[GitHub] [incubator-nuttx] michi-jung commented on a diff in pull request #6353: Add driver for WIZnet W5500 Ethernet controller
michi-jung commented on code in PR #6353:
URL: https://github.com/apache/incubator-nuttx/pull/6353#discussion_r888371894
##########
drivers/net/w5500.c:
##########
@@ -0,0 +1,2326 @@
+/****************************************************************************
+ * 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 poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define W5500_WDDELAY (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#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_txpoll; /* TX poll timer */
+ 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);
+
+static void w5500_poll_work(FAR void *arg);
+static void w5500_poll_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 wheter socket 0 is open in MACRAW mode. */
Review Comment:
Thanks, I will fix this.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org