📄 cs8900if.c
字号:
/* * Copyright (c) 2001 Leon Woestenberg <leon.woestenberg@axon.nl> * Copyright (c) 2001 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. Neither the name of Axon Digital Design 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 AXON DIGITAL DESIGN 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 INSTITUTE 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. * * This file is a contribution to the lwIP TCP/IP stack. * The Swedish Institute of Computer Science and Adam Dunkels * are specifically granted permission to redistribute this * source code. * * This is a device driver for the Cirrus/Crystal Semiconductor CS8900A * chip in combination with the lwIP stack, release 0.5.0 and higher. * * This is work under development. Please coordinate changes * and requests with Leon Woestenberg <leon.woestenberg@axon.nl> * * 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 static functions of 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 intialize 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. * */#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 "lwip/arp.h"#include "lwip/lcd.h"#include "lwip/cs8900if.h"#if defined(CS8900_TX_QUEUE) && (CS8900_TX_QUEUE > 0)# include "lwip/ip.h"# include "lwip/ip_addr.h"#endif/* Define those to better describe your network interface. */#define IFNAME0 'e'#define IFNAME1 'n'#pragma section FAR_CODE=RAMCODE /* located the program into RAM */#pragma section CODE=RAMCODEstatic const struct eth_addr ethbroadcast = {{0xffU,0xffU,0xffU,0xffU,0xffU,0xffU}};/* Forward declarations. */static void cs8900if_input(struct netif *netif);static err_t cs8900if_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr);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);/* Define these to match your hardware setup */#define MEM_BASE 0x200000#define IO_BASE 0x0300#define INT_NR 0x00#define IOADR volatile __far unsigned int#define TXCMD *(IOADR *)(MEM_BASE + IO_BASE + 0x04)#define TXLENGTH *(IOADR *)(MEM_BASE + IO_BASE + 0x06)#define ISQ *(IOADR *)(MEM_BASE + IO_BASE + 0x08)#define PACKETPP *(IOADR *)(MEM_BASE + IO_BASE + 0x0A)#define PPDATA *(IOADR *)(MEM_BASE + IO_BASE + 0x0C)#define RXTXREG *(IOADR *)(MEM_BASE + IO_BASE)// 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#define ETHADDR0 (0x00U)#define ETHADDR1 (0x03U)#define ETHADDR2 (0x41U)#define ETHADDR3 (0xbbU)#define ETHADDR4 (0xaaU)#define ETHADDR5 (0xddU)static struct netif *cs8900if_netif;// hardware interrupt vector handlervoid cs8900_interrupt(void){ struct cs8900if *cs8900if = cs8900if_netif->state; if (cs8900if) { cs8900if->needs_service = 1;#if (CS8900_STATS > 0) cs8900if->interrupts++;#endif }}// cs8900_init()//// initializes the CS8900Astatic voidcs8900_init(struct netif *netif){ u16_t dummy; /* Obtain MAC address from network interface. */ /* XXX: make up an address. */ ((struct cs8900if *)netif->state)->ethaddr->addr[0] = ETHADDR0; ((struct cs8900if *)netif->state)->ethaddr->addr[1] = ETHADDR1; ((struct cs8900if *)netif->state)->ethaddr->addr[2] = ETHADDR2; ((struct cs8900if *)netif->state)->ethaddr->addr[3] = ETHADDR3; ((struct cs8900if *)netif->state)->ethaddr->addr[4] = ETHADDR4; ((struct cs8900if *)netif->state)->ethaddr->addr[5] = ETHADDR5; // 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 } // TODO: check if this is correct and/or necessary dummy = *(u8_t *)(MEM_BASE + IO_BASE + 0x0D) ; /* Dummy read, put chip in 16bit-mode */ dummy = *(u16_t *)(MEM_BASE + IO_BASE + 0x0D) ; // Set MAC address PACKETPP = CS_PP_IA1; PPDATA = (u16_t)ETHADDR0 | ((u16_t)ETHADDR1 << 8U); PACKETPP = CS_PP_IA2; PPDATA = (u16_t)ETHADDR2 | ((u16_t)ETHADDR3 << 8U); PACKETPP = CS_PP_IA3; PPDATA = (u16_t)ETHADDR4 | ((u16_t)ETHADDR5 << 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; struct pbuf *q;//Put_String("cs8900_output: call\n"); // exit if link has failed PACKETPP = CS_PP_LINESTATUS; if ((PPDATA & 0x0080U/*LinkOK*/) == 0) return ERR_CONN; // no Ethernet link /* transmit command */ TXCMD = 0x00C9U; 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) { 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 (CS8900_STATS > 0) ((struct cs8900if *)netif->state)->sentpackets++;#endif } return ERR_OK;}/* * cs8900_input(): * * Allocates a pbuf and transfers the bytes of the incoming * packet from the interface into the pbuf. * */// To be called just after reading an ISQ event// containing the "Receiver Event" register.//// This function copies a frame from the CS8900A.// It is designed fail-safe:// - It does not assume a frame is actually present.// - It checks for non-zero length// - It does not overflow the frame bufferstatic struct pbuf *cs8900_input(struct netif *netif){ struct pbuf *p = 0, *q; u16_t len = 0; u16_t event_type; u16_t i; u16_t *ptr; // read RxStatus event_type = RXTXREG; // correctly received frame, either broadcast or individual address? // TODO: think where these checks should appear: here or in cs8900_input() if ((event_type & 0x0100U/*RxOK*/) && (event_type & 0x0c00U/*Broadcast | Individual*/)) { event_type = 0; // read RxLength len = RXTXREG; DEBUGF(ETH_DEBUG, ("cs8900_input: packet len %u\n", len)); // 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(ETH_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?// Put_String("cs8900: pbuf error\n");// Put_String("cs8900: pack len=");// mprintf(len,6,0); // mprintf(stats.pbuf.used,6,0); PACKETPP = CS_PP_RXCFG; PPDATA = (0x0003U | 0x0100U/*RxOKiE*/ | 0x0040U/*Skip_1*/);#if (CS8900_STATS > 0) ((struct cs8900if *)netif->state)->dropped++;#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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -