LPC1768 Mini-DK EasyWeb application with SPI TFT output. Started from EasyWebCR and modified for DM9161 PHY support.
This is a very basic EasyWeb application.
No error checking is performed during initialisation.
Information
If the webpage is not reachable or the 'Webserver running' message does not appear, press the reset button on the Mini-DK and wait until the message 'Webserver running' appears.
This happens sometimes when powering up the Mini-DK because the DM9161 reset pin is NOT controlled by the LPC1768, it is directly connected to the reset button.
IP adress/mask/gateway in tcpip.h : 192.168.0.200 / 255.255.255.0 / 192.168.0.1
MAC address in ethmac.h : 6-5-4-3-2-1
tcpip.cpp
- Committer:
- frankvnk
- Date:
- 2013-01-15
- Revision:
- 8:4c3db9231e3f
- Parent:
- 3:342aa2cf54e8
File content as of revision 8:4c3db9231e3f:
/****************************************************************** ***** ***** ***** Name: tcpip.c ***** ***** Ver.: 1.0 ***** ***** Date: 17/12/2012 ***** ***** Func: implements the TCP/IP-stack and provides a ***** ***** simple API to the user ***** ***** Rewrite from Andreas Dannenberg ***** ***** HTWK Leipzig ***** ***** university of applied sciences ***** ***** Germany ***** ***** adannenb@et.htwk-leipzig.de ***** ***** ***** ******************************************************************/ #include "tcpip.h" #include "ethmac.h" #include "string.h" #include "mbed.h" unsigned short MyIP[] = // "MYIP1.MYIP2.MYIP3.MYIP4" { MYIP_1 + (MYIP_2 << 8), MYIP_3 + (MYIP_4 << 8) }; unsigned short SubnetMask[] = // "SUBMASK1.SUBMASK2.SUBMASK3.SUBMASK4" { SUBMASK_1 + (SUBMASK_2 << 8), SUBMASK_3 + (SUBMASK_4 << 8) }; unsigned short GatewayIP[] = // "GWIP1.GWIP2.GWIP3.GWIP4" { GWIP_1 + (GWIP_2 << 8), GWIP_3 + (GWIP_4 << 8) }; // easyWEB's internal variables TTCPStateMachine TCPStateMachine; // perhaps the most important var at all ;-) TLastFrameSent LastFrameSent; // retransmission type unsigned short ISNGenHigh; // upper word of our Initial Sequence Number unsigned long TCPSeqNr; // next sequence number to send unsigned long TCPUNASeqNr; // last unaknowledged sequence number // incremented AFTER sending data unsigned long TCPAckNr; // next seq to receive and ack to send // incremented AFTER receiving data unsigned char TCPTimer; // inc'd each 262ms unsigned char RetryCounter; // nr. of retransmissions // properties of the just received frame unsigned short RecdFrameLength; // EMAC reported frame length unsigned short RecdFrameMAC[3]; // 48 bit MAC unsigned short RecdFrameIP[2]; // 32 bit IP unsigned short RecdIPFrameLength; // 16 bit IP packet length // the next 3 buffers must be word-aligned! // (here the 'RecdIPFrameLength' above does that) unsigned short _TxFrame1[(ETH_HEADER_SIZE + IP_HEADER_SIZE + TCP_HEADER_SIZE + MAX_TCP_TX_DATA_SIZE)/2]; unsigned short _TxFrame2[(ETH_HEADER_SIZE + MAX_ETH_TX_DATA_SIZE)/2]; unsigned short _RxTCPBuffer[MAX_TCP_RX_DATA_SIZE/2]; // space for incoming TCP-data unsigned short TxFrame1Size; // bytes to send in TxFrame1 unsigned char TxFrame2Size; // bytes to send in TxFrame2 unsigned char TransmitControl; #define SEND_FRAME1 0x01 #define SEND_FRAME2 0x02 unsigned char TCPFlags; #define TCP_ACTIVE_OPEN 0x01 // easyWEB shall initiate a connection #define IP_ADDR_RESOLVED 0x02 // IP sucessfully resolved to MAC #define TCP_TIMER_RUNNING 0x04 #define TIMER_TYPE_RETRY 0x08 #define TCP_CLOSE_REQUESTED 0x10 // easyWEB-API global vars and flags unsigned short TCPRxDataCount; // nr. of bytes rec'd unsigned short TCPTxDataCount; // nr. of bytes to send unsigned short TCPLocalPort; // TCP ports unsigned short TCPRemotePort; unsigned short RemoteMAC[3]; // MAC and IP of current TCP-session unsigned short RemoteIP[2]; unsigned char SocketStatus; void Start_SysTick10ms(void); 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) { Start_SysTick10ms(); // Start SysTick timer running (10ms ticks) Init_EthMAC(); 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 BroadcastMessage(void) { unsigned short FrameDestination[3]; // to hold 48 bit MAC address RecdFrameLength = StartReadingFrame(); // Read destination address CopyFromFrame_EthMAC(&FrameDestination, 6); // Save it for reply CopyFromFrame_EthMAC(&RecdFrameMAC, 6); if ((FrameDestination[0] == 0xFFFF) && (FrameDestination[1] == 0xFFFF) && (FrameDestination[2] == 0xFFFF)) { return(1); // Broadcast message } 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) { // Check to see if packet received if (CheckIfFrameReceived()) { // Was it a broadcast message? if (BroadcastMessage()) { ProcessEthBroadcastFrame(); } else { ProcessEthIAFrame(); } // now release ethernet controller buffer StopReadingFrame(); } 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) | (LPC_TIM0->TC & 0xFFFF); // 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 CS8900 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()) // CS8900 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_EthMAC() == FRAME_ARP) // get frame type, check for ARP if (ReadFrameBE_EthMAC() == HARDW_ETH10) // Ethernet frame if (ReadFrameBE_EthMAC() == FRAME_IP) // check protocol if (ReadFrameBE_EthMAC() == IP_HLEN_PLEN) // check HLEN, PLEN if (ReadFrameBE_EthMAC() == OP_ARP_REQUEST) { DummyReadFrame_EthMAC(6); // ignore sender's hardware address CopyFromFrame_EthMAC(&RecdFrameIP, 4); // read sender's protocol address DummyReadFrame_EthMAC(6); // ignore target's hardware address CopyFromFrame_EthMAC(&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 CS8900's address filter // (individual addressed = IA) void ProcessEthIAFrame(void) { unsigned short TargetIP[2]; unsigned char ProtocolType; switch (ReadFrameBE_EthMAC()) // get frame type { case FRAME_ARP : // check for ARP { if ((TCPFlags & (TCP_ACTIVE_OPEN | IP_ADDR_RESOLVED)) == TCP_ACTIVE_OPEN) if (ReadFrameBE_EthMAC() == HARDW_ETH10) // check for the right prot. etc. if (ReadFrameBE_EthMAC() == FRAME_IP) if (ReadFrameBE_EthMAC() == IP_HLEN_PLEN) if (ReadFrameBE_EthMAC() == OP_ARP_ANSWER) { TCPStopTimer(); // OK, now we've the MAC we wanted ;-) CopyFromFrame_EthMAC(&RemoteMAC, 6); // extract opponents MAC TCPFlags |= IP_ADDR_RESOLVED; } break; } case FRAME_IP : // check for IP-type { if ((ReadFrameBE_EthMAC() & 0xFF00 ) == IP_VER_IHL) // IPv4, IHL=5 (20 Bytes Header) { // ignore Type Of Service RecdIPFrameLength = ReadFrameBE_EthMAC(); // get IP frame's length ReadFrameBE_EthMAC(); // ignore identification if (!(ReadFrameBE_EthMAC() & (IP_FLAG_MOREFRAG | IP_FRAGOFS_MASK))) // only unfragm. frames { ProtocolType = ReadFrameBE_EthMAC() & 0xFF ; // get protocol, ignore TTL ReadFrameBE_EthMAC(); // ignore checksum CopyFromFrame_EthMAC(&RecdFrameIP, 4); // get source IP CopyFromFrame_EthMAC(&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_EthMAC(); // get Message Type and Code ReadFrameBE_EthMAC(); // 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_EthMAC(); // get ports TCPSegDestPort = ReadFrameBE_EthMAC(); if (TCPSegDestPort != TCPLocalPort) return; // drop segment if port doesn't match TCPSegSeq = (unsigned long)ReadFrameBE_EthMAC() << 16; // get segment sequence nr. TCPSegSeq |= ReadFrameBE_EthMAC(); TCPSegAck = (unsigned long)ReadFrameBE_EthMAC() << 16; // get segment acknowledge nr. TCPSegAck |= ReadFrameBE_EthMAC(); TCPCode = ReadFrameBE_EthMAC(); // 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_EthMAC(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) | (LPC_TIM0->TC & 0xFFFF); // 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_EthMAC(6); // ignore window, checksum, urgent pointer CopyFromFrame_EthMAC(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_EthMAC(&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) { // Code Red - added pStart unsigned short *pStart; unsigned long Sum = 0; 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); } pStart = (unsigned short *)Start; while (Count > 1) { // sum words Sum += *pStart++; Count -= 2; } if (Count) // add left-over byte, if any Sum += *(unsigned char *)pStart; 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 MCU. 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 CS8900A void SendFrame1(void) { CopyToFrame_EthMAC(TxFrame1, TxFrame1Size); } // easyWEB internal function // transfers the contents of 'TxFrame2'-Buffer to the CS8900A void SendFrame2(void) { CopyToFrame_EthMAC(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 = 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); }