--- /dev/null
+/** @file
+/*
+ * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ *
+ * This is a device driver for the Crystal Semiconductor CS8900
+ * chip in combination with the lwIP stack.
+ *
+ * This is work under development. Please coordinate changes
+ * and requests with Leon Woestenberg <leon.woestenberg@axon.tv>
+ *
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code under any conditions they seem fit.
+ *
+ * A quick function roadmap:
+ *
+ * cs8900_*() are low level, cs8900 hardware specific functions.
+ * These are declared static in the device driver source and
+ * SHOULD NOT need to be called from outside this source.
+ *
+ * cs8900if_*() are the lwIP network interface functions.
+ *
+ * cs8900_interrupt() is an early interrupt service routine (ISR).
+ * It merely sets a flag to indicate the cs8900 needs servicing.
+ * (This function MAY be tied to an interrupt vector, IF present).
+ *
+ * cs8900_service() is the actual interrupt event service routine.
+ * It must be called whenever the cs8900 needs servicing. It MAY
+ * be polled safely (so, you do NOT NEED interrupt support.)
+ *
+ * cs8900_init() sets up the cs8900, using its register set. When
+ * using the driver on your particular hardware platform, make sure
+ * the register setups match.
+ * Function is called from cs8900if_init().
+ *
+ * cs8900_input() transfers a received packet from the chip.
+ * Function is called from cs8900if_input().
+ *
+ * cs8900_output() transfers a packet to the chip for transmission.
+ * Function is called from cs8900if_output().
+ *
+ * cs8900if_init() initializes the lwIP network interface, and
+ * calls cs8900_init() to initialize the hardware.
+ * Function is called from lwIP.
+ *
+ * cs8900if_service() is the service routine, which must be called
+ * upon the need for service, or on a regular basis, in order to
+ * service the Ethernet chip.
+ *
+ * cs8900if_input() calls cs8900_input() to get a received packet
+ * and then forwards the packet to protocol(s) handler(s).
+ * Function is called from cs8900_service().
+ *
+ * cs8900if_output() resolves the hardware address, then
+ * calls cs8900_output() to transfer the packet.
+ * Function is called from lwIP.
+ *
+ * Future development:
+ *
+ * Split the generic Ethernet functionality (a lot of the
+ * cs8900if_*() functions) and the actual cs8900a dependencies.
+ *
+ * Enhance the interrupt handler to service the Ethernet
+ * chip (to decrease latency); support early packet
+ * inspection (during reception) to early drop unwanted
+ * packets, minimize chip buffer use and maximize throughput.
+ *
+ * Statistics gathering, currently under development.
+ * SNMP support, currently under development.
+ *
+ */
+
+#include "lwip/debug.h"
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "netif/etharp.h"
+
+#if 0
+// include some debugging help
+# define DBG_LEVEL 1
+# include "leds.h"
+# include "display.h"
+//# include "page.h"
+# define LED_NEED_SERVICE LED_FP1
+#else
+// no debugging
+# define leds_on()
+# define leds_off()
+#endif
+
+#include "cs8900if.h"
+#if LWIP_SNMP > 0
+# include "snmp.h"
+#endif
+
+// Define those to better describe your network interface
+#define IFNAME0 'e'
+#define IFNAME1 'n'
+
+static const struct eth_addr ethbroadcast = {{0xffU,0xffU,0xffU,0xffU,0xffU,0xffU}};
+
+// Forward declarations
+static err_t cs8900_output(struct netif *netif, struct pbuf *p);
+static struct pbuf *cs8900_input(struct netif *netif);
+static void cs8900_service(struct netif *netif);
+static u32_t cs8900_chksum(void *dataptr, int len);
+
+// Define these to match your hardware setup
+#define MEM_BASE 0x00E000
+#define IO_BASE 0x800
+#define INT_NR 0x00
+
+#define RXTXREG *((volatile u16_t *)(MEM_BASE + IO_BASE))
+#define TXCMD *((volatile u16_t *)(MEM_BASE + IO_BASE + 0x04))
+#define TXLENGTH *((volatile u16_t *)(MEM_BASE + IO_BASE + 0x06))
+#define ISQ *((volatile u16_t *)(MEM_BASE + IO_BASE + 0x08))
+#define PACKETPP *((volatile u16_t *)(MEM_BASE + IO_BASE + 0x0A))
+#define PPDATA *((volatile u16_t *)(MEM_BASE + IO_BASE + 0x0C))
+
+// CS8900 PacketPage register offsets
+#define CS_PP_EISA 0x0000 // EISA Registration number of CS8900
+#define CS_PP_PRODID 0x0002 // Product ID Number
+#define CS_PP_IOBASE 0x0020 // I/O Base Address
+#define CS_PP_INTNUM 0x0022 // Interrupt number (0,1,2, or 3)
+#define CS_PP_RXCFG 0x0102 // Receiver Configuration
+#define CS_PP_RXCTL 0x0104 // Receiver Control
+#define CS_PP_TXCFG 0x0106 // Transmit Configuration
+#define CS_PP_BUFCFG 0x010A // Buffer Configuration
+#define CS_PP_LINECTL 0x0112 // Line Control Register offset
+#define CS_PP_SELFCTL 0x0114 // Self Control
+#define CS_PP_BUSCTL 0x0116 // Bus Control
+#define CS_PP_TESTCTL 0x0118 // Test Control
+#define CS_PP_ISQ 0x0120 // Interrupt status queue
+#define CS_PP_RXEVENT 0x0124 // Receiver Event
+#define CS_PP_TX_EVENT 0x0128 // Transmitter Event
+#define CS_PP_BUF_EVENT 0x012C // Buffer Event
+#define CS_PP_RXMISS 0x0130 // Receiver Miss Counter
+#define CS_PP_TXCOL 0x0132 // Transmit Collision Counter
+#define CS_PP_LINESTATUS 0x0134 // Line Status
+#define CS_PP_SELFTEST 0x0136 // Self Status
+#define CS_PP_BUSSTATUS 0x0138 // Bus Status
+#define CS_PP_TXCMD 0x0144 // Transmit Command Request
+#define CS_PP_TXLEN 0x0146 // Transmit Length
+#define CS_PP_IA1 0x0158 // Individual Address (IA)
+#define CS_PP_IA2 0x015A // Individual Address (IA)
+#define CS_PP_IA3 0x015C // Individual Address (IA)
+
+#define CS_PP_RXSTATUS 0x0400 // Receive Status
+#define CS_PP_RXLEN 0x0402 // Receive Length
+#define CS_PP_RXFRAME 0x0404 // Receive Frame Location
+#define CS_PP_TXFRAME 0x0A00 // Transmit Frame Location
+
+
+// removed interrupt from library
+#if 0
+// hardware interrupt vector handler
+_interrupt(0x18) void cs8900_interrupt(void)
+{
+ struct cs8900if *cs8900if = cs8900if_netif->state;
+ // network interface is configured?
+ if (cs8900if != NULL)
+ {
+ // chip needs service
+ cs8900if->needs_service = 1;
+#if (CS8900_STATS > 0)
+ cs8900if->interrupts++;
+#endif
+ }
+#ifdef LED_NEED_SERVICE
+ leds_on(LED_NEED_SERVICE);
+#endif
+}
+#endif
+
+// cs8900_init()
+//
+// initializes the CS8900A chip
+//
+static void cs8900_init(struct netif *netif)
+{
+#ifdef LED_NEED_SERVICE
+ leds_off(LED_NEED_SERVICE);
+#endif
+
+ // set RESET bit
+ PACKETPP = CS_PP_SELFCTL;
+ PPDATA = 0x0055U;
+
+ // { the RESET bit will be cleared by the cs8900a
+ // as a result of the reset }
+ // RESET bit cleared?
+ while((PPDATA & 0x0040U) != 0); // TODO: add timeout
+
+ // { after full initialization of the cs8900a
+ // the INITD bit will be set }
+
+ PACKETPP = CS_PP_SELFTEST;
+ // INITD bit still clear?
+ while ((PPDATA & 0x0080U) == 0); // TODO: add timeout
+ // { INITD bit is set }
+
+ // SIBUSY bit still set?
+ while ((PPDATA & 0x0100U) == 0x0100); // TODO: add timeout
+ // { SIBUSY bit clear }
+
+#if 1
+ {
+ u16_t dummy;
+ // datasheet section 3.3.3
+ dummy = *(u16_t *)(MEM_BASE + IO_BASE + 0x0D);
+ // Dummy read, put chip in 16-bit mode
+ dummy = *(u16_t *)(MEM_BASE + IO_BASE + 0x0D);
+ }
+#endif
+
+ // Set MAC address
+ PACKETPP = CS_PP_IA1;
+ PPDATA = (u16_t)(netif->hwaddr[0]) | (u16_t)(netif->hwaddr[1] << 8U);
+ PACKETPP = CS_PP_IA2;
+ PPDATA = (u16_t)(netif->hwaddr[2]) | (u16_t)(netif->hwaddr[3] << 8U);
+ PACKETPP = CS_PP_IA3;
+ PPDATA = (u16_t)(netif->hwaddr[4]) | (u16_t)(netif->hwaddr[5] << 8U);
+
+ // accept valid unicast or broadcast frames
+ PACKETPP = CS_PP_RXCTL;
+ PPDATA = (0x0005U | 0x0800U/*broadcast*/ | 0x0400U/*individual*/ | 0x0100U/*RxOK*/);
+
+ // enable receive interrupt
+ PACKETPP = CS_PP_RXCFG;
+ PPDATA = (0x0003U | 0x0100U/*RXIRQ*/);
+
+ // disable transmit interrupt (is default)
+ PACKETPP = CS_PP_TXCFG;
+ PPDATA = (0x0007U | 0);
+
+ // use interrupt number 0
+ PACKETPP = CS_PP_INTNUM;
+ PPDATA = (0x0000U);
+
+ // generate interrupt event on:
+ // - the RxMISS counter reaches 0x200, or
+ // - a received frame is lost
+ PACKETPP = CS_PP_BUFCFG;
+ PPDATA = (0x000bU |
+#if (CS8900_STATS > 0) // interrupt before counter overflow
+ (0x2000U/*MissOvfloiE*/ | 0x1000U/*TxColOvfloiE*/) |
+#endif
+#if (CS8900_STATS > 1) // interrupt on counter increment
+ (0x0400U/*RxMissiE*/) |
+#endif
+ 0x0000);
+
+ // enable interrupt generation
+ PACKETPP = CS_PP_BUSCTL;
+ PPDATA = (0x0017U | 0x8000U/*EnableIRQ*/);
+
+ // enable:
+ // - receiver
+ // - transmitter
+ PACKETPP = CS_PP_LINECTL;
+ PPDATA = (0x0013U | 0x0080U/*SerTxOn*/ | 0x0040U/*SerRxOn*/);
+}
+
+static err_t cs8900_output(struct netif *netif, struct pbuf *p)
+{
+ int tries = 0;
+
+ // exit if link has failed
+ PACKETPP = CS_PP_LINESTATUS;
+ if ((PPDATA & 0x0080U/*LinkOK*/) == 0) return ERR_CONN; // no Ethernet link
+
+ /* issue 'transmit' command to CS8900 */
+ TXCMD = 0x00C9U;
+ /* send length (in bytes) of packet to send */
+ TXLENGTH = p->tot_len;
+
+ PACKETPP = CS_PP_BUSSTATUS;
+ // not ready for transmission and still within 100 retries?
+ while(((PPDATA & 0x0100U/*Rdy4TxNOW*/) == 0) && (tries++ < 100))
+ {
+ // throw away the last committed received frame
+ PACKETPP = CS_PP_RXCFG;
+ PPDATA = (0x0003U | 0x0040U/*Skip_1*/ | 0x0100U/*RxOKiE*/);
+ PACKETPP = CS_PP_BUSSTATUS;
+ /* cs8900if->dropped++; // CHECK: we do not know if we actually will drop a frame here */
+ }
+ // ready to transmit?
+ if((PPDATA & 0x0100U/*Rdy4TxNOW*/) != 0)
+ {
+ // q traverses through linked list of pbuf's
+ struct pbuf *q;
+ for(q = p; q != NULL; q = q->next)
+ {
+ u16_t i;
+ u16_t *ptr = (u16_t *)q->payload;
+ // Send the data from the pbuf to the interface, one pbuf at a
+ // time. The size of the data in each pbuf is kept in the ->len
+ // variable.
+ for(i = 0; i < q->len; i += 2)
+ {
+ RXTXREG = *ptr++;
+ }
+#if (CS8900_STATS > 0)
+ ((struct cs8900if *)netif->state)->sentbytes += q->len;
+#endif
+#if LWIP_SNMP > 0
+ snmp_add_ifoutoctets(p->tot_len);
+#endif
+#if (CS8900_STATS > 0)
+ ((struct cs8900if *)netif->state)->sentpackets++;
+#endif
+ }
+ }
+ else
+ {
+ // { not ready to transmit!? }
+#if LWIP_SNMP > 0
+ snmp_inc_ifoutdiscards();
+#endif
+ }
+ return ERR_OK;
+}
+
+/**
+ * Move a received packet from the cs8900 into a new pbuf.
+ *
+ * Must be called after reading an ISQ event containing the
+ * "Receiver Event" register, before reading new ISQ events.
+ *
+ * This function copies a frame from the CS8900A.
+ * It is designed failsafe:
+ * - It does not assume a frame is actually present.
+ * - It checks for non-zero length
+ * - It does not overflow the frame buffer
+ */
+static struct pbuf *cs8900_input(struct netif *netif)
+{
+ struct pbuf *p = NULL, *q = NULL;
+ u16_t len = 0;
+ u16_t event_type;
+ u16_t i;
+ u16_t *ptr = NULL;
+
+ // read RxStatus
+ event_type = RXTXREG;
+
+ // correctly received frame, either broadcast or individual address?
+ // TODO: maybe defer these conditions to cs8900_input()
+ if ((event_type & 0x0100U/*RxOK*/) && (event_type & 0x0c00U/*Broadcast | Individual*/))
+ {
+#if LWIP_SNMP > 0
+ // update number of received MAC-unicast and non-MAC-unicast packets
+ if (event_type & 0x0400U/*Individual*/)
+ {
+ snmp_inc_ifinucastpkts();
+ }
+ else
+ {
+ snmp_inc_ifinnucastpkts();
+ }
+#endif
+ event_type = 0;
+ // read RxLength
+ len = RXTXREG;
+ DEBUGF(NETIF_DEBUG, ("cs8900_input: packet len %u\n", len));
+#if LWIP_SNMP > 0
+ snmp_add_ifinoctets(len);
+#endif
+ // positive length?
+ if (len > 0)
+ {
+ // allocate a pbuf chain with total length 'len'
+ p = pbuf_alloc(PBUF_LINK, len, PBUF_POOL);
+ if (p != 0)
+ {
+ for (q = p; q != 0; q = q->next)
+ {
+ DEBUGF(NETIF_DEBUG, ("cs8900_input: pbuf @%p len %u\n", q, q->len));
+ ptr = q->payload;
+ // TODO: CHECK: what if q->len is odd? we don't use the last byte?
+ for (i = 0; i < (q->len + 1) / 2; i++)
+ {
+ *ptr = RXTXREG;
+ ptr++;
+ }
+ }
+ }
+ // could not allocate a pbuf
+ else
+ {
+ // skip received frame
+ // TODO: maybe do not skip the frame at this point in time?
+ PACKETPP = CS_PP_RXCFG;
+ PPDATA = (0x0003U | 0x0100U/*RxOKiE*/ | 0x0040U/*Skip_1*/);
+#if (CS8900_STATS > 0)
+ ((struct cs8900if *)netif->state)->dropped++;
+#endif
+#if LWIP_SNMP > 0
+ snmp_inc_ifindiscards();
+#endif
+ len = 0;
+ }
+ }
+ // length was zero
+ else
+ {
+ }
+ }
+ return p;
+}
+
+
+/**
+ * To be called when the cs8900a needs service. Does
+ * not assume the cs8900a needs service. Does test the
+ * cs8900a whether it needs service.
+ *
+ * As such, may be used robustly called as a deferred
+ * (or "late") interrupt handler, or may be called in
+ * a loop to implement polling, or both.
+ *
+ * Use cs8900if_service() from your application instead
+ * of this function.
+ */
+
+static void cs8900_service(struct netif *netif)
+{
+ // amount of ISQ's to handle (> 0) in one cs8900_service() call
+ unsigned char events2service = 1;
+ // NOTES:
+ // static, so only initialized to zero at program start.
+ // irq_status will always hold the last ISQ event register that
+ // still needs service. As such, we may leave this function if
+ // we encounter an event we cannot service yet, and return later
+ // to try to service it.
+ static u16_t irq_status = 0x0000U;
+
+ // The "cs8900_needs_service" flag indicates whether any events
+ // still need to be serviced.
+ // clear flag here.
+ // a receive interrupt can, *concurrently with this function*,
+ // set this flag on new ISQ event occurences.
+ // we will re-evaluate the correct setting of this flag at
+ // function exit (below).
+ ((struct cs8900if *)netif->state)->needs_service = 0;
+#ifdef LED_NEED_SERVICE
+ leds_off(LED_NEED_SERVICE);
+#endif
+ // no unhandled irq_status left?
+ if (irq_status == 0x0000U)
+ {
+ // read ISQ register
+ irq_status = ISQ;
+ }
+ // ISQ interrupt event, and allowed to service in this loop?
+ while ((irq_status != 0x0000U) && (events2service-- > 0))
+ {
+ // investigate event
+ if ((irq_status & 0x003fU) == 0x0004U/*Receiver Event*/)
+ {
+ // correctly received frame, either broadcast or individual address
+ // TODO: think where these checks should appear: here or in cs8900_input()
+ if ((irq_status & 0x0100U/*RxOK*/) && (irq_status & 0x0c00U/*Broadcast | Individual*/))
+ {
+ // read the frame from the cs8900a
+ cs8900if_input(netif);
+ }
+ else
+ {
+ // skip this frame
+ PACKETPP = CS_PP_RXCFG;
+ PPDATA |= 0x0040U/*Skip_1*/;
+#if (CS8900_STATS > 0)
+ ((struct cs8900if *)netif->state)->dropped++;
+#endif
+ }
+ }
+#if (CS8900_STATS > 0)
+ else if ((irq_status & 0x003fU) == 0x0010U/*RxMISS Event*/)
+ {
+ ((struct cs8900if *)netif->state)->missed += (irq_status >> 6);
+ }
+ else if ((irq_status & 0x003fU) == 0x0012U/*TxCOL Event*/)
+ {
+ ((struct cs8900if *)netif->state)->collisions += (irq_status >> 6);
+ }
+#endif
+ // read ISQ register
+ irq_status = ISQ;
+ }
+
+ // we did not deplete the ISQ?
+ if (irq_status != 0x0000U)
+ {
+ // the cs8900a still needs service
+ ((struct cs8900if *)netif->state)->needs_service = 1;
+#ifdef LED_NEED_SERVICE
+ leds_on(LED_NEED_SERVICE);
+#endif
+ }
+#if (CS8900_STATS > 1)
+ // read RxMiss Counter (zeroes itself upon read)
+ PACKETPP = CS_PP_RXMISS;
+ ((struct cs8900if *)netif->state)->missed += (PPDATA >> 6);
+ // read RxCol Counter (zeroes itself upon read)
+ PACKETPP = CS_PP_TXCOL;
+ ((struct cs8900if *)netif->state)->collisions += (PPDATA >> 6);
+#endif
+}
+
+/**
+ * Service the CS8900.
+ *
+ * Can be called in a polling manner, or only after the CS8900 has raised
+ * an interrupt request.
+ *
+ * @param netif The lwIP network interface data structure belonging to this device.
+ *
+ */
+void cs8900if_service(struct netif *netif)
+{
+ // is there a reason to call the service routine?
+ if ((((struct cs8900if *)netif->state)->needs_service) ||
+ (((struct cs8900if *)netif->state)->use_polling))
+ {
+ cs8900_service(netif);
+ }
+}
+
+/**
+ * Writing an IP packet (to be transmitted) to the CS8900.
+ *
+ * Before writing a frame to the CS8900, the ARP module is asked to resolve the
+ * Ethernet MAC address. The ARP module might undertake actions to resolve the
+ * address first, and queue this packet for later transmission.
+ *
+ * @param netif The lwIP network interface data structure belonging to this device.
+ * @param p pbuf to be transmitted (or the first pbuf of a chained list of pbufs).
+ * @param ipaddr destination IP address.
+ *
+ * @internal It uses the function cs8900_input() that should handle the actual
+ * reception of bytes from the network interface.
+ *
+ */
+err_t cs8900if_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)
+{
+ struct cs8900if *cs8900if = netif->state;
+ p = etharp_output(netif, ipaddr, p);
+ /* network hardware address obtained? */
+ if (p != NULL)
+ {
+ /* send out the packet */
+ cs8900_output(netif, p);
+ /* ARP cleanup */
+ etharp_output_sent(p);
+ p = NULL;
+ }
+ // { p == NULL }
+ else
+ {
+ /* we cannot tell if the packet was sent, the packet could have been queued */
+ /* on an ARP entry that was already pending. */
+ }
+ return ERR_OK;
+}
+/**
+ * Read a received packet from the CS8900.
+ *
+ * This function should be called when a packet is received by the CS8900
+ * and is fully available to read. It moves the received packet to a pbuf
+ * which is forwarded to the IP network layer or ARP module. It transmits
+ * a resulting ARP reply or queued packet.
+ *
+ * @param netif The lwIP network interface to read from.
+ *
+ * @internal Uses cs8900_input() to move the packet from the CS8900 to a
+ * newly allocated pbuf.
+ *
+ */
+void cs8900if_input(struct netif *netif)
+{
+ struct cs8900if *cs8900if = netif->state;
+ struct eth_hdr *ethhdr = NULL;
+ struct pbuf *p = NULL, *q = NULL;
+
+ /* move received packet into a new pbuf */
+ p = cs8900_input(netif);
+ /* no packet could be read */
+ if (p == NULL) {
+ /* silently ignore this */
+ return;
+ }
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = p->payload;
+
+ q = NULL;
+ switch(htons(ethhdr->type)) {
+ /* IP packet? */
+ case ETHTYPE_IP:
+ /* update ARP table, obtain first queued packet */
+ q = etharp_ip_input(netif, p);
+ /* skip Ethernet header */
+ pbuf_header(p, -14);
+ /* pass to network layer */
+ netif->input(p, netif);
+ break;
+ /* ARP packet? */
+ case ETHTYPE_ARP:
+ /* pass p to ARP module, get ARP reply or ARP queued packet */
+ q = etharp_arp_input(netif, (struct eth_addr *)&netif->hwaddr, p);
+ break;
+ /* unsupported Ethernet packet type */
+ default:
+ /* free pbuf */
+ pbuf_free(p);
+ p = NULL;
+ break;
+ }
+ /* send out the ARP reply or ARP queued packet */
+ if (q != NULL) {
+ /* q pbuf has been succesfully sent? */
+ if (cs8900_output(netif, q) == ERR_OK)
+ {
+ pbuf_free(q);
+ q = NULL;
+ }
+ else
+ {
+ /* TODO: re-queue packet in the ARP cache here (?) */
+ pbuf_free(q);
+ q = NULL;
+ }
+ }
+}
+/**
+ * Initialize the CS8900 Ethernet MAC/PHY device driver.
+ *
+ * @param netif The lwIP network interface data structure belonging to this device.
+ *
+ */
+void cs8900if_init(struct netif *netif)
+{
+ struct cs8900if *cs8900if;
+
+ cs8900if = mem_malloc(sizeof(struct cs8900if));
+ if(cs8900if == NULL) return;
+
+ // initialize lwip network interface
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+ netif->output = cs8900if_output;
+ netif->linkoutput = cs8900_output;
+
+ // initialize cs8900 specific interface structure
+ netif->state = cs8900if;
+
+ // initially assume no ISQ event
+ cs8900if->needs_service = 0;
+ // set to 1 if polling method is used
+ cs8900if->use_polling = 0;
+
+#if (CS8900_STATS > 0)
+ // number of interrupt service routine calls
+ cs8900if->interrupts = 0;
+ cs8900if->missed = 0;
+ cs8900if->dropped = 0;
+ cs8900if->sentpackets = 0;
+ cs8900if->sentbytes = 0;
+#endif
+
+ // intialize the cs8900a chip
+ cs8900_init(netif);
+}
+
+#if 1
+/**
+ * Dump an array of bytes inside a UDP message's data field.
+ *
+ * It is a self-contained function, independent of higher protocol layers or other
+ * functions, so it allows you to debug these higher layers, such as lwIP.
+ *
+ * @param p pointer to an array of bytes, at least with length 'len'
+ * @param len number of bytes available at the address pointed to by 'p'
+ */
+void cs8900_send_debug(unsigned char *p, unsigned int len)
+{
+ int tries = 0, i;
+
+ // network interface state
+ extern struct netif *ethif;
+
+ // exit if link has failed
+ PACKETPP = CS_PP_LINESTATUS;
+ if ((PPDATA & 0x0080U/*LinkOK*/) == 0) return; // TODO: find a correct error code
+
+ // transmit command
+ TXCMD = 0x00C9U;
+ // send at least 60 bytes
+ TXLENGTH = (14 + 20 + 8 + len < 60) ? 60 : (14 + 20 + 8 + len);
+
+ PACKETPP = CS_PP_BUSSTATUS;
+ // not ready for transmission and still within 100 retries?
+ while (((PPDATA & 0x0100U/*Rdy4TxNOW*/) == 0) && (tries++ < 100))
+ {
+ // throw away the last committed received frame
+ PACKETPP = CS_PP_RXCFG;
+ PPDATA = (0x0003U | 0x0040U/*Skip_1*/ | 0x0100U/*RxOKiE*/);
+ PACKETPP = CS_PP_BUSSTATUS;
+ /* cs8900if->dropped++; CHECK: we do not know if we actually will drop a frame here, do we? */
+ }
+ // ready to transmit?
+ if((PPDATA & 0x0100U/*Rdy4TxNOW*/) != 0)
+ {
+ u16_t data, checksum = 0;
+ u32_t udp_checksum = 0;
+
+ // destination Ethernet address
+ RXTXREG = 0xa000U;
+ RXTXREG = 0xc524U;
+ RXTXREG = 0x6d72U;
+ // source Ethernet address
+ RXTXREG = htons(((u16_t)ethif->hwaddr[0] << 8U) | (u16_t)ethif->hwaddr[1]);
+ RXTXREG = htons(((u16_t)ethif->hwaddr[2] << 8U) | (u16_t)ethif->hwaddr[3]);
+ RXTXREG = htons(((u16_t)ethif->hwaddr[4] << 8U) | (u16_t)ethif->hwaddr[5]);
+ // frame type
+ RXTXREG = htons(0x0800);
+ // TOS, version
+ RXTXREG = htons(data = ((0x40 | 0x05) << 8) | 0x00);
+ checksum += data;
+ // length
+ RXTXREG = htons(data = 20 + 8 + len);
+ checksum += data;
+ // identifier
+ RXTXREG = htons(data = 0);
+ checksum += data;
+ // fragment offset
+ RXTXREG = htons(data = 0);
+ checksum += data;
+ // TTL, UDP protocol
+ RXTXREG = htons(data = (255U << 8) | 17U);
+ checksum += data;
+
+ checksum += (htonl(ethif->ip_addr.addr) & 0xffff0000U) >> 16;
+ checksum += (htonl(ethif->ip_addr.addr) & 0x0000ffffU);
+ checksum += 0xc0a8U;
+ checksum += 0x0001U;
+ checksum += 6; // LW: kludge/hack: checksum calculation seems to be wrong somehow
+ // LW: this seems (?) to fix it
+ // checksum
+ RXTXREG = htons(~checksum);
+
+ // source IP
+ RXTXREG = htons((htonl(ethif->ip_addr.addr) & 0xffff0000U) >> 16);
+ // source IP
+ RXTXREG = htons( htonl(ethif->ip_addr.addr) & 0x0000ffffU);
+ // destination IP
+ RXTXREG = htons(0xc0a8U);
+ // destination IP
+ RXTXREG = htons(0x0001U);
+ // source port 3000
+ RXTXREG = htons(3000U);
+ // destination port 3000
+ RXTXREG = htons(3000U);
+ // UDP length
+ RXTXREG = htons(len);
+ // UDP checksum (not present)
+
+ udp_checksum = (htonl(ethif->ip_addr.addr) & 0xffff0000U) >> 16;
+ udp_checksum += (htonl(ethif->ip_addr.addr) & 0x0000ffffU);
+ udp_checksum += 0xc0a8U;
+ udp_checksum += 0x0001U;
+ udp_checksum += 0x0011U;
+ udp_checksum += (8 + len);
+ udp_checksum += 3000;
+ udp_checksum += 3000;
+ udp_checksum += (8 + len);
+ udp_checksum += cs8900_chksum(p, len);
+ while(udp_checksum >> 16) {
+ udp_checksum = (udp_checksum & 0xffffUL) + (udp_checksum >> 16);
+ }
+
+ RXTXREG = htons(~(udp_checksum & 0xffff));
+ // UDP data
+ for (i = 0; i < len; i += 2)
+ {
+ RXTXREG = htons((p[i] << 8) | p[i + 1]);
+ }
+ // pad to 60 bytes
+ while (i < 60)
+ {
+ RXTXREG = 0;
+ i += 2;
+ }
+ }
+}
+
+static u32_t cs8900_chksum(void *dataptr, int len)
+{
+ u32_t acc = 0;
+ u16_t *ptr = (u16_t *)dataptr;
+
+ for(acc = 0; len > 1; len -= 2) {
+ acc += *ptr;
+ ptr++;
+ }
+ /* add up any odd byte */
+ if(len == 1) {
+ acc += htons((u16_t)((*(u8_t *)ptr) & 0xffU) << 8);
+ }
+ return acc;
+}
+
+#endif
\ No newline at end of file