From df5507c8c86904bca3c27f63bef715928f80f324 Mon Sep 17 00:00:00 2001 From: David Crocker Date: Fri, 23 Dec 2016 23:19:20 +0000 Subject: More work on Duet Ethernet support --- src/Duet/Network.cpp | 4 +- src/Duet/Network.h | 10 +- src/DuetNG/DuetEthernet/Network.cpp | 821 ++++------------ src/DuetNG/DuetEthernet/Network.h | 161 +--- src/DuetNG/DuetEthernet/NetworkTransacrion.cpp | 595 ++++++++++++ src/DuetNG/DuetEthernet/NetworkTransaction.h | 160 ++++ src/DuetNG/DuetEthernet/Webserver.cpp | 162 ++-- .../DuetEthernet/Wiznet/Ethernet/W5500/w5500.cpp | 35 +- .../DuetEthernet/Wiznet/Ethernet/W5500/w5500.h | 2 +- src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.cpp | 431 +++++++++ src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.h | 25 + src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.cpp | 23 +- src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.h | 2 +- .../DuetEthernet/Wiznet/Ethernet/wizchip_conf.cpp | 4 +- .../DuetEthernet/Wiznet/Ethernet/wizchip_conf.h | 4 +- .../DuetEthernet/Wiznet/Internet/DHCP/dhcp.cpp | 1004 ++++++++++++++++++++ .../DuetEthernet/Wiznet/Internet/DHCP/dhcp.h | 144 +++ src/DuetNG/DuetWiFi/Network.cpp | 2 +- src/DuetNG/DuetWiFi/Network.h | 8 +- src/Platform.cpp | 13 +- 20 files changed, 2686 insertions(+), 924 deletions(-) create mode 100644 src/DuetNG/DuetEthernet/NetworkTransacrion.cpp create mode 100644 src/DuetNG/DuetEthernet/NetworkTransaction.h create mode 100644 src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.cpp create mode 100644 src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.h create mode 100644 src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.cpp create mode 100644 src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.h (limited to 'src') diff --git a/src/Duet/Network.cpp b/src/Duet/Network.cpp index f3ecfcf3..ad2fdb8b 100644 --- a/src/Duet/Network.cpp +++ b/src/Duet/Network.cpp @@ -72,7 +72,7 @@ static struct mdns_service mdns_services[] = { }, { .name = "\x05_http\x04_tcp\x05local", - .port = DEFAULT_HTTP_PORT, + .port = DefaultHttpPort, }, { .name = "\x04_ftp\x04_tcp\x05local", @@ -102,7 +102,7 @@ static uint16_t sendingWindowSize, sentDataOutstanding; static uint8_t sendingRetries; static err_t writeResult, outputResult; -static uint16_t httpPort = DEFAULT_HTTP_PORT; +static uint16_t httpPort = DefaultHttpPort; /*-----------------------------------------------------------------------------------*/ diff --git a/src/Duet/Network.h b/src/Duet/Network.h index cc099864..d3d9904b 100644 --- a/src/Duet/Network.h +++ b/src/Duet/Network.h @@ -33,12 +33,12 @@ const size_t NETWORK_TRANSACTION_COUNT = 24; // Number of NetworkTransacti const uint32_t TCP_WRITE_TIMEOUT = 4000; // Miliseconds to wait for data we have written to be acknowledged const uint32_t TCP_MAX_SEND_RETRIES = 8; // How many times can we attempt to write data -const uint8_t MAC_ADDRESS[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED }; // Need some sort of default... -const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 }; -const uint8_t NET_MASK[4] = { 255, 255, 255, 0 }; -const uint8_t GATE_WAY[4] = { 192, 168, 1, 1 }; +const uint8_t DefaultMacAddress[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED }; // Need some sort of default... +const uint8_t DefaultIpAddress[4] = { 192, 168, 1, 10 }; +const uint8_t DefaultNetMask[4] = { 255, 255, 255, 0 }; +const uint8_t DefaultGateway[4] = { 192, 168, 1, 1 }; -const uint16_t DEFAULT_HTTP_PORT = 80; +const uint16_t DefaultHttpPort = 80; const uint16_t FTP_PORT = 21; const uint16_t TELNET_PORT = 23; diff --git a/src/DuetNG/DuetEthernet/Network.cpp b/src/DuetNG/DuetEthernet/Network.cpp index 27deb978..e2c17ddf 100644 --- a/src/DuetNG/DuetEthernet/Network.cpp +++ b/src/DuetNG/DuetEthernet/Network.cpp @@ -8,8 +8,9 @@ #include "RepRapFirmware.h" #include "compiler.h" #include "Pins.h" -#include "Ethernet3/Ethernet3.h" - +#include "IPAddress.h" +#include "wizchip_conf.h" +#include "Wiznet/Internet/DHCP/dhcp.h" void Network::SetIPAddress(const uint8_t p_ipAddress[], const uint8_t p_netmask[], const uint8_t p_gateway[]) { @@ -18,11 +19,12 @@ void Network::SetIPAddress(const uint8_t p_ipAddress[], const uint8_t p_netmask[ memcpy(gateway, p_gateway, sizeof(gateway)); } -Network::Network(Platform* p) : platform(p), responseCode(0), responseBody(nullptr), responseText(nullptr), responseFile(nullptr), - httpPort(DEFAULT_HTTP_PORT), - state(disabled), activated(false) +Network::Network(Platform* p) + : platform(p), lastTickMillis(0), + freeTransactions(nullptr), readyTransactions(nullptr), writingTransactions(nullptr), + httpPort(DefaultHttpPort), state(NetworkState::disabled), activated(false) { - SetIPAddress(IP_ADDRESS, NET_MASK, GATE_WAY); + SetIPAddress(DefaultIpAddress, DefaultNetMask, DefaultGateway); strcpy(hostname, HOSTNAME); } @@ -30,7 +32,9 @@ void Network::Init() { // Ensure that the chip is in the reset state pinMode(EspResetPin, OUTPUT_LOW); - state = disabled; + state = NetworkState::disabled; + longWait = platform->Time(); + lastTickMillis = millis(); } // This is called at the end of config.g processing. @@ -38,7 +42,7 @@ void Network::Init() void Network::Activate() { activated = true; - if (state == enabled) + if (state == NetworkState::enabled) { Start(); } @@ -51,106 +55,176 @@ void Network::Exit() void Network::Spin() { -#if 1 -#if 0 - if (state == starting) + switch(state) { - const int rc = Ethernet.begin(platform->MACAddress(), 12000, 5000); // for now we always use DHCP - if (rc == 1) + case NetworkState::enabled: + case NetworkState::disabled: + // Nothing to do + break; + + case NetworkState::establishingLink: + if (wizphy_getphylink() == PHY_LINK_ON) { - state = running; + + usingDhcp = (ipAddress[0] == 0 && ipAddress[1] == 0 && ipAddress[2] == 0 && ipAddress[3] == 0); + if (usingDhcp) + { + debugPrintf("Link established, getting IP address\n"); + // IP address is all zeros, so use DHCP + DHCP_init(DhcpSocketNumber, hostname); + lastTickMillis = millis(); + state = NetworkState::obtainingIP; + } + else + { + debugPrintf("Link established, network running\n"); + state = NetworkState::active; + } } - } - else if (state == running) -#else - if (state == starting || state == running) -#endif - { - // Check DHCP - const int rc = Ethernet.maintain(); - if (state == starting && (rc == DHCP_CHECK_RENEW_OK || rc == DHCP_CHECK_REBIND_OK)) + break; + + case NetworkState::obtainingIP: + if (wizphy_getphylink() == PHY_LINK_ON) + { + const uint32_t now = millis(); + if (now - lastTickMillis >= 1000) + { + lastTickMillis += 1000; + DHCP_time_handler(); + } + const DhcpRunResult ret = DHCP_run(); + if (ret == DhcpRunResult::DHCP_IP_ASSIGN) + { + debugPrintf("IP address obtained, network running\n"); + getSIPR(ipAddress); + // Send mDNS announcement so that some routers can perform hostname mapping + // if this board is connected via a non-IGMP capable WiFi bridge (like the TP-Link WR701N) + //mdns_announce(); + state = NetworkState::active; + } + } + else { - state = running; + DHCP_stop(); + state = NetworkState::establishingLink; } + break; + + case NetworkState::active: + if (wizphy_getphylink() == PHY_LINK_ON) + { + if (usingDhcp) + { + const uint32_t now = millis(); + if (now - lastTickMillis >= 1000) + { + lastTickMillis += 1000; + DHCP_time_handler(); + } + const DhcpRunResult ret = DHCP_run(); + if (ret == DhcpRunResult::DHCP_IP_CHANGED) + { + getSIPR(ipAddress); + } + } + + // See if we can read any packets +// ethernet_task(); + + // See if we can send anything + NetworkTransaction *transaction = writingTransactions; + if (transaction != nullptr /*&& sendingConnection == nullptr*/ ) + { + if (transaction->GetNext() != nullptr) + { + // Data is supposed to be sent and the last packet has been acknowledged. + // Rotate the transactions so every client is served even while multiple files are sent + NetworkTransaction *next = transaction->GetNext(); + writingTransactions = next; + AppendTransaction(&writingTransactions, transaction); + transaction = next; + } + + if (transaction->Send()) + { + // This transaction can be released, do this here + writingTransactions = transaction->GetNext(); + PrependTransaction(&freeTransactions, transaction); + + // If there is more data to write on this connection, do it sometime soon + NetworkTransaction *nextWrite = transaction->GetNextWrite(); + if (nextWrite != nullptr) + { + PrependTransaction(&writingTransactions, nextWrite); + } + } + } + } + else + { + DHCP_stop(); + state = NetworkState::establishingLink; + } + break; } -#endif + platform->ClassReport(longWait); } void Network::Diagnostics(MessageType mtype) { - + platform->Message(mtype, "=== Network ===\n"); + platform->MessageF(mtype, "State: %d\n", (int)state); } void Network::Start() { + SetIPAddress(platform->GetIPAddress(), platform->NetMask(), platform->GateWay()); pinMode(EspResetPin, OUTPUT_LOW); delayMicroseconds(550); // W550 reset pulse must be at least 500us long Platform::WriteDigital(EspResetPin, HIGH); // raise /Reset pin delay(55); // W5500 needs 50ms to start up - state = starting; -#if 0 - w5500.init(); - w5500.setMACAddress(platform->MACAddress()); -#endif - const int rc = Ethernet.begin(platform->MACAddress(), 12000, 5000); // for now we always use DHCP - if (rc == 1) - { - state = running; - } - else - { -#if 1 - { - uint8_t tmp = w5500.readPHYCFGR(); - uint8_t version = w5500.readVERSIONR(); - uint8_t macBuf[6]; - w5500.readSHAR(macBuf); - platform->MessageF(GENERIC_MESSAGE, "Phy %02x ver %02x Mac %02x:%02x:%02x:%02x:%02x:%02x\n", - tmp, version, macBuf[0], macBuf[1], macBuf[2], macBuf[3], macBuf[4], macBuf[5]); - } -#endif - platform->Message(GENERIC_MESSAGE, "Failed to start Ethernet interface\n"); - Stop(); - } + + static const uint8_t bufSizes[8] = { 2, 2, 2, 2, 2, 2, 2, 2 }; + wizchip_init(bufSizes, bufSizes); + + setSHAR(platform->MACAddress()); + setSIPR(ipAddress); + setGAR(gateway); + setSUBR(netmask); + + state = NetworkState::establishingLink; } void Network::Stop() { - if (state != disabled) + if (state != NetworkState::disabled) { - Ethernet.stop(); +//TODO Ethernet.stop(); + if (usingDhcp) + { + DHCP_stop(); + } digitalWrite(EspResetPin, LOW); // put the ESP back into reset - state = disabled; + state = NetworkState::disabled; } } void Network::Enable() { - if (state == disabled) + if (state == NetworkState::disabled) { - state = enabled; + state = NetworkState::enabled; if (activated) { Start(); } } -#if 1 - else if (state == starting) - { - uint8_t tmp = w5500.readPHYCFGR(); - uint8_t version = w5500.readVERSIONR(); - uint8_t macBuf[6]; - w5500.readSHAR(macBuf); - platform->MessageF(GENERIC_MESSAGE, "Phy %02x ver %02x Mac %02x:%02x:%02x:%02x:%02x:%02x\n", - tmp, version, macBuf[0], macBuf[1], macBuf[2], macBuf[3], macBuf[4], macBuf[5]); - } -#endif } void Network::Disable() { - if (activated && state != disabled) + if (activated && state != NetworkState::disabled) { Stop(); platform->Message(GENERIC_MESSAGE, "WiFi server stopped\n"); @@ -159,21 +233,12 @@ void Network::Disable() bool Network::IsEnabled() const { - return state != disabled; + return state != NetworkState::disabled; } const uint8_t *Network::GetIPAddress() const { - if (state == running) - { - static IPAddress ip; - ip = Ethernet.localIP(); - return ip.GetRawAddress(); - } - else - { - return ipAddress; - } + return ipAddress; } void Network::SetHttpPort(uint16_t port) @@ -277,6 +342,22 @@ NetworkTransaction *Network::GetTransaction(const ConnectionState *cs) #endif } +void Network::AppendTransaction(NetworkTransaction* * list, NetworkTransaction *r) +{ + r->next = nullptr; + while (*list != nullptr) + { + list = &((*list)->next); + } + *list = r; +} + +void Network::PrependTransaction(NetworkTransaction* * list, NetworkTransaction *r) +{ + r->next = *list; + *list = r; +} + void Network::OpenDataPort(uint16_t port) { //TODO @@ -384,586 +465,4 @@ bool Network::AcquireTelnetTransaction() #endif } -//*************************************************************************************************** - -// ConnectionState class - -#if 0 -void ConnectionState::Init(tcp_pcb *p) -{ - pcb = p; - localPort = p->local_port; - remoteIPAddress = p->remote_ip.addr; - remotePort = p->remote_port; - next = nullptr; - sendingTransaction = nullptr; - persistConnection = true; - isTerminated = false; -} -#endif - -void ConnectionState::Terminate() -{ - //TODO -#if 0 - if (pcb != nullptr) - { - tcp_abort(pcb); - } -#endif -} - -//*************************************************************************************************** -// NetworkTransaction class - -NetworkTransaction::NetworkTransaction(NetworkTransaction *n) : next(n), status(released) -{ - sendStack = new OutputStack(); -} - -void NetworkTransaction::Set(pbuf *p, ConnectionState *c, TransactionStatus s) -{ - cs = c; -// pb = readingPb = p; - status = s; -// inputPointer = 0; - sendBuffer = nullptr; - fileBeingSent = nullptr; - closeRequested = false; - nextWrite = nullptr; - dataAcknowledged = false; -} - -bool NetworkTransaction::HasMoreDataToRead() const -{ - //TODO - return false; -} - -// Read one char from the NetworkTransaction -bool NetworkTransaction::Read(char& b) -{ -#if 1 - return false; -#else - if (readingPb == nullptr) - { - b = 0; - return false; - } - - b = ((const char*)readingPb->payload)[inputPointer++]; - if (inputPointer == readingPb->len) - { - readingPb = readingPb->next; - inputPointer = 0; - } - return true; -#endif -} - -// Read data from the NetworkTransaction and return true on success -bool NetworkTransaction::ReadBuffer(const char *&buffer, size_t &len) -{ -#if 1 - return false; -#else - if (readingPb == nullptr) - { - return false; - } - - if (inputPointer >= readingPb->len) - { - readingPb = readingPb->next; - inputPointer = 0; - if (readingPb == nullptr) - { - return false; - } - } - - buffer = (const char*)readingPb->payload + inputPointer; - len = readingPb->len - inputPointer; - readingPb = readingPb->next; - inputPointer = 0; - return true; -#endif -} - -void NetworkTransaction::Write(char b) -{ - if (CanWrite()) - { - if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) - { - // Should never get here - return; - } - sendBuffer->cat(b); - } -} - -void NetworkTransaction::Write(const char* s) -{ - if (CanWrite()) - { - if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) - { - // Should never get here - return; - } - sendBuffer->cat(s); - } -} - -void NetworkTransaction::Write(StringRef ref) -{ - Write(ref.Pointer(), ref.strlen()); -} - -void NetworkTransaction::Write(const char* s, size_t len) -{ - if (CanWrite()) - { - if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) - { - // Should never get here - return; - } - sendBuffer->cat(s, len); - } -} - -void NetworkTransaction::Write(OutputBuffer *buffer) -{ - if (CanWrite()) - { - // Note we use an individual stack here, because we don't want to link different - // OutputBuffers for different destinations together... - sendStack->Push(buffer); - } - else - { - // Don't keep buffers we can't send... - OutputBuffer::ReleaseAll(buffer); - } -} - -void NetworkTransaction::Write(OutputStack *stack) -{ - if (stack != nullptr) - { - if (CanWrite()) - { - sendStack->Append(stack); - } - else - { - stack->ReleaseAll(); - } - } -} - -void NetworkTransaction::Printf(const char* fmt, ...) -{ - if (CanWrite() && (sendBuffer != nullptr || OutputBuffer::Allocate(sendBuffer))) - { - va_list p; - va_start(p, fmt); - sendBuffer->vprintf(fmt, p); - va_end(p); - } -} - -void NetworkTransaction::SetFileToWrite(FileStore *file) -{ - if (CanWrite()) - { - fileBeingSent = file; - } - else if (file != nullptr) - { - file->Close(); - } -} - -// Send exactly one TCP window of data and return true when this transaction can be released -bool NetworkTransaction::Send() -{ -#if 1 - return true; -#else - // Free up this transaction if the connection is supposed to be closed - if (closeRequested) - { - reprap.GetNetwork()->ConnectionClosed(cs, true); // This will release the transaction too - return false; - } - - // Fill up the TCP window with some data chunks from our OutputBuffer instances - size_t bytesBeingSent = 0, bytesLeftToSend = TCP_WND; - while (sendBuffer != nullptr && bytesLeftToSend > 0) - { - size_t copyLength = min(bytesLeftToSend, sendBuffer->BytesLeft()); - memcpy(sendingWindow + bytesBeingSent, sendBuffer->Read(copyLength), copyLength); - bytesBeingSent += copyLength; - bytesLeftToSend -= copyLength; - - if (sendBuffer->BytesLeft() == 0) - { - sendBuffer = OutputBuffer::Release(sendBuffer); - if (sendBuffer == nullptr) - { - sendBuffer = sendStack->Pop(); - } - } - } - - // We also intend to send a file, so check if we can fill up the TCP window - if (sendBuffer == nullptr && bytesLeftToSend != 0 && fileBeingSent != nullptr) - { - // For HSMCI efficiency, read from the file in multiples of 4 bytes except at the end. - // This ensures that the second and subsequent chunks can be DMA'd directly into sendingWindow. - size_t bytesToRead = bytesLeftToSend & (~3); - if (bytesToRead != 0) - { - int bytesRead = fileBeingSent->Read(sendingWindow + bytesBeingSent, bytesToRead); - if (bytesRead > 0) - { - bytesBeingSent += bytesRead; - } - - if (bytesRead != (int)bytesToRead) - { - fileBeingSent->Close(); - fileBeingSent = nullptr; - } - } - } - - if (bytesBeingSent == 0) - { - // If we have no data to send, this connection can be closed next time - if (!cs->persistConnection && nextWrite == nullptr) - { - Close(); - return false; - } - - // We want to send data from another transaction as well, so only free up this one - cs->sendingTransaction = nextWrite; - return true; - } - - // The TCP window has been filled up as much as possible, so send it now. There is no need to check - // the available space in the SNDBUF queue, because we really write only one TCP window at once. - writeResult = tcp_write(cs->pcb, sendingWindow, bytesBeingSent, 0); - if (ERR_IS_FATAL(writeResult)) - { - reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Failed to write data in Send (code %d)\n", writeResult); - tcp_abort(cs->pcb); - return false; - } - - outputResult = tcp_output(cs->pcb); - if (ERR_IS_FATAL(outputResult)) - { - reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Failed to output data in Send (code %d)\n", outputResult); - tcp_abort(cs->pcb); - return false; - } - - if (outputResult != ERR_OK && reprap.Debug(moduleNetwork)) - { - reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: tcp_output resulted in error code %d\n", outputResult); - } - - // Set LwIP callbacks for ACK and retransmission handling - tcp_poll(cs->pcb, conn_poll, TCP_WRITE_TIMEOUT / TCP_SLOW_INTERVAL / TCP_MAX_SEND_RETRIES); - tcp_sent(cs->pcb, conn_sent); - - // Set all values for the send process - sendingConnection = cs; - sendingRetries = 0; - sendingWindowSize = sentDataOutstanding = bytesBeingSent; - return false; -#endif -} - -// This is called by the Webserver to send output data to a client. If keepConnectionAlive is set to false, -// the current connection will be terminated once everything has been sent. -void NetworkTransaction::Commit(bool keepConnectionAlive) -{ -#if 0 - // If the connection has been terminated (e.g. RST received while writing upload data), discard this transaction - if (!IsConnected() || status == released) - { - Discard(); - return; - } - - // Free buffer holding the incoming data and prepare some values for the sending process - FreePbuf(); - cs->persistConnection = keepConnectionAlive; - if (sendBuffer == nullptr) - { - sendBuffer = sendStack->Pop(); - } - status = sending; - - // Unlink the item(s) from the list of ready transactions - if (keepConnectionAlive) - { - // Our connection is still of interest, remove only this transaction from the list - NetworkTransaction *previous = nullptr; - for(NetworkTransaction *item = reprap.GetNetwork()->readyTransactions; item != nullptr; item = item->next) - { - if (item == this) - { - if (previous == nullptr) - { - reprap.GetNetwork()->readyTransactions = next; - } - else - { - previous->next = next; - } - break; - } - previous = item; - } - } - else - { - // We will close this connection soon, stop receiving data from this PCB - tcp_recv(cs->pcb, nullptr); - - // Also remove all ready transactions pointing to our ConnectionState - NetworkTransaction *previous = nullptr, *item = reprap.GetNetwork()->readyTransactions; - while (item != nullptr) - { - if (item->cs == cs) - { - if (item == this) - { - // Only unlink this item - if (previous == nullptr) - { - reprap.GetNetwork()->readyTransactions = next; - } - else - { - previous->next = next; - } - item = next; - } - else - { - // Remove all others - item->Discard(); - item = (previous == nullptr) ? reprap.GetNetwork()->readyTransactions : previous->next; - } - } - else - { - previous = item; - item = item->next; - } - } - } - - // Enqueue this transaction, so it's sent in the right order - NetworkTransaction *mySendingTransaction = cs->sendingTransaction; - if (mySendingTransaction == nullptr) - { - cs->sendingTransaction = this; - reprap.GetNetwork()->AppendTransaction(&reprap.GetNetwork()->writingTransactions, this); - } - else - { - while (mySendingTransaction->nextWrite != nullptr) - { - mySendingTransaction = mySendingTransaction->nextWrite; - } - mySendingTransaction->nextWrite = this; - } -#endif -} - -// Call this to perform some networking tasks while processing deferred requests, -// and to move this transaction and all transactions that are associated with its -// connection to the end of readyTransactions. There are three ways to do this: -// -// 1) DeferOnly: Do not modify any of the processed data and don't send an ACK. -// This will ensure that zero-window packets are sent back to the client -// 2) ResetData: Reset the read pointers and acknowledge that the data has been processed -// 3) DiscardData: Free the processed data, acknowledge it and append this transaction as -// an empty item again without payload (i.e. without pbufs) -// -void NetworkTransaction::Defer(DeferralMode mode) -{ -#if 0 - if (mode == DeferralMode::ResetData) - { - // Reset the reading pointers and send an ACK - inputPointer = 0; - readingPb = pb; - if (IsConnected() && pb != nullptr && !dataAcknowledged) - { - tcp_recved(cs->pcb, pb->tot_len); - dataAcknowledged = true; - } - } - else if (mode == DeferralMode::DiscardData) - { - // Discard the incoming data, because we don't need to process it any more - FreePbuf(); - } - - status = deferred; - - // Unlink this transaction from the list of ready transactions and append it again - Network *network = reprap.GetNetwork(); - NetworkTransaction *item, *previous = nullptr; - for(item = network->readyTransactions; item != nullptr; item = item->next) - { - if (item == this) - { - if (previous == nullptr) - { - network->readyTransactions = next; - } - else - { - previous->next = next; - } - break; - } - previous = item; - } - network->AppendTransaction(&network->readyTransactions, this); - - // Append all other transactions that are associated to this connection, so that the - // Webserver gets a chance to deal with all connected clients even while multiple - // deferred requests are present in the list. - item = network->readyTransactions; - previous = nullptr; - while (item != this) - { - if (item->cs == cs) - { - NetworkTransaction *nextItem = item->next; - if (previous == nullptr) - { - network->readyTransactions = item->next; - network->AppendTransaction(&network->readyTransactions, item); - } - else - { - previous->next = item->next; - network->AppendTransaction(&network->readyTransactions, item); - } - item = nextItem; - } - else - { - previous = item; - item = item->next; - } - } -#endif -} - - -// This method should be called if we don't want to send data to the client and if we -// don't want to interfere with the connection state. May also be called from ISR! -void NetworkTransaction::Discard() -{ -#if 0 - // Can we do anything? - if (status == released) - { - // No - don't free up released items multiple times - return; - } - - // Free up some resources - FreePbuf(); - - if (fileBeingSent != nullptr) - { - fileBeingSent->Close(); - fileBeingSent = nullptr; - } - - OutputBuffer::ReleaseAll(sendBuffer); - sendStack->ReleaseAll(); - - // Unlink this transactions from the list of ready transactions and free it. It is then appended to the list of - // free transactions because we don't want to risk reusing it when the ethernet ISR processes incoming data - NetworkTransaction *previous = nullptr; - for(NetworkTransaction *item = reprap.GetNetwork()->readyTransactions; item != nullptr; item = item->next) - { - if (item == this) - { - if (previous == nullptr) - { - reprap.GetNetwork()->readyTransactions = next; - } - else - { - previous->next = next; - } - break; - } - previous = item; - } - reprap.GetNetwork()->AppendTransaction(&reprap.GetNetwork()->freeTransactions, this); - bool callDisconnectHandler = (cs != nullptr && status == disconnected); - status = released; - - // Call disconnect event if this transaction indicates a graceful disconnect and if the connection - // still persists (may not be the case if a RST packet was received before) - if (callDisconnectHandler) - { - if (reprap.Debug(moduleNetwork)) - { - reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Discard() is handling a graceful disconnect\n"); - } - reprap.GetNetwork()->ConnectionClosed(cs, false); - } -#endif -} - -uint32_t NetworkTransaction::GetRemoteIP() const -{ - return (cs != nullptr) ? cs->GetRemoteIP() : 0; -} - -uint16_t NetworkTransaction::GetRemotePort() const -{ - return (cs != nullptr) ? cs->GetRemotePort() : 0; -} - -uint16_t NetworkTransaction::GetLocalPort() const -{ - return (cs != nullptr) ? cs->GetLocalPort() : 0; -} - -void NetworkTransaction::Close() -{ -#if 0 - tcp_pcb *pcb = cs->pcb; - tcp_recv(pcb, nullptr); - closeRequested = true; -#endif -} - -bool ConnectionState::IsConnected() const -{ - //TODO - return false; -} - // End diff --git a/src/DuetNG/DuetEthernet/Network.h b/src/DuetNG/DuetEthernet/Network.h index a2e1b4d7..36501dcc 100644 --- a/src/DuetNG/DuetEthernet/Network.h +++ b/src/DuetNG/DuetEthernet/Network.h @@ -15,131 +15,26 @@ Separated out from Platform.h by dc42 and extended by zpl #include #include "MessageType.h" +#include "NetworkTransaction.h" -const uint8_t MAC_ADDRESS[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED }; // Need some sort of default... -const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 }; // Need some sort of default... -const uint8_t NET_MASK[4] = { 255, 255, 255, 0 }; -const uint8_t GATE_WAY[4] = { 192, 168, 1, 1 }; +const uint8_t DefaultMacAddress[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED }; // Need some sort of default... +const uint8_t DefaultIpAddress[4] = { 0, 0, 0, 0 }; // Need some sort of default... +const uint8_t DefaultNetMask[4] = { 255, 255, 255, 0 }; +const uint8_t DefaultGateway[4] = { 0, 0, 0, 0 }; -const uint16_t DEFAULT_HTTP_PORT = 80; +const uint16_t DefaultHttpPort = 80; const uint16_t FTP_PORT = 21; const uint16_t TELNET_PORT = 23; -class Platform; - -struct tcp_pcb; -struct pbuf; - -class NetworkTransaction; - -// ConnectionState structure that we use to track TCP connections. It is usually combined with NetworkTransactions. -struct ConnectionState -{ -// tcp_pcb *volatile pcb; // Connection PCB - uint16_t localPort, remotePort; // Copy of the local and remote ports, because the PCB may be unavailable - uint32_t remoteIPAddress; // Same for the remote IP address - NetworkTransaction * volatile sendingTransaction; // NetworkTransaction that is currently sending via this connection - ConnectionState * volatile next; // Next ConnectionState in this list - bool persistConnection; // Do we expect this connection to stay alive? - volatile bool isTerminated; // Will be true if the connection has gone down unexpectedly (TCP RST) - -// void Init(tcp_pcb *p); - uint16_t GetLocalPort() const { return localPort; } - uint32_t GetRemoteIP() const { return remoteIPAddress; } - uint16_t GetRemotePort() const { return remotePort; } - bool IsConnected() const; // { return pcb != nullptr; } - bool IsTerminated() const { return isTerminated; } - void Terminate(); -}; +// We have 8 sockets available on the W5500. We reserve one for DHCP, leaving 7 for TCP/IP transactions i.e. HTTP, FTP and Telnet. +const size_t NumTcpSockets = 7; +const uint8_t DhcpSocketNumber = 7; -// Assign a status to each NetworkTransaction -enum TransactionStatus -{ - released, - connected, - receiving, - sending, - disconnected, - deferred, - acquired -}; - -// How is a deferred request supposed to be handled? -enum class DeferralMode -{ - DeferOnly, // don't change anything, because we want to read more of it next time - ResetData, // keep the data and reset all reading pointers allowing us to process it again - DiscardData // discard all incoming data and re-enqueue the empty transaction -}; - -// Start with a class to hold input and output from the network that needs to be responded to. -// This includes changes in the connection state, e.g. connects and disconnects. -class NetworkTransaction -{ - public: - friend class Network; - - NetworkTransaction(NetworkTransaction* n); - void Set(pbuf *p, ConnectionState* c, TransactionStatus s); - TransactionStatus GetStatus() const { return status; } - bool IsConnected() const; - - bool HasMoreDataToRead() const; // { return readingPb != nullptr; } - bool Read(char& b); - bool ReadBuffer(const char *&buffer, size_t &len); - void Write(char b); - void Write(const char* s); - void Write(StringRef ref); - void Write(const char* s, size_t len); - void Write(OutputBuffer *buffer); - void Write(OutputStack *stack); - void Printf(const char *fmt, ...); - void SetFileToWrite(FileStore *file); - - ConnectionState *GetConnection() const { return cs; } - uint16_t GetLocalPort() const; - uint32_t GetRemoteIP() const; - uint16_t GetRemotePort() const; - - void Commit(bool keepConnectionAlive); - void Defer(DeferralMode mode); - void Discard(); - - private: - bool CanWrite() const; - bool Send(); - void Close(); - - ConnectionState* cs; - NetworkTransaction* volatile next; // next NetworkTransaction in the list we are in - NetworkTransaction* volatile nextWrite; // next NetworkTransaction queued to write to assigned connection -// pbuf *pb, *readingPb; // received packet queue and a pointer to the pbuf being read from -// size_t inputPointer; // amount of data already taken from the first packet buffer - - OutputBuffer *sendBuffer; - OutputStack *sendStack; - FileStore * volatile fileBeingSent; - - volatile TransactionStatus status; - volatile bool closeRequested, dataAcknowledged; -}; +class Platform; // The main network class that drives the network. class Network { - enum NetworkState - { - disabled, // WiFi not active - enabled, // WiFi enabled but not started yet - starting, // starting up (waiting for initialisation) - running -// idle, // nothing happening -// receivePending, // we have asserted TransferReady and await completion of a receive-only transaction -// sendReceivePending, // we have asserted TransferReady and await completion of a transmit/receive -// transferDone, // transfer completed but receive DMA fifo may not have been flushed yet -// processing, // a transaction has been completed but we haven't released the input buffer yet -// sending // a transaction has been completed and we are sending the response - }; public: const uint8_t *GetIPAddress() const; void SetIPAddress(const uint8_t p_ipAddress[], const uint8_t p_netmask[], const uint8_t p_gateway[]); @@ -183,19 +78,26 @@ public: bool AcquireTelnetTransaction(); private: - void SetupSpi(); + enum class NetworkState + { + disabled, // WiFi not active + enabled, // WiFi enabled but not started yet + establishingLink, // starting up (waiting for initialisation) + obtainingIP, + active + }; Platform *platform; + float longWait; + uint32_t lastTickMillis; -// uint32_t responseIp; - uint32_t responseCode; -// uint32_t responseFragment; - OutputBuffer *responseBody; - const char* responseText; - FileStore *responseFile; -// uint32_t responseFileBytes; + void AppendTransaction(NetworkTransaction* * list, NetworkTransaction *r); + void PrependTransaction(NetworkTransaction* * list, NetworkTransaction *r); + bool AcquireTransaction(ConnectionState *cs); - float longWait; + NetworkTransaction * freeTransactions; + NetworkTransaction * readyTransactions; + NetworkTransaction * writingTransactions; uint16_t httpPort; uint8_t ipAddress[4]; @@ -205,16 +107,7 @@ private: NetworkState state; bool activated; + bool usingDhcp; }; -inline bool NetworkTransaction::IsConnected() const -{ - return (cs != nullptr && cs->IsConnected()); -} - -inline bool NetworkTransaction::CanWrite() const -{ - return (IsConnected() && status != released); -} - #endif diff --git a/src/DuetNG/DuetEthernet/NetworkTransacrion.cpp b/src/DuetNG/DuetEthernet/NetworkTransacrion.cpp new file mode 100644 index 00000000..e954723d --- /dev/null +++ b/src/DuetNG/DuetEthernet/NetworkTransacrion.cpp @@ -0,0 +1,595 @@ +/* + * NetworkTransacrion.cpp + * + * Created on: 23 Dec 2016 + * Author: David + */ + +#include "NetworkTransaction.h" +#include + +//*************************************************************************************************** + +// ConnectionState class + +#if 0 +void ConnectionState::Init(tcp_pcb *p) +{ + pcb = p; + localPort = p->local_port; + remoteIPAddress = p->remote_ip.addr; + remotePort = p->remote_port; + next = nullptr; + sendingTransaction = nullptr; + persistConnection = true; + isTerminated = false; +} +#endif + +void ConnectionState::Terminate() +{ + //TODO +#if 0 + if (pcb != nullptr) + { + tcp_abort(pcb); + } +#endif +} + +bool ConnectionState::IsConnected() const +{ + //TODO + return false; +} + +//*************************************************************************************************** +// NetworkTransaction class + +NetworkTransaction::NetworkTransaction(NetworkTransaction *n) : next(n), status(released) +{ + sendStack = new OutputStack(); +} + +#if 0 +void NetworkTransaction::Set(pbuf *p, ConnectionState *c, TransactionStatus s) +{ + cs = c; +// pb = readingPb = p; + status = s; +// inputPointer = 0; + sendBuffer = nullptr; + fileBeingSent = nullptr; + closeRequested = false; + nextWrite = nullptr; + dataAcknowledged = false; +} +#endif + +bool NetworkTransaction::HasMoreDataToRead() const +{ + //TODO + return false; +} + +// Read one char from the NetworkTransaction +bool NetworkTransaction::Read(char& b) +{ +#if 1 + return false; +#else + if (readingPb == nullptr) + { + b = 0; + return false; + } + + b = ((const char*)readingPb->payload)[inputPointer++]; + if (inputPointer == readingPb->len) + { + readingPb = readingPb->next; + inputPointer = 0; + } + return true; +#endif +} + +// Read data from the NetworkTransaction and return true on success +bool NetworkTransaction::ReadBuffer(const char *&buffer, size_t &len) +{ +#if 1 + return false; +#else + if (readingPb == nullptr) + { + return false; + } + + if (inputPointer >= readingPb->len) + { + readingPb = readingPb->next; + inputPointer = 0; + if (readingPb == nullptr) + { + return false; + } + } + + buffer = (const char*)readingPb->payload + inputPointer; + len = readingPb->len - inputPointer; + readingPb = readingPb->next; + inputPointer = 0; + return true; +#endif +} + +void NetworkTransaction::Write(char b) +{ + if (CanWrite()) + { + if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) + { + // Should never get here + return; + } + sendBuffer->cat(b); + } +} + +void NetworkTransaction::Write(const char* s) +{ + if (CanWrite()) + { + if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) + { + // Should never get here + return; + } + sendBuffer->cat(s); + } +} + +void NetworkTransaction::Write(StringRef ref) +{ + Write(ref.Pointer(), ref.strlen()); +} + +void NetworkTransaction::Write(const char* s, size_t len) +{ + if (CanWrite()) + { + if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) + { + // Should never get here + return; + } + sendBuffer->cat(s, len); + } +} + +void NetworkTransaction::Write(OutputBuffer *buffer) +{ + if (CanWrite()) + { + // Note we use an individual stack here, because we don't want to link different + // OutputBuffers for different destinations together... + sendStack->Push(buffer); + } + else + { + // Don't keep buffers we can't send... + OutputBuffer::ReleaseAll(buffer); + } +} + +void NetworkTransaction::Write(OutputStack *stack) +{ + if (stack != nullptr) + { + if (CanWrite()) + { + sendStack->Append(stack); + } + else + { + stack->ReleaseAll(); + } + } +} + +void NetworkTransaction::Printf(const char* fmt, ...) +{ + if (CanWrite() && (sendBuffer != nullptr || OutputBuffer::Allocate(sendBuffer))) + { + va_list p; + va_start(p, fmt); + sendBuffer->vprintf(fmt, p); + va_end(p); + } +} + +void NetworkTransaction::SetFileToWrite(FileStore *file) +{ + if (CanWrite()) + { + fileBeingSent = file; + } + else if (file != nullptr) + { + file->Close(); + } +} + +// Send exactly one TCP window of data and return true when this transaction can be released +bool NetworkTransaction::Send() +{ +#if 1 + return true; +#else + // Free up this transaction if the connection is supposed to be closed + if (closeRequested) + { + reprap.GetNetwork()->ConnectionClosed(cs, true); // This will release the transaction too + return false; + } + + // Fill up the TCP window with some data chunks from our OutputBuffer instances + size_t bytesBeingSent = 0, bytesLeftToSend = TCP_WND; + while (sendBuffer != nullptr && bytesLeftToSend > 0) + { + size_t copyLength = min(bytesLeftToSend, sendBuffer->BytesLeft()); + memcpy(sendingWindow + bytesBeingSent, sendBuffer->Read(copyLength), copyLength); + bytesBeingSent += copyLength; + bytesLeftToSend -= copyLength; + + if (sendBuffer->BytesLeft() == 0) + { + sendBuffer = OutputBuffer::Release(sendBuffer); + if (sendBuffer == nullptr) + { + sendBuffer = sendStack->Pop(); + } + } + } + + // We also intend to send a file, so check if we can fill up the TCP window + if (sendBuffer == nullptr && bytesLeftToSend != 0 && fileBeingSent != nullptr) + { + // For HSMCI efficiency, read from the file in multiples of 4 bytes except at the end. + // This ensures that the second and subsequent chunks can be DMA'd directly into sendingWindow. + size_t bytesToRead = bytesLeftToSend & (~3); + if (bytesToRead != 0) + { + int bytesRead = fileBeingSent->Read(sendingWindow + bytesBeingSent, bytesToRead); + if (bytesRead > 0) + { + bytesBeingSent += bytesRead; + } + + if (bytesRead != (int)bytesToRead) + { + fileBeingSent->Close(); + fileBeingSent = nullptr; + } + } + } + + if (bytesBeingSent == 0) + { + // If we have no data to send, this connection can be closed next time + if (!cs->persistConnection && nextWrite == nullptr) + { + Close(); + return false; + } + + // We want to send data from another transaction as well, so only free up this one + cs->sendingTransaction = nextWrite; + return true; + } + + // The TCP window has been filled up as much as possible, so send it now. There is no need to check + // the available space in the SNDBUF queue, because we really write only one TCP window at once. + writeResult = tcp_write(cs->pcb, sendingWindow, bytesBeingSent, 0); + if (ERR_IS_FATAL(writeResult)) + { + reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Failed to write data in Send (code %d)\n", writeResult); + tcp_abort(cs->pcb); + return false; + } + + outputResult = tcp_output(cs->pcb); + if (ERR_IS_FATAL(outputResult)) + { + reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: Failed to output data in Send (code %d)\n", outputResult); + tcp_abort(cs->pcb); + return false; + } + + if (outputResult != ERR_OK && reprap.Debug(moduleNetwork)) + { + reprap.GetPlatform()->MessageF(HOST_MESSAGE, "Network: tcp_output resulted in error code %d\n", outputResult); + } + + // Set LwIP callbacks for ACK and retransmission handling + tcp_poll(cs->pcb, conn_poll, TCP_WRITE_TIMEOUT / TCP_SLOW_INTERVAL / TCP_MAX_SEND_RETRIES); + tcp_sent(cs->pcb, conn_sent); + + // Set all values for the send process + sendingConnection = cs; + sendingRetries = 0; + sendingWindowSize = sentDataOutstanding = bytesBeingSent; + return false; +#endif +} + +// This is called by the Webserver to send output data to a client. If keepConnectionAlive is set to false, +// the current connection will be terminated once everything has been sent. +void NetworkTransaction::Commit(bool keepConnectionAlive) +{ +#if 0 + // If the connection has been terminated (e.g. RST received while writing upload data), discard this transaction + if (!IsConnected() || status == released) + { + Discard(); + return; + } + + // Free buffer holding the incoming data and prepare some values for the sending process + FreePbuf(); + cs->persistConnection = keepConnectionAlive; + if (sendBuffer == nullptr) + { + sendBuffer = sendStack->Pop(); + } + status = sending; + + // Unlink the item(s) from the list of ready transactions + if (keepConnectionAlive) + { + // Our connection is still of interest, remove only this transaction from the list + NetworkTransaction *previous = nullptr; + for(NetworkTransaction *item = reprap.GetNetwork()->readyTransactions; item != nullptr; item = item->next) + { + if (item == this) + { + if (previous == nullptr) + { + reprap.GetNetwork()->readyTransactions = next; + } + else + { + previous->next = next; + } + break; + } + previous = item; + } + } + else + { + // We will close this connection soon, stop receiving data from this PCB + tcp_recv(cs->pcb, nullptr); + + // Also remove all ready transactions pointing to our ConnectionState + NetworkTransaction *previous = nullptr, *item = reprap.GetNetwork()->readyTransactions; + while (item != nullptr) + { + if (item->cs == cs) + { + if (item == this) + { + // Only unlink this item + if (previous == nullptr) + { + reprap.GetNetwork()->readyTransactions = next; + } + else + { + previous->next = next; + } + item = next; + } + else + { + // Remove all others + item->Discard(); + item = (previous == nullptr) ? reprap.GetNetwork()->readyTransactions : previous->next; + } + } + else + { + previous = item; + item = item->next; + } + } + } + + // Enqueue this transaction, so it's sent in the right order + NetworkTransaction *mySendingTransaction = cs->sendingTransaction; + if (mySendingTransaction == nullptr) + { + cs->sendingTransaction = this; + reprap.GetNetwork()->AppendTransaction(&reprap.GetNetwork()->writingTransactions, this); + } + else + { + while (mySendingTransaction->nextWrite != nullptr) + { + mySendingTransaction = mySendingTransaction->nextWrite; + } + mySendingTransaction->nextWrite = this; + } +#endif +} + +// Call this to perform some networking tasks while processing deferred requests, +// and to move this transaction and all transactions that are associated with its +// connection to the end of readyTransactions. There are three ways to do this: +// +// 1) DeferOnly: Do not modify any of the processed data and don't send an ACK. +// This will ensure that zero-window packets are sent back to the client +// 2) ResetData: Reset the read pointers and acknowledge that the data has been processed +// 3) DiscardData: Free the processed data, acknowledge it and append this transaction as +// an empty item again without payload (i.e. without pbufs) +// +void NetworkTransaction::Defer(DeferralMode mode) +{ +#if 0 + if (mode == DeferralMode::ResetData) + { + // Reset the reading pointers and send an ACK + inputPointer = 0; + readingPb = pb; + if (IsConnected() && pb != nullptr && !dataAcknowledged) + { + tcp_recved(cs->pcb, pb->tot_len); + dataAcknowledged = true; + } + } + else if (mode == DeferralMode::DiscardData) + { + // Discard the incoming data, because we don't need to process it any more + FreePbuf(); + } + + status = deferred; + + // Unlink this transaction from the list of ready transactions and append it again + Network *network = reprap.GetNetwork(); + NetworkTransaction *item, *previous = nullptr; + for(item = network->readyTransactions; item != nullptr; item = item->next) + { + if (item == this) + { + if (previous == nullptr) + { + network->readyTransactions = next; + } + else + { + previous->next = next; + } + break; + } + previous = item; + } + network->AppendTransaction(&network->readyTransactions, this); + + // Append all other transactions that are associated to this connection, so that the + // Webserver gets a chance to deal with all connected clients even while multiple + // deferred requests are present in the list. + item = network->readyTransactions; + previous = nullptr; + while (item != this) + { + if (item->cs == cs) + { + NetworkTransaction *nextItem = item->next; + if (previous == nullptr) + { + network->readyTransactions = item->next; + network->AppendTransaction(&network->readyTransactions, item); + } + else + { + previous->next = item->next; + network->AppendTransaction(&network->readyTransactions, item); + } + item = nextItem; + } + else + { + previous = item; + item = item->next; + } + } +#endif +} + + +// This method should be called if we don't want to send data to the client and if we +// don't want to interfere with the connection state. May also be called from ISR! +void NetworkTransaction::Discard() +{ +#if 0 + // Can we do anything? + if (status == released) + { + // No - don't free up released items multiple times + return; + } + + // Free up some resources + FreePbuf(); + + if (fileBeingSent != nullptr) + { + fileBeingSent->Close(); + fileBeingSent = nullptr; + } + + OutputBuffer::ReleaseAll(sendBuffer); + sendStack->ReleaseAll(); + + // Unlink this transactions from the list of ready transactions and free it. It is then appended to the list of + // free transactions because we don't want to risk reusing it when the ethernet ISR processes incoming data + NetworkTransaction *previous = nullptr; + for(NetworkTransaction *item = reprap.GetNetwork()->readyTransactions; item != nullptr; item = item->next) + { + if (item == this) + { + if (previous == nullptr) + { + reprap.GetNetwork()->readyTransactions = next; + } + else + { + previous->next = next; + } + break; + } + previous = item; + } + reprap.GetNetwork()->AppendTransaction(&reprap.GetNetwork()->freeTransactions, this); + bool callDisconnectHandler = (cs != nullptr && status == disconnected); + status = released; + + // Call disconnect event if this transaction indicates a graceful disconnect and if the connection + // still persists (may not be the case if a RST packet was received before) + if (callDisconnectHandler) + { + if (reprap.Debug(moduleNetwork)) + { + reprap.GetPlatform()->Message(HOST_MESSAGE, "Network: Discard() is handling a graceful disconnect\n"); + } + reprap.GetNetwork()->ConnectionClosed(cs, false); + } +#endif +} + +uint32_t NetworkTransaction::GetRemoteIP() const +{ + return (cs != nullptr) ? cs->GetRemoteIP() : 0; +} + +uint16_t NetworkTransaction::GetRemotePort() const +{ + return (cs != nullptr) ? cs->GetRemotePort() : 0; +} + +uint16_t NetworkTransaction::GetLocalPort() const +{ + return (cs != nullptr) ? cs->GetLocalPort() : 0; +} + +void NetworkTransaction::Close() +{ +#if 0 + tcp_pcb *pcb = cs->pcb; + tcp_recv(pcb, nullptr); + closeRequested = true; +#endif +} + +// End diff --git a/src/DuetNG/DuetEthernet/NetworkTransaction.h b/src/DuetNG/DuetEthernet/NetworkTransaction.h new file mode 100644 index 00000000..91a9eedb --- /dev/null +++ b/src/DuetNG/DuetEthernet/NetworkTransaction.h @@ -0,0 +1,160 @@ +/* + * NetworkTransaction.h + * + * Created on: 23 Dec 2016 + * Author: David + */ + +#ifndef SRC_DUETNG_DUETETHERNET_NETWORKTRANSACTION_H_ +#define SRC_DUETNG_DUETETHERNET_NETWORKTRANSACTION_H_ + +#include +#include +#include "Libraries/General/StringRef.h" +#include "OutputMemory.h" +#include "Storage/FileStore.h" + +typedef uint8_t SocketNumber; + +const SocketNumber NoSocket = 255; + +// Network buffer class. These buffers are 2K long so that they can accept as much data as the W5500 can provide in one go. +class NetworkBuffer +{ +public: + friend class NetworkTransaction; + + NetworkBuffer(); + + // Release this buffer and return the next one in the chain + NetworkBuffer *Release(); + + // Read 1 character, returning true of successful, false if no data left + bool Read(char& b); + + // Read some data + bool ReadBuffer(const char *&buffer, size_t &len); + + // Return the amount of data available, not including continuation buffers + size_t Remaining() const; + + // Return the amount of data available, including continuation buffers + size_t TotalRemaining() const; + +private: + NetworkBuffer *next; + size_t dataLength; + size_t readPointer; + uint8_t data[2048]; +}; + +class NetworkTransaction; + +// ConnectionState structure that we use to track TCP connections. It is usually combined with NetworkTransactions. +class ConnectionState +{ +public: + void Init(SocketNumber s); + uint16_t GetLocalPort() const { return localPort; } + uint32_t GetRemoteIP() const { return remoteIPAddress; } + uint16_t GetRemotePort() const { return remotePort; } + bool IsConnected() const; // { return pcb != nullptr; } + bool IsTerminated() const { return isTerminated; } + void Terminate(); + +private: + uint16_t localPort, remotePort; // Copy of the local and remote ports, because the PCB may be unavailable + uint32_t remoteIPAddress; // Same for the remote IP address + NetworkTransaction * volatile sendingTransaction; // NetworkTransaction that is currently sending via this connection + ConnectionState * volatile next; // Next ConnectionState in this list + bool persistConnection; // Do we expect this connection to stay alive? + bool isTerminated; // Will be true if the connection has gone down unexpectedly (TCP RST) + SocketNumber socketNum; // The W5500 socket number we are using +}; + +// Assign a status to each NetworkTransaction +enum TransactionStatus +{ + released, + connected, + receiving, + sending, + disconnected, + deferred, + acquired +}; + +// How is a deferred request supposed to be handled? +enum class DeferralMode +{ + DeferOnly, // don't change anything, because we want to read more of it next time + ResetData, // keep the data and reset all reading pointers allowing us to process it again + DiscardData // discard all incoming data and re-enqueue the empty transaction +}; + +// Start with a class to hold input and output from the network that needs to be responded to. +// This includes changes in the connection state, e.g. connects and disconnects. +class NetworkTransaction +{ +public: + friend class Network; + + NetworkTransaction(NetworkTransaction* n); +// void Set(pbuf *p, ConnectionState* c, TransactionStatus s); + TransactionStatus GetStatus() const { return status; } + bool IsConnected() const; + + bool HasMoreDataToRead() const; // { return readingPb != nullptr; } + bool Read(char& b); + bool ReadBuffer(const char *&buffer, size_t &len); + void Write(char b); + void Write(const char* s); + void Write(StringRef ref); + void Write(const char* s, size_t len); + void Write(OutputBuffer *buffer); + void Write(OutputStack *stack); + void Printf(const char *fmt, ...); + void SetFileToWrite(FileStore *file); + + ConnectionState *GetConnection() const { return cs; } + uint16_t GetLocalPort() const; + uint32_t GetRemoteIP() const; + uint16_t GetRemotePort() const; + + bool Send(); + void Commit(bool keepConnectionAlive); + void Defer(DeferralMode mode); + void Discard(); + + NetworkTransaction *GetNext() const { return next; } + NetworkTransaction *GetNextWrite() const { return nextWrite; } + +private: + bool CanWrite() const; + void Close(); + + ConnectionState* cs; + NetworkTransaction* next; // next NetworkTransaction in the list we are in + NetworkTransaction* nextWrite; // next NetworkTransaction queued to write to assigned connection + NetworkBuffer *pb, *readingPb; // received packet queue and a pointer to the pbuf being read from +// size_t inputPointer; // amount of data already taken from the first packet buffer + + OutputBuffer *sendBuffer; + OutputStack *sendStack; + FileStore * volatile fileBeingSent; + + volatile TransactionStatus status; + volatile bool closeRequested, dataAcknowledged; +}; + +inline bool NetworkTransaction::IsConnected() const +{ + return (cs != nullptr && cs->IsConnected()); +} + +inline bool NetworkTransaction::CanWrite() const +{ + return (IsConnected() && status != released); +} + +#endif /* SRC_DUETNG_DUETETHERNET_NETWORKTRANSACTION_H_ */ diff --git a/src/DuetNG/DuetEthernet/Webserver.cpp b/src/DuetNG/DuetEthernet/Webserver.cpp index 293b77e0..af1bdcbe 100644 --- a/src/DuetNG/DuetEthernet/Webserver.cpp +++ b/src/DuetNG/DuetEthernet/Webserver.cpp @@ -121,36 +121,26 @@ void Webserver::Init() // Deal with input/output from/to the client (if any) void Webserver::Spin() { - if (webserverActive) + // Check if we are enabled and we can actually send something back to the client + if (webserverActive && OutputBuffer::GetBytesLeft(nullptr) != 0) { - // Check if we can actually send something back to the client - if (OutputBuffer::GetBytesLeft(nullptr) == 0) - { - platform->ClassReport(longWait); - return; - } - // We must ensure that we have exclusive access to LWIP if (!network->Lock()) { - platform->ClassReport(longWait); - return; - } - - // Allow each ProtocolInterpreter to do something - httpInterpreter->Spin(); - ftpInterpreter->Spin(); - telnetInterpreter->Spin(); + // Allow each ProtocolInterpreter to do something + httpInterpreter->Spin(); + ftpInterpreter->Spin(); + telnetInterpreter->Spin(); - // See if we have new data to process - currentTransaction = network->GetTransaction(readingConnection); - if (currentTransaction != nullptr) - { - // Take care of different protocol types here - ProtocolInterpreter *interpreter; - uint16_t localPort = currentTransaction->GetLocalPort(); - switch (localPort) + // See if we have new data to process + currentTransaction = network->GetTransaction(readingConnection); + if (currentTransaction != nullptr) { + // Take care of different protocol types here + ProtocolInterpreter *interpreter; + const uint16_t localPort = currentTransaction->GetLocalPort(); + switch (localPort) + { case FTP_PORT: /* FTP */ interpreter = ftpInterpreter; break; @@ -169,82 +159,82 @@ void Webserver::Spin() interpreter = ftpInterpreter; } break; - } + } - // See if we have to print some debug info - if (reprap.Debug(moduleWebserver)) - { - const char *type; - switch (currentTransaction->GetStatus()) + // See if we have to print some debug info + if (reprap.Debug(moduleWebserver)) { - case released: type = "released"; break; - case connected: type = "connected"; break; - case receiving: type = "receiving"; break; - case sending: type = "sending"; break; - case disconnected: type = "disconnected"; break; - case deferred: type = "deferred"; break; - case acquired: type = "acquired"; break; - default: type = "unknown"; break; + const char *type; + switch (currentTransaction->GetStatus()) + { + case released: type = "released"; break; + case connected: type = "connected"; break; + case receiving: type = "receiving"; break; + case sending: type = "sending"; break; + case disconnected: type = "disconnected"; break; + case deferred: type = "deferred"; break; + case acquired: type = "acquired"; break; + default: type = "unknown"; break; + } + platform->MessageF(HOST_MESSAGE, "Incoming transaction: Type %s at local port %d (remote port %d)\n", + type, localPort, currentTransaction->GetRemotePort()); } - platform->MessageF(HOST_MESSAGE, "Incoming transaction: Type %s at local port %d (remote port %d)\n", - type, localPort, currentTransaction->GetRemotePort()); - } - // For protocols other than HTTP it is important to send a HELO message - TransactionStatus status = currentTransaction->GetStatus(); - if (status == connected) - { - interpreter->ConnectionEstablished(); - } - // Graceful disconnects are handled here, because prior NetworkTransactions might still contain valid - // data. That's why it's a bad idea to close these connections immediately in the Network class. - else if (status == disconnected) - { - // This will call the disconnect events and effectively close the connection - currentTransaction->Discard(); - } - // Check for fast uploads via this connection - else if (interpreter->DoingFastUpload()) - { - interpreter->DoFastUpload(); - } - // Process other messages (if we can) - else if (interpreter->CanParseData()) - { - readingConnection = currentTransaction->GetConnection(); - for(size_t i = 0; i < TCP_MSS / 3; i++) + // For protocols other than HTTP it is important to send a HELO message + TransactionStatus status = currentTransaction->GetStatus(); + if (status == connected) + { + interpreter->ConnectionEstablished(); + } + // Graceful disconnects are handled here, because prior NetworkTransactions might still contain valid + // data. That's why it's a bad idea to close these connections immediately in the Network class. + else if (status == disconnected) + { + // This will call the disconnect events and effectively close the connection + currentTransaction->Discard(); + } + // Check for fast uploads via this connection + else if (interpreter->DoingFastUpload()) + { + interpreter->DoFastUpload(); + } + // Process other messages (if we can) + else if (interpreter->CanParseData()) { - char c; - if (currentTransaction->Read(c)) + readingConnection = currentTransaction->GetConnection(); + for (size_t i = 0; i < TCP_MSS / 3; i++) { - // Each ProtocolInterpreter must take care of the current NetworkTransaction by - // calling either Commit(), Discard() or Defer() - if (interpreter->CharFromClient(c)) + char c; + if (currentTransaction->Read(c)) { + // Each ProtocolInterpreter must take care of the current NetworkTransaction by calling either Commit(), Discard() or Defer() + if (interpreter->CharFromClient(c)) + { + readingConnection = nullptr; + break; + } + } + else + { + // We ran out of data before finding a complete request. This happens when the incoming + // message length exceeds the TCP MSS. Notify the current ProtocolInterpreter about this, + // which will remove the current transaction too + interpreter->NoMoreDataAvailable(); readingConnection = nullptr; break; } } - else - { - // We ran out of data before finding a complete request. This happens when the incoming - // message length exceeds the TCP MSS. Notify the current ProtocolInterpreter about this, - // which will remove the current transaction too - interpreter->NoMoreDataAvailable(); - readingConnection = nullptr; - break; - } } } + else if (readingConnection != nullptr) + { + // We failed to find a transaction for a reading connection. + // This should never happen, but if it does, terminate this connection instantly + platform->Message(HOST_MESSAGE, "Error: Transaction for reading connection not found\n"); + readingConnection->Terminate(); + } + network->Unlock(); // unlock LWIP again } - else if (readingConnection != nullptr) - { - // We failed to find a transaction for a reading connection. - // This should never happen, but if it does, terminate this connection instantly - platform->Message(HOST_MESSAGE, "Error: Transaction for reading connection not found\n"); - readingConnection->Terminate(); - } - network->Unlock(); // unlock LWIP again } platform->ClassReport(longWait); } diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.cpp b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.cpp index 7447386b..3fab829e 100644 --- a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.cpp +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.cpp @@ -53,6 +53,7 @@ //***************************************************************************** #include "w5500.h" +#include "WizSpi.h" #define _W5500_SPI_VDM_OP_ 0x00 #define _W5500_SPI_FDM_OP_LEN1_ 0x01 @@ -63,35 +64,35 @@ uint8_t WIZCHIP_READ(uint32_t AddrSel) { - SpiAssertSS(); - SpiSendAddress(AddrSel | (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_)); - const uint8_t ret = SpiReadByte(); - SpiReleaseSS(); + WizSpi::AssertSS(); + WizSpi::SendAddress(AddrSel | (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_)); + const uint8_t ret = WizSpi::ExchangeByte(0xFF, true); + WizSpi::ReleaseSS(); return ret; } void WIZCHIP_WRITE(uint32_t AddrSel, uint8_t wb ) { - SpiAssertSS(); - SpiSendAddress(AddrSel | (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_)); - SpiSendByte(wb); - SpiReleaseSS(); + WizSpi::AssertSS(); + WizSpi::SendAddress(AddrSel | (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_)); + WizSpi::ExchangeByte(wb, true); + WizSpi::ReleaseSS(); } void WIZCHIP_READ_BUF (uint32_t AddrSel, uint8_t* pBuf, uint16_t len) { - SpiAssertSS(); - SpiSendAddress(AddrSel | (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_)); - SpiReadBurst(pBuf, len); - SpiReleaseSS(); + WizSpi::AssertSS(); + WizSpi::SendAddress(AddrSel | (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_)); + WizSpi::ReadBurst(pBuf, len); + WizSpi::ReleaseSS(); } void WIZCHIP_WRITE_BUF(uint32_t AddrSel, const uint8_t* pBuf, uint16_t len) { - SpiAssertSS(); - SpiSendAddress(AddrSel | (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_)); - SpiSendBurst(pBuf, len); - SpiReleaseSS(); + WizSpi::AssertSS(); + WizSpi::SendAddress(AddrSel | (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_)); + WizSpi::SendBurst(pBuf, len); + WizSpi::ReleaseSS(); } @@ -128,7 +129,7 @@ uint16_t getSn_RX_RSR(uint8_t sn) return val; } -void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len) +void wiz_send_data(uint8_t sn, const uint8_t *wizdata, uint16_t len) { if (len != 0) { diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.h b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.h index 048c47eb..495772ac 100644 --- a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.h +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/W5500/w5500.h @@ -2266,7 +2266,7 @@ static inline uint16_t getSn_TxMAX(uint8_t sn) * @param len Data length * @sa wiz_recv_data() */ -void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len); +void wiz_send_data(uint8_t sn, const uint8_t *wizdata, uint16_t len); /** * @ingroup Basic_IO_function diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.cpp b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.cpp new file mode 100644 index 00000000..c32a14a5 --- /dev/null +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.cpp @@ -0,0 +1,431 @@ +/* + * WizSpi.cpp + * + * Created on: 16 Dec 2016 + * Author: David + */ + +#include "WizSpi.h" +#include "variant.h" +#include "Pins.h" + +// Define exactly one of the following as 1, the other as zero +// The PDC seems to be too slow to work reliably without getting transmit underruns, so we use the DMAC now. +#define USE_PDC 0 // use peripheral DMA controller +#define USE_DMAC 0 // use general DMA controller + +#if USE_PDC +#include "pdc.h" +#endif + +#if USE_DMAC +#include "dmac.h" +#endif + +#include "matrix.h" + +// Functions called by the W5500 module to transfer data to/from the W5500 via SPI +const uint32_t SpiClockFrequency = 10000000; // use 10MHz for now, increase it later +const unsigned int SpiPeripheralChannelId = 0; // we use NPCS0 as the slave select signal + +#if USE_PDC +static Pdc *spi_pdc; +static inline void spi_rx_dma_enable() +{ + pdc_enable_transfer(spi_pdc, PERIPH_PTCR_RXTEN); +} + +static inline void spi_tx_dma_enable() +{ + pdc_enable_transfer(spi_pdc, PERIPH_PTCR_TXTEN); +} + +static inline void spi_rx_dma_disable() +{ + pdc_disable_transfer(spi_pdc, PERIPH_PTCR_RXTDIS); +} + +static inline void spi_tx_dma_disable() +{ + pdc_disable_transfer(spi_pdc, PERIPH_PTCR_TXTDIS); +} + +static bool spi_dma_check_rx_complete() +{ + return true; +} + +static void spi_tx_dma_setup(const TransactionBuffer *buf, uint32_t maxTransmitLength) +{ + pdc_packet_t pdc_spi_packet; + pdc_spi_packet.ul_addr = reinterpret_cast(buf); + pdc_spi_packet.ul_size = buf->PacketLength() * 4; // need length in bytes + pdc_tx_init(spi_pdc, &pdc_spi_packet, NULL); +} + +static void spi_rx_dma_setup(const TransactionBuffer *buf) +{ + pdc_packet_t pdc_spi_packet; + pdc_spi_packet.ul_addr = reinterpret_cast(buf); + pdc_spi_packet.ul_size = TransactionBuffer::MaxReceiveBytes; + pdc_rx_init(spi_pdc, &pdc_spi_packet, NULL); +} + +#endif + +#if USE_DMAC + +// Our choice of DMA channels to use +const uint32_t CONF_SPI_DMAC_TX_CH = 1; +const uint32_t CONF_SPI_DMAC_RX_CH = 2; + +// Hardware IDs of the SPI transmit and receive DMA interfaces. See atsam datasheet. +const uint32_t DMA_HW_ID_SPI_TX = 1; +const uint32_t DMA_HW_ID_SPI_RX = 2; + +static inline void spi_rx_dma_enable() +{ + dmac_channel_enable(DMAC, CONF_SPI_DMAC_RX_CH); +} + +static inline void spi_tx_dma_enable() +{ + dmac_channel_enable(DMAC, CONF_SPI_DMAC_TX_CH); +} + +static inline void spi_rx_dma_disable() +{ + dmac_channel_disable(DMAC, CONF_SPI_DMAC_RX_CH); +} + +static inline void spi_tx_dma_disable() +{ + dmac_channel_disable(DMAC, CONF_SPI_DMAC_TX_CH); +} + +static bool spi_dma_check_rx_complete() +{ + uint32_t status = DMAC->DMAC_CHSR; + if ( ((status & (DMAC_CHSR_ENA0 << CONF_SPI_DMAC_RX_CH)) == 0) // controller is not enabled, perhaps because it finished a full buffer transfer + || ((status & (DMAC_CHSR_EMPT0 << CONF_SPI_DMAC_RX_CH)) != 0) // controller is enabled, probably suspended, and the FIFO is empty + ) + { + // Disable the channel. + // We also need to set the resume bit, otherwise it remains suspended when we re-enable it. + DMAC->DMAC_CHDR = (DMAC_CHDR_DIS0 << CONF_SPI_DMAC_RX_CH) | (DMAC_CHDR_RES0 << CONF_SPI_DMAC_RX_CH); + return true; + } + return false; +} + +static void spi_tx_dma_setup(const TransactionBuffer *buf, uint32_t maxTransmitLength) +{ + DMAC->DMAC_EBCISR; // clear any pending interrupts + + dmac_channel_set_source_addr(DMAC, CONF_SPI_DMAC_TX_CH, reinterpret_cast(buf)); + dmac_channel_set_destination_addr(DMAC, CONF_SPI_DMAC_TX_CH, reinterpret_cast(& SPI->SPI_TDR)); + dmac_channel_set_descriptor_addr(DMAC, CONF_SPI_DMAC_TX_CH, 0); + dmac_channel_set_ctrlA(DMAC, CONF_SPI_DMAC_TX_CH, maxTransmitLength | DMAC_CTRLA_SRC_WIDTH_WORD | DMAC_CTRLA_DST_WIDTH_BYTE); + dmac_channel_set_ctrlB(DMAC, CONF_SPI_DMAC_TX_CH, + DMAC_CTRLB_SRC_DSCR | DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC | DMAC_CTRLB_SRC_INCR_INCREMENTING | DMAC_CTRLB_DST_INCR_FIXED); +} + +static void spi_rx_dma_setup(const TransactionBuffer *buf) +{ + DMAC->DMAC_EBCISR; // clear any pending interrupts + + dmac_channel_set_source_addr(DMAC, CONF_SPI_DMAC_RX_CH, reinterpret_cast(& SPI->SPI_RDR)); + dmac_channel_set_destination_addr(DMAC, CONF_SPI_DMAC_RX_CH, reinterpret_cast(buf)); + dmac_channel_set_descriptor_addr(DMAC, CONF_SPI_DMAC_RX_CH, 0); +// dmac_channel_set_ctrlA(DMAC, CONF_SPI_DMAC_RX_CH, TransactionBuffer::MaxTransferBytes | DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_WORD); + dmac_channel_set_ctrlB(DMAC, CONF_SPI_DMAC_RX_CH, + DMAC_CTRLB_SRC_DSCR | DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC | DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING); +} + +#endif + +#if USE_PDC || USE_DMAC + +static void spi_dma_disable() +{ + spi_tx_dma_disable(); + spi_rx_dma_disable(); +} + +#if 0 +/** + * \brief Set SPI slave transfer. + */ +static void spi_slave_dma_setup(bool dataToSend, bool allowReceive) +{ +#if USE_PDC + pdc_disable_transfer(spi_pdc, PERIPH_PTCR_TXTDIS | PERIPH_PTCR_RXTDIS); + + TransactionBuffer *outBufPointer = (dataToSend) ? &outBuffer : reinterpret_cast(&dummyOutBuffer); + spi_tx_dma_setup(outBufPointer); + if (allowReceive) + { + outBufPointer->SetDataTaken(); + spi_rx_dma_setup(&inBuffer); + pdc_enable_transfer(spi_pdc, PERIPH_PTCR_TXTEN | PERIPH_PTCR_RXTEN); + } + else + { + outBufPointer->ClearDataTaken(); + pdc_enable_transfer(spi_pdc, PERIPH_PTCR_TXTEN); + } +#endif + +#if USE_DMAC + spi_dma_disable(); + + TransactionBuffer *outBufPointer = (dataToSend) ? &outBuffer : reinterpret_cast(&dummyOutBuffer); + if (allowReceive) + { + spi_rx_dma_setup(&inBuffer); + spi_rx_dma_enable(); +// outBufPointer->SetDataTaken(); + } + else + { +// outBufPointer->ClearDataTaken(); + } + +// spi_tx_dma_setup(outBufPointer, (dataToSend) ? TransactionBuffer::MaxTransferBytes : 4 * TransactionBuffer::headerDwords); + spi_tx_dma_enable(); +#endif +} +#endif + +#endif + +namespace WizSpi +{ + // Initialise the SPI interface + void Init() + { + #if USE_PDC + spi_pdc = spi_get_pdc_base(SPI); + // The PDCs are masters 2 and 3 and the SRAM is slave 0. Give the PDCs the highest priority. + matrix_set_master_burst_type(0, MATRIX_ULBT_8_BEAT_BURST); + matrix_set_slave_default_master_type(0, MATRIX_DEFMSTR_LAST_DEFAULT_MASTER); + matrix_set_slave_priority(0, (3 << MATRIX_PRAS0_M2PR_Pos) | (3 << MATRIX_PRAS0_M3PR_Pos)); + matrix_set_slave_slot_cycle(0, 8); + #endif + + #if USE_DMAC + pmc_enable_periph_clk(ID_DMAC); + dmac_init(DMAC); + dmac_set_priority_mode(DMAC, DMAC_PRIORITY_ROUND_ROBIN); + dmac_enable(DMAC); + // The DMAC is master 4 and the SRAM is slave 0. Give the DMAC the highest priority. + matrix_set_slave_default_master_type(0, MATRIX_DEFMSTR_LAST_DEFAULT_MASTER); + matrix_set_slave_priority(0, (3 << MATRIX_PRAS0_M4PR_Pos)); + // Set the slave slot cycle limit. + // If we leave it at the default value of 511 clock cycles, we get transmit underruns due to the HSMCI using the bus for too long. + // A value of 8 seems to work. I haven't tried other values yet. + matrix_set_slave_slot_cycle(0, 8); + #endif + + // Set up the SPI pins + ConfigurePin(g_APinDescription[APIN_SPI_SCK]); + ConfigurePin(g_APinDescription[APIN_SPI_MOSI]); + ConfigurePin(g_APinDescription[APIN_SPI_MISO]); +#if 1 + pinMode(APIN_SPI_SS0, OUTPUT_HIGH); // use manual SS control for now +#else + ConfigurePin(g_APinDescription[APIN_SPI_SS0]); +#endif + + pmc_enable_periph_clk(ID_SPI); + +#if USE_PDC || USE_DMAC + spi_dma_disable(); +#endif + + spi_reset(SPI); // this clears the transmit and receive registers and puts the SPI into slave mode + SPI->SPI_MR = SPI_MR_MSTR // master mode + | SPI_MR_MODFDIS // disable fault detection + | SPI_MR_PCS(SpiPeripheralChannelId); // fixed peripheral select + + // Set SPI mode, clock frequency, CS active after transfer, delay between transfers + const uint16_t baud_div = (uint16_t)spi_calc_baudrate_div(SpiClockFrequency, SystemCoreClock); + const uint32_t csr = SPI_CSR_SCBR(baud_div) // Baud rate + | SPI_CSR_BITS_8_BIT // Transfer bit width + | SPI_CSR_DLYBCT(0) // Transfer delay + | SPI_CSR_CSAAT // Keep CS low after transfer in case we are slow in writing the next byte + | SPI_CSR_NCPHA; // Data is captured on the leading edge of the clock (SPI mode 0) + SPI->SPI_CSR[SpiPeripheralChannelId] = csr; + spi_enable(SPI); + +#if USE_DMAC + // Configure DMA RX channel + dmac_channel_set_configuration(DMAC, CONF_SPI_DMAC_RX_CH, + DMAC_CFG_SRC_PER(DMA_HW_ID_SPI_RX) | DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG); + + // Configure DMA TX channel + dmac_channel_set_configuration(DMAC, CONF_SPI_DMAC_TX_CH, + DMAC_CFG_DST_PER(DMA_HW_ID_SPI_TX) | DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG); +#endif + } + + void Stop() + { + NVIC_DisableIRQ(SPI_IRQn); + spi_disable(SPI); +#if USE_PDC || USE_DMA + spi_dma_check_rx_complete(); + spi_dma_disable(); +#endif + } + + // Wait for transmitter ready returning true if timed out + static inline bool waitForTxReady() + { + uint32_t timeout = SPI_TIMEOUT; + while (!spi_is_tx_ready(SPI)) + { + if (--timeout == 0) + { + return true; + } + } + return false; + } + + // Wait for transmitter empty returning true if timed out + static inline bool waitForTxEmpty() + { + uint32_t timeout = SPI_TIMEOUT; + while (!spi_is_tx_empty(SPI)) + { + if (!timeout--) + { + return true; + } + } + return false; + } + + // Wait for receive data available returning true if timed out + static inline bool waitForRxReady() + { + uint32_t timeout = SPI_TIMEOUT; + while (!spi_is_rx_ready(SPI)) + { + if (--timeout == 0) + { + return true; + } + } + return false; + } + + // Set the SS pin low to address the W5500 + void AssertSS() + { + spi_set_peripheral_chip_select_value(SPI, spi_get_pcs(SpiPeripheralChannelId)); + digitalWrite(SamCsPin, LOW); + } + + // Set the SS pin high again + void ReleaseSS() + { + waitForTxEmpty(); + digitalWrite(SamCsPin, HIGH); + } + + void SendAddress(uint32_t addr) + { + (void)ExchangeByte((uint8_t)(addr >> 16), false); + (void)ExchangeByte((uint8_t)(addr >> 8), false); + (void)ExchangeByte((uint8_t)addr, false); + } + + uint8_t ExchangeByte(uint8_t b, bool isLast) + { + if (waitForTxReady()) + { + return 0; + } + + // Write to transmit register + uint32_t dOut = b; + if (isLast) + { + dOut |= SPI_TDR_LASTXFER; + } + SPI->SPI_TDR = dOut; + + // Wait for receive register + if (waitForRxReady()) + { + return 0; + } + + // Get data from receive register + return (uint8_t)SPI->SPI_RDR; + } + + spi_status_t ReadBurst(uint8_t* rx_data, size_t len) + { + for (size_t i = 0; i < len; ++i) + { + uint32_t dOut = 0x000000FF; + if (waitForTxReady()) + { + return SPI_ERROR_TIMEOUT; + } + + // Write to transmit register + if (i + 1 == len) + { + dOut |= SPI_TDR_LASTXFER; + } + SPI->SPI_TDR = dOut; + + // Wait for receive register + if (waitForRxReady()) + { + return SPI_ERROR_TIMEOUT; + } + + // Get data from receive register + *rx_data++ = (uint8_t)SPI->SPI_RDR; + } + + return SPI_OK; + } + + spi_status_t SendBurst(const uint8_t* tx_data, size_t len) + { + for (uint32_t i = 0; i < len; ++i) + { + uint32_t dOut = (uint32_t)*tx_data++; + if (waitForTxReady()) + { + return SPI_ERROR_TIMEOUT; + } + + // Write to transmit register + if (i + 1 == len) + { + dOut |= SPI_TDR_LASTXFER; + } + SPI->SPI_TDR = dOut; + // Wait for receive register + if (waitForRxReady()) + { + return SPI_ERROR_TIMEOUT; + } + + // Get data from receive register + (void)SPI->SPI_RDR; + } + + return SPI_OK; + } + +} // end namespace + +// End diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.h b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.h new file mode 100644 index 00000000..2829e168 --- /dev/null +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/WizSpi.h @@ -0,0 +1,25 @@ +/* + * Spi.h + * + * Created on: 16 Dec 2016 + * Author: David + */ + +#ifndef SRC_DUETNG_DUETETHERNET_ETHERNET3_UTILITY_WIZSPI_H_ +#define SRC_DUETNG_DUETETHERNET_ETHERNET3_UTILITY_WIZSPI_H_ + +#include "spi.h" + +namespace WizSpi +{ + void Init(); + void Stop(); + void AssertSS(); + void ReleaseSS(); + void SendAddress(uint32_t addr); + uint8_t ExchangeByte(uint8_t b, bool isLast); + spi_status_t ReadBurst(uint8_t* rx_data, size_t len); + spi_status_t SendBurst(const uint8_t* tx_data, size_t len); +} + +#endif /* SRC_DUETNG_DUETETHERNET_ETHERNET3_UTILITY_WIZSPI_H_ */ diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.cpp b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.cpp index 5aeca9b8..a647d08a 100644 --- a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.cpp +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.cpp @@ -55,6 +55,18 @@ //***************************************************************************** #include "socket.h" +#define _SOCKET_DEBUG_ + +#ifdef _SOCKET_DEBUG_ +extern "C" void debugPrintf(const char *fmt, ...); +extern "C" void delay(uint32_t); +# define DEBUG_PRINTF(...) debugPrintf(__VA_ARGS__) +#else +# define DEBUG_PRINTF(_fmt, ...) +#endif + + + #define SOCK_ANY_PORT_NUM 0xC000 static uint16_t sock_any_port = SOCK_ANY_PORT_NUM; @@ -82,7 +94,7 @@ uint8_t sock_pack_info[_WIZCHIP_SOCK_NUM_] = {0,}; static void ExecCommand(uint8_t sn, uint8_t cmd) { - setSn_CR(sn, Sn_CR_OPEN); + setSn_CR(sn, cmd); while(getSn_CR(sn) != 0) { } } @@ -309,6 +321,7 @@ int32_t send(uint8_t sn, uint8_t * buf, uint16_t len) { break; } + DEBUG_PRINTF("Socket %u need %u free %u\n", sn, len, freesize); } wiz_send_data(sn, buf, len); @@ -373,7 +386,7 @@ int32_t recv(uint8_t sn, uint8_t * buf, uint16_t len) return (int32_t)len; } -int32_t sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port) +int32_t sendto(uint8_t sn, const uint8_t * buf, uint16_t len, const uint8_t * addr, uint16_t port) { switch(getSn_MR(sn) & 0x0F) { @@ -426,10 +439,10 @@ int32_t sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t { break; } + DEBUG_PRINTF("Socket %u need %u free %u\n", sn, len, freesize); }; wiz_send_data(sn, buf, len); - ExecCommand(sn, Sn_CR_SEND); while(1) @@ -445,6 +458,10 @@ int32_t sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t setSn_IR(sn, Sn_IR_TIMEOUT); return SOCKERR_TIMEOUT; } + DEBUG_PRINTF("Socket %u waiting for send to complete, IR=%02x\n", sn, tmp); +#ifdef _SOCKET_DEBUG_ + delay(10); // to avoid too many messages +#endif } return (int32_t)len; } diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.h b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.h index d7da7a8f..24bb7f7c 100644 --- a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.h +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/socket.h @@ -294,7 +294,7 @@ int32_t recv(uint8_t sn, uint8_t * buf, uint16_t len); * @ref SOCKERR_TIMEOUT - Timeout occurred \n * @ref SOCK_BUSY - Socket is busy. */ -int32_t sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port); +int32_t sendto(uint8_t sn, const uint8_t * buf, uint16_t len, const uint8_t * addr, uint16_t port); /** * @ingroup WIZnet_socket_APIs diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.cpp b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.cpp index 85d19e26..6e3a8c2c 100644 --- a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.cpp +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.cpp @@ -53,6 +53,7 @@ #include #include #include "wizchip_conf.h" +#include "WizSpi.h" /** * @\ref WIZCHIP instance data @@ -181,8 +182,9 @@ void wizchip_sw_reset(void) setSIPR(sip); } -int8_t wizchip_init(uint8_t* txsize, uint8_t* rxsize) +int8_t wizchip_init(const uint8_t* txsize, const uint8_t* rxsize) { + WizSpi::Init(); wizchip_sw_reset(); if (txsize != nullptr) { diff --git a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.h b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.h index edb0f590..ceeec9a6 100644 --- a/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.h +++ b/src/DuetNG/DuetEthernet/Wiznet/Ethernet/wizchip_conf.h @@ -305,7 +305,7 @@ int8_t ctlnetwork(ctlnetwork_type cntype, void* arg); * @ingroup extra_functions * @brief Reset WIZCHIP by softly. */ -void wizchip_sw_reset(void); +void wizchip_sw_reset(void); /** * @ingroup extra_functions @@ -315,7 +315,7 @@ void wizchip_sw_reset(void); * @return 0 : succcess \n * -1 : fail. Invalid buffer size */ -int8_t wizchip_init(uint8_t* txsize, uint8_t* rxsize); +int8_t wizchip_init(const uint8_t* txsize, const uint8_t* rxsize); /** * @ingroup extra_functions diff --git a/src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.cpp b/src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.cpp new file mode 100644 index 00000000..079f72bf --- /dev/null +++ b/src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.cpp @@ -0,0 +1,1004 @@ +//***************************************************************************** +// +//! \file dhcp.c +//! \brief DHCP APIs implement file. +//! \details Processig DHCP protocol as DISCOVER, OFFER, REQUEST, ACK, NACK and DECLINE. +//! \version 1.1.0 +//! \date 2013/11/18 +//! \par Revision history +//! <2013/11/18> 1st Release +//! <2012/12/20> V1.1.0 +//! 1. Optimize code +//! 2. Add reg_dhcp_cbfunc() +//! 3. Add DHCP_stop() +//! 4. Integrate check_DHCP_state() & DHCP_run() to DHCP_run() +//! 5. Don't care system endian +//! 6. Add comments +//! <2012/12/26> V1.1.1 +//! 1. Modify variable declaration: dhcp_tick_1s is declared volatile for code optimization +//! \author Eric Jung & MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** + +#include "socket.h" +#include "dhcp.h" +#include + +/* If you want to display debug & processing message, Define _DHCP_DEBUG_ in dhcp.h */ + +#define _DHCP_DEBUG_ + +#ifdef _DHCP_DEBUG_ +extern "C" void debugPrintf(const char *fmt, ...); +# define DEBUG_PRINTF(...) debugPrintf(__VA_ARGS__) +#else +# define DEBUG_PRINTF(_fmt, ...) +#endif + +/* DHCP state machine. */ +#define STATE_DHCP_INIT 0 ///< Initialize +#define STATE_DHCP_DISCOVER 1 ///< send DISCOVER and wait OFFER +#define STATE_DHCP_REQUEST 2 ///< send REQEUST and wait ACK or NACK +#define STATE_DHCP_LEASED 3 ///< ReceiveD ACK and IP leased +#define STATE_DHCP_REREQUEST 4 ///< send REQUEST for maintaining leased IP +#define STATE_DHCP_RELEASE 5 ///< No use +#define STATE_DHCP_STOP 6 ///< Stop processing DHCP + +#define DHCP_FLAGSBROADCAST 0x8000 ///< The broadcast value of flags in @ref RIP_MSG +#define DHCP_FLAGSUNICAST 0x0000 ///< The unicast value of flags in @ref RIP_MSG + +/* DHCP message OP code */ +#define DHCP_BOOTREQUEST 1 ///< Request Message used in op of @ref RIP_MSG +#define DHCP_BOOTREPLY 2 ///< Reply Message used i op of @ref RIP_MSG + +/* DHCP message type */ +#define DHCP_DISCOVER 1 ///< DISCOVER message in OPT of @ref RIP_MSG +#define DHCP_OFFER 2 ///< OFFER message in OPT of @ref RIP_MSG +#define DHCP_REQUEST 3 ///< REQUEST message in OPT of @ref RIP_MSG +#define DHCP_DECLINE 4 ///< DECLINE message in OPT of @ref RIP_MSG +#define DHCP_ACK 5 ///< ACK message in OPT of @ref RIP_MSG +#define DHCP_NAK 6 ///< NACK message in OPT of @ref RIP_MSG +#define DHCP_RELEASE 7 ///< RELEASE message in OPT of @ref RIP_MSG. No use +#define DHCP_INFORM 8 ///< INFORM message in OPT of @ref RIP_MSG. No use + +#define DHCP_HTYPE10MB 1 ///< Used in type of @ref RIP_MSG +#define DHCP_HTYPE100MB 2 ///< Used in type of @ref RIP_MSG + +#define DHCP_HLENETHERNET 6 ///< Used in hlen of @ref RIP_MSG +#define DHCP_HOPS 0 ///< Used in hops of @ref RIP_MSG +#define DHCP_SECS 0 ///< Used in secs of @ref RIP_MSG + +#define INFINITE_LEASETIME 0xffffffff ///< Infinite lease time + +#define OPT_SIZE 312 /// Max OPT size of @ref RIP_MSG +#define RIP_MSG_SIZE (236+OPT_SIZE) /// Max size of @ref RIP_MSG + +/* + * @brief DHCP option and value (cf. RFC1533) + */ +enum +{ + padOption = 0, + subnetMask = 1, + timerOffset = 2, + routersOnSubnet = 3, + timeServer = 4, + nameServer = 5, + dns = 6, + logServer = 7, + cookieServer = 8, + lprServer = 9, + impressServer = 10, + resourceLocationServer = 11, + hostName = 12, + bootFileSize = 13, + meritDumpFile = 14, + domainName = 15, + swapServer = 16, + rootPath = 17, + extentionsPath = 18, + IPforwarding = 19, + nonLocalSourceRouting = 20, + policyFilter = 21, + maxDgramReasmSize = 22, + defaultIPTTL = 23, + pathMTUagingTimeout = 24, + pathMTUplateauTable = 25, + ifMTU = 26, + allSubnetsLocal = 27, + broadcastAddr = 28, + performMaskDiscovery = 29, + maskSupplier = 30, + performRouterDiscovery = 31, + routerSolicitationAddr = 32, + staticRoute = 33, + trailerEncapsulation = 34, + arpCacheTimeout = 35, + ethernetEncapsulation = 36, + tcpDefaultTTL = 37, + tcpKeepaliveInterval = 38, + tcpKeepaliveGarbage = 39, + nisDomainName = 40, + nisServers = 41, + ntpServers = 42, + vendorSpecificInfo = 43, + netBIOSnameServer = 44, + netBIOSdgramDistServer = 45, + netBIOSnodeType = 46, + netBIOSscope = 47, + xFontServer = 48, + xDisplayManager = 49, + dhcpRequestedIPaddr = 50, + dhcpIPaddrLeaseTime = 51, + dhcpOptionOverload = 52, + dhcpMessageType = 53, + dhcpServerIdentifier = 54, + dhcpParamRequest = 55, + dhcpMsg = 56, + dhcpMaxMsgSize = 57, + dhcpT1value = 58, + dhcpT2value = 59, + dhcpClassIdentifier = 60, + dhcpClientIdentifier = 61, + endOption = 255 +}; + +/* + * @brief DHCP message format + */ +typedef struct { + uint8_t op; ///< @ref DHCP_BOOTREQUEST or @ref DHCP_BOOTREPLY + uint8_t htype; ///< @ref DHCP_HTYPE10MB or @ref DHCP_HTYPE100MB + uint8_t hlen; ///< @ref DHCP_HLENETHERNET + uint8_t hops; ///< @ref DHCP_HOPS + uint32_t xid; ///< @ref DHCP_XID This increase one every DHCP transaction. + uint16_t secs; ///< @ref DHCP_SECS + uint16_t flags; ///< @ref DHCP_FLAGSBROADCAST or @ref DHCP_FLAGSUNICAST + uint8_t ciaddr[4]; ///< @ref Request IP to DHCP sever + uint8_t yiaddr[4]; ///< @ref Offered IP from DHCP server + uint8_t siaddr[4]; ///< No use + uint8_t giaddr[4]; ///< No use + uint8_t chaddr[16]; ///< DHCP client 6bytes MAC address. Others is filled to zero + uint8_t sname[64]; ///< No use + uint8_t file[128]; ///< No use + uint8_t OPT[OPT_SIZE]; ///< Option +} RIP_MSG; + + + +uint8_t DHCP_SOCKET; // Socket number for DHCP + +uint8_t DHCP_SIP[4]; // DHCP Server IP address + +// Network information from DHCP Server +uint8_t OLD_allocated_ip[4] = {0, }; // Previous IP address +uint8_t DHCP_allocated_ip[4] = {0, }; // IP address from DHCP +uint8_t DHCP_allocated_gw[4] = {0, }; // Gateway address from DHCP +uint8_t DHCP_allocated_sn[4] = {0, }; // Subnet mask from DHCP +uint8_t DHCP_allocated_dns[4] = {0, }; // DNS address from DHCP + + +int8_t dhcp_state = STATE_DHCP_INIT; // DHCP state +int8_t dhcp_retry_count = 0; + +uint32_t dhcp_lease_time = INFINITE_LEASETIME; +volatile uint32_t dhcp_tick_1s = 0; // unit 1 second +uint32_t dhcp_tick_next = DHCP_WAIT_TIME ; + +uint32_t DHCP_XID; // Any number + +static RIP_MSG dhcpMessageBuffer; +RIP_MSG* const pDHCPMSG = &dhcpMessageBuffer; // Buffer pointer for DHCP processing + +char HOST_NAME[16] = DCHP_HOST_NAME; +uint8_t DHCP_CHADDR[6]; // DHCP Client MAC address. + +/* The default callback function */ +void default_ip_assign(void); +void default_ip_update(void); +void default_ip_conflict(void); + +/* Callback handler */ +void (*dhcp_ip_assign)(void) = default_ip_assign; /* handler to be called when the IP address from DHCP server is first assigned */ +void (*dhcp_ip_update)(void) = default_ip_update; /* handler to be called when the IP address from DHCP server is updated */ +void (*dhcp_ip_conflict)(void) = default_ip_conflict; /* handler to be called when the IP address from DHCP server is conflict */ + +void reg_dhcp_cbfunc(void(*ip_assign)(void), void(*ip_update)(void), void(*ip_conflict)(void)); + + +/* send DISCOVER message to DHCP server */ +void send_DHCP_DISCOVER(void); + +/* send REQEUST message to DHCP server */ +void send_DHCP_REQUEST(void); + +/* send DECLINE message to DHCP server */ +void send_DHCP_DECLINE(void); + +/* IP conflict check by sending ARP-request to leased IP and wait ARP-response. */ +bool check_DHCP_leasedIP(void); + +/* check the timeout in DHCP process */ +DhcpRunResult check_DHCP_timeout(void); + +/* Initialize to timeout process. */ +void reset_DHCP_timeout(void); + +/* Parse message as OFFER and ACK and NACK from DHCP server.*/ +int8_t parseDHCPMSG(void); + +/* The default handler of ip assign first */ +void default_ip_assign(void) +{ + setSIPR(DHCP_allocated_ip); + setSUBR(DHCP_allocated_sn); + setGAR (DHCP_allocated_gw); +} + +/* The default handler of ip chaged */ +void default_ip_update(void) +{ + /* WIZchip Software Reset */ + setMR(MR_RST); + getMR(); // for delay + default_ip_assign(); + setSHAR(DHCP_CHADDR); +} + +/* The default handler of ip chaged */ +void default_ip_conflict(void) +{ + // WIZchip Software Reset + setMR(MR_RST); + getMR(); // for delay + setSHAR(DHCP_CHADDR); +} + +/* register the call back func. */ +void reg_dhcp_cbfunc(void(*ip_assign)(void), void(*ip_update)(void), void(*ip_conflict)(void)) +{ + dhcp_ip_assign = default_ip_assign; + dhcp_ip_update = default_ip_update; + dhcp_ip_conflict = default_ip_conflict; + if(ip_assign) { dhcp_ip_assign = ip_assign; } + if(ip_update) { dhcp_ip_update = ip_update; } + if(ip_conflict) { dhcp_ip_conflict = ip_conflict; } +} + +/* make the common DHCP message */ +void makeDHCPMSG(void) +{ + uint8_t bk_mac[6]; + getSHAR(bk_mac); + pDHCPMSG->op = DHCP_BOOTREQUEST; + pDHCPMSG->htype = DHCP_HTYPE10MB; + pDHCPMSG->hlen = DHCP_HLENETHERNET; + pDHCPMSG->hops = DHCP_HOPS; + uint8_t* ptmp = (uint8_t*)(&pDHCPMSG->xid); + *(ptmp+0) = (uint8_t)((DHCP_XID & 0xFF000000) >> 24); + *(ptmp+1) = (uint8_t)((DHCP_XID & 0x00FF0000) >> 16); + *(ptmp+2) = (uint8_t)((DHCP_XID & 0x0000FF00) >> 8); + *(ptmp+3) = (uint8_t)((DHCP_XID & 0x000000FF) >> 0); + pDHCPMSG->secs = DHCP_SECS; + ptmp = (uint8_t*)(&pDHCPMSG->flags); + *(ptmp+0) = (uint8_t)((DHCP_FLAGSBROADCAST & 0xFF00) >> 8); + *(ptmp+1) = (uint8_t)((DHCP_FLAGSBROADCAST & 0x00FF) >> 0); + + pDHCPMSG->ciaddr[0] = 0; + pDHCPMSG->ciaddr[1] = 0; + pDHCPMSG->ciaddr[2] = 0; + pDHCPMSG->ciaddr[3] = 0; + + pDHCPMSG->yiaddr[0] = 0; + pDHCPMSG->yiaddr[1] = 0; + pDHCPMSG->yiaddr[2] = 0; + pDHCPMSG->yiaddr[3] = 0; + + pDHCPMSG->siaddr[0] = 0; + pDHCPMSG->siaddr[1] = 0; + pDHCPMSG->siaddr[2] = 0; + pDHCPMSG->siaddr[3] = 0; + + pDHCPMSG->giaddr[0] = 0; + pDHCPMSG->giaddr[1] = 0; + pDHCPMSG->giaddr[2] = 0; + pDHCPMSG->giaddr[3] = 0; + + pDHCPMSG->chaddr[0] = DHCP_CHADDR[0]; + pDHCPMSG->chaddr[1] = DHCP_CHADDR[1]; + pDHCPMSG->chaddr[2] = DHCP_CHADDR[2]; + pDHCPMSG->chaddr[3] = DHCP_CHADDR[3]; + pDHCPMSG->chaddr[4] = DHCP_CHADDR[4]; + pDHCPMSG->chaddr[5] = DHCP_CHADDR[5]; + + for (size_t i = 6; i < 16; i++) + { + pDHCPMSG->chaddr[i] = 0; + } + for (size_t i = 0; i < 64; i++) + { + pDHCPMSG->sname[i] = 0; + } + for (size_t i = 0; i < 128; i++) + { + pDHCPMSG->file[i] = 0; + } + + // MAGIC_COOKIE + pDHCPMSG->OPT[0] = (uint8_t)((MAGIC_COOKIE & 0xFF000000) >> 24); + pDHCPMSG->OPT[1] = (uint8_t)((MAGIC_COOKIE & 0x00FF0000) >> 16); + pDHCPMSG->OPT[2] = (uint8_t)((MAGIC_COOKIE & 0x0000FF00) >> 8); + pDHCPMSG->OPT[3] = (uint8_t) (MAGIC_COOKIE & 0x000000FF) >> 0; +} + +/* SEND DHCP DISCOVER */ +void send_DHCP_DISCOVER(void) +{ + makeDHCPMSG(); + + size_t k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG() + + // Option Request Param + pDHCPMSG->OPT[k++] = dhcpMessageType; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_DISCOVER; + + // Client identifier + pDHCPMSG->OPT[k++] = dhcpClientIdentifier; + pDHCPMSG->OPT[k++] = 0x07; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[0]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[1]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[2]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[3]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[4]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[5]; + + // host name + pDHCPMSG->OPT[k++] = hostName; + pDHCPMSG->OPT[k++] = 0; // fill zero length of hostname + size_t i; + for (i = 0 ; HOST_NAME[i] != 0; i++) + { + pDHCPMSG->OPT[k++] = HOST_NAME[i]; + } + pDHCPMSG->OPT[k - (i+1)] = i; // length of hostname + pDHCPMSG->OPT[k++] = dhcpParamRequest; + pDHCPMSG->OPT[k++] = 0x06; // length of request + pDHCPMSG->OPT[k++] = subnetMask; + pDHCPMSG->OPT[k++] = routersOnSubnet; + pDHCPMSG->OPT[k++] = dns; + pDHCPMSG->OPT[k++] = domainName; + pDHCPMSG->OPT[k++] = dhcpT1value; + pDHCPMSG->OPT[k++] = dhcpT2value; + pDHCPMSG->OPT[k++] = endOption; + + for (i = k; i < OPT_SIZE; i++) + { + pDHCPMSG->OPT[i] = 0; + } + + // send broadcasting packet + uint8_t ip[4]; + ip[0] = 255; + ip[1] = 255; + ip[2] = 255; + ip[3] = 255; + + DEBUG_PRINTF("> Send DHCP_DISCOVER\n"); + + sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); + DEBUG_PRINTF("Sent\n"); +} + +/* SEND DHCP REQUEST */ +void send_DHCP_REQUEST(void) +{ + + makeDHCPMSG(); + + uint8_t ip[4]; + if (dhcp_state == STATE_DHCP_LEASED || dhcp_state == STATE_DHCP_REREQUEST) + { + *((uint8_t*)(&pDHCPMSG->flags)) = ((DHCP_FLAGSUNICAST & 0xFF00)>> 8); + *((uint8_t*)(&pDHCPMSG->flags)+1) = (DHCP_FLAGSUNICAST & 0x00FF); + pDHCPMSG->ciaddr[0] = DHCP_allocated_ip[0]; + pDHCPMSG->ciaddr[1] = DHCP_allocated_ip[1]; + pDHCPMSG->ciaddr[2] = DHCP_allocated_ip[2]; + pDHCPMSG->ciaddr[3] = DHCP_allocated_ip[3]; + ip[0] = DHCP_SIP[0]; + ip[1] = DHCP_SIP[1]; + ip[2] = DHCP_SIP[2]; + ip[3] = DHCP_SIP[3]; + } + else + { + ip[0] = 255; + ip[1] = 255; + ip[2] = 255; + ip[3] = 255; + } + + size_t k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG() + + // Option Request Param. + pDHCPMSG->OPT[k++] = dhcpMessageType; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_REQUEST; + + pDHCPMSG->OPT[k++] = dhcpClientIdentifier; + pDHCPMSG->OPT[k++] = 0x07; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[0]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[1]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[2]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[3]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[4]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[5]; + + if (ip[3] == 255) // if(dchp_state == STATE_DHCP_LEASED || dchp_state == DHCP_REREQUEST_STATE) + { + pDHCPMSG->OPT[k++] = dhcpRequestedIPaddr; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[0]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[1]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[2]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[3]; + + pDHCPMSG->OPT[k++] = dhcpServerIdentifier; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_SIP[0]; + pDHCPMSG->OPT[k++] = DHCP_SIP[1]; + pDHCPMSG->OPT[k++] = DHCP_SIP[2]; + pDHCPMSG->OPT[k++] = DHCP_SIP[3]; + } + + // host name + pDHCPMSG->OPT[k++] = hostName; + pDHCPMSG->OPT[k++] = 0; // length of hostname + size_t i; + for (i = 0 ; HOST_NAME[i] != 0; i++) + { + pDHCPMSG->OPT[k++] = HOST_NAME[i]; + } + pDHCPMSG->OPT[k - (i+1)] = i; // length of hostname + pDHCPMSG->OPT[k++] = dhcpParamRequest; + pDHCPMSG->OPT[k++] = 0x08; + pDHCPMSG->OPT[k++] = subnetMask; + pDHCPMSG->OPT[k++] = routersOnSubnet; + pDHCPMSG->OPT[k++] = dns; + pDHCPMSG->OPT[k++] = domainName; + pDHCPMSG->OPT[k++] = dhcpT1value; + pDHCPMSG->OPT[k++] = dhcpT2value; + pDHCPMSG->OPT[k++] = performRouterDiscovery; + pDHCPMSG->OPT[k++] = staticRoute; + pDHCPMSG->OPT[k++] = endOption; + + for (size_t i = k; i < OPT_SIZE; i++) + { + pDHCPMSG->OPT[i] = 0; + } + + DEBUG_PRINTF("> Send DHCP_REQUEST\n"); + + sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); +} + +/* SEND DHCP DHCPDECLINE */ +void send_DHCP_DECLINE(void) +{ + makeDHCPMSG(); + + size_t k = 4; // beacaue MAGIC_COOKIE already made by makeDHCPMSG() + + *((uint8_t*)(&pDHCPMSG->flags)) = ((DHCP_FLAGSUNICAST & 0xFF00)>> 8); + *((uint8_t*)(&pDHCPMSG->flags)+1) = (DHCP_FLAGSUNICAST & 0x00FF); + + // Option Request Param. + pDHCPMSG->OPT[k++] = dhcpMessageType; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_DECLINE; + + pDHCPMSG->OPT[k++] = dhcpClientIdentifier; + pDHCPMSG->OPT[k++] = 0x07; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[0]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[1]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[2]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[3]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[4]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[5]; + + pDHCPMSG->OPT[k++] = dhcpRequestedIPaddr; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[0]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[1]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[2]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[3]; + + pDHCPMSG->OPT[k++] = dhcpServerIdentifier; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_SIP[0]; + pDHCPMSG->OPT[k++] = DHCP_SIP[1]; + pDHCPMSG->OPT[k++] = DHCP_SIP[2]; + pDHCPMSG->OPT[k++] = DHCP_SIP[3]; + + pDHCPMSG->OPT[k++] = endOption; + + for (size_t i = k; i < OPT_SIZE; i++) + { + pDHCPMSG->OPT[i] = 0; + } + + //send broadcasting packet + uint8_t ip[4]; + ip[0] = 0xFF; + ip[1] = 0xFF; + ip[2] = 0xFF; + ip[3] = 0xFF; + + DEBUG_PRINTF("\n> Send DHCP_DECLINE\n"); + + sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); +} + +/* PARSE REPLY pDHCPMSG */ +int8_t parseDHCPMSG(void) +{ + uint8_t type = 0; + uint16_t len; + if ((len = getSn_RX_RSR(DHCP_SOCKET)) > 0) + { + if (len > sizeof(RIP_MSG)) + { + len = sizeof(RIP_MSG); + } + + uint8_t svr_addr[6]; + uint16_t svr_port; + len = recvfrom(DHCP_SOCKET, (uint8_t *)pDHCPMSG, len, svr_addr, &svr_port); + + DEBUG_PRINTF("DHCP message : %d.%d.%d.%d(%d) %d received. \n", svr_addr[0], svr_addr[1], svr_addr[2], svr_addr[3],svr_port, len); + + if (svr_port == DHCP_SERVER_PORT) + { + // compare mac address + if ( (pDHCPMSG->chaddr[0] == DHCP_CHADDR[0]) && (pDHCPMSG->chaddr[1] == DHCP_CHADDR[1]) + && (pDHCPMSG->chaddr[2] == DHCP_CHADDR[2]) && (pDHCPMSG->chaddr[3] == DHCP_CHADDR[3]) + && (pDHCPMSG->chaddr[4] == DHCP_CHADDR[4]) && (pDHCPMSG->chaddr[5] == DHCP_CHADDR[5]) + ) + { + const uint8_t *p = (uint8_t *)(&pDHCPMSG->op); + p = p + 240; // 240 = sizeof(RIP_MSG) + MAGIC_COOKIE size in RIP_MSG.opt - sizeof(RIP_MSG.opt) + const uint8_t * const e = p + (len - 240); + + while (p < e) + { + switch (*p ) + { + case endOption : + p = e; // for break while(p < e) + break; + case padOption : + p++; + break; + case dhcpMessageType : + p++; + p++; + type = *p++; + break; + case subnetMask : + p++; + p++; + DHCP_allocated_sn[0] = *p++; + DHCP_allocated_sn[1] = *p++; + DHCP_allocated_sn[2] = *p++; + DHCP_allocated_sn[3] = *p++; + break; + case routersOnSubnet : + { + p++; + const uint8_t opt_len = *p++; + DHCP_allocated_gw[0] = *p++; + DHCP_allocated_gw[1] = *p++; + DHCP_allocated_gw[2] = *p++; + DHCP_allocated_gw[3] = *p++; + p = p + (opt_len - 4); + } + break; + case dns : + { + p++; + const uint8_t opt_len = *p++; + DHCP_allocated_dns[0] = *p++; + DHCP_allocated_dns[1] = *p++; + DHCP_allocated_dns[2] = *p++; + DHCP_allocated_dns[3] = *p++; + p = p + (opt_len - 4); + } + break; + case dhcpIPaddrLeaseTime : + { + p++; + p++; + dhcp_lease_time = *p++; + dhcp_lease_time = (dhcp_lease_time << 8) + *p++; + dhcp_lease_time = (dhcp_lease_time << 8) + *p++; + dhcp_lease_time = (dhcp_lease_time << 8) + *p++; +#if 0 //#ifdef _DHCP_DEBUG_ + dhcp_lease_time = 10; +#endif + } + break; + case dhcpServerIdentifier : + { + p++; + p++; + DHCP_SIP[0] = *p++; + DHCP_SIP[1] = *p++; + DHCP_SIP[2] = *p++; + DHCP_SIP[3] = *p++; + } + break; + + default : + { + p++; + const uint8_t opt_len = *p++; + p += opt_len; + } + break; + } // switch + } // while + } // if + } // if + } // if + return type; +} + +DhcpRunResult DHCP_run(void) +{ + if (dhcp_state == STATE_DHCP_STOP) + { + return DhcpRunResult::DHCP_STOPPED; + } + + if (getSn_SR(DHCP_SOCKET) != SOCK_UDP) + { + socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00); + } + + DhcpRunResult ret = DhcpRunResult::DHCP_RUNNING; + const uint8_t type = parseDHCPMSG(); + + switch (dhcp_state) + { + case STATE_DHCP_INIT: + DHCP_allocated_ip[0] = 0; + DHCP_allocated_ip[1] = 0; + DHCP_allocated_ip[2] = 0; + DHCP_allocated_ip[3] = 0; + send_DHCP_DISCOVER(); + dhcp_state = STATE_DHCP_DISCOVER; + break; + + case STATE_DHCP_DISCOVER: + if (type == DHCP_OFFER) + { + DEBUG_PRINTF("> Receive DHCP_OFFER\n"); + DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0]; + DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1]; + DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2]; + DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3]; + + send_DHCP_REQUEST(); + dhcp_state = STATE_DHCP_REQUEST; + } + else + { + ret = check_DHCP_timeout(); + } + break; + + case STATE_DHCP_REQUEST : + if (type == DHCP_ACK) + { + DEBUG_PRINTF("> Receive DHCP_ACK\n"); + if (check_DHCP_leasedIP()) + { + // Network info assignment from DHCP + dhcp_ip_assign(); + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_LEASED; + ret = DhcpRunResult::DHCP_IP_ASSIGN; + } + else + { + // IP address conflict occurred + reset_DHCP_timeout(); + dhcp_ip_conflict(); + dhcp_state = STATE_DHCP_INIT; + } + } + else if (type == DHCP_NAK) + { + DEBUG_PRINTF("> Receive DHCP_NACK\n"); + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_DISCOVER; + } + else + { + ret = check_DHCP_timeout(); + } + break; + + case STATE_DHCP_LEASED : + ret = DhcpRunResult::DHCP_IP_LEASED; + if ((dhcp_lease_time != INFINITE_LEASETIME) && ((dhcp_lease_time/2) < dhcp_tick_1s)) + { + DEBUG_PRINTF("> Maintains the IP address \n"); + OLD_allocated_ip[0] = DHCP_allocated_ip[0]; + OLD_allocated_ip[1] = DHCP_allocated_ip[1]; + OLD_allocated_ip[2] = DHCP_allocated_ip[2]; + OLD_allocated_ip[3] = DHCP_allocated_ip[3]; + + DHCP_XID++; + + send_DHCP_REQUEST(); + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_REREQUEST; + } + break; + + case STATE_DHCP_REREQUEST : + ret = DhcpRunResult::DHCP_IP_LEASED; + if (type == DHCP_ACK) + { + dhcp_retry_count = 0; + if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] || + OLD_allocated_ip[1] != DHCP_allocated_ip[1] || + OLD_allocated_ip[2] != DHCP_allocated_ip[2] || + OLD_allocated_ip[3] != DHCP_allocated_ip[3]) + { + ret = DhcpRunResult::DHCP_IP_CHANGED; + dhcp_ip_update(); + DEBUG_PRINTF(">IP changed.\n"); + } + else + { + DEBUG_PRINTF(">IP is continued.\n"); + } + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_LEASED; + } + else if (type == DHCP_NAK) + { + DEBUG_PRINTF("> Receive DHCP_NACK, Failed to maintain ip\n"); + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_DISCOVER; + } + else + { + ret = check_DHCP_timeout(); + } + break; + + default : + break; + } + + return ret; +} + +void DHCP_stop(void) +{ + close(DHCP_SOCKET); + dhcp_state = STATE_DHCP_STOP; +} + +DhcpRunResult check_DHCP_timeout(void) +{ + DhcpRunResult ret = DhcpRunResult::DHCP_RUNNING; + + if (dhcp_retry_count < MAX_DHCP_RETRY) + { + if (dhcp_tick_next < dhcp_tick_1s) + { + switch ( dhcp_state ) + { + case STATE_DHCP_DISCOVER : + DEBUG_PRINTF("<> state : STATE_DHCP_DISCOVER\n"); + send_DHCP_DISCOVER(); + break; + + case STATE_DHCP_REQUEST : + DEBUG_PRINTF("<> state : STATE_DHCP_REQUEST\n"); + + send_DHCP_REQUEST(); + break; + + case STATE_DHCP_REREQUEST : + DEBUG_PRINTF("<> state : STATE_DHCP_REREQUEST\n"); + + send_DHCP_REQUEST(); + break; + + default : + break; + } + + dhcp_tick_1s = 0; + dhcp_tick_next = dhcp_tick_1s + DHCP_WAIT_TIME; + dhcp_retry_count++; + } + } + else + { // exceeded retry count + switch(dhcp_state) + { + case STATE_DHCP_DISCOVER: + dhcp_state = STATE_DHCP_INIT; + ret = DhcpRunResult::DHCP_FAILED; + break; + case STATE_DHCP_REQUEST: + case STATE_DHCP_REREQUEST: + send_DHCP_DISCOVER(); + dhcp_state = STATE_DHCP_DISCOVER; + break; + default : + break; + } + reset_DHCP_timeout(); + } + return ret; +} + +bool check_DHCP_leasedIP(void) +{ + uint8_t tmp; + int32_t ret; + + //WIZchip RCR value changed for ARP Timeout count control + tmp = getRCR(); + setRCR(0x03); + + // IP conflict detection : ARP request - ARP reply + // Broadcasting ARP Request for check the IP conflict using UDP sendto() function + // TODO the following takes 620ms to time out - hanging for this long isn't very nice + ret = sendto(DHCP_SOCKET, (const uint8_t *)"CHECK_IP_CONFLICT", 17, DHCP_allocated_ip, 5000); + + // RCR value restore + setRCR(tmp); + + if (ret == SOCKERR_TIMEOUT) + { + // UDP send Timeout occurred : allocated IP address is unique, DHCP Success + DEBUG_PRINTF("\n> Check leased IP - OK\n"); + return true; + } + else + { + // Received ARP reply or etc : IP address conflict occur, DHCP Failed + send_DHCP_DECLINE(); + + ret = dhcp_tick_1s; + //TODO can't tolerate a 1 to 2 second wait here + while((dhcp_tick_1s - ret) < 2) { } // wait for 1s over; wait to complete to send DECLINE message; + + return false; + } +} + +void DHCP_init(uint8_t s, const char *hname) +{ + strncpy(HOST_NAME, hname, sizeof(HOST_NAME)); + HOST_NAME[sizeof(HOST_NAME) - 1] = 0; + + uint8_t zeroip[4] = {0,0,0,0}; + getSHAR(DHCP_CHADDR); + DEBUG_PRINTF("MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", DHCP_CHADDR[0], DHCP_CHADDR[1], DHCP_CHADDR[2], DHCP_CHADDR[3], DHCP_CHADDR[4], DHCP_CHADDR[5]); + + if ((DHCP_CHADDR[0] | DHCP_CHADDR[1] | DHCP_CHADDR[2] | DHCP_CHADDR[3] | DHCP_CHADDR[4] | DHCP_CHADDR[5]) == 0x00) + { + // assing temporary mac address, you should be set SHAR before call this function. + DHCP_CHADDR[0] = 0x00; + DHCP_CHADDR[1] = 0x08; + DHCP_CHADDR[2] = 0xdc; + DHCP_CHADDR[3] = 0x00; + DHCP_CHADDR[4] = 0x00; + DHCP_CHADDR[5] = 0x00; + setSHAR(DHCP_CHADDR); + } + + DHCP_SOCKET = s; // SOCK_DHCP + DHCP_XID = 0x12345678; + + // WIZchip Netinfo Clear + setSIPR(zeroip); + setSIPR(zeroip); + setGAR(zeroip); + + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_INIT; +} + +/* Rset the DHCP timeout count and retry count. */ +void reset_DHCP_timeout(void) +{ + dhcp_tick_1s = 0; + dhcp_tick_next = DHCP_WAIT_TIME; + dhcp_retry_count = 0; +} + +void DHCP_time_handler(void) +{ + dhcp_tick_1s++; +} + +void getIPfromDHCP(uint8_t* ip) +{ + ip[0] = DHCP_allocated_ip[0]; + ip[1] = DHCP_allocated_ip[1]; + ip[2] = DHCP_allocated_ip[2]; + ip[3] = DHCP_allocated_ip[3]; +} + +void getGWfromDHCP(uint8_t* ip) +{ + ip[0] =DHCP_allocated_gw[0]; + ip[1] =DHCP_allocated_gw[1]; + ip[2] =DHCP_allocated_gw[2]; + ip[3] =DHCP_allocated_gw[3]; +} + +void getSNfromDHCP(uint8_t* ip) +{ + ip[0] = DHCP_allocated_sn[0]; + ip[1] = DHCP_allocated_sn[1]; + ip[2] = DHCP_allocated_sn[2]; + ip[3] = DHCP_allocated_sn[3]; +} + +void getDNSfromDHCP(uint8_t* ip) +{ + ip[0] = DHCP_allocated_dns[0]; + ip[1] = DHCP_allocated_dns[1]; + ip[2] = DHCP_allocated_dns[2]; + ip[3] = DHCP_allocated_dns[3]; +} + +uint32_t getDHCPLeasetime(void) +{ + return dhcp_lease_time; +} + +// End diff --git a/src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.h b/src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.h new file mode 100644 index 00000000..4f238b6e --- /dev/null +++ b/src/DuetNG/DuetEthernet/Wiznet/Internet/DHCP/dhcp.h @@ -0,0 +1,144 @@ +//***************************************************************************** +// +//! \file dhcp.h +//! \brief DHCP APIs Header file. +//! \details Processig DHCP protocol as DISCOVER, OFFER, REQUEST, ACK, NACK and DECLINE. +//! \version 1.1.0 +//! \date 2013/11/18 +//! \par Revision history +//! <2013/11/18> 1st Release +//! <2012/12/20> V1.1.0 +//! 1. Move unreferenced DEFINE to dhcp.c +//! <2012/12/26> V1.1.1 +//! \author Eric Jung & MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** +#ifndef _DHCP_H_ +#define _DHCP_H_ + +/* Retry to processing DHCP */ +#define MAX_DHCP_RETRY 2 ///< Maxium retry count +#define DHCP_WAIT_TIME 10 ///< Wait Time 10s + + +/* UDP port numbers for DHCP */ +#define DHCP_SERVER_PORT 67 ///< DHCP server port number +#define DHCP_CLIENT_PORT 68 ///< DHCP client port number + + +#define MAGIC_COOKIE 0x63825363 ///< Any number. You can be modified it any number + +#define DCHP_HOST_NAME "DuetEthernet" + +/* + * @brief return value of @ref DHCP_run() + */ +enum class DhcpRunResult : uint8_t +{ + DHCP_FAILED = 0, ///< Processing Fail + DHCP_RUNNING, ///< Processing DHCP protocol + DHCP_IP_ASSIGN, ///< First Occupy IP from DHPC server (if cbfunc == null, act as default default_ip_assign) + DHCP_IP_CHANGED, ///< Change IP address by new ip from DHCP (if cbfunc == null, act as default default_ip_update) + DHCP_IP_LEASED, ///< Stand by + DHCP_STOPPED ///< Stop processing DHCP protocol +}; + +/* + * @brief DHCP client initialization (outside of the main loop) + * @param s - socket number + * @param hname - null-terminated host name string + */ +void DHCP_init(uint8_t s, const char *hname); + +/* + * @brief DHCP 1s Tick Timer handler + * @note SHOULD BE register to your system 1s Tick timer handler + */ +void DHCP_time_handler(void); + +/* + * @brief Register call back function + * @param ip_assign - callback func when IP is assigned from DHCP server first + * @param ip_update - callback func when IP is changed + * @prarm ip_conflict - callback func when the assigned IP is conflict with others. + */ +void reg_dhcp_cbfunc(void(*ip_assign)(void), void(*ip_update)(void), void(*ip_conflict)(void)); + +/* + * @brief DHCP client in the main loop + * @return The value is as the follow \n + * @ref DHCP_FAILED \n + * @ref DHCP_RUNNING \n + * @ref DHCP_IP_ASSIGN \n + * @ref DHCP_IP_CHANGED \n + * @ref DHCP_IP_LEASED \n + * @ref DHCP_STOPPED \n + * + * @note This function is always called by you main task. + */ +DhcpRunResult DHCP_run(void); + +/* + * @brief Stop DHCP processing + * @note If you want to restart. call DHCP_init() and DHCP_run() + */ +void DHCP_stop(void); + +/* Get Network information assigned from DHCP server */ +/* + * @brief Get IP address + * @param ip - IP address to be returned + */ +void getIPfromDHCP(uint8_t* ip); +/* + * @brief Get Gateway address + * @param ip - Gateway address to be returned + */ +void getGWfromDHCP(uint8_t* ip); +/* + * @brief Get Subnet mask value + * @param ip - Subnet mask to be returned + */ +void getSNfromDHCP(uint8_t* ip); +/* + * @brief Get DNS address + * @param ip - DNS address to be returned + */ +void getDNSfromDHCP(uint8_t* ip); + +/* + * @brief Get the leased time by DHCP sever + * @return unit 1s + */ +uint32_t getDHCPLeasetime(void); + +#endif /* _DHCP_H_ */ diff --git a/src/DuetNG/DuetWiFi/Network.cpp b/src/DuetNG/DuetWiFi/Network.cpp index 3b9c6226..31aa3459 100644 --- a/src/DuetNG/DuetWiFi/Network.cpp +++ b/src/DuetNG/DuetWiFi/Network.cpp @@ -743,7 +743,7 @@ const uint8_t *Network::GetIPAddress() const uint16_t Network::GetHttpPort() const { - return DEFAULT_HTTP_PORT; + return DefaultHttpPort; } void Network::SetHttpPort(uint16_t port) diff --git a/src/DuetNG/DuetWiFi/Network.h b/src/DuetNG/DuetWiFi/Network.h index a4bf3759..2d403412 100644 --- a/src/DuetNG/DuetWiFi/Network.h +++ b/src/DuetNG/DuetWiFi/Network.h @@ -21,10 +21,10 @@ const uint32_t rcNumber = 0x0000FFFF; const uint32_t rcJson = 0x00010000; const uint32_t rcKeepOpen = 0x00020000; -static const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 }; // Need some sort of default... -static const uint8_t NET_MASK[4] = { 255, 255, 255, 0 }; -static const uint8_t GATE_WAY[4] = { 192, 168, 1, 1 }; -static const uint16_t DEFAULT_HTTP_PORT = 80; +static const uint8_t DefaultIpAddress[4] = { 192, 168, 1, 10 }; // Need some sort of default... +static const uint8_t DefaultNetMask[4] = { 255, 255, 255, 0 }; +static const uint8_t DefaultGateway[4] = { 192, 168, 1, 1 }; +static const uint16_t DefaultHttpPort = 80; class TransactionBuffer; class WifiFirmwareUploader; diff --git a/src/Platform.cpp b/src/Platform.cpp index 972f7877..b118ba5d 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -208,15 +208,15 @@ void Platform::Init() SERIAL_AUX2_DEVICE.begin(baudRates[2]); #endif - compatibility = marlin; // default to Marlin because the common host programs expect the "OK" response to commands - ARRAY_INIT(ipAddress, IP_ADDRESS); - ARRAY_INIT(netMask, NET_MASK); - ARRAY_INIT(gateWay, GATE_WAY); + compatibility = marlin; // default to Marlin because the common host programs expect the "OK" response to commands + ARRAY_INIT(ipAddress, DefaultIpAddress); + ARRAY_INIT(netMask, DefaultNetMask); + ARRAY_INIT(gateWay, DefaultGateway); #if defined(DUET_NG) && defined(DUET_WIFI) memset(macAddress, 0xFF, sizeof(macAddress)); #else - ARRAY_INIT(macAddress, MAC_ADDRESS); + ARRAY_INIT(macAddress, DefaultMacAddress); #endif zProbeType = 0; // Default is to use no Z probe switch @@ -435,7 +435,7 @@ void Platform::Init() } #endif - // MCU temperature and power monitoring + // MCU temperature monitoring - doesn't work in RADDS due to pin assignmentgs and SAM3X chip bug #ifndef __RADDS__ temperatureAdcChannel = GetTemperatureAdcChannel(); AnalogInEnableChannel(temperatureAdcChannel, true); @@ -446,6 +446,7 @@ void Platform::Init() mcuTemperatureAdjust = 0.0; #ifdef DUET_NG + // Power monitoring vInMonitorAdcChannel = PinToAdcChannel(PowerMonitorVinDetectPin); pinMode(PowerMonitorVinDetectPin, AIN); AnalogInEnableChannel(vInMonitorAdcChannel, true); -- cgit v1.2.3