| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033 |
- /*
- * Copyright (C) 2001-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/
- *
- */
- /*!
- * \file arch/avr/dev/nicrtl.c
- * \brief AVR network driver for RTL8019AS.
- *
- * \verbatim
- * $Id: nicrtl.c 4711 2012-10-07 14:38:49Z haraldkipp $
- * \endverbatim
- */
- #include <cfg/os.h>
- #include <cfg/arch/avr.h>
- #include <cfg/dev.h>
- #include <string.h>
- #include <sys/atom.h>
- #include <sys/heap.h>
- #include <sys/thread.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/confnet.h>
- #include <net/ether.h>
- #include <net/if_var.h>
- #include <dev/irqreg.h>
- #include <dev/nicrtl.h>
- #include "rtlregs.h"
- #ifdef NUTDEBUG
- #include <sys/osdebug.h>
- #include <net/netdebug.h>
- #endif
- #ifndef NUT_THREAD_NICRXSTACK
- #define NUT_THREAD_NICRXSTACK 256
- #endif
- /*
- * Determine ports, which had not been explicitely configured.
- */
- #if (RTL_RESET_AVRPORT == AVRPORTB)
- #define RTL_RESET_PORT PORTB
- #define RTL_RESET_DDR DDRB
- #elif (RTL_RESET_AVRPORT == AVRPORTD)
- #define RTL_RESET_PORT PORTD
- #define RTL_RESET_DDR DDRD
- #elif (RTL_RESET_AVRPORT == AVRPORTE)
- #define RTL_RESET_PORT PORTE
- #define RTL_RESET_DDR DDRE
- #elif (RTL_RESET_AVRPORT == AVRPORTF)
- #define RTL_RESET_PORT PORTF
- #define RTL_RESET_DDR DDRF
- #endif /* RTL_RESET_AVRPORT */
- /*!
- * \brief Interrupt used.
- */
- #if (RTL_SIGNAL_IRQ == INT0)
- #define RTL_SIGNAL sig_INTERRUPT0
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRA, ISC00); sbi(EICRA, ISC01)
- #endif
- #elif (RTL_SIGNAL_IRQ == INT1)
- #define RTL_SIGNAL sig_INTERRUPT1
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRA, ISC10); sbi(EICRA, ISC11)
- #endif
- #elif (RTL_SIGNAL_IRQ == INT2)
- #define RTL_SIGNAL sig_INTERRUPT2
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRA, ISC20); sbi(EICRA, ISC21)
- #endif
- #elif (RTL_SIGNAL_IRQ == INT3)
- #define RTL_SIGNAL sig_INTERRUPT3
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRA, ISC30); sbi(EICRA, ISC31)
- #endif
- #elif (RTL_SIGNAL_IRQ == INT4)
- #define RTL_SIGNAL sig_INTERRUPT4
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRB, ISC40); sbi(EICRB, ISC41)
- #endif
- #elif (RTL_SIGNAL_IRQ == INT6)
- #define RTL_SIGNAL sig_INTERRUPT6
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRB, ISC60); sbi(EICRB, ISC61)
- #endif
- #elif (RTL_SIGNAL_IRQ == INT7)
- #define RTL_SIGNAL sig_INTERRUPT7
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRB, ISC70); sbi(EICRB, ISC71)
- #endif
- #else
- #define RTL_SIGNAL sig_INTERRUPT5
- #ifdef __AVR_ENHANCED__
- #define RTL_RISING_EDGE_MODE() sbi(EICRB, ISC50); sbi(EICRB, ISC51)
- #endif
- #endif /* RTL_SIGNAL_IRQ */
- /*!
- * \brief Size of a single ring buffer page.
- */
- #define NIC_PAGE_SIZE 0x100
- /*!
- * \brief First ring buffer page address.
- */
- #define NIC_START_PAGE 0x40
- /*!
- * \brief Last ring buffer page address plus 1.
- */
- #define NIC_STOP_PAGE 0x60
- /*!
- * \brief Number of pages in a single transmit buffer.
- *
- * This should be at least the MTU size.
- */
- #define NIC_TX_PAGES 6
- /*!
- * \brief Number of transmit buffers.
- *
- * The initial value had been 2. The idea was to use two alternating
- * buffers. However, this had never been implemented and we took over
- * Bengt Florin's change, defining 1 transmit buffer only and give
- * more buffer space to the receiver.
- *
- * The controller memory layout is now
- *
- * - 0x4000..0x45ff 1.5K bytes transmit buffer
- * - 0x4600..0x5fff 6.5K bytes receive buffer
- *
- * The routines are still not using the buffers in an optimal way,
- * as transmission is limited to 1 packet at a time. In fact several
- * smaller packets would fit into the 1.5 kByte buffer. On the other
- * hand, filling the buffer with more than one transmission packet
- * may result in other bad effects, like pulling the IORDY line more
- * often.
- */
- #define NIC_TX_BUFFERS 1
- #define NIC_FIRST_TX_PAGE NIC_START_PAGE
- #define NIC_FIRST_RX_PAGE (NIC_FIRST_TX_PAGE + NIC_TX_PAGES * NIC_TX_BUFFERS)
- #define NIC_CR_PAGE0 (0)
- #define NIC_CR_PAGE1 (NIC_CR_PS0)
- #define NIC_CR_PAGE2 (NIC_CR_PS1)
- #define NIC_CR_PAGE3 (NIC_CR_PS1 | NIC_CR_PS0)
- /*
- * 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.
- */
- static INLINE void Delay16Cycles(void)
- {
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- _NOP();
- }
- /*!
- * \addtogroup xgNicRtl
- */
- /*@{*/
- /*!
- * Realtek 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 */
- };
- #define NICINB(reg) (*((volatile uint8_t *)RTL_BASE_ADDR + reg))
- #define NICOUTB(reg, val) (*((volatile uint8_t *)RTL_BASE_ADDR + reg) = val)
- /*!
- * \brief Reset the Ethernet controller.
- *
- */
- static int NicReset(void)
- {
- uint8_t i;
- uint8_t j;
- /*
- * Toggle the hardware reset line. Since Ethernut version 1.3 the
- * hardware reset pin of the nic is no longer connected to bit 4
- * on port E, but wired to the board reset line.
- */
- #ifdef RTL_RESET_BIT
- sbi(RTL_RESET_DDR, RTL_RESET_BIT);
- sbi(RTL_RESET_PORT, RTL_RESET_BIT);
- NutDelay(WAIT100);
- cbi(RTL_RESET_PORT, RTL_RESET_BIT);
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- #endif
- /*
- * Do the software reset by reading from the reset register followed
- * by writing to the reset register. Wait until the controller enters
- * the reset state.
- */
- for (j = 0; j < 20; j++) {
- i = NICINB(NIC_RESET);
- NutDelay(WAIT5);
- NICOUTB(NIC_RESET, i);
- for (i = 0; i < 20; i++) {
- NutDelay(WAIT50);
- /*
- * We got the reset bit. However, Ethernut 1.1 may
- * still fail because the NIC hasn't got it's hardware
- * reset and the data lines remain in tristate. So we
- * read noise instead of the register. To solve this
- * problem, we will verify the NIC's id.
- */
- if ((NICINB(NIC_PG0_ISR) & NIC_ISR_RST) != 0 && /* */
- NICINB(NIC_PG0_RBCR0) == 0x50 && /* */
- NICINB(NIC_PG0_RBCR1) == 0x70)
- return 0;
- }
- }
- return -1;
- }
- /*
- * Fires up the network interface. NIC interrupts
- * should have been disabled when calling this
- * function.
- */
- static int NicStart(const uint8_t * mac)
- {
- uint8_t i;
- if (NicReset()) {
- return -1;
- }
- /*
- * Mask all interrupts and clear any interrupt status flag to set the
- * INT pin back to low.
- */
- NICOUTB(NIC_PG0_IMR, 0);
- NICOUTB(NIC_PG0_ISR, 0xff);
- /*
- * During reset the nic loaded its initial configuration from an
- * external eeprom. On the ethernut board we do not have any
- * configuration eeprom, but simply tied the eeprom data line to
- * high level. So we have to clear some bits in the configuration
- * register. Switch to register page 3.
- */
- NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1);
- /*
- * The nic configuration registers are write protected unless both
- * EEM bits are set to 1.
- */
- NICOUTB(NIC_PG3_EECR, NIC_EECR_EEM0 | NIC_EECR_EEM1);
- /*
- * Network media had been set to 10Base2 by the virtual EEPROM and
- * will be set now to auto detect. This will initiate a link test.
- * We don't force 10BaseT, because this would disable the link test.
- */
- NICOUTB(NIC_PG3_CONFIG2, NIC_CONFIG2_BSELB);
- /*
- * Disable sleep and power down.
- *
- * The virtual EEPROM (resistor tight to VCC) will set all bits of
- * CONFIG3 to 1. Unfortunately we are not able to modify the full
- * duplex bit. The only solution is to use a real EEPROM or emulate
- * one.
- */
- NICOUTB(NIC_PG3_CONFIG3, NIC_CONFIG3_LEDS1 | NIC_CONFIG3_LEDS1);
- /*
- * Reenable write protection of the nic configuration registers
- * and wait for link test to complete.
- */
- NICOUTB(NIC_PG3_EECR, 0);
- NutDelay(255);
- /*
- * Switch to register page 0 and set data configuration register
- * to byte-wide DMA transfers, normal operation (no loopback),
- * send command not executed and 8 byte fifo threshold.
- */
- NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2);
- NICOUTB(NIC_PG0_DCR, NIC_DCR_LS | NIC_DCR_FT1);
- /*
- * Clear remote dma byte count register.
- */
- NICOUTB(NIC_PG0_RBCR0, 0);
- NICOUTB(NIC_PG0_RBCR1, 0);
- /*
- * Temporarily set receiver to monitor mode and transmitter to
- * internal loopback mode. Incoming packets will not be stored
- * in the nic ring buffer and no data will be send to the network.
- */
- NICOUTB(NIC_PG0_RCR, NIC_RCR_MON);
- NICOUTB(NIC_PG0_TCR, NIC_TCR_LB0);
- /*
- * Configure the nic's ring buffer page layout.
- * NIC_PG0_BNRY: Last page read.
- * NIC_PG0_PSTART: First page of receiver buffer.
- * NIC_PG0_PSTOP: Last page of receiver buffer.
- */
- NICOUTB(NIC_PG0_TPSR, NIC_FIRST_TX_PAGE);
- NICOUTB(NIC_PG0_BNRY, NIC_STOP_PAGE - 1);
- NICOUTB(NIC_PG0_PSTART, NIC_FIRST_RX_PAGE);
- NICOUTB(NIC_PG0_PSTOP, NIC_STOP_PAGE);
- /*
- * Once again clear interrupt status register.
- */
- NICOUTB(NIC_PG0_ISR, 0xff);
- /*
- * Switch to register page 1 and copy our MAC address into the nic.
- * We are still in stop mode.
- */
- NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0);
- for (i = 0; i < 6; i++)
- NICOUTB(NIC_PG1_PAR0 + i, mac[i]);
- /*
- * Clear multicast filter bits to disable all packets.
- */
- for (i = 0; i < 8; i++)
- NICOUTB(NIC_PG1_MAR0 + i, 0);
- /*
- * Set current page pointer to one page after the boundary pointer.
- */
- NICOUTB(NIC_PG1_CURR, NIC_FIRST_RX_PAGE);
- /*
- * Switch back to register page 0, remaining in stop mode.
- */
- NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2);
- /*
- * Take receiver out of monitor mode and enable it for accepting
- * broadcasts.
- */
- NICOUTB(NIC_PG0_RCR, NIC_RCR_AB);
- /*
- * Clear all interrupt status flags and enable interrupts.
- */
- NICOUTB(NIC_PG0_ISR, 0xff);
- /* Note: transmitter if polled, thus no NIC_IMR_PTXE */
- NICOUTB(NIC_PG0_IMR, NIC_IMR_PRXE | NIC_IMR_RXEE | NIC_IMR_TXEE | NIC_IMR_OVWE);
- /*
- * Fire up the nic by clearing the stop bit and setting the start bit.
- * To activate the local receive dma we must also take the nic out of
- * the local loopback mode.
- */
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2);
- NICOUTB(NIC_PG0_TCR, 0);
- NutDelay(255);
- return 0;
- }
- /*!
- * Complete remote DMA.
- */
- static void NicCompleteDma(void)
- {
- uint8_t i;
- /*
- * Complete remote dma.
- */
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2);
- /*
- * Check that we have a DMA complete flag.
- */
- for (i = 0; i <= 20; i++)
- if (NICINB(NIC_PG0_ISR) & NIC_ISR_RDC)
- break;
- /*
- * Reset remote dma complete flag.
- */
- NICOUTB(NIC_PG0_ISR, NIC_ISR_RDC);
- }
- /*
- * Write data block to the NIC.
- */
- static void NicWrite(uint8_t * buf, uint16_t len)
- {
- register uint16_t l = len - 1;
- register uint8_t ih = (uint16_t) l >> 8;
- register uint8_t il = (uint8_t) l;
- if (!len)
- return;
- do {
- do {
- NICOUTB(NIC_IOPORT, *buf++);
- } while (il-- != 0);
- } while (ih-- != 0);
- }
- /*
- * Read data block from the NIC.
- */
- static void NicRead(uint8_t * buf, uint16_t len)
- {
- register uint16_t l = len - 1;
- register uint8_t ih = (uint16_t) l >> 8;
- register uint8_t il = (uint8_t) l;
- if (!len)
- return;
- do {
- do {
- *buf++ = NICINB(NIC_IOPORT);
- } while (il-- != 0);
- } while (ih-- != 0);
- }
- /*!
- * \brief Load a packet into the nic's transmit ring buffer.
- *
- *
- * \param base NIC hardware base address.
- * \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. Errors
- * will automatically release the network buffer
- * structure.
- */
- static int NicPutPacket(NETBUF * nb)
- {
- uint16_t sz;
- uint16_t i;
- uint8_t padding = 0;
- /*
- * Calculate the number of bytes to be send. Do not
- * send packets larger than 1514 bytes.
- *
- * The previous version was wrong by specifying a maximum
- * of 1518, because it didn't take the CRC into account,
- * which is generated by the hardware and automatically
- * appended. Thanks to Bengt Florin, who discovered this.
- */
- sz = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz;
- if (sz > 1514)
- return -1;
- /*
- * The controller will not append pad bytes,
- * so we have to do this.
- */
- if (sz < 60) {
- padding = (uint8_t) (60 - sz);
- sz = 60;
- }
- /*
- * 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.
- */
- while (NICINB(NIC_CR) & NIC_CR_TXP)
- NutThreadYield();
- /* we don't want to be interrupted by NIC owerflow */
- cbi(EIMSK, RTL_SIGNAL_IRQ);
- /*
- * Set remote dma byte count
- * and start address.
- */
- NICOUTB(NIC_PG0_RBCR0, sz);
- NICOUTB(NIC_PG0_RBCR1, sz >> 8);
- NICOUTB(NIC_PG0_RSAR0, 0);
- NICOUTB(NIC_PG0_RSAR1, NIC_FIRST_TX_PAGE);
- /*
- * Peform the write.
- */
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD1);
- /*
- * 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);
- /*
- * Add pad bytes.
- */
- for (i = 0; i < padding; i++)
- NICOUTB(NIC_IOPORT, 0);
- /*
- * Complete remote dma.
- */
- NicCompleteDma();
- /*
- * Number of bytes to be transmitted.
- */
- NICOUTB(NIC_PG0_TBCR0, (sz & 0xff));
- NICOUTB(NIC_PG0_TBCR1, ((sz >> 8) & 0xff));
- /*
- * First page of packet to be transmitted.
- */
- NICOUTB(NIC_PG0_TPSR, NIC_FIRST_TX_PAGE);
- /*
- * Start transmission.
- */
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2);
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- return 0;
- }
- /*!
- * \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 *buf;
- uint8_t nextpg;
- uint8_t bnry;
- uint8_t curr;
- uint16_t i;
- uint8_t drop = 0;
- /* we don't want to be interrupted by NIC owerflow */
- cbi(EIMSK, RTL_SIGNAL_IRQ);
- /*
- * Get the current page pointer. It points to the page where the NIC
- * will start saving the next incoming packet.
- */
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2 | NIC_CR_PS0);
- Delay16Cycles();
- curr = NICINB(NIC_PG1_CURR);
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2);
- /*
- * 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 = NICINB(NIC_PG0_BNRY) + 1) >= NIC_STOP_PAGE)
- bnry = NIC_FIRST_RX_PAGE;
- if (bnry == curr) {
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- return 0;
- }
- /*
- * Read the NIC specific packet header.
- */
- NICOUTB(NIC_PG0_RBCR0, sizeof(struct nic_pkt_header));
- NICOUTB(NIC_PG0_RBCR1, 0);
- NICOUTB(NIC_PG0_RSAR0, 0);
- NICOUTB(NIC_PG0_RSAR1, bnry);
- buf = (uint8_t *) & hdr;
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD0);
- Delay16Cycles();
- for (i = 0; i < sizeof(struct nic_pkt_header); i++)
- *buf++ = NICINB(NIC_IOPORT);
- NicCompleteDma();
- /*
- * 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)) {
- drop = 1;
- }
- /*
- * Calculate the page of the next packet. If it differs from the
- * pointer in the packet header, we return with errorcode.
- */
- nextpg = bnry + (hdr.ph_size >> 8) + ((hdr.ph_size & 0xFF) != 0);
- if (nextpg >= NIC_STOP_PAGE) {
- nextpg -= NIC_STOP_PAGE;
- nextpg += NIC_FIRST_RX_PAGE;
- }
- if (nextpg != hdr.ph_nextpg) {
- uint8_t nextpg1 = nextpg + 1;
- if (nextpg1 >= NIC_STOP_PAGE) {
- nextpg1 -= NIC_STOP_PAGE;
- nextpg1 += NIC_FIRST_RX_PAGE;
- }
- if (nextpg1 != hdr.ph_nextpg) {
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- return (NETBUF *) 0xFFFF;
- }
- nextpg = nextpg1;
- }
- /*
- * Check packet status. It should have set bit 0, but
- * even without this bit packets seem to be OK.
- */
- if (!drop && ((hdr.ph_status & 0x0E) == 0)) {
- /*
- * Allocate a NETBUF.
- * Omit the fcs.
- */
- count = hdr.ph_size - 4;
- if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, count))) {
- /*
- * Set remote dma byte count and
- * start address. Don't read the
- * header again.
- */
- NICOUTB(NIC_PG0_RBCR0, count);
- NICOUTB(NIC_PG0_RBCR1, count >> 8);
- NICOUTB(NIC_PG0_RSAR0, sizeof(struct nic_pkt_header));
- NICOUTB(NIC_PG0_RSAR1, bnry);
- /*
- * Perform the read.
- */
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_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 < NIC_FIRST_RX_PAGE)
- nextpg = NIC_STOP_PAGE - 1;
- NICOUTB(NIC_PG0_BNRY, nextpg);
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- 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(void)
- {
- uint8_t cr;
- uint8_t resend = 0;
- uint8_t 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 (NICINB(NIC_CR) & NIC_CR_TXP);
- cr = NICINB(NIC_CR);
- /*
- * Get the current page pointer. It points to the page where the NIC
- * will start saving the next incoming packet.
- */
- NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0);
- curr = NICINB(NIC_PG1_CURR);
- NICOUTB(NIC_CR, NIC_CR_STP | NIC_CR_RD2);
- /* Clear remote byte count register. */
- NICOUTB(NIC_PG0_RBCR0, 0);
- NICOUTB(NIC_PG0_RBCR1, 0);
- /* Check for any incomplete transmission. */
- if ((cr & NIC_CR_TXP) && ((NICINB(NIC_PG0_ISR) & (NIC_ISR_PTX | NIC_ISR_TXE)) == 0)) {
- resend = 1;
- }
- /* Enter loopback mode and restart the NIC. */
- NICOUTB(NIC_PG0_TCR, NIC_TCR_LB0);
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_RD2);
- /*
- * Discard all packets from the receiver buffer. Set boundary
- * register to the last page we read.
- */
- if (--curr < NIC_FIRST_RX_PAGE) {
- curr = NIC_STOP_PAGE - 1;
- }
- NICOUTB(NIC_PG0_BNRY, curr);
- /* Switch from loopback to normal mode mode. */
- NICOUTB(NIC_PG0_TCR, 0);
- /* Re-invoke any interrupted transmission. */
- if (resend) {
- NICOUTB(NIC_CR, NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2);
- }
- /* Finally clear the overflow flag */
- NICOUTB(NIC_PG0_ISR, NIC_ISR_OVW);
- return resend;
- }
- /*
- * \brief NIC interrupt entry.
- */
- static void NicInterrupt(void *arg)
- {
- uint8_t isr;
- NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb;
- ni->ni_interrupts++;
- #ifdef RTL_IRQ_RISING_EDGE
- do
- {
- #endif
- isr = NICINB(NIC_PG0_ISR);
- NICOUTB(NIC_PG0_ISR, isr);
- /*
- * Recover from receive buffer overflow. This may take some
- * time, so we enable global interrupts but keep NIC
- * interrupts disabled.
- */
- if (isr & NIC_ISR_OVW) {
- /* The AVR platform uses a dedicated interrupt stack, which
- * forbids interrupt nesting. */
- #if !defined(__AVR__)
- cbi(EIMSK, RTL_SIGNAL_IRQ);
- sei();
- #endif
- NicOverflow();
- #if !defined(__AVR__)
- cli();
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- #endif
- ni->ni_rx_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 & NIC_ISR_TXE)
- ni->ni_tx_errors++;
- /*
- * If this is a receive interrupt, then wake up the receiver
- * thread.
- */
- if (isr & NIC_ISR_PRX)
- NutEventPostFromIrq(&ni->ni_rx_rdy);
- if (isr & NIC_ISR_RXE) {
- ni->ni_rx_frame_errors += NICINB(NIC_PG0_CNTR0);
- ni->ni_rx_crc_errors += NICINB(NIC_PG0_CNTR1);
- ni->ni_rx_missed_errors += NICINB(NIC_PG0_CNTR2);
- }
- }
- #ifdef RTL_IRQ_RISING_EDGE
- /* Check that all unmasked interrupts are cleared before we
- * leave the ISR to assert the INT line goes back to low
- * and a new interrupt edge will be generated for following
- * interrupts.
- */
- }
- while (bit_is_set(PINE, RTL_SIGNAL_IRQ));
- #endif
- }
- /*! \fn NicRx(void *arg)
- * \brief NIC receiver thread.
- *
- *
- * It runs with high priority.
- */
- THREAD(NicRx, arg)
- {
- NUTDEVICE *dev;
- IFNET *ifn;
- NICINFO *ni;
- NETBUF *nb;
- dev = arg;
- ifn = (IFNET *) dev->dev_icb;
- ni = (NICINFO *) dev->dev_dcb;
- NutThreadSetPriority(9);
- /*
- * This is a temporary hack. Due to a change in initialization,
- * we may not have got a MAC address yet. Wait until one has been
- * set.
- */
- if ((ifn->if_mac[0] & ifn->if_mac[1] & ifn->if_mac[2]) == 0xFF) {
- while ((ifn->if_mac[0] & ifn->if_mac[1] & ifn->if_mac[2]) == 0xFF)
- NutSleep(125);
- cbi(EIMSK, RTL_SIGNAL_IRQ);
- NicStart(ifn->if_mac);
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- }
- while (1) {
- NutEventWait(&ni->ni_rx_rdy, 0);
- /*
- * 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 ((uint16_t) 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);
- }
- }
- /*!
- * \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 NicOutput(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 RTL8019AS 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(). Note, that base address and interrupt number
- * passed to NutRegisterDevice() are silently ignored by this driver
- * for performance reasons. These values can be changed only by
- * using the Nut/OS Configurator to rebuild the system.
- *
- * 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 NicInit(NUTDEVICE * dev)
- {
- IFNET *ifn;
- NICINFO *ni;
- /*
- * We need to know our MAC address. If no configuration is
- * available, load it now.
- */
- if (confnet.cd_size == 0)
- NutNetLoadConfig(dev->dev_name);
- ifn = dev->dev_icb;
- memcpy(ifn->if_mac, confnet.cdn_mac, 6);
- ni = (NICINFO *) dev->dev_dcb;
- memset(ni, 0, sizeof(NICINFO));
- /*
- * Start the receiver thread.
- */
- NutThreadCreate("rxi5", NicRx, dev,
- (NUT_THREAD_NICRXSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
- NutSleep(WAIT500);
- /*
- * Register interrupt handler and enable interrupts.
- */
- if (NutRegisterIrqHandler(&RTL_SIGNAL, NicInterrupt, dev))
- return -1;
- cbi(EIMSK, RTL_SIGNAL_IRQ);
- #ifdef RTL_IRQ_RISING_EDGE
- /* Support of rising edge interrupts for HW w/o inverter gate */
- RTL_RISING_EDGE_MODE();
- #endif
- if (ifn->if_mac[0] | ifn->if_mac[1] | ifn->if_mac[2])
- if (NicStart(ifn->if_mac))
- return -1;
- sbi(EIMSK, RTL_SIGNAL_IRQ);
- return 0;
- }
- /*@}*/
|