-
Notifications
You must be signed in to change notification settings - Fork 1
/
tcpip.c
958 lines (817 loc) · 35.1 KB
/
tcpip.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
/******************************************************************
***** *****
***** Name: tcpip.c *****
***** Ver.: 1.0 *****
***** Date: 07/05/2001 *****
***** Auth: Andreas Dannenberg *****
***** HTWK Leipzig *****
***** university of applied sciences *****
***** Germany *****
***** Func: implements the TCP/IP-stack and provides a *****
***** simple API to the user *****
***** *****
******************************************************************/
#include "tcpip.h"
#include "EMAC.h" // Keil: Line added
#include <string.h> // Keil: Line added
#include "LPC17xx.h" // Keil: Register definition file for LPC17xx
const unsigned char MyMAC[6] = // "M1-M2-M3-M4-M5-M6"
{
MYMAC_6, MYMAC_5, MYMAC_4,
MYMAC_3, MYMAC_2, MYMAC_1
};
// easyWEB-API function
// initalizes the LAN-controller, reset flags, starts timer-ISR
void TCPLowLevelInit(void)
{
Init_EMAC();
TransmitControl = 0;
TCPFlags = 0;
TCPStateMachine = CLOSED;
SocketStatus = 0;
}
// easyWEB-API function
// does a passive open (listen on 'MyIP:TCPLocalPort' for an incoming
// connection)
void TCPPassiveOpen(void)
{
if (TCPStateMachine == CLOSED)
{
TCPFlags &= ~TCP_ACTIVE_OPEN; // let's do a passive open!
TCPStateMachine = LISTENING;
SocketStatus = SOCK_ACTIVE; // reset, socket now active
}
}
// easyWEB-API function
// does an active open (tries to establish a connection between
// 'MyIP:TCPLocalPort' and 'RemoteIP:TCPRemotePort')
void TCPActiveOpen(void)
{
if ((TCPStateMachine == CLOSED) || (TCPStateMachine == LISTENING))
{
TCPFlags |= TCP_ACTIVE_OPEN; // let's do an active open!
TCPFlags &= ~IP_ADDR_RESOLVED; // we haven't opponents MAC yet
PrepareARP_REQUEST(); // ask for MAC by sending a broadcast
LastFrameSent = ARP_REQUEST;
TCPStartRetryTimer();
SocketStatus = SOCK_ACTIVE; // reset, socket now active
}
}
// easyWEB-API function
// closes an open connection
void TCPClose(void)
{
switch (TCPStateMachine)
{
case LISTENING :
case SYN_SENT :
{
TCPStateMachine = CLOSED;
TCPFlags = 0;
SocketStatus = 0;
break;
}
case SYN_RECD :
case ESTABLISHED :
{
TCPFlags |= TCP_CLOSE_REQUESTED;
break;
}
}
}
// easyWEB-API function
// releases the receive-buffer and allows easyWEB to store new data
// NOTE: rx-buffer MUST be released periodically, else the other TCP
// get no ACKs for the data it sent
void TCPReleaseRxBuffer(void)
{
SocketStatus &= ~SOCK_DATA_AVAILABLE;
}
// easyWEB-API function
// transmitts data stored in 'TCP_TX_BUF'
// NOTE: * number of bytes to transmit must have been written to 'TCPTxDataCount'
// * data-count MUST NOT exceed 'MAX_TCP_TX_DATA_SIZE'
void TCPTransmitTxBuffer(void)
{
if ((TCPStateMachine == ESTABLISHED) || (TCPStateMachine == CLOSE_WAIT))
if (SocketStatus & SOCK_TX_BUF_RELEASED)
{
SocketStatus &= ~SOCK_TX_BUF_RELEASED; // occupy tx-buffer
TCPUNASeqNr += TCPTxDataCount; // advance UNA
TxFrame1Size = ETH_HEADER_SIZE + IP_HEADER_SIZE + TCP_HEADER_SIZE + TCPTxDataCount;
TransmitControl |= SEND_FRAME1;
LastFrameSent = TCP_DATA_FRAME;
TCPStartRetryTimer();
}
}
// Reads the length of the received ethernet frame and checks if the
// destination address is a broadcast message or not
unsigned int IsBroadcast(void) {
unsigned short RecdDestMAC[3]; // 48 bit MAC
RecdFrameLength = StartReadFrame();
CopyFromFrame_EMAC(&RecdDestMAC, 6); // receive DA to see if it was a broadcast
CopyFromFrame_EMAC(&RecdFrameMAC, 6); // store SA (for our answer)
if ((RecdDestMAC[0] == 0xFFFF) &&
(RecdDestMAC[1] == 0xFFFF) &&
(RecdDestMAC[2] == 0xFFFF)) {
return(1);
} else {
return (0);
}
}
// easyWEB's 'main()'-function
// must be called from user program periodically (the often - the better)
// handles network, TCP/IP-stack and user events
void DoNetworkStuff(void)
{
if (CheckFrameReceived()) // Packet received
{
if (IsBroadcast()) {
ProcessEthBroadcastFrame();
} else {
ProcessEthIAFrame();
}
EndReadFrame(); // release buffer in ethernet controller
}
if (TCPFlags & TCP_TIMER_RUNNING)
if (TCPFlags & TIMER_TYPE_RETRY)
{
if (TCPTimer > RETRY_TIMEOUT)
{
TCPRestartTimer(); // set a new timeout
if (RetryCounter)
{
TCPHandleRetransmission(); // resend last frame
RetryCounter--;
}
else
{
TCPStopTimer();
TCPHandleTimeout();
}
}
}
else if (TCPTimer > FIN_TIMEOUT)
{
TCPStateMachine = CLOSED;
TCPFlags = 0; // reset all flags, stop retransmission...
SocketStatus &= SOCK_DATA_AVAILABLE; // clear all flags but data available
}
switch (TCPStateMachine)
{
case CLOSED :
case LISTENING :
{
if (TCPFlags & TCP_ACTIVE_OPEN) // stack has to open a connection?
if (TCPFlags & IP_ADDR_RESOLVED) // IP resolved?
if (!(TransmitControl & SEND_FRAME2)) // buffer free?
{
// TCPSeqNr = ((unsigned long)ISNGenHigh << 16) | (T0TC & 0xFFFF); // Keil: changed from TAR to T0TC;
TCPSeqNr = ((unsigned long)ISNGenHigh << 16) | (LPC_TIM0->TC & 0xFFFF); // Keil: changed from TAR to T0TC;
// set local ISN
TCPUNASeqNr = TCPSeqNr;
TCPAckNr = 0; // we don't know what to ACK!
TCPUNASeqNr++; // count SYN as a byte
PrepareTCP_FRAME(TCP_CODE_SYN); // send SYN frame
LastFrameSent = TCP_SYN_FRAME;
TCPStartRetryTimer(); // we NEED a retry-timeout
TCPStateMachine = SYN_SENT;
}
break;
}
case SYN_RECD :
case ESTABLISHED :
{
if (TCPFlags & TCP_CLOSE_REQUESTED) // user has user initated a close?
if (!(TransmitControl & (SEND_FRAME2 | SEND_FRAME1))) // buffers free?
if (TCPSeqNr == TCPUNASeqNr) // all data ACKed?
{
TCPUNASeqNr++;
PrepareTCP_FRAME(TCP_CODE_FIN | TCP_CODE_ACK);
LastFrameSent = TCP_FIN_FRAME;
TCPStartRetryTimer();
TCPStateMachine = FIN_WAIT_1;
}
break;
}
case CLOSE_WAIT :
{
if (!(TransmitControl & (SEND_FRAME2 | SEND_FRAME1))) // buffers free?
if (TCPSeqNr == TCPUNASeqNr) // all data ACKed?
{
TCPUNASeqNr++; // count FIN as a byte
PrepareTCP_FRAME(TCP_CODE_FIN | TCP_CODE_ACK); // we NEED a retry-timeout
LastFrameSent = TCP_FIN_FRAME; // time to say goodbye...
TCPStartRetryTimer();
TCPStateMachine = LAST_ACK;
}
break;
}
}
if (TransmitControl & SEND_FRAME2)
{
RequestSend(TxFrame2Size);
if (Rdy4Tx()) // NOTE: when using a very fast MCU, maybe
SendFrame2(); // the EMAC isn't ready yet, include
else { // a kind of timer or counter here
TCPStateMachine = CLOSED;
SocketStatus = SOCK_ERR_ETHERNET; // indicate an error to user
TCPFlags = 0; // clear all flags, stop timers etc.
}
TransmitControl &= ~SEND_FRAME2; // clear tx-flag
}
if (TransmitControl & SEND_FRAME1)
{
PrepareTCP_DATA_FRAME(); // build frame w/ actual SEQ, ACK....
RequestSend(TxFrame1Size);
if (Rdy4Tx()) // EMAC ready to accept our frame?
SendFrame1(); // (see note above)
else {
TCPStateMachine = CLOSED;
SocketStatus = SOCK_ERR_ETHERNET; // indicate an error to user
TCPFlags = 0; // clear all flags, stop timers etc.
}
TransmitControl &= ~SEND_FRAME1; // clear tx-flag
}
}
// easyWEB internal function
// handles an incoming broadcast frame
void ProcessEthBroadcastFrame(void)
{
unsigned short TargetIP[2];
if (ReadFrameBE_EMAC() == FRAME_ARP) // get frame type, check for ARP
if (ReadFrameBE_EMAC() == HARDW_ETH10) // Ethernet frame
if (ReadFrameBE_EMAC() == FRAME_IP) // check protocol
if (ReadFrameBE_EMAC() == IP_HLEN_PLEN) // check HLEN, PLEN
if (ReadFrameBE_EMAC() == OP_ARP_REQUEST)
{
DummyReadFrame_EMAC(6); // ignore sender's hardware address
CopyFromFrame_EMAC(&RecdFrameIP, 4); // read sender's protocol address
DummyReadFrame_EMAC(6); // ignore target's hardware address
CopyFromFrame_EMAC(&TargetIP, 4); // read target's protocol address
if (!memcmp(&MyIP, &TargetIP, 4)) // is it for us?
PrepareARP_ANSWER(); // yes->create ARP_ANSWER frame
}
}
// easyWEB internal function
// handles an incoming frame that passed EMAC's address filter
// (individual addressed = IA)
void ProcessEthIAFrame(void)
{
unsigned short TargetIP[2];
unsigned char ProtocolType;
switch (ReadFrameBE_EMAC()) // get frame type
{
case FRAME_ARP : // check for ARP
{
if ((TCPFlags & (TCP_ACTIVE_OPEN | IP_ADDR_RESOLVED)) == TCP_ACTIVE_OPEN)
if (ReadFrameBE_EMAC() == HARDW_ETH10) // check for the right prot. etc.
if (ReadFrameBE_EMAC() == FRAME_IP)
if (ReadFrameBE_EMAC() == IP_HLEN_PLEN)
if (ReadFrameBE_EMAC() == OP_ARP_ANSWER)
{
TCPStopTimer(); // OK, now we've the MAC we wanted ;-)
CopyFromFrame_EMAC(&RemoteMAC, 6); // extract opponents MAC
TCPFlags |= IP_ADDR_RESOLVED;
}
break;
}
case FRAME_IP : // check for IP-type
{
if ((ReadFrameBE_EMAC() & 0xFF00 ) == IP_VER_IHL) // IPv4, IHL=5 (20 Bytes Header)
{ // ignore Type Of Service
RecdIPFrameLength = ReadFrameBE_EMAC(); // get IP frame's length
ReadFrameBE_EMAC(); // ignore identification
if (!(ReadFrameBE_EMAC() & (IP_FLAG_MOREFRAG | IP_FRAGOFS_MASK))) // only unfragm. frames
{
ProtocolType = ReadFrameBE_EMAC() & 0xFF; // get protocol, ignore TTL
ReadFrameBE_EMAC(); // ignore checksum
CopyFromFrame_EMAC(&RecdFrameIP, 4); // get source IP
CopyFromFrame_EMAC(&TargetIP, 4); // get destination IP
if (!memcmp(&MyIP, &TargetIP, 4)) // is it for us?
switch (ProtocolType) {
case PROT_ICMP : { ProcessICMPFrame(); break; }
case PROT_TCP : { ProcessTCPFrame(); break; }
case PROT_UDP : break; // not implemented!
}
}
}
break;
}
}
}
// easyWEB internal function
// we've just rec'd an ICMP-frame (Internet Control Message Protocol)
// check what to do and branch to the appropriate sub-function
void ProcessICMPFrame(void)
{
unsigned short ICMPTypeAndCode;
ICMPTypeAndCode = ReadFrameBE_EMAC(); // get Message Type and Code
ReadFrameBE_EMAC(); // ignore ICMP checksum
switch (ICMPTypeAndCode >> 8) { // check type
case ICMP_ECHO : // is echo request?
{
PrepareICMP_ECHO_REPLY(); // echo as much as we can...
break;
}
}
}
// easyWEB internal function
// we've just rec'd an TCP-frame (Transmission Control Protocol)
// this function mainly implements the TCP state machine according to RFC793
void ProcessTCPFrame(void)
{
unsigned short TCPSegSourcePort; // segment's source port
unsigned short TCPSegDestPort; // segment's destination port
unsigned long TCPSegSeq; // segment's sequence number
unsigned long TCPSegAck; // segment's acknowledge number
unsigned short TCPCode; // TCP code and header length
unsigned char TCPHeaderSize; // real TCP header length
unsigned short NrOfDataBytes; // real number of data
TCPSegSourcePort = ReadFrameBE_EMAC(); // get ports
TCPSegDestPort = ReadFrameBE_EMAC();
if (TCPSegDestPort != TCPLocalPort) return; // drop segment if port doesn't match
TCPSegSeq = (unsigned long)ReadFrameBE_EMAC() << 16; // get segment sequence nr.
TCPSegSeq |= ReadFrameBE_EMAC();
TCPSegAck = (unsigned long)ReadFrameBE_EMAC() << 16; // get segment acknowledge nr.
TCPSegAck |= ReadFrameBE_EMAC();
TCPCode = ReadFrameBE_EMAC(); // get control bits, header length...
TCPHeaderSize = (TCPCode & DATA_OFS_MASK) >> 10; // header length in bytes
NrOfDataBytes = RecdIPFrameLength - IP_HEADER_SIZE - TCPHeaderSize; // seg. text length
if (NrOfDataBytes > MAX_TCP_RX_DATA_SIZE) return; // packet too large for us :...-(
if (TCPHeaderSize > TCP_HEADER_SIZE) // ignore options if any
DummyReadFrame_EMAC(TCPHeaderSize - TCP_HEADER_SIZE);
switch (TCPStateMachine) // implement the TCP state machine
{
case CLOSED :
{
if (!(TCPCode & TCP_CODE_RST))
{
TCPRemotePort = TCPSegSourcePort;
memcpy(&RemoteMAC, &RecdFrameMAC, 6); // save opponents MAC and IP
memcpy(&RemoteIP, &RecdFrameIP, 4); // for later use
if (TCPCode & TCP_CODE_ACK) // make the reset sequence
{ // acceptable to the other
TCPSeqNr = TCPSegAck; // TCP
PrepareTCP_FRAME(TCP_CODE_RST);
}
else
{
TCPSeqNr = 0;
TCPAckNr = TCPSegSeq + NrOfDataBytes;
if (TCPCode & (TCP_CODE_SYN | TCP_CODE_FIN)) TCPAckNr++;
PrepareTCP_FRAME(TCP_CODE_RST | TCP_CODE_ACK);
}
}
break;
}
case LISTENING :
{
if (!(TCPCode & TCP_CODE_RST)) // ignore segment containing RST
{
TCPRemotePort = TCPSegSourcePort;
memcpy(&RemoteMAC, &RecdFrameMAC, 6); // save opponents MAC and IP
memcpy(&RemoteIP, &RecdFrameIP, 4); // for later use
if (TCPCode & TCP_CODE_ACK) // reset a bad
{ // acknowledgement
TCPSeqNr = TCPSegAck;
PrepareTCP_FRAME(TCP_CODE_RST);
}
else if (TCPCode & TCP_CODE_SYN)
{
TCPAckNr = TCPSegSeq + 1; // get remote ISN, next byte we expect
// TCPSeqNr = ((unsigned long)ISNGenHigh << 16) | (T0TC & 0xFFFF); // Keil: changed from TAR to T0TC;
TCPSeqNr = ((unsigned long)ISNGenHigh << 16) | (LPC_TIM0->TC & 0xFFFF); // Keil: changed from TAR to T0TC;
// set local ISN
TCPUNASeqNr = TCPSeqNr + 1; // one byte out -> increase by one
PrepareTCP_FRAME(TCP_CODE_SYN | TCP_CODE_ACK);
LastFrameSent = TCP_SYN_ACK_FRAME;
TCPStartRetryTimer();
TCPStateMachine = SYN_RECD;
}
}
break;
}
case SYN_SENT :
{
if (memcmp(&RemoteIP, &RecdFrameIP, 4)) break; // drop segment if its IP doesn't belong
// to current session
if (TCPSegSourcePort != TCPRemotePort) break; // drop segment if port doesn't match
if (TCPCode & TCP_CODE_ACK) // ACK field significant?
if (TCPSegAck != TCPUNASeqNr) // is our ISN ACKed?
{
if (!(TCPCode & TCP_CODE_RST))
{
TCPSeqNr = TCPSegAck;
PrepareTCP_FRAME(TCP_CODE_RST);
}
break; // drop segment
}
if (TCPCode & TCP_CODE_RST) // RST??
{
if (TCPCode & TCP_CODE_ACK) // if ACK was acceptable, reset
{ // connection
TCPStateMachine = CLOSED;
TCPFlags = 0; // reset all flags, stop retransmission...
SocketStatus = SOCK_ERR_CONN_RESET;
}
break; // drop segment
}
if (TCPCode & TCP_CODE_SYN) // SYN??
{
TCPAckNr = TCPSegSeq; // get opponents ISN
TCPAckNr++; // inc. by one...
if (TCPCode & TCP_CODE_ACK)
{
TCPStopTimer(); // stop retransmission, other TCP got our SYN
TCPSeqNr = TCPUNASeqNr; // advance our sequence number
PrepareTCP_FRAME(TCP_CODE_ACK); // ACK this ISN
TCPStateMachine = ESTABLISHED;
SocketStatus |= SOCK_CONNECTED;
SocketStatus |= SOCK_TX_BUF_RELEASED; // user may send data now :-)
}
else
{
TCPStopTimer();
PrepareTCP_FRAME(TCP_CODE_SYN | TCP_CODE_ACK); // our SYN isn't ACKed yet,
LastFrameSent = TCP_SYN_ACK_FRAME; // now continue with sending
TCPStartRetryTimer(); // SYN_ACK frames
TCPStateMachine = SYN_RECD;
}
}
break;
}
default :
{
if (memcmp(&RemoteIP, &RecdFrameIP, 4)) break; // drop segment if IP doesn't belong
// to current session
if (TCPSegSourcePort != TCPRemotePort) break; // drop segment if port doesn't match
if (TCPSegSeq != TCPAckNr) break; // drop if it's not the segment we expect
if (TCPCode & TCP_CODE_RST) // RST??
{
TCPStateMachine = CLOSED; // close the state machine
TCPFlags = 0; // reset all flags, stop retransmission...
SocketStatus = SOCK_ERR_CONN_RESET; // indicate an error to user
break;
}
if (TCPCode & TCP_CODE_SYN) // SYN??
{
PrepareTCP_FRAME(TCP_CODE_RST); // is NOT allowed here! send a reset,
TCPStateMachine = CLOSED; // close connection...
TCPFlags = 0; // reset all flags, stop retransmission...
SocketStatus = SOCK_ERR_REMOTE; // fatal error!
break; // ...and drop the frame
}
if (!(TCPCode & TCP_CODE_ACK)) break; // drop segment if the ACK bit is off
if (TCPSegAck == TCPUNASeqNr) // is our last data sent ACKed?
{
TCPStopTimer(); // stop retransmission
TCPSeqNr = TCPUNASeqNr; // advance our sequence number
switch (TCPStateMachine) // change state if necessary
{
case SYN_RECD : // ACK of our SYN?
{
TCPStateMachine = ESTABLISHED; // user may send data now :-)
SocketStatus |= SOCK_CONNECTED;
break;
}
case FIN_WAIT_1 : { TCPStateMachine = FIN_WAIT_2; break; } // ACK of our FIN?
case CLOSING : { TCPStateMachine = TIME_WAIT; break; } // ACK of our FIN?
case LAST_ACK : // ACK of our FIN?
{
TCPStateMachine = CLOSED;
TCPFlags = 0; // reset all flags, stop retransmission...
SocketStatus &= SOCK_DATA_AVAILABLE; // clear all flags but data available
break;
}
case TIME_WAIT :
{
PrepareTCP_FRAME(TCP_CODE_ACK); // ACK a retransmission of remote FIN
TCPRestartTimer(); // restart TIME_WAIT timeout
break;
}
}
if (TCPStateMachine == ESTABLISHED) // if true, give the frame buffer back
SocketStatus |= SOCK_TX_BUF_RELEASED; // to user
}
if ((TCPStateMachine == ESTABLISHED) || (TCPStateMachine == FIN_WAIT_1) || (TCPStateMachine == FIN_WAIT_2))
if (NrOfDataBytes) // data available?
if (!(SocketStatus & SOCK_DATA_AVAILABLE)) // rx data-buffer empty?
{
DummyReadFrame_EMAC(6); // ignore window, checksum, urgent pointer
CopyFromFrame_EMAC(RxTCPBuffer, NrOfDataBytes);// fetch data and
TCPRxDataCount = NrOfDataBytes; // ...tell the user...
SocketStatus |= SOCK_DATA_AVAILABLE; // indicate the new data to user
TCPAckNr += NrOfDataBytes;
PrepareTCP_FRAME(TCP_CODE_ACK); // ACK rec'd data
}
if (TCPCode & TCP_CODE_FIN) // FIN??
{
switch (TCPStateMachine)
{
case SYN_RECD :
case ESTABLISHED :
{
TCPStateMachine = CLOSE_WAIT;
break;
}
case FIN_WAIT_1 :
{ // if our FIN was ACKed, we automatically
TCPStateMachine = CLOSING; // enter FIN_WAIT_2 (look above) and therefore
SocketStatus &= ~SOCK_CONNECTED; // TIME_WAIT
break;
}
case FIN_WAIT_2 :
{
TCPStartTimeWaitTimer();
TCPStateMachine = TIME_WAIT;
SocketStatus &= ~SOCK_CONNECTED;
break;
}
case TIME_WAIT :
{
TCPRestartTimer();
break;
}
}
TCPAckNr++; // ACK remote's FIN flag
PrepareTCP_FRAME(TCP_CODE_ACK);
}
}
}
}
// easyWEB internal function
// prepares the TxFrame2-buffer to send an ARP-request
void PrepareARP_REQUEST(void)
{
// Ethernet
memset(&TxFrame2[ETH_DA_OFS], (char)0xFF, 6); // we don't know opposites MAC!
memcpy(&TxFrame2[ETH_SA_OFS], &MyMAC, 6);
*(unsigned short *)&TxFrame2[ETH_TYPE_OFS] = SWAPB(FRAME_ARP);
// ARP
*(unsigned short *)&TxFrame2[ARP_HARDW_OFS] = SWAPB(HARDW_ETH10);
*(unsigned short *)&TxFrame2[ARP_PROT_OFS] = SWAPB(FRAME_IP);
*(unsigned short *)&TxFrame2[ARP_HLEN_PLEN_OFS] = SWAPB(IP_HLEN_PLEN);
*(unsigned short *)&TxFrame2[ARP_OPCODE_OFS] = SWAPB(OP_ARP_REQUEST);
memcpy(&TxFrame2[ARP_SENDER_HA_OFS], &MyMAC, 6);
memcpy(&TxFrame2[ARP_SENDER_IP_OFS], &MyIP, 4);
memset(&TxFrame2[ARP_TARGET_HA_OFS], 0x00, 6); // we don't know opposites MAC!
if (((RemoteIP[0] ^ MyIP[0]) & SubnetMask[0]) || ((RemoteIP[1] ^ MyIP[1]) & SubnetMask[1]))
memcpy(&TxFrame2[ARP_TARGET_IP_OFS], &GatewayIP, 4); // IP not in subnet, use gateway
else
memcpy(&TxFrame2[ARP_TARGET_IP_OFS], &RemoteIP, 4); // other IP is next to us...
TxFrame2Size = ETH_HEADER_SIZE + ARP_FRAME_SIZE;
TransmitControl |= SEND_FRAME2;
}
// easyWEB internal function
// prepares the TxFrame2-buffer to send an ARP-answer (reply)
void PrepareARP_ANSWER(void)
{
// Ethernet
memcpy(&TxFrame2[ETH_DA_OFS], &RecdFrameMAC, 6);
memcpy(&TxFrame2[ETH_SA_OFS], &MyMAC, 6);
*(unsigned short *)&TxFrame2[ETH_TYPE_OFS] = SWAPB(FRAME_ARP);
// ARP
*(unsigned short *)&TxFrame2[ARP_HARDW_OFS] = SWAPB(HARDW_ETH10);
*(unsigned short *)&TxFrame2[ARP_PROT_OFS] = SWAPB(FRAME_IP);
*(unsigned short *)&TxFrame2[ARP_HLEN_PLEN_OFS] = SWAPB(IP_HLEN_PLEN);
*(unsigned short *)&TxFrame2[ARP_OPCODE_OFS] = SWAPB(OP_ARP_ANSWER);
memcpy(&TxFrame2[ARP_SENDER_HA_OFS], &MyMAC, 6);
memcpy(&TxFrame2[ARP_SENDER_IP_OFS], &MyIP, 4);
memcpy(&TxFrame2[ARP_TARGET_HA_OFS], &RecdFrameMAC, 6);
memcpy(&TxFrame2[ARP_TARGET_IP_OFS], &RecdFrameIP, 4);
TxFrame2Size = ETH_HEADER_SIZE + ARP_FRAME_SIZE;
TransmitControl |= SEND_FRAME2;
}
// easyWEB internal function
// prepares the TxFrame2-buffer to send an ICMP-echo-reply
void PrepareICMP_ECHO_REPLY(void)
{
unsigned short ICMPDataCount;
if (RecdIPFrameLength > MAX_ETH_TX_DATA_SIZE) // don't overload TX-buffer
ICMPDataCount = MAX_ETH_TX_DATA_SIZE - IP_HEADER_SIZE - ICMP_HEADER_SIZE;
else
ICMPDataCount = RecdIPFrameLength - IP_HEADER_SIZE - ICMP_HEADER_SIZE;
// Ethernet
memcpy(&TxFrame2[ETH_DA_OFS], &RecdFrameMAC, 6);
memcpy(&TxFrame2[ETH_SA_OFS], &MyMAC, 6);
*(unsigned short *)&TxFrame2[ETH_TYPE_OFS] = SWAPB(FRAME_IP);
// IP
*(unsigned short *)&TxFrame2[IP_VER_IHL_TOS_OFS] = SWAPB(IP_VER_IHL);
WriteWBE(&TxFrame2[IP_TOTAL_LENGTH_OFS], IP_HEADER_SIZE + ICMP_HEADER_SIZE + ICMPDataCount);
*(unsigned short *)&TxFrame2[IP_IDENT_OFS] = 0;
*(unsigned short *)&TxFrame2[IP_FLAGS_FRAG_OFS] = 0;
*(unsigned short *)&TxFrame2[IP_TTL_PROT_OFS] = SWAPB((DEFAULT_TTL << 8) | PROT_ICMP);
*(unsigned short *)&TxFrame2[IP_HEAD_CHKSUM_OFS] = 0;
memcpy(&TxFrame2[IP_SOURCE_OFS], &MyIP, 4);
memcpy(&TxFrame2[IP_DESTINATION_OFS], &RecdFrameIP, 4);
*(unsigned short *)&TxFrame2[IP_HEAD_CHKSUM_OFS] = CalcChecksum(&TxFrame2[IP_VER_IHL_TOS_OFS], IP_HEADER_SIZE, 0);
// ICMP
*(unsigned short *)&TxFrame2[ICMP_TYPE_CODE_OFS] = SWAPB(ICMP_ECHO_REPLY << 8);
*(unsigned short *)&TxFrame2[ICMP_CHKSUM_OFS] = 0; // initialize checksum field
CopyFromFrame_EMAC(&TxFrame2[ICMP_DATA_OFS], ICMPDataCount); // get data to echo...
*(unsigned short *)&TxFrame2[ICMP_CHKSUM_OFS] = CalcChecksum(&TxFrame2[IP_DATA_OFS], ICMPDataCount + ICMP_HEADER_SIZE, 0);
TxFrame2Size = ETH_HEADER_SIZE + IP_HEADER_SIZE + ICMP_HEADER_SIZE + ICMPDataCount;
TransmitControl |= SEND_FRAME2;
}
// easyWEB internal function
// prepares the TxFrame2-buffer to send a general TCP frame
// the TCPCode-field is passed as an argument
void PrepareTCP_FRAME(unsigned short TCPCode)
{
// Ethernet
memcpy(&TxFrame2[ETH_DA_OFS], &RemoteMAC, 6);
memcpy(&TxFrame2[ETH_SA_OFS], &MyMAC, 6);
*(unsigned short *)&TxFrame2[ETH_TYPE_OFS] = SWAPB(FRAME_IP);
// IP
*(unsigned short *)&TxFrame2[IP_VER_IHL_TOS_OFS] = SWAPB(IP_VER_IHL | IP_TOS_D);
if (TCPCode & TCP_CODE_SYN) // if SYN, we want to use the MSS option
*(unsigned short *)&TxFrame2[IP_TOTAL_LENGTH_OFS] = SWAPB(IP_HEADER_SIZE + TCP_HEADER_SIZE + TCP_OPT_MSS_SIZE);
else
*(unsigned short *)&TxFrame2[IP_TOTAL_LENGTH_OFS] = SWAPB(IP_HEADER_SIZE + TCP_HEADER_SIZE);
*(unsigned short *)&TxFrame2[IP_IDENT_OFS] = 0;
*(unsigned short *)&TxFrame2[IP_FLAGS_FRAG_OFS] = 0;
*(unsigned short *)&TxFrame2[IP_TTL_PROT_OFS] = SWAPB((DEFAULT_TTL << 8) | PROT_TCP);
*(unsigned short *)&TxFrame2[IP_HEAD_CHKSUM_OFS] = 0;
memcpy(&TxFrame2[IP_SOURCE_OFS], &MyIP, 4);
memcpy(&TxFrame2[IP_DESTINATION_OFS], &RemoteIP, 4);
*(unsigned short *)&TxFrame2[IP_HEAD_CHKSUM_OFS] = CalcChecksum(&TxFrame2[IP_VER_IHL_TOS_OFS], IP_HEADER_SIZE, 0);
// TCP
WriteWBE(&TxFrame2[TCP_SRCPORT_OFS], TCPLocalPort);
WriteWBE(&TxFrame2[TCP_DESTPORT_OFS], TCPRemotePort);
WriteDWBE(&TxFrame2[TCP_SEQNR_OFS], TCPSeqNr);
WriteDWBE(&TxFrame2[TCP_ACKNR_OFS], TCPAckNr);
*(unsigned short *)&TxFrame2[TCP_WINDOW_OFS] = SWAPB(MAX_TCP_RX_DATA_SIZE); // data bytes to accept
*(unsigned short *)&TxFrame2[TCP_CHKSUM_OFS] = 0; // initalize checksum
*(unsigned short *)&TxFrame2[TCP_URGENT_OFS] = 0;
if (TCPCode & TCP_CODE_SYN) // if SYN, we want to use the MSS option
{
*(unsigned short *)&TxFrame2[TCP_DATA_CODE_OFS] = SWAPB(0x6000 | TCPCode); // TCP header length = 24
*(unsigned short *)&TxFrame2[TCP_DATA_OFS] = SWAPB(TCP_OPT_MSS); // MSS option
*(unsigned short *)&TxFrame2[TCP_DATA_OFS + 2] = SWAPB(MAX_TCP_RX_DATA_SIZE);// max. length of TCP-data we accept
*(unsigned short *)&TxFrame2[TCP_CHKSUM_OFS] = CalcChecksum(&TxFrame2[TCP_SRCPORT_OFS], TCP_HEADER_SIZE + TCP_OPT_MSS_SIZE, 1);
TxFrame2Size = ETH_HEADER_SIZE + IP_HEADER_SIZE + TCP_HEADER_SIZE + TCP_OPT_MSS_SIZE;
}
else
{
*(unsigned short *)&TxFrame2[TCP_DATA_CODE_OFS] = SWAPB(0x5000 | TCPCode); // TCP header length = 20
*(unsigned short *)&TxFrame2[TCP_CHKSUM_OFS] = CalcChecksum(&TxFrame2[TCP_SRCPORT_OFS], TCP_HEADER_SIZE, 1);
TxFrame2Size = ETH_HEADER_SIZE + IP_HEADER_SIZE + TCP_HEADER_SIZE;
}
TransmitControl |= SEND_FRAME2;
}
// easyWEB internal function
// prepares the TxFrame1-buffer to send a payload-packet
void PrepareTCP_DATA_FRAME(void)
{
// Ethernet
memcpy(&TxFrame1[ETH_DA_OFS], &RemoteMAC, 6);
memcpy(&TxFrame1[ETH_SA_OFS], &MyMAC, 6);
*(unsigned short *)&TxFrame1[ETH_TYPE_OFS] = SWAPB(FRAME_IP);
// IP
*(unsigned short *)&TxFrame1[IP_VER_IHL_TOS_OFS] = SWAPB(IP_VER_IHL | IP_TOS_D);
WriteWBE(&TxFrame1[IP_TOTAL_LENGTH_OFS], IP_HEADER_SIZE + TCP_HEADER_SIZE + TCPTxDataCount);
*(unsigned short *)&TxFrame1[IP_IDENT_OFS] = 0;
*(unsigned short *)&TxFrame1[IP_FLAGS_FRAG_OFS] = 0;
*(unsigned short *)&TxFrame1[IP_TTL_PROT_OFS] = SWAPB((DEFAULT_TTL << 8) | PROT_TCP);
*(unsigned short *)&TxFrame1[IP_HEAD_CHKSUM_OFS] = 0;
memcpy(&TxFrame1[IP_SOURCE_OFS], &MyIP, 4);
memcpy(&TxFrame1[IP_DESTINATION_OFS], &RemoteIP, 4);
*(unsigned short *)&TxFrame1[IP_HEAD_CHKSUM_OFS] = CalcChecksum(&TxFrame1[IP_VER_IHL_TOS_OFS], IP_HEADER_SIZE, 0);
// TCP
WriteWBE(&TxFrame1[TCP_SRCPORT_OFS], TCPLocalPort);
WriteWBE(&TxFrame1[TCP_DESTPORT_OFS], TCPRemotePort);
WriteDWBE(&TxFrame1[TCP_SEQNR_OFS], TCPSeqNr);
WriteDWBE(&TxFrame1[TCP_ACKNR_OFS], TCPAckNr);
*(unsigned short *)&TxFrame1[TCP_DATA_CODE_OFS] = SWAPB(0x5000 | TCP_CODE_ACK); // TCP header length = 20
*(unsigned short *)&TxFrame1[TCP_WINDOW_OFS] = SWAPB(MAX_TCP_RX_DATA_SIZE); // data bytes to accept
*(unsigned short *)&TxFrame1[TCP_CHKSUM_OFS] = 0;
*(unsigned short *)&TxFrame1[TCP_URGENT_OFS] = 0;
*(unsigned short *)&TxFrame1[TCP_CHKSUM_OFS] = CalcChecksum(&TxFrame1[TCP_SRCPORT_OFS], TCP_HEADER_SIZE + TCPTxDataCount, 1);
}
// easyWEB internal function
// calculates the TCP/IP checksum. if 'IsTCP != 0', the TCP pseudo-header
// will be included.
unsigned short CalcChecksum(void *Start, unsigned short Count, unsigned char IsTCP)
{
unsigned long Sum = 0;
unsigned short * piStart; // Keil: Pointer added to correct expression
if (IsTCP) { // if we've a TCP frame...
Sum += MyIP[0]; // ...include TCP pseudo-header
Sum += MyIP[1];
Sum += RemoteIP[0];
Sum += RemoteIP[1];
Sum += SwapBytes(Count); // TCP header length plus data length
Sum += SWAPB(PROT_TCP);
}
piStart = Start; // Keil: Line added
while (Count > 1) { // sum words
// Sum += *((unsigned short *)Start)++; // Keil: Line replaced with following line
Sum += *piStart++;
Count -= 2;
}
if (Count) // add left-over byte, if any
// Sum += *(unsigned char *)Start; // Keil: Line replaced with following line
Sum += *(unsigned char *)piStart;
while (Sum >> 16) // fold 32-bit sum to 16 bits
Sum = (Sum & 0xFFFF) + (Sum >> 16);
return ~Sum;
}
// easyWEB internal function
// starts the timer as a retry-timer (used for retransmission-timeout)
void TCPStartRetryTimer(void)
{
TCPTimer = 0;
RetryCounter = MAX_RETRYS;
TCPFlags |= TCP_TIMER_RUNNING;
TCPFlags |= TIMER_TYPE_RETRY;
}
// easyWEB internal function
// starts the timer as a 'TIME_WAIT'-timer (used to finish a TCP-session)
void TCPStartTimeWaitTimer(void)
{
TCPTimer = 0;
TCPFlags |= TCP_TIMER_RUNNING;
TCPFlags &= ~TIMER_TYPE_RETRY;
}
// easyWEB internal function
// restarts the timer
void TCPRestartTimer(void)
{
TCPTimer = 0;
}
// easyWEB internal function
// stopps the timer
void TCPStopTimer(void)
{
TCPFlags &= ~TCP_TIMER_RUNNING;
}
// easyWEB internal function
// if a retransmission-timeout occured, check which packet
// to resend.
void TCPHandleRetransmission(void)
{
switch (LastFrameSent)
{
case ARP_REQUEST : { PrepareARP_REQUEST(); break; }
case TCP_SYN_FRAME : { PrepareTCP_FRAME(TCP_CODE_SYN); break; }
case TCP_SYN_ACK_FRAME : { PrepareTCP_FRAME(TCP_CODE_SYN | TCP_CODE_ACK); break; }
case TCP_FIN_FRAME : { PrepareTCP_FRAME(TCP_CODE_FIN | TCP_CODE_ACK); break; }
case TCP_DATA_FRAME : { TransmitControl |= SEND_FRAME1; break; }
}
}
// easyWEB internal function
// if all retransmissions failed, close connection and indicate an error
void TCPHandleTimeout(void)
{
TCPStateMachine = CLOSED;
if ((TCPFlags & (TCP_ACTIVE_OPEN | IP_ADDR_RESOLVED)) == TCP_ACTIVE_OPEN)
SocketStatus = SOCK_ERR_ARP_TIMEOUT; // indicate an error to user
else
SocketStatus = SOCK_ERR_TCP_TIMEOUT;
TCPFlags = 0; // clear all flags
}
// easyWEB internal function
// function executed every 0.210s by the CPU. used for the
// inital sequence number generator (ISN) and the TCP-timer
void TCPClockHandler(void)
{
ISNGenHigh++; // upper 16 bits of initial sequence number
TCPTimer++; // timer for retransmissions
}
// easyWEB internal function
// transfers the contents of 'TxFrame1'-Buffer to the EMAC
void SendFrame1(void)
{
CopyToFrame_EMAC(TxFrame1, TxFrame1Size);
}
// easyWEB internal function
// transfers the contents of 'TxFrame2'-Buffer to the EMAC
void SendFrame2(void)
{
CopyToFrame_EMAC(TxFrame2, TxFrame2Size);
}
// easyWEB internal function
// help function to write a WORD in big-endian byte-order
// to MCU-memory
void WriteWBE(unsigned char *Add, unsigned short Data)
{
*Add++ = Data >> 8;
*Add = (char)Data;
}
// easyWEB internal function
// help function to write a DWORD in big-endian byte-order
// to MCU-memory
void WriteDWBE(unsigned char *Add, unsigned long Data)
{
*Add++ = Data >> 24;
*Add++ = Data >> 16;
*Add++ = Data >> 8;
*Add = (char)Data;
}
// easyWEB internal function
// help function to swap the byte order of a WORD
unsigned short SwapBytes(unsigned short Data)
{
return (Data >> 8) | (Data << 8);
}