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