/* * Copyright (C) 2003-2005 by egnite Software GmbH. 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 the copyright holders nor the names of * 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. * * For additional information see http://www.ethernut.de/ * * -- * Initially taken from Marek Hummel's port of the Realtek driver. * * Revision 1.0 2004/07/20 17:29:08 MarekH */ /* * $Log$ * Revision 1.9 2009/01/17 11:26:37 haraldkipp * Getting rid of two remaining BSD types in favor of stdint. * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'. * * Revision 1.8 2008/08/28 11:12:15 haraldkipp * Added interface flags, which will be required to implement Ethernet ioctl * functions. * * Revision 1.7 2008/08/11 06:59:07 haraldkipp * BSD types replaced by stdint types (feature request #1282721). * * Revision 1.6 2007/05/02 11:22:51 haraldkipp * Added multicast table entry. * * Revision 1.5 2006/06/28 17:10:15 haraldkipp * Include more general header file for ARM. * * Revision 1.4 2006/03/02 19:48:19 haraldkipp * Replaced inline assembly nops with their portable counterparts. * * Revision 1.3 2006/01/23 17:27:47 haraldkipp * Previous hack to fix missing network configuration routines disabled * non-volatile memory access for ARM. * * Revision 1.2 2005/10/22 08:55:47 haraldkipp * CPU specific headers moved to subdirectories for the CPU family. * * Revision 1.1 2005/07/26 18:02:26 haraldkipp * Moved from dev. * * Revision 1.1 2005/04/05 17:47:48 haraldkipp * Initial check in. For ARM only. * */ #include #include //#include #include #include #include #include #include #include #include #include #include "reg_ax88796.h" #define ASIX_RESET_PIN 10 static NICINFO dcb_eth0; /*! * \addtogroup xgNicAsix */ /*@{*/ /*! * \brief Network interface information structure. * * Used to call. */ static IFNET ifn_eth0 = { IFT_ETHER, /*!< \brief Interface type. */ 0, /*!< \brief Interface flags, if_flags. */ {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address. */ 0, /*!< \brief IP address. */ 0, /*!< \brief Remote IP address for point to point. */ 0, /*!< \brief IP network mask. */ ETHERMTU, /*!< \brief Maximum size of a transmission unit. */ 0, /*!< \brief Packet identifier. */ 0, /*!< \brief Linked list of arp entries. */ 0, /*!< \brief Linked list of multicast address entries, if_mcast. */ NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */ AsixOutput, /*!< \brief Driver output routine, if_send(). */ NutEtherOutput, /*!< \brief Media output routine, if_output(). */ NULL /*!< \brief Interface specific control function, if_ioctl(). */ #ifdef NUT_PERFMON , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 #endif }; /*! * \brief Device information structure. * * A pointer to this structure must be passed to NutRegisterDevice() * to bind this Ethernet device driver to the Nut/OS kernel. * An application may then call NutNetIfConfig() with the name \em eth0 * of this driver to initialize the network interface. * */ NUTDEVICE devAx88796 = { 0, /* Pointer to next device. */ {'e', 't', 'h', '0', 0, 0, 0, 0, 0}, /* Unique device name. */ IFTYP_NET, /* Type of device. */ 0, /* Base address. */ 0, /* First interrupt number. */ &ifn_eth0, /* Interface control block. */ &dcb_eth0, /* Driver control block. */ AsixInit, /* Driver initialization routine. */ 0, /* Driver specific control function. */ 0, /* Read from device. */ 0, /* Write to device. */ 0, /* Open a device or file. */ 0, /* Close a device or file. */ 0, /* Request file size. */ 0, /* Select function, optional, not yet implemented */ }; /*! * Asix packet header. */ struct nic_pkt_header { uint8_t ph_status; /*!< \brief Status, contents of RSR register */ uint8_t ph_nextpg; /*!< \brief Page for next packet */ uint16_t ph_size; /*!< \brief Size of header and packet in octets */ }; /* * This delay has been added by Bengt Florin and is used to minimize * the effect of the IORDY line during reads. Bengt contributed a * more versatile loop, which unfortunately wasn't portable to the * ImageCraft compiler. * * Both versions depend on the CPU clock and had been tested with * 14.7456 MHz. */ void Delay16Cycles(void) { _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); _NOP(); } //============================================================================== /*! * \brief Read and write MII bus. * * */ static uint16_t MIIPutGet(uint16_t data, uint8_t bitCount) { uint16_t rc = 0; uint16_t mask; uint8_t i; mask = 1 << (bitCount - 1); for (i = 0; i < bitCount; i++) { /* send data to MII */ if (data & mask) { Asix_Write(MII_EEP, (Asix_Read(MII_EEP) | MII_EEP_MDO)); } else { Asix_Write(MII_EEP, (Asix_Read(MII_EEP) & ~(MII_EEP_MDO))); } /* clock and data recieve from MII */ Asix_Write(MII_EEP, (Asix_Read(MII_EEP) | MII_EEP_MDC)); //clock up Delay16Cycles(); data <<= 1; rc <<= 1; rc |= (Asix_Read(MII_EEP) & MII_EEP_MDI) != 0; //read MII Asix_Write(MII_EEP, (Asix_Read(MII_EEP) & ~(MII_EEP_MDC))); //clock down } return rc; } //============================================================================== /*! * \brief Read contents of internel PHY register on 0x10 adress. * * * \return Contents of the specified register. */ uint16_t NicPhyRead(uint8_t reg) { uint16_t rc = 0; /* Select Bank 0. */ Asix_Write(CR, (Asix_Read(CR) & ~(CR_PS0))); /* Preamble. Send idle pattern, 32 '1's to synchronize MII. */ MIIPutGet(0xFFFF, 16); MIIPutGet(0xFFFF, 16); /* Start(01), Read(10), PhyAdr(10000) */ MIIPutGet(0xD0, 9); /* Reg address<5:0> */ MIIPutGet(reg, 5); /* TA(Z0), no support high-Z */ MIIPutGet(0x0, 1); /* Read data from internel PHY */ rc = MIIPutGet(0, 16); return rc; } //============================================================================== /*! * \brief Write value to PHY register. * * \note NIC interrupts must have been disabled before calling this routine. * * \param reg PHY register number. * \param val Value to write. */ void NicPhyWrite(uint8_t reg, uint16_t val) { /* Select Bank 0. */ Asix_Write(CR, (Asix_Read(CR) & ~(CR_PS0))); /* Preamble. Send idle pattern, 32 '1's to synchronize MII. */ MIIPutGet(0xFFFF, 16); MIIPutGet(0xFFFF, 16); /* Start(01), Write(01), PhyAdr(10000) */ MIIPutGet(0xB0, 9); /* Reg address<5:0> */ MIIPutGet(reg, 5); /* TA(01) */ MIIPutGet(0x02, 2); /* Write data to internel PHY */ MIIPutGet(val, 16); } //============================================================================== /*! * Complete remote DMA. */ static void NicCompleteDma(void) { uint8_t i; /* Check that we have a DMA complete flag. */ do { i = Asix_Read(PG0_ISR); } while ((i & ISR_RDC) == 0); /* Complete remote dma. */ Asix_Write(CR, CR_START | CR_RD2); /* Reset remote dma complete flag. */ Asix_Write(PG0_ISR, ISR_RDC); /* Wait for intterupt flag. */ Delay16Cycles(); } //============================================================================== /*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */ static int NicReset(void) { int tmp; //uint16_t test; //printf("NicReset()\n"); outr(PIO_PER, _BV(ASIX_RESET_PIN)); /* Set PIO Enable Register */ outr(PIO_OER, _BV(ASIX_RESET_PIN)); /* Set PIO Status Register */ outr(PIO_SODR, _BV(ASIX_RESET_PIN)); /* ASIX_RESET_PIN = 1 */ NutDelay(100); outr(PIO_CODR, _BV(ASIX_RESET_PIN)); /* ASIX_RESET_PIN = 0 */ /* wait for PHY to come out of reset. */ tmp = 10; while (1) { NutDelay(255); if (!(Asix_Read(TR) & TR_RST_B)) break; if (tmp-- == 0) return -1; } //test = NicPhyRead(PHY_MR2); //printf("PHY_MR2 = 0x%.04x\n\r", test); //test = NicPhyRead(PHY_MR3); //printf("PHY_MR3 = 0x%.04x\n\r", test); /* Following actions will fix the problem of long auto negotiation. */ // NicPhyWrite(0x00,0x0800); // NutSleep(2500); // NicPhyWrite(0x00,0x1200); /* set speed to auto */ return 0; } //============================================================================== /* * Fires up the network interface. NIC interrupts * should have been disabled when calling this * function. * * \param mac Six byte unique MAC address. */ static int NicStart(const uint8_t * mac) { uint8_t i; //printf("NicStart()\n"); if (NicReset()) return -1; /* Stop the NIC, abort DMA, page 0. */ Asix_Write(CR, (CR_RD2 | CR_STOP)); /* Selects word-wide DMA transfers. */ Asix_Write(PG0_DCR, DCR_WTS); /* Load data byte count for remote DMA. */ Asix_Write(PG0_RBCR0, 0x00); Asix_Write(PG0_RBCR1, 0x00); /* Temporarily set receiver to monitor mode. */ Asix_Write(PG0_RCR, RCR_MON); /* Transmitter set to internal loopback mode. */ Asix_Write(PG0_TCR, TCR_LB0); /* Initialize Recieve Buffer Ring. */ Asix_Write(PG0_BNRY, RXSTART_INIT); Asix_Write(PG0_PSTART, RXSTART_INIT); Asix_Write(PG0_PSTOP, RXSTOP_INIT); /* Clear interrupt status. */ Asix_Write(PG0_ISR, 0xFF); /* Initialize interrupt mask. */ Asix_Write(PG0_IMR, IMR_PRXE | IMR_PTXE | IMR_RXEE | IMR_TXEE | IMR_OVWE); /* Stop the NIC, abort DMA, page 1. */ Asix_Write(CR, (CR_PS0 | CR_RD2 | CR_STOP)); Delay16Cycles(); /* Set Physical address - MAC. */ for (i = 0; i < 6; i++) { Asix_Write(PG1_PAR0 + i, mac[i]); } /* Set Multicast address. */ for (i = 0; i < 8; i++) { Asix_Write(PG1_MAR0 + i, 0x00); } /* Set Current pointer point. */ Asix_Write(PG1_CPR, RXSTART_INIT + 1); /* Start the NIC, Abort DMA, page 0. */ Asix_Write(CR, (CR_RD2 | CR_START)); // stop the NIC, abort DMA, page 0 Delay16Cycles(); /* Select media interfac. */ Asix_Write(GPOC, 0x10); /* Check PHY speed setting 100base = full duplex, 10base > half duplex. */ if (Asix_Read(GPI) & 0x04) { //printf("Full duplex\n"); Asix_Write(PG0_TCR, TCR_FDU); } else { //printf("Half duplex\n"); Asix_Write(PG0_TCR, 0); } /* Enable receiver and set accept broadcast. */ //Asix_Write(PG0_RCR, (RCR_INTT | RCR_AB)); Asix_Write(PG0_RCR, RCR_AB); return 0; } //============================================================================== /* * Write data block to the NIC. */ static void NicWrite(uint8_t * buf, uint16_t len) { register uint16_t *wp = (uint16_t *) buf; if (len & 1) len++; len >>= 1; //printf("Write(%u): ", len); while (len--) { //printf("%04X ", *wp); Asix_WriteWord(DATAPORT, *wp); wp++; } //putchar('\n'); } //============================================================================== /* * Read data block from the NIC. */ static void NicRead(uint8_t * buf, uint16_t len) { register uint16_t *wp = (uint16_t *) buf; if (len & 1) len++; len >>= 1; //printf("Read(%u): ", len); while (len--) { *wp = Asix_ReadWord(DATAPORT); //printf("%04X ", *wp); wp++; } //putchar('\n'); } //============================================================================== /*! * \brief Fetch the next packet out of the receive ring buffer. * * Nic interrupts must be disabled when calling this funtion. * * \return Pointer to an allocated ::NETBUF. If there is no * no data available, then the function returns a * null pointer. If the NIC's buffer seems to be * corrupted, a pointer to 0xFFFF is returned. */ static NETBUF *NicGetPacket(void) { NETBUF *nb = 0; struct nic_pkt_header hdr; uint16_t count; uint8_t nextpg; uint8_t bnry; uint8_t curr; uint8_t drop = 0; /* we don't want to be interrupted by NIC owerflow */ NutEnterCritical(); /* * Get the current page pointer. It points to the page where the NIC * will start saving the next incoming packet. */ curr = Asix_Read(PG0_CPR); //printf("curr=%02X\n", curr); /* * Get the pointer to the last page we read from. The following page * is the one where we start reading. If it's equal to the current * page pointer, then there's nothing to read. In this case we return * a null pointer. */ if ((bnry = Asix_Read(PG0_BNRY) + 1) >= RXSTOP_INIT) { //printf("bnry=%02X?\n", bnry); bnry = RXSTART_INIT; } if (bnry == curr) { //printf("bnry=%02X\n", bnry); NutJumpOutCritical(); return 0; } /* * Read the NIC specific packet header (4 bytes). */ Asix_Write(PG0_RBCR0, sizeof(struct nic_pkt_header)); Asix_Write(PG0_RBCR1, 0); Asix_Write(PG0_RSAR0, 0); Asix_Write(PG0_RSAR1, bnry); Asix_Write(CR, CR_START | CR_RD0); Delay16Cycles(); NicRead((uint8_t *) & hdr, sizeof(struct nic_pkt_header)); NicCompleteDma(); //printf("[S=%02X N=%02X L=%u]", hdr.ph_status, hdr.ph_nextpg, hdr.ph_size); /* * Check packet length. Silently discard packets of illegal size. */ if (hdr.ph_size < 60 + sizeof(struct nic_pkt_header) || hdr.ph_size > 1514 + sizeof(struct nic_pkt_header)) { //printf("Drop\n"); drop = 1; } /* * Calculate the page of the next packet. If it differs from the * pointer in the packet header, we discard the whole buffer * and return a null pointer. */ // nextpg = hdr.ph_nextpg; // bnry = hdr.ph_nextpg - 1; // if(bnry < RXSTART_INIT) bnry = RXSTOP_INIT - 1; // printf("hdr.ph_size = %02u\n\r",hdr.ph_size); // printf("hdr.ph_nextpg = %02x\n\r",hdr.ph_nextpg); nextpg = bnry + (hdr.ph_size >> 8) + ((hdr.ph_size & 0xFF) != 0); //printf("[nextpg = %02x ->", nextpg); if (nextpg >= RXSTOP_INIT) { nextpg -= RXSTOP_INIT; nextpg += RXSTART_INIT; } if (nextpg != hdr.ph_nextpg) { uint8_t nextpg1 = nextpg + 1; if (nextpg1 >= RXSTOP_INIT) { nextpg1 -= RXSTOP_INIT; nextpg1 += RXSTART_INIT; } //HK if (nextpg1 != hdr.ph_nextpg) { //HK return (NETBUF *) 0xFFFF; //HK } nextpg = nextpg1; } //printf("%02x]", nextpg); /* * Check packet status. It should have set bit 0, but * even without this bit packets seem to be OK. */ //printf("Drop=%u Status=%02X\n", drop, hdr.ph_status & 0x0E); if (!drop && ((hdr.ph_status & 0x0E) == 0)) { /* Allocate a NETBUF. */ count = hdr.ph_size - sizeof(struct nic_pkt_header); //printf("Count=%u\n", count); if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, count))) { /* * Set remote dma byte count and * start address. Don't read the * header again. */ Asix_Write(PG0_RBCR0, count); Asix_Write(PG0_RBCR1, count >> 8); Asix_Write(PG0_RSAR0, sizeof(struct nic_pkt_header)); Asix_Write(PG0_RSAR1, bnry); /* * Perform the read. */ Asix_Write(CR, CR_START | CR_RD0); Delay16Cycles(); NicRead(nb->nb_dl.vp, count); NicCompleteDma(); } } /* * Set boundary register to the last page we read. * This also drops packets with errors */ if (--nextpg < RXSTART_INIT) nextpg = RXSTOP_INIT - 1; Asix_Write(PG0_BNRY, nextpg); NutExitCritical(); return nb; } //============================================================================== /* * \brief Handle NIC overflows. * * When a receiver buffer overflow occurs, the NIC will defer any subsequent * action until properly restarted. * * This routine is called within interrupt context, which introduces a big * problem. It waits for the last transmission to finish, which may take * several milliseconds. Since Nut/OS 3.5, we do not support nested interrupts * on AVR systems anymore. So this routine may now increase interrupt * latency in an unacceptable way. The solution might be to handle overflows * in the receiver thread. * * In any case, this routines needs a major redesign. But it has been * tested in its current form to gracefully withstand ping floods. Thanks * to Bengt Florin for contributing his code, which provides much more * stability than its predecessor. */ static uint8_t NicOverflow(volatile uint8_t * base) { unsigned int cr; unsigned int resend = 0; unsigned int curr; /* * Wait for any transmission in progress. Save the command register, * so we can later determine, if NIC transmitter has been interrupted. * or reception in progress. */ while (Asix_Read(CR) & CR_TXP); cr = Asix_Read(CR); /* * Get the current page pointer. It points to the page where the NIC * will start saving the next incoming packet. */ Asix_Write(CR, CR_STOP | CR_RD2 | CR_PS0); curr = Asix_Read(PG1_CPR); Asix_Write(CR, CR_STOP | CR_RD2); /* Clear remote byte count register. */ Asix_Write(PG0_RBCR0, 0); Asix_Write(PG0_RBCR1, 0); /* Check for any incomplete transmission. */ if ((cr & CR_TXP) && ((Asix_Read(PG0_ISR) & (ISR_PTX | ISR_TXE)) == 0)) { resend = 1; } /* Enter loopback mode and restart the NIC. */ Asix_Write(PG0_TCR, TCR_LB0); Asix_Write(CR, CR_START | CR_RD2); /* * Discard all packets from the receiver buffer. Set boundary * register to the last page we read. */ if (--curr < TXSTART_INIT) { curr = RXSTOP_INIT - 1; } Asix_Write(PG0_BNRY, curr); /* Switch from loopback to normal mode mode. */ Asix_Write(PG0_TCR, 0); /* Re-invoke any interrupted transmission. */ if (resend) { Asix_Write(CR, CR_START | CR_TXP | CR_RD2); } /* Finally clear the overflow flag */ Asix_Write(PG0_ISR, ISR_OVW); return resend; } //============================================================================== static int NicPutPacket(NETBUF * nb) { uint16_t sz; // packed size uint16_t send_sz; // send packed size, min 60 static uint8_t first_put = 0; int tmp; /* * Fix problem after power on. */ if (first_put != 1) { Asix_Write(CR, 0x21); NutDelay(1); Asix_Write(CR, 0x22); first_put = 1; } /* * Calculate the number of bytes to be send. Do not * send packets larger than 1518 bytes. */ sz = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz; if (sz > 1518) return -1; //printf("Asix Send %u\n", sz); /* * Calculate the number of min bytes. Pad will be * added when packet lenght less than 60. Enable in TCR reg. PD = 0. */ send_sz = sz; if (sz <= 60) send_sz = 60; /* Disable Asix interrupts. */ NutEnterCritical(); /* start the NIC */ Asix_Write(CR, (CR_RD2 | CR_START)); /* * Bengt Florin introduces polling mode for the transmitter. Be * aware, that this may introduce other problems. If a high * priority thread is waiting for the transmitter, it may hold * the CPU for more than 1.2 milliseconds in worst cases. */ tmp = 120; while ((Asix_Read(CR) & CR_TXP) && tmp--) NutDelay(1); /* set start address for remote DMA operation */ Asix_Write(PG0_RSAR0, 0x00); Asix_Write(PG0_RSAR1, TXSTART_INIT); /* load data byte count for remote DMA */ Asix_Write(PG0_RBCR0, (unsigned char) (sz)); Asix_Write(PG0_RBCR1, (unsigned char) (sz >> 8)); /* do remote write operation */ Asix_Write(CR, (CR_RD1 | CR_START)); /* Transfer the Ethernet frame. */ NicWrite(nb->nb_dl.vp, nb->nb_dl.sz); NicWrite(nb->nb_nw.vp, nb->nb_nw.sz); NicWrite(nb->nb_tp.vp, nb->nb_tp.sz); NicWrite(nb->nb_ap.vp, nb->nb_ap.sz); /* Complete remote dma. */ NicCompleteDma(); /* Clear interrupt flags. */ Asix_Write(PG0_ISR, (ISR_PTX | ISR_TXE)); Delay16Cycles(); /* Set size and pointer to data. */ Asix_Write(CR, CR_START | CR_RD2); //printf("Asix Start Send\n"); Asix_Write(PG0_TBCR0, (unsigned char) (send_sz)); Asix_Write(PG0_TBCR1, (unsigned char) ((send_sz) >> 8)); Asix_Write(PG0_TPSR, TXSTART_INIT); /* Start transmission. */ Asix_Write(CR, CR_START | CR_TXP | CR_RD2); /* Enable Asix interrupts. */ NutExitCritical(); return 0; } //============================================================================== /*! * \brief NIC receiver thread. * */ THREAD(NicRxAsix, arg) { NUTDEVICE *dev; IFNET *ifn; NICINFO *ni; NETBUF *nb; /* Hack! Unfortunately no parameter passing with ARM */ dev = &devAx88796; ifn = (IFNET *) dev->dev_icb; ni = (NICINFO *) dev->dev_dcb; /* * This is a temporary hack. Due to a change in initialization, * we may not have got a MAC address yet. Wait until a valid one * has been set. */ while (!ETHER_IS_UNICAST(ifn->if_mac)) { NutSleep(10); } NutEnterCritical(); NicStart(ifn->if_mac); NutExitCritical(); /* Run at high priority. */ //printf("Increase prio\n"); NutThreadSetPriority(9); while (1) { //printf("-------- Wait rx\n"); NutEventWait(&ni->ni_rx_rdy, 0); //printf("-------- Got rx\n"); /* * Fetch all packets from the NIC's internal * buffer and pass them to the registered handler. */ do { nb = NicGetPacket(); /* The sanity check may fail because the controller is too busy. restart the NIC. */ if (0) { //HK if ((u_short)nb == 0xFFFF) { NicStart(ifn->if_mac); // ni->ni_rx_size_errors++; } else if (nb) { ni->ni_rx_packets++; (*ifn->if_recv) (dev, nb); } } while (nb); } } //============================================================================== /* * NIC interrupt entry. */ static void NicInterrupt(void *arg) { uint8_t isr; volatile uint8_t *base = (uint8_t *) (((NUTDEVICE *) arg)->dev_base); NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb; ni->ni_interrupts++; isr = Asix_Read(PG0_ISR); Delay16Cycles(); Asix_Write(PG0_ISR, isr); //if(isr) // printf("%02X\n", isr); //else // return; /* * Recover from receive buffer overflow. This may take some * time, so we enable global interrupts but keep NIC * interrupts disabled. */ if (isr & ISR_OVW) { ni->ni_rx_pending++; if (NicOverflow(base)) ni->ni_tx_bsy++; else { NutEventPostAsync(&ni->ni_tx_rdy); } ni->ni_overruns++; } else { /* * If this is a transmit interrupt, then a packet has been sent. * So we can clear the transmitter busy flag and wake up the * transmitter thread. */ if (isr & (ISR_PTX | ISR_TXE)) { ni->ni_tx_bsy = 0; } /* * If this is a receive interrupt, then wake up the receiver * thread. */ if (isr & ISR_PRX) { ni->ni_rx_pending++; //printf("Post %lX\n", (uint32_t) ni->ni_rx_rdy); NutEventPostFromIrq(&ni->ni_rx_rdy); } /* Rx error. */ if (isr & ISR_RXE) { if (Asix_Read(PG0_RSR) & RSR_FAE) ni->ni_rx_frame_errors++; if (Asix_Read(PG0_RSR) & RSR_CR) ni->ni_rx_crc_errors++; if (Asix_Read(PG0_RSR) & RSR_MPA) ni->ni_rx_missed_errors++; } } } void NicInterruptEntry(void) NUT_NAKED_FUNC; void NicInterruptEntry(void) { IRQ_ENTRY(); //putchar('i'); NicInterrupt(&devAx88796); IRQ_EXIT(); } //============================================================================== /*! * \brief Send Ethernet packet. * * \param dev Identifies the device to use. * \param nb Network buffer structure containing the packet to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc(). * * \return 0 on success, -1 in case of any errors. */ int AsixOutput(NUTDEVICE * dev, NETBUF * nb) { int rc = -1; NICINFO *ni = (NICINFO *) dev->dev_dcb; if (NicPutPacket(nb) == 0) { ni->ni_tx_packets++; rc = 0; } return rc; } //============================================================================== /*! * \brief Initialize Ethernet hardware. * * Resets the Asix Asix_L Ethernet controller, initializes all required * hardware registers and starts a background thread for incoming * Ethernet traffic. * * Applications should do not directly call this function. It is * automatically executed during during device registration by * NutRegisterDevice(). * * If the network configuration hasn't been set by the application * before registering the specified device, this function will * call NutNetLoadConfig() to get the MAC address. * * \param dev Identifies the device to initialize. */ int AsixInit(NUTDEVICE * dev) { //printf("Set confnet\n"); confnet.cd_size = sizeof(CONFNET); strcpy(confnet.cd_name, "eth0"); memset(confnet.cdn_mac, 0xFF, 6); /* Disable NIC interrupt and clear NICINFO structure. */ //printf("Clear nicinfo\n"); memset(dev->dev_dcb, 0, sizeof(NICINFO)); /* * Start the receiver thread. */ //printf("Create rxi5(%lX)\n", (u_long) dev); NutThreadCreate("rxi5", NicRxAsix, dev, 1024); outr(PIO_PDR, _BV(9)); /* Register interrupt handler and enable interrupts. */ /* Disable timer 0 interrupts. */ outr(AIC_IDCR, _BV(IRQ0_ID)); /* Set the TC0 IRQ handler address */ outr(AIC_SVR(IRQ0_ID), (unsigned int) NicInterruptEntry); /* Set the trigg and priority for timer 0 interrupt */ /* Level 7 is highest, level 0 lowest. */ outr(AIC_SMR(IRQ0_ID), (AIC_SRCTYPE_EXT_NEGATIVE_EDGE | 5)); /* Clear timer 0 interrupt */ outr(AIC_ICCR, _BV(IRQ0_ID)); /* Enable timer 0 interrupts */ outr(AIC_IECR, _BV(IRQ0_ID)); //printf("======== AsixcInit done ========\n"); return 0; } /*@}*/