184ed5f988
Massive update. The highlights: o dramatically cut memory usage by writing better, less intertwingled code. o implement booting off mmc/sd cards (sd only tested one at the moment) o start to split out board specific stuff for boot2.
563 lines
14 KiB
C
563 lines
14 KiB
C
/*******************************************************************************
|
|
*
|
|
* Filename: emac.c
|
|
*
|
|
* Instantiation of routines for MAC/ethernet functions supporting tftp.
|
|
*
|
|
* Revision information:
|
|
*
|
|
* 28AUG2004 kb_admin initial creation
|
|
* 08JAN2005 kb_admin added tftp download
|
|
* also adapted from external sources
|
|
*
|
|
* BEGIN_KBDD_BLOCK
|
|
* No warranty, expressed or implied, is included with this software. It is
|
|
* provided "AS IS" and no warranty of any kind including statutory or aspects
|
|
* relating to merchantability or fitness for any purpose is provided. All
|
|
* intellectual property rights of others is maintained with the respective
|
|
* owners. This software is not copyrighted and is intended for reference
|
|
* only.
|
|
* END_BLOCK
|
|
*
|
|
* $FreeBSD$
|
|
******************************************************************************/
|
|
|
|
#include "at91rm9200.h"
|
|
#include "at91rm9200_lowlevel.h"
|
|
#include "emac.h"
|
|
#include "lib.h"
|
|
|
|
/* ****************************** GLOBALS *************************************/
|
|
|
|
/* ********************** PRIVATE FUNCTIONS/DATA ******************************/
|
|
|
|
static receive_descriptor_t *p_rxBD;
|
|
static unsigned short localPort;
|
|
static unsigned short serverPort;
|
|
static unsigned serverMACSet;
|
|
static unsigned localIPSet, serverIPSet;
|
|
static unsigned lastSize;
|
|
static unsigned char serverMACAddr[6];
|
|
static unsigned char localIPAddr[4], serverIPAddr[4];
|
|
static int ackBlock;
|
|
static char *dlAddress;
|
|
|
|
static unsigned transmitBuffer[1024 / sizeof(unsigned)];
|
|
static unsigned tftpSendPacket[256 / sizeof(unsigned)];
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* unsigned short IP_checksum(unsigned short *p, int len)
|
|
* This private function calculates the IP checksum for various headers.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static unsigned short
|
|
IP_checksum(unsigned short *p, int len)
|
|
{
|
|
unsigned i, t;
|
|
|
|
len &= ~1;
|
|
|
|
for (i=0,t=0; i<len; i+=2, ++p)
|
|
t += SWAP16(*p);
|
|
|
|
t = (t & 0xffff) + (t >> 16);
|
|
return (~t);
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void GetServerAddress(void)
|
|
* This private function sends an ARP request to determine the server MAC.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static void
|
|
GetServerAddress(void)
|
|
{
|
|
arp_header_t *p_ARP;
|
|
|
|
p_ARP = (arp_header_t*)transmitBuffer;
|
|
|
|
p_memset((char*)p_ARP->dest_mac, 0xFF, 6);
|
|
|
|
memcpy(p_ARP->src_mac, localMACAddr, 6);
|
|
|
|
p_ARP->frame_type = SWAP16(PROTOCOL_ARP);
|
|
p_ARP->hard_type = SWAP16(1);
|
|
p_ARP->prot_type = SWAP16(PROTOCOL_IP);
|
|
p_ARP->hard_size = 6;
|
|
p_ARP->prot_size = 4;
|
|
p_ARP->operation = SWAP16(ARP_REQUEST);
|
|
|
|
memcpy(p_ARP->sender_mac, localMACAddr, 6);
|
|
memcpy(p_ARP->sender_ip, localIPAddr, 4);
|
|
p_memset((char*)p_ARP->target_mac, 0, 6);
|
|
memcpy(p_ARP->target_ip, serverIPAddr, 4);
|
|
|
|
// wait until transmit is available
|
|
while (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) ;
|
|
|
|
*AT91C_EMAC_TSR |= AT91C_EMAC_COMP;
|
|
*AT91C_EMAC_TAR = (unsigned)transmitBuffer;
|
|
*AT91C_EMAC_TCR = 0x40;
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void Send_TFTP_Packet(char *tftpData, unsigned tftpLength)
|
|
* This private function initializes and send a TFTP packet.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static void
|
|
Send_TFTP_Packet(char *tftpData, unsigned tftpLength)
|
|
{
|
|
transmit_header_t *macHdr = (transmit_header_t*)tftpSendPacket;
|
|
ip_header_t *ipHdr;
|
|
udp_header_t *udpHdr;
|
|
unsigned t_checksum;
|
|
|
|
memcpy(macHdr->dest_mac, serverMACAddr, 6);
|
|
memcpy(macHdr->src_mac, localMACAddr, 6);
|
|
macHdr->proto_mac = SWAP16(PROTOCOL_IP);
|
|
|
|
ipHdr = (ip_header_t*)&macHdr->packet_length;
|
|
|
|
ipHdr->ip_v_hl = 0x45;
|
|
ipHdr->ip_tos = 0;
|
|
ipHdr->ip_len = SWAP16(28 + tftpLength);
|
|
ipHdr->ip_id = 0;
|
|
ipHdr->ip_off = SWAP16(0x4000);
|
|
ipHdr->ip_ttl = 64;
|
|
ipHdr->ip_p = PROTOCOL_UDP;
|
|
ipHdr->ip_sum = 0;
|
|
|
|
memcpy(ipHdr->ip_src, localIPAddr, 4);
|
|
memcpy(ipHdr->ip_dst, serverIPAddr, 4);
|
|
|
|
ipHdr->ip_sum = SWAP16(IP_checksum((unsigned short*)ipHdr, 20));
|
|
|
|
udpHdr = (udp_header_t*)(ipHdr + 1);
|
|
|
|
udpHdr->src_port = localPort;
|
|
udpHdr->dst_port = serverPort;
|
|
udpHdr->udp_len = SWAP16(8 + tftpLength);
|
|
udpHdr->udp_cksum = 0;
|
|
|
|
memcpy((char *)udpHdr+8, tftpData, tftpLength);
|
|
|
|
t_checksum = IP_checksum((unsigned short*)ipHdr + 6, (16 + tftpLength));
|
|
|
|
t_checksum = (~t_checksum) & 0xFFFF;
|
|
t_checksum += 25 + tftpLength;
|
|
|
|
t_checksum = (t_checksum & 0xffff) + (t_checksum >> 16);
|
|
t_checksum = (~t_checksum) & 0xFFFF;
|
|
|
|
udpHdr->udp_cksum = SWAP16(t_checksum);
|
|
|
|
while (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) ;
|
|
|
|
*AT91C_EMAC_TSR |= AT91C_EMAC_COMP;
|
|
*AT91C_EMAC_TAR = (unsigned)tftpSendPacket;
|
|
*AT91C_EMAC_TCR = 42 + tftpLength;
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void TFTP_RequestFile(char *filename)
|
|
* This private function sends a RRQ packet to the server.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static void
|
|
TFTP_RequestFile(char *filename)
|
|
{
|
|
tftp_header_t tftpHeader;
|
|
char *cPtr, *ePtr, *mPtr;
|
|
unsigned length;
|
|
|
|
tftpHeader.opcode = TFTP_RRQ_OPCODE;
|
|
|
|
cPtr = (char*)&(tftpHeader.block_num);
|
|
|
|
ePtr = strcpy(cPtr, filename);
|
|
mPtr = strcpy(ePtr, "octet");
|
|
|
|
length = mPtr - cPtr;
|
|
length += 2;
|
|
|
|
Send_TFTP_Packet((char*)&tftpHeader, length);
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void TFTP_ACK_Data(char *data, unsigned short block_num, unsigned short len)
|
|
* This private function sends an ACK packet to the server.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static void
|
|
TFTP_ACK_Data(char *data, unsigned short block_num, unsigned short len)
|
|
{
|
|
tftp_header_t tftpHeader;
|
|
|
|
if (block_num == (ackBlock + 1)) {
|
|
++ackBlock;
|
|
memcpy(dlAddress, data, len);
|
|
dlAddress += len;
|
|
lastSize += len;
|
|
if (ackBlock % 128 == 0)
|
|
printf("tftp: %u kB\r", lastSize / 1024);
|
|
}
|
|
tftpHeader.opcode = TFTP_ACK_OPCODE;
|
|
tftpHeader.block_num = SWAP16(ackBlock);
|
|
Send_TFTP_Packet((char*)&tftpHeader, 4);
|
|
if (len < 512) {
|
|
ackBlock = -2;
|
|
printf("tftp: %u byte\n", lastSize);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void CheckForNewPacket(ip_header_t *pHeader)
|
|
* This private function polls for received ethernet packets and handles
|
|
* any here.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static int
|
|
CheckForNewPacket(ip_header_t *pHeader)
|
|
{
|
|
unsigned short *pFrameType;
|
|
unsigned i;
|
|
char *pData;
|
|
ip_header_t *pIpHeader;
|
|
arp_header_t *p_ARP;
|
|
int process = 0;
|
|
|
|
process = 0;
|
|
for (i = 0; i < MAX_RX_PACKETS; ++i) {
|
|
if(p_rxBD[i].address & 0x1) {
|
|
process = 1;
|
|
(*AT91C_EMAC_RSR) |= (*AT91C_EMAC_RSR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!process)
|
|
return (0);
|
|
process = i;
|
|
|
|
pFrameType = (unsigned short *)((p_rxBD[i].address & 0xFFFFFFFC) + 12);
|
|
pData = (char *)(p_rxBD[i].address & 0xFFFFFFFC);
|
|
|
|
switch (*pFrameType) {
|
|
|
|
case SWAP16(PROTOCOL_ARP):
|
|
p_ARP = (arp_header_t*)pData;
|
|
if (p_ARP->operation == SWAP16(ARP_REPLY)) {
|
|
// check if new server info is available
|
|
if ((!serverMACSet) &&
|
|
(!(p_memcmp((char*)p_ARP->sender_ip,
|
|
(char*)serverIPAddr, 4)))) {
|
|
|
|
serverMACSet = 1;
|
|
memcpy(serverMACAddr, p_ARP->sender_mac, 6);
|
|
}
|
|
} else if (p_ARP->operation == SWAP16(ARP_REQUEST)) {
|
|
// ARP REPLY operation
|
|
p_ARP->operation = SWAP16(ARP_REPLY);
|
|
|
|
// Fill the dest address and src address
|
|
for (i = 0; i <6; i++) {
|
|
// swap ethernet dest address and ethernet src address
|
|
pData[i] = pData[i+6];
|
|
pData[i+6] = localMACAddr[i];
|
|
// swap sender ethernet address and target ethernet address
|
|
pData[i+22] = localMACAddr[i];
|
|
pData[i+32] = pData[i+6];
|
|
}
|
|
|
|
// swap sender IP address and target IP address
|
|
for (i = 0; i<4; i++) {
|
|
pData[i+38] = pData[i+28];
|
|
pData[i+28] = localIPAddr[i];
|
|
}
|
|
|
|
if (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) break;
|
|
|
|
*AT91C_EMAC_TSR |= AT91C_EMAC_COMP;
|
|
*AT91C_EMAC_TAR = (unsigned)pData;
|
|
*AT91C_EMAC_TCR = 0x40;
|
|
}
|
|
break;
|
|
case SWAP16(PROTOCOL_IP):
|
|
pIpHeader = (ip_header_t*)(pData + 14);
|
|
memcpy(pHeader, pIpHeader, sizeof(ip_header_t));
|
|
|
|
if (pIpHeader->ip_p == PROTOCOL_UDP) {
|
|
udp_header_t *udpHdr;
|
|
tftp_header_t *tftpHdr;
|
|
|
|
udpHdr = (udp_header_t*)((char*)pIpHeader+20);
|
|
tftpHdr = (tftp_header_t*)((char*)udpHdr + 8);
|
|
|
|
if (udpHdr->dst_port != localPort)
|
|
break;
|
|
|
|
if (tftpHdr->opcode != TFTP_DATA_OPCODE)
|
|
break;
|
|
|
|
if (ackBlock == -1) {
|
|
if (tftpHdr->block_num != SWAP16(1))
|
|
break;
|
|
serverPort = udpHdr->src_port;
|
|
ackBlock = 0;
|
|
}
|
|
|
|
if (serverPort != udpHdr->src_port)
|
|
break;
|
|
|
|
TFTP_ACK_Data(tftpHdr->data,
|
|
SWAP16(tftpHdr->block_num),
|
|
SWAP16(udpHdr->udp_len) - 12);
|
|
}
|
|
}
|
|
p_rxBD[process].address &= ~0x01;
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* unsigned short AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr)
|
|
* This private function reads the PHY device.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static unsigned short
|
|
AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr)
|
|
{
|
|
unsigned value = 0x60020000 | (addr << 18);
|
|
|
|
pEmac->EMAC_CTL |= AT91C_EMAC_MPE;
|
|
pEmac->EMAC_MAN = value;
|
|
while(!((pEmac->EMAC_SR) & AT91C_EMAC_IDLE));
|
|
pEmac->EMAC_CTL &= ~AT91C_EMAC_MPE;
|
|
return (pEmac->EMAC_MAN & 0x0000ffff);
|
|
}
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* unsigned short AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr)
|
|
* This private function reads the PHY device.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
#ifdef BOOT_TSC
|
|
static unsigned short
|
|
AT91F_MII_WritePhy (AT91PS_EMAC pEmac, unsigned char addr, unsigned short s)
|
|
{
|
|
unsigned value = 0x50020000 | (addr << 18) | s;
|
|
|
|
pEmac->EMAC_CTL |= AT91C_EMAC_MPE;
|
|
pEmac->EMAC_MAN = value;
|
|
while(!((pEmac->EMAC_SR) & AT91C_EMAC_IDLE));
|
|
pEmac->EMAC_CTL &= ~AT91C_EMAC_MPE;
|
|
return (pEmac->EMAC_MAN & 0x0000ffff);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void MII_GetLinkSpeed(AT91PS_EMAC pEmac)
|
|
* This private function determines the link speed set by the PHY.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static void
|
|
MII_GetLinkSpeed(AT91PS_EMAC pEmac)
|
|
{
|
|
unsigned short stat2;
|
|
unsigned update;
|
|
#ifdef BOOT_TSC
|
|
unsigned sec;
|
|
int i;
|
|
#endif
|
|
#ifdef BOOT_KB9202
|
|
stat2 = AT91F_MII_ReadPhy(pEmac, MII_STS2_REG);
|
|
if (!(stat2 & MII_STS2_LINK))
|
|
return ;
|
|
update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
|
|
if (stat2 & MII_STS2_100TX)
|
|
update |= AT91C_EMAC_SPD;
|
|
if (stat2 & MII_STS2_FDX)
|
|
update |= AT91C_EMAC_FD;
|
|
#endif
|
|
#ifdef BOOT_TSC
|
|
while (1) {
|
|
for (i = 0; i < 10; i++) {
|
|
stat2 = AT91F_MII_ReadPhy(pEmac, MII_STS_REG);
|
|
if (stat2 & MII_STS_LINK_STAT)
|
|
break;
|
|
printf(".");
|
|
sec = GetSeconds();
|
|
while (GetSeconds() <= sec) continue;
|
|
}
|
|
if (stat2 & MII_STS_LINK_STAT)
|
|
break;
|
|
printf("Resetting MII...");
|
|
AT91F_MII_WritePhy(pEmac, 0x0, 0x8000);
|
|
while (AT91F_MII_ReadPhy(pEmac, 0x0) & 0x8000) continue;
|
|
}
|
|
printf("emac: link");
|
|
stat2 = AT91F_MII_ReadPhy(pEmac, MII_SPEC_STS_REG);
|
|
update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
|
|
if (stat2 & (MII_SSTS_100FDX | MII_SSTS_100HDX)) {
|
|
printf(" 100TX");
|
|
update |= AT91C_EMAC_SPD;
|
|
}
|
|
if (stat2 & (MII_SSTS_100FDX | MII_SSTS_10FDX)) {
|
|
printf(" FDX");
|
|
update |= AT91C_EMAC_FD;
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
pEmac->EMAC_CFG = update;
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void AT91F_EmacEntry(void)
|
|
* This private function initializes the EMAC on the chip.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
static void
|
|
AT91F_EmacEntry(void)
|
|
{
|
|
unsigned i;
|
|
char *pRxPacket = (char*)RX_DATA_START;
|
|
AT91PS_EMAC pEmac = AT91C_BASE_EMAC;
|
|
|
|
p_rxBD = (receive_descriptor_t*)RX_BUFFER_START;
|
|
localPort = SWAP16(0x8002);
|
|
|
|
for (i = 0; i < MAX_RX_PACKETS; ++i) {
|
|
|
|
p_rxBD[i].address = (unsigned)pRxPacket;
|
|
p_rxBD[i].size = 0;
|
|
pRxPacket += RX_PACKET_SIZE;
|
|
}
|
|
|
|
// Set the WRAP bit at the end of the list descriptor
|
|
p_rxBD[MAX_RX_PACKETS-1].address |= 0x02;
|
|
|
|
if (!(pEmac->EMAC_SR & AT91C_EMAC_LINK))
|
|
MII_GetLinkSpeed(pEmac);
|
|
|
|
pEmac->EMAC_RBQP = (unsigned) p_rxBD;
|
|
pEmac->EMAC_RSR |= (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
|
|
pEmac->EMAC_CTL = AT91C_EMAC_TE | AT91C_EMAC_RE;
|
|
|
|
pEmac->EMAC_TAR = (unsigned)transmitBuffer;
|
|
}
|
|
|
|
|
|
/* ************************** GLOBAL FUNCTIONS ********************************/
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void SetServerIPAddress(unsigned address)
|
|
* This global function sets the IP of the TFTP download server.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
void
|
|
SetServerIPAddress(unsigned address)
|
|
{
|
|
// force update in case the IP has changed
|
|
serverMACSet = 0;
|
|
|
|
serverIPAddr[0] = (address >> 24) & 0xFF;
|
|
serverIPAddr[1] = (address >> 16) & 0xFF;
|
|
serverIPAddr[2] = (address >> 8) & 0xFF;
|
|
serverIPAddr[3] = (address >> 0) & 0xFF;
|
|
|
|
serverIPSet = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void SetLocalIPAddress(unsigned address)
|
|
* This global function sets the IP of this module.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
void
|
|
SetLocalIPAddress(unsigned address)
|
|
{
|
|
// force update in case the IP has changed
|
|
serverMACSet = 0;
|
|
|
|
localIPAddr[0] = (address >> 24) & 0xFF;
|
|
localIPAddr[1] = (address >> 16) & 0xFF;
|
|
localIPAddr[2] = (address >> 8) & 0xFF;
|
|
localIPAddr[3] = (address >> 0) & 0xFF;
|
|
|
|
localIPSet = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* .KB_C_FN_DEFINITION_START
|
|
* void TFTP_Download(unsigned address, char *filename)
|
|
* This global function initiates and processes a tftp download request.
|
|
* The server IP, local IP, local MAC must be set before this function is
|
|
* executed.
|
|
* .KB_C_FN_DEFINITION_END
|
|
*/
|
|
void
|
|
TFTP_Download(unsigned address, char *filename)
|
|
{
|
|
ip_header_t IpHeader;
|
|
unsigned thisSeconds;
|
|
int timeout;
|
|
|
|
if ((!localMACSet) || (!localIPSet) || (!serverIPSet))
|
|
return ;
|
|
|
|
AT91F_EmacEntry();
|
|
GetServerAddress();
|
|
dlAddress = (char*)address;
|
|
lastSize = 0;
|
|
timeout = 10;
|
|
thisSeconds = GetSeconds() + 1;
|
|
serverPort = SWAP16(69);
|
|
++localPort;
|
|
ackBlock = -1;
|
|
|
|
while (timeout) {
|
|
if (CheckForNewPacket(&IpHeader)) {
|
|
if (ackBlock == -2)
|
|
break;
|
|
timeout = 10;
|
|
thisSeconds = GetSeconds() + 1;
|
|
} else if (GetSeconds() > thisSeconds) {
|
|
--timeout;
|
|
thisSeconds = GetSeconds() + 1;
|
|
if (!serverMACSet)
|
|
GetServerAddress();
|
|
else if (ackBlock == -1)
|
|
TFTP_RequestFile(filename);
|
|
else {
|
|
// Be sure to send a NAK, which is done by
|
|
// ACKing the last block we got.
|
|
TFTP_ACK_Data(0, ackBlock, 512);
|
|
printf("\nNAK %u\n", ackBlock);
|
|
}
|
|
}
|
|
}
|
|
if (timeout == 0)
|
|
printf("TFTP TIMEOUT!\n");
|
|
}
|