aboutsummaryrefslogtreecommitdiff
path: root/nuttx/drivers/net
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
commit57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch)
tree25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/drivers/net
downloadpx4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.gz
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.bz2
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.zip
Resync new repository with old repo r5166
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/drivers/net')
-rw-r--r--nuttx/drivers/net/Kconfig79
-rw-r--r--nuttx/drivers/net/Make.defs71
-rw-r--r--nuttx/drivers/net/cs89x0.c959
-rw-r--r--nuttx/drivers/net/cs89x0.h326
-rw-r--r--nuttx/drivers/net/dm90x0.c1815
-rw-r--r--nuttx/drivers/net/e1000.c1049
-rw-r--r--nuttx/drivers/net/e1000.h123
-rw-r--r--nuttx/drivers/net/enc28j60.c2302
-rw-r--r--nuttx/drivers/net/enc28j60.h478
-rw-r--r--nuttx/drivers/net/skeleton.c692
-rw-r--r--nuttx/drivers/net/slip.c1017
-rw-r--r--nuttx/drivers/net/vnet.c673
12 files changed, 9584 insertions, 0 deletions
diff --git a/nuttx/drivers/net/Kconfig b/nuttx/drivers/net/Kconfig
new file mode 100644
index 000000000..b5c09bf01
--- /dev/null
+++ b/nuttx/drivers/net/Kconfig
@@ -0,0 +1,79 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+config NET_DM90x0
+ bool "Davicom dm9000/dm9010 support"
+ default n
+ ---help---
+ References: Davicom data sheets (DM9000-DS-F03-041906.pdf,
+ DM9010-DS-F01-103006.pdf) and looking at lots of other DM90x0
+ drivers.
+
+config NET_CS89x0
+ bool "CS89x0 support"
+ default n
+ depends on EXPERIMENTAL
+ ---help---
+ Under construction -- do not use
+
+config ENC28J60
+ bool "Microchip ENC28J60 support"
+ default n
+ select SPI
+ ---help---
+ References:
+ ENC28J60 Data Sheet, Stand-Alone Ethernet Controller with SPI Interface,
+ DS39662C, 2008 Microchip Technology Inc.
+if ENC28J60
+config ENC28J60_NINTERFACES
+ int "Number of physical ENC28J60"
+ default 1
+ range 1,1
+ ---help---
+ Specifies the number of physical ENC28J60
+ devices that will be supported.
+
+config ENC28J60_SPIMODE
+ int "SPI mode"
+ default 0
+ ---help---
+ Controls the SPI mode. The ENC28J60 spec says that it supports SPI
+ mode 0,0 only: "The implementation used on this device supports SPI
+ mode 0,0 only. In addition, the SPI port requires that SCK be at Idle
+ in a low state; selectable clock polarity is not supported."
+ However, sometimes you need to tinker with these things.
+
+config ENC28J60_FREQUENCY
+ int "SPI frequency"
+ default 20000000
+ ---help---
+ Define to use a different bus frequency
+
+config ENC28J60_STATS
+ bool "Network statistics support"
+ default n
+ ---help---
+ Collect network statistics
+
+config ENC28J60_HALFDUPPLEX
+ bool "Enable half dupplex"
+ default n
+ ---help---
+ Default is full duplex
+endif
+
+config NET_E1000
+ bool "E1000 support"
+ default n
+
+config NET_SLIP
+ bool "SLIP (serial line) support"
+ default n
+ ---help---
+ Reference: RFC 1055
+
+config NET_VNET
+ bool "VNET support"
+ default n
+
diff --git a/nuttx/drivers/net/Make.defs b/nuttx/drivers/net/Make.defs
new file mode 100644
index 000000000..bb0d6a718
--- /dev/null
+++ b/nuttx/drivers/net/Make.defs
@@ -0,0 +1,71 @@
+############################################################################
+# drivers/net/Make.defs
+#
+# Copyright (C) 2007, 2010-2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name NuttX nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+# Include nothing if networking is disabled
+
+ifeq ($(CONFIG_NET),y)
+
+# Include network interface drivers
+
+ifeq ($(CONFIG_NET_DM90x0),y)
+ CSRCS += dm90x0.c
+endif
+
+ifeq ($(CONFIG_NET_CS89x0),y)
+ CSRCS += cs89x0.c
+endif
+
+ifeq ($(CONFIG_ENC28J60),y)
+ CSRCS += enc28j60.c
+endif
+
+ifeq ($(CONFIG_NET_VNET),y)
+ CSRCS += vnet.c
+endif
+
+ifeq ($(CONFIG_NET_E1000),y)
+ CSRCS += e1000.c
+endif
+
+ifeq ($(CONFIG_NET_SLIP),y)
+ CSRCS += slip.c
+endif
+
+# Include network build support
+
+DEPPATH += --dep-path net
+VPATH += :net
+endif
+
diff --git a/nuttx/drivers/net/cs89x0.c b/nuttx/drivers/net/cs89x0.c
new file mode 100644
index 000000000..22b9b87a5
--- /dev/null
+++ b/nuttx/drivers/net/cs89x0.c
@@ -0,0 +1,959 @@
+/****************************************************************************
+ * drivers/net/cs89x0.c
+ *
+ * Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_CS89x0)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+#error "Under construction -- do not use"
+
+/* CONFIG_CS89x0_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_CS89x0_NINTERFACES
+# define CONFIG_CS89x0_NINTERFACES 1
+#endif
+
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define CS89x0_WDDELAY (1*CLK_TCK)
+#define CS89x0_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define CS89x0_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)cs89x0->cs_dev.d_buf)
+
+/* If there is only one CS89x0 instance, then mapping the CS89x0 IRQ to
+ * a driver state instance is trivial.
+ */
+
+#if CONFIG_CS89x0_NINTERFACES == 1
+# define cs89x0_mapirq(irq) g_cs89x0[0]
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct cs89x0_driver_s *g_cs89x0[CONFIG_CS89x0_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* CS89x0 register access */
+
+static uint16_t cs89x0_getreg(struct cs89x0_driver_s *cs89x0, int offset);
+static void cs89x0_putreg(struct cs89x0_driver_s *cs89x0, int offset,
+ uint16_t value);
+static uint16_t cs89x0_getppreg(struct cs89x0_driver_s *cs89x0, int addr);
+static void cs89x0_putppreg(struct cs89x0_driver_s *cs89x0, int addr,
+ uint16_t value);
+
+/* Common TX logic */
+
+static int cs89x0_transmit(struct cs89x0_driver_s *cs89x0);
+static int cs89x0_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void cs89x0_receive(struct cs89x0_driver_s *cs89x0);
+static void cs89x0_txdone(struct cs89x0_driver_s *cs89x0, uint16_t isq);
+#if CONFIG_CS89x0_NINTERFACES > 1
+static inline FAR struct cs89x0_driver_s *cs89x0_mapirq(int irq);
+#endif
+static int cs89x0_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void cs89x0_polltimer(int argc, uint32_t arg, ...);
+static void cs89x0_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int cs89x0_ifup(struct uip_driver_s *dev);
+static int cs89x0_ifdown(struct uip_driver_s *dev);
+static int cs89x0_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int cs89x0_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int cs89x0_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: cs89x0_getreg and cs89x0_putreg
+ *
+ * Description:
+ * Read from and write to a CS89x0 register
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ * offset - Offset to the CS89x0 register
+ * value - Value to be written (cs89x0_putreg only)
+ *
+ * Returned Value:
+ * cs89x0_getreg: The 16-bit value of the register
+ * cs89x0_putreg: None
+ *
+ ****************************************************************************/
+
+static uint16_t cs89x0_getreg(struct cs89x0_driver_s *cs89x0, int offset)
+{
+#ifdef CONFIG_CS89x0_ALIGN16
+ return getreg16(s89x0->cs_base + offset);
+#else
+ return (uint16_t)getreg32(s89x0->cs_base + offset);
+#endif
+}
+
+static void cs89x0_putreg(struct cs89x0_driver_s *cs89x0, int offset, uint16_t value)
+{
+#ifdef CONFIG_CS89x0_ALIGN16
+ return putreg16(value, s89x0->cs_base + offset);
+#else
+ return (uint16_t)putreg32((uint32_t)value, s89x0->cs_base + offset);
+#endif
+}
+
+/****************************************************************************
+ * Function: cs89x0_getppreg and cs89x0_putppreg
+ *
+ * Description:
+ * Read from and write to a CS89x0 page packet register
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ * addr - Address of the CS89x0 page packet register
+ * value - Value to be written (cs89x0_putppreg only)
+ *
+ * Returned Value:
+ * cs89x0_getppreg: The 16-bit value of the page packet register
+ * cs89x0_putppreg: None
+ *
+ ****************************************************************************/
+
+static uint16_t cs89x0_getppreg(struct cs89x0_driver_s *cs89x0, int addr)
+{
+ /* In memory mode, the CS89x0's internal registers and frame buffers are mapped
+ * into a contiguous 4kb block providing direct access to the internal registers
+ * and frame buffers.
+ */
+
+#ifdef CONFIG_CS89x0_MEMMODE
+ if (cs89x0->cs_memmode)
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ return getreg16(s89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#else
+ return (uint16_t)getreg32(s89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#endif
+ }
+
+ /* When configured in I/O mode, the CS89x0 is accessed through eight, 16-bit
+ * I/O ports that in the host system's I/O space.
+ */
+
+ else
+#endif
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ putreg16((uint16_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ return getreg16(s89x0->cs_base + CS89x0_PDATA_OFFSET);
+#else
+ putreg32((uint32_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ return (uint16_t)getreg32(s89x0->cs_base + CS89x0_PDATA_OFFSET);
+#endif
+ }
+}
+
+static void cs89x0_putppreg(struct cs89x0_driver_s *cs89x0, int addr, uint16_t value)
+{
+ /* In memory mode, the CS89x0's internal registers and frame buffers are mapped
+ * into a contiguous 4kb block providing direct access to the internal registers
+ * and frame buffers.
+ */
+
+#ifdef CONFIG_CS89x0_MEMMODE
+ if (cs89x0->cs_memmode)
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ putreg16(value), cs89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#else
+ putreg32((uint32_t)value, cs89x0->cs_ppbase + (CS89x0_PDATA_OFFSET << ??));
+#endif
+ }
+
+ /* When configured in I/O mode, the CS89x0 is accessed through eight, 16-bit
+ * I/O ports that in the host system's I/O space.
+ */
+
+ else
+#endif
+ {
+#ifdef CONFIG_CS89x0_ALIGN16
+ putreg16((uint16_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ putreg16(value, cs89x0->cs_base + CS89x0_PDATA_OFFSET);
+#else
+ putreg32((uint32_t)addr, cs89x0->cs_base + CS89x0_PPTR_OFFSET);
+ putreg32((uint32_t)value, cs89x0->cs_base + CS89x0_PDATA_OFFSET);
+#endif
+ }
+}
+
+/****************************************************************************
+ * Function: cs89x0_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_transmit(struct cs89x0_driver_s *cs89x0)
+{
+ /* Verify that the hardware is ready to send another packet */
+#warning "Missing logic"
+
+ /* Increment statistics */
+#warning "Missing logic"
+
+ /* Disable Ethernet interrupts */
+#warning "Missing logic"
+
+ /* Send the packet: address=cs89x0->cs_dev.d_buf, length=cs89x0->cs_dev.d_len */
+#warning "Missing logic"
+
+ /* Restore Ethernet interrupts */
+#warning "Missing logic"
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(cs89x0->cs_txtimeout, CS89x0_TXTIMEOUT, cs89x0_txtimeout, 1, (uint32_t)cs89x0);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_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
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_uiptxpoll(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_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 (cs89x0->cs_dev.d_len > 0)
+ {
+ uip_arp_out(&cs89x0->cs_dev);
+ cs89x0_transmit(cs89x0);
+
+ /* Check if there is room in the CS89x0 to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+#warning "Missing logic"
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: cs89x0_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ * isq - Interrupt status queue value read by interrupt handler
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_receive(struct cs89x0_driver_s *cs89x0, uint16_t isq)
+{
+ uint16_t *dest;
+ uint16_t rxlength;
+ int nbytes;
+
+ /* Check for errors and update statistics */
+
+ rxlength = cs89x0_getreg(PPR_RXLENGTH);
+ if ((isq & RX_OK) == 0)
+ {
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_errors++;
+ if ((isq & RX_RUNT) != 0)
+ {
+ cd89x0->cs_stats.rx_lengtherrors++;
+ }
+ if ((isq & RX_EXTRA_DATA) != 0)
+ {
+ cd89x0->cs_stats.rx_lengtherrors++;
+ }
+ if (isq & RX_CRC_ERROR) != 0)
+ {
+ if (!(isq & (RX_EXTRA_DATA|RX_RUNT)))
+ {
+ cd89x0->cs_stats.rx_crcerrors++;
+ }
+ }
+ if ((isq & RX_DRIBBLE) != 0)
+ {
+ cd89x0->cs_stats.rx_frameerrors++;
+ }
+#endif
+ return;
+ }
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+
+ if (rxlength > ???)
+ {
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_errors++;
+ cd89x0->cs_stats.rx_lengtherrors++;
+#endif
+ return;
+ }
+
+ /* Copy the data data from the hardware to cs89x0->cs_dev.d_buf. Set
+ * amount of data in cs89x0->cs_dev.d_len
+ */
+
+ dest = (uint16_t*)cs89x0->cs_dev.d_buf;
+ for (nbytes = 0; nbytes < rxlength; nbytes += sizeof(uint16_t))
+ {
+ *dest++ = cs89x0_getreg(PPR_RXFRAMELOCATION);
+ }
+
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_packets++;
+#endif
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&cs89x0->cs_dev);
+ uip_input(&cs89x0->cs_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 (cs89x0->cs_dev.d_len > 0)
+ {
+ uip_arp_out(&cs89x0->cs_dev);
+ cs89x0_transmit(cs89x0);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ uip_arp_arpin(&cs89x0->cs_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 (cs89x0->cs_dev.d_len > 0)
+ {
+ cs89x0_transmit(cs89x0);
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: cs89x0_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * cs89x0 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_txdone(struct cs89x0_driver_s *cs89x0, uint16_t isq)
+{
+ /* Check for errors and update statistics. The lower 6-bits of the ISQ
+ * hold the register address causing the interrupt. We got here because
+ * those bits indicated */
+
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.tx_packets++;
+ if ((isq & ISQ_TXEVENT_TXOK) == 0)
+ {
+ cd89x0->cs_stats.tx_errors++;
+ }
+ if ((isq & ISQ_TXEVENT_LOSSOFCRS) != 0)
+ {
+ cd89x0->cs_stats.tx_carriererrors++;
+ }
+ if ((isq & ISQ_TXEVENT_SQEERROR) != 0)
+ {
+ cd89x0->cs_stats.tx_heartbeaterrors++;
+ }
+ if (i(sq & ISQ_TXEVENT_OUTWINDOW) != 0)
+ {
+ cd89x0->cs_stats.tx_windowerrors++;
+ }
+ if (isq & TX_16_COL)
+ {
+ cd89x0->cs_stats.tx_abortederrors++;
+ }
+#endif
+
+ /* If no further xmits are pending, then cancel the TX timeout */
+
+ wd_cancel(cs89x0->cs_txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&cs89x0->cs_dev, cs89x0_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: cs89x0_mapirq
+ *
+ * Description:
+ * Map an IRQ number to a CS89x0 device state instance. This is only
+ * necessary to handler the case where the architecture includes more than
+ * on CS89x0 chip.
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ *
+ * Returned Value:
+ * A reference to device state structure (NULL if irq does not correspond
+ * to any CS89x0 device).
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if CONFIG_CS89x0_NINTERFACES > 1
+static inline FAR struct cs89x0_driver_s *cs89x0_mapirq(int irq)
+{
+ int i;
+ for (i = 0; i < CONFIG_CS89x0_NINTERFACES; i++)
+ {
+ if (g_cs89x0[i] && g_cs89x0[i].irq == irq)
+ {
+ return g_cs89x0[i];
+ }
+ }
+ return NULL;
+}
+#endif
+
+/****************************************************************************
+ * Function: cs89x0_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_interrupt(int irq, FAR void *context)
+{
+ register struct cs89x0_driver_s *cs89x0 = s89x0_mapirq(irq);
+ uint16_t isq;
+
+#ifdef CONFIG_DEBUG
+ if (!cs89x0)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ /* Read and process all of the events from the ISQ */
+
+ while ((isq = cs89x0_getreg(dev, CS89x0_ISQ_OFFSET)) != 0)
+ {
+ nvdbg("ISQ: %04x\n", isq);
+ switch (isq & ISQ_EVENTMASK)
+ {
+ case ISQ_RXEVENT:
+ cs89x0_receive(cs89x0);
+ break;
+
+ case ISQ_TXEVENT:
+ cs89x0_txdone(cs89x0, isq);
+ break;
+
+ case ISQ_BUFEVENT:
+ if ((isq & ISQ_BUFEVENT_TXUNDERRUN) != 0)
+ {
+ ndbg("Transmit underrun\n");
+#ifdef CONFIG_CS89x0_XMITEARLY
+ cd89x0->cs_txunderrun++;
+ if (cd89x0->cs_txunderrun == 3)
+ {
+ cd89x0->cs_txstart = PPR_TXCMD_TXSTART381;
+ }
+ else if (cd89x0->cs_txunderrun == 6)
+ {
+ cd89x0->cs_txstart = PPR_TXCMD_TXSTARTFULL;
+ }
+#endif
+ }
+ break;
+
+ case ISQ_RXMISSEVENT:
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.rx_missederrors += (isq >>6);
+#endif
+ break;
+
+ case ISQ_TXCOLEVENT:
+#ifdef CONFIG_C89x0_STATISTICS
+ cd89x0->cs_stats.collisions += (isq >>6);
+#endif
+ break;
+ }
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_txtimeout(int argc, uint32_t arg, ...)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+#warning "Missing logic"
+
+ /* Then reset the hardware */
+#warning "Missing logic"
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&cs89x0->cs_dev, cs89x0_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: cs89x0_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void cs89x0_polltimer(int argc, uint32_t arg, ...)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)arg;
+
+ /* Check if there is room in the send another TXr packet. */
+#warning "Missing logic"
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data */
+
+ (void)uip_timer(&cs89x0->cs_dev, cs89x0_uiptxpoll, CS89x0_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(cs89x0->cs_txpoll, CS89x0_WDDELAY, cs89x0_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: cs89x0_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_ifup(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize the Ethernet interface */
+#warning "Missing logic"
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(cs89x0->cs_txpoll, CS89x0_WDDELAY, cs89x0_polltimer, 1, (uint32_t)cs89x0);
+
+ /* Enable the Ethernet interrupt */
+
+ cs89x0->cs_bifup = true;
+ up_enable_irq(CONFIG_CS89x0_IRQ);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int cs89x0_ifdown(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ up_disable_irq(CONFIG_CS89x0_IRQ);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(cs89x0->cs_txpoll);
+ wd_cancel(cs89x0->cs_txtimeout);
+
+ /* Reset the device */
+
+ cs89x0->cs_bifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int cs89x0_txavail(struct uip_driver_s *dev)
+{
+ struct cs89x0_driver_s *cs89x0 = (struct cs89x0_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (cs89x0->cs_bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+#warning "Missing logic"
+
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&cs89x0->cs_dev, cs89x0_uiptxpoll);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: cs89x0_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int cs89x0_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct cs89x0_driver_s *priv = (FAR struct cs89x0_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: cs89x0_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int cs89x0_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct cs89x0_driver_s *priv = (FAR struct cs89x0_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: cs89x0_initialize
+ *
+ * Description:
+ * Initialize the Ethernet driver
+ *
+ * Parameters:
+ * impl - decribes the implementation of the cs89x00 implementation.
+ * This reference is retained so so must remain stable throughout the
+ * life of the driver instance.
+ * devno - Identifies the device number. This must be a number between
+ * zero CONFIG_CS89x0_NINTERFACES and the same devno must not be
+ * initialized twice. The associated network device will be referred
+ * to with the name "eth" followed by this number (eth0, eth1, etc).
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+/* Initialize the CS89x0 chip and driver */
+
+int cs89x0_initialize(FAR const cs89x0_driver_s *cs89x0, int devno)
+{
+ /* Sanity checks -- only performed with debug enabled */
+
+#ifdef CONFIG_DEBUG
+ if (!cs89x0 || (unsigned)devno > CONFIG_CS89x0_NINTERFACES || g_cs89x00[devno])
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Check if a Ethernet chip is recognized at its I/O base */
+
+#warning "Missing logic"
+
+ /* Attach the IRQ to the driver */
+
+ if (irq_attach(cs89x0->irq, cs89x0_interrupt))
+ {
+ /* We could not attach the ISR to the ISR */
+
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ g_cs89x[devno] = cs89x0; /* Used to map IRQ back to instance */
+ cs89x0->cs_dev.d_ifup = cs89x0_ifup; /* I/F down callback */
+ cs89x0->cs_dev.d_ifdown = cs89x0_ifdown; /* I/F up (new IP address) callback */
+ cs89x0->cs_dev.d_txavail = cs89x0_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ cs89x0->cs_dev.d_addmac = cs89x0_addmac; /* Add multicast MAC address */
+ cs89x0->cs_dev.d_rmmac = cs89x0_rmmac; /* Remove multicast MAC address */
+#endif
+ cs89x0->cs_dev.d_private = (void*)cs89x0; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ cs89x0->cs_txpoll = wd_create(); /* Create periodic poll timer */
+ cs89x0->cs_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ /* Read the MAC address from the hardware into cs89x0->cs_dev.d_mac.ether_addr_octet */
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&cs89x0->cs_dev);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_CS89x0 */
+
diff --git a/nuttx/drivers/net/cs89x0.h b/nuttx/drivers/net/cs89x0.h
new file mode 100644
index 000000000..f6d99120a
--- /dev/null
+++ b/nuttx/drivers/net/cs89x0.h
@@ -0,0 +1,326 @@
+/****************************************************************************
+ * drivers/net/cs89x0.h
+ *
+ * Copyright (C) 2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_CS89x0_H
+#define __DRIVERS_NET_CS89x0_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* CONFIG_CS89x0_ALIGN16/32 determines if the 16-bit CS89x0 registers are
+ * aligned to 16-bit or 32-bit address boundaries. NOTE: If there multiple
+ * CS89x00 parts in the board architecture, we assume that the address
+ * alignment is the same for all implementations. If that is not the
+ * case, then it will be necessary to move a shift value into
+ * the cs89x0_driver_s structure and calculate the offsets dynamically in
+ * the putreg and getreg functions.
+ */
+
+#if defined(CONFIG_CS89x0_ALIGN16)
+# define CS89x0_RTDATA_OFFSET (0 << 1)
+# define CS89x0_TxCMD_OFFSET (2 << 1)
+# define CS89x0_TxLEN_OFFSET (3 << 1)
+# define CS89x0_ISQ_OFFSET (4 << 1)
+# define CS89x0_PPTR_OFFSET (5 << 1)
+# define CS89x0_PDATA_OFFSET (6 << 1)
+#elif defined(CONFIG_CS89x0_ALIGN32)
+# define CS89x0_RTDATA_OFFSET (0 << 2)
+# define CS89x0_TxCMD_OFFSET (2 << 2)
+# define CS89x0_TxLEN_OFFSET (3 << 2)
+# define CS89x0_ISQ_OFFSET (4 << 2)
+# define CS89x0_PPTR_OFFSET (5 << 2)
+# define CS89x0_PDATA_OFFSET (6 << 2)
+#else
+# error "CS89x00 address alignment is not defined"
+#endif
+
+/* ISQ register bit definitions */
+
+#define ISQ_EVENTMASK 0x003f /* Bits 0-5 indicate the status register */
+#define ISQ_RXEVENT 0x0004
+#define ISQ_TXEVENT 0x0008
+#define ISQ_BUFEVENT 0x000c
+#define ISQ_RXMISSEVENT 0x0010
+#define ISQ_TXCOLEVENT 0x0012
+
+/* ISQ register TxEVENT bit definitions*/
+
+#define ISQ_RXEVENT_IAHASH (1 << 6)
+#define ISQ_RXEVENT_DRIBBLE (1 << 7)
+#define ISQ_RXEVENT_RXOK (1 << 8)
+#define ISQ_RXEVENT_HASHED (1 << 9)
+#define ISQ_RXEVENT_HASHNDX_SHIFT 10
+#define ISQ_RXEVENT_HASHNDX_MASK (0x3f << ISQ_RXEVENT_HASHNDX_SHIFT)
+
+/* ISQ register TxEVENT bit definitions*/
+
+#define ISQ_TXEVENT_LOSSOFCRS (1 << 6)
+#define ISQ_TXEVENT_SQEERROR (1 << 7)
+#define ISQ_TXEVENT_TXOK (1 << 8)
+#define ISQ_TXEVENT_OUTWINDOW (1 << 9)
+#define ISQ_TXEVENT_JABBER (1 << 10)
+#define ISQ_TXEVENT_NCOLLISION_SHIFT 11
+#define ISQ_TXEVENT_NCOLLISION_MASK (15 << ISQ_TXEVENT_NCOLLISION_SHIFT)
+#define ISQ_TXEVENT_16COLL (1 << 15)
+
+/* ISQ register BufEVENT bit definitions */
+
+#define ISQ_BUFEVENT_SWINT (1 << 6)
+#define ISQ_BUFEVENT_RXDMAFRAME (1 << 7)
+#define ISQ_BUFEVENT_RDY4TX (1 << 8)
+#define ISQ_BUFEVENT_TXUNDERRUN (1 << 9)
+#define ISQ_BUFEVENT_RXMISS (1 << 10)
+#define ISQ_BUFEVENT_RX128 (1 << 11)
+#define ISQ_BUFEVENT_RXDEST (1 << 15)
+
+/* Packet page register offsets *********************************************/
+
+/* 0x0000 Bus interface registers */
+
+#define PPR_CHIPID 0x0000 /* Chip identifier - must be 0x630E */
+#define PPR_CHIPREV 0x0002 /* Chip revision, model codes */
+#define PPR_IOBASEADDRESS 0x0020 /* I/O Base Address */
+#define PPR_INTREG 0x0022 /* Interrupt configuration */
+# define PPR_INTREG_IRQ0 0x0000 /* Use INTR0 pin */
+# define PPR_INTREG_IRQ1 0x0001 /* Use INTR1 pin */
+# define PPR_INTREG_IRQ2 0x0002 /* Use INTR2 pin */
+# define PPR_INTREG_IRQ3 0x0003 /* Use INTR3 pin */
+
+#define PPR_DMACHANNELNUMBER 0x0024 /* DMA Channel Number (0,1, or 2) */
+#define PPR_DMASTARTOFFRAME 0x0026 /* DMA Start of Frame */
+#define PPR_DMAFRAMECOUNT 0x0028 /* DMA Frame Count (12-bits) */
+#define PPR_RXDMABYTECOUNT 0x002a /* Rx DMA Byte Count */
+#define PPR_MEMORYBASEADDRESS 0x002c /* Memory Base Address Register (20-bit) */
+#define PPR_BOOTPROMBASEADDRESS 0x0030 /* Boot PROM Base Address */
+#define PPR_BOOTPROMADDRESSMASK 0x0034 /* Boot PROM Address Mask */
+#define PPR_EEPROMCOMMAND 0x0040 /* EEPROM Command */
+#define PPR_EEPROMDATA 0x0042 /* EEPROM Data */
+#define PPR_RECVFRAMEBYTES 0x0050 /* Received Frame Byte Counter */
+
+/* 0x0100 - Configuration and control registers */
+
+#define PPR_RXCFG 0x0102 /* Receiver configuration */
+# define PPR_RXCFG_SKIP1 (1 << 6) /* Skip (discard) current frame */
+# define PPR_RXCFG_STREAM (1 << 7) /* Enable streaming mode */
+# define PPR_RXCFG_RXOK (1 << 8) /* RxOK interrupt enable */
+# define PPR_RxCFG_RxDMAonly (1 << 9) /* Use RxDMA for all frames */
+# define PPR_RxCFG_AutoRxDMA (1 << 10) /* Select RxDMA automatically */
+# define PPR_RxCFG_BufferCRC (1 << 11) /* Include CRC characters in frame */
+# define PPR_RxCFG_CRC (1 << 12) /* Enable interrupt on CRC error */
+# define PPR_RxCFG_RUNT (1 << 13) /* Enable interrupt on RUNT frames */
+# define PPR_RxCFG_EXTRA (1 << 14) /* Enable interrupt on frames with extra data */
+
+#define PPR_RXCTL 0x0104 /* Receiver control */
+# define PPR_RXCTL_IAHASH (1 << 6) /* Accept frames that match hash */
+# define PPR_RXCTL_PROMISCUOUS (1 << 7) /* Accept any frame */
+# define PPR_RXCTL_RXOK (1 << 8) /* Accept well formed frames */
+# define PPR_RXCTL_MULTICAST (1 << 9) /* Accept multicast frames */
+# define PPR_RXCTL_IA (1 << 10) /* Accept frame that matches IA */
+# define PPR_RXCTL_BROADCAST (1 << 11) /* Accept broadcast frames */
+# define PPR_RXCTL_CRC (1 << 12) /* Accept frames with bad CRC */
+# define PPR_RXCTL_RUNT (1 << 13) /* Accept runt frames */
+# define PPR_RXCTL_EXTRA (1 << 14) /* Accept frames that are too long */
+
+#define PPR_TXCFG 0x0106 /* Transmit configuration */
+# define PPR_TXCFG_CRS (1 << 6) /* Enable interrupt on loss of carrier */
+# define PPR_TXCFG_SQE (1 << 7) /* Enable interrupt on Signal Quality Error */
+# define PPR_TXCFG_TXOK (1 << 8) /* Enable interrupt on successful xmits */
+# define PPR_TXCFG_LATE (1 << 9) /* Enable interrupt on "out of window" */
+# define PPR_TXCFG_JABBER (1 << 10) /* Enable interrupt on jabber detect */
+# define PPR_TXCFG_COLLISION (1 << 11) /* Enable interrupt if collision */
+# define PPR_TXCFG_16COLLISIONS (1 << 15) /* Enable interrupt if > 16 collisions */
+
+#define PPR_TXCMD 0x0108 /* Transmit command status */
+# define PPR_TXCMD_TXSTART5 (0 << 6) /* Start after 5 bytes in buffer */
+# define PPR_TXCMD_TXSTART381 (1 << 6) /* Start after 381 bytes in buffer */
+# define PPR_TXCMD_TXSTART1021 (2 << 6) /* Start after 1021 bytes in buffer */
+# define PPR_TXCMD_TXSTARTFULL (3 << 6) /* Start after all bytes loaded */
+# define PPR_TXCMD_FORCE (1 << 8) /* Discard any pending packets */
+# define PPR_TXCMD_ONECOLLISION (1 << 9) /* Abort after a single collision */
+# define PPR_TXCMD_NOCRC (1 << 12) /* Do not add CRC */
+# define PPR_TXCMD_NOPAD (1 << 13) /* Do not pad short packets */
+
+#define PPR_BUFCFG 0x010a /* Buffer configuration */
+# define PPR_BUFCFG_SWI (1 << 6) /* Force interrupt via software */
+# define PPR_BUFCFG_RXDMA (1 << 7) /* Enable interrupt on Rx DMA */
+# define PPR_BUFCFG_TXRDY (1 << 8) /* Enable interrupt when ready for Tx */
+# define PPR_BUFCFG_TXUE (1 << 9) /* Enable interrupt in Tx underrun */
+# define PPR_BUFCFG_RXMISS (1 << 10) /* Enable interrupt on missed Rx packets */
+# define PPR_BUFCFG_RX128 (1 << 11) /* Enable Rx interrupt after 128 bytes */
+# define PPR_BUFCFG_TXCOL (1 << 12) /* Enable int on Tx collision ctr overflow */
+# define PPR_BUFCFG_MISS (1 << 13) /* Enable int on Rx miss ctr overflow */
+# define PPR_BUFCFG_RXDEST (1 << 15) /* Enable int on Rx dest addr match */
+
+#define PPR_LINECTL 0x0112 /* Line control */
+# define PPR_LINECTL_RX (1 << 6) /* Enable receiver */
+# define PPR_LINECTL_TX (1 << 7) /* Enable transmitter */
+# define PPR_LINECTL_AUIONLY (1 << 8) /* AUI interface only */
+# define PPR_LINECTL_AUTOAUI10BT (1 << 9) /* Autodetect AUI or 10BaseT interface */
+# define PPR_LINECTL_MODBACKOFFE (1 << 11) /* Enable modified backoff algorithm */
+# define PPR_LINECTL_POLARITYDIS (1 << 12) /* Disable Rx polarity autodetect */
+# define PPR_LINECTL_2PARTDEFDIS (1 << 13) /* Disable two-part defferal */
+# define PPR_LINECTL_LORXSQUELCH (1 << 14) /* Reduce receiver squelch threshold */
+
+#define PPR_SELFCTL 0x0114 /* Chip self control */
+# define PPR_SELFCTL_RESET (1 << 6) /* Self-clearing reset */
+# define PPR_SELFCTL_SWSUSPEND (1 << 8) /* Initiate suspend mode */
+# define PPR_SELFCTL_HWSLEEPE (1 << 9) /* Enable SLEEP input */
+# define PPR_SELFCTL_HWSTANDBYE (1 << 10) /* Enable standby mode */
+# define PPR_SELFCTL_HC0E (1 << 12) /* Use HCB0 for LINK LED */
+# define PPR_SELFCTL_HC1E (1 << 13) /* Use HCB1 for BSTATUS LED */
+# define PPR_SELFCTL_HCB0 (1 << 14) /* Control LINK LED if HC0E set */
+# define PPR_SELFCTL_HCB1 (1 << 15) /* Cntrol BSTATUS LED if HC1E set */
+
+#define PPR_BUSCTL 0x0116 /* Bus control */
+# define PPR_BUSCTL_RESETRXDMA (1 << 6) /* Reset RxDMA pointer */
+# define PPR_BUSCTL_DMAEXTEND (1 << 8) /* Extend DMA cycle */
+# define PPR_BUSCTL_USESA (1 << 9) /* Assert MEMCS16 on address decode */
+# define PPR_BUSCTL_MEMORYE (1 << 10) /* Enable memory mode */
+# define PPR_BUSCTL_DMABURST (1 << 11) /* Limit DMA access burst */
+# define PPR_BUSCTL_IOCHRDYE (1 << 12) /* Set IOCHRDY high impedence */
+# define PPR_BUSCTL_RXDMASIZE (1 << 13) /* Set DMA buffer size 64KB */
+# define PPR_BUSCTL_ENABLEIRQ (1 << 15) /* Generate interrupt on interrupt event */
+
+#define PPR_TESTCTL 0x0118 /* Test control */
+# define PPR_TESTCTL_DISABLELT (1 << 7) /* Disable link status */
+# define PPR_TESTCTL_ENDECLOOP (1 << 9) /* Internal loopback */
+# define PPR_TESTCTL_AUILOOP (1 << 10) /* AUI loopback */
+# define PPR_TESTCTL_DISBACKOFF (1 << 11) /* Disable backoff algorithm */
+# define PPR_TESTCTL_FDX (1 << 14) /* Enable full duplex mode */
+
+/* 0x0120 - Status and Event Registers */
+
+#define PPR_ISQ 0x0120 /* Interrupt Status Queue */
+#define PPR_RER 0x0124 /* Receive event */
+# define PPR_RER_IAHASH (1 << 6) /* Frame hash match */
+# define PPR_RER_DRIBBLE (1 << 7) /* Frame had 1-7 extra bits after last byte */
+# define PPR_RER_RXOK (1 << 8) /* Frame received with no errors */
+# define PPR_RER_HASHED (1 << 9) /* Frame address hashed OK */
+# define PPR_RER_IA (1 << 10) /* Frame address matched IA */
+# define PPR_RER_BROADCAST (1 << 11) /* Broadcast frame */
+# define PPR_RER_CRC (1 << 12) /* Frame had CRC error */
+# define PPR_RER_RUNT (1 << 13) /* Runt frame */
+# define PPR_RER_EXTRA (1 << 14) /* Frame was too long */
+
+#define PPR_TER 0x0128 /* Transmit event */
+# define PPR_TER_CRS (1 << 6) /* Carrier lost */
+# define PPR_TER_SQE (1 << 7) /* Signal Quality Error */
+# define PPR_TER_TXOK (1 << 8) /* Packet sent without error */
+# define PPR_TER_LATE (1 << 9) /* Out of window */
+# define PPR_TER_JABBER (1 << 10) /* Stuck transmit? */
+# define PPR_TER_NUMCOLLISIONS_SHIFT 11
+# define PPR_TER_NUMCOLLISIONS_MASK (15 << PPR_TER_NUMCOLLISIONS_SHIFT)
+# define PPR_TER_16COLLISIONS (1 << 15) /* > 16 collisions */
+
+#define PPR_BER 0x012C /* Buffer event */
+# define PPR_BER_SWINT (1 << 6) /* Software interrupt */
+# define PPR_BER_RXDMAFRAME (1 << 7) /* Received framed DMAed */
+# define PPR_BER_RDY4TX (1 << 8) /* Ready for transmission */
+# define PPR_BER_TXUNDERRUN (1 << 9) /* Transmit underrun */
+# define PPR_BER_RXMISS (1 << 10) /* Received frame missed */
+# define PPR_BER_RX128 (1 << 11) /* 128 bytes received */
+# define PPR_BER_RXDEST (1 << 15) /* Received framed passed address filter */
+
+#define PPR_RXMISS 0x0130 /* Receiver miss counter */
+#define PPR_TXCOL 0x0132 /* Transmit collision counter */
+#define PPR_LINESTAT 0x0134 /* Line status */
+# define PPR_LINESTAT_LINKOK (1 << 7) /* Line is connected and working */
+# define PPR_LINESTAT_AUI (1 << 8) /* Connected via AUI */
+# define PPR_LINESTAT_10BT (1 << 9) /* Connected via twisted pair */
+# define PPR_LINESTAT_POLARITY (1 << 12) /* Line polarity OK (10BT only) */
+# define PPR_LINESTAT_CRS (1 << 14) /* Frame being received */
+
+#define PPR_SELFSTAT 0x0136 /* Chip self status */
+# define PPR_SELFSTAT_33VACTIVE (1 << 6) /* supply voltage is 3.3V */
+# define PPR_SELFSTAT_INITD (1 << 7) /* Chip initialization complete */
+# define PPR_SELFSTAT_SIBSY (1 << 8) /* EEPROM is busy */
+# define PPR_SELFSTAT_EEPROM (1 << 9) /* EEPROM present */
+# define PPR_SELFSTAT_EEPROMOK (1 << 10) /* EEPROM checks out */
+# define PPR_SELFSTAT_ELPRESENT (1 << 11) /* External address latch logic available */
+# define PPR_SELFSTAT_EESIZE (1 << 12) /* Size of EEPROM */
+
+#define PPR_BUSSTAT 0x0138 /* Bus status */
+# define PPR_BUSSTAT_TXBID (1 << 7) /* Tx error */
+# define PPR_BUSSTAT_TXRDY (1 << 8) /* Ready for Tx data */
+
+#define PPR_TDR 0x013C /* AUI Time Domain Reflectometer */
+
+/* 0x0144 - Initiate transmit registers */
+
+#define PPR_TXCOMMAND 0x0144 /* Tx Command */
+#define PPR_TXLENGTH 0x0146 /* Tx Length */
+
+/* 0x0150 - Address filter registers */
+
+#define PPR_LAF 0x0150 /* Logical address filter (6 bytes) */
+#define PPR_IA 0x0158 /* Individual address (MAC) */
+
+/* 0x0400 - Frame location registers */
+
+#define PPR_RXSTATUS 0x0400 /* Rx Status */
+#define PPR_RXLENGTH 0x0402 /* Rx Length */
+#define PPR_RXFRAMELOCATION 0x0404 /* Rx Frame Location */
+#define PPR_TXFRAMELOCATION 0x0a00 /* Tx Frame Location */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVERS_NET_CS89x0_H */
diff --git a/nuttx/drivers/net/dm90x0.c b/nuttx/drivers/net/dm90x0.c
new file mode 100644
index 000000000..2f5b26abb
--- /dev/null
+++ b/nuttx/drivers/net/dm90x0.c
@@ -0,0 +1,1815 @@
+/****************************************************************************
+ * drivers/net/dm9x.c
+ *
+ * Copyright (C) 2007-2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Davicom data sheets (DM9000-DS-F03-041906.pdf,
+ * DM9010-DS-F01-103006.pdf) and looking at lots of other DM90x0
+ * drivers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_DM90x0)
+
+/* Only one hardware interface supported at present (although there are
+ * hooks throughout the design to that extending the support to multiple
+ * interfaces should not be that difficult)
+ */
+
+#undef CONFIG_DM9X_NINTERFACES
+#define CONFIG_DM9X_NINTERFACES 1
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <net/ethernet.h>
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* DM90000 and DM9010 register offets */
+
+#define DM9X_NETC 0x00 /* Network control register */
+#define DM9X_NETS 0x01 /* Network Status register */
+#define DM9X_TXC 0x02 /* TX control register */
+#define DM9X_TXS1 0x03 /* TX status register 1 */
+#define DM9X_TXS2 0x03 /* TX status register 2 */
+#define DM9X_RXC 0x05 /* RX control register */
+#define DM9X_RXS 0x06 /* RX status register */
+#define DM9X_RXOVF 0x07 /* Receive overflow counter register */
+#define DM9X_BPTHRES 0x08 /* Back pressure threshold register */
+#define DM9X_FCTHRES 0x09 /* Flow control threshold register */
+#define DM9X_FC 0x0a /* RX/TX flow control register */
+#define DM9X_EEPHYC 0x0b /* EEPROM & PHY control register */
+#define DM9X_EEPHYA 0x0c /* EEPROM & PHY address register */
+#define DM9X_EEPHYDL 0x0d /* EEPROM & PHY data register (lo) */
+#define DM9X_EEPHYDH 0x0e /* EEPROM & PHY data register (hi) */
+#define DM9X_WAKEUP 0x0f /* Wake-up control register */
+#define DM9X_PAB0 0x10 /* Physical address register (byte 0) */
+#define DM9X_PAB1 0x11 /* Physical address register (byte 1) */
+#define DM9X_PAB2 0x12 /* Physical address register (byte 2) */
+#define DM9X_PAB3 0x13 /* Physical address register (byte 3) */
+#define DM9X_PAB4 0x14 /* Physical address register (byte 4) */
+#define DM9X_PAB5 0x15 /* Physical address register (byte 5) */
+#define DM9X_MAB0 0x16 /* Multicast address register (byte 0) */
+#define DM9X_MAB1 0x17 /* Multicast address register (byte 1) */
+#define DM9X_MAB2 0x18 /* Multicast address register (byte 2) */
+#define DM9X_MAB3 0x19 /* Multicast address register (byte 3) */
+#define DM9X_MAB4 0x1a /* Multicast address register (byte 4) */
+#define DM9X_MAB5 0x1b /* Multicast address register (byte 5) */
+#define DM9X_MAB6 0x1c /* Multicast address register (byte 6) */
+#define DM9X_MAB7 0x1d /* Multicast address register (byte 7) */
+#define DM9X_GPC 0x1e /* General purpose control register */
+#define DM9X_GPD 0x1f /* General purpose register */
+
+#define DM9X_TRPAL 0x22 /* TX read pointer address (lo) */
+#define DM9X_TRPAH 0x23 /* TX read pointer address (hi) */
+#define DM9X_RWPAL 0x24 /* RX write pointer address (lo) */
+#define DM9X_RWPAH 0x25 /* RX write pointer address (hi) */
+
+#define DM9X_VIDL 0x28 /* Vendor ID (lo) */
+#define DM9X_VIDH 0x29 /* Vendor ID (hi) */
+#define DM9X_PIDL 0x2a /* Product ID (lo) */
+#define DM9X_PIDH 0x2b /* Product ID (hi) */
+#define DM9X_CHIPR 0x2c /* Product ID (lo) */
+#define DM9X_TXC2 0x2d /* Transmit control register 2 (dm9010) */
+#define DM9X_OTC 0x2e /* Operation test control register (dm9010) */
+#define DM9X_SMODEC 0x2f /* Special mode control register */
+#define DM9X_ETXCSR 0x30 /* Early transmit control/status register (dm9010) */
+#define DM9X_TCCR 0x31 /* Transmit checksum control register (dm9010) */
+#define DM9X_RCSR 0x32 /* Receive checksum control/status register (dm9010) */
+#define DM9X_EPHYA 0x33 /* External PHY address register (dm9010) */
+#define DM9X_GPC2 0x34 /* General purpose control register 2 (dm9010) */
+#define DM9X_GPD2 0x35 /* General purpose register 2 */
+#define DM9X_GPC3 0x36 /* General purpose control register 3 (dm9010) */
+#define DM9X_GPD3 0x37 /* General purpose register 3 */
+#define DM9X_PBUSC 0x38 /* Processor bus control register (dm9010) */
+#define DM9X_IPINC 0x39 /* INT pin control register (dm9010) */
+
+#define DM9X_MON1 0x40 /* Monitor register 1 (dm9010) */
+#define DM9X_MON2 0x41 /* Monitor register 2 (dm9010) */
+
+#define DM9X_SCLKC 0x50 /* System clock turn ON control register (dm9010) */
+#define DM9X_SCLKR 0x51 /* Resume system clock control register (dm9010) */
+
+#define DM9X_MRCMDX 0xf0 /* Memory data pre-fetch read command without address increment */
+#define DM9X_MRCMDX1 0xf1 /* memory data read command without address increment (dm9010) */
+#define DM9X_MRCMD 0xf2 /* Memory data read command with address increment */
+#define DM9X_MDRAL 0xf4 /* Memory data read address register (lo) */
+#define DM9X_MDRAH 0xf5 /* Memory data read address register (hi) */
+#define DM9X_MWCMDX 0xf6 /* Memory data write command without address increment */
+#define DM9X_MWCMD 0xf8 /* Memory data write command with address increment */
+#define DM9X_MDWAL 0xfa /* Memory data write address register (lo) */
+#define DM9X_MDWAH 0xfb /* Memory data write address register (lo) */
+#define DM9X_TXPLL 0xfc /* Memory data write address register (lo) */
+#define DM9X_TXPLH 0xfd /* Memory data write address register (hi) */
+#define DM9X_ISR 0xfe /* Interrupt status register */
+#define DM9X_IMR 0xff /* Interrupt mask register */
+
+/* Network control register bit definitions */
+
+#define DM9X_NETC_RST (1 << 0) /* Software reset */
+#define DM9X_NETC_LBKM (3 << 1) /* Loopback mode mask */
+#define DM9X_NETC_LBK0 (0 << 1) /* 0: Normal */
+#define DM9X_NETC_LBK1 (1 << 1) /* 1: MAC internal loopback */
+#define DM9X_NETC_LBK2 (2 << 1) /* 2: Internal PHY 100M mode loopback */
+#define DM9X_NETC_FDX (1 << 3) /* Full dupliex mode */
+#define DM9X_NETC_FCOL (1 << 4) /* Force collision mode */
+#define DM9X_NETC_WAKEEN (1 << 6) /* Wakeup event enable */
+#define DM9X_NETC_EXTPHY (1 << 7) /* Select external PHY */
+
+/* Network status bit definitions */
+
+#define DM9X_NETS_RXOV (1 << 1) /* RX Fifo overflow */
+#define DM9X_NETS_TX1END (1 << 2) /* TX packet 1 complete status */
+#define DM9X_NETS_TX2END (1 << 3) /* TX packet 2 complete status */
+#define DM9X_NETS_WAKEST (1 << 5) /* Wakeup event status */
+#define DM9X_NETS_LINKST (1 << 6) /* Link status */
+#define DM9X_NETS_SPEED (1 << 7) /* Media speed */
+
+/* IMR/ISR bit definitions */
+
+#define DM9X_INT_PR (1 << 0) /* Packet received interrupt */
+#define DM9X_INT_PT (1 << 1) /* Packet transmitted interrupt */
+#define DM9X_INT_RO (1 << 2) /* Receive overflow interrupt */
+#define DM9X_INT_ROO (1 << 3) /* Receive overflow counter overflow int */
+#define DM9X_INT_UDRUN (1 << 4) /* Transmit underrun interrupt */
+#define DM9X_INT_LNKCHG (1 << 5) /* Link status change interrupt */
+#define DM9X_INT_ALL (0x3f)
+
+#define DM9X_IMR_UNUSED (1 << 6) /* (not used) */
+#define DM9X_IMR_PAR (1 << 7) /* Enable auto R/W pointer reset */
+
+#define DM9X_ISR_IOMODEM (3 << 6) /* IO mode mask */
+#define DM9X_ISR_IOMODE8 (2 << 6) /* IO mode = 8 bit */
+#define DM9X_ISR_IOMODE16 (0 << 6) /* IO mode = 16 bit */
+#define DM9X_ISR_IOMODE32 (1 << 6) /* IO mode = 32 bit */
+
+#define DM9X_IMRENABLE (DM9X_INT_PR|DM9X_INT_PT|DM9X_INT_LNKCHG|DM9X_IMR_PAR)
+#define DM9X_IMRRXDISABLE (DM9X_INT_PT|DM9X_INT_LNKCHG|DM9X_IMR_PAR)
+#define DM9X_IMRDISABLE (DM9X_IMR_PAR)
+
+/* EEPROM/PHY control regiser bits */
+
+#define DM9X_EEPHYC_ERRE (1 << 0) /* EEPROM (vs PHY) access status */
+#define DM9X_EEPHYC_ERPRW (1 << 1) /* EEPROM/PHY write access */
+#define DM9X_EEPHYC_ERPRR (1 << 2) /* EEPROM/PHY read access */
+#define DM9X_EEPHYC_EPOS (1 << 3) /* EEPROM/PHY operation select */
+#define DM9X_EEPHYC_WEP (1 << 4) /* Write EEPROM enable */
+#define DM9X_EEPHYC_REEP (1 << 5) /* Reload EEPROM */
+
+/* Supported values from the vendor and product ID register */
+
+#define DM9X_DAVICOMVID 0x0a46
+#define DM9X_DM9000PID 0x9000
+#define DM9X_DM9010PID 0x9010
+
+/* RX control register bit settings */
+
+#define DM9X_RXC_RXEN (1 << 0) /* RX enable */
+#define DM9X_RXC_PRMSC (1 << 1) /* Promiscuous mode */
+#define DM9X_RXC_RUNT (1 << 2) /* Pass runt packet */
+#define DM9X_RXC_ALL (1 << 3) /* Pass all multicast */
+#define DM9X_RXC_DISCRC (1 << 4) /* Discard CRC error packets */
+#define DM9X_RXC_DISLONG (1 << 5) /* Discard long packets */
+#define DM9X_RXC_WTDIS (1 << 6) /* Disable watchdog timer */
+#define DM9X_RXC_HASHALL (1 << 7) /* Filter all addresses in hash table */
+
+#define DM9X_RXCSETUP (DM9X_RXC_DISCRC|DM9X_RXC_DISLONG)
+
+/* EEPHY bit settings */
+
+#define DM9X_EEPHYA_EROA 0x40 /* PHY register address 0x01 */
+
+#define DM9X_PKTRDY 0x01 /* Packet ready to receive */
+
+/* The RX interrupt will be disabled if more than the following RX
+ * interrupts are received back-to-back.
+ */
+
+#define DM9X_CRXTHRES 10
+
+/* All access is via an index register and a data regist. Select accecss
+ * according to user supplied base address and bus width.
+ */
+
+#if defined(CONFIG_DM9X_BUSWIDTH8)
+# define DM9X_INDEX *(volatile uint8_t*)(CONFIG_DM9X_BASE)
+# define DM9X_DATA *(volatile uint8_t*)(CONFIG_DM9X_BASE + 2)
+#elif defined(CONFIG_DM9X_BUSWIDTH16)
+# define DM9X_INDEX *(volatile uint16_t*)(CONFIG_DM9X_BASE)
+# define DM9X_DATA *(volatile uint16_t*)(CONFIG_DM9X_BASE + 2)
+#elif defined(CONFIG_DM9X_BUSWIDTH32)
+# define DM9X_INDEX *(volatile uint32_t*)(CONFIG_DM9X_BASE)
+# define DM9X_DATA *(volatile uint32_t*)(CONFIG_DM9X_BASE + 2)
+#endif
+
+/* Phy operating mode. Default is AUTO, but this setting can be overridden
+ * in the NuttX configuration file.
+ */
+
+#define DM9X_MODE_AUTO 0
+#define DM9X_MODE_10MHD 1
+#define DM9X_MODE_100MHD 2
+#define DM9X_MODE_10MFD 3
+#define DM9X_MODE_100MFD 4
+
+#ifndef CONFIG_DM9X_MODE
+# define CONFIG_DM9X_MODE DM9X_MODE_AUTO
+#endif
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define DM6X_WDDELAY (1*CLK_TCK)
+#define DM6X_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define DM6X_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)dm9x->dm_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+union rx_desc_u
+{
+ uint8_t rx_buf[4];
+ struct
+ {
+ uint8_t rx_byte;
+ uint8_t rx_status;
+ uint16_t rx_len;
+ } desc;
+};
+
+/* The dm9x_driver_s encapsulates all DM90x0 state information for a single
+ * DM90x0 hardware interface
+ */
+
+struct dm9x_driver_s
+{
+ bool dm_bifup; /* true:ifup false:ifdown */
+ bool dm_b100M; /* true:speed == 100M; false:speed == 10M */
+ WDOG_ID dm_txpoll; /* TX poll timer */
+ WDOG_ID dm_txtimeout; /* TX timeout timer */
+ uint8_t dm_ntxpending; /* Count of packets pending transmission */
+ uint8_t ncrxpackets; /* Number of continuous rx packets */
+
+ /* Mode-dependent function to move data in 8/16/32 I/O modes */
+
+ void (*dm_read)(uint8_t *ptr, int len);
+ void (*dm_write)(const uint8_t *ptr, int len);
+ void (*dm_discard)(int len);
+
+#if defined(CONFIG_DM9X_STATS)
+ uint32_t dm_ntxpackets; /* Count of packets sent */
+ uint32_t dm_ntxbytes; /* Count of bytes sent */
+ uint32_t dm_ntxerrors; /* Count of TX errors */
+ uint32_t dm_nrxpackets; /* Count of packets received */
+ uint32_t dm_nrxbytes; /* Count of bytes received */
+ uint32_t dm_nrxfifoerrors; /* Count of RX FIFO overflow errors */
+ uint32_t dm_nrxcrcerrors; /* Count of RX CRC errors */
+ uint32_t dm_nrxlengtherrors; /* Count of RX length errors */
+ uint32_t dm_nphyserrors; /* Count of physical layer errors */
+ uint32_t dm_nresets; /* Counts number of resets */
+ uint32_t dm_ntxtimeouts; /* Counts resets caused by TX timeouts */
+#endif
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dm_dev;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* At present, only a single DM90x0 device is supported. */
+
+static struct dm9x_driver_s g_dm9x[CONFIG_DM9X_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Utility functions */
+
+static uint8_t getreg(int reg);
+static void putreg(int reg, uint8_t value);
+static void read8(uint8_t *ptr, int len);
+static void read16(uint8_t *ptr, int len);
+static void read32(uint8_t *ptr, int len);
+static void discard8(int len);
+static void discard16(int len);
+static void discard32(int len);
+static void write8(const uint8_t *ptr, int len);
+static void write16(const uint8_t *ptr, int len);
+static void write32(const uint8_t *ptr, int len);
+
+/* static uint16_t dm9x_readsrom(struct dm9x_driver_s *dm9x, int offset); */
+static uint16_t dm9x_phyread(struct dm9x_driver_s *dm9x, int reg);
+static void dm9x_phywrite(struct dm9x_driver_s *dm9x, int reg, uint16_t value);
+
+#if defined(CONFIG_DM9X_STATS)
+static void dm9x_resetstatistics(struct dm9x_driver_s *dm9x);
+#else
+# define dm9x_resetstatistics(dm9x)
+#endif
+
+#if defined(CONFIG_DM9X_STATS) && defined(CONFIG_DEBUG)
+static void dm9x_dumpstatistics(struct dm9x_driver_s *dm9x);
+#else
+# define dm9x_dumpstatistics(dm9x)
+#endif
+
+#if defined(CONFIG_DM9X_CHECKSUM)
+static bool dm9x_rxchecksumready(uint8_t);
+#else
+# define dm9x_rxchecksumready(a) ((a) == 0x01)
+#endif
+
+/* Common TX logic */
+
+static int dm9x_transmit(struct dm9x_driver_s *dm9x);
+static int dm9x_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void dm9x_receive(struct dm9x_driver_s *dm9x);
+static void dm9x_txdone(struct dm9x_driver_s *dm9x);
+static int dm9x_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void dm9x_polltimer(int argc, uint32_t arg, ...);
+static void dm9x_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int dm9x_ifup(struct uip_driver_s *dev);
+static int dm9x_ifdown(struct uip_driver_s *dev);
+static int dm9x_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int dm9x_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int dm9x_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/* Initialization functions */
+
+static void dm9x_bringup(struct dm9x_driver_s *dm9x);
+static void dm9x_reset(struct dm9x_driver_s *dm9x);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: getreg and setreg
+ *
+ * Description:
+ * Access to memory-mapped DM90x0 8-bit registers
+ *
+ * Parameters:
+ * reg - Register number
+ * value - Value to write to the register (setreg only)
+ *
+ * Returned Value:
+ * Value read from the register (getreg only)
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint8_t getreg(int reg)
+{
+ DM9X_INDEX = reg;
+ return DM9X_DATA & 0xff;
+}
+
+static void putreg(int reg, uint8_t value)
+{
+ DM9X_INDEX = reg;
+ DM9X_DATA = value & 0xff;
+}
+
+/****************************************************************************
+ * Function: read8, read16, read32
+ *
+ * Description:
+ * Read packet data from the DM90x0 SRAM based on its current I/O mode
+ *
+ * Parameters:
+ * ptr - Location to write the packet data
+ * len - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void read8(uint8_t *ptr, int len)
+{
+ nvdbg("Read %d bytes (8-bit mode)\n", len);
+ for (; len > 0; len--)
+ {
+ *ptr++ = DM9X_DATA;
+ }
+}
+
+static void read16(uint8_t *ptr, int len)
+{
+ register uint16_t *ptr16 = (uint16_t*)ptr;
+ nvdbg("Read %d bytes (16-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint16_t))
+ {
+ *ptr16++ = DM9X_DATA;
+ }
+}
+
+static void read32(uint8_t *ptr, int len)
+{
+ register uint32_t *ptr32 = (uint32_t*)ptr;
+ nvdbg("Read %d bytes (32-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint32_t))
+ {
+ *ptr32++ = DM9X_DATA;
+ }
+}
+
+/****************************************************************************
+ * Function: discard8, discard16, discard32
+ *
+ * Description:
+ * Read and discard packet data in the DM90x0 SRAM based on its current
+ * I/O mode
+ *
+ * Parameters:
+ * len - The number of bytes to discard
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void discard8(int len)
+{
+ nvdbg("Discard %d bytes (8-bit mode)\n", len);
+ for (; len > 0; len--)
+ {
+ DM9X_DATA;
+ }
+}
+
+static void discard16(int len)
+{
+ nvdbg("Discard %d bytes (16-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint16_t))
+ {
+ DM9X_DATA;
+ }
+}
+
+static void discard32(int len)
+{
+ nvdbg("Discard %d bytes (32-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint32_t))
+ {
+ DM9X_DATA;
+ }
+}
+
+/****************************************************************************
+ * Function: write8, write16, write32
+ *
+ * Description:
+ * Write packet data into the DM90x0 SRAM based on its current I/O mode
+ *
+ * Parameters:
+ * ptr - Location to write the packet data
+ * len - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void write8(const uint8_t *ptr, int len)
+{
+ nvdbg("Write %d bytes (8-bit mode)\n", len);
+ for (; len > 0; len--)
+ {
+ DM9X_DATA = (*ptr++ & 0xff);
+ }
+}
+
+static void write16(const uint8_t *ptr, int len)
+{
+ register uint16_t *ptr16 = (uint16_t*)ptr;
+ nvdbg("Write %d bytes (16-bit mode)\n", len);
+
+ for (; len > 0; len -= sizeof(uint16_t))
+ {
+ DM9X_DATA = *ptr16++;
+ }
+}
+
+static void write32(const uint8_t *ptr, int len)
+{
+ register uint32_t *ptr32 = (uint32_t*)ptr;
+ nvdbg("Write %d bytes (32-bit mode)\n", len);
+ for (; len > 0; len -= sizeof(uint32_t))
+ {
+ DM9X_DATA = *ptr32++;
+ }
+}
+
+/****************************************************************************
+ * Function: dm9x_readsrom
+ *
+ * Description:
+ * Read a word from SROM
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ * offset - SROM offset to read from
+ *
+ * Returned Value:
+ * SROM content at that offset
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static uint16_t dm9x_readsrom(struct dm9x_driver_s *dm9x, int offset)
+{
+ putreg(DM9X_EEPHYA, offset);
+ putreg(DM9X_EEPHYC, DM9X_EEPHYC_ERPRR);
+ up_udelay(200);
+ putreg(DM9X_EEPHYC, 0x00);
+ return (getreg(DM9X_EEPHYDL) + (getreg(DM9X_EEPHYDH) << 8) );
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_phyread and dm9x_phywrite
+ *
+ * Description:
+ * Read/write data from/to the PHY
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ * reg - PHY register offset
+ * value - The value to write to the PHY register (dm9x_write only)
+ *
+ * Returned Value:
+ * The value read from the PHY (dm9x_read only)
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t dm9x_phyread(struct dm9x_driver_s *dm9x, int reg)
+{
+ /* Setup DM9X_EEPHYA, the EEPROM/PHY address register */
+
+ putreg(DM9X_EEPHYA, DM9X_EEPHYA_EROA | reg);
+
+ /* Issue PHY read command pulse in the EEPROM/PHY control register */
+
+ putreg(DM9X_EEPHYC, (DM9X_EEPHYC_ERPRR|DM9X_EEPHYC_EPOS));
+ up_udelay(100);
+ putreg(DM9X_EEPHYC, 0x00);
+
+ /* Return the data from the EEPROM/PHY data register pair */
+
+ return (((uint16_t)getreg(DM9X_EEPHYDH)) << 8) | (uint16_t)getreg(DM9X_EEPHYDL);
+}
+
+static void dm9x_phywrite(struct dm9x_driver_s *dm9x, int reg, uint16_t value)
+{
+ /* Setup DM9X_EEPHYA, the EEPROM/PHY address register */
+
+ putreg(DM9X_EEPHYA, DM9X_EEPHYA_EROA | reg);
+
+ /* Put the data to write in the EEPROM/PHY data register pair */
+
+ putreg(DM9X_EEPHYDL, (value & 0xff));
+ putreg(DM9X_EEPHYDH, ((value >> 8) & 0xff));
+
+ /* Issue PHY write command pulse in the EEPROM/PHY control register */
+
+ putreg(DM9X_EEPHYC, (DM9X_EEPHYC_ERPRW|DM9X_EEPHYC_EPOS));
+ up_udelay(500);
+ putreg(DM9X_EEPHYC, 0x0);
+}
+
+/****************************************************************************
+ * Function: dm9x_resetstatistics
+ *
+ * Description:
+ * Reset all DM90x0 statistics
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DM9X_STATS)
+static void dm9x_resetstatistics(struct dm9x_driver_s *dm9x)
+{
+ dm9x->dm_ntxpackets = 0; /* Count of packets sent */
+ dm9x->dm_ntxbytes = 0; /* Count of bytes sent */
+ dm9x->dm_ntxerrors = 0; /* Count of TX errors */
+ dm9x->dm_nrxpackets = 0; /* Count of packets received */
+ dm9x->dm_nrxbytes = 0; /* Count of bytes received */
+ dm9x->dm_nrxfifoerrors = 0; /* Count of RX FIFO overflow errors */
+ dm9x->dm_nrxcrcerrors = 0; /* Count of RX CRC errors */
+ dm9x->dm_nrxlengtherrors = 0; /* Count of RX length errors */
+ dm9x->dm_nphyserrors = 0; /* Count of physical layer errors */
+ dm9x->dm_nresets = 0; /* Counts number of resets */
+ dm9x->dm_ntxtimeouts = 0; /* Counts resets caused by TX timeouts */
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_dumpstatistics
+ *
+ * Description:
+ * Print the current value of all DM90x0 statistics
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DM9X_STATS) && defined(CONFIG_DEBUG)
+static void dm9x_dumpstatistics(struct dm9x_driver_s *dm9x)
+{
+ ndbg("TX packets: %d\n", dm9x->dm_ntxpackets);
+ ndbg(" bytes: %d\n", dm9x->dm_ntxbytes);
+ ndbg(" errors: %d\n", dm9x->dm_ntxerrors);
+ ndbg("RX packets: %d\n", dm9x->dm_nrxpackets);
+ ndbg(" bytes: %d\n", dm9x->dm_nrxbytes);
+ ndbg(" FIFO overflows: %d\n", dm9x->dm_nrxfifoerrors);
+ ndbg(" CRC errors: %d\n", dm9x->dm_nrxcrcerrors);
+ ndbg(" length errors: %d\n", dm9x->dm_nrxlengtherrors);
+ ndbg("Physical layer errors: %d\n", dm9x->dm_nphyserrors);
+ ndbg("Resets: %d\n", dm9x->dm_nresets);
+ ndbg("TX timeout resets: %d\n", dm9x->dm_ntxtimeouts);
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_rxchecksumready
+ *
+ * Description:
+ * Return true if the RX checksum is available
+ *
+ * Parameters:
+ * rxbyte
+ *
+ * Returned Value:
+ * true: checksum is ready
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DM9X_CHECKSUM)
+static inline bool dm9x_rxchecksumready(uint8_t rxbyte)
+{
+ if ((rxbyte & 0x01) == 0)
+ {
+ return false;
+ }
+
+ return ((rxbyte >> 4) | 0x01) != 0;
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_transmit(struct dm9x_driver_s *dm9x)
+{
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M mode,
+ * that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending < 1 || (dm9x->dm_b100M && dm9x->dm_ntxpending < 2))
+ {
+ /* Increment count of packets transmitted */
+
+ dm9x->dm_ntxpending++;
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_ntxpackets++;
+ dm9x->dm_ntxbytes += dm9x->dm_dev.d_len;
+#endif
+
+ /* Disable all DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRDISABLE);
+
+ /* Set the TX length */
+
+ putreg(DM9X_TXPLL, (dm9x->dm_dev.d_len & 0xff));
+ putreg(DM9X_TXPLH, (dm9x->dm_dev.d_len >> 8) & 0xff);
+
+ /* Move the data to be sent into TX SRAM */
+
+ DM9X_INDEX = DM9X_MWCMD;
+ dm9x->dm_write(dm9x->dm_dev.d_buf, dm9x->dm_dev.d_len);
+
+#if !defined(CONFIG_DM9X_ETRANS)
+ /* Issue TX polling command */
+
+ putreg(DM9X_TXC, 0x1); /* Cleared after TX complete*/
+#endif
+
+ /* Clear count of back-to-back RX packet transfers */
+
+ dm9x->ncrxpackets = 0;
+
+ /* Re-enable DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRENABLE);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(dm9x->dm_txtimeout, DM6X_TXTIMEOUT, dm9x_txtimeout, 1, (uint32_t)dm9x);
+ return OK;
+ }
+ return -EBUSY;
+}
+
+/****************************************************************************
+ * Function: dm9x_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the DM90x0 is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_uiptxpoll(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_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 (dm9x->dm_dev.d_len > 0)
+ {
+ uip_arp_out(&dm9x->dm_dev);
+ dm9x_transmit(dm9x);
+
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M mode,
+ * that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending > 1 || !dm9x->dm_b100M)
+ {
+ /* Returning a non-zero value will terminate the poll operation */
+
+ return 1;
+ }
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: dm9x_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_receive(struct dm9x_driver_s *dm9x)
+{
+ union rx_desc_u rx;
+ bool bchecksumready;
+ uint8_t mdrah;
+ uint8_t mdral;
+ uint8_t rxbyte;
+
+ nvdbg("Packet received\n");
+
+ do
+ {
+ /* Store the value of memory data read address register */
+
+ mdrah = getreg(DM9X_MDRAH);
+ mdral = getreg(DM9X_MDRAL);
+
+ getreg(DM9X_MRCMDX); /* Dummy read */
+ rxbyte = (uint8_t)DM9X_DATA; /* Get the most up-to-date data */
+
+ /* Packet ready for receive check */
+
+ bchecksumready = dm9x_rxchecksumready(rxbyte);
+ if (!bchecksumready)
+ {
+ break;
+ }
+
+ /* A packet is ready now. Get status/length */
+
+ DM9X_INDEX = DM9X_MRCMD; /* set read ptr ++ */
+
+ /* Read packet status & length */
+
+ dm9x->dm_read((uint8_t*)&rx, 4);
+
+ /* Check if any errors were reported by the hardware */
+
+ if (rx.desc.rx_status & 0xbf)
+ {
+ /* Bad RX packet... update statistics */
+
+#if defined(CONFIG_DM9X_STATS)
+ if (rx.desc.rx_status & 0x01)
+ {
+ dm9x->dm_nrxfifoerrors++;
+ ndbg("RX FIFO error: %d\n", dm9x->dm_nrxfifoerrors);
+ }
+
+ if (rx.desc.rx_status & 0x02)
+ {
+ dm9x->dm_nrxcrcerrors++;
+ ndbg("RX CRC error: %d\n", dm9x->dm_nrxcrcerrors);
+ }
+
+ if (rx.desc.rx_status & 0x80)
+ {
+ dm9x->dm_nrxlengtherrors++;
+ ndbg("RX length error: %d\n", dm9x->dm_nrxlengtherrors);
+ }
+
+ if (rx.desc.rx_status & 0x08)
+ {
+ dm9x->dm_nphyserrors++;
+ ndbg("Physical Layer error: %d\n", dm9x->dm_nphyserrors);
+ }
+#else
+ ndbg("Received packet with errors: %02x\n", rx.desc.rx_status);
+#endif
+ /* Drop this packet and continue to check the next packet */
+
+ dm9x->dm_discard(rx.desc.rx_len);
+ }
+
+ /* Also check if the packet is a valid size for the uIP configuration */
+
+ else if (rx.desc.rx_len < UIP_LLH_LEN || rx.desc.rx_len > (CONFIG_NET_BUFSIZE + 2))
+ {
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_nrxlengtherrors++;
+ ndbg("RX length error: %d\n", dm9x->dm_nrxlengtherrors);
+#endif
+ /* Drop this packet and continue to check the next packet */
+
+ dm9x->dm_discard(rx.desc.rx_len);
+ }
+ else
+ {
+ /* Good packet... Copy the packet data out of SRAM and pass it one to uIP */
+
+ dm9x->dm_dev.d_len = rx.desc.rx_len;
+ dm9x->dm_read(dm9x->dm_dev.d_buf, rx.desc.rx_len);
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&dm9x->dm_dev);
+ uip_input(&dm9x->dm_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 (dm9x->dm_dev.d_len > 0)
+ {
+ uip_arp_out(&dm9x->dm_dev);
+ dm9x_transmit(dm9x);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ uip_arp_arpin(&dm9x->dm_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 (dm9x->dm_dev.d_len > 0)
+ {
+ dm9x_transmit(dm9x);
+ }
+ }
+ }
+
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_nrxpackets++;
+ dm9x->dm_nrxbytes += rx.desc.rx_len;
+#endif
+ dm9x->ncrxpackets++;
+ }
+ while ((rxbyte & 0x01) == DM9X_PKTRDY && dm9x->ncrxpackets < DM9X_CRXTHRES);
+ nvdbg("All RX packets processed\n");
+}
+
+/****************************************************************************
+ * Function: dm9x_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_txdone(struct dm9x_driver_s *dm9x)
+{
+ int nsr;
+
+ nvdbg("TX done\n");
+
+ /* Another packet has completed transmission. Decrement the count of
+ * of pending TX transmissions.
+ */
+
+ nsr = getreg(DM9X_NETS);
+ if (nsr & DM9X_NETS_TX1END)
+ {
+ if (dm9x->dm_ntxpending)
+ {
+ dm9x->dm_ntxpending--;
+ }
+ else
+ {
+ ndbg("Bad TX count (TX1END)\n");
+ }
+ }
+
+ if (nsr & DM9X_NETS_TX2END)
+ {
+ if (dm9x->dm_ntxpending)
+ {
+ dm9x->dm_ntxpending--;
+ }
+ else
+ {
+ ndbg("Bad TX count (TX2END)\n");
+ }
+ }
+
+ /* Cancel the TX timeout */
+
+ if (dm9x->dm_ntxpending == 0)
+ {
+ wd_cancel(dm9x->dm_txtimeout);
+ }
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&dm9x->dm_dev, dm9x_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: dm9x_interrupt
+ *
+ * Description:
+ * DM90x0 interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_interrupt(int irq, FAR void *context)
+{
+#if CONFIG_DM9X_NINTERFACES == 1
+ register struct dm9x_driver_s *dm9x = &g_dm9x[0];
+#else
+# error "Additional logic needed to support multiple interfaces"
+#endif
+ uint8_t isr;
+ uint8_t save;
+ int i;
+
+ /* Save previous register address */
+
+ save = (uint8_t)DM9X_INDEX;
+
+ /* Disable all DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRDISABLE);
+
+ /* Get and clear the DM90x0 interrupt status bits */
+
+ isr = getreg(DM9X_ISR);
+ putreg(DM9X_ISR, isr);
+ nvdbg("Interrupt status: %02x\n", isr);
+
+ /* Check for link status change */
+
+ if (isr & DM9X_INT_LNKCHG)
+ {
+ /* Wait up to 0.5s for link OK */
+
+ for (i = 0; i < 500; i++)
+ {
+ dm9x_phyread(dm9x,0x1);
+ if (dm9x_phyread(dm9x,0x1) & 0x4) /*Link OK*/
+ {
+ /* Wait to get detected speed */
+
+ for (i = 0; i < 200; i++)
+ {
+ up_mdelay(1);
+ }
+
+ /* Set the new network speed */
+
+ if (dm9x_phyread(dm9x, 0) & 0x2000)
+ {
+ dm9x->dm_b100M = true;
+ }
+ else
+ {
+ dm9x->dm_b100M = false;
+ }
+ break;
+ }
+ up_mdelay(1);
+ }
+ ndbg("delay: %dmS speed: %s\n", i, dm9x->dm_b100M ? "100M" : "10M");
+ }
+
+ /* Check if we received an incoming packet */
+
+ if (isr & DM9X_INT_PR)
+ {
+ dm9x_receive(dm9x);
+ }
+
+ /* Check if we are able to transmit a packet */
+
+ if (isr & DM9X_INT_PT)
+ {
+ dm9x_txdone(dm9x);
+ }
+
+ /* If the number of consecutive receive packets exceeds a threshold,
+ * then disable the RX interrupt.
+ */
+
+ if (dm9x->ncrxpackets >= DM9X_CRXTHRES)
+ {
+ /* Eanble all DM90x0 interrupts EXCEPT for RX */
+
+ putreg(DM9X_IMR, DM9X_IMRRXDISABLE);
+ }
+ else
+ {
+ /* Enable all DM90x0 interrupts */
+
+ putreg(DM9X_IMR, DM9X_IMRENABLE);
+ }
+
+ /* Restore previous register address */
+
+ DM9X_INDEX = save;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the DM90x0 and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_txtimeout(int argc, uint32_t arg, ...)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)arg;
+
+ ndbg("TX timeout\n");
+
+ /* Increment statistics and dump debug info */
+
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_ntxtimeouts++;
+ dm9x->dm_ntxerrors++;
+#endif
+
+ ndbg(" TX packet count: %d\n", dm9x->dm_ntxpending);
+#if defined(CONFIG_DM9X_STATS)
+ ndbg(" TX timeouts: %d\n", dm9x->dm_ntxtimeouts);
+#endif
+ ndbg(" TX read pointer address: 0x%02x:%02x\n",
+ getreg(DM9X_TRPAH), getreg(DM9X_TRPAL));
+ ndbg(" Memory data write address: 0x%02x:%02x (DM9010)\n",
+ getreg(DM9X_MDWAH), getreg(DM9X_MDWAL));
+
+ /* Then reset the DM90x0 */
+
+ dm9x_reset(dm9x);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&dm9x->dm_dev, dm9x_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: dm9x_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_polltimer(int argc, uint32_t arg, ...)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)arg;
+
+ /* If the number of contiguous RX packets exceeds a threshold, reset the counter and
+ * re-enable RX interrupts
+ */
+
+ if (dm9x->ncrxpackets >= DM9X_CRXTHRES)
+ {
+ dm9x->ncrxpackets = 0;
+ putreg(DM9X_IMR, DM9X_IMRENABLE);
+ }
+
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M mode,
+ * that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending < 1 || (dm9x->dm_b100M && dm9x->dm_ntxpending < 2))
+ {
+ /* If so, update TCP timing states and poll uIP for new XMIT data */
+
+ (void)uip_timer(&dm9x->dm_dev, dm9x_uiptxpoll, DM6X_POLLHSEC);
+ }
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(dm9x->dm_txpoll, DM6X_WDDELAY, dm9x_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: dm9x_phymode
+ *
+ * Description:
+ * Configure the PHY operating mode
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void dm9x_phymode(struct dm9x_driver_s *dm9x)
+{
+ uint16_t phyreg0;
+ uint16_t phyreg4;
+
+#if CONFIG_DM9X_MODE == DM9X_MODE_AUTO
+ phyreg0 = 0x1200; /* Auto-negotiation & Restart Auto-negotiation */
+ phyreg4 = 0x01e1; /* Default flow control disable*/
+#elif CONFIG_DM9X_MODE == DM9X_MODE_10MHD
+ phyreg4 = 0x21;
+ phyreg0 = 0x1000;
+#elif CONFIG_DM9X_MODE == DM9X_MODE_10MFD
+ phyreg4 = 0x41;
+ phyreg0 = 0x1100;
+#elif CONFIG_DM9X_MODE == DM9X_MODE_100MHD
+ phyreg4 = 0x81;
+ phyreg0 = 0x3000;
+#elif CONFIG_DM9X_MODE == DM9X_MODE_100MFD
+ phyreg4 = 0x101;
+ phyreg0 = 0x3100;
+#else
+# error "Recognized PHY mode"
+#endif
+
+ dm9x_phywrite(dm9x, 0, phyreg0);
+ dm9x_phywrite(dm9x, 4, phyreg4);
+}
+
+/****************************************************************************
+ * Function: dm9x_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the DM90x0 interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_ifup(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+ uint8_t netstatus;
+ int i;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initilize DM90x0 chip */
+
+ dm9x_bringup(dm9x);
+
+ /* Check link state and media speed (waiting up to 3s for link OK) */
+
+ dm9x->dm_b100M = false;
+ for (i = 0; i < 3000; i++)
+ {
+ netstatus = getreg(DM9X_NETS);
+ if (netstatus & DM9X_NETS_LINKST)
+ {
+ /* Link OK... Wait a bit before getting the detected speed */
+
+ up_mdelay(200);
+ netstatus = getreg(DM9X_NETS);
+ if ((netstatus & DM9X_NETS_SPEED) == 0)
+ {
+ dm9x->dm_b100M = true;
+ }
+ break;
+ }
+ i++;
+ up_mdelay(1);
+ }
+
+ ndbg("delay: %dmS speed: %s\n", i, dm9x->dm_b100M ? "100M" : "10M");
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(dm9x->dm_txpoll, DM6X_WDDELAY, dm9x_polltimer, 1, (uint32_t)dm9x);
+
+ /* Enable the DM9X interrupt */
+
+ dm9x->dm_bifup = true;
+ up_enable_irq(CONFIG_DM9X_IRQ);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int dm9x_ifdown(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ ndbg("Stopping\n");
+
+ /* Disable the DM9X interrupt */
+
+ flags = irqsave();
+ up_disable_irq(CONFIG_DM9X_IRQ);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(dm9x->dm_txpoll);
+ wd_cancel(dm9x->dm_txtimeout);
+
+ /* Reset the device */
+
+ dm9x_phywrite(dm9x, 0x00, 0x8000); /* PHY reset */
+ putreg(DM9X_GPD, 0x01); /* Power-down PHY (GEPIO0=1) */
+ putreg(DM9X_IMR, DM9X_IMRDISABLE); /* Disable all interrupts */
+ putreg(DM9X_RXC, 0x00); /* Disable RX */
+ putreg(DM9X_ISR, DM9X_INT_ALL); /* Clear interrupt status */
+
+ dm9x->dm_bifup = false;
+ irqrestore(flags);
+
+ /* Dump statistics */
+
+ dm9x_dumpstatistics(dm9x);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int dm9x_txavail(struct uip_driver_s *dev)
+{
+ struct dm9x_driver_s *dm9x = (struct dm9x_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ ndbg("Polling\n");
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (dm9x->dm_bifup)
+ {
+
+ /* Check if there is room in the DM90x0 to hold another packet. In 100M
+ * mode, that can be 2 packets, otherwise it is a single packet.
+ */
+
+ if (dm9x->dm_ntxpending < 1 || (dm9x->dm_b100M && dm9x->dm_ntxpending < 2))
+ {
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&dm9x->dm_dev, dm9x_uiptxpoll);
+ }
+ }
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: dm9x_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int dm9x_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct dm9x_driver_s *priv = (FAR struct dm9x_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int dm9x_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct dm9x_driver_s *priv = (FAR struct dm9x_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: dm9x_bringup
+ *
+ * Description:
+ * Initialize the dm90x0 chip
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_bringup(struct dm9x_driver_s *dm9x)
+{
+ ndbg("Initializing\n");
+
+ /* Set the internal PHY power-on, GPIOs normal, and wait 2ms */
+
+ putreg(DM9X_GPD, 0x01); /* Power-down the PHY (GEPIO0=1) */
+ up_udelay(500);
+ putreg(DM9X_GPD, 0x00); /* Preactivate PHY (GPIO0=0 */
+ up_udelay(20); /* Wait 20us for PHY power-on ready */
+
+ /* Do a software reset and wait 20us (twice). The reset autoclears
+ * in 10us; 20us guarantees completion of the reset
+ */
+
+ putreg(DM9X_NETC, (DM9X_NETC_RST|DM9X_NETC_LBK1));
+ up_udelay(20);
+ putreg(DM9X_NETC, (DM9X_NETC_RST|DM9X_NETC_LBK1));
+ up_udelay(20);
+
+ /* Configure I/O mode */
+
+ switch (getreg(DM9X_ISR) & DM9X_ISR_IOMODEM)
+ {
+ case DM9X_ISR_IOMODE8:
+ dm9x->dm_read = read8;
+ dm9x->dm_write = write8;
+ dm9x->dm_discard = discard8;
+ break;
+
+ case DM9X_ISR_IOMODE16:
+ dm9x->dm_read = read16;
+ dm9x->dm_write = write16;
+ dm9x->dm_discard = discard16;
+ break;
+
+ case DM9X_ISR_IOMODE32:
+ dm9x->dm_read = read32;
+ dm9x->dm_write = write32;
+ dm9x->dm_discard = discard32;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Program PHY operating mode */
+
+ dm9x_phymode(dm9x);
+
+ /* Program operating mode */
+
+ putreg(DM9X_NETC, 0x00); /* Network control */
+ putreg(DM9X_TXC, 0x00); /* Clear TX Polling */
+ putreg(DM9X_BPTHRES, 0x3f); /* Less 3kb, 600us */
+ putreg(DM9X_SMODEC, 0x00); /* Special mode */
+ putreg(DM9X_NETS, (DM9X_NETS_WAKEST|DM9X_NETS_TX1END|DM9X_NETS_TX2END)); /* Clear TX status */
+ putreg(DM9X_ISR, DM9X_INT_ALL); /* Clear interrupt status */
+
+#if defined(CONFIG_DM9X_CHECKSUM)
+ putreg(DM9X_TCCR, 0x07); /* TX UDP/TCP/IP checksum enable */
+ putreg(DM9X_RCSR, 0x02); /* Receive checksum enable */
+#endif
+
+#if defined(CONFIG_DM9X_ETRANS)
+ putreg(DM9X_ETXCSR, 0x83);
+#endif
+
+ /* Initialize statistics */
+
+ dm9x->ncrxpackets = 0; /* Number of continuous RX packets */
+ dm9x->dm_ntxpending = 0; /* Number of pending TX packets */
+ dm9x_resetstatistics(dm9x);
+
+ /* Activate DM9000A/DM9010 */
+
+ putreg(DM9X_RXC, DM9X_RXCSETUP | 1); /* RX enable */
+ putreg(DM9X_IMR, DM9X_IMRENABLE); /* Enable TX/RX interrupts */
+}
+
+/****************************************************************************
+ * Function: dm9x_reset
+ *
+ * Description:
+ * Stop, reset, re-initialize, and restart the DM90x0 chip and driver. At
+ * present, the chip is only reset after a TX timeout.
+ *
+ * Parameters:
+ * dm9x - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void dm9x_reset(struct dm9x_driver_s *dm9x)
+{
+ uint8_t save;
+ int i;
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(dm9x->dm_txpoll);
+ wd_cancel(dm9x->dm_txtimeout);
+
+ /* Save previous register address */
+
+ save = (uint8_t)DM9X_INDEX;
+
+#if defined(CONFIG_DM9X_STATS)
+ dm9x->dm_nresets++;
+#endif
+ dm9x_bringup(dm9x);
+
+ /* Wait up to 1 second for the link to be OK */
+
+ dm9x->dm_b100M = false;
+ for (i = 0; i < 1000; i++)
+ {
+ if (dm9x_phyread(dm9x,0x1) & 0x4)
+ {
+ if (dm9x_phyread(dm9x, 0) &0x2000)
+ {
+ dm9x->dm_b100M = true;
+ }
+ break;
+ }
+ up_mdelay(1);
+ }
+
+ /* Restore previous register address */
+
+ DM9X_INDEX = save;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: dm9x_initialize
+ *
+ * Description:
+ * Initialize the DM90x0 driver
+ *
+ * Parameters:
+ * None
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+/* Initialize the DM90x0 chip and driver */
+
+int dm9x_initialize(void)
+{
+ uint8_t *mptr;
+ uint16_t vid;
+ uint16_t pid;
+ int i;
+ int j;
+
+ /* Get the chip vendor ID and product ID */
+
+ vid = (((uint16_t)getreg(DM9X_VIDH)) << 8) | (uint16_t)getreg(DM9X_VIDL);
+ pid = (((uint16_t)getreg(DM9X_PIDH)) << 8) | (uint16_t)getreg(DM9X_PIDL);
+ nlldbg("I/O base: %08x VID: %04x PID: %04x\n", CONFIG_DM9X_BASE, vid, pid);
+
+ /* Check if a DM90x0 chip is recognized at this I/O base */
+
+ if (vid != DM9X_DAVICOMVID || (pid != DM9X_DM9000PID && pid != DM9X_DM9010PID))
+ {
+ nlldbg("DM90x0 vendor/product ID not found at this base address\n");
+ return -ENODEV;
+ }
+
+ /* Attach the IRQ to the driver */
+
+ if (irq_attach(CONFIG_DM9X_IRQ, dm9x_interrupt))
+ {
+ /* We could not attach the ISR to the ISR */
+
+ nlldbg("irq_attach() failed\n");
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ memset(g_dm9x, 0, CONFIG_DM9X_NINTERFACES*sizeof(struct dm9x_driver_s));
+ g_dm9x[0].dm_dev.d_ifup = dm9x_ifup; /* I/F down callback */
+ g_dm9x[0].dm_dev.d_ifdown = dm9x_ifdown; /* I/F up (new IP address) callback */
+ g_dm9x[0].dm_dev.d_txavail = dm9x_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ g_dm9x[0].dm_dev.d_addmac = dm9x_addmac; /* Add multicast MAC address */
+ g_dm9x[0].dm_dev.d_rmmac = dm9x_rmmac; /* Remove multicast MAC address */
+#endif
+ g_dm9x[0].dm_dev.d_private = (void*)g_dm9x; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ g_dm9x[0].dm_txpoll = wd_create(); /* Create periodic poll timer */
+ g_dm9x[0].dm_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ /* Read the MAC address */
+
+ mptr = g_dm9x[0].dm_dev.d_mac.ether_addr_octet;
+ for (i = 0, j = DM9X_PAB0; i < ETHER_ADDR_LEN; i++, j++)
+ {
+ mptr[i] = getreg(j);
+ }
+
+ nlldbg("MAC: %0x:%0x:%0x:%0x:%0x:%0x\n",
+ mptr[0], mptr[1], mptr[2], mptr[3], mptr[4], mptr[5]);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&g_dm9x[0].dm_dev);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_DM90x0 */
+
diff --git a/nuttx/drivers/net/e1000.c b/nuttx/drivers/net/e1000.c
new file mode 100644
index 000000000..ec2b29b6a
--- /dev/null
+++ b/nuttx/drivers/net/e1000.c
@@ -0,0 +1,1049 @@
+/****************************************************************************
+ * drivers/net/e1000.c
+ *
+ * Copyright (C) 2011 Yu Qiang. All rights reserved.
+ * Author: Yu Qiang <yuq825@gmail.com>
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/kmalloc.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include <rgmp/pmap.h>
+#include <rgmp/string.h>
+#include <rgmp/stdio.h>
+#include <rgmp/arch/pci.h>
+#include <rgmp/memio.h>
+#include "e1000.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define E1000_WDDELAY (1*CLK_TCK)
+#define E1000_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define E1000_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)e1000->uip_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct tx_ring {
+ struct tx_desc *desc;
+ char *buf;
+ int tail; // where to write desc
+};
+
+struct rx_ring {
+ struct rx_desc *desc;
+ char *buf;
+ int head; // where to read
+ int tail; // where to release free desc
+ int free; // number of freed desc
+};
+
+struct e1000_dev {
+ uint32_t phy_mem_base;
+ uint32_t io_mem_base;
+ uint32_t mem_size;
+ int pci_dev_id;
+ unsigned char src_mac[6];
+ unsigned char dst_mac[6];
+ int irq;
+ struct irq_action int_desc;
+ struct tx_ring tx_ring;
+ struct rx_ring rx_ring;
+ struct e1000_dev *next;
+
+ // NuttX net data
+ bool bifup; /* true:ifup false:ifdown */
+ WDOG_ID txpoll; /* TX poll timer */
+ WDOG_ID txtimeout; /* TX timeout timer */
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s uip_dev; /* Interface understood by uIP */
+};
+
+struct e1000_dev_head {
+ struct e1000_dev *next;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct e1000_dev_head e1000_list = {0};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int e1000_transmit(struct e1000_dev *e1000);
+static int e1000_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void e1000_receive(struct e1000_dev *e1000);
+
+/* Watchdog timer expirations */
+
+static void e1000_polltimer(int argc, uint32_t arg, ...);
+static void e1000_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int e1000_ifup(struct uip_driver_s *dev);
+static int e1000_ifdown(struct uip_driver_s *dev);
+static int e1000_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int e1000_addmac(struct uip_driver_s *dev, const uint8_t *mac);
+static int e1000_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline void e1000_outl(struct e1000_dev *dev, int reg, uint32_t val)
+{
+ writel(dev->io_mem_base+reg, val);
+}
+
+static inline uint32_t e1000_inl(struct e1000_dev *dev, int reg)
+{
+ return readl(dev->io_mem_base+reg);
+}
+
+/****************************** e1000 driver ********************************/
+
+void e1000_reset(struct e1000_dev *dev)
+{
+ uint32_t dev_control;
+
+ // Reset the network controller hardware
+ dev_control = 0;
+ dev_control |= (1<<0); // FD-bit (Full Duplex)
+ dev_control |= (0<<2); // GIOMD-bit (GIO Master Disable)
+ dev_control |= (1<<3); // LRST-bit (Link Reset)
+ dev_control |= (1<<6); // SLU-bit (Set Link Up)
+ dev_control |= (2<<8); // SPEED=2 (1000Mbps)
+ dev_control |= (0<<11); // FRCSPD-bit (Force Speed)
+ dev_control |= (0<<12); // FRCDPLX-bit (Force Duplex)
+ dev_control |= (0<<20); // ADVD3WUC-bit (Advertise D3 Wake Up Cap)
+ dev_control |= (1<<26); // RST-bit (Device Reset)
+ dev_control |= (1<<27); // RFCE-bit (Receive Flow Control Enable)
+ dev_control |= (1<<28); // TFCE-bit (Transmit Flow Control Enable)
+ dev_control |= (0<<30); // VME-bit (VLAN Mode Enable)
+ dev_control |= (0<<31); // PHY_RST-bit (PHY Reset)
+
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+ e1000_outl(dev, E1000_STATUS, 0x00000000);
+ e1000_outl(dev, E1000_CTRL, dev_control);
+ dev_control &= ~(1<<26); // clear RST-bit (Device Reset)
+ e1000_outl(dev, E1000_CTRL, dev_control);
+ up_mdelay(10);
+ e1000_outl(dev, E1000_CTRL_EXT, 0x001401C0);
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+}
+
+void e1000_turn_on(struct e1000_dev *dev)
+{
+ int tx_control, rx_control;
+ uint32_t ims = 0;
+
+ // turn on the controller's receive engine
+ rx_control = e1000_inl(dev, E1000_RCTL);
+ rx_control |= (1<<1);
+ e1000_outl(dev, E1000_RCTL, rx_control);
+
+ // turn on the controller's transmit engine
+ tx_control = e1000_inl(dev, E1000_TCTL);
+ tx_control |= (1<<1);
+ e1000_outl(dev, E1000_TCTL, tx_control);
+
+ // enable the controller's interrupts
+ e1000_outl(dev, E1000_ICR, 0xFFFFFFFF);
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+
+ ims |= 1<<0; // TXDW
+ ims |= 1<<1; // TXQE
+ ims |= 1<<2; // LSC
+ ims |= 1<<4; // RXDMT0
+ ims |= 1<<7; // RXT0
+ e1000_outl(dev, E1000_IMS, ims);
+}
+
+void e1000_turn_off(struct e1000_dev *dev)
+{
+ int tx_control, rx_control;
+
+ // turn off the controller's receive engine
+ rx_control = e1000_inl(dev, E1000_RCTL);
+ rx_control &= ~(1<<1);
+ e1000_outl(dev, E1000_RCTL, rx_control);
+
+ // turn off the controller's transmit engine
+ tx_control = e1000_inl(dev, E1000_TCTL);
+ tx_control &= ~(1<<1);
+ e1000_outl(dev, E1000_TCTL, tx_control);
+
+ // turn off the controller's interrupts
+ e1000_outl(dev, E1000_IMC, 0xFFFFFFFF);
+}
+
+void e1000_init(struct e1000_dev *dev)
+{
+ uint32_t rxd_phys, txd_phys, kmem_phys;
+ uint32_t rx_control, tx_control;
+ uint32_t pba;
+ int i;
+
+ e1000_reset(dev);
+
+ // configure the controller's 'receive' engine
+ rx_control = 0;
+ rx_control |= (0<<1); // EN-bit (Enable)
+ rx_control |= (0<<2); // SPB-bit (Store Bad Packets)
+ rx_control |= (0<<3); // UPE-bit (Unicast Promiscuous Mode)
+ rx_control |= (1<<4); // MPE-bit (Multicast Promiscuous Mode)
+ rx_control |= (0<<5); // LPE-bit (Long Packet Enable)
+ rx_control |= (0<<6); // LBM=0 (Loop-Back Mode)
+ rx_control |= (0<<8); // RDMTS=0 (Rx Descriptor Min Threshold Size)
+ rx_control |= (0<<10); // DTYPE=0 (Descriptor Type)
+ rx_control |= (0<<12); // MO=0 (Multicast Offset)
+ rx_control |= (1<<15); // BAM-bit (Broadcast Address Mode)
+ rx_control |= (0<<16); // BSIZE=0 (Buffer Size = 2048)
+ rx_control |= (0<<18); // VLE-bit (VLAN filter Enable)
+ rx_control |= (0<<19); // CFIEN-bit (Canonical Form Indicator Enable)
+ rx_control |= (0<<20); // CFI-bit (Canonical Form Indicator)
+ rx_control |= (1<<22); // DPF-bit (Discard Pause Frames)
+ rx_control |= (0<<23); // PMCF-bit (Pass MAC Control Frames)
+ rx_control |= (0<<25); // BSEX=0 (Buffer Size EXtension)
+ rx_control |= (1<<26); // SECRC-bit (Strip Ethernet CRC)
+ rx_control |= (0<<27); // FLEXBUF=0 (Flexible Buffer size)
+ e1000_outl(dev, E1000_RCTL, rx_control);
+
+ // configure the controller's 'transmit' engine
+ tx_control = 0;
+ tx_control |= (0<<1); // EN-bit (Enable)
+ tx_control |= (1<<3); // PSP-bit (Pad Short Packets)
+ tx_control |= (15<<4); // CT=15 (Collision Threshold)
+ tx_control |= (63<<12); // COLD=63 (Collision Distance)
+ tx_control |= (0<<22); // SWXOFF-bit (Software XOFF)
+ tx_control |= (1<<24); // RTLC-bit (Re-Transmit on Late Collision)
+ tx_control |= (0<<25); // UNORTX-bit (Underrun No Re-Transmit)
+ tx_control |= (0<<26); // TXCSCMT=0 (TxDesc Mininum Threshold)
+ tx_control |= (0<<28); // MULR-bit (Multiple Request Support)
+ e1000_outl(dev, E1000_TCTL, tx_control);
+
+ // hardware flow control
+ pba = e1000_inl(dev, E1000_PBA);
+ // get receive FIFO size
+ pba = (pba & 0x000000ff)<<10;
+ e1000_outl(dev, E1000_FCAL, 0x00C28001);
+ e1000_outl(dev, E1000_FCAH, 0x00000100);
+ e1000_outl(dev, E1000_FCT, 0x00008808);
+ e1000_outl(dev, E1000_FCTTV, 0x00000680);
+ e1000_outl(dev, E1000_FCRTL, (pba*8/10)|0x80000000);
+ e1000_outl(dev, E1000_FCRTH, pba*9/10);
+
+ // setup tx rings
+ txd_phys = PADDR(dev->tx_ring.desc);
+ kmem_phys = PADDR(dev->tx_ring.buf);
+ for (i=0; i<CONFIG_E1000_N_TX_DESC; i++,kmem_phys+=CONFIG_E1000_BUFF_SIZE) {
+ dev->tx_ring.desc[i].base_address = kmem_phys;
+ dev->tx_ring.desc[i].packet_length = 0;
+ dev->tx_ring.desc[i].cksum_offset = 0;
+ dev->tx_ring.desc[i].cksum_origin = 0;
+ dev->tx_ring.desc[i].desc_status = 1;
+ dev->tx_ring.desc[i].desc_command = (1<<0)|(1<<1)|(1<<3);
+ dev->tx_ring.desc[i].special_info = 0;
+ }
+ dev->tx_ring.tail = 0;
+ e1000_outl(dev, E1000_TDT, 0);
+ e1000_outl(dev, E1000_TDH, 0);
+ // tell controller the location, size, and fetch-policy for Tx queue
+ e1000_outl(dev, E1000_TDBAL, txd_phys);
+ e1000_outl(dev, E1000_TDBAH, 0x00000000);
+ e1000_outl(dev, E1000_TDLEN, CONFIG_E1000_N_TX_DESC*16);
+ e1000_outl(dev, E1000_TXDCTL, 0x01010000);
+
+ // setup rx rings
+ rxd_phys = PADDR(dev->rx_ring.desc);
+ kmem_phys = PADDR(dev->rx_ring.buf);
+ for (i=0; i<CONFIG_E1000_N_RX_DESC; i++,kmem_phys+=CONFIG_E1000_BUFF_SIZE) {
+ dev->rx_ring.desc[i].base_address = kmem_phys;
+ dev->rx_ring.desc[i].packet_length = 0;
+ dev->rx_ring.desc[i].packet_cksum = 0;
+ dev->rx_ring.desc[i].desc_status = 0;
+ dev->rx_ring.desc[i].desc_errors = 0;
+ dev->rx_ring.desc[i].vlan_tag = 0;
+ }
+ dev->rx_ring.head = 0;
+ dev->rx_ring.tail = CONFIG_E1000_N_RX_DESC-1;
+ dev->rx_ring.free = 0;
+ // give the controller ownership of all receive descriptors
+ e1000_outl(dev, E1000_RDH, 0);
+ e1000_outl(dev, E1000_RDT, CONFIG_E1000_N_RX_DESC-1);
+ // tell controller the location, size, and fetch-policy for RX queue
+ e1000_outl(dev, E1000_RDBAL, rxd_phys);
+ e1000_outl(dev, E1000_RDBAH, 0x00000000);
+ e1000_outl(dev, E1000_RDLEN, CONFIG_E1000_N_RX_DESC*16);
+ e1000_outl(dev, E1000_RXDCTL, 0x01010000);
+
+ e1000_turn_on(dev);
+}
+
+/****************************************************************************
+ * Function: e1000_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * e1000 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int e1000_transmit(struct e1000_dev *e1000)
+{
+ int tail = e1000->tx_ring.tail;
+ unsigned char *cp = (unsigned char *)
+ (e1000->tx_ring.buf + tail * CONFIG_E1000_BUFF_SIZE);
+ int count = e1000->uip_dev.d_len;
+
+ /* 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 not transmission in progress.
+ */
+
+ if (!e1000->tx_ring.desc[tail].desc_status)
+ return -1;
+
+ /* Increment statistics */
+
+ /* Send the packet: address=skel->sk_dev.d_buf, length=skel->sk_dev.d_len */
+ memcpy(cp, e1000->uip_dev.d_buf, e1000->uip_dev.d_len);
+
+ // prepare the transmit-descriptor
+ e1000->tx_ring.desc[tail].packet_length = count<60 ? 60:count;
+ e1000->tx_ring.desc[tail].desc_status = 0;
+
+ // give ownership of this descriptor to the network controller
+ tail = (tail + 1) % CONFIG_E1000_N_TX_DESC;
+ e1000->tx_ring.tail = tail;
+ e1000_outl(e1000, E1000_TDT, tail);
+
+ /* Enable Tx interrupts */
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ wd_start(e1000->txtimeout, E1000_TXTIMEOUT, e1000_txtimeout, 1, (uint32_t)e1000);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_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
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int e1000_uiptxpoll(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+ int tail = e1000->tx_ring.tail;
+
+ /* 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 (e1000->uip_dev.d_len > 0) {
+ uip_arp_out(&e1000->uip_dev);
+ e1000_transmit(e1000);
+
+ /* Check if there is room in the device to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+ if (!e1000->tx_ring.desc[tail].desc_status)
+ return -1;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: e1000_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * e1000 - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void e1000_receive(struct e1000_dev *e1000)
+{
+ int head = e1000->rx_ring.head;
+ unsigned char *cp = (unsigned char *)
+ (e1000->rx_ring.buf + head * CONFIG_E1000_BUFF_SIZE);
+ int cnt;
+
+ while (e1000->rx_ring.desc[head].desc_status) {
+
+ /* Check for errors and update statistics */
+
+ // Here we do not handle packets that exceed packet-buffer size
+ if ((e1000->rx_ring.desc[head].desc_status & 3) == 1) {
+ cprintf("NIC READ: Oversized packet\n");
+ goto next;
+ }
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+
+ // get the number of actual data-bytes in this packet
+ cnt = e1000->rx_ring.desc[head].packet_length;
+
+ if (cnt > CONFIG_NET_BUFSIZE || cnt < 14) {
+ cprintf("NIC READ: invalid package size\n");
+ goto next;
+ }
+
+ /* Copy the data data from the hardware to e1000->uip_dev.d_buf. Set
+ * amount of data in e1000->uip_dev.d_len
+ */
+
+ // now we try to copy these data-bytes to the UIP buffer
+ memcpy(e1000->uip_dev.d_buf, cp, cnt);
+ e1000->uip_dev.d_len = cnt;
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&e1000->uip_dev);
+ uip_input(&e1000->uip_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 (e1000->uip_dev.d_len > 0) {
+ uip_arp_out(&e1000->uip_dev);
+ e1000_transmit(e1000);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP)) {
+ uip_arp_arpin(&e1000->uip_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 (e1000->uip_dev.d_len > 0) {
+ e1000_transmit(e1000);
+ }
+ }
+
+ next:
+ e1000->rx_ring.desc[head].desc_status = 0;
+ e1000->rx_ring.head = (head + 1) % CONFIG_E1000_N_RX_DESC;
+ e1000->rx_ring.free++;
+ head = e1000->rx_ring.head;
+ cp = (unsigned char *)(e1000->rx_ring.buf + head * CONFIG_E1000_BUFF_SIZE);
+ }
+}
+
+/****************************************************************************
+ * Function: e1000_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void e1000_txtimeout(int argc, uint32_t arg, ...)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ /* Then reset the hardware */
+ e1000_init(e1000);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: e1000_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void e1000_polltimer(int argc, uint32_t arg, ...)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)arg;
+ int tail = e1000->tx_ring.tail;
+
+ /* Check if there is room in the send another TX packet. We cannot perform
+ * the TX poll if he are unable to accept another packet for transmission.
+ */
+ if (!e1000->tx_ring.desc[tail].desc_status)
+ return;
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * might be bug here. Does this mean if there is a transmit in progress,
+ * we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&e1000->uip_dev, e1000_uiptxpoll, E1000_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(e1000->txpoll, E1000_WDDELAY, e1000_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: e1000_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int e1000_ifup(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
+ e1000_init(e1000);
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(e1000->txpoll, E1000_WDDELAY, e1000_polltimer, 1, (uint32_t)e1000);
+
+ if (e1000_inl(e1000, E1000_STATUS) & 2)
+ e1000->bifup = true;
+ else
+ e1000->bifup = false;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int e1000_ifdown(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+
+ e1000_turn_off(e1000);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(e1000->txpoll);
+ wd_cancel(e1000->txtimeout);
+
+ /* Put the the EMAC is its reset, non-operational state. This should be
+ * a known configuration that will guarantee the skel_ifup() always
+ * successfully brings the interface back up.
+ */
+ //e1000_reset(e1000);
+
+ /* Mark the device "down" */
+
+ e1000->bifup = false;
+ irqrestore(flags);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int e1000_txavail(struct uip_driver_s *dev)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+ int tail = e1000->tx_ring.tail;
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (e1000->bifup) {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+ if (e1000->tx_ring.desc[tail].desc_status)
+ (void)uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: e1000_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int e1000_addmac(struct uip_driver_s *dev, const uint8_t *mac)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: e1000_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int e1000_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+irqreturn_t e1000_interrupt_handler(struct Trapframe *tf, void *dev_id)
+{
+ struct e1000_dev *e1000 = (struct e1000_dev *)dev_id;
+
+ /* Get and clear interrupt status bits */
+ int intr_cause = e1000_inl(e1000, E1000_ICR);
+ e1000_outl(e1000, E1000_ICR, intr_cause);
+
+ // not for me
+ if (intr_cause == 0)
+ return IRQ_NONE;
+
+ /* Handle interrupts according to status bit settings */
+
+ // Link status change
+ if (intr_cause & (1<<2)) {
+ if (e1000_inl(e1000, E1000_STATUS) & 2)
+ e1000->bifup = true;
+ else
+ e1000->bifup = false;
+ }
+
+ /* Check if we received an incoming packet, if so, call skel_receive() */
+
+ // Rx-descriptor Timer expired
+ if (intr_cause & (1<<7))
+ e1000_receive(e1000);
+
+ // Tx queue empty
+ if (intr_cause & (1<<1))
+ wd_cancel(e1000->txtimeout);
+
+ /* Check is a packet transmission just completed. If so, call skel_txdone.
+ * This may disable further Tx interrupts if there are no pending
+ * tansmissions.
+ */
+
+ // Tx-descriptor Written back
+ if (intr_cause & (1<<0))
+ uip_poll(&e1000->uip_dev, e1000_uiptxpoll);
+
+
+ // Rx-Descriptors Low
+ if (intr_cause & (1<<4)) {
+ int tail;
+ tail = e1000->rx_ring.tail + e1000->rx_ring.free;
+ tail %= CONFIG_E1000_N_RX_DESC;
+ e1000->rx_ring.tail = tail;
+ e1000->rx_ring.free = 0;
+ e1000_outl(e1000, E1000_RDT, tail);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/******************************* PCI driver *********************************/
+
+static pci_id_t e1000_id_table[] = {
+ {.sep = {INTEL_VENDERID, E1000_82573L}},
+ {.sep = {INTEL_VENDERID, E1000_82540EM}},
+ {.sep = {INTEL_VENDERID, E1000_82574L}},
+ {.sep = {INTEL_VENDERID, E1000_82567LM}},
+ {.sep = {INTEL_VENDERID, E1000_82541PI}},
+ {.sep = {0,0}}
+};
+
+static int e1000_probe(uint16_t addr, pci_id_t id)
+{
+ uint32_t mmio_base, mmio_size;
+ uint32_t pci_cmd, size;
+ int err, irq, flags;
+ void *kmem, *omem;
+ struct e1000_dev *dev;
+
+ // alloc e1000_dev memory
+ dev = kzalloc(sizeof(struct e1000_dev));
+ if (dev == NULL)
+ return -1;
+
+ // enable device
+ err = pci_enable_device(addr, PCI_RESOURCE_MEM);
+ if (err)
+ goto error;
+
+ // get e1000 device type
+ dev->pci_dev_id = id.join;
+
+ // remap the controller's i/o-memory into kernel's address-space
+ mmio_base = pci_resource_start(addr, 0);
+ mmio_size = pci_resource_len(addr, 0);
+ err = rgmp_memmap_nocache(mmio_base, mmio_size, mmio_base);
+ if (err)
+ goto error;
+ dev->phy_mem_base = mmio_base;
+ dev->io_mem_base = mmio_base;
+ dev->mem_size = mmio_size;
+
+ // make sure the controller's Bus Master capability is enabled
+ pci_cmd = pci_config_readl(addr, PCI_COMMAND);
+ pci_cmd |= (1<<2);
+ pci_config_writel(addr, PCI_COMMAND, pci_cmd);
+
+ // MAC address
+ memset(dev->dst_mac, 0xFF, 6);
+ memcpy(dev->src_mac, (void *)(dev->io_mem_base+E1000_RA), 6);
+
+ // get e1000 IRQ
+ flags = 0;
+ irq = pci_enable_msi(addr);
+ if (irq == 0) {
+ irq = pci_read_irq(addr);
+ flags |= IDC_SHARE;
+ }
+ dev->irq = irq;
+ dev->int_desc.handler = e1000_interrupt_handler;
+ dev->int_desc.dev_id = dev;
+ err = rgmp_request_irq(irq, &dev->int_desc, flags);
+ if (err)
+ goto err0;
+
+ // Here we alloc a big block of memory once and make it
+ // aligned to page boundary and multiple of page size. This
+ // is because the memory can be modified by E1000 DMA and
+ // should be mapped no-cache which will hugely reduce memory
+ // access performance. The page size alloc will restrict
+ // this bad effect only within the memory we alloc here.
+ size = CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc) +
+ CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE +
+ CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc) +
+ CONFIG_E1000_N_RX_DESC * CONFIG_E1000_BUFF_SIZE;
+ size = ROUNDUP(size, PGSIZE);
+ omem = kmem = memalign(PGSIZE, size);
+ if (kmem == NULL) {
+ err = -ENOMEM;
+ goto err1;
+ }
+ rgmp_memremap_nocache((uintptr_t)kmem, size);
+
+ // alloc memory for tx ring
+ dev->tx_ring.desc = (struct tx_desc*)kmem;
+ kmem += CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc);
+ dev->tx_ring.buf = kmem;
+ kmem += CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE;
+
+ // alloc memory for rx rings
+ dev->rx_ring.desc = (struct rx_desc*)kmem;
+ kmem += CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc);
+ dev->rx_ring.buf = kmem;
+
+ /* Initialize the driver structure */
+
+ dev->uip_dev.d_ifup = e1000_ifup; /* I/F up (new IP address) callback */
+ dev->uip_dev.d_ifdown = e1000_ifdown; /* I/F down callback */
+ dev->uip_dev.d_txavail = e1000_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ dev->uip_dev.d_addmac = e1000_addmac; /* Add multicast MAC address */
+ dev->uip_dev.d_rmmac = e1000_rmmac; /* Remove multicast MAC address */
+#endif
+ dev->uip_dev.d_private = dev; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ dev->txpoll = wd_create(); /* Create periodic poll timer */
+ dev->txtimeout = wd_create(); /* Create TX timeout timer */
+
+ // Put the interface in the down state.
+ // e1000 reset
+ e1000_reset(dev);
+
+ /* Read the MAC address from the hardware */
+ memcpy(dev->uip_dev.d_mac.ether_addr_octet, (void *)(dev->io_mem_base+E1000_RA), 6);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+ err = netdev_register(&dev->uip_dev);
+ if (err)
+ goto err2;
+
+ // insert into e1000_list
+ dev->next = e1000_list.next;
+ e1000_list.next = dev;
+ cprintf("bring up e1000 device: %04x %08x\n", addr, id.join);
+
+ return 0;
+
+ err2:
+ rgmp_memremap((uintptr_t)omem, size);
+ free(omem);
+ err1:
+ rgmp_free_irq(irq, &dev->int_desc);
+ err0:
+ rgmp_memunmap(mmio_base, mmio_size);
+ error:
+ kfree(dev);
+ cprintf("e1000 device probe fail: %d\n", err);
+ return err;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void e1000_mod_init(void)
+{
+ pci_probe_device(e1000_id_table, e1000_probe);
+}
+
+void e1000_mod_exit(void)
+{
+ uint32_t size;
+ struct e1000_dev *dev;
+
+ size = CONFIG_E1000_N_TX_DESC * sizeof(struct tx_desc) +
+ CONFIG_E1000_N_TX_DESC * CONFIG_E1000_BUFF_SIZE +
+ CONFIG_E1000_N_RX_DESC * sizeof(struct rx_desc) +
+ CONFIG_E1000_N_RX_DESC * CONFIG_E1000_BUFF_SIZE;
+ size = ROUNDUP(size, PGSIZE);
+
+ for (dev=e1000_list.next; dev!=NULL; dev=dev->next) {
+ netdev_unregister(&dev->uip_dev);
+ e1000_reset(dev);
+ wd_delete(dev->txpoll);
+ wd_delete(dev->txtimeout);
+ rgmp_memremap((uintptr_t)dev->tx_ring.desc, size);
+ free(dev->tx_ring.desc);
+ rgmp_free_irq(dev->irq, &dev->int_desc);
+ rgmp_memunmap((uintptr_t)dev->io_mem_base, dev->mem_size);
+ kfree(dev);
+ }
+
+ e1000_list.next = NULL;
+}
diff --git a/nuttx/drivers/net/e1000.h b/nuttx/drivers/net/e1000.h
new file mode 100644
index 000000000..6614ad77e
--- /dev/null
+++ b/nuttx/drivers/net/e1000.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+ * drivers/net/e1000.h
+ *
+ * Copyright (C) 2011 Yu Qiang. All rights reserved.
+ * Author: Yu Qiang <yuq825@gmail.com>
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_E1000_H
+#define __DRIVERS_NET_E1000_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <rgmp/types.h>
+#include <rgmp/trap.h>
+#include <rgmp/arch/arch.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/************** PCI ID ***************/
+
+#define INTEL_VENDERID 0x8086
+#define E1000_82573L 0x109a
+#define E1000_82540EM 0x100e
+#define E1000_82574L 0x10d3
+#define E1000_82567LM 0x10f5
+#define E1000_82541PI 0x107c
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum e1000_registers {
+ E1000_CTRL = 0x0000, // Device Control
+ E1000_STATUS = 0x0008, // Device Status
+ E1000_CTRL_EXT = 0x0018, // Device Control Extension
+ E1000_FCAL = 0x0028, // Flow Control Address Low
+ E1000_FCAH = 0x002C, // Flow Control Address High
+ E1000_FCT = 0x0030, // Flow Control Type
+ E1000_ICR = 0x00C0, // Interrupt Cause Read
+ E1000_ICS = 0x00C8, // Interrupt Cause Set
+ E1000_IMS = 0x00D0, // Interrupt Mask Set
+ E1000_IMC = 0x00D8, // Interrupt Mask Clear
+ E1000_RCTL = 0x0100, // Receive Control
+ E1000_FCTTV = 0x0170, // Flow Control Transmit Timer Value
+ E1000_TCTL = 0x0400, // Transmit Control
+ E1000_PBA = 0x1000, // Packet Buffer Allocation
+ E1000_FCRTL = 0x2160, // Flow Control Receive Threshold Low
+ E1000_FCRTH = 0x2168, // Flow Control Receive Threshold High
+ E1000_RDBAL = 0x2800, // Rx Descriptor Base Address Low
+ E1000_RDBAH = 0x2804, // Rx Descriptor Base Address High
+ E1000_RDLEN = 0x2808, // Rx Descriptor Length
+ E1000_RDH = 0x2810, // Rx Descriptor Head
+ E1000_RDT = 0x2818, // Rx Descriptor Tail
+ E1000_RXDCTL = 0x2828, // Rx Descriptor Control
+ E1000_TDBAL = 0x3800, // Tx Descriptor Base Address Low
+ E1000_TDBAH = 0x3804, // Tx Descriptor Base Address High
+ E1000_TDLEN = 0x3808, // Tx Descriptor Length
+ E1000_TDH = 0x3810, // Tx Descriptor Head
+ E1000_TDT = 0x3818, // Tx Descriptor Tail
+ E1000_TXDCTL = 0x3828, // Tx Descriptor Control
+ E1000_TPR = 0x40D0, // Total Packets Received
+ E1000_TPT = 0x40D4, // Total Packets Transmitted
+ E1000_RA = 0x5400, // Receive-filter Array
+};
+
+/***************** e1000 device structure *****************/
+
+struct tx_desc {
+ uint64_t base_address;
+ uint16_t packet_length;
+ uint8_t cksum_offset;
+ uint8_t desc_command;
+ uint8_t desc_status;
+ uint8_t cksum_origin;
+ uint16_t special_info;
+};
+
+struct rx_desc {
+ uint64_t base_address;
+ uint16_t packet_length;
+ uint16_t packet_cksum;
+ uint8_t desc_status;
+ uint8_t desc_errors;
+ uint16_t vlan_tag;
+};
+
+#endif
diff --git a/nuttx/drivers/net/enc28j60.c b/nuttx/drivers/net/enc28j60.c
new file mode 100644
index 000000000..d3c735b11
--- /dev/null
+++ b/nuttx/drivers/net/enc28j60.c
@@ -0,0 +1,2302 @@
+/****************************************************************************
+ * drivers/net/enc28j60.c
+ *
+ * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * - ENC28J60 Data Sheet, Stand-Alone Ethernet Controller with SPI Interface,
+ * DS39662C, 2008 Microchip Technology Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_ENC28J60)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/spi.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/clock.h>
+#include <nuttx/net/enc28j60.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include "enc28j60.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* ENC28J60 Configuration Settings:
+ *
+ * CONFIG_ENC28J60 - Enabled ENC28J60 support
+ * CONFIG_ENC28J60_SPIMODE - Controls the SPI mode
+ * CONFIG_ENC28J60_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_ENC28J60_NINTERFACES - Specifies the number of physical ENC28J60
+ * devices that will be supported.
+ * CONFIG_ENC28J60_STATS - Collect network statistics
+ * CONFIG_ENC28J60_HALFDUPPLEX - Default is full duplex
+ */
+
+/* The ENC28J60 spec says that it supports SPI mode 0,0 only: "The
+ * implementation used on this device supports SPI mode 0,0 only. In
+ * addition, the SPI port requires that SCK be at Idle in a low state;
+ * selectable clock polarity is not supported." However, sometimes you
+ * need to tinker with these things.
+ */
+
+#ifndef CONFIG_ENC28J60_SPIMODE
+# define CONFIG_ENC28J60_SPIMODE SPIDEV_MODE0
+#endif
+
+/* CONFIG_ENC28J60_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_ENC28J60_NINTERFACES
+# define CONFIG_ENC28J60_NINTERFACES 1
+#endif
+
+/* CONFIG_NET_BUFSIZE must always be defined */
+
+#if !defined(CONFIG_NET_BUFSIZE) && (CONFIG_NET_BUFSIZE <= MAX_FRAMELEN)
+# error "CONFIG_NET_BUFSIZE is not valid for the ENC28J60"
+#endif
+
+/* We need to have the work queue to handle SPI interrupts */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE) && !defined(CONFIG_SPI_OWNBUS)
+# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* CONFIG_ENC28J60_DUMPPACKET will dump the contents of each packet to the console. */
+
+#ifdef CONFIG_ENC28J60_DUMPPACKET
+# define enc_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+# define enc_dumppacket(m,a,n)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define ENC_WDDELAY (1*CLK_TCK)
+#define ENC_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define ENC_TXTIMEOUT (60*CLK_TCK)
+
+/* Poll timeout */
+
+#define ENC_POLLTIMEOUT MSEC2TICK(50)
+
+/* Packet Memory ************************************************************/
+
+/* Packet memory layout */
+
+#define ALIGNED_BUFSIZE ((CONFIG_NET_BUFSIZE + 255) & ~255)
+
+#define PKTMEM_TX_START 0x0000 /* Start TX buffer at 0 */
+#define PKTMEM_TX_ENDP1 ALIGNED_BUFSIZE /* Allow TX buffer for one frame + */
+#define PKTMEM_RX_START PKTMEM_TX_ENDP1 /* Followed by RX buffer */
+#define PKTMEM_RX_END PKTMEM_END /* RX buffer goes to the end of SRAM */
+
+/* Misc. Helper Macros ******************************************************/
+
+#define enc_rdgreg(priv,ctrlreg) \
+ enc_rdgreg2(priv, ENC_RCR | GETADDR(ctrlreg))
+#define enc_wrgreg(priv,ctrlreg,wrdata) \
+ enc_wrgreg2(priv, ENC_WCR | GETADDR(ctrlreg), wrdata)
+#define enc_bfcgreg(priv,ctrlreg,clrbits) \
+ enc_wrgreg2(priv, ENC_BFC | GETADDR(ctrlreg), clrbits)
+#define enc_bfsgreg(priv,ctrlreg,setbits) \
+ enc_wrgreg2(priv, ENC_BFS | GETADDR(ctrlreg), setbits)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)priv->dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The state of the interface */
+
+enum enc_state_e
+{
+ ENCSTATE_UNIT = 0, /* The interface is in an unknown state */
+ ENCSTATE_DOWN, /* The interface is down */
+ ENCSTATE_UP /* The interface is up */
+};
+
+/* The enc_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct enc_driver_s
+{
+ /* Device control */
+
+ uint8_t ifstate; /* Interface state: See ENCSTATE_* */
+ uint8_t bank; /* Currently selected bank */
+#ifndef CONFIG_SPI_OWNBUS
+ uint8_t lockcount; /* Avoid recursive locks */
+#endif
+ uint16_t nextpkt; /* Next packet address */
+ FAR const struct enc_lower_s *lower; /* Low-level MCU-specific support */
+
+ /* Timing */
+
+ WDOG_ID txpoll; /* TX poll timer */
+ WDOG_ID txtimeout; /* TX timeout timer */
+
+ /* If we don't own the SPI bus, then we cannot do SPI accesses from the
+ * interrupt handler.
+ */
+
+#ifndef CONFIG_SPI_OWNBUS
+ struct work_s work; /* Work queue support */
+#endif
+
+ /* This is the contained SPI driver intstance */
+
+ FAR struct spi_dev_s *spi;
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dev; /* Interface understood by uIP */
+
+ /* Statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ struct enc_stats_s stats;
+#endif
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct enc_driver_s g_enc28j60[CONFIG_ENC28J60_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Low-level SPI helpers */
+
+static inline void enc_configspi(FAR struct spi_dev_s *spi);
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_select(FAR struct enc_driver_s *priv);
+static inline void enc_deselect(FAR struct enc_driver_s *priv);
+#else
+static void enc_select(FAR struct enc_driver_s *priv);
+static void enc_deselect(FAR struct enc_driver_s *priv);
+#endif
+
+/* SPI control register access */
+
+static uint8_t enc_rdgreg2(FAR struct enc_driver_s *priv, uint8_t cmd);
+static void enc_wrgreg2(FAR struct enc_driver_s *priv, uint8_t cmd,
+ uint8_t wrdata);
+static inline void enc_src(FAR struct enc_driver_s *priv);
+static void enc_setbank(FAR struct enc_driver_s *priv, uint8_t bank);
+static uint8_t enc_rdbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg);
+static void enc_wrbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t wrdata);
+static int enc_waitbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t bits, uint8_t value);
+
+/* SPI buffer transfers */
+
+static void enc_rdbuffer(FAR struct enc_driver_s *priv, FAR uint8_t *buffer,
+ size_t buflen);
+static void enc_wrbuffer(FAR struct enc_driver_s *priv,
+ FAR const uint8_t *buffer, size_t buflen);
+
+/* PHY register access */
+
+static uint16_t enc_rdphy(FAR struct enc_driver_s *priv, uint8_t phyaddr);
+static void enc_wrphy(FAR struct enc_driver_s *priv, uint8_t phyaddr,
+ uint16_t phydata);
+
+/* Common TX logic */
+
+static int enc_transmit(FAR struct enc_driver_s *priv);
+static int enc_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void enc_linkstatus(FAR struct enc_driver_s *priv);
+static void enc_txif(FAR struct enc_driver_s *priv);
+static void enc_txerif(FAR struct enc_driver_s *priv);
+static void enc_txerif(FAR struct enc_driver_s *priv);
+static void enc_rxerif(FAR struct enc_driver_s *priv);
+static void enc_rxdispath(FAR struct enc_driver_s *priv);
+static void enc_pktif(FAR struct enc_driver_s *priv);
+static void enc_worker(FAR void *arg);
+static int enc_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void enc_polltimer(int argc, uint32_t arg, ...);
+static void enc_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int enc_ifup(struct uip_driver_s *dev);
+static int enc_ifdown(struct uip_driver_s *dev);
+static int enc_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int enc_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/* Initialization */
+
+static void enc_pwrsave(FAR struct enc_driver_s *priv);
+static void enc_pwrfull(FAR struct enc_driver_s *priv);
+static void enc_setmacaddr(FAR struct enc_driver_s *priv);
+static int enc_reset(FAR struct enc_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_configspi
+ *
+ * Description:
+ * Configure the SPI for use with the ENC28J60
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void enc_configspi(FAR struct spi_dev_s *spi)
+{
+ /* Configure SPI for the ENC28J60. But only if we own the SPI bus.
+ * Otherwise, don't bother because it might change.
+ */
+
+#ifdef CONFIG_SPI_OWNBUS
+ SPI_SETMODE(spi, CONFIG_ENC28J60_SPIMODE);
+ SPI_SETBITS(spi, 8);
+#ifdef CONFIG_ENC28J60_FREQUENCY
+ SPI_SETFREQUENCY(spi, CONFIG_ENC28J60_FREQUENCY)
+#endif
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_select(FAR struct enc_driver_s *priv)
+{
+ /* We own the SPI bus, so just select the chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);
+}
+#else
+static void enc_select(FAR struct enc_driver_s *priv)
+{
+ /* Lock the SPI bus in case there are multiple devices competing for the SPI
+ * bus. First check if we already hold the lock.
+ */
+
+ if (priv->lockcount > 0)
+ {
+ /* Yes... just increment the lock count. In this case, we know
+ * that the bus has already been configured for the ENC28J60.
+ */
+
+ DEBUGASSERT(priv->lockcount < 255);
+ priv->lockcount++;
+ }
+ else
+ {
+ /* No... take the lock and set the lock count to 1 */
+
+ DEBUGASSERT(priv->lockcount == 0);
+ SPI_LOCK(priv->spi, true);
+ priv->lockcount = 1;
+
+ /* Now make sure that the SPI bus is configured for the ENC28J60 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(priv->spi, CONFIG_ENC28J60_SPIMODE);
+ SPI_SETBITS(priv->spi, 8);
+#ifdef CONFIG_ENC28J60_FREQUENCY
+ SPI_SETFREQUENCY(priv->spi, CONFIG_ENC28J60_FREQUENCY);
+#endif
+ }
+
+ /* Select ENC28J60 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_deselect
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_deselect(FAR struct enc_driver_s *priv)
+{
+ /* We own the SPI bus, so just de-select the chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+}
+#else
+static void enc_deselect(FAR struct enc_driver_s *priv)
+{
+ /* De-select ENC28J60 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+
+ /* And relinquishthe lock on the bus. If the lock count is > 1 then we
+ * are in a nested lock and we only need to decrement the lock cound.
+ */
+
+ if (priv->lockcount <= 1)
+ {
+ DEBUGASSERT(priv->lockcount == 1);
+ SPI_LOCK(priv->spi, false);
+ priv->lockcount = 0;
+ }
+ else
+ {
+ priv->lockcount--;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_rdgreg2
+ *
+ * Description:
+ * Read a global register (EIE, EIR, ESTAT, ECON2, or ECON1). The cmd
+ * include the CMD 'OR'd with the the global address register.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * cmd - The full command to received (cmd | address)
+ *
+ * Returned Value:
+ * The value read from the register
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint8_t enc_rdgreg2(FAR struct enc_driver_s *priv, uint8_t cmd)
+{
+ uint8_t rddata;
+
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the read command and collect the data. The sequence requires
+ * 16-clocks: 8 to clock out the cmd + 8 to clock in the data.
+ */
+
+ (void)SPI_SEND(priv->spi, cmd); /* Clock out the command */
+ rddata = SPI_SEND(priv->spi, 0); /* Clock in the data */
+
+ /* De-select ENC28J60 chip */
+
+ enc_deselect(priv);
+ return rddata;
+}
+
+/****************************************************************************
+ * Function: enc_wrgreg2
+ *
+ * Description:
+ * Write to a global register (EIE, EIR, ESTAT, ECON2, or ECON1). The cmd
+ * include the CMD 'OR'd with the the global address register.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * cmd - The full command to received (cmd | address)
+ * wrdata - The data to send
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrgreg2(FAR struct enc_driver_s *priv, uint8_t cmd,
+ uint8_t wrdata)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the write command and data. The sequence requires 16-clocks:
+ * 8 to clock out the cmd + 8 to clock out the data.
+ */
+
+ (void)SPI_SEND(priv->spi, cmd); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, wrdata); /* Clock out the data */
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_src
+ *
+ * Description:
+ * Send the single byte system reset command (SRC).
+ *
+ * "The System Reset Command (SRC) allows the host controller to issue a
+ * System Soft Reset command. Unlike other SPI commands, the SRC is
+ * only a single byte command and does not operate on any register. The
+ * command is started by pulling the CS pin low. The SRC opcode is the
+ * sent, followed by a 5-bit Soft Reset command constant of 1Fh. The
+ * SRC operation is terminated by raising the CS pin."
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void enc_src(FAR struct enc_driver_s *priv)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the system reset command. */
+
+ (void)SPI_SEND(priv->spi, ENC_SRC);
+
+ /* Check CLKRDY bit to see when the reset is complete. There is an errata
+ * that says the CLKRDY may be invalid. We'll wait a couple of msec to
+ * workaround this condition.
+ *
+ * Also, "After a System Reset, all PHY registers should not be read or
+ * written to until at least 50 µs have passed since the Reset has ended.
+ * All registers will revert to their Reset default values. The dual
+ * port buffer memory will maintain state throughout the System Reset."
+ */
+
+ up_mdelay(2);
+ /* while ((enc_rdgreg(priv, ENC_ESTAT) & ESTAT_CLKRDY) != 0); */
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_setbank
+ *
+ * Description:
+ * Set the bank for these next control register access.
+ *
+ * Assumption:
+ * The caller has exclusive access to the SPI bus
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * bank - The bank to select (0-3)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_setbank(FAR struct enc_driver_s *priv, uint8_t bank)
+{
+ /* Check if the bank setting has changed*/
+
+ if (bank != priv->bank)
+ {
+ /* Select bank 0 (just so that all of the bits are cleared) */
+
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_BSEL_MASK);
+
+ /* Then OR in bits to get the correct bank */
+
+ if (bank != 0)
+ {
+ enc_bfsgreg(priv, ENC_ECON1, (bank << ECON1_BSEL_SHIFT));
+ }
+
+ /* Then remember the bank setting */
+
+ priv->bank = bank;
+ }
+}
+
+/****************************************************************************
+ * Function: enc_rdbreg
+ *
+ * Description:
+ * Read from a banked control register using the RCR command.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to read
+ *
+ * Returned Value:
+ * The byte read from the banked register
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint8_t enc_rdbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg)
+{
+ uint8_t rddata;
+
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Set the bank */
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ /* Send the RCR command and collect the data. How we collect the data
+ * depends on if this is a PHY/CAN or not. The normal sequence requires
+ * 16-clocks: 8 to clock out the cmd and 8 to clock in the data.
+ */
+
+ (void)SPI_SEND(priv->spi, ENC_RCR | GETADDR(ctrlreg)); /* Clock out the command */
+ if (ISPHYMAC(ctrlreg))
+ {
+ /* The PHY/MAC sequence requires 24-clocks: 8 to clock out the cmd,
+ * 8 dummy bits, and 8 to clock in the PHY/MAC data.
+ */
+
+ (void)SPI_SEND(priv->spi, 0); /* Clock in the dummy byte */
+ }
+
+ rddata = SPI_SEND(priv->spi, 0); /* Clock in the data */
+
+ /* De-select ENC28J60 chip */
+
+ enc_deselect(priv);
+ return rddata;
+}
+
+/****************************************************************************
+ * Function: enc_wrbreg
+ *
+ * Description:
+ * Write to a banked control register using the WCR command. Unlike
+ * reading, this same SPI sequence works for normal, MAC, and PHY
+ * registers.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to write
+ * wrdata - The data to send
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t wrdata)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Set the bank */
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ /* Send the WCR command and data. The sequence requires 16-clocks:
+ * 8 to clock out the cmd + 8 to clock out the data.
+ */
+
+ (void)SPI_SEND(priv->spi, ENC_WCR | GETADDR(ctrlreg)); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, wrdata); /* Clock out the data */
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_waitbreg
+ *
+ * Description:
+ * Wait until banked register bit(s) take a specific value (or a timeout
+ * occurs).
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to check
+ * bits - The bits to check (a mask)
+ * value - The value of the bits to return (value under mask)
+ *
+ * Returned Value:
+ * OK on success, negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_waitbreg(FAR struct enc_driver_s *priv, uint8_t ctrlreg,
+ uint8_t bits, uint8_t value)
+{
+ uint32_t start = clock_systimer();
+ uint32_t elapsed;
+ uint8_t rddata;
+
+ /* Loop until the exit condition is met */
+
+ do
+ {
+ /* Read the byte from the requested banked register */
+
+ rddata = enc_rdbreg(priv, ctrlreg);
+ elapsed = clock_systimer() - start;
+ }
+ while ((rddata & bits) != value || elapsed > ENC_POLLTIMEOUT);
+
+ return (rddata & bits) == value ? -ETIMEDOUT : OK;
+}
+
+/****************************************************************************
+ * Function: enc_rdbuffer
+ *
+ * Description:
+ * Read a buffer of data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - A pointer to the buffer to read into
+ * buflen - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Read pointer is set to the correct address
+ *
+ ****************************************************************************/
+
+static void enc_rdbuffer(FAR struct enc_driver_s *priv, FAR uint8_t *buffer,
+ size_t buflen)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the read buffer memory command (ignoring the response) */
+
+ (void)SPI_SEND(priv->spi, ENC_RBM);
+
+ /* Then read the buffer data */
+
+ SPI_RECVBLOCK(priv->spi, buffer, buflen);
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_wrbuffer
+ *
+ * Description:
+ * Write a buffer of data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - A pointer to the buffer to write from
+ * buflen - The number of bytes to write
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Read pointer is set to the correct address
+ *
+ ****************************************************************************/
+
+static void enc_wrbuffer(FAR struct enc_driver_s *priv,
+ FAR const uint8_t *buffer, size_t buflen)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ enc_select(priv);
+
+ /* Send the write buffer memory command (ignoring the response) */
+
+ (void)SPI_SEND(priv->spi, ENC_WBM);
+
+ /* Then send the buffer */
+
+ SPI_SNDBLOCK(priv->spi, buffer, buflen);
+
+ /* De-select ENC28J60 chip. */
+
+ enc_deselect(priv);
+}
+
+/****************************************************************************
+ * Function: enc_rdphy
+ *
+ * Description:
+ * Read 16-bits of PHY data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * phyaddr - The PHY register address
+ *
+ * Returned Value:
+ * 16-bit value read from the PHY
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t enc_rdphy(FAR struct enc_driver_s *priv, uint8_t phyaddr)
+{
+ uint16_t data = 0;
+
+ /* Set the PHY address (and start the PHY read operation) */
+
+ enc_wrbreg(priv, ENC_MIREGADR, phyaddr);
+ enc_wrbreg(priv, ENC_MICMD, MICMD_MIIRD);
+
+ /* Wait until the PHY read completes */
+
+ if (enc_waitbreg(priv, ENC_MISTAT, MISTAT_BUSY, 0x00) == OK);
+ {
+ /* Terminate reading */
+
+ enc_wrbreg(priv, ENC_MICMD, 0x00);
+
+ /* Get the PHY data */
+
+ data = (uint16_t)enc_rdbreg(priv, ENC_MIRDL);
+ data |= (uint16_t)enc_rdbreg(priv, ENC_MIRDH) << 8;
+ }
+
+ return data;
+}
+
+/****************************************************************************
+ * Function: enc_wrphy
+ *
+ * Description:
+ * write 16-bits of PHY data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * phyaddr - The PHY register address
+ * phydata - 16-bit data to write to the PHY
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrphy(FAR struct enc_driver_s *priv, uint8_t phyaddr,
+ uint16_t phydata)
+{
+ /* Set the PHY register address */
+
+ enc_wrbreg(priv, ENC_MIREGADR, phyaddr);
+
+ /* Write the PHY data */
+
+ enc_wrbreg(priv, ENC_MIWRL, phydata);
+ enc_wrbreg(priv, ENC_MIWRH, phydata >> 8);
+
+ /* Wait until the PHY write completes */
+
+ enc_waitbreg(priv, ENC_MISTAT, MISTAT_BUSY, 0x00);
+}
+
+/****************************************************************************
+ * Function: enc_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from:
+ *
+ * - pkif interrupt when an application responds to the receipt of data
+ * by trying to send something, or
+ * - From watchdog based polling.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_transmit(FAR struct enc_driver_s *priv)
+{
+ uint16_t txend;
+
+ /* Increment statistics */
+
+ nllvdbg("Sending packet, pktlen: %d\n", priv->dev.d_len);
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txrequests++;
+#endif
+
+ /* Verify that the hardware is ready to send another packet. The driver
+ * starts a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ *
+ * NOTE: If we got here, then we have committed to sending a packet.
+ * higher level logic must have assured that (1) there is no transmission
+ * in progress, and that (2) TX-related interrupts are disabled.
+ */
+
+ DEBUGASSERT((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0);
+
+ /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */
+
+ enc_dumppacket("Transmit Packet", priv->dev.d_buf, priv->dev.d_len);
+
+ /* Reset the write pointer to start of transmit buffer */
+
+ enc_wrbreg(priv, ENC_EWRPTL, PKTMEM_TX_START & 0xff);
+ enc_wrbreg(priv, ENC_EWRPTH, PKTMEM_TX_START >> 8);
+
+ /* Set the TX End pointer based on the size of the packet to send */
+
+ txend = PKTMEM_TX_START + priv->dev.d_len;
+ enc_wrbreg(priv, ENC_ETXNDL, txend & 0xff);
+ enc_wrbreg(priv, ENC_ETXNDH, txend >> 8);
+
+ /* Write the per-packet control byte into the transmit buffer */
+
+ enc_wrgreg(priv, ENC_WBM, 0x00);
+
+ /* Copy the packet itself into the transmit buffer */
+
+ enc_wrbuffer(priv, priv->dev.d_buf, priv->dev.d_len);
+
+ /* Set TXRTS to send the packet in the transmit buffer */
+
+ enc_bfsgreg(priv, ENC_ECON1, ECON1_TXRTS);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer). Note:
+ * Is there a race condition. Could the TXIF interrupt occur before
+ * the timer is started?
+ */
+
+ (void)wd_start(priv->txtimeout, ENC_TXTIMEOUT, enc_txtimeout, 1, (uint32_t)priv);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_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
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_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.
+ */
+
+ nllvdbg("Poll result: d_len=%d\n", priv->dev.d_len);
+ if (priv->dev.d_len > 0)
+ {
+ uip_arp_out(&priv->dev);
+ enc_transmit(priv);
+
+ /* Stop the poll now because we can queue only one packet */
+
+ return -EBUSY;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_linkstatus
+ *
+ * Description:
+ * The current link status can be obtained from the PHSTAT1.LLSTAT or
+ * PHSTAT2.LSTAT.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_linkstatus(FAR struct enc_driver_s *priv)
+{
+#if 0
+ uint16_t regval = enc_rdphy(priv, ENC_PHSTAT2);
+ priv->duplex = ((regval & PHSTAT2_DPXSTAT) != 0);
+ priv->carrier = ((regval & PHSTAT2_LSTAT) != 0);
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_txif
+ *
+ * Description:
+ * An TXIF interrupt was received indicating that the last TX packet(s) is
+ * done
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txif(FAR struct enc_driver_s *priv)
+{
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txifs++;
+ if (enc_rdgreg(priv, ENC_ESTAT) & ESTAT_TXABRT)
+ {
+ priv->stats.txabrts++;
+ }
+#endif
+
+ /* Clear the request to send bit */
+
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_TXRTS);
+
+ /* If no further xmits are pending, then cancel the TX timeout */
+
+ wd_cancel(priv->txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: enc_txerif
+ *
+ * Description:
+ * An TXERIF interrupt was received indicating that a TX abort has occurred.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txerif(FAR struct enc_driver_s *priv)
+{
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txerifs++;
+#endif
+
+ /* Reset TX */
+
+ enc_bfsgreg(priv, ENC_ECON1, ECON1_TXRST);
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_TXRST | ECON1_TXRTS);
+
+ /* Here we really should re-transmit (I fact, if we want half duplex to
+ * work right, then it is necessary to do this!):
+ *
+ * 1. Read the TSV:
+ * - Read ETXNDL to get the end pointer
+ * - Read 7 bytes from that pointer + 1 using ENC_RMB.
+ * 2. Determine if we need to retransmit. Check the LATE COLLISION bit, if
+ * set, then we need to transmit.
+ * 3. Retranmit by resetting ECON1_TXRTS.
+ */
+
+#ifdef CONFIG_ENC28J60_HALFDUPLEX
+# error "Missing logic for half duplex"
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_rxerif
+ *
+ * Description:
+ * An RXERIF interrupt was received indicating that the last TX packet(s) is
+ * done
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_rxerif(FAR struct enc_driver_s *priv)
+{
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.rxerifs++;
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_rxdispath
+ *
+ * Description:
+ * Give the newly received packet to uIP.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_rxdispath(FAR struct enc_driver_s *priv)
+{
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ nllvdbg("IP packet received (%02x)\n", BUF->type);
+ uip_arp_ipin(&priv->dev);
+ uip_input(&priv->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 (priv->dev.d_len > 0)
+ {
+ uip_arp_out(&priv->dev);
+ enc_transmit(priv);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ nllvdbg("ARP packet received (%02x)\n", BUF->type);
+ uip_arp_arpin(&priv->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 (priv->dev.d_len > 0)
+ {
+ enc_transmit(priv);
+ }
+ }
+ else
+ {
+ nlldbg("Unsupported packet type dropped (%02x)\n", htons(BUF->type));
+ }
+}
+
+/****************************************************************************
+ * Function: enc_pktif
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pktif(FAR struct enc_driver_s *priv)
+{
+ uint8_t rsv[6];
+ uint16_t pktlen;
+ uint16_t rxstat;
+
+ /* Update statistics */
+
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.pktifs++;
+#endif
+
+ /* Set the read pointer to the start of the received packet */
+
+ DEBUGASSERT(priv->nextpkt <= PKTMEM_RX_END);
+ enc_wrbreg(priv, ENC_ERDPTL, (priv->nextpkt));
+ enc_wrbreg(priv, ENC_ERDPTH, (priv->nextpkt) >> 8);
+
+ /* Read the next packet pointer and the 4 byte read status vector (RSV)
+ * at the beginning of the received packet
+ */
+
+ enc_rdbuffer(priv, rsv, 6);
+
+ /* Decode the new next packet pointer, and the RSV. The
+ * RSV is encoded as:
+ *
+ * Bits 0-15: Indicates length of the received frame. This includes the
+ * destination address, source address, type/length, data,
+ * padding and CRC fields. This field is stored in little-
+ * endian format.
+ * Bits 16-31: Bit encoded RX status.
+ */
+
+ priv->nextpkt = (uint16_t)rsv[1] << 8 | (uint16_t)rsv[0];
+ pktlen = (uint16_t)rsv[3] << 8 | (uint16_t)rsv[2];
+ rxstat = (uint16_t)rsv[5] << 8 | (uint16_t)rsv[4];
+ nllvdbg("Receiving packet, pktlen: %d\n", pktlen);
+
+ /* Check if the packet was received OK */
+
+ if ((rxstat & RXSTAT_OK) == 0)
+ {
+ nlldbg("ERROR: RXSTAT: %04x\n", rxstat);
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.rxnotok++;
+#endif
+ }
+
+ /* Check for a usable packet length (4 added for the CRC) */
+
+ else if (pktlen > (CONFIG_NET_BUFSIZE + 4) || pktlen <= (UIP_LLH_LEN + 4))
+ {
+ nlldbg("Bad packet size dropped (%d)\n", pktlen);
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.rxpktlen++;
+#endif
+ }
+
+ /* Otherwise, read and process the packet */
+
+ else
+ {
+ /* Save the packet length (without the 4 byte CRC) in priv->dev.d_len*/
+
+ priv->dev.d_len = pktlen - 4;
+
+ /* Copy the data data from the receive buffer to priv->dev.d_buf */
+
+ enc_rdbuffer(priv, priv->dev.d_buf, priv->dev.d_len);
+ enc_dumppacket("Received Packet", priv->ld_dev.d_buf, priv->ld_dev.d_len);
+
+ /* Dispatch the packet to uIP */
+
+ enc_rxdispath(priv);
+ }
+
+ /* Move the RX read pointer to the start of the next received packet.
+ * This frees the memory we just read.
+ */
+
+ enc_wrbreg(priv, ENC_ERXRDPTL, (priv->nextpkt));
+ enc_wrbreg(priv, ENC_ERXRDPTH, (priv->nextpkt) >> 8);
+
+ /* Decrement the packet counter indicate we are done with this packet */
+
+ enc_bfsgreg(priv, ENC_ECON2, ECON2_PKTDEC);
+}
+
+/****************************************************************************
+ * Function: enc_worker
+ *
+ * Description:
+ * Perform interrupt handling logic outside of the interrupt handler (on
+ * the work queue thread).
+ *
+ * Parameters:
+ * arg - The reference to the driver structure (case to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_worker(FAR void *arg)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ uint8_t eir;
+
+ DEBUGASSERT(priv);
+
+ /* Disable further interrupts by clearing the global interrup enable bit */
+
+ enc_bfcgreg(priv, ENC_EIE, EIE_INTIE);
+
+ /* Loop until all interrupts have been processed (EIR==0). Note that
+ * there is no infinite loop check... if there are always pending interrupts,
+ * we are just broken.
+ */
+
+ while ((eir = enc_rdgreg(priv, ENC_EIR) & EIR_ALLINTS) != 0)
+ {
+ /* Handle interrupts according to interrupt register register bit
+ * settings
+ *
+ * DMAIF: The DMA interrupt indicates that the DMA module has completed
+ * its memory copy or checksum calculation. Additionally, this interrupt
+ * will be caused if the host controller cancels a DMA operation by
+ * manually clearing the DMAST bit. Once set, DMAIF can only be cleared
+ * by the host controller or by a Reset condition.
+ */
+
+ if ((eir & EIR_DMAIF) != 0) /* DMA interrupt */
+ {
+ /* Not used by this driver. Just clear the interrupt request. */
+
+ enc_bfcgreg(priv, ENC_EIR, EIR_DMAIF);
+ }
+
+ /* LINKIF: The LINKIF indicates that the link status has changed.
+ * The actual current link status can be obtained from the
+ * PHSTAT1.LLSTAT or PHSTAT2.LSTAT. Unlike other interrupt sources, the
+ * link status change interrupt is created in the integrated PHY
+ * module.
+ *
+ * To receive it, the host controller must set the PHIE.PLNKIE and
+ * PGEIE bits. After setting the two PHY interrupt enable bits, the
+ * LINKIF bit will then shadow the contents of the PHIR.PGIF bit.
+ *
+ * Once LINKIF is set, it can only be cleared by the host controller or
+ * by a Reset. The LINKIF bit is read-only. Performing an MII read on
+ * the PHIR register will clear the LINKIF, PGIF and PLNKIF bits
+ * automatically and allow for future link status change interrupts.
+ */
+
+ if ((eir & EIR_LINKIF) != 0) /* Link change interrupt */
+ {
+ enc_linkstatus(priv); /* Get current link status */
+ enc_rdphy(priv, ENC_PHIR); /* Clear the LINKIF interrupt */
+ }
+
+ /* TXIF: The Transmit Interrupt Flag (TXIF) is used to indicate that
+ * the requested packet transmission has ended. Upon transmission
+ * completion, abort or transmission cancellation by the host
+ * controller, the EIR.TXIF flag will be set to 1.
+ *
+ * Once TXIF is set, it can only be cleared by the host controller
+ * or by a Reset condition. Once processed, the host controller should
+ * use the BFC command to clear the EIR.TXIF bit.
+ */
+
+ if ((eir & EIR_TXIF) != 0) /* Transmit interrupt */
+ {
+ enc_txif(priv); /* Handle TX completion */
+ enc_bfcgreg(priv, ENC_EIR, EIR_TXIF); /* Clear the TXIF interrupt */
+ }
+
+ /* TXERIF: The Transmit Error Interrupt Flag (TXERIF) is used to
+ * indicate that a transmit abort has occurred. An abort can occur
+ * because of any of the following:
+ *
+ * 1. Excessive collisions occurred as defined by the Retransmission
+ * Maximum (RETMAX) bits in the MACLCON1 register.
+ * 2. A late collision occurred as defined by the Collision Window
+ * (COLWIN) bits in the MACLCON2 register.
+ * 3. A collision after transmitting 64 bytes occurred (ESTAT.LATECOL
+ * set).
+ * 4. The transmission was unable to gain an opportunity to transmit
+ * the packet because the medium was constantly occupied for too long.
+ * The deferral limit (2.4287 ms) was reached and the MACON4.DEFER bit
+ * was clear.
+ * 5. An attempt to transmit a packet larger than the maximum frame
+ * length defined by the MAMXFL registers was made without setting
+ * the MACON3.HFRMEN bit or per packet POVERRIDE and PHUGEEN bits.
+ *
+ * Upon any of these conditions, the EIR.TXERIF flag is set to 1. Once
+ * set, it can only be cleared by the host controller or by a Reset
+ * condition.
+ *
+ * After a transmit abort, the TXRTS bit will be cleared, the
+ * ESTAT.TXABRT bit will be set and the transmit status vector will be
+ * written at ETXND + 1. The MAC will not automatically attempt to
+ * retransmit the packet. The host controller may wish to read the
+ * transmit status vector and LATECOL bit to determine the cause of
+ * the abort. After determining the problem and solution, the host
+ * controller should clear the LATECOL (if set) and TXABRT bits so
+ * that future aborts can be detected accurately.
+ *
+ * In Full-Duplex mode, condition 5 is the only one that should cause
+ * this interrupt. Collisions and other problems related to sharing
+ * the network are not possible on full-duplex networks. The conditions
+ * which cause the transmit error interrupt meet the requirements of the
+ * transmit interrupt. As a result, when this interrupt occurs, TXIF
+ * will also be simultaneously set.
+ */
+
+ if ((eir & EIR_TXERIF) != 0) /* Transmit Error Interrupts */
+ {
+ enc_txerif(priv); /* Handle the TX error */
+ enc_bfcgreg(priv, ENC_EIR, EIR_TXERIF); /* Clear the TXERIF interrupt */
+ }
+
+ /* PKTIF The Receive Packet Pending Interrupt Flag (PKTIF) is used to
+ * indicate the presence of one or more data packets in the receive
+ * buffer and to provide a notification means for the arrival of new
+ * packets. When the receive buffer has at least one packet in it,
+ * EIR.PKTIF will be set. In other words, this interrupt flag will be
+ * set anytime the Ethernet Packet Count register (EPKTCNT) is non-zero.
+ *
+ * The PKTIF bit can only be cleared by the host controller or by a Reset
+ * condition. In order to clear PKTIF, the EPKTCNT register must be
+ * decremented to 0. If the last data packet in the receive buffer is
+ * processed, EPKTCNT will become zero and the PKTIF bit will automatically
+ * be cleared.
+ */
+
+ /* Ignore PKTIF because is unreliable. Use EPKTCNT instead */
+ /* if ((eir & EIR_PKTIF) != 0) */
+ {
+ uint8_t pktcnt = enc_rdbreg(priv, ENC_EPKTCNT);
+ if (pktcnt > 0)
+ {
+#ifdef CONFIG_ENC28J60_STATS
+ if (pktcnt > priv->stats.maxpktcnt)
+ {
+ priv->stats.maxpktcnt = pktcnt;
+ }
+#endif
+ /* Handle packet receipt */
+
+ enc_pktif(priv);
+ }
+ }
+
+ /* RXERIF: The Receive Error Interrupt Flag (RXERIF) is used to
+ * indicate a receive buffer overflow condition. Alternately, this
+ * interrupt may indicate that too many packets are in the receive
+ * buffer and more cannot be stored without overflowing the EPKTCNT
+ * register. When a packet is being received and the receive buffer
+ * runs completely out of space, or EPKTCNT is 255 and cannot be
+ * incremented, the packet being received will be aborted (permanently
+ * lost) and the EIR.RXERIF bit will be set to 1.
+ *
+ * Once set, RXERIF can only be cleared by the host controller or by a
+ * Reset condition. Normally, upon the receive error condition, the
+ * host controller would process any packets pending from the receive
+ * buffer and then make additional room for future packets by
+ * advancing the ERXRDPT registers (low byte first) and decrementing
+ * the EPKTCNT register.
+ *
+ * Once processed, the host controller should use the BFC command to
+ * clear the EIR.RXERIF bit.
+ */
+
+ if ((eir & EIR_RXERIF) != 0) /* Receive Errror Interrupts */
+ {
+ enc_rxerif(priv); /* Handle the RX error */
+ enc_bfcgreg(priv, ENC_EIR, EIR_RXERIF); /* Clear the RXERIF interrupt */
+ }
+
+ }
+
+ /* Enable Ethernet interrupts */
+
+ enc_bfsgreg(priv, ENC_EIE, EIE_INTIE);
+}
+
+/****************************************************************************
+ * Function: enc_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_interrupt(int irq, FAR void *context)
+{
+ register FAR struct enc_driver_s *priv = &g_enc28j60[0];
+
+#ifdef CONFIG_SPI_OWNBUS
+ /* In very simple environments, we own the SPI and can do data transfers
+ * from the interrupt handler. That is actually a very bad idea in any
+ * case because it keeps interrupts disabled for a long time.
+ */
+
+ enc_worker((FAR void*)priv);
+ return OK;
+#else
+ /* In complex environments, we cannot do SPI transfers from the interrupt
+ * handler because semaphores are probably used to lock the SPI bus. In
+ * this case, we will defer processing to the worker thread. This is also
+ * much kinder in the use of system resources and is, therefore, probably
+ * a good thing to do in any event.
+ */
+
+ return work_queue(HPWORK, &priv->work, enc_worker, (FAR void *)priv, 0);
+#endif
+}
+
+/****************************************************************************
+ * Function: enc_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ int ret;
+
+ /* Increment statistics and dump debug info */
+
+ nlldbg("Tx timeout\n");
+#ifdef CONFIG_ENC28J60_STATS
+ priv->stats.txtimeouts++;
+#endif
+
+ /* Then reset the hardware: Take the interface down, then bring it
+ * back up
+ */
+
+ ret = enc_ifdown(&priv->dev);
+ DEBUGASSERT(ret == OK);
+ ret = enc_ifup(&priv->dev);
+ DEBUGASSERT(ret == OK);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: enc_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+
+ /* Verify that the hardware is ready to send another packet. The driver
+ * start a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ */
+
+ if ((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
+ {
+ /* Yes.. update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * looks like a bug here to me. Does this mean if there is a transmit
+ * in progress, we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&priv->dev, enc_uiptxpoll, ENC_POLLHSEC);
+ }
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: enc_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_ifup(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ int ret;
+
+ nlldbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize Ethernet interface, set the MAC address, and make sure that
+ * the ENC28J80 is not in power save mode.
+ */
+
+ ret = enc_reset(priv);
+ if (ret == OK)
+ {
+ enc_setmacaddr(priv);
+ enc_pwrfull(priv);
+
+ /* Enable interrupts at the ENC28J60. Interrupts are still disabled
+ * at the interrupt controller.
+ */
+
+ enc_wrphy(priv, ENC_PHIE, PHIE_PGEIE | PHIE_PLNKIE);
+ enc_bfsgreg(priv, ENC_EIE, EIE_INTIE | EIE_PKTIE);
+ enc_bfsgreg(priv, ENC_EIR, EIR_DMAIF | EIR_LINKIF | EIR_TXIF |
+ EIR_TXERIF | EIR_RXERIF | EIR_PKTIF);
+ enc_wrgreg(priv, ENC_EIE, EIE_INTIE | EIE_PKTIE | EIE_LINKIE |
+ EIE_TXIE | EIE_TXERIE | EIE_RXERIE);
+
+ /* Enable the receiver */
+
+ enc_bfsgreg(priv, ENC_ECON1, ECON1_RXEN);
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, (uint32_t)priv);
+
+ /* Mark the interface up and enable the Ethernet interrupt at the
+ * controller
+ */
+
+ priv->ifstate = ENCSTATE_UP;
+ priv->lower->enable(priv->lower);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Function: enc_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ irqstate_t flags;
+ int ret;
+
+ nlldbg("Taking down: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ priv->lower->disable(priv->lower);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(priv->txpoll);
+ wd_cancel(priv->txtimeout);
+
+ /* Reset the device and leave in the power save state */
+
+ ret = enc_reset(priv);
+ enc_pwrsave(priv);
+
+ priv->ifstate = ENCSTATE_DOWN;
+ irqrestore(flags);
+ return ret;
+}
+
+/****************************************************************************
+ * Function: enc_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int enc_txavail(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->ifstate == ENCSTATE_UP)
+ {
+ /* Check if the hardware is ready to send another packet. The driver
+ * starts a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ */
+
+ if ((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
+ {
+ /* The interface is up and TX is idle; poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+ }
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int enc_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_pwrsave
+ *
+ * Description:
+ * The ENC28J60 may be commanded to power-down via the SPI interface.
+ * When powered down, it will no longer be able to transmit and receive
+ * any packets. To maximize power savings:
+ *
+ * 1. Turn off packet reception by clearing ECON1.RXEN.
+ * 2. Wait for any in-progress packets to finish being received by
+ * polling ESTAT.RXBUSY. This bit should be clear before proceeding.
+ * 3. Wait for any current transmissions to end by confirming ECON1.TXRTS
+ * is clear.
+ * 4. Set ECON2.VRPS (if not already set).
+ * 5. Enter Sleep by setting ECON2.PWRSV. All MAC, MII and PHY registers
+ * become inaccessible as a result. Setting PWRSV also clears
+ * ESTAT.CLKRDY automatically.
+ *
+ * In Sleep mode, all registers and buffer memory will maintain their
+ * states. The ETH registers and buffer memory will still be accessible
+ * by the host controller. Additionally, the clock driver will continue
+ * to operate. The CLKOUT function will be unaffected.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pwrsave(FAR struct enc_driver_s *priv)
+{
+ nllvdbg("Set PWRSV\n");
+
+ /* 1. Turn off packet reception by clearing ECON1.RXEN. */
+
+ enc_bfcgreg(priv, ENC_ECON1, ECON1_RXEN);
+
+ /* 2. Wait for any in-progress packets to finish being received by
+ * polling ESTAT.RXBUSY. This bit should be clear before proceeding.
+ */
+
+ if (enc_waitbreg(priv, ENC_ESTAT, ESTAT_RXBUSY, 0) == OK)
+ {
+ /* 3. Wait for any current transmissions to end by confirming
+ * ECON1.TXRTS is clear.
+ */
+
+ enc_waitbreg(priv, ENC_ECON1, ECON1_TXRTS, 0);
+
+ /* 4. Set ECON2.VRPS (if not already set). */
+ /* enc_bfsgreg(priv, ENC_ECON2, ECON2_VRPS); <-- Set in enc_reset() */
+
+ /* 5. Enter Sleep by setting ECON2.PWRSV. */
+
+ enc_bfsgreg(priv, ENC_ECON2, ECON2_PWRSV);
+ }
+}
+
+/****************************************************************************
+ * Function: enc_pwrfull
+ *
+ * Description:
+ * When normal operation is desired, the host controller must perform
+ * a slightly modified procedure:
+ *
+ * 1. Wake-up by clearing ECON2.PWRSV.
+ * 2. Wait at least 300 ìs for the PHY to stabilize. To accomplish the
+ * delay, the host controller may poll ESTAT.CLKRDY and wait for it
+ * to become set.
+ * 3. Restore receive capability by setting ECON1.RXEN.
+ *
+ * After leaving Sleep mode, there is a delay of many milliseconds
+ * before a new link is established (assuming an appropriate link
+ * partner is present). The host controller may wish to wait until
+ * the link is established before attempting to transmit any packets.
+ * The link status can be determined by polling the PHSTAT2.LSTAT bit.
+ * Alternatively, the link change interrupt may be used if it is
+ * enabled.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pwrfull(FAR struct enc_driver_s *priv)
+{
+ nllvdbg("Clear PWRSV\n");
+
+ /* 1. Wake-up by clearing ECON2.PWRSV. */
+
+ enc_bfcgreg(priv, ENC_ECON2, ECON2_PWRSV);
+
+ /* 2. Wait at least 300 ìs for the PHY to stabilize. To accomplish the
+ * delay, the host controller may poll ESTAT.CLKRDY and wait for it to
+ * become set.
+ */
+
+ enc_waitbreg(priv, ENC_ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+
+ /* 3. Restore receive capability by setting ECON1.RXEN.
+ *
+ * The caller will do this when it is read to receive packets
+ */
+}
+
+/****************************************************************************
+ * Function: enc_setmacaddr
+ *
+ * Description:
+ * Set the MAC address to the configured value. This is done after ifup
+ * or after a TX timeout. Note that this means that the interface must
+ * be down before configuring the MAC addr.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_setmacaddr(FAR struct enc_driver_s *priv)
+{
+ /* Program the hardware with it's MAC address (for filtering) */
+
+ enc_wrbreg(priv, ENC_MAADR1, priv->dev.d_mac.ether_addr_octet[5]);
+ enc_wrbreg(priv, ENC_MAADR2, priv->dev.d_mac.ether_addr_octet[4]);
+ enc_wrbreg(priv, ENC_MAADR3, priv->dev.d_mac.ether_addr_octet[3]);
+ enc_wrbreg(priv, ENC_MAADR4, priv->dev.d_mac.ether_addr_octet[2]);
+ enc_wrbreg(priv, ENC_MAADR5, priv->dev.d_mac.ether_addr_octet[1]);
+ enc_wrbreg(priv, ENC_MAADR6, priv->dev.d_mac.ether_addr_octet[0]);
+}
+
+/****************************************************************************
+ * Function: enc_reset
+ *
+ * Description:
+ * Stop, reset, re-initialize, and restart the ENC28J60. This is done
+ * initially, on ifup, and after a TX timeout.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_reset(FAR struct enc_driver_s *priv)
+{
+ uint8_t regval;
+
+ nlldbg("Reset\n");
+
+ /* Configure SPI for the ENC28J60 */
+
+ enc_configspi(priv->spi);
+
+ /* Reset the ENC28J60 */
+
+ enc_src(priv);
+
+ /* Initialize ECON1: Clear ECON1 */
+
+ enc_wrgreg(priv, ENC_ECON1, 0x00);
+
+ /* Initialize ECON2: Enable address auto increment and voltage
+ * regulator powersave.
+ */
+
+ enc_wrgreg(priv, ENC_ECON2, ECON2_AUTOINC | ECON2_VRPS);
+
+ /* Initialize receive buffer.
+ * First, set the receive buffer start address.
+ */
+
+ priv->nextpkt = PKTMEM_RX_START;
+ enc_wrbreg(priv, ENC_ERXSTL, PKTMEM_RX_START & 0xff);
+ enc_wrbreg(priv, ENC_ERXSTH, PKTMEM_RX_START >> 8);
+
+ /* Set the receive data pointer */
+
+ enc_wrbreg(priv, ENC_ERXRDPTL, PKTMEM_RX_START & 0xff);
+ enc_wrbreg(priv, ENC_ERXRDPTH, PKTMEM_RX_START >> 8);
+
+ /* Set the receive buffer end. */
+
+ enc_wrbreg(priv, ENC_ERXNDL, PKTMEM_RX_END & 0xff);
+ enc_wrbreg(priv, ENC_ERXNDH, PKTMEM_RX_END >> 8);
+
+ /* Set transmit buffer start. */
+
+ enc_wrbreg(priv, ENC_ETXSTL, PKTMEM_TX_START & 0xff);
+ enc_wrbreg(priv, ENC_ETXSTH, PKTMEM_TX_START >> 8);
+
+ /* Check if we are actually communicating with the ENC28J60. If its
+ * 0x00 or 0xff, then we are probably not communicating correctly
+ * via SPI.
+ */
+
+ regval = enc_rdbreg(priv, ENC_EREVID);
+ if (regval == 0x00 || regval == 0xff)
+ {
+ nlldbg("Bad Rev ID: %02x\n", regval);
+ return -ENODEV;
+ }
+
+ nllvdbg("Rev ID: %02x\n", regval);
+
+ /* Set filter mode: unicast OR broadcast AND crc valid */
+
+ enc_wrbreg(priv, ENC_ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN);
+
+ /* Enable MAC receive */
+
+ enc_wrbreg(priv, ENC_MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
+
+ /* Enable automatic padding and CRC operations */
+
+#ifdef CONFIG_ENC28J60_HALFDUPLEX
+ enc_wrbreg(priv, ENC_MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN);
+ enc_wrbreg(priv, ENC_MACON4, MACON4_DEFER); /* Defer transmission enable */
+
+ /* Set Non-Back-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MAIPGL, 0x12);
+ enc_wrbreg(priv, ENC_MAIPGH, 0x0c);
+
+ /* Set Back-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MABBIPG, 0x12);
+#else
+ /* Set filter mode: unicast OR broadcast AND crc valid AND Full Duplex */
+
+ enc_wrbreg(priv, ENC_MACON3,
+ MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
+
+ /* Set Non-Back-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MAIPGL, 0x12);
+
+ /* Set ack-to-Back Inter-Packet Gap */
+
+ enc_wrbreg(priv, ENC_MABBIPG, 0x15);
+#endif
+
+ /* Set the maximum packet size which the controller will accept */
+
+ enc_wrbreg(priv, ENC_MAMXFLL, CONFIG_NET_BUFSIZE & 0xff);
+ enc_wrbreg(priv, ENC_MAMXFLH, CONFIG_NET_BUFSIZE >> 8);
+
+ /* Configure LEDs (No, just use the defaults for now) */
+ /* enc_wrphy(priv, ENC_PHLCON, ??); */
+
+ /* Setup up PHCON1 & 2 */
+
+#ifdef CONFIG_ENC28J60_HALFDUPLEX
+ enc_wrphy(priv, ENC_PHCON1, 0x00);
+ enc_wrphy(priv, ENC_PHCON2, PHCON2_HDLDIS);
+#else
+ enc_wrphy(priv, ENC_PHCON1, PHCON1_PDPXMD);
+ enc_wrphy(priv, ENC_PHCON2, 0x00);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_initialize
+ *
+ * Description:
+ * Initialize the Ethernet driver. The ENC28J60 device is assumed to be
+ * in the post-reset state upon entry to this function.
+ *
+ * Parameters:
+ * spi - A reference to the platform's SPI driver for the ENC28J60
+ * lower - The MCU-specific interrupt used to control low-level MCU
+ * functions (i.e., ENC28J60 GPIO interrupts).
+ * devno - If more than one ENC28J60 is supported, then this is the
+ * zero based number that identifies the ENC28J60;
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int enc_initialize(FAR struct spi_dev_s *spi,
+ FAR const struct enc_lower_s *lower, unsigned int devno)
+{
+ FAR struct enc_driver_s *priv;
+
+ DEBUGASSERT(devno < CONFIG_ENC28J60_NINTERFACES);
+ priv = &g_enc28j60[devno];
+
+ /* Initialize the driver structure */
+
+ memset(g_enc28j60, 0, CONFIG_ENC28J60_NINTERFACES*sizeof(struct enc_driver_s));
+ priv->dev.d_ifup = enc_ifup; /* I/F down callback */
+ priv->dev.d_ifdown = enc_ifdown; /* I/F up (new IP address) callback */
+ priv->dev.d_txavail = enc_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->dev.d_addmac = enc_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = enc_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->dev.d_private = priv; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->txpoll = wd_create(); /* Create periodic poll timer */
+ priv->txtimeout = wd_create(); /* Create TX timeout timer */
+ priv->spi = spi; /* Save the SPI instance */
+ priv->lower = lower; /* Save the low-level MCU interface */
+
+ /* The interface should be in the down state. However, this function is called
+ * too early in initalization to perform the ENC28J60 reset in enc_ifdown. We
+ * are depending upon the fact that the application level logic will call enc_ifdown
+ * later to reset the ENC28J60. NOTE: The MAC address will not be set up until
+ * enc_ifup() is called. That gives the app time to set the MAC address before
+ * bringing the interface up.
+ */
+
+ priv->ifstate = ENCSTATE_UNIT;
+
+ /* Attach the interrupt to the driver (but don't enable it yet) */
+
+ if (lower->attach(lower, enc_interrupt))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ return -EAGAIN;
+ }
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ return netdev_register(&priv->dev);
+}
+
+/****************************************************************************
+ * Function: enc_stats
+ *
+ * Description:
+ * Return accumulated ENC28J60 statistics. Statistics are cleared after
+ * being returned.
+ *
+ * Parameters:
+ * devno - If more than one ENC28J60 is supported, then this is the
+ * zero based number that identifies the ENC28J60;
+ * stats - The user-provided location to return the statistics.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENC28J60_STATS
+int enc_stats(unsigned int devno, struct enc_stats_s *stats)
+{
+ FAR struct enc_driver_s *priv ;
+ irqstate_t flags;
+
+ DEBUGASSERT(devno < CONFIG_ENC28J60_NINTERFACES);
+ priv = &g_enc28j60[devno];
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ memcpy(stats, &priv->stats, sizeof(struct enc_stats_s));
+ memset(&priv->stats, 0, sizeof(struct enc_stats_s));
+ irqrestore(flags);
+ return OK;
+}
+#endif
+#endif /* CONFIG_NET && CONFIG_ENC28J60_NET */
+
diff --git a/nuttx/drivers/net/enc28j60.h b/nuttx/drivers/net/enc28j60.h
new file mode 100644
index 000000000..3c787c533
--- /dev/null
+++ b/nuttx/drivers/net/enc28j60.h
@@ -0,0 +1,478 @@
+/****************************************************************************
+ * drivers/net/enc28j60.h
+ *
+ * Copyright (C) 2010 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * - ENC28J60 Data Sheet, Stand-Alone Ethernet Controller with SPI Interface,
+ * DS39662C, 2008 Microchip Technology Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_ENC28J60_H
+#define __DRIVERS_NET_ENC28J60_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* ENC28J60 Commands ********************************************************/
+/* A total of seven instructions are implemented on the ENC28J60. Where:
+ *
+ * aaaaaa is the 5-bit address of a control register, and
+ * dddddddd is one or more bytes of data that may accompany the command.
+ */
+
+#define ENC_RCR (0x00) /* Read Control Register
+ * 000 | aaaaa | (Register value returned)) */
+#define ENC_RBM (0x3a) /* Read Buffer Memory
+ * 001 | 11010 | (Read buffer data follows) */
+#define ENC_WCR (0x40) /* Write Control Register
+ * 010 | aaaaa | dddddddd */
+#define ENC_WBM (0x7a) /* Write Buffer Memory
+ * 011 | 11010 | (Write buffer data follows) */
+#define ENC_BFS (0x80) /* Bit Field Set
+ * 100 | aaaaa | dddddddd */
+#define ENC_BFC (0xa0) /* Bit Field Clear
+ * 101 | aaaaa | dddddddd */
+#define ENC_SRC (0xff) /* System Reset
+ * 111 | 11111 | (No data) */
+
+/* Global Control Registers *************************************************/
+/* Control registers are accessed with the RCR, RBM, WCR, BFS, and BFC
+ * commands. The following identifies all ENC28J60 control registers. The
+ * control register memory is partitioned into four banks, selectable by the
+ * bank select bits, BSEL1:BSEL0, in the ECON1 register.
+ *
+ * The last five locations (0x1b to 0x1f) of all banks point to a common set
+ * of registers: EIE, EIR, ESTAT, ECON2 and ECON1. These are key registers
+ * usedin controlling and monitoring the operation of the device. Their
+ * common mapping allows easy access without switching the bank.
+ *
+ * Control registers for the ENC28J60 are generically grouped as ETH, MAC and
+ * MII registers. Register names starting with E belong to the ETH group.
+ * Similarly, registers names starting with MA belong to the MAC group and
+ * registers prefixed with MI belong to the MII group.
+ */
+
+#define ENC_EIE (0x1b) /* Ethernet Interrupt Enable Register */
+#define ENC_EIR (0x1c) /* Ethernet Interupt Request Register */
+#define ENC_ESTAT (0x1d) /* Ethernet Status Register */
+#define ENC_ECON2 (0x1e) /* Ethernet Control 2 Register */
+#define ENC_ECON1 (0x1f) /* Ethernet Control 1 Register */
+
+/* Ethernet Interrupt Enable Register Bit Definitions */
+
+#define EIE_RXERIE (1 << 0) /* Bit 0: Receive Error Interrupt Enable */
+#define EIE_TXERIE (1 << 1) /* Bit 1: Transmit Error Interrupt Enable */
+ /* Bit 2: Reserved */
+#define EIE_TXIE (1 << 3) /* Bit 3: Transmit Enable */
+#define EIE_LINKIE (1 << 4) /* Bit 4: Link Status Change Interrupt Enable */
+#define EIE_DMAIE (1 << 5) /* Bit 5: DMA Interrupt Enable */
+#define EIE_PKTIE (1 << 6) /* Bit 6: Receive Packet Pending Interrupt Enable */
+#define EIE_INTIE (1 << 7) /* Bit 7: Global INT Interrupt Enable */
+
+/* Ethernet Interrupt Request Register Bit Definitions */
+
+#define EIR_RXERIF (1 << 0) /* Bit 0: Receive Error Interrupt */
+#define EIR_TXERIF (1 << 1) /* Bit 1: Transmit Error Interrupt */
+ /* Bit 2: Reserved */
+#define EIR_TXIF (1 << 3) /* Bit 3: Transmit Interrupt */
+#define EIR_LINKIF (1 << 4) /* Bit 4: Link Change Interrupt */
+#define EIR_DMAIF (1 << 5) /* Bit 5: DMA Interrupt */
+#define EIR_PKTIF (1 << 6) /* Bit 6: Receive Packet Pending Interrupt */
+ /* Bit 7: Reserved */
+#define EIR_ALLINTS (0x7b) /* All interrupts */
+
+/* Ethernet Status Register Bit Definitions */
+
+#define ESTAT_CLKRDY (1 << 0) /* Bit 0: Clock Ready */
+#define ESTAT_TXABRT (1 << 1) /* Bit 1: Transmit Abort Error */
+#define ESTAT_RXBUSY (1 << 2) /* Bit 2: Receive Busy */
+ /* Bit 3: Reserved */
+#define ESTAT_LATECOL (1 << 4) /* Bit 4: Late Collision Error */
+ /* Bit 5: Reserved */
+#define ESTAT_BUFER (1 << 6) /* Bit 6: Ethernet Buffer Error Status */
+#define ESTAT_INT (1 << 7) /* Bit 7: INT Interrupt */
+
+/* Ethernet Control 1 Register Bit Definitions */
+
+#define ECON1_BSEL_SHIFT (0) /* Bits 0-1: Bank select */
+#define ECON1_BSEL_MASK (3 << ECON1_BSEL_SHIFT)
+# define ECON1_BSEL_BANK0 (0 << 0) /* Bank 0 */
+# define ECON1_BSEL_BANK1 (1 << 1) /* Bank 1 */
+# define ECON1_BSEL_BANK2 (2 << 0) /* Bank 2 */
+# define ECON1_BSEL_BANK3 (3 << 0) /* Bank 3 */
+#define ECON1_RXEN (1 << 2) /* Bit 2: Receive Enable */
+#define ECON1_TXRTS (1 << 3) /* Bit 3: Transmit Request to Send */
+#define ECON1_CSUMEN (1 << 4) /* Bit 4: DMA Checksum Enable */
+#define ECON1_DMAST (1 << 5) /* Bit 5: DMA Start and Busy Status */
+#define ECON1_RXRST (1 << 6) /* Bit 6: Receive Logic Reset */
+#define ECON1_TXRST (1 << 7) /* Bit 7: Transmit Logic Reset */
+
+/* Ethernet Control 2 Register */
+ /* Bits 0-2: Reserved */
+#define ECON2_VRPS (1 << 3) /* Bit 3: Voltage Regulator Power Save Enable */
+ /* Bit 4: Reserved */
+#define ECON2_PWRSV (1 << 5) /* Bit 5: Power Save Enable */
+#define ECON2_PKTDEC (1 << 6) /* Bit 6: Packet Decrement */
+#define ECON2_AUTOINC (1 << 7) /* Bit 7: Automatic Buffer Pointer Increment Enable */
+
+/* Banked Control Registers *************************************************/
+/* The remaining control registers are identified with a a 5 bit address and
+ * a bank selection. We pack the bank number and an indication if this is
+ * a MAC/PHY register access together with the control register address
+ * together to keep the design simpler.
+ */
+
+#define ENC_ADDR_SHIFT (0) /* Bits 0-4: Register address */
+#define ENC_ADDR_MASK (0x1f << ENC_ADDR_SHIFT)
+#define ENC_BANK_SHIFT (5) /* Bits 5-6: Bank number */
+#define ENC_BANK_MASK (3 << ENC_BSEL_SHIFT)
+# define ENC_BANK0 (0 << ENC_BSEL_SHIFT)
+# define ENC_BANK1 (1 << ENC_BSEL_SHIFT)
+# define ENC_BANK2 (2 << ENC_BSEL_SHIFT)
+# define ENC_BANK3 (3 << ENC_BSEL_SHIFT)
+#define ENC_PHYMAC_SHIFT (7) /* Bit 7: This is a PHY/MAC command */
+#define ENC_PHYMAC (1 << ENC_PHYMAC_SHIFT)
+
+#define REGADDR(a,b,m) ((m) << ENC_PHYMAC_SHIFT | (b) << ENC_BANK_SHIFT | (a))
+#define GETADDR(a) ((a) & ENC_ADDR_MASK)
+#define GETBANK(a) (((a) >> ENC_BANK_SHIFT) & 3)
+#define ISPHYMAC(a) (((a) & ENC_PHYMAC) != 0)
+
+/* Bank 0 Control Register Addresses */
+
+#define ENC_ERDPTL REGADDR(0x00, 0, 0) /* Read Pointer Low Byte (ERDPT<7:0> */
+#define ENC_ERDPTH REGADDR(0x01, 0, 0) /* Read Pointer High Byte (ERDPT<12:8>) */
+#define ENC_EWRPTL REGADDR(0x02, 0, 0) /* Write Pointer Low Byte (EWRPT<7:0>) */
+#define ENC_EWRPTH REGADDR(0x03, 0, 0) /* Write Pointer High Byte (EWRPT<12:8>) */
+#define ENC_ETXSTL REGADDR(0x04, 0, 0) /* TX Start Low Byte (ETXST<7:0>) */
+#define ENC_ETXSTH REGADDR(0x05, 0, 0) /* TX Start High Byte (ETXST<12:8>) */
+#define ENC_ETXNDL REGADDR(0x06, 0, 0) /* TX End Low Byte (ETXND<7:0>) */
+#define ENC_ETXNDH REGADDR(0x07, 0, 0) /* TX End High Byte (ETXND<12:8>) */
+#define ENC_ERXSTL REGADDR(0x08, 0, 0) /* RX Start Low Byte (ERXST<7:0>) */
+#define ENC_ERXSTH REGADDR(0x09, 0, 0) /* RX Start High Byte (ERXST<12:8>) */
+#define ENC_ERXNDL REGADDR(0x0a, 0, 0) /* RX End Low Byte (ERXND<7:0>) */
+#define ENC_ERXNDH REGADDR(0x0b, 0, 0) /* RX End High Byte (ERXND<12:8>) */
+#define ENC_ERXRDPTL REGADDR(0x0c, 0, 0) /* RX RD Pointer Low Byte (ERXRDPT<7:0>) */
+#define ENC_ERXRDPTH REGADDR(0x0d, 0, 0) /* RX RD Pointer High Byte (ERXRDPT<12:8>) */
+#define ENC_ERXWRPTL REGADDR(0x0e, 0, 0) /* RX WR Pointer Low Byte (ERXWRPT<7:0>) */
+#define ENC_ERXWRPTH REGADDR(0x0f, 0, 0) /* RX WR Pointer High Byte (ERXWRPT<12:8>) */
+#define ENC_EDMASTL REGADDR(0x10, 0, 0) /* DMA Start Low Byte (EDMAST<7:0>) */
+#define ENC_EDMASTH REGADDR(0x11, 0, 0) /* DMA Start High Byte (EDMAST<12:8>) */
+#define ENC_EDMANDL REGADDR(0x12, 0, 0) /* DMA End Low Byte (EDMAND<7:0>) */
+#define ENC_EDMANDH REGADDR(0x13, 0, 0) /* DMA End High Byte (EDMAND<12:8>) */
+#define ENC_EDMADSTL REGADDR(0x14, 0, 0) /* DMA Destination Low Byte (EDMADST<7:0>) */
+#define ENC_EDMADSTH REGADDR(0x15, 0, 0) /* DMA Destination High Byte (EDMADST<12:8>) */
+#define ENC_EDMACSL REGADDR(0x16, 0, 0) /* DMA Checksum Low Byte (EDMACS<7:0>) */
+#define ENC_EDMACSH REGADDR(0x17, 0, 0) /* DMA Checksum High Byte (EDMACS<15:8>) */
+ /* 0x18-0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+/* Bank 1 Control Register Addresses */
+
+#define ENC_EHT0 REGADDR(0x00, 1, 0) /* Hash Table Byte 0 (EHT<7:0>) */
+#define ENC_EHT1 REGADDR(0x01, 1, 0) /* Hash Table Byte 1 (EHT<15:8>) */
+#define ENC_EHT2 REGADDR(0x02, 1, 0) /* Hash Table Byte 2 (EHT<23:16>) */
+#define ENC_EHT3 REGADDR(0x03, 1, 0) /* Hash Table Byte 3 (EHT<31:24>) */
+#define ENC_EHT4 REGADDR(0x04, 1, 0) /* Hash Table Byte 4 (EHT<39:32>) */
+#define ENC_EHT5 REGADDR(0x05, 1, 0) /* Hash Table Byte 5 (EHT<47:40>) */
+#define ENC_EHT6 REGADDR(0x06, 1, 0) /* Hash Table Byte 6 (EHT<55:48>) */
+#define ENC_EHT7 REGADDR(0x07, 1, 0) /* Hash Table Byte 7 (EHT<63:56>) */
+#define ENC_EPMM0 REGADDR(0x08, 1, 0) /* Pattern Match Mask Byte 0 (EPMM<7:0>) */
+#define ENC_EPMM1 REGADDR(0x09, 1, 0) /* Pattern Match Mask Byte 1 (EPMM<15:8>) */
+#define ENC_EPMM2 REGADDR(0x0a, 1, 0) /* Pattern Match Mask Byte 2 (EPMM<23:16>) */
+#define ENC_EPMM3 REGADDR(0x0b, 1, 0) /* Pattern Match Mask Byte 3 (EPMM<31:24>) */
+#define ENC_EPMM4 REGADDR(0x0c, 1, 0) /* Pattern Match Mask Byte 4 (EPMM<39:32>) */
+#define ENC_EPMM5 REGADDR(0x0d, 1, 0) /* Pattern Match Mask Byte 5 (EPMM<47:40>) */
+#define ENC_EPMM6 REGADDR(0x0e, 1, 0) /* Pattern Match Mask Byte 6 (EPMM<55:48>) */
+#define ENC_EPMM7 REGADDR(0x0f, 1, 0) /* Pattern Match Mask Byte 7 (EPMM<63:56>) */
+#define ENC_EPMCSL REGADDR(0x10, 1, 0) /* Pattern Match Checksum Low Byte (EPMCS<7:0>) */
+#define ENC_EPMCSH REGADDR(0x11, 1, 0) /* Pattern Match Checksum High Byte (EPMCS<15:0>) */
+ /* 0x12-0x13: Reserved */
+#define ENC_EPMOL REGADDR(0x14, 1, 0) /* Pattern Match Offset Low Byte (EPMO<7:0>) */
+#define ENC_EPMOH REGADDR(0x15, 1, 0) /* Pattern Match Offset High Byte (EPMO<12:8>) */
+ /* 0x16-0x17: Reserved */
+#define ENC_ERXFCON REGADDR(0x18, 1, 0) /* Receive Filter Configuration */
+#define ENC_EPKTCNT REGADDR(0x19, 1, 0) /* Ethernet Packet Count */
+ /* 0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+
+/* Receive Filter Configuration Bit Definitions */
+
+#define ERXFCON_BCEN (1 << 0) /* Bit 0: Broadcast Filter Enable */
+#define ERXFCON_MCEN (1 << 1) /* Bit 1: Multicast Filter Enable */
+#define ERXFCON_HTEN (1 << 2) /* Bit 2: Hash Table Filter Enable */
+#define ERXFCON_MPEN (1 << 3) /* Bit 3: Magic Packet Filter Enable */
+#define ERXFCON_PMEN (1 << 4) /* Bit 4: Pattern Match Filter Enable */
+#define ERXFCON_CRCEN (1 << 5) /* Bit 5: Post-Filter CRC Check Enable */
+#define ERXFCON_ANDOR (1 << 6) /* Bit 6: AND/OR Filter Select */
+#define ERXFCON_UCEN (1 << 7) /* Bit 7: Unicast Filter Enable */
+
+/* Bank 2 Control Register Addresses */
+
+#define ENC_MACON1 REGADDR(0x00, 2, 1) /* MAC Control 1 */
+ /* 0x01: Reserved */
+#define ENC_MACON3 REGADDR(0x02, 2, 1) /* MAC Control 3 */
+#define ENC_MACON4 REGADDR(0x03, 2, 1) /* MAC Control 4 */
+#define ENC_MABBIPG REGADDR(0x04, 2, 1) /* Back-to-Back Inter-Packet Gap (BBIPG<6:0>) */
+ /* 0x05: Reserved */
+#define ENC_MAIPGL REGADDR(0x06, 2, 1) /* Non-Back-to-Back Inter-Packet Gap Low Byte (MAIPGL<6:0>) */
+#define ENC_MAIPGH REGADDR(0x07, 2, 1) /* Non-Back-to-Back Inter-Packet Gap High Byte (MAIPGH<6:0>) */
+#define ENC_MACLCON1 REGADDR(0x08, 2, 1) /* MAC Collision Control 1 */
+#define ENC_MACLCON2 REGADDR(0x09, 2, 1) /* MAC Collision Control 2 */
+#define ENC_MAMXFLL REGADDR(0x0a, 2, 1) /* Maximum Frame Length Low Byte (MAMXFL<7:0>) */
+#define ENC_MAMXFLH REGADDR(0x0b, 2, 1) /* Maximum Frame Length High Byte (MAMXFL<15:8>) */
+ /* 0x0c-0x11: Reserved */
+#define ENC_MICMD REGADDR(0x12, 2, 1) /* MII Command Register */
+ /* 0x13: Reserved */
+#define ENC_MIREGADR REGADDR(0x14, 2, 1) /* MII Register Address */
+ /* 0x15: Reserved */
+#define ENC_MIWRL REGADDR(0x16, 2, 1) /* MII Write Data Low Byte (MIWR<7:0>) */
+#define ENC_MIWRH REGADDR(0x17, 2, 1) /* MII Write Data High Byte (MIWR<15:8>) */
+#define ENC_MIRDL REGADDR(0x18, 2, 1) /* MII Read Data Low Byte (MIRD<7:0>) */
+#define ENC_MIRDH REGADDR(0x19, 2, 1) /* MII Read Data High Byte(MIRD<15:8>) */
+ /* 0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+
+/* MAC Control 1 Register Bit Definitions */
+
+#define MACON1_MARXEN (1 << 0) /* Bit 0: MAC Receive Enable */
+#define MACON1_PASSALL (1 << 1) /* Bit 1: Pass All Received Frames Enable */
+#define MACON1_RXPAUS (1 << 2) /* Bit 2: Pause Control Frame Reception Enable */
+#define MACON1_TXPAUS (1 << 3) /* Bit 3: Pause Control Frame Transmission Enable */
+ /* Bits 4-7: Unimplemented or reserved */
+
+/* MAC Control 1 Register Bit Definitions */
+
+#define MACON3_FULDPX (1 << 0) /* Bit 0: MAC Full-Duplex Enable */
+#define MACON3_FRMLNEN (1 << 1) /* Bit 1: Frame Length Checking Enable */
+#define MACON3_HFRMLEN (1 << 2) /* Bit 2: Huge Frame Enable */
+#define MACON3_PHDRLEN (1 << 3) /* Bit 3: Proprietary Header Enable */
+#define MACON3_TXCRCEN (1 << 4) /* Bit 4: Transmit CRC Enable */
+#define MACON3_PADCFG0 (1 << 5) /* Bit 5: Automatic Pad and CRC Configuration */
+#define MACON3_PADCFG1 (1 << 6) /* Bit 6: " " " " " " " " " " */
+#define MACON3_PADCFG2 (1 << 7) /* Bit 7: " " " " " " " " " " */
+
+/* MAC Control 1 Register Bit Definitions */
+
+#define MACON4_NOBKOFF (1 << 4) /* Bit 4: No Backoff Enable */
+#define MACON4_BPEN (1 << 5) /* Bit 5: No Backoff During Backpressure Enable */
+#define MACON4_DEFER (1 << 6) /* Bit 6: Defer Transmission Enable bit */
+
+/* MII Command Register Bit Definitions */
+
+#define MICMD_MIIRD (1 << 0) /* Bit 0: MII Read Enable */
+#define MICMD_MIISCAN (1 << 1) /* Bit 1: MII Scan Enable */
+
+/* Bank 3 Control Register Addresses */
+
+#define ENC_MAADR5 REGADDR(0x00, 3, 1) /* MAC Address Byte 5 (MAADR<15:8>) */
+#define ENC_MAADR6 REGADDR(0x01, 3, 1) /* MAC Address Byte 6 (MAADR<7:0>) */
+#define ENC_MAADR3 REGADDR(0x02, 3, 1) /* MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 */
+#define ENC_MAADR4 REGADDR(0x03, 3, 1) /* MAC Address Byte 4 (MAADR<23:16>) */
+#define ENC_MAADR1 REGADDR(0x04, 3, 1) /* MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 */
+#define ENC_MAADR2 REGADDR(0x05, 3, 1) /* MAC Address Byte 2 (MAADR<39:32>), OUI Byte */
+#define ENC_EBSTSD REGADDR(0x06, 3, 0) /* Built-in Self-Test Fill Seed (EBSTSD<7:0>) */
+#define ENC_EBSTCON REGADDR(0x07, 3, 0) /* Built-in Self-Test Control */
+#define ENC_EBSTCSL REGADDR(0x08, 3, 0) /* Built-in Self-Test Checksum Low Byte (EBSTCS<7:0>) */
+#define ENC_EBSTCSH REGADDR(0x09, 3, 0) /* Built-in Self-Test Checksum High Byte (EBSTCS<15:8>) */
+#define ENC_MISTAT REGADDR(0x0a, 3, 1) /* MII Status Register */
+ /* 0x0b-0x11: Reserved */
+#define ENC_EREVID REGADDR(0x12, 3, 0) /* Ethernet Revision ID */
+ /* 0x13-0x14: Reserved */
+#define ENC_ECOCON REGADDR(0x15, 3, 0) /* Clock Output Control */
+ /* 0x16: Reserved */
+#define ENC_EFLOCON REGADDR(0x17, 3, 0) /* Ethernet Flow Control */
+#define ENC_EPAUSL REGADDR(0x18, 3, 0) /* Pause Timer Value Low Byte (EPAUS<7:0>) */
+#define ENC_EPAUSH REGADDR(0x19, 3, 0) /* Pause Timer Value High Byte (EPAUS<15:8>) */
+ /* 0x1a: Reserved */
+ /* 0x1b-0x1f: EIE, EIR, ESTAT, ECON2, ECON1 */
+
+/* Built-in Self-Test Control Register Bit Definitions */
+
+#define EBSTCON_BISTST (1 << 0) /* Bit 0: Built-in Self-Test Start/Busy */
+#define EBSTCON_TME (1 << 1) /* Bit 1: Test Mode Enable */
+#define EBSTCON_TMSEL0 (1 << 2) /* Bit 2: Test Mode Select */
+#define EBSTCON_TMSEL1 (1 << 3) /* Bit 3: " " " " " " */
+#define EBSTCON_PSEL (1 << 4) /* Bit 4: Port Select */
+#define EBSTCON_PSV0 (1 << 5) /* Bit 5: Pattern Shift Value */
+#define EBSTCON_PSV1 (1 << 6) /* Bit 6: " " " " " */
+#define EBSTCON_PSV2 (1 << 7) /* Bit 7: " " " " " */
+
+/* MII Status Register Register Bit Definitions */
+
+#define MISTAT_BUSY (1 << 0) /* Bit 0: MII Management Busy */
+#define MISTAT_SCAN (1 << 1) /* Bit 1: MII Management Scan Operation */
+#define MISTAT_NVALID (1 << 2) /* Bit 2: MII Management Read Data Not Valid */
+ /* Bits 3-7: Reserved or unimplemented */
+
+/* Ethernet Flow Control Register Bit Definitions */
+
+#define EFLOCON_FCEN0 (1 << 0) /* Bit 0: Flow Control Enable */
+#define EFLOCON_FCEN1 (1 << 1) /* Bit 1: " " " " " " */
+#define EFLOCON_FULDPXS (1 << 2) /* Bit 2: Read-Only MAC Full-Duplex Shadow */
+ /* Bits 3-7: Reserved or unimplemented */
+
+/* PHY Registers ************************************************************/
+
+#define ENC_PHCON1 (0x00) /* PHY Control Register 1 */
+#define ENC_PHSTAT1 (0x01) /* PHY Status 1 */
+#define ENC_PHID1 (0x02) /* PHY ID Register 1 */
+#define ENC_PHID2 (0x03) /* PHY ID Register 2 */
+#define ENC_PHCON2 (0x10) /* PHY Control Register 2 */
+#define ENC_PHSTAT2 (0x11) /* PHY Status 2 */
+#define ENC_PHIE (0x12) /* PHY Interrupt Enable Register */
+#define ENC_PHIR (0x13) /* PHY Interrupt Request Register */
+#define ENC_PHLCON (0x14)
+
+/* PHY Control Register 1 Register Bit Definitions */
+
+#define PHCON1_PDPXMD (1 << 8) /* Bit 8: PHY Power-Down */
+#define PHCON1_PPWRSV (1 << 11) /* Bit 11: PHY Power-Down */
+#define PHCON1_PLOOPBK (1 << 14) /* Bit 14: PHY Loopback */
+#define PHCON1_PRST (1 << 15) /* Bit 15: PHY Software Reset */
+
+/* HY Status 1 Register Bit Definitions */
+
+#define PHSTAT1_JBSTAT (1 << 1) /* Bit 1: PHY Latching Jabber Status */
+#define PHSTAT1_LLSTAT (1 << 2) /* Bit 2: PHY Latching Link Status */
+#define PHSTAT1_PHDPX (1 << 11) /* Bit 11: PHY Half-Duplex Capable */
+#define PHSTAT1_PFDPX (1 << 12) /* Bit 12: PHY Full-Duplex Capable */
+
+/* PHY Control Register 2 Register Bit Definitions */
+
+#define PHCON2_HDLDIS (1 << 8) /* Bit 8: PHY Half-Duplex Loopback Disable */
+#define PHCON2_JABBER (1 << 10) /* Bit 10: Jabber Correction Disable */
+#define PHCON2_TXDIS (1 << 13) /* Bit 13: Twisted-Pair Transmitter Disable */
+#define PHCON2_FRCLINK (1 << 14) /* Bit 14: PHY Force Linkup */
+
+/* PHY Status 2 Register Bit Definitions */
+
+#define PHSTAT2_PLRITY (1 << 5) /* Bit 5: Polarity Status */
+#define PHSTAT2_DPXSTAT (1 << 9) /* Bit 9: PHY Duplex Status */
+#define PHSTAT2_LSTAT (1 << 10) /* Bit 10: PHY Link Status */
+#define PHSTAT2_COLSTAT (1 << 11) /* Bit 11: PHY Collision Status */
+#define PHSTAT2_RXSTAT (1 << 12) /* Bit 12: PHY Receive Status */
+#define PHSTAT2_TXSTAT (1 << 13) /* Bit 13: PHY Transmit Status */
+
+/* PHY Interrupt Enable Register Bit Definitions */
+
+#define PHIE_PGEIE (1 << 1) /* Bit 1: PHY Global Interrupt Enable */
+#define PHIE_PLNKIE (1 << 4) /* Bit 4: PHY Link Change Interrupt Enable */
+
+/* PHIR Regiser Bit Definitions */
+
+#define PHIR_PGIF (1 << 2) /* Bit 2: PHY Global Interrupt */
+#define PHIR_PLNKIF (1 << 4) /* Bit 4: PHY Link Change Interrupt */
+
+/* PHLCON Regiser Bit Definitions */
+
+ /* Bit 0: Reserved */
+#define PHLCON_STRCH (1 << 1) /* Bit 1: LED Pulse Stretching Enable */
+#define PHLCON_LFRQ0 (1 << 2) /* Bit 2: LED Pulse Stretch Time Configuration */
+#define PHLCON_LFRQ1 (1 << 3) /* Bit 3: " " " " " " " " " */
+#define PHLCON_LBCFG0 (1 << 4) /* Bit 4: LEDB Configuration */
+#define PHLCON_LBCFG1 (1 << 5) /* Bit 5: " " " " */
+#define PHLCON_LBCFG2 (1 << 6) /* Bit 6: " " " " */
+#define PHLCON_LBCFG3 (1 << 7) /* Bit 7: " " " " */
+#define PHLCON_LACFG0 (1 << 8) /* Bit 8: LEDA Configuration */
+#define PHLCON_LACFG1 (1 << 9) /* Bit 9: " " " " */
+#define PHLCON_LACFG2 (1 << 10) /* Bit 10: " " " " */
+#define PHLCON_LACFG3 (1 << 11) /* Bit 11: " " " " */
+
+/* Packet Memory ************************************************************/
+
+/* 8-Kbyte Transmit/Receive Packet Dual Port SRAM */
+
+#define PKTMEM_START 0x0000
+#define PKTMEM_END 0x1fff
+
+/* Ethernet frames are between 64 and 1518 bytes long */
+
+#define MIN_FRAMELEN 64
+#define MAX_FRAMELEN 1518
+
+/* Packet Control Bits Definitions ******************************************/
+
+#define PKTCTRL_POVERRIDE (1 << 0) /* Bit 0: Per Packet Override */
+#define PKTCTRL_PCRCEN (1 << 1) /* Bit 1: Per Packet CRC Enable */
+#define PKTCTRL_PPADEN (1 << 2) /* Bit 2: Per Packet Padding Enable */
+#define PKTCTRL_PHUGEEN (1 << 3) /* Bit 3: Per Packet Huge Frame Enable */
+
+/* RX Status Bit Definitions ************************************************/
+
+#define RXSTAT_LDEVENT (1 << 0) /* Bit 0: Long event or pack dropped */
+ /* Bit 1: Reserved */
+#define RXSTAT_CEPS (1 << 2) /* Bit 2: Carrier event previously seen */
+ /* Bit 3: Reserved */
+#define RXSTAT_CRCERROR (1 << 4) /* Bit 4: Frame CRC field bad */
+#define RXSTAT_LENERROR (1 << 5) /* Bit 5: Packet length != data length */
+#define RXSTAT_LENRANGE (1 << 6) /* Bit 6: Type/length field > 1500 bytes */
+#define RXSTAT_OK (1 << 7) /* Bit 7: Packet with valid CRC and no symbol errors */
+#define RXSTAT_MCAST (1 << 8) /* Bit 8: Packet with multicast address */
+#define RXSTAT_BCAST (1 << 9) /* Bit 9: Packet with broadcast address */
+#define RXSTAT_DRIBBLE (1 << 10) /* Bit 10: Additional bits received after packet */
+#define RXSTAT_CTRLFRAME (1 << 11) /* Bit 11: Control frame with valid type/length */
+#define RXSTAT_PAUSE (1 << 12) /* Bit 12: Control frame with pause frame opcde */
+#define RXSTAT_UNKOPCODE (1 << 13) /* Bit 13: Control frame with unknown opcode */
+#define RXSTAT_VLANTYPE (1 << 14) /* Bit 14: Current frame is a VLAN tagged frame */
+ /* Bit 15: Zero */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVERS_NET_ENC28J60_H */
diff --git a/nuttx/drivers/net/skeleton.c b/nuttx/drivers/net/skeleton.c
new file mode 100644
index 000000000..00ebea35f
--- /dev/null
+++ b/nuttx/drivers/net/skeleton.c
@@ -0,0 +1,692 @@
+/****************************************************************************
+ * drivers/net/skeleton.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_skeleton)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* CONFIG_skeleton_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_skeleton_NINTERFACES
+# define CONFIG_skeleton_NINTERFACES 1
+#endif
+
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define skeleton_WDDELAY (1*CLK_TCK)
+#define skeleton_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define skeleton_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)skel->sk_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The skel_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct skel_driver_s
+{
+ bool sk_bifup; /* true:ifup false:ifdown */
+ WDOG_ID sk_txpoll; /* TX poll timer */
+ WDOG_ID sk_txtimeout; /* TX timeout timer */
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s sk_dev; /* Interface understood by uIP */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct skel_driver_s g_skel[CONFIG_skeleton_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int skel_transmit(FAR struct skel_driver_s *skel);
+static int skel_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void skel_receive(FAR struct skel_driver_s *skel);
+static void skel_txdone(FAR struct skel_driver_s *skel);
+static int skel_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void skel_polltimer(int argc, uint32_t arg, ...);
+static void skel_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int skel_ifup(struct uip_driver_s *dev);
+static int skel_ifdown(struct uip_driver_s *dev);
+static int skel_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int skel_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int skel_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: skel_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * skel - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int skel_transmit(FAR struct skel_driver_s *skel)
+{
+ /* 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.
+ */
+
+ /* Increment statistics */
+
+ /* Send the packet: address=skel->sk_dev.d_buf, length=skel->sk_dev.d_len */
+
+ /* Enable Tx interrupts */
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(skel->sk_txtimeout, skeleton_TXTIMEOUT, skel_txtimeout, 1, (uint32_t)skel);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_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
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int skel_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_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 (skel->sk_dev.d_len > 0)
+ {
+ uip_arp_out(&skel->sk_dev);
+ skel_transmit(skel);
+
+ /* Check if there is room in the device to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: skel_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * skel - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void skel_receive(FAR struct skel_driver_s *skel)
+{
+ do
+ {
+ /* Check for errors and update statistics */
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+
+ /* Copy the data data from the hardware to skel->sk_dev.d_buf. Set
+ * amount of data in skel->sk_dev.d_len
+ */
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&skel->sk_dev);
+ uip_input(&skel->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 (skel->sk_dev.d_len > 0)
+ {
+ uip_arp_out(&skel->sk_dev);
+ skel_transmit(skel);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ uip_arp_arpin(&skel->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 (skel->sk_dev.d_len > 0)
+ {
+ skel_transmit(skel);
+ }
+ }
+ }
+ while (); /* While there are more packets to be processed */
+}
+
+/****************************************************************************
+ * Function: skel_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * skel - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void skel_txdone(FAR struct skel_driver_s *skel)
+{
+ /* Check for errors and update statistics */
+
+ /* If no further xmits are pending, then cancel the TX timeout and
+ * disable further Tx interrupts.
+ */
+
+ wd_cancel(skel->sk_txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: skel_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int skel_interrupt(int irq, FAR void *context)
+{
+ register FAR struct skel_driver_s *skel = &g_skel[0];
+
+ /* Get and clear interrupt status bits */
+
+ /* Handle interrupts according to status bit settings */
+
+ /* Check if we received an incoming packet, if so, call skel_receive() */
+
+ skel_receive(skel);
+
+ /* Check if a packet transmission just completed. If so, call skel_txdone.
+ * This may disable further Tx interrupts if there are no pending
+ * tansmissions.
+ */
+
+ skel_txdone(skel);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void skel_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ /* Then reset the hardware */
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: skel_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void skel_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)arg;
+
+ /* Check if there is room in the send another TX packet. We cannot perform
+ * the TX poll if he are unable to accept another packet for transmission.
+ */
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * might be bug here. Does this mean if there is a transmit in progress,
+ * we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&skel->sk_dev, skel_uiptxpoll, skeleton_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(skel->sk_txpoll, skeleton_WDDELAY, skel_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: skel_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int skel_ifup(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(skel->sk_txpoll, skeleton_WDDELAY, skel_polltimer, 1, (uint32_t)skel);
+
+ /* Enable the Ethernet interrupt */
+
+ skel->sk_bifup = true;
+ up_enable_irq(CONFIG_skeleton_IRQ);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int skel_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ up_disable_irq(CONFIG_skeleton_IRQ);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(skel->sk_txpoll);
+ wd_cancel(skel->sk_txtimeout);
+
+ /* Put the EMAC in its reset, non-operational state. This should be
+ * a known configuration that will guarantee the skel_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ /* Mark the device "down" */
+
+ skel->sk_bifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int skel_txavail(struct uip_driver_s *dev)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (skel->sk_bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&skel->sk_dev, skel_uiptxpoll);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: skel_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int skel_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: skel_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int skel_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct skel_driver_s *skel = (FAR struct skel_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: skel_initialize
+ *
+ * Description:
+ * Initialize the Ethernet controller and driver
+ *
+ * Parameters:
+ * intf - In the case where there are multiple EMACs, this value
+ * identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int skel_initialize(int intf)
+{
+ struct skel_driver_s *priv;
+
+ /* Get the interface structure associated with this interface number. */
+
+ DEBUGASSERT(inf < CONFIG_skeleton_NINTERFACES);
+ priv = &g_skel[intf];
+
+ /* Check if a Ethernet chip is recognized at its I/O base */
+
+ /* Attach the IRQ to the driver */
+
+ if (irq_attach(CONFIG_skeleton_IRQ, skel_interrupt))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ return -EAGAIN;
+ }
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct skel_driver_s));
+ priv->sk_dev.d_ifup = skel_ifup; /* I/F up (new IP address) callback */
+ priv->sk_dev.d_ifdown = skel_ifdown; /* I/F down callback */
+ priv->sk_dev.d_txavail = skel_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->sk_dev.d_addmac = skel_addmac; /* Add multicast MAC address */
+ priv->sk_dev.d_rmmac = skel_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->sk_dev.d_private = (void*)g_skel; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->sk_txpoll = wd_create(); /* Create periodic poll timer */
+ priv->sk_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ /* Put the interface in the down state. This usually amounts to resetting
+ * the device and/or calling skel_ifdown().
+ */
+
+ /* Read the MAC address from the hardware into priv->sk_dev.d_mac.ether_addr_octet */
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&priv->sk_dev);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_skeleton */
diff --git a/nuttx/drivers/net/slip.c b/nuttx/drivers/net/slip.c
new file mode 100644
index 000000000..31f44cbb9
--- /dev/null
+++ b/nuttx/drivers/net/slip.c
@@ -0,0 +1,1017 @@
+/****************************************************************************
+ * drivers/net/slip.c
+ *
+ * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Reference: RFC 1055
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/net/net.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#if defined(CONFIG_NET) && defined(CONFIG_NET_SLIP)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* NOTE: Slip requires UART hardware handshake. If hardware handshake is
+ * not available with your UART, then you might try the 'slattach' option
+ * -L which enable "3-wire operation." That allows operation without the
+ * hardware handshake (but with the possibility of data overrun).
+ */
+
+/* Configuration ************************************************************/
+
+#if UIP_LLH_LEN > 0
+# error "UIP_LLH_LEN must be set to zero"
+#endif
+
+#ifndef CONFIG_NET_NOINTS
+# warning "CONFIG_NET_NOINTS must be set"
+#endif
+
+#ifndef CONFIG_NET_MULTIBUFFER
+# warning "CONFIG_NET_MULTIBUFFER must be set"
+#endif
+
+#ifndef CONFIG_SLIP_STACKSIZE
+# define CONFIG_SLIP_STACKSIZE 2048
+#endif
+
+#ifndef CONFIG_SLIP_DEFPRIO
+# define CONFIG_SLIP_DEFPRIO 128
+#endif
+
+/* The Linux slip module hard-codes its MTU size to 296 (40 bytes for the
+ * IP+TPC headers plus 256 bytes of data). So you might as well set
+ * CONFIG_NET_BUFSIZE to 296 as well.
+ *
+ * There may be an issue with this setting, however. I see that Linux uses
+ * a MTU of 296 and window of 256, but actually only sends 168 bytes of data:
+ * 40 + 128. I believe that is to allow for the 2x worst cast packet
+ * expansion. Ideally we would like to advertise the 256 MSS, but restrict
+ * uIP to 128 bytes (possibly by modifying the uip_mss() macro).
+ */
+
+#if CONFIG_NET_BUFSIZE < 296
+# error "CONFIG_NET_BUFSIZE >= 296 is required"
+#elif CONFIG_NET_BUFSIZE > 296
+# warning "CONFIG_NET_BUFSIZE == 296 is optimal"
+#endif
+
+/* CONFIG_SLIP_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_SLIP_NINTERFACES
+# define CONFIG_SLIP_NINTERFACES 1
+#endif
+
+/* SLIP special character codes *******************************************/
+
+#define SLIP_END 0300 /* Indicates end of packet */
+#define SLIP_ESC 0333 /* Indicates byte stuffing */
+#define SLIP_ESC_END 0334 /* ESC ESC_END means SLIP_END data byte */
+#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
+
+/* General driver definitions **********************************************/
+
+/* TX poll delay = 1 second = 1000000 microseconds. */
+
+#define SLIP_WDDELAY (1*1000000)
+#define SLIP_POLLHSEC (1*2)
+
+/* Statistics helper */
+
+#ifdef CONFIG_NET_STATISTICS
+# define SLIP_STAT(p,f) (p->stats.f)++
+#else
+# define SLIP_STAT(p,f)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Driver statistics */
+
+#ifdef CONFIG_NET_STATISTICS
+struct slip_statistics_s
+{
+ uint32_t transmitted; /* Number of packets transmitted */
+ uint32_t received /* Number of packets received */
+};
+#endif
+
+/* The slip_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct slip_driver_s
+{
+ volatile bool bifup; /* true:ifup false:ifdown */
+ int fd; /* TTY file descriptor */
+ pid_t rxpid; /* Receiver thread ID */
+ pid_t txpid; /* Transmitter thread ID */
+ sem_t waitsem; /* Mutually exclusive access to uIP */
+ uint16_t rxlen; /* The number of bytes in rxbuf */
+
+ /* Driver statistics */
+
+#ifdef CONFIG_NET_STATISTICS
+ struct slip_statistics_s stats;
+#endif
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dev; /* Interface understood by uIP */
+ uint8_t rxbuf[CONFIG_NET_BUFSIZE + 2];
+ uint8_t txbuf[CONFIG_NET_BUFSIZE + 2];
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+ /* We really should get rid of CONFIG_SLIP_NINTERFACES and, instead,
+ * kmalloc() new interface instances as needed.
+ */
+
+static struct slip_driver_s g_slip[CONFIG_SLIP_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void slip_semtake(FAR struct slip_driver_s *priv);
+
+/* Common TX logic */
+
+static void slip_write(FAR struct slip_driver_s *priv, const uint8_t *buffer, int len);
+static void slip_putc(FAR struct slip_driver_s *priv, int ch);
+static int slip_transmit(FAR struct slip_driver_s *priv);
+static int slip_uiptxpoll(struct uip_driver_s *dev);
+static void slip_txtask(int argc, char *argv[]);
+
+/* Packet receiver task */
+
+static int slip_getc(FAR struct slip_driver_s *priv);
+static inline void slip_receive(FAR struct slip_driver_s *priv);
+static int slip_rxtask(int argc, char *argv[]);
+
+/* NuttX callback functions */
+
+static int slip_ifup(struct uip_driver_s *dev);
+static int slip_ifdown(struct uip_driver_s *dev);
+static int slip_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int slip_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int slip_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: slip_semtake
+ ****************************************************************************/
+
+static void slip_semtake(FAR struct slip_driver_s *priv)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&priv->waitsem) != 0)
+ {
+ /* The only case that an error should occur here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+#define slip_semgive(p) sem_post(&(p)->waitsem);
+
+/****************************************************************************
+ * Function: slip_write
+ *
+ * Description:
+ * Just an inline wrapper around fwrite with error checking.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - Buffer data to send
+ * len - Buffer length in bytes
+ *
+ ****************************************************************************/
+
+static inline void slip_write(FAR struct slip_driver_s *priv,
+ const uint8_t *buffer, int len)
+{
+ /* Handle the case where the write is awakened by a signal */
+
+ while (write(priv->fd, buffer, len) < 0)
+ {
+ DEBUGASSERT(errno == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Function: slip_putc
+ *
+ * Description:
+ * Just an inline wrapper around putc with error checking.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ch - The character to send
+ *
+ ****************************************************************************/
+
+static inline void slip_putc(FAR struct slip_driver_s *priv, int ch)
+{
+ uint8_t buffer = (uint8_t)ch;
+ slip_write(priv, &buffer, 1);
+}
+
+/****************************************************************************
+ * Function: slip_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int slip_transmit(FAR struct slip_driver_s *priv)
+{
+ uint8_t *src;
+ uint8_t *start;
+ uint8_t esc;
+ int remaining;
+ int len;
+
+ /* Increment statistics */
+
+ nvdbg("Sending packet size %d\n", priv->dev.d_len);
+ SLIP_STAT(priv, transmitted);
+
+ /* Send an initial END character to flush out any data that may have
+ * accumulated in the receiver due to line noise
+ */
+
+ slip_putc(priv, SLIP_END);
+
+ /* For each byte in the packet, send the appropriate character sequence */
+
+ src = priv->dev.d_buf;
+ remaining = priv->dev.d_len;
+ start = src;
+ len = 0;
+
+ while (remaining-- > 0)
+ {
+ switch (*src)
+ {
+ /* If it's the same code as an END character, we send a special two
+ * character code so as not to make the receiver think we sent an
+ * END
+ */
+
+ case SLIP_END:
+ esc = SLIP_ESC_END;
+ goto escape;
+
+ /* If it's the same code as an ESC character, we send a special two
+ * character code so as not to make the receiver think we sent an
+ * ESC
+ */
+
+ case SLIP_ESC:
+ esc = SLIP_ESC_ESC;
+
+ escape:
+ {
+ /* Flush any unsent data */
+
+ if (len > 0)
+ {
+ slip_write(priv, start, len);
+
+ /* Reset */
+
+ start = src + 1;
+ len = 0;
+ }
+
+ /* Then send the escape sequence */
+
+ slip_putc(priv, SLIP_ESC);
+ slip_putc(priv, esc);
+ }
+ break;
+
+ /* otherwise, just bump up the count */
+
+ default:
+ len++;
+ break;
+ }
+
+ /* Point to the next character in the packet */
+
+ src++;
+ }
+
+ /* We have looked at every character in the packet. Now flush any unsent
+ * data
+ */
+
+ if (len > 0)
+ {
+ slip_write(priv, start, len);
+ }
+
+ /* And send the END token */
+
+ slip_putc(priv, SLIP_END);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_uiptxpoll
+ *
+ * Description:
+ * Check if uIP has any outgoing packets ready to send. This is a
+ * callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete, or
+ * 2. When the preceding TX packet send times o ]ut and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * The initiator of the poll holds the priv->waitsem;
+ *
+ ****************************************************************************/
+
+static int slip_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_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 (priv->dev.d_len > 0)
+ {
+ slip_transmit(priv);
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: slip_txtask
+ *
+ * Description:
+ * Polling and transmission is performed on tx thread.
+ *
+ * Parameters:
+ * arg - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void slip_txtask(int argc, char *argv[])
+{
+ FAR struct slip_driver_s *priv;
+ unsigned int index = *(argv[1]) - '0';
+ uip_lock_t flags;
+
+ ndbg("index: %d\n", index);
+ DEBUGASSERT(index < CONFIG_SLIP_NINTERFACES);
+
+ /* Get our private data structure instance and wake up the waiting
+ * initialization logic.
+ */
+
+ priv = &g_slip[index];
+ slip_semgive(priv);
+
+ /* Loop forever */
+
+ for (;;)
+ {
+ /* Wait for the timeout to expire (or until we are signaled by by */
+
+ usleep(SLIP_WDDELAY);
+
+ /* Is the interface up? */
+
+ if (priv->bifup)
+ {
+ /* Get exclusive access to uIP (if it it is already being used
+ * slip_rxtask, then we have to wait).
+ */
+
+ slip_semtake(priv);
+
+ /* Poll uIP for new XMIT data. BUG: We really need to calculate
+ * the number of hsecs! When we are awakened by slip_txavail, the
+ * number will be smaller; when we have to wait for the semaphore
+ * (above), it may be larger.
+ */
+
+ flags = uip_lock();
+ priv->dev.d_buf = priv->txbuf;
+ (void)uip_timer(&priv->dev, slip_uiptxpoll, SLIP_POLLHSEC);
+ uip_unlock(flags);
+ slip_semgive(priv);
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: slip_getc
+ *
+ * Description:
+ * Get one byte from the serial input.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * The returned byte
+ *
+ ****************************************************************************/
+
+static inline int slip_getc(FAR struct slip_driver_s *priv)
+{
+ uint8_t ch;
+
+ while (read(priv->fd, &ch, 1) < 0)
+ {
+ DEBUGASSERT(errno == EINTR);
+ }
+
+ return (int)ch;
+}
+
+/****************************************************************************
+ * Function: slip_receive
+ *
+ * Description:
+ * Read a packet from the serial input
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void slip_receive(FAR struct slip_driver_s *priv)
+{
+ uint8_t ch;
+
+ /* Copy the data data from the hardware to to the RX buffer until we
+ * put together a whole packet. Make sure not to copy them into the
+ * packet if we run out of room.
+ */
+
+ nvdbg("Receiving packet\n");
+ for (;;)
+ {
+ /* Get the next character in the stream. */
+
+ ch = slip_getc(priv);
+
+ /* Handle bytestuffing if necessary */
+
+ switch (ch)
+ {
+ /* If it's an END character then we're done with the packet.
+ * (OR we are just starting a packet)
+ */
+
+ case SLIP_END:
+ nvdbg("END\n");
+
+ /* A minor optimization: if there is no data in the packet, ignore
+ * it. This is meant to avoid bothering IP with all the empty
+ * packets generated by the duplicate END characters which are in
+ * turn sent to try to detect line noise.
+ */
+
+ if (priv->rxlen > 0)
+ {
+ nvdbg("Received packet size %d\n", priv->rxlen);
+ return;
+ }
+ break;
+
+ /* if it's the same code as an ESC character, wait and get another
+ * character and then figure out what to store in the packet based
+ * on that.
+ */
+
+ case SLIP_ESC:
+ nvdbg("ESC\n");
+ ch = slip_getc(priv);
+
+ /* if "ch" is not one of these two, then we have a protocol
+ * violation. The best bet seems to be to leave the byte alone
+ * and just stuff it into the packet
+ */
+
+ switch (ch)
+ {
+ case SLIP_ESC_END:
+ nvdbg("ESC-END\n");
+ ch = SLIP_END;
+ break;
+ case SLIP_ESC_ESC:
+ nvdbg("ESC-ESC\n");
+ ch = SLIP_ESC;
+ break;
+ default:
+ ndbg("ERROR: Protocol violation: %02x\n", ch);
+ break;
+ }
+
+ /* Here we fall into the default handler and let it store the
+ * character for us
+ */
+
+ default:
+ if (priv->rxlen < CONFIG_NET_BUFSIZE+2)
+ {
+ priv->rxbuf[priv->rxlen++] = ch;
+ }
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: slip_rxtask
+ *
+ * Description:
+ * Wait for incoming data.
+ *
+ * Parameters:
+ * argc
+ * argv
+ *
+ * Returned Value:
+ * (Does not return)
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int slip_rxtask(int argc, char *argv[])
+{
+ FAR struct slip_driver_s *priv;
+ unsigned int index = *(argv[1]) - '0';
+ uip_lock_t flags;
+ int ch;
+
+ ndbg("index: %d\n", index);
+ DEBUGASSERT(index < CONFIG_SLIP_NINTERFACES);
+
+ /* Get our private data structure instance and wake up the waiting
+ * initialization logic.
+ */
+
+ priv = &g_slip[index];
+ slip_semgive(priv);
+
+ /* Loop forever */
+
+ for (;;)
+ {
+ /* Wait for the next character to be available on the input stream. */
+
+ nvdbg("Waiting...\n");
+ ch = slip_getc(priv);
+
+ /* Ignore any input that we receive before the interface is up. */
+
+ if (!priv->bifup)
+ {
+ continue;
+ }
+
+ /* We have something...
+ *
+ * END characters may appear at packet boundaries BEFORE as well as
+ * after the beginning of the packet. This is normal and expected.
+ */
+
+ if (ch == SLIP_END)
+ {
+ priv->rxlen = 0;
+ }
+
+ /* Otherwise, we are in danger of being out-of-sync. Apparently the
+ * leading END character is optional. Let's try to continue.
+ */
+
+ else
+ {
+ priv->rxbuf[0] = (uint8_t)ch;
+ priv->rxlen = 1;
+ }
+
+ /* Copy the data data from the hardware to priv->rxbuf until we put
+ * together a whole packet.
+ */
+
+ slip_receive(priv);
+ SLIP_STAT(priv, received);
+
+ /* All packets are assumed to be IP packets (we don't have a choice..
+ * there is no Ethernet header containing the EtherType). So pass the
+ * received packet on for IP processing -- but only if it is big
+ * enough to hold an IP header.
+ */
+
+ if (priv->rxlen >= UIP_IPH_LEN)
+ {
+ /* Handle the IP input. Get exclusive access to uIP. */
+
+ slip_semtake(priv);
+ priv->dev.d_buf = priv->rxbuf;
+ priv->dev.d_len = priv->rxlen;
+
+ flags = uip_lock();
+ uip_input(&priv->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. NOTE that we are transmitting using the RX buffer!
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ slip_transmit(priv);
+ }
+ uip_unlock(flags);
+ slip_semgive(priv);
+ }
+ else
+ {
+ SLIP_STAT(priv, rxsmallpacket);
+ }
+ }
+
+ /* We won't get here */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int slip_ifup(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Mark the interface up */
+
+ priv->bifup = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int slip_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Mark the device "down" */
+
+ priv->bifup = false;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int slip_txavail(struct uip_driver_s *dev)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->bifup)
+ {
+ /* Wake up the TX polling thread */
+
+ kill(priv->txpid, SIGALRM);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: slip_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int slip_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: slip_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int slip_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct slip_driver_s *priv = (FAR struct slip_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: slip_initialize
+ *
+ * Description:
+ * Instantiate a SLIP network interface.
+ *
+ * Parameters:
+ * intf - In the case where there are multiple SLIP interfaces, this value
+ * identifies which is to be initialized. The network name will be,
+ * for example, "/dev/slip5" for intf == 5
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int slip_initialize(int intf, const char *devname)
+{
+ struct slip_driver_s *priv;
+ char buffer[8];
+ const char *argv[2];
+
+ /* Get the interface structure associated with this interface number. */
+
+ DEBUGASSERT(intf < CONFIG_SLIP_NINTERFACES);
+ priv = &g_slip[intf];
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct slip_driver_s));
+ priv->dev.d_ifup = slip_ifup; /* I/F up (new IP address) callback */
+ priv->dev.d_ifdown = slip_ifdown; /* I/F down callback */
+ priv->dev.d_txavail = slip_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->dev.d_addmac = slip_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = slip_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->dev.d_private = priv; /* Used to recover private state from dev */
+
+ /* Open the device */
+
+ priv->fd = open(devname, O_RDWR, 0666);
+ if (priv->fd < 0)
+ {
+ ndbg("ERROR: Failed to open %s: %d\n", devname, errno);
+ return -errno;
+ }
+
+ /* Initialize the wait semaphore */
+
+ sem_init(&priv->waitsem, 0, 0);
+
+ /* Put the interface in the down state. This usually amounts to resetting
+ * the device and/or calling slip_ifdown().
+ */
+
+ slip_ifdown(&priv->dev);
+
+ /* Start the SLIP receiver task */
+
+ snprintf(buffer, 8, "%d", intf);
+ argv[0] = buffer;
+ argv[1] = NULL;
+
+#ifndef CONFIG_CUSTOM_STACK
+ priv->rxpid = task_create("rxslip", CONFIG_SLIP_DEFPRIO,
+ CONFIG_SLIP_STACKSIZE, (main_t)slip_rxtask, argv);
+#else
+ priv->rxpid = task_create("rxslip", CONFIG_SLIP_DEFPRIO,
+ (main_t)slip_rxtask, argv);
+#endif
+ if (priv->rxpid < 0)
+ {
+ ndbg("ERROR: Failed to start receiver task\n");
+ return -errno;
+ }
+
+ /* Wait and make sure that the receive task is started. */
+
+ slip_semtake(priv);
+
+ /* Start the SLIP transmitter task */
+
+#ifndef CONFIG_CUSTOM_STACK
+ priv->txpid = task_create("txslip", CONFIG_SLIP_DEFPRIO,
+ CONFIG_SLIP_STACKSIZE, (main_t)slip_txtask, argv);
+#else
+ priv->txpid = task_create("txslip", CONFIG_SLIP_DEFPRIO,
+ (main_t)slip_txtask, argv);
+#endif
+ if (priv->txpid < 0)
+ {
+ ndbg("ERROR: Failed to start receiver task\n");
+ return -errno;
+ }
+
+ /* Wait and make sure that the transmit task is started. */
+
+ slip_semtake(priv);
+
+ /* Bump the semaphore count so that it can now be used as a mutex */
+
+ slip_semgive(priv);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ (void)netdev_register(&priv->dev);
+
+ /* When the RX and TX tasks were created, the TTY file descriptor was
+ * dup'ed for each task. This task no longer needs the file descriptor
+ * and we can safely close it.
+ */
+
+ close(priv->fd);
+ return OK;
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_SLIP */
+
diff --git a/nuttx/drivers/net/vnet.c b/nuttx/drivers/net/vnet.c
new file mode 100644
index 000000000..f1e2465b9
--- /dev/null
+++ b/nuttx/drivers/net/vnet.c
@@ -0,0 +1,673 @@
+/****************************************************************************
+ * drivers/net/vnet.c
+ *
+ * Copyright (C) 2011 Yu Qiang. All rights reserved.
+ * Author: Yu Qiang <yuq825@gmail.com>
+ *
+ * This file is a part of NuttX:
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#if defined(CONFIG_NET) && defined(CONFIG_NET_VNET)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include <rgmp/vnet.h>
+#include <rgmp/stdio.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* CONFIG_VNET_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_VNET_NINTERFACES
+# define CONFIG_VNET_NINTERFACES 1
+#endif
+
+/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define VNET_WDDELAY (1*CLK_TCK)
+#define VNET_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define VNET_TXTIMEOUT (60*CLK_TCK)
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)vnet->sk_dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The vnet_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct vnet_driver_s
+{
+ bool sk_bifup; /* true:ifup false:ifdown */
+ WDOG_ID sk_txpoll; /* TX poll timer */
+ //WDOG_ID sk_txtimeout; /* TX timeout timer */
+
+ /* This holds the information visible to uIP/NuttX */
+ struct rgmp_vnet *vnet;
+ struct uip_driver_s sk_dev; /* Interface understood by uIP */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct vnet_driver_s g_vnet[CONFIG_VNET_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static int vnet_transmit(FAR struct vnet_driver_s *vnet);
+static int vnet_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void vnet_txdone(FAR struct vnet_driver_s *vnet);
+
+/* Watchdog timer expirations */
+
+static void vnet_polltimer(int argc, uint32_t arg, ...);
+static void vnet_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int vnet_ifup(struct uip_driver_s *dev);
+static int vnet_ifdown(struct uip_driver_s *dev);
+static int vnet_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int vnet_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int vnet_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: vnet_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Parameters:
+ * vnet - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int vnet_transmit(FAR struct vnet_driver_s *vnet)
+{
+ int err;
+
+ /* 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 not transmission in progress.
+ */
+
+ /* Increment statistics */
+
+ /* Send the packet: address=vnet->sk_dev.d_buf, length=vnet->sk_dev.d_len */
+ err = vnet_xmit(vnet->vnet, (char *)vnet->sk_dev.d_buf, vnet->sk_dev.d_len);
+ if (err) {
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+ //(void)wd_start(vnet->sk_txtimeout, VNET_TXTIMEOUT, vnet_txtimeout, 1, (uint32_t)vnet);
+
+ // When vnet_xmit fail, it means TX buffer is full. Watchdog
+ // is of no use here because no TX done INT will happen. So
+ // we reset the TX buffer directly.
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: TX buffer is full\n");
+#endif
+ return ERROR;
+ }
+ else {
+ // this step may be unnecessary here
+ vnet_txdone(vnet);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_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
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int vnet_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_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 (vnet->sk_dev.d_len > 0)
+ {
+ uip_arp_out(&vnet->sk_dev);
+ vnet_transmit(vnet);
+
+ /* Check if there is room in the device to hold another packet. If not,
+ * return a non-zero value to terminate the poll.
+ */
+ if (vnet_is_txbuff_full(vnet->vnet))
+ return 1;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: rtos_vnet_recv
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * vnet - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+void rtos_vnet_recv(struct rgmp_vnet *vnet_dummy, char *data, int len)
+{
+ // now only support 1 vnet
+ struct vnet_driver_s *vnet = &g_vnet[0];
+
+ do {
+ /* Check for errors and update statistics */
+
+ /* Check if the packet is a valid size for the uIP buffer configuration */
+ if (len > CONFIG_NET_BUFSIZE || len < 14) {
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: receive invalid packet of size %d\n", len);
+#endif
+ return;
+ }
+
+ // Copy the data data from the hardware to vnet->sk_dev.d_buf. Set
+ // amount of data in vnet->sk_dev.d_len
+ memcpy(vnet->sk_dev.d_buf, data, len);
+ vnet->sk_dev.d_len = len;
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ uip_arp_ipin(&vnet->sk_dev);
+ uip_input(&vnet->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 (vnet->sk_dev.d_len > 0) {
+ uip_arp_out(&vnet->sk_dev);
+ vnet_transmit(vnet);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP)) {
+ uip_arp_arpin(&vnet->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 (vnet->sk_dev.d_len > 0) {
+ vnet_transmit(vnet);
+ }
+ }
+ }
+ while (0); /* While there are more packets to be processed */
+}
+
+/****************************************************************************
+ * Function: vnet_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Parameters:
+ * vnet - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void vnet_txdone(FAR struct vnet_driver_s *vnet)
+{
+ /* Check for errors and update statistics */
+
+ /* If no further xmits are pending, then cancel the TX timeout and
+ * disable further Tx interrupts.
+ */
+
+ //wd_cancel(vnet->sk_txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&vnet->sk_dev, vnet_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: vnet_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void vnet_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ /* Then reset the hardware */
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&vnet->sk_dev, vnet_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: vnet_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void vnet_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)arg;
+
+ /* Check if there is room in the send another TX packet. We cannot perform
+ * the TX poll if he are unable to accept another packet for transmission.
+ */
+ if (vnet_is_txbuff_full(vnet->vnet)) {
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: TX buffer is full\n");
+#endif
+ return;
+ }
+
+ /* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * might be bug here. Does this mean if there is a transmit in progress,
+ * we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&vnet->sk_dev, vnet_uiptxpoll, VNET_POLLHSEC);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(vnet->sk_txpoll, VNET_WDDELAY, vnet_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: vnet_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int vnet_ifup(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(vnet->sk_txpoll, VNET_WDDELAY, vnet_polltimer, 1, (uint32_t)vnet);
+
+ vnet->sk_bifup = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int vnet_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(vnet->sk_txpoll);
+ //wd_cancel(vnet->sk_txtimeout);
+
+ /* Put the the EMAC is its reset, non-operational state. This should be
+ * a known configuration that will guarantee the vnet_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ /* Mark the device "down" */
+
+ vnet->sk_bifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_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.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int vnet_txavail(struct uip_driver_s *dev)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (vnet->sk_bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing packet. */
+ if (vnet_is_txbuff_full(vnet->vnet)) {
+#ifdef CONFIG_DEBUG
+ cprintf("VNET: TX buffer is full\n");
+#endif
+ goto out;
+ }
+
+ /* If so, then poll uIP for new XMIT data */
+
+ (void)uip_poll(&vnet->sk_dev, vnet_uiptxpoll);
+ }
+
+ out:
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: vnet_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int vnet_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: vnet_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int vnet_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct vnet_driver_s *vnet = (FAR struct vnet_driver_s *)dev->d_private;
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: vnet_initialize
+ *
+ * Description:
+ * Initialize the Ethernet controller and driver
+ *
+ * Parameters:
+ * intf - In the case where there are multiple EMACs, this value
+ * identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+void vnet_initialize(void)
+{
+ struct vnet_driver_s *priv;
+ struct rgmp_vnet *vnet = vnet_list.next;
+ int i;
+
+ for (i=0; i<CONFIG_VNET_NINTERFACES; i++) {
+ if (vnet == NULL)
+ break;
+ priv = &g_vnet[i];
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct vnet_driver_s));
+ priv->sk_dev.d_ifup = vnet_ifup; /* I/F down callback */
+ priv->sk_dev.d_ifdown = vnet_ifdown; /* I/F up (new IP address) callback */
+ priv->sk_dev.d_txavail = vnet_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->sk_dev.d_addmac = vnet_addmac; /* Add multicast MAC address */
+ priv->sk_dev.d_rmmac = vnet_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->sk_dev.d_private = (void*)g_vnet; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->sk_txpoll = wd_create(); /* Create periodic poll timer */
+ //priv->sk_txtimeout = wd_create(); /* Create TX timeout timer */
+
+ priv->vnet = vnet;
+
+ /* Register the device with the OS */
+
+ (void)netdev_register(&priv->sk_dev);
+ vnet = vnet->next;
+ }
+}
+
+#endif /* CONFIG_NET && CONFIG_NET_VNET */