/* ETH.h - espre ETH PHY support. Based on WiFi.h from Arduino WiFi shield library. Copyright (c) 2011-2014 Arduino. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ETH.h" #include "esp_system.h" #if ESP_IDF_VERSION_MAJOR > 3 #include "esp_event.h" #include "esp_eth.h" #include "esp_eth_phy.h" #include "esp_eth_mac.h" #include "esp_eth_com.h" #if CONFIG_IDF_TARGET_ESP32 #include "soc/emac_ext_struct.h" #include "soc/rtc.h" //#include "soc/io_mux_reg.h" //#include "hal/gpio_hal.h" #endif #else #include "eth_phy/phy.h" #include "eth_phy/phy_tlk110.h" #include "eth_phy/phy_lan8720.h" #endif #include "lwip/err.h" #include "lwip/dns.h" extern void tcpipInit(); #if ESP_IDF_VERSION_MAJOR > 3 /** * @brief Callback function invoked when lowlevel initialization is finished * * @param[in] eth_handle: handle of Ethernet driver * * @return * - ESP_OK: process extra lowlevel initialization successfully * - ESP_FAIL: error occurred when processing extra lowlevel initialization */ static eth_clock_mode_t eth_clock_mode = ETH_CLK_MODE; #if CONFIG_ETH_RMII_CLK_INPUT static void emac_config_apll_clock(void) { /* apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2) */ rtc_xtal_freq_t rtc_xtal_freq = rtc_clk_xtal_freq_get(); switch (rtc_xtal_freq) { case RTC_XTAL_FREQ_40M: // Recommended /* 50 MHz = 40MHz * (4 + 6) / (2 * (2 + 2) = 50.000 */ /* sdm0 = 0, sdm1 = 0, sdm2 = 6, o_div = 2 */ rtc_clk_apll_enable(true, 0, 0, 6, 2); break; case RTC_XTAL_FREQ_26M: /* 50 MHz = 26MHz * (4 + 15 + 118 / 256 + 39/65536) / ((3 + 2) * 2) = 49.999992 */ /* sdm0 = 39, sdm1 = 118, sdm2 = 15, o_div = 3 */ rtc_clk_apll_enable(true, 39, 118, 15, 3); break; case RTC_XTAL_FREQ_24M: /* 50 MHz = 24MHz * (4 + 12 + 255 / 256 + 255/65536) / ((2 + 2) * 2) = 49.499977 */ /* sdm0 = 255, sdm1 = 255, sdm2 = 12, o_div = 2 */ rtc_clk_apll_enable(true, 255, 255, 12, 2); break; default: // Assume we have a 40M xtal rtc_clk_apll_enable(true, 0, 0, 6, 2); break; } } #endif static esp_err_t on_lowlevel_init_done(esp_eth_handle_t eth_handle){ #if CONFIG_IDF_TARGET_ESP32 if(eth_clock_mode > ETH_CLOCK_GPIO17_OUT){ return ESP_FAIL; } // First deinit current config if different #if CONFIG_ETH_RMII_CLK_INPUT if(eth_clock_mode != ETH_CLOCK_GPIO0_IN && eth_clock_mode != ETH_CLOCK_GPIO0_OUT){ pinMode(0, INPUT); } #endif #if CONFIG_ETH_RMII_CLK_OUTPUT #if CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0 if(eth_clock_mode > ETH_CLOCK_GPIO0_OUT){ pinMode(0, INPUT); } #elif CONFIG_ETH_RMII_CLK_OUT_GPIO == 16 if(eth_clock_mode != ETH_CLOCK_GPIO16_OUT){ pinMode(16, INPUT); } #elif CONFIG_ETH_RMII_CLK_OUT_GPIO == 17 if(eth_clock_mode != ETH_CLOCK_GPIO17_OUT){ pinMode(17, INPUT); } #endif #endif // Setup interface for the correct pin #if CONFIG_ETH_PHY_INTERFACE_MII EMAC_EXT.ex_phyinf_conf.phy_intf_sel = 4; #endif if(eth_clock_mode == ETH_CLOCK_GPIO0_IN){ #ifndef CONFIG_ETH_RMII_CLK_INPUT // RMII clock (50MHz) input to GPIO0 //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_EMAC_TX_CLK); //PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[0]); pinMode(0, INPUT); pinMode(0, FUNCTION_6); EMAC_EXT.ex_clk_ctrl.ext_en = 1; EMAC_EXT.ex_clk_ctrl.int_en = 0; EMAC_EXT.ex_oscclk_conf.clk_sel = 1; #endif } else { if(eth_clock_mode == ETH_CLOCK_GPIO0_OUT){ #ifndef CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0 // APLL clock output to GPIO0 (must be configured to 50MHz!) //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); //PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[0]); pinMode(0, OUTPUT); pinMode(0, FUNCTION_2); // Choose the APLL clock to output on GPIO REG_WRITE(PIN_CTRL, 6); #endif } else if(eth_clock_mode == ETH_CLOCK_GPIO16_OUT){ #if CONFIG_ETH_RMII_CLK_OUT_GPIO != 16 // RMII CLK (50MHz) output to GPIO16 //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_EMAC_CLK_OUT); //PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[16]); pinMode(16, OUTPUT); pinMode(16, FUNCTION_6); #endif } else if(eth_clock_mode == ETH_CLOCK_GPIO17_OUT){ #if CONFIG_ETH_RMII_CLK_OUT_GPIO != 17 // RMII CLK (50MHz) output to GPIO17 //gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_EMAC_CLK_OUT_180); //PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[17]); pinMode(17, OUTPUT); pinMode(17, FUNCTION_6); #endif } #if CONFIG_ETH_RMII_CLK_INPUT EMAC_EXT.ex_clk_ctrl.ext_en = 0; EMAC_EXT.ex_clk_ctrl.int_en = 1; EMAC_EXT.ex_oscclk_conf.clk_sel = 0; emac_config_apll_clock(); EMAC_EXT.ex_clkout_conf.div_num = 0; EMAC_EXT.ex_clkout_conf.h_div_num = 0; #endif } #endif return ESP_OK; } /** * @brief Callback function invoked when lowlevel deinitialization is finished * * @param[in] eth_handle: handle of Ethernet driver * * @return * - ESP_OK: process extra lowlevel deinitialization successfully * - ESP_FAIL: error occurred when processing extra lowlevel deinitialization */ //static esp_err_t on_lowlevel_deinit_done(esp_eth_handle_t eth_handle){ // return ESP_OK; //} #else static int _eth_phy_mdc_pin = -1; static int _eth_phy_mdio_pin = -1; static int _eth_phy_power_pin = -1; static eth_phy_power_enable_func _eth_phy_power_enable_orig = NULL; static void _eth_phy_config_gpio(void) { if(_eth_phy_mdc_pin < 0 || _eth_phy_mdio_pin < 0){ log_e("MDC and MDIO pins are not configured!"); return; } phy_rmii_configure_data_interface_pins(); phy_rmii_smi_configure_pins(_eth_phy_mdc_pin, _eth_phy_mdio_pin); } static void _eth_phy_power_enable(bool enable) { pinMode(_eth_phy_power_pin, OUTPUT); digitalWrite(_eth_phy_power_pin, enable); delay(1); } #endif ETHClass::ETHClass() :initialized(false) ,staticIP(false) #if ESP_IDF_VERSION_MAJOR > 3 ,eth_handle(NULL) #endif ,started(false) #if ESP_IDF_VERSION_MAJOR > 3 ,eth_link(ETH_LINK_DOWN) #endif { } ETHClass::~ETHClass() {} bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_type_t type, eth_clock_mode_t clock_mode) { #if ESP_IDF_VERSION_MAJOR > 3 eth_clock_mode = clock_mode; tcpipInit(); tcpip_adapter_set_default_eth_handlers(); esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); esp_netif_t *eth_netif = esp_netif_new(&cfg); if(esp_eth_set_default_handlers(eth_netif) != ESP_OK){ log_e("esp_eth_set_default_handlers failed"); return false; } esp_eth_mac_t *eth_mac = NULL; #if CONFIG_ETH_SPI_ETHERNET_DM9051 if(type == ETH_PHY_DM9051){ return false;//todo } else { #endif #if CONFIG_ETH_USE_ESP32_EMAC eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); mac_config.smi_mdc_gpio_num = mdc; mac_config.smi_mdio_gpio_num = mdio; mac_config.sw_reset_timeout_ms = 1000; eth_mac = esp_eth_mac_new_esp32(&mac_config); #endif #if CONFIG_ETH_SPI_ETHERNET_DM9051 } #endif if(eth_mac == NULL){ log_e("esp_eth_mac_new_esp32 failed"); return false; } eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); phy_config.phy_addr = phy_addr; phy_config.reset_gpio_num = power; esp_eth_phy_t *eth_phy = NULL; switch(type){ case ETH_PHY_LAN8720: eth_phy = esp_eth_phy_new_lan8720(&phy_config); break; case ETH_PHY_TLK110: eth_phy = esp_eth_phy_new_ip101(&phy_config); break; case ETH_PHY_RTL8201: eth_phy = esp_eth_phy_new_rtl8201(&phy_config); break; case ETH_PHY_DP83848: eth_phy = esp_eth_phy_new_dp83848(&phy_config); break; #if CONFIG_ETH_SPI_ETHERNET_DM9051 case ETH_PHY_DM9051: eth_phy = esp_eth_phy_new_dm9051(&phy_config); break; #endif case ETH_PHY_KSZ8081: eth_phy = esp_eth_phy_new_ksz8081(&phy_config); default: break; } if(eth_phy == NULL){ log_e("esp_eth_phy_new failed"); return false; } eth_handle = NULL; esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy); eth_config.on_lowlevel_init_done = on_lowlevel_init_done; //eth_config.on_lowlevel_deinit_done = on_lowlevel_deinit_done; if(esp_eth_driver_install(ð_config, ð_handle) != ESP_OK || eth_handle == NULL){ log_e("esp_eth_driver_install failed"); return false; } /* attach Ethernet driver to TCP/IP stack */ if(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)) != ESP_OK){ log_e("esp_netif_attach failed"); return false; } if(esp_eth_start(eth_handle) != ESP_OK){ log_e("esp_eth_start failed"); return false; } #else esp_err_t err; if(initialized){ err = esp_eth_enable(); if(err){ log_e("esp_eth_enable error: %d", err); return false; } started = true; return true; } _eth_phy_mdc_pin = mdc; _eth_phy_mdio_pin = mdio; _eth_phy_power_pin = power; if(type == ETH_PHY_LAN8720){ eth_config_t config = phy_lan8720_default_ethernet_config; memcpy(ð_config, &config, sizeof(eth_config_t)); } else if(type == ETH_PHY_TLK110){ eth_config_t config = phy_tlk110_default_ethernet_config; memcpy(ð_config, &config, sizeof(eth_config_t)); } else if(type == ETH_PHY_IP101) { eth_config_t config = phy_ip101_default_ethernet_config; memcpy(ð_config, &config, sizeof(eth_config_t)); } else { log_e("Bad ETH_PHY type: %u", (uint8_t)type); return false; } eth_config.phy_addr = (eth_phy_base_t)phy_addr; eth_config.clock_mode = clock_mode; eth_config.gpio_config = _eth_phy_config_gpio; eth_config.tcpip_input = tcpip_adapter_eth_input; if(_eth_phy_power_pin >= 0){ _eth_phy_power_enable_orig = eth_config.phy_power_enable; eth_config.phy_power_enable = _eth_phy_power_enable; } tcpipInit(); err = esp_eth_init(ð_config); if(!err){ initialized = true; err = esp_eth_enable(); if(err){ log_e("esp_eth_enable error: %d", err); } else { started = true; return true; } } else { log_e("esp_eth_init error: %d", err); } #endif return true; } bool ETHClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) { esp_err_t err = ESP_OK; tcpip_adapter_ip_info_t info; if(local_ip != (uint32_t)0x00000000 && local_ip != INADDR_NONE){ info.ip.addr = static_cast(local_ip); info.gw.addr = static_cast(gateway); info.netmask.addr = static_cast(subnet); } else { info.ip.addr = 0; info.gw.addr = 0; info.netmask.addr = 0; } err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH); if(err != ESP_OK && err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED){ log_e("DHCP could not be stopped! Error: %d", err); return false; } err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH, &info); if(err != ERR_OK){ log_e("STA IP could not be configured! Error: %d", err); return false; } if(info.ip.addr){ staticIP = true; } else { err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_ETH); if(err != ESP_OK && err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STARTED){ log_w("DHCP could not be started! Error: %d", err); return false; } staticIP = false; } ip_addr_t d; d.type = IPADDR_TYPE_V4; if(dns1 != (uint32_t)0x00000000 && dns1 != INADDR_NONE) { // Set DNS1-Server d.u_addr.ip4.addr = static_cast(dns1); dns_setserver(0, &d); } if(dns2 != (uint32_t)0x00000000 && dns2 != INADDR_NONE) { // Set DNS2-Server d.u_addr.ip4.addr = static_cast(dns2); dns_setserver(1, &d); } return true; } IPAddress ETHClass::localIP() { tcpip_adapter_ip_info_t ip; if(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)){ return IPAddress(); } return IPAddress(ip.ip.addr); } IPAddress ETHClass::subnetMask() { tcpip_adapter_ip_info_t ip; if(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)){ return IPAddress(); } return IPAddress(ip.netmask.addr); } IPAddress ETHClass::gatewayIP() { tcpip_adapter_ip_info_t ip; if(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)){ return IPAddress(); } return IPAddress(ip.gw.addr); } IPAddress ETHClass::dnsIP(uint8_t dns_no) { const ip_addr_t * dns_ip = dns_getserver(dns_no); return IPAddress(dns_ip->u_addr.ip4.addr); } IPAddress ETHClass::broadcastIP() { tcpip_adapter_ip_info_t ip; if(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)){ return IPAddress(); } return WiFiGenericClass::calculateBroadcast(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr)); } IPAddress ETHClass::networkID() { tcpip_adapter_ip_info_t ip; if(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)){ return IPAddress(); } return WiFiGenericClass::calculateNetworkID(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr)); } uint8_t ETHClass::subnetCIDR() { tcpip_adapter_ip_info_t ip; if(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)){ return (uint8_t)0; } return WiFiGenericClass::calculateSubnetCIDR(IPAddress(ip.netmask.addr)); } const char * ETHClass::getHostname() { const char * hostname; if(tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_ETH, &hostname)){ return NULL; } return hostname; } bool ETHClass::setHostname(const char * hostname) { return tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_ETH, hostname) == 0; } bool ETHClass::fullDuplex() { #ifdef ESP_IDF_VERSION_MAJOR return true;//todo: do not see an API for this #else return eth_config.phy_get_duplex_mode(); #endif } bool ETHClass::linkUp() { #ifdef ESP_IDF_VERSION_MAJOR return eth_link == ETH_LINK_UP; #else return eth_config.phy_check_link(); #endif } uint8_t ETHClass::linkSpeed() { #ifdef ESP_IDF_VERSION_MAJOR eth_speed_t link_speed; esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &link_speed); return (link_speed == ETH_SPEED_10M)?10:100; #else return eth_config.phy_get_speed_mode()?100:10; #endif } bool ETHClass::enableIpV6() { return tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_ETH) == 0; } IPv6Address ETHClass::localIPv6() { static ip6_addr_t addr; if(tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_ETH, &addr)){ return IPv6Address(); } return IPv6Address(addr.addr); } uint8_t * ETHClass::macAddress(uint8_t* mac) { if(!mac){ return NULL; } #ifdef ESP_IDF_VERSION_MAJOR esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac); #else esp_eth_get_mac(mac); #endif return mac; } String ETHClass::macAddress(void) { uint8_t mac[6] = {0,0,0,0,0,0}; char macStr[18] = { 0 }; macAddress(mac); sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return String(macStr); } ETHClass ETH;