diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-09-17 18:18:44 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-09-17 18:18:44 +0000 |
commit | 57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch) | |
tree | 25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/drivers/net | |
download | px4-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/Kconfig | 79 | ||||
-rw-r--r-- | nuttx/drivers/net/Make.defs | 71 | ||||
-rw-r--r-- | nuttx/drivers/net/cs89x0.c | 959 | ||||
-rw-r--r-- | nuttx/drivers/net/cs89x0.h | 326 | ||||
-rw-r--r-- | nuttx/drivers/net/dm90x0.c | 1815 | ||||
-rw-r--r-- | nuttx/drivers/net/e1000.c | 1049 | ||||
-rw-r--r-- | nuttx/drivers/net/e1000.h | 123 | ||||
-rw-r--r-- | nuttx/drivers/net/enc28j60.c | 2302 | ||||
-rw-r--r-- | nuttx/drivers/net/enc28j60.h | 478 | ||||
-rw-r--r-- | nuttx/drivers/net/skeleton.c | 692 | ||||
-rw-r--r-- | nuttx/drivers/net/slip.c | 1017 | ||||
-rw-r--r-- | nuttx/drivers/net/vnet.c | 673 |
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 */ |