xref: /trueos/sys/boot/arm/at91/libat91/emac.c (revision 0dafbe1e19029614ba170c486057cf10e1091ca9)
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