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$
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
34 static receive_descriptor_t *p_rxBD;
35 static unsigned short localPort;
36 static unsigned short serverPort;
37 static unsigned serverMACSet;
38 static unsigned localIPSet, serverIPSet;
39 static unsigned lastSize;
40 static unsigned char serverMACAddr[6];
41 static unsigned char localIPAddr[4], serverIPAddr[4];
42 static int ackBlock;
43 static char *dlAddress;
44
45 static unsigned transmitBuffer[1024 / sizeof(unsigned)];
46 static 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 */
54 static unsigned short
IP_checksum(unsigned short * p,int len)55 IP_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 */
75 static void
GetServerAddress(void)76 GetServerAddress(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 */
113 static void
Send_TFTP_Packet(char * tftpData,unsigned tftpLength)114 Send_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 */
174 static void
TFTP_RequestFile(char * filename)175 TFTP_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 */
201 static void
TFTP_ACK_Data(unsigned char * data,unsigned short block_num,unsigned short len)202 TFTP_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 */
231 static int
CheckForNewPacket(ip_header_t * pHeader)232 CheckForNewPacket(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
341 static unsigned short
AT91F_MII_ReadPhy(AT91PS_EMAC pEmac,unsigned char addr)342 AT91F_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
361 static unsigned short
AT91F_MII_WritePhy(AT91PS_EMAC pEmac,unsigned char addr,unsigned short s)362 AT91F_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 */
380 static void
MII_GetLinkSpeed(AT91PS_EMAC pEmac)381 MII_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 */
447 static void
AT91F_EmacEntry(void)448 AT91F_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 */
486 void
SetServerIPAddress(unsigned address)487 SetServerIPAddress(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 */
507 void
SetLocalIPAddress(unsigned address)508 SetLocalIPAddress(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 */
530 void
TFTP_Download(unsigned address,char * filename)531 TFTP_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