📄 nicrtl.c
字号:
/* * Copyright (C) 2001-2004 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 EGNITE SOFTWARE GMBH 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 EGNITE * SOFTWARE GMBH 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/ * *//* * $Log: nicrtl.c,v $ * Revision 1.1 2005/07/26 18:02:40 haraldkipp * Moved from dev. * * Revision 1.4 2005/04/30 16:42:41 chaac * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG * is defined in NutConf, it will make effect where it is used. * * Revision 1.3 2005/01/24 21:11:52 freckle * renamed NutEventPostFromIRQ into NutEventPostFromIrq * * Revision 1.2 2005/01/21 16:49:46 freckle * Seperated calls to NutEventPostAsync between Threads and IRQs * * Revision 1.1 2004/03/16 16:48:27 haraldkipp * Added Jan Dubiec's H8/300 port. * */#include <cfg/os.h>#include <string.h>#include <sys/nutconfig.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 <netinet/if_ether.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/*! * \brief Read word from controller DMA port. */#define nic_read_dma() *((volatile u_short*) (base + NIC_IOPORT))/*! * \brief Write word to controller DMA port. */#define nic_write_dma(data) *((u_short*) (base + NIC_IOPORT)) = data/*! * \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. */#define NIC_TX_BUFFERS 2/*! * \brief Controller memory layout: * * 0x4000 - 0x4bff 3k bytes transmit buffer * 0x4c00 - 0x5fff 5k bytes receive buffer */#define NIC_FIRST_TX_PAGE NIC_START_PAGE#define NIC_FIRST_RX_PAGE (NIC_FIRST_TX_PAGE + NIC_TX_PAGES * NIC_TX_BUFFERS)/*! * \brief Standard sizing information */#define TX_PAGES 12 /* Allow for 2 back-to-back frames *//*! * \addtogroup xgNicRtl *//*@{*//*! * Realtek packet header. */struct nic_pkt_header { u_char ph_status; /*!< \brief Status, contents of RSR register */ u_char ph_nextpg; /*!< \brief Page for next packet */ u_short ph_size; /*!< \brief Size of header and packet in octets */};/*! * \brief Reset the Ethernet controller. * */static int NicReset(volatile u_char * base){ u_char i; u_char j; /* * 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 = nic_read(NIC_RESET); NutDelay(WAIT5); nic_write(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 ((nic_read(NIC_PG0_ISR) & NIC_ISR_RST) != 0 && nic_read(NIC_PG0_RBCR0) == 0x50 && nic_read(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(volatile u_char * base, CONST u_char * mac){ u_char i; if (NicReset(base)) return -1; /* * Mask all interrupts and clear any interrupt status flag to set the * INT pin back to low. */ nic_write(NIC_PG0_IMR, 0); nic_write(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. */ nic_write(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. */ nic_write(NIC_PG3_EECR, NIC_EECR_EEM0 | NIC_EECR_EEM1); /* * Disable sleep and power down. */ nic_write(NIC_PG3_CONFIG3, 0); /* * 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. */ nic_write(NIC_PG3_CONFIG2, NIC_CONFIG2_BSELB); /* * Reenable write protection of the nic configuration registers * and wait for link test to complete. */ nic_write(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. */ nic_write(NIC_CR, NIC_CR_STP | NIC_CR_RD2);/* nic_write(NIC_PG0_DCR, NIC_DCR_LS | NIC_DCR_FT1); */ nic_write(NIC_PG0_DCR, NIC_DCR_WTS | NIC_DCR_LS | NIC_DCR_FT1); /* * Clear remote dma byte count register. */ nic_write(NIC_PG0_RBCR0, 0); nic_write(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. */ nic_write(NIC_PG0_RCR, NIC_RCR_MON); nic_write(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. */ nic_write(NIC_PG0_TPSR, NIC_FIRST_TX_PAGE); nic_write(NIC_PG0_BNRY, NIC_STOP_PAGE - 1); nic_write(NIC_PG0_PSTART, NIC_FIRST_RX_PAGE); nic_write(NIC_PG0_PSTOP, NIC_STOP_PAGE); /* * Once again clear interrupt status register. */ nic_write(NIC_PG0_ISR, 0xff); /* * Switch to register page 1 and copy our MAC address into the nic. * We are still in stop mode. */ nic_write(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0); for (i = 0; i < 6; i++) nic_write(NIC_PG1_PAR0 + i, mac[i]); /* * Clear multicast filter bits to disable all packets. */ for (i = 0; i < 8; i++) nic_write(NIC_PG1_MAR0 + i, 0); /* * Set current page pointer to one page after the boundary pointer. */ nic_write(NIC_PG1_CURR, NIC_START_PAGE + TX_PAGES); /* * Switch back to register page 0, remaining in stop mode. */ nic_write(NIC_CR, NIC_CR_STP | NIC_CR_RD2); /* * Take receiver out of monitor mode and enable it for accepting * broadcasts. */ nic_write(NIC_PG0_RCR, NIC_RCR_AB); /* * Clear all interrupt status flags and enable interrupts. */ nic_write(NIC_PG0_ISR, 0xff); nic_write(NIC_PG0_IMR, NIC_IMR_PRXE | NIC_IMR_PTXE | 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. */ nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD2); nic_write(NIC_PG0_TCR, 0); NutDelay(255); return 0;}/*! * Complete remote DMA. */static void NicCompleteDma(volatile u_char * base){ u_char i; /* * Complete remote dma. */ nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD2); /* * Check that we have a DMA complete flag. */ for (i = 0; i <= 20; i++) if (nic_read(NIC_PG0_ISR) & NIC_ISR_RDC) break; /* * Reset remote dma complete flag. */ nic_write(NIC_PG0_ISR, NIC_ISR_RDC);}/*! * \brief Load a packet into the nic's transmit ring buffer. * * Interupts must have been disabled when calling this function. * * \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(). 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(volatile u_char * base, NETBUF * nb){ u_short sz; u_short i; u_short *p; u_char padding = 0; /* * 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; /* * The controller will not append pad bytes, * so we have to do this. */ if (sz < 60) { padding = (u_char) (60 - sz); sz = 60; } /* * Set remote dma byte count * and start address. */ nic_write(NIC_PG0_RBCR0, sz); nic_write(NIC_PG0_RBCR1, sz >> 8); nic_write(NIC_PG0_RSAR0, 0); nic_write(NIC_PG0_RSAR1, NIC_FIRST_TX_PAGE); /* * Peform the write. */ nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD1); /* * Switch MCU data bus to 16-bit mode */ NicMcu16bitBus(); /* * Transfer the Ethernet frame. */ p = nb->nb_dl.vp; for (i = nb->nb_dl.sz >> 1; i; i--) nic_write_dma(*p++); p = nb->nb_nw.vp; for (i = nb->nb_nw.sz >> 1; i; i--) nic_write_dma(*p++); p = nb->nb_tp.vp; for (i = nb->nb_tp.sz >> 1; i; i--) nic_write_dma(*p++); p = nb->nb_ap.vp; for (i = (nb->nb_ap.sz + 1) >> 1; i; i--) nic_write_dma(*p++); /* * Add pad bytes. */ if ((nb->nb_ap.sz & 0x0001) != 0) padding--; for (i = 0; i < (padding + 1) >> 1; i++) nic_write_dma(0); /* * Switch MCU data bus to 8-bit mode */ NicMcu8bitBus(); /* * Complete remote dma. */ NicCompleteDma(base); /* * Number of bytes to be transmitted. */ nic_write(NIC_PG0_TBCR0, (sz & 0xff)); nic_write(NIC_PG0_TBCR1, ((sz >> 8) & 0xff)); /* * First page of packet to be transmitted. */ nic_write(NIC_PG0_TPSR, NIC_FIRST_TX_PAGE); /* * Start transmission. */ nic_write(NIC_CR, NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2); 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(volatile u_char * base, u_char dflg){ NETBUF *nb = 0; struct nic_pkt_header hdr; u_short count; u_char nextpg; u_char bnry; u_char curr; u_short i; u_short *buf; /* * Get the current page pointer. It points to the page where the NIC * will start saving the next incoming packet. */ nic_write(NIC_CR, NIC_CR_STA | NIC_CR_RD2 | NIC_CR_PS0); curr = nic_read(NIC_PG1_CURR); nic_write(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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -