]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/boot/arm/at91/libat91/emac.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / sys / boot / arm / at91 / libat91 / emac.c
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 char serverMACAddr[6];
35 static unsigned char localIPAddr[4], serverIPAddr[4];
36 static int      ackBlock;
37 static char *dlAddress;
38
39 static unsigned transmitBuffer[1024 / sizeof(unsigned)];
40 static unsigned tftpSendPacket[256 / sizeof(unsigned)];
41
42 /*
43  * .KB_C_FN_DEFINITION_START
44  * unsigned short IP_checksum(unsigned short *p, int len)
45  *  This private function calculates the IP checksum for various headers.
46  * .KB_C_FN_DEFINITION_END
47  */
48 static unsigned short
49 IP_checksum(unsigned short *p, int len) 
50 {
51         unsigned        i, t;
52
53         len &= ~1;
54
55         for (i=0,t=0; i<len; i+=2, ++p)
56                 t += SWAP16(*p);
57
58         t = (t & 0xffff) + (t >> 16);
59         return (~t);                                                                    
60 }
61
62
63 /*
64  * .KB_C_FN_DEFINITION_START
65  * void GetServerAddress(void)
66  *  This private function sends an ARP request to determine the server MAC.
67  * .KB_C_FN_DEFINITION_END
68  */
69 static void
70 GetServerAddress(void)
71 {
72         arp_header_t    *p_ARP;
73
74         p_ARP = (arp_header_t*)transmitBuffer;
75
76         p_memset((char*)p_ARP->dest_mac, 0xFF, 6);
77
78         p_memcpy((char*)p_ARP->src_mac, (char*)localMACAddr, 6);
79
80         p_ARP->frame_type = SWAP16(PROTOCOL_ARP);
81         p_ARP->hard_type  = SWAP16(1);
82         p_ARP->prot_type  = SWAP16(PROTOCOL_IP);
83         p_ARP->hard_size  = 6;
84         p_ARP->prot_size  = 4;
85         p_ARP->operation  = SWAP16(ARP_REQUEST);
86
87         p_memcpy((char*)p_ARP->sender_mac, (char*)localMACAddr, 6);
88
89         p_memcpy((char*)p_ARP->sender_ip, (char*)localIPAddr, 4);
90
91         p_memset((char*)p_ARP->target_mac, 0, 6);
92
93         p_memcpy((char*)p_ARP->target_ip, (char*)serverIPAddr, 4);
94
95         // wait until transmit is available
96         while (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) ;
97
98         *AT91C_EMAC_TSR |= AT91C_EMAC_COMP;
99         *AT91C_EMAC_TAR = (unsigned)transmitBuffer;
100         *AT91C_EMAC_TCR = 0x40;
101 }
102
103
104 /*
105  * .KB_C_FN_DEFINITION_START
106  * void Send_TFTP_Packet(char *tftpData, unsigned tftpLength)
107  *  This private function initializes and send a TFTP packet.
108  * .KB_C_FN_DEFINITION_END
109  */
110 static void
111 Send_TFTP_Packet(char *tftpData, unsigned tftpLength)
112 {
113         transmit_header_t       *macHdr = (transmit_header_t*)tftpSendPacket;
114         ip_header_t             *ipHdr;
115         udp_header_t            *udpHdr;
116         unsigned                t_checksum;
117
118         p_memcpy((char*)macHdr->dest_mac, (char*)serverMACAddr, 6);
119
120         p_memcpy((char*)macHdr->src_mac, (char*)localMACAddr, 6);
121
122         macHdr->proto_mac = SWAP16(PROTOCOL_IP);
123
124         ipHdr = (ip_header_t*)&macHdr->packet_length;
125
126         ipHdr->ip_v_hl = 0x45;
127         ipHdr->ip_tos = 0;
128         ipHdr->ip_len = SWAP16(28 + tftpLength);
129         ipHdr->ip_id = 0;
130         ipHdr->ip_off = SWAP16(0x4000);
131         ipHdr->ip_ttl = 64;
132         ipHdr->ip_p = PROTOCOL_UDP;
133         ipHdr->ip_sum = 0;
134
135         p_memcpy((char*)ipHdr->ip_src, (char*)localIPAddr, 4);
136
137         p_memcpy((char*)ipHdr->ip_dst, (char*)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         p_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
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 = p_strcpy(cPtr, filename);
186         mPtr = p_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
202 TFTP_ACK_Data(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                 p_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\r\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
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                                                 
253         process = i;
254                 
255         pFrameType = (unsigned short *)((p_rxBD[i].address & 0xFFFFFFFC) + 12);
256         pData      = (char *)(p_rxBD[i].address & 0xFFFFFFFC);
257
258         switch (*pFrameType) {
259
260         case SWAP16(PROTOCOL_ARP):
261                 p_ARP = (arp_header_t*)pData;
262                 if (p_ARP->operation == SWAP16(ARP_REPLY)) {
263                         // check if new server info is available
264                         if ((!serverMACSet) &&
265                                 (!(p_memcmp((char*)p_ARP->sender_ip,
266                                         (char*)serverIPAddr, 4)))) {
267
268                                 serverMACSet = 1;
269
270                                 p_memcpy((char*)serverMACAddr,
271                                         (char*)p_ARP->sender_mac, 6);
272                         }
273                 } else if (p_ARP->operation == SWAP16(ARP_REQUEST)) {
274                         // ARP REPLY operation
275                         p_ARP->operation =  SWAP16(ARP_REPLY);
276
277                         // Fill the dest address and src address
278                         for (i = 0; i <6; i++) {
279                                 // swap ethernet dest address and ethernet src address
280                                 pData[i] = pData[i+6];
281                                 pData[i+6] = localMACAddr[i];
282                                 // swap sender ethernet address and target ethernet address
283                                 pData[i+22] = localMACAddr[i];
284                                 pData[i+32] = pData[i+6];
285                         }                                                                       
286
287                         // swap sender IP address and target IP address
288                         for (i = 0; i<4; i++) {                         
289                                 pData[i+38] = pData[i+28];
290                                 pData[i+28] = localIPAddr[i];
291                         }
292
293                         if (!(*AT91C_EMAC_TSR & AT91C_EMAC_BNQ)) break;
294
295                         *AT91C_EMAC_TSR |= AT91C_EMAC_COMP;
296                         *AT91C_EMAC_TAR = (unsigned)pData;
297                         *AT91C_EMAC_TCR = 0x40;
298                 }
299                 break;
300         case SWAP16(PROTOCOL_IP):
301                 pIpHeader = (ip_header_t*)(pData + 14);                 
302                 p_memcpy((char*)pHeader, (char*)pIpHeader,sizeof(ip_header_t));
303                 
304                 if (pIpHeader->ip_p == PROTOCOL_UDP) {
305                         udp_header_t    *udpHdr;
306                         tftp_header_t   *tftpHdr;
307
308                         udpHdr = (udp_header_t*)((char*)pIpHeader+20);
309                         tftpHdr = (tftp_header_t*)((char*)udpHdr + 8);
310
311                         if (udpHdr->dst_port != localPort)
312                                 break;
313
314                         if (tftpHdr->opcode != TFTP_DATA_OPCODE)
315                                 break;
316
317                         if (ackBlock == -1) {
318                                 if (tftpHdr->block_num != SWAP16(1))
319                                         break;
320                                 serverPort = udpHdr->src_port;
321                                 ackBlock = 0;
322                         }
323
324                         if (serverPort != udpHdr->src_port)
325                                 break;
326
327                         TFTP_ACK_Data(tftpHdr->data,
328                             SWAP16(tftpHdr->block_num),
329                             SWAP16(udpHdr->udp_len) - 12);
330                 }
331         }
332         p_rxBD[process].address &= ~0x01;
333         return (1);
334 }
335
336
337 /*
338  * .KB_C_FN_DEFINITION_START
339  * unsigned short AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr)
340  *  This private function reads the PHY device.
341  * .KB_C_FN_DEFINITION_END
342  */
343 static unsigned short
344 AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr)
345 {
346         unsigned value = 0x60020000 | (addr << 18);
347
348         pEmac->EMAC_CTL |= AT91C_EMAC_MPE;
349         pEmac->EMAC_MAN = value;
350         while(!((pEmac->EMAC_SR) & AT91C_EMAC_IDLE));
351         pEmac->EMAC_CTL &= ~AT91C_EMAC_MPE;
352         return (pEmac->EMAC_MAN & 0x0000ffff);
353 }
354
355 /*
356  * .KB_C_FN_DEFINITION_START
357  * unsigned short AT91F_MII_ReadPhy (AT91PS_EMAC pEmac, unsigned char addr)
358  *  This private function reads the PHY device.
359  * .KB_C_FN_DEFINITION_END
360  */
361 #ifdef BOOT_TSC
362 static unsigned short
363 AT91F_MII_WritePhy (AT91PS_EMAC pEmac, unsigned char addr, unsigned short s)
364 {
365         unsigned value = 0x50020000 | (addr << 18) | s;
366
367         pEmac->EMAC_CTL |= AT91C_EMAC_MPE;
368         pEmac->EMAC_MAN = value;
369         while(!((pEmac->EMAC_SR) & AT91C_EMAC_IDLE));
370         pEmac->EMAC_CTL &= ~AT91C_EMAC_MPE;
371         return (pEmac->EMAC_MAN & 0x0000ffff);
372 }
373 #endif
374
375 /*
376  * .KB_C_FN_DEFINITION_START
377  * void MII_GetLinkSpeed(AT91PS_EMAC pEmac)
378  *  This private function determines the link speed set by the PHY.
379  * .KB_C_FN_DEFINITION_END
380  */
381 static void
382 MII_GetLinkSpeed(AT91PS_EMAC pEmac)
383 {
384         unsigned short stat2; 
385         unsigned update;
386 #ifdef BOOT_TSC
387         unsigned sec;
388         int i;
389 #endif
390 #ifdef BOOT_KB9202
391         stat2 = AT91F_MII_ReadPhy(pEmac, MII_STS2_REG);
392         if (!(stat2 & MII_STS2_LINK))
393                 return ;
394         update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
395         if (stat2 & MII_STS2_100TX)
396                 update |= AT91C_EMAC_SPD;
397         if (stat2 & MII_STS2_FDX)
398                 update |= AT91C_EMAC_FD;
399 #endif
400 #ifdef BOOT_TSC
401         while (1) {
402                 for (i = 0; i < 10; i++) {
403                         stat2 = AT91F_MII_ReadPhy(pEmac, MII_STS_REG);
404                         if (stat2 & MII_STS_LINK_STAT)
405                                 break;
406                         printf(".");
407                         sec = GetSeconds();
408                         while (GetSeconds() <= sec) continue;
409                 }
410                 if (stat2 & MII_STS_LINK_STAT)
411                         break;
412                 printf("Resetting MII...");
413                 AT91F_MII_WritePhy(pEmac, 0x0, 0x8000);
414                 while (AT91F_MII_ReadPhy(pEmac, 0x0) & 0x8000) continue;
415         }
416         printf("emac: link");
417         stat2 = AT91F_MII_ReadPhy(pEmac, MII_SPEC_STS_REG);
418         update = pEmac->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
419         if (stat2 & (MII_SSTS_100FDX | MII_SSTS_100HDX)) {
420                 printf(" 100TX");
421                 update |= AT91C_EMAC_SPD;
422         }
423         if (stat2 & (MII_SSTS_100FDX | MII_SSTS_10FDX)) {
424                 printf(" FDX");
425                 update |= AT91C_EMAC_FD;
426         }
427         printf("\r\n");
428 #endif
429         pEmac->EMAC_CFG = update;
430 }
431
432
433 /*
434  * .KB_C_FN_DEFINITION_START
435  * void AT91F_EmacEntry(void)
436  *  This private function initializes the EMAC on the chip.
437  * .KB_C_FN_DEFINITION_END
438  */
439 static void
440 AT91F_EmacEntry(void)
441 {
442         unsigned        i;
443         char            *pRxPacket = (char*)RX_DATA_START;
444         AT91PS_EMAC     pEmac = AT91C_BASE_EMAC;
445
446         for (i = 0; i < MAX_RX_PACKETS; ++i) {
447
448                 p_rxBD[i].address = (unsigned)pRxPacket;
449                 p_rxBD[i].size = 0;
450                 pRxPacket += RX_PACKET_SIZE;
451         }
452         
453         // Set the WRAP bit at the end of the list descriptor
454         p_rxBD[MAX_RX_PACKETS-1].address |= 0x02;
455
456         if (!(pEmac->EMAC_SR & AT91C_EMAC_LINK))
457                 MII_GetLinkSpeed(pEmac);
458
459         pEmac->EMAC_RBQP = (unsigned) p_rxBD;
460         pEmac->EMAC_RSR  |= (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
461         pEmac->EMAC_CTL  = AT91C_EMAC_TE | AT91C_EMAC_RE;
462
463         pEmac->EMAC_TAR = (unsigned)transmitBuffer;
464 }       
465
466
467 /* ************************** GLOBAL FUNCTIONS ********************************/
468
469 /*
470  * .KB_C_FN_DEFINITION_START
471  * void SetServerIPAddress(unsigned address)
472  *  This global function sets the IP of the TFTP download server.
473  * .KB_C_FN_DEFINITION_END
474  */
475 void
476 SetServerIPAddress(unsigned address)
477 {
478         // force update in case the IP has changed
479         serverMACSet = 0;
480
481         serverIPAddr[0] = (address >> 24) & 0xFF;
482         serverIPAddr[1] = (address >> 16) & 0xFF;
483         serverIPAddr[2] = (address >>  8) & 0xFF;
484         serverIPAddr[3] = (address >>  0) & 0xFF;
485
486         serverIPSet = 1;
487 }
488
489
490 /*
491  * .KB_C_FN_DEFINITION_START
492  * void SetLocalIPAddress(unsigned address)
493  *  This global function sets the IP of this module.
494  * .KB_C_FN_DEFINITION_END
495  */
496 void
497 SetLocalIPAddress(unsigned address)
498 {
499         // force update in case the IP has changed
500         serverMACSet = 0;
501
502         localIPAddr[0] = (address >> 24) & 0xFF;
503         localIPAddr[1] = (address >> 16) & 0xFF;
504         localIPAddr[2] = (address >>  8) & 0xFF;
505         localIPAddr[3] = (address >>  0) & 0xFF;
506
507         localIPSet = 1;
508 }
509
510
511 /*
512  * .KB_C_FN_DEFINITION_START
513  * void TFTP_Download(unsigned address, char *filename)
514  *  This global function initiates and processes a tftp download request.
515  * The server IP, local IP, local MAC must be set before this function is
516  * executed.
517  * .KB_C_FN_DEFINITION_END
518  */
519 void
520 TFTP_Download(unsigned address, char *filename)
521 {
522         ip_header_t     IpHeader;
523         unsigned        thisSeconds;
524         int             timeout;
525
526         if ((!localMACSet) || (!localIPSet) || (!serverIPSet))
527                 return ;
528
529         AT91F_EmacEntry();
530         GetServerAddress();
531         dlAddress = (char*)address;
532         lastSize = 0;
533         timeout = 10;
534         thisSeconds = GetSeconds() + 1;
535         serverPort = SWAP16(69);
536         ++localPort;
537         ackBlock = -1;
538
539         while (timeout) {
540                 if (CheckForNewPacket(&IpHeader)) {
541                         if (ackBlock == -2)
542                                 break;
543                         timeout = 10;
544                         thisSeconds = GetSeconds() + 1;
545                 } else if (GetSeconds() > thisSeconds) {
546                         --timeout;
547                         thisSeconds = GetSeconds() + 1;
548                         if (!serverMACSet)
549                                 GetServerAddress();
550                         else if (ackBlock == -1)
551                                 TFTP_RequestFile(filename);
552                         else {
553                                 // Be sure to send a NAK, which is done by
554                                 // ACKing the last block we got.
555                                 TFTP_ACK_Data(0, ackBlock, 512);
556                                 printf("\nNAK %u\r\n", ackBlock);
557                         }
558                 }
559         }
560         if (timeout == 0)
561                 printf("TFTP TIMEOUT!\r\n");
562 }