📄 ether.c
字号:
/*
* Modifications by FOCUS Software Engineering, Australia <www.focus-sw.com>
*
* 2006-03-21, Henrik Maier (HM):
* - Added Modifications for XNUT hardware.
*/
/*
* 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/
*
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* $Log: ether.c,v $
* Revision 1.3 2005/05/26 13:11:14 haraldkipp
* Get MAC from EEPROM instead of using a default.
* Deprecated GCC port macros replaced.
*
* Revision 1.2 2004/09/10 10:08:54 haraldkipp
* Minimal EEPROM emulation
*
* Revision 1.1 2004/04/15 09:34:45 haraldkipp
* Checked in
*
*/
#include <string.h>
#include <avr/io.h>
#include "rtlregs.h"
#include "util.h"
#include "eboot.h"
#include "arp.h"
#include "ether.h"
/*!
* \addtogroup xgStack
*/
/*@{*/
#define NIC_PAGE_SIZE 0x100
#define NIC_START_PAGE 0x40
#define NIC_STOP_PAGE 0x60
#define NIC_TX_PAGES 6
#define NIC_TX_BUFFERS 2
#define NIC_FIRST_TX_PAGE NIC_START_PAGE
#define NIC_FIRST_RX_PAGE (NIC_FIRST_TX_PAGE + NIC_TX_PAGES * NIC_TX_BUFFERS)
#define TX_PAGES 12
unsigned char EEPROM_read(unsigned int uiAddress)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEWE))
;
/* Set up address register */
EEAR = uiAddress;
/* Start eeprom read by writing EERE */
EECR |= (1<<EERE);
/* Return data from data register */
return EEDR;
}
/*!
* 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 */
};
#if !defined(XNUT_100) && !defined(XNUT_105)
static void EmulateNicEeprom(void)
{
register u_int cnt;
/*
* Prepare the EEPROM emulation port bits. Configure the EEDO
* and the EEMU lines as outputs and set both lines to high.
*/
PORTC = 0xC0;
DDRC = 0xC0;
Delay(20);
/*
* Force the chip to re-read the EEPROM contents.
*/
NIC_CR = NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1;
NIC_PG3_EECR = NIC_EECR_EEM0;
/*
* No external memory access beyond this point.
*/
#if defined(__AVR_AT90CAN128__)
XMCRA &= ~_BV(SRE);
#else
MCUCR &= ~_BV(SRE);
#endif
/*
* Loop until the chip stops toggling our EESK input.
*/
do {
cnt = 0;
if(bit_is_set(PINC, 5)) {
while(++cnt && bit_is_set(PINC, 5));
}
else {
while(++cnt && bit_is_clear(PINC, 5));
}
} while(cnt);
/*
* Enable memory interface.
*/
#if defined(__AVR_AT90CAN128__)
XMCRA |= _BV(SRE);
#else
MCUCR |= _BV(SRE);
#endif
/* Reset port outputs to default. */
PORTC = 0x00;
DDRC = 0x00;
/* Wait until controller ready. */
while(NIC_CR != (NIC_CR_STP | NIC_CR_RD2));
}
#endif
/*!
* \brief Initialize the NIC.
*
* For further description of the initialization
* please refer to the original Ethernut code.
*/
void NicInit(void)
{
u_char c;
mac[3]=EEPROM_read(0x4D);
mac[4]=EEPROM_read(0x4E);
mac[5]=EEPROM_read(0x4F);
/*
* Enable external data and address
* bus.
*/
#if defined(__AVR_AT90CAN128__)
XMCRA = (_BV(SRE) | _BV(SRW10));
#else
MCUCR = (_BV(SRE) | _BV(SRW));
#endif
#if defined(XNUT_100) || defined(XNUT_105)
// Reset pulse must be at least 800 ns.
DDRD |= _BV(7);
PORTD |= _BV(7);
Delay(100);
PORTD &= ~_BV(7);
Delay(100);
#endif
c = NIC_RESET;
Delay(5);
NIC_RESET = c;
Delay(NIC_DELAY_1);
#if !defined(XNUT_100) && !defined(XNUT_105)
EmulateNicEeprom();
#endif
NIC_PG0_IMR = 0;
NIC_PG0_ISR = 0xff;
NIC_CR = NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1;
NIC_PG3_EECR = NIC_EECR_EEM0 | NIC_EECR_EEM1;
NIC_PG3_CONFIG3 = 0;
NIC_PG3_CONFIG2 = NIC_CONFIG2_BSELB;
NIC_PG3_EECR = 0;
Delay(200);
NIC_CR = NIC_CR_STP | NIC_CR_RD2;
NIC_PG0_DCR = NIC_DCR_LS | NIC_DCR_FT1;
NIC_PG0_RBCR0 = 0;
NIC_PG0_RBCR1 = 0;
NIC_PG0_RCR = NIC_RCR_MON;
NIC_PG0_TCR = NIC_TCR_LB0;
NIC_PG0_TPSR = NIC_FIRST_TX_PAGE;
NIC_PG0_BNRY = NIC_STOP_PAGE - 1;
NIC_PG0_PSTART = NIC_FIRST_RX_PAGE;
NIC_PG0_PSTOP = NIC_STOP_PAGE;
NIC_PG0_ISR = 0xff;
NIC_CR = NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0;
NIC_PG1_PAR0 = mac[0];
NIC_PG1_PAR1 = mac[1];
NIC_PG1_PAR2 = mac[2];
NIC_PG1_PAR3 = mac[3];
NIC_PG1_PAR4 = mac[4];
NIC_PG1_PAR5 = mac[5];
NIC_PG1_MAR0 = 0;
NIC_PG1_MAR1 = 0;
NIC_PG1_MAR2 = 0;
NIC_PG1_MAR3 = 0;
NIC_PG1_MAR4 = 0;
NIC_PG1_MAR5 = 0;
NIC_PG1_MAR6 = 0;
NIC_PG1_MAR7 = 0;
NIC_PG1_CURR = NIC_START_PAGE + TX_PAGES;
NIC_CR = NIC_CR_STP | NIC_CR_RD2;
NIC_PG0_RCR = NIC_RCR_AB;
NIC_PG0_ISR = 0xff;
NIC_PG0_IMR = 0;
#if !defined(NIC_LINK_DETECT)
NIC_CR = NIC_CR_STA | NIC_CR_RD2;
NIC_PG0_TCR = 0;
Delay(1000);
#else
/*
* 10BASE-T Cable must be plugged in and connected to a switch
* LINK LED must be ON
*/
// Switch to page 3 and check configuration registers
NIC_CR = NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1;
{
u_char i;
for (i = 0; i < 100; i++)
{
if (_MMIO_BYTE(NIC_BASE + 0x03) == 0x00)
goto line_is_up;
else
Delay(10);
}
}
Debug("Link is down, jmp 0\n");
asm volatile ("jmp 0");
line_is_up:
// Back to page 0
NIC_CR = NIC_CR_STA | NIC_CR_RD2;
NIC_PG0_TCR = 0;
#endif
}
/*!
* \brief Send an Ethernet frame.
*
* \param dmac Destination MAC address.
* \param type Frame type.
* \param len Frame size.
*
* \return 0 on success, -1 otherwise.
*/
int EtherOutput(u_char * dmac, u_short type, u_short len)
{
u_short i;
u_short sz;
u_char *cp;
ETHERHDR *eh;
if (type == ETHERTYPE_ARP) {
eh = &arpframe.eth_hdr;
cp = (u_char *) & arpframe;
} else {
eh = &sframe.eth_hdr;
cp = (u_char *) & sframe;
}
for (i = 0; i < 6; i++)
eh->ether_shost[i] = mac[i];
if (dmac) {
for (i = 0; i < 6; i++)
eh->ether_dhost[i] = dmac[i];
} else {
for (i = 0; i < 6; i++)
eh->ether_dhost[i] = 0xFF;
}
eh->ether_type = type;
if ((len += sizeof(ETHERHDR)) < 60)
sz = 60;
else
sz = len;
NIC_CR = NIC_CR_STA | NIC_CR_RD2;
NIC_PG0_RBCR0 = (u_char) sz;
NIC_PG0_RBCR1 = (u_char) (sz >> 8);
NIC_PG0_RSAR0 = 0;
NIC_PG0_RSAR1 = NIC_FIRST_TX_PAGE;
NIC_CR = NIC_CR_STA | NIC_CR_RD1;
/*
* Transfer frame.
*/
for (i = 0; i < len; i++)
NIC_IOPORT = *cp++;
for (i = len; i < sz; i++)
NIC_IOPORT = 0;
/*
* Complete remote dma.
*/
NIC_CR = NIC_CR_STA | NIC_CR_RD2;
for (i = 0; i <= 20; i++)
if (NIC_PG0_ISR & NIC_ISR_RDC)
break;
NIC_PG0_ISR = NIC_ISR_RDC;
/*
* Number of bytes to be transmitted.
*/
NIC_PG0_TBCR0 = (sz & 0xff);
NIC_PG0_TBCR1 = ((sz >> 8) & 0xff);
/*
* First page of packet to be transmitted.
*/
NIC_PG0_TPSR = NIC_FIRST_TX_PAGE;
/*
* Start transmission.
*/
NIC_CR = NIC_CR_STA | NIC_CR_TXP | NIC_CR_RD2;
/*
* Wait until transmission is completed or aborted.
*/
while (NIC_CR & NIC_CR_TXP);
return 0;
}
/*!
* \brief Receive an Ethernet frame.
*
* \param tms Return with timeout after the specified
* number of waiting loops. On a 14 Mhz ATmega
* this value represents approximately the number
* of milliseconds to wait.
*
* \return The number of bytes received, 0 on timeout
* or -1 in case of a failure.
*/
int EtherInput(u_short type, u_short tms)
{
int rc = 0;
u_short maxl = 0;
u_char isr = 0;
struct nic_pkt_header hdr;
u_char *buf;
u_char bnry;
u_char curr;
u_short i;
u_char wtc;
while (tms && rc == 0) {
/*
* Wait until frame received, timeout or receiver error.
*/
for (wtc = 1; tms; wtc++) {
isr = NIC_PG0_ISR;
if (isr & NIC_ISR_RXE) {
return -1;
}
if (isr & NIC_ISR_PRX) {
break;
}
if (wtc == 0)
tms--;
}
if ((isr & NIC_ISR_PRX) == 0) {
NIC_PG0_ISR = isr;
break;
}
/*
* Get the current page pointer. It points to the page where the NIC
* will start saving the next incoming packet.
*/
NIC_CR = NIC_CR_STA | NIC_CR_RD2 | NIC_CR_PS0;
curr = NIC_PG1_CURR;
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.
*/
if ((bnry = NIC_PG0_BNRY + 1) >= NIC_STOP_PAGE)
bnry = NIC_FIRST_RX_PAGE;
if (bnry == curr) {
NIC_PG0_ISR = isr;
continue;
}
/*
* Read the NIC specific packet header.
*/
NIC_PG0_RBCR0 = sizeof(struct nic_pkt_header);
NIC_PG0_RBCR1 = 0;
NIC_PG0_RSAR0 = 0;
NIC_PG0_RSAR1 = bnry;
buf = (u_char *) & hdr;
NIC_CR = NIC_CR_STA | NIC_CR_RD0;
for (i = 0; i < sizeof(struct nic_pkt_header); i++)
*buf++ = NIC_IOPORT;
/*
* Complete remote dma.
*/
NIC_CR = NIC_CR_STA | NIC_CR_RD2;
for (i = 0; i <= 20; i++) {
if (NIC_PG0_ISR & NIC_ISR_RDC)
break;
}
NIC_PG0_ISR = NIC_ISR_RDC;
/*
* Check frame length. If it's outside limits, we assume
* that the NIC is corrupted. Reset and retry.
*/
if (hdr.ph_size < 60 + sizeof(struct nic_pkt_header) || hdr.ph_size > 1518 + sizeof(struct nic_pkt_header)) {
NicInit();
continue;
}
/*
* Check packet status.
*/
if ((hdr.ph_status & 0x0F) == 1) {
rc = hdr.ph_size - sizeof(struct nic_pkt_header);
/*
* Set remote dma byte count and
* start address. Don't read the
* header again.
*/
NIC_PG0_RBCR0 = (u_char) rc;
NIC_PG0_RBCR1 = (u_char) ((u_short) rc >> 8);
NIC_PG0_RSAR0 = sizeof(struct nic_pkt_header);
NIC_PG0_RSAR1 = bnry;
/*
* Perform the read.
*/
NIC_CR = NIC_CR_STA | NIC_CR_RD0;
buf = (u_char *) & rframe;
maxl = sizeof(rframe) < (u_short)rc ? sizeof(rframe) : (u_short)rc;
for (i = 0; i < maxl; i++) {
*buf++ = NIC_IOPORT;
}
for (; i < (u_short) rc; i++)
NIC_IOPORT;
/*
* Complete remote dma.
*/
NIC_CR = NIC_CR_STA | NIC_CR_RD2;
for (i = 0; i <= 20; i++) {
if (NIC_PG0_ISR & NIC_ISR_RDC)
break;
}
NIC_PG0_ISR = NIC_ISR_RDC;
}
/*
* Set boundary register to the last page we read.
*/
if (--hdr.ph_nextpg < NIC_FIRST_RX_PAGE)
hdr.ph_nextpg = NIC_STOP_PAGE - 1;
NIC_PG0_BNRY = hdr.ph_nextpg;
/*
* Handle incoming ARP requests.
*/
if (rframe.eth_hdr.ether_type != type) {
if (rframe.eth_hdr.ether_type == ETHERTYPE_ARP)
ArpRespond();
rc = 0;
}
}
return rc;
}
/*@}*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -