text: /**
   * Marlin 3D Printer Firmware
   * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
   *
   * Based on Sprinter and grbl.
   * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <https://www.gnu.org/licenses/>.
   *
   */
  
  #include "../inc/MarlinConfigPre.h"
  
  #if HAS_ETHERNET
  
  #include "ethernet.h"
  #include "../core/serial.h"
  
  #define DEBUG_OUT ENABLED(DEBUG_ETHERNET)
  #include "../core/debug_out.h"
  
  bool MarlinEthernet::hardware_enabled, // = false
       MarlinEthernet::have_telnet_client; // = false
  
  IPAddress MarlinEthernet::ip,
            MarlinEthernet::myDns,
            MarlinEthernet::gateway,
            MarlinEthernet::subnet;
  
  EthernetClient  MarlinEthernet::telnetClient;  // connected client
  
  MarlinEthernet ethernet;
  
  EthernetServer server(23);    // telnet server
  
  enum linkStates { UNLINKED, LINKING, LINKED, CONNECTING, CONNECTED, NO_HARDWARE } linkState;
  
  #ifdef __IMXRT1062__
  
    static void teensyMAC(uint8_t * const mac) {
      const uint32_t m1 = HW_OCOTP_MAC1, m2 = HW_OCOTP_MAC0;
      mac[0] = m1 >> 8;
      mac[1] = m1 >> 0;
      mac[2] = m2 >> 24;
      mac[3] = m2 >> 16;
      mac[4] = m2 >> 8;
      mac[5] = m2 >> 0;
    }
  
  #else
  
    byte mac[] = MAC_ADDRESS;
  
  #endif
  
  void ethernet_cable_error() { SERIAL_ERROR_MSG("Ethernet cable is not connected."); }
  
  void MarlinEthernet::init() {
    if (!hardware_enabled) return;
  
    SERIAL_ECHO_MSG("Starting network...");
  
    // Init the Ethernet device
    #ifdef __IMXRT1062__
      uint8_t mac[6];
      teensyMAC(mac);
    #endif
  
    if (!ip) {
      Ethernet.begin(mac); // use DHCP
    }
    else {
      if (!gateway) {
        gateway = ip;
        gateway[3] = 1;
        myDns = gateway;
        subnet = IPAddress(255,255,255,0);
      }
      if (!myDns) myDns = gateway;
      if (!subnet) subnet = IPAddress(255,255,255,0);
      Ethernet.begin(mac, ip, myDns, gateway, subnet);
    }
  
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      SERIAL_ERROR_MSG("No Ethernet hardware found.");
      linkState = NO_HARDWARE;
      return;
    }
  
    linkState = UNLINKED;
  
    if (Ethernet.linkStatus() == LinkOFF)
      ethernet_cable_error();
  }
  
  void MarlinEthernet::check() {
    if (!hardware_enabled) return;
  
    switch (linkState) {
      case NO_HARDWARE:
        break;
  
      case UNLINKED:
        if (Ethernet.linkStatus() == LinkOFF) break;
  
        SERIAL_ECHOLNPGM("Ethernet cable connected");
        server.begin();
        linkState = LINKING;
        break;
  
      case LINKING:
        if (!Ethernet.localIP()) break;
  
        SERIAL_ECHOPGM("Successfully started telnet server with IP ");
        MYSERIAL1.println(Ethernet.localIP());
  
        linkState = LINKED;
        break;
  
      case LINKED:
        if (Ethernet.linkStatus() == LinkOFF) {
          ethernet_cable_error();
          linkState = UNLINKED;
          break;
        }
        telnetClient = server.accept();
        if (telnetClient) linkState = CONNECTING;
        break;
  
      case CONNECTING:
        telnetClient.println("Marlin " SHORT_BUILD_VERSION);
        #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR)
          telnetClient.println(
            " Last Updated: " STRING_DISTRIBUTION_DATE
            " | Author: " STRING_CONFIG_H_AUTHOR
          );
        #endif
        telnetClient.println("Compiled: " __DATE__);
  
        SERIAL_ECHOLNPGM("Client connected");
        have_telnet_client = true;
        linkState = CONNECTED;
        break;
  
      case CONNECTED:
        if (telnetClient && !telnetClient.connected()) {
          SERIAL_ECHOLNPGM("Client disconnected");
          telnetClient.stop();
          have_telnet_client = false;
          linkState = LINKED;
        }
        if (Ethernet.linkStatus() == LinkOFF) {
          ethernet_cable_error();
          if (telnetClient) telnetClient.stop();
          linkState = UNLINKED;
        }
        break;
  
      default: break;
    }
  }
  
  #endif // HAS_ETHERNET
  