1/******************************************************************************* 2 * 3 * Filename: emac.c 4 * 5 * Instantiation of routines for MAC/ethernet functions supporting tftp. 6 * 7 * Revision information: 8 * 9 * 28AUG2004 kb_admin initial creation 10 * 08JAN2005 kb_admin added tftp download 11 * also adapted from external sources 12 * 13 * BEGIN_KBDD_BLOCK 14 * No warranty, expressed or implied, is included with this software. It is 15 * provided "AS IS" and no warranty of any kind including statutory or aspects 16 * relating to merchantability or fitness for any purpose is provided. All 17 * intellectual property rights of others is maintained with the respective 18 * owners. This software is not copyrighted and is intended for reference 19 * only. 20 * END_BLOCK 21 * 22 * $FreeBSD: releng/10.3/sys/boot/arm/at91/libat91/emac.c 172991 2007-10-25 22:50:25Z cognet $ 23 ******************************************************************************/ 24 25#include "at91rm9200.h" 26#include "at91rm9200_lowlevel.h" 27#include "emac.h" 28#include "lib.h" 29 30/* ****************************** GLOBALS *************************************/ 31 32/* ********************** PRIVATE FUNCTIONS/DATA ******************************/ 33 34static receive_descriptor_t *p_rxBD; 35static unsigned short localPort; 36static unsigned short serverPort; 37static unsigned serverMACSet; 38static unsigned localIPSet, serverIPSet; 39static unsigned lastSize; 40static unsigned char serverMACAddr[6]; 41static unsigned char localIPAddr[4], serverIPAddr[4]; 42static int ackBlock; 43static char *dlAddress; 44 45static unsigned transmitBuffer[1024 / sizeof(unsigned)]; 46static unsigned tftpSendPacket[256 / sizeof(unsigned)]; 47 48/* 49 * .KB_C_FN_DEFINITION_START 50 * unsigned short IP_checksum(unsigned short *p, int len) 51 * This private function calculates the IP checksum for various headers. 52 * .KB_C_FN_DEFINITION_END 53 */ 54static unsigned short 55IP_checksum(unsigned short *p, int len) 56{ 57 unsigned i, t; 58 59 len &= ~1; 60 61 for (i=0,t=0; i<len; i+=2, ++p) 62 t += SWAP16(*p); 63 64 t = (t & 0xffff) + (t >> 16); 65 return (~t); 66} 67 68 69/* 70 * .KB_C_FN_DEFINITION_START 71 * void GetServerAddress(void) 72 * This private function sends an ARP request to determine the server MAC. 73 * .KB_C_FN_DEFINITION_END 74 */ 75static void 76GetServerAddress(void) 77{ 78 arp_header_t *p_ARP; 79 80 p_ARP = (arp_header_t*)transmitBuffer; 81 82 p_memset((char*)p_ARP->dest_mac, 0xFF, 6); 83 84 memcpy(p_ARP->src_mac, localMACAddr, 6); 85 86 p_ARP->frame_type = SWAP16(PROTOCOL_ARP); 87 p_ARP->hard_type = SWAP16(1); 88 p_ARP->prot_type = SWAP16(PROTOCOL_IP); 89 p_ARP->hard_size = 6; 90 p_ARP->prot_size = 4; 91 p_ARP->operation = SWAP16(ARP_REQUEST); 92 93 memcpy(p_ARP->sender_mac, localMACAddr, 6); 94 memcpy(p_ARP->sender_ip, localIPAddr, 4); 95 p_memset((char*)p_ARP->target_mac, 0, 6); 96 memcpy(p_ARP->target_ip, serverIPAddr, 4); 97 98 // wait until transmit is available 99 while (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) ; 100 101 *AT91C_EMAC_TSR |= AT91C_EMAC_COMP; 102 *AT91C_EMAC_TAR = (unsigned)transmitBuffer; 103 *AT91C_EMAC_TCR = 0x40; 104} 105 106 107/* 108 * .KB_C_FN_DEFINITION_START 109 * void Send_TFTP_Packet(char *tftpData, unsigned tftpLength) 110 * This private function initializes and send a TFTP packet. 111 * .KB_C_FN_DEFINITION_END 112 */ 113static void 114Send_TFTP_Packet(char *tftpData, unsigned tftpLength) 115{ 116 transmit_header_t *macHdr = (transmit_header_t*)tftpSendPacket; 117 ip_header_t *ipHdr; 118 udp_header_t *udpHdr; 119 unsigned t_checksum; 120 121 memcpy(macHdr->dest_mac, serverMACAddr, 6); 122 memcpy(macHdr->src_mac, localMACAddr, 6); 123 macHdr->proto_mac = SWAP16(PROTOCOL_IP); 124 125 ipHdr = (ip_header_t*)&macHdr->packet_length; 126 127 ipHdr->ip_v_hl = 0x45; 128 ipHdr->ip_tos = 0; 129 ipHdr->ip_len = SWAP16(28 + tftpLength); 130 ipHdr->ip_id = 0; 131 ipHdr->ip_off = SWAP16(0x4000); 132 ipHdr->ip_ttl = 64; 133 ipHdr->ip_p = PROTOCOL_UDP; 134 ipHdr->ip_sum = 0; 135 136 memcpy(ipHdr->ip_src, localIPAddr, 4); 137 memcpy(ipHdr->ip_dst, serverIPAddr, 4); 138 139 ipHdr->ip_sum = SWAP16(IP_checksum((unsigned short*)ipHdr, 20)); 140 141 udpHdr = (udp_header_t*)(ipHdr + 1); 142 143 udpHdr->src_port = localPort; 144 udpHdr->dst_port = serverPort; 145 udpHdr->udp_len = SWAP16(8 + tftpLength); 146 udpHdr->udp_cksum = 0; 147 148 memcpy((char *)udpHdr+8, tftpData, tftpLength); 149 150 t_checksum = IP_checksum((unsigned short*)ipHdr + 6, (16 + tftpLength)); 151 152 t_checksum = (~t_checksum) & 0xFFFF; 153 t_checksum += 25 + tftpLength; 154 155 t_checksum = (t_checksum & 0xffff) + (t_checksum >> 16); 156 t_checksum = (~t_checksum) & 0xFFFF; 157 158 udpHdr->udp_cksum = SWAP16(t_checksum); 159 160 while (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) ; 161 162 *AT91C_EMAC_TSR |= AT91C_EMAC_COMP; 163 *AT91C_EMAC_TAR = (unsigned)tftpSendPacket; 164 *AT91C_EMAC_TCR = 42 + tftpLength; 165} 166 167 168/* 169 * .KB_C_FN_DEFINITION_START 170 * void TFTP_RequestFile(char *filename) 171 * This private function sends a RRQ packet to the server. 172 * .KB_C_FN_DEFINITION_END 173 */ 174static void 175TFTP_RequestFile(char *filename) 176{ 177 tftp_header_t tftpHeader; 178 char *cPtr, *ePtr, *mPtr; 179 unsigned length; 180 181 tftpHeader.opcode = TFTP_RRQ_OPCODE; 182 183 cPtr = (char*)&(tftpHeader.block_num); 184 185 ePtr = strcpy(cPtr, filename); 186 mPtr = strcpy(ePtr, "octet"); 187 188 length = mPtr - cPtr; 189 length += 2; 190 191 Send_TFTP_Packet((char*)&tftpHeader, length); 192} 193 194 195/* 196 * .KB_C_FN_DEFINITION_START 197 * void TFTP_ACK_Data(char *data, unsigned short block_num, unsigned short len) 198 * This private function sends an ACK packet to the server. 199 * .KB_C_FN_DEFINITION_END 200 */ 201static void 202TFTP_ACK_Data(unsigned char *data, unsigned short block_num, unsigned short len) 203{ 204 tftp_header_t tftpHeader; 205 206 if (block_num == (ackBlock + 1)) { 207 ++ackBlock; 208 memcpy(dlAddress, data, len); 209 dlAddress += len; 210 lastSize += len; 211 if (ackBlock % 128 == 0) 212 printf("tftp: %u kB\r", lastSize / 1024); 213 } 214 tftpHeader.opcode = TFTP_ACK_OPCODE; 215 tftpHeader.block_num = SWAP16(ackBlock); 216 Send_TFTP_Packet((char*)&tftpHeader, 4); 217 if (len < 512) { 218 ackBlock = -2; 219 printf("tftp: %u byte\n", lastSize); 220 } 221} 222 223 224/* 225 * .KB_C_FN_DEFINITION_START 226 * void CheckForNewPacket(ip_header_t *pHeader) 227 * This private function polls for received ethernet packets and handles 228 * any here. 229 * .KB_C_FN_DEFINITION_END 230 */ 231static int 232CheckForNewPacket(ip_header_t *pHeader) 233{ 234 unsigned short *pFrameType; 235 unsigned i; 236 char *pData; 237 ip_header_t *pIpHeader; 238 arp_header_t *p_ARP; 239 int process = 0; 240 241 process = 0; 242 for (i = 0; i < MAX_RX_PACKETS; ++i) { 243 if(p_rxBD[i].address & 0x1) { 244 process = 1; 245 (*AT91C_EMAC_RSR) |= (*AT91C_EMAC_RSR); 246 break; 247 } 248 } 249 250 if (!process) 251 return (0); 252 process = i; 253 254 pFrameType = (unsigned short *)((p_rxBD[i].address & 0xFFFFFFFC) + 12); 255 pData = (char *)(p_rxBD[i].address & 0xFFFFFFFC); 256 257 switch (*pFrameType) { 258 259 case SWAP16(PROTOCOL_ARP): 260 p_ARP = (arp_header_t*)pData; 261 if (p_ARP->operation == SWAP16(ARP_REPLY)) { 262 // check if new server info is available 263 if ((!serverMACSet) && 264 (!(p_memcmp((char*)p_ARP->sender_ip, 265 (char*)serverIPAddr, 4)))) { 266 267 serverMACSet = 1; 268 memcpy(serverMACAddr, p_ARP->sender_mac, 6); 269 } 270 } else if (p_ARP->operation == SWAP16(ARP_REQUEST)) { 271 // ARP REPLY operation 272 p_ARP->operation = SWAP16(ARP_REPLY); 273 274 // Fill the dest address and src address 275 for (i = 0; i <6; i++) { 276 // swap ethernet dest address and ethernet src address 277 pData[i] = pData[i+6]; 278 pData[i+6] = localMACAddr[i]; 279 // swap sender ethernet address and target ethernet address 280 pData[i+22] = localMACAddr[i]; 281 pData[i+32] = pData[i+6]; 282 } 283 284 // swap sender IP address and target IP address 285 for (i = 0; i<4; i++) { 286 pData[i+38] = pData[i+28]; 287 pData[i+28] = localIPAddr[i]; 288 } 289 290 if (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) break; 291 292 *AT91C_EMAC_TSR |= AT91C_EMAC_COMP; 293 *AT91C_EMAC_TAR = (unsigned)pData; 294 *AT91C_EMAC_TCR = 0x40; 295 } 296 break; 297 case SWAP16(PROTOCOL_IP): 298 pIpHeader = (ip_header_t*)(pData + 14); 299 memcpy(pHeader, pIpHeader, sizeof(ip_header_t)); 300 301 if (pIpHeader->ip_p == PROTOCOL_UDP) { 302 udp_header_t *udpHdr; 303 tftp_header_t *tftpHdr; 304 305 udpHdr = (udp_header_t*)((char*)pIpHeader+20); 306 tftpHdr = (tftp_header_t*)((char*)udpHdr + 8); 307 308 if (udpHdr->dst_port != localPort) 309 break; 310 311 if (tftpHdr->opcode != TFTP_DATA_OPCODE) 312 break; 313 314 if (ackBlock == -1) { 315 if (tftpHdr->block_num != SWAP16(1)) 316 break; 317 serverPort = udpHdr->src_port; 318 ackBlock = 0; 319 } 320 321 if (serverPort != udpHdr->src_port) 322 break; 323 324 TFTP_ACK_Data(tftpHdr->data, 325 SWAP16(tftpHdr->block_num), 326 SWAP16(udpHdr->udp_len) - 12); 327 } 328 } 329 p_rxBD[process].address &= ~0x01; 330 return (1); 331} 332 333 334/* 335 * .KB_C_FN_DEFINITION_START 336 * unsigned short AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr) 337 * This private function reads the PHY device. 338 * .KB_C_FN_DEFINITION_END 339 */ 340#ifndef BOOT_BWCT 341static unsigned short 342AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr) 343{ 344 unsigned value = 0x60020000 | (addr << 18); 345 346 pEmac->EMAC_CTL |= AT91C_EMAC_MPE; 347 pEmac->EMAC_MAN = value; 348 while(!((pEmac->EMAC_SR) & AT91C_EMAC_IDLE)); 349 pEmac->EMAC_CTL &= ~AT91C_EMAC_MPE; 350 return (pEmac->EMAC_MAN & 0x0000ffff); 351} 352#endif 353 354/* 355 * .KB_C_FN_DEFINITION_START 356 * unsigned short AT91F_MII_WritePhy (AT91PS_EMAC pEmac, unsigned char addr, unsigned short s) 357 * This private function writes the PHY device. 358 * .KB_C_FN_DEFINITION_END 359 */ 360#ifdef BOOT_TSC 361static unsigned short 362AT91F_MII_WritePhy (AT91PS_EMAC pEmac, unsigned char addr, unsigned short s) 363{ 364 unsigned value = 0x50020000 | (addr << 18) | s; 365 366 pEmac->EMAC_CTL |= AT91C_EMAC_MPE; 367 pEmac->EMAC_MAN = value; 368 while(!((pEmac->EMAC_SR) & AT91C_EMAC_IDLE)); 369 pEmac->EMAC_CTL &= ~AT91C_EMAC_MPE; 370 return (pEmac->EMAC_MAN & 0x0000ffff); 371} 372#endif 373 374/* 375 * .KB_C_FN_DEFINITION_START 376 * void MII_GetLinkSpeed(AT91PS_EMAC pEmac) 377 * This private function determines the link speed set by the PHY. 378 * .KB_C_FN_DEFINITION_END 379 */ 380static void 381MII_GetLinkSpeed(AT91PS_EMAC pEmac) 382{ 383#if defined(BOOT_TSC) || defined(BOOT_KB920X) || defined(BOOT_CENTIPAD) 384 unsigned short stat2; 385#endif 386 unsigned update; 387#ifdef BOOT_TSC 388 unsigned sec; 389 int i; 390#endif 391#ifdef BOOT_BWCT 392 /* hardcoded link speed since we connect a switch via MII */ 393 update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); 394 update |= AT91C_EMAC_SPD; 395 update |= AT91C_EMAC_FD; 396#endif 397#if defined(BOOT_KB920X) || defined(BOOT_CENTIPAD) 398 stat2 = AT91F_MII_ReadPhy(pEmac, MII_STS2_REG); 399 if (!(stat2 & MII_STS2_LINK)) 400 return ; 401 update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); 402 if (stat2 & MII_STS2_100TX) 403 update |= AT91C_EMAC_SPD; 404 if (stat2 & MII_STS2_FDX) 405 update |= AT91C_EMAC_FD; 406#endif 407#ifdef BOOT_TSC 408 while (1) { 409 for (i = 0; i < 10; i++) { 410 stat2 = AT91F_MII_ReadPhy(pEmac, MII_STS_REG); 411 if (stat2 & MII_STS_LINK_STAT) 412 break; 413 printf("."); 414 sec = GetSeconds(); 415 while (GetSeconds() == sec) 416 continue; 417 } 418 if (stat2 & MII_STS_LINK_STAT) 419 break; 420 printf("Resetting MII..."); 421 AT91F_MII_WritePhy(pEmac, 0x0, 0x8000); 422 while (AT91F_MII_ReadPhy(pEmac, 0x0) & 0x8000) continue; 423 } 424 printf("emac: link"); 425 stat2 = AT91F_MII_ReadPhy(pEmac, MII_SPEC_STS_REG); 426 update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); 427 if (stat2 & (MII_SSTS_100FDX | MII_SSTS_100HDX)) { 428 printf(" 100TX"); 429 update |= AT91C_EMAC_SPD; 430 } 431 if (stat2 & (MII_SSTS_100FDX | MII_SSTS_10FDX)) { 432 printf(" FDX"); 433 update |= AT91C_EMAC_FD; 434 } 435 printf("\n"); 436#endif 437 pEmac->EMAC_CFG = update; 438} 439 440 441/* 442 * .KB_C_FN_DEFINITION_START 443 * void AT91F_EmacEntry(void) 444 * This private function initializes the EMAC on the chip. 445 * .KB_C_FN_DEFINITION_END 446 */ 447static void 448AT91F_EmacEntry(void) 449{ 450 unsigned i; 451 char *pRxPacket = (char*)RX_DATA_START; 452 AT91PS_EMAC pEmac = AT91C_BASE_EMAC; 453 454 p_rxBD = (receive_descriptor_t*)RX_BUFFER_START; 455 localPort = SWAP16(0x8002); 456 457 for (i = 0; i < MAX_RX_PACKETS; ++i) { 458 459 p_rxBD[i].address = (unsigned)pRxPacket; 460 p_rxBD[i].size = 0; 461 pRxPacket += RX_PACKET_SIZE; 462 } 463 464 // Set the WRAP bit at the end of the list descriptor 465 p_rxBD[MAX_RX_PACKETS-1].address |= 0x02; 466 467 if (!(pEmac->EMAC_SR & AT91C_EMAC_LINK)) 468 MII_GetLinkSpeed(pEmac); 469 470 pEmac->EMAC_RBQP = (unsigned) p_rxBD; 471 pEmac->EMAC_RSR |= (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA); 472 pEmac->EMAC_CTL = AT91C_EMAC_TE | AT91C_EMAC_RE; 473 474 pEmac->EMAC_TAR = (unsigned)transmitBuffer; 475} 476 477 478/* ************************** GLOBAL FUNCTIONS ********************************/ 479 480/* 481 * .KB_C_FN_DEFINITION_START 482 * void SetServerIPAddress(unsigned address) 483 * This global function sets the IP of the TFTP download server. 484 * .KB_C_FN_DEFINITION_END 485 */ 486void 487SetServerIPAddress(unsigned address) 488{ 489 // force update in case the IP has changed 490 serverMACSet = 0; 491 492 serverIPAddr[0] = (address >> 24) & 0xFF; 493 serverIPAddr[1] = (address >> 16) & 0xFF; 494 serverIPAddr[2] = (address >> 8) & 0xFF; 495 serverIPAddr[3] = (address >> 0) & 0xFF; 496 497 serverIPSet = 1; 498} 499 500 501/* 502 * .KB_C_FN_DEFINITION_START 503 * void SetLocalIPAddress(unsigned address) 504 * This global function sets the IP of this module. 505 * .KB_C_FN_DEFINITION_END 506 */ 507void 508SetLocalIPAddress(unsigned address) 509{ 510 // force update in case the IP has changed 511 serverMACSet = 0; 512 513 localIPAddr[0] = (address >> 24) & 0xFF; 514 localIPAddr[1] = (address >> 16) & 0xFF; 515 localIPAddr[2] = (address >> 8) & 0xFF; 516 localIPAddr[3] = (address >> 0) & 0xFF; 517 518 localIPSet = 1; 519} 520 521 522/* 523 * .KB_C_FN_DEFINITION_START 524 * void TFTP_Download(unsigned address, char *filename) 525 * This global function initiates and processes a tftp download request. 526 * The server IP, local IP, local MAC must be set before this function is 527 * executed. 528 * .KB_C_FN_DEFINITION_END 529 */ 530void 531TFTP_Download(unsigned address, char *filename) 532{ 533 ip_header_t IpHeader; 534 unsigned thisSeconds; 535 int timeout; 536 537 if ((!localMACSet) || (!localIPSet) || (!serverIPSet)) 538 return ; 539 540 AT91F_EmacEntry(); 541 GetServerAddress(); 542 dlAddress = (char*)address; 543 lastSize = 0; 544 timeout = 10; 545 thisSeconds = (GetSeconds() + 2) % 32; 546 serverPort = SWAP16(69); 547 ++localPort; 548 ackBlock = -1; 549 550 while (timeout) { 551 if (CheckForNewPacket(&IpHeader)) { 552 if (ackBlock == -2) 553 break; 554 timeout = 10; 555 thisSeconds = (GetSeconds() + 2) % 32; 556 } else if (GetSeconds() == thisSeconds) { 557 --timeout; 558 thisSeconds = (GetSeconds() + 2) % 32; 559 if (!serverMACSet) 560 GetServerAddress(); 561 else if (ackBlock == -1) 562 TFTP_RequestFile(filename); 563 else { 564 // Be sure to send a NAK, which is done by 565 // ACKing the last block we got. 566 TFTP_ACK_Data(0, ackBlock, 512); 567 printf("\nNAK %u\n", ackBlock); 568 } 569 } 570 } 571 if (timeout == 0) 572 printf("TFTP TIMEOUT!\n"); 573} 574