/* * Copyright (C) 2003-2006 by egnite Software GmbH. * Copyright (C) 2008 by egnite 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/ * */ /* * $Id: lan91.c 5472 2013-12-06 00:16:28Z olereinhardt $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NUTDEBUG #include #endif /* * Determine interrupt settings. */ #if (LAN91_SIGNAL_IRQ == INT0) #define LAN91_SIGNAL sig_INTERRUPT0 #elif (LAN91_SIGNAL_IRQ == INT1) #define LAN91_SIGNAL sig_INTERRUPT1 #elif (LAN91_SIGNAL_IRQ == INT2) #define LAN91_SIGNAL sig_INTERRUPT2 #elif (LAN91_SIGNAL_IRQ == INT3) #define LAN91_SIGNAL sig_INTERRUPT3 #elif (LAN91_SIGNAL_IRQ == INT4) #define LAN91_SIGNAL sig_INTERRUPT4 #elif (LAN91_SIGNAL_IRQ == INT5) #define LAN91_SIGNAL sig_INTERRUPT5 #elif (LAN91_SIGNAL_IRQ == INT6) #define LAN91_SIGNAL sig_INTERRUPT6 #elif (LAN91_SIGNAL_IRQ == INT7) #define LAN91_SIGNAL sig_INTERRUPT7 #endif /* * Determine poll timers. */ #if !defined(LAN91_RX_POLLTIME) #if defined(LAN91_SIGNAL) #define LAN91_RX_POLLTIME 2000 #else #define LAN91_RX_POLLTIME 200 #endif #endif #if !defined(LAN91_TX_POLLTIME) #if defined(LAN91_SIGNAL) #define LAN91_TX_POLLTIME 5000 #else #define LAN91_TX_POLLTIME 200 #endif #endif /*! * \addtogroup xgNicLan91 */ /*@{*/ #define nic_outlb(addr, val) (*(volatile uint8_t *)(addr) = (val)) #define nic_outhb(addr, val) (*(volatile uint8_t *)((addr) + 1) = (val)) #define nic_outwx(addr, val) (*(volatile uint16_t *)(addr) = (val)) #define nic_outw(addr, val) { \ *(volatile uint8_t *)(addr) = (uint8_t)(val); \ *((volatile uint8_t *)(addr) + 1) = (uint8_t)((val) >> 8); \ } #define nic_inlb(addr) (*(volatile uint8_t *)(addr)) #define nic_inhb(addr) (*(volatile uint8_t *)((addr) + 1)) #define nic_inw(addr) (*(volatile uint16_t *)(addr)) #define nic_bs(bank) nic_outlb(LAN91_BSR, bank) /*! * \struct _NICINFO * \brief Network interface controller information structure. */ struct _NICINFO { HANDLE volatile ni_rx_rdy; /*!< Receiver event queue. */ uint16_t ni_tx_cnt; /*!< Number of bytes in transmission queue. */ #ifdef NUT_PERFMON uint32_t ni_rx_packets; /*!< Number of packets received. */ uint32_t ni_tx_packets; /*!< Number of packets sent. */ uint32_t ni_interrupts; /*!< Number of interrupts. */ uint32_t ni_overruns; /*!< Number of packet overruns. */ uint32_t ni_rx_frame_errors; /*!< Number of frame errors. */ uint32_t ni_rx_crc_errors; /*!< Number of CRC errors. */ uint32_t ni_rx_missed_errors; /*!< Number of missed packets. */ #endif }; /*! * \brief Network interface controller information type. */ typedef struct _NICINFO NICINFO; static HANDLE mutex; static HANDLE maq; /*! * \brief Select specified PHY register for reading or writing. * * \note NIC interrupts must have been disabled before calling this routine. * * \param reg PHY register number. * \param we Indicates type of access, 1 for write and 0 for read. * * \return Contents of the PHY interface rgister. */ static uint8_t NicPhyRegSelect(uint8_t reg, uint8_t we) { uint8_t rs; uint8_t msk; uint_fast8_t i; nic_bs(3); rs = (nic_inlb(LAN91_MGMT) & ~(LAN91_MGMT_MCLK | LAN91_MGMT_MDO)) | LAN91_MGMT_MDOE; /* Send idle pattern. */ for (i = 0; i < 33; i++) { nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); } /* Send start sequence. */ nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); /* Write or read mode. */ if (we) { nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); } else { nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); } /* Send PHY address. Zero is used for the internal PHY. */ for (i = 0; i < 5; i++) { nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); } /* Send PHY register number. */ for (msk = 0x10; msk; msk >>= 1) { if (reg & msk) { nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); } else { nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); } } nic_outlb(LAN91_MGMT, rs); return rs; } /*! * \brief Read contents of PHY register. * * \note NIC interrupts must have been disabled before calling this routine. * * \param reg PHY register number. * * \return Contents of the specified register. */ static uint16_t NicPhyRead(uint8_t reg) { uint16_t rc = 0; uint8_t rs; uint_fast8_t i; /* Select register for reading. */ rs = NicPhyRegSelect(reg, 0); /* Switch data direction. */ rs &= ~LAN91_MGMT_MDOE; nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); /* Clock data in. */ for (i = 0; i < 16; i++) { nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); rc <<= 1; rc |= (nic_inlb(LAN91_MGMT) & LAN91_MGMT_MDI) != 0; } /* This will set the clock line to low. */ nic_outlb(LAN91_MGMT, rs); 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. */ static void NicPhyWrite(uint8_t reg, uint16_t val) { uint16_t msk; uint8_t rs; /* Select register for writing. */ rs = NicPhyRegSelect(reg, 1); /* Switch data direction dummy. */ nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); /* Clock data out. */ for (msk = 0x8000; msk; msk >>= 1) { if (val & msk) { nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MDO | LAN91_MGMT_MCLK); } else { nic_outlb(LAN91_MGMT, rs); nic_outlb(LAN91_MGMT, rs | LAN91_MGMT_MCLK); } } /* Set clock line low and output line int z-state. */ nic_outlb(LAN91_MGMT, rs & ~LAN91_MGMT_MDOE); } /*! * \brief Configure the internal PHY. * * Reset the PHY and initiate auto-negotiation. */ static int NicPhyConfig(void) { uint16_t phy_sr; uint16_t phy_to; uint16_t mode; /* * Reset the PHY and wait until this self clearing bit * becomes zero. We sleep 63 ms before each poll and * give up after 3 retries. */ NicPhyWrite(LAN91_PHYCR, LAN91_PHYCR_RST); for (phy_to = 0;; phy_to++) { NutSleep(63); if ((NicPhyRead(LAN91_PHYCR) & LAN91_PHYCR_RST) == 0) break; if (phy_to > 3) return -1; } /* Enable PHY interrupts. */ NicPhyWrite(LAN91_PHYMSK, LAN91_PHYMSK_MLOSSSYN | LAN91_PHYMSK_MCWRD | LAN91_PHYMSK_MSSD | LAN91_PHYMSK_MESD | LAN91_PHYMSK_MRPOL | LAN91_PHYMSK_MJAB | LAN91_PHYMSK_MSPDDT | LAN91_PHYMSK_MDPLDT); /* Set RPC register. */ mode = LAN91_RPCR_ANEG | LAN91_RPCR_LEDA_PAT | LAN91_RPCR_LEDB_PAT; nic_bs(0); nic_outw(LAN91_RPCR, mode); #ifdef LAN91_FIXED /* Disable link. */ phy_sr = NicPhyRead(LAN91_PHYCFR1); NicPhyWrite(LAN91_PHYCFR1, phy_sr | 0x8000); NutSleep(63); /* Set fixed capabilities. */ NicPhyWrite(LAN91_PHYCR, LAN91_FIXED); nic_bs(0); nic_outw(LAN91_RPCR, mode); /* Enable link. */ phy_sr = NicPhyRead(LAN91_PHYCFR1); NicPhyWrite(LAN91_PHYCFR1, phy_sr & ~0x8000); phy_sr = NicPhyRead(LAN91_PHYCFR1); #else /* * Advertise our capabilities, initiate auto negotiation * and wait until this has been completed. */ NicPhyWrite(LAN91_PHYANAD, LAN91_PHYANAD_TX_FDX | LAN91_PHYANAD_TX_HDX | LAN91_PHYANAD_10FDX | LAN91_PHYANAD_10_HDX | LAN91_PHYANAD_CSMA); NutSleep(63); for (phy_to = 0, phy_sr = 0;; phy_to++) { /* Give up after 10 seconds. */ if (phy_to >= 1024) return -1; /* Restart auto negotiation every 4 seconds or on failures. */ if ((phy_to & 127) == 0 /* || (phy_sr & LAN91_PHYSR_REM_FLT) != 0 */ ) { NicPhyWrite(LAN91_PHYCR, LAN91_PHYCR_ANEG_EN | LAN91_PHYCR_ANEG_RST); NutSleep(63); } /* Check if we are done. */ phy_sr = NicPhyRead(LAN91_PHYSR); if (phy_sr & LAN91_PHYSR_ANEG_ACK) break; NutSleep(63); } #endif return 0; } /*! * \brief Wait until MMU is ready. * * Poll the MMU command register until \ref LAN91_MMUCR_BUSY * is cleared. * * \param tmo Timeout in milliseconds. * * \return 0 on success or -1 on timeout. */ static INLINE int NicMmuWait(uint_fast16_t tmo) { while (tmo--) { if ((nic_inlb(LAN91_MMUCR) & LAN91_MMUCR_BUSY) == 0) break; NutDelay(1); } return tmo ? 0 : -1; } /*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */ static int NicReset(void) { #ifdef LAN91_RESET_BIT GpioPinConfigSet(LAN91_RESET_GPIO_BANK, LAN91_RESET_GPIO_BIT, GPIO_CFG_OUTPUT); GpioPinSet(LAN91_RESET_GPIO_BANK, LAN91_RESET_GPIO_BIT, 1); NutDelay(10); GpioPinSet(LAN91_RESET_GPIO_BANK, LAN91_RESET_GPIO_BIT, 0); NutDelay(100); #endif /* Disable all interrupts. */ nic_outlb(LAN91_MSK, 0); /* MAC and PHY software reset. */ nic_bs(0); nic_outw(LAN91_RCR, LAN91_RCR_SOFT_RST); /* Enable Ethernet protocol handler. */ nic_bs(1); nic_outw(LAN91_CR, LAN91_CR_EPH_EN); NutDelay(10); /* Disable transmit and receive. */ nic_bs(0); nic_outw(LAN91_RCR, 0); nic_outw(LAN91_TCR, 0); /* Enable auto release. */ nic_bs(1); nic_outw(LAN91_CTR, LAN91_CTR_AUTO_RELEASE); /* Reset MMU. */ nic_bs(2); nic_outlb(LAN91_MMUCR, LAN91_MMU_RST); if (NicMmuWait(1000)) return -1; 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) { uint_fast8_t i; if (NicReset()) return -1; /* Enable receiver. */ nic_bs(3); nic_outlb(LAN91_ERCV, 7); nic_bs(0); nic_outw(LAN91_RCR, LAN91_RCR_RXEN); /* Enable transmitter and padding. */ nic_outw(LAN91_TCR, LAN91_TCR_PAD_EN | LAN91_TCR_TXENA); /* Configure the PHY. */ if (NicPhyConfig()) return -1; /* Set MAC address. */ nic_bs(1); for (i = 0; i < 6; i++) nic_outlb(LAN91_IAR + i, mac[i]); /* Enable interrupts. */ nic_bs(2); nic_outlb(LAN91_MSK, LAN91_INT_ERCV | LAN91_INT_RCV | LAN91_INT_RX_OVRN); return 0; } #if defined(LAN91_SIGNAL) /* * NIC interrupt entry. */ static void NicInterrupt(void *arg) { uint8_t isr; uint8_t imr; NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb; #ifdef NUT_PERFMON ni->ni_interrupts++; #endif /* Read the interrupt mask and disable all interrupts. */ nic_bs(2); imr = nic_inlb(LAN91_MSK); nic_outlb(LAN91_MSK, 0); /* Read the interrupt status and acknowledge all interrupts. */ isr = nic_inlb(LAN91_IST); isr &= imr; /* * 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 & LAN91_INT_TX_EMPTY) { nic_outlb(LAN91_ACK, LAN91_INT_TX_EMPTY); imr &= ~LAN91_INT_TX_EMPTY; } /* Transmit error. */ else if (isr & LAN91_INT_TX) { /* re-enable transmit */ nic_bs(0); nic_outw(LAN91_TCR, nic_inlb(LAN91_TCR) | LAN91_TCR_TXENA); nic_bs(2); nic_outlb(LAN91_ACK, LAN91_INT_TX); /* kill the packet */ nic_outlb(LAN91_MMUCR, LAN91_MMU_PKT); } /* * If this is a receive interrupt, then wake up the receiver * thread. */ if (isr & LAN91_INT_RX_OVRN) { nic_outlb(LAN91_ACK, LAN91_INT_RX_OVRN); NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & LAN91_INT_ERCV) { nic_outlb(LAN91_ACK, LAN91_INT_ERCV); NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & LAN91_INT_RCV) { nic_outlb(LAN91_ACK, LAN91_INT_RCV); imr &= ~LAN91_INT_RCV; NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & LAN91_INT_ALLOC) { imr &= ~LAN91_INT_ALLOC; NutEventPostFromIrq(&maq); } nic_outlb(LAN91_MSK, imr); } #endif /* * 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 { nic_outlb(LAN91_DATA, *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++ = nic_inlb(LAN91_DATA); } while (il-- != 0); } while (ih-- != 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; uint16_t fsw; uint16_t fbc; /* Check the fifo empty bit. If it is set, then there is nothing in the receiver fifo. */ nic_bs(2); if (nic_inw(LAN91_FIFO) & 0x8000) { return 0; } /* Inialize pointer register. */ nic_outw(LAN91_PTR, LAN91_PTR_READ | LAN91_PTR_RCV | LAN91_PTR_AUTO_INCR); _NOP(); _NOP(); _NOP(); _NOP(); /* Read status word and byte count. */ fsw = nic_inw(LAN91_DATA); fbc = nic_inw(LAN91_DATA); /* Check for frame errors. */ if (fsw & 0xAC00) { nb = (NETBUF *) 0xFFFF; } /* Check the byte count. */ else if (fbc < 66 || fbc > 1524) { nb = (NETBUF *) 0xFFFF; } else { /* * Allocate a NETBUF. * Hack alert: Rev A chips never set the odd frame indicator. */ fbc -= 3; nb = NutNetBufAlloc(0, NBAF_DATALINK, fbc); /* Perform the read. */ if (nb) NicRead(nb->nb_dl.vp, fbc); } /* Release the packet. */ nic_outlb(LAN91_MMUCR, LAN91_MMU_TOP); return nb; } /*! * \brief Load a packet into the nic's transmit ring buffer. * * Interupts must have been disabled when calling this function. * * \param nb Network buffer structure containing the packet to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc(). This routine will automatically * release the buffer in case of an error. * * \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; uint_fast8_t odd = 0; uint8_t imsk; /* * Calculate the number of bytes to be send. Do not send packets * larger than the Ethernet maximum transfer unit. The MTU * consist of 1500 data bytes plus the 14 byte Ethernet header * plus 4 bytes CRC. We check the data bytes only. */ if ((sz = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz) > ETHERMTU) return -1; /* Disable all interrupts. */ imsk = nic_inlb(LAN91_MSK); nic_outlb(LAN91_MSK, 0); /* Allocate packet buffer space. */ nic_bs(2); nic_outlb(LAN91_MMUCR, LAN91_MMU_ALO); if (NicMmuWait(100)) return -1; /* Enable interrupts including allocation success. */ nic_outlb(LAN91_MSK, imsk | LAN91_INT_ALLOC); /* The MMU needs some time. Use it to calculate the byte count. */ sz += nb->nb_dl.sz; sz += 6; if (sz & 1) { sz++; odd++; } /* Wait for allocation success. */ while ((nic_inlb(LAN91_IST) & LAN91_INT_ALLOC) == 0) { if (NutEventWait(&maq, 125)) { nic_outlb(LAN91_MMUCR, LAN91_MMU_RST); NicMmuWait(1000); nic_outlb(LAN91_MMUCR, LAN91_MMU_ALO); if (NicMmuWait(100) || (nic_inlb(LAN91_IST) & LAN91_INT_ALLOC) == 0) { if (NutEventWait(&maq, 125)) { return -1; } } } } /* Disable interrupts. */ imsk = nic_inlb(LAN91_MSK); nic_outlb(LAN91_MSK, 0); nic_outlb(LAN91_PNR, nic_inhb(LAN91_PNR)); nic_outw(LAN91_PTR, 0x4000); /* Transfer control word. */ nic_outlb(LAN91_DATA, 0); nic_outlb(LAN91_DATA, 0); /* Transfer the byte count. */ nic_outw(LAN91_DATA, sz); /* 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); if (odd) nic_outlb(LAN91_DATA, 0); /* Transfer the control word. */ nic_outw(LAN91_DATA, 0); /* Enqueue packet. */ if (NicMmuWait(100)) return -1; nic_outlb(LAN91_MMUCR, LAN91_MMU_ENQ); /* Enable interrupts. */ imsk |= LAN91_INT_TX | LAN91_INT_TX_EMPTY; nic_outlb(LAN91_MSK, imsk); return 0; } /*! \fn NicRxLanc(void *arg) * \brief NIC receiver thread. * */ THREAD(NicRxLanc, arg) { NUTDEVICE *dev; IFNET *ifn; NICINFO *ni; NETBUF *nb; uint8_t imsk; dev = arg; 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); } /* * Do not continue unless we managed to start the NIC. We are * trapped here if the Ethernet link cannot be established. * This happens, for example, if no Ethernet cable is plugged * in. */ while(NicStart(ifn->if_mac)) { NutSleep(1000); } #ifdef LAN91_SIGNAL NutIrqSetMode(&LAN91_SIGNAL, NUT_IRQMODE_RISINGEDGE); NutIrqEnable(&LAN91_SIGNAL); #endif NutEventPost(&mutex); /* Run at high priority. */ NutThreadSetPriority(9); for (;;) { /* * Wait for the arrival of new packets or * check the receiver every two second. */ NutEventWait(&ni->ni_rx_rdy, LAN91_RX_POLLTIME); /* * Fetch all packets from the NIC's internal * buffer and pass them to the registered handler. */ imsk = nic_inlb(LAN91_MSK); nic_outlb(LAN91_MSK, 0); while ((nb = NicGetPacket()) != 0) { if (nb != (NETBUF *) 0xFFFF) { #ifdef NUT_PERFMON ni->ni_rx_packets++; #endif (*ifn->if_recv) (dev, nb); } } nic_outlb(LAN91_MSK, imsk | LAN91_INT_RCV | LAN91_INT_ERCV); } } /*! * \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. */ static int Lan91Output(NUTDEVICE * dev, NETBUF * nb) { static uint_fast16_t mx_wait = LAN91_TX_POLLTIME; int rc = -1; /* * After initialization we are waiting for a long time to give * the PHY a chance to establish an Ethernet link. */ if (NutEventWait(&mutex, mx_wait) == 0) { if (NicPutPacket(nb) == 0) { #ifdef NUT_PERFMON NICINFO *ni = (NICINFO *) dev->dev_dcb; ni->ni_tx_packets++; #endif rc = 0; /* Ethernet works. Set a long waiting time in case we temporarly lose the link next time. */ mx_wait = LAN91_TX_POLLTIME; } NutEventPost(&mutex); } #if defined(LAN91_SIGNAL) /* * Probably no Ethernet link. Significantly reduce the waiting * time, so following transmission will soon return an error. */ else { mx_wait = 500; } #endif return rc; } /*! * \brief Initialize Ethernet hardware. * * Resets the LAN91 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. */ static int Lan91Init(NUTDEVICE * dev) { /* Disable NIC interrupt and clear NICINFO structure. */ #ifdef LAN91_SIGNAL NutIrqDisable(&LAN91_SIGNAL); #endif memset(dev->dev_dcb, 0, sizeof(NICINFO)); #ifdef LAN91_SIGNAL /* Register interrupt handler and enable interrupts. */ if (NutRegisterIrqHandler(&LAN91_SIGNAL, NicInterrupt, dev)) return -1; #endif /* * Start the receiver thread. */ NutThreadCreate("lan91rx", NicRxLanc, dev, 640); return 0; } static int Lan91IOCtl(NUTDEVICE * dev, int req, void *conf) { int rc = 0; uint32_t *lvp = (uint32_t *) conf; IFNET *nif = (IFNET *) dev->dev_icb; switch (req) { case SIOCSIFFLAGS: /* Set interface flags. */ if (*lvp & IFF_UP) { if ((nif->if_flags & IFF_UP) == 0) { /* Start interface. */ } } else if (nif->if_flags & IFF_UP) { /* Stop interface. */ } break; case SIOCGIFFLAGS: /* Get interface flags. */ *lvp = nif->if_flags; break; case SIOCSIFADDR: /* Set interface hardware address. */ memcpy(nif->if_mac, conf, sizeof(nif->if_mac)); break; case SIOCGIFADDR: /* Get interface hardware address. */ memcpy(conf, nif->if_mac, sizeof(nif->if_mac)); break; default: rc = -1; break; } return rc; } static NICINFO dcb_eth0; /*! * \brief Network interface information structure. * * Used to call. */ static IFNET ifn_eth0 = { IFT_ETHER, /*!< \brief Interface type, if_type. */ 0, /*!< \brief Interface flags, if_flags. */ {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address, if_mac. */ 0, /*!< \brief IP address, if_local_ip. */ 0, /*!< \brief Remote IP address for point to point, if_remote_ip. */ 0, /*!< \brief IP network mask, if_mask. */ ETHERMTU, /*!< \brief Maximum size of a transmission unit, if_mtu. */ 0, /*!< \brief Packet identifier, if_pkt_id. */ 0, /*!< \brief Linked list of arp entries, arpTable. */ 0, /*!< \brief Linked list of multicast address entries, if_mcast. */ NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */ Lan91Output, /*!< \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 devLan91 = { 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. */ Lan91Init, /* Driver initialization routine. */ Lan91IOCtl, /* Driver specific control function. */ 0, /* Read from device. */ 0, /* Write to device. */ #ifdef __HARVARD_ARCH__ 0, /* Write from program space data to device. */ #endif 0, /* Open a device or file. */ 0, /* Close a device or file. */ 0, /* Request file size. */ 0, /* Select function, optional, not yet implemented */ }; /*@}*/