diff --git a/components/esp_eth/include/esp_eth.h b/components/esp_eth/include/esp_eth.h index d204b50898..6aea512a69 100644 --- a/components/esp_eth/include/esp_eth.h +++ b/components/esp_eth/include/esp_eth.h @@ -265,12 +265,24 @@ esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length) * * @param[in] hdl: handle of Ethernet driver * @param[in] cmd: IO control command -* @param[in] data: specificed data for command +* @param[in, out] data: address of data for `set` command or address where to store the data when used with `get` command * * @return * - ESP_OK: process io command successfully * - ESP_ERR_INVALID_ARG: process io command failed because of some invalid argument * - ESP_FAIL: process io command failed because some other error occurred +* +* The following IO control commands are supported: +* @li @c ETH_CMD_S_MAC_ADDR sets Ethernet interface MAC address. @c data argument is pointer to MAC address buffer with expected size of 6 bytes. +* @li @c ETH_CMD_G_MAC_ADDR gets Ethernet interface MAC address. @c data argument is pointer to a buffer to which MAC address is to be copied. The buffer size must be at least 6 bytes. +* @li @c ETH_CMD_S_PHY_ADDR sets PHY address in range of <0-31>. @c data argument is pointer to memory of uint32_t datatype from where the configuration option is read. +* @li @c ETH_CMD_G_PHY_ADDR gets PHY address. @c data argument is pointer to memory of uint32_t datatype to which the PHY address is to be stored. +* @li @c ETH_CMD_G_SPEED gets current Ethernet link speed. @c data argument is pointer to memory of eth_speed_t datatype to which the speed is to be stored. +* @li @c ETH_CMD_S_PROMISCUOUS sets/resets Ethernet interface promiscuous mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read. +* @li @c ETH_CMD_S_FLOW_CTRL sets/resets Ethernet interface flow control. @c data argument is pointer to memory of bool datatype from which the configuration option is read. +* @li @c ETH_CMD_G_DUPLEX_MODE gets current Ethernet link duplex mode. @c data argument is pointer to memory of eth_duplex_t datatype to which the duplex mode is to be stored. +* @li @c ETH_CMD_S_PHY_LOOPBACK sets/resets PHY to/from loopback mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read. +* */ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data); diff --git a/components/esp_eth/include/esp_eth_com.h b/components/esp_eth/include/esp_eth_com.h index 46409ed710..7ce0538fbe 100644 --- a/components/esp_eth/include/esp_eth_com.h +++ b/components/esp_eth/include/esp_eth_com.h @@ -89,6 +89,7 @@ typedef enum { ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */ ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */ ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */ + ETH_CMD_S_PHY_LOOPBACK,/*!< Set PHY loopback */ } esp_eth_io_cmd_t; /** diff --git a/components/esp_eth/include/esp_eth_phy.h b/components/esp_eth/include/esp_eth_phy.h index 4b4d860bd7..f85b7d4304 100644 --- a/components/esp_eth/include/esp_eth_phy.h +++ b/components/esp_eth/include/esp_eth_phy.h @@ -173,6 +173,19 @@ struct esp_eth_phy_s { */ esp_err_t (*advertise_pause_ability)(esp_eth_phy_t *phy, uint32_t ability); + /** + * @brief + * + * @param[in] phy: Ethernet PHY instance + * @param[in] enable: enables or disables PHY loopback + * + * @return + * - ESP_OK: configures PHY instance loopback function successfully + * - ESP_FAIL: PHY instance loopback configuration failed because some error occurred + * + */ + esp_err_t (*loopback)(esp_eth_phy_t *phy, bool enable); + /** * @brief Free memory of Ethernet PHY instance * diff --git a/components/esp_eth/src/esp_eth.c b/components/esp_eth/src/esp_eth.c index 611ba936c9..bef3e7966c 100644 --- a/components/esp_eth/src/esp_eth.c +++ b/components/esp_eth/src/esp_eth.c @@ -20,6 +20,7 @@ #include "esp_event.h" #include "esp_heap_caps.h" #include "esp_timer.h" +#include "soc/soc.h" // TODO: for esp_eth_ioctl API compatibility reasons, will be removed with next major release #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -360,8 +361,11 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data) ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed"); break; case ETH_CMD_S_PHY_ADDR: - ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set phy addr to null"); - ESP_GOTO_ON_ERROR(phy->set_addr(phy, (uint32_t)data), err, TAG, "set phy address failed"); + if ((uint32_t)data >= SOC_DRAM_LOW) { + ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed"); + } else { // TODO: for API compatibility reasons, will be removed with next major release + ESP_GOTO_ON_ERROR(phy->set_addr(phy, (uint32_t)data), err, TAG, "set phy address failed"); + } break; case ETH_CMD_G_PHY_ADDR: ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store phy addr"); @@ -372,16 +376,32 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data) *(eth_speed_t *)data = eth_driver->speed; break; case ETH_CMD_S_PROMISCUOUS: - ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, (bool)data), err, TAG, "set promiscuous mode failed"); + if ((uint32_t)data >= SOC_DRAM_LOW) { + ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed"); + } else { // TODO: for API compatibility reasons, will be removed with next major release + ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, (bool)data), err, TAG, "set promiscuous mode failed"); + } break; case ETH_CMD_S_FLOW_CTRL: - ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, (bool)data), err, TAG, "enable mac flow control failed"); - ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, (uint32_t)data), err, TAG, "phy advertise pause ability failed"); + if ((uint32_t)data >= SOC_DRAM_LOW) { + ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed"); + ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed"); + } else { // TODO: for API compatibility reasons, will be removed with next major release + ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, (bool)data), err, TAG, "enable mac flow control failed"); + ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, (uint32_t)data), err, TAG, "phy advertise pause ability failed"); + } break; case ETH_CMD_G_DUPLEX_MODE: ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value"); *(eth_duplex_t *)data = eth_driver->duplex; break; + case ETH_CMD_S_PHY_LOOPBACK: + if ((uint32_t)data >= SOC_DRAM_LOW) { + ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed"); + } else { // TODO: for API compatibility reasons, will be removed with next major release + ESP_GOTO_ON_ERROR(phy->loopback(phy, (bool)data), err, TAG, "configuration of phy loopback mode failed"); + } + break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd); break; diff --git a/components/esp_eth/src/esp_eth_phy_dm9051.c b/components/esp_eth/src/esp_eth_phy_dm9051.c index dcbc3d44fe..9f31d71253 100644 --- a/components/esp_eth/src/esp_eth_phy_dm9051.c +++ b/components/esp_eth/src/esp_eth_phy_dm9051.c @@ -316,6 +316,25 @@ err: return ret; } +static esp_err_t dm9051_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); + esp_eth_mediator_t *eth = dm9051->eth; + /* Set Loopback function */ + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + if (enable) { + bmcr.en_loopback = 1; + } else { + bmcr.en_loopback = 0; + } + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); + return ESP_OK; +err: + return ret; +} + static esp_err_t dm9051_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -372,6 +391,7 @@ esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config) dm9051->parent.get_addr = dm9051_get_addr; dm9051->parent.set_addr = dm9051_set_addr; dm9051->parent.advertise_pause_ability = dm9051_advertise_pause_ability; + dm9051->parent.loopback = dm9051_loopback; dm9051->parent.del = dm9051_del; return &(dm9051->parent); err: diff --git a/components/esp_eth/src/esp_eth_phy_dp83848.c b/components/esp_eth/src/esp_eth_phy_dp83848.c index a245a902d2..bbc6bffbf2 100644 --- a/components/esp_eth/src/esp_eth_phy_dp83848.c +++ b/components/esp_eth/src/esp_eth_phy_dp83848.c @@ -310,6 +310,25 @@ err: return ret; } +static esp_err_t dp83848_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent); + esp_eth_mediator_t *eth = dp83848->eth; + /* Set Loopback function */ + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + if (enable) { + bmcr.en_loopback = 1; + } else { + bmcr.en_loopback = 0; + } + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); + return ESP_OK; +err: + return ret; +} + static esp_err_t dp83848_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -366,6 +385,7 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config) dp83848->parent.get_addr = dp83848_get_addr; dp83848->parent.set_addr = dp83848_set_addr; dp83848->parent.advertise_pause_ability = dp83848_advertise_pause_ability; + dp83848->parent.loopback = dp83848_loopback; dp83848->parent.del = dp83848_del; return &(dp83848->parent); err: diff --git a/components/esp_eth/src/esp_eth_phy_ip101.c b/components/esp_eth/src/esp_eth_phy_ip101.c index 280f116a3c..ac72e3eec3 100644 --- a/components/esp_eth/src/esp_eth_phy_ip101.c +++ b/components/esp_eth/src/esp_eth_phy_ip101.c @@ -349,6 +349,25 @@ err: return ret; } +static esp_err_t ip101_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent); + esp_eth_mediator_t *eth = ip101->eth; + /* Set Loopback function */ + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + if (enable) { + bmcr.en_loopback = 1; + } else { + bmcr.en_loopback = 0; + } + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); + return ESP_OK; +err: + return ret; +} + static esp_err_t ip101_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -405,6 +424,7 @@ esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config) ip101->parent.get_addr = ip101_get_addr; ip101->parent.set_addr = ip101_set_addr; ip101->parent.advertise_pause_ability = ip101_advertise_pause_ability; + ip101->parent.loopback = ip101_loopback; ip101->parent.del = ip101_del; return &(ip101->parent); diff --git a/components/esp_eth/src/esp_eth_phy_ksz80xx.c b/components/esp_eth/src/esp_eth_phy_ksz80xx.c index 638f22c2ff..79d65fd2a3 100644 --- a/components/esp_eth/src/esp_eth_phy_ksz80xx.c +++ b/components/esp_eth/src/esp_eth_phy_ksz80xx.c @@ -327,6 +327,25 @@ err: return ret; } +static esp_err_t ksz80xx_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent); + esp_eth_mediator_t *eth = ksz80xx->eth; + /* Set Loopback function */ + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + if (enable) { + bmcr.en_loopback = 1; + } else { + bmcr.en_loopback = 0; + } + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); + return ESP_OK; +err: + return ret; +} + static esp_err_t ksz80xx_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -380,6 +399,7 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config) ksz8041->parent.get_addr = ksz80xx_get_addr; ksz8041->parent.set_addr = ksz80xx_set_addr; ksz8041->parent.advertise_pause_ability = ksz80xx_advertise_pause_ability; + ksz8041->parent.loopback = ksz80xx_loopback; ksz8041->parent.del = ksz80xx_del; return &(ksz8041->parent); err: diff --git a/components/esp_eth/src/esp_eth_phy_ksz8851snl.c b/components/esp_eth/src/esp_eth_phy_ksz8851snl.c index 3871a3e010..4f85ad8ea9 100644 --- a/components/esp_eth/src/esp_eth_phy_ksz8851snl.c +++ b/components/esp_eth/src/esp_eth_phy_ksz8851snl.c @@ -244,6 +244,26 @@ err: return ret; } +static esp_err_t phy_ksz8851_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent); + esp_eth_mediator_t *eth = ksz8851->eth; + + uint32_t mbcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1MBCR, &mbcr), err, TAG, "P1MBCR read failed"); + if (enable) { + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1MBCR, mbcr | P1MBCR_LOCAL_LOOPBACK), err, TAG, "P1MBCR write failed"); + ESP_LOGD(TAG, "set Local (far-end) loopback"); + } else { + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1MBCR, mbcr & ~P1MBCR_LOCAL_LOOPBACK), err, TAG, "P1MBCR write failed"); + ESP_LOGD(TAG, "disabled Local (far-end) loopback"); + } + return ESP_OK; +err: + return ret; +} + static esp_err_t phy_ksz8851_del(esp_eth_phy_t *phy) { ESP_LOGD(TAG, "deleting PHY"); @@ -274,6 +294,7 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8851snl(const eth_phy_config_t *config) ksz8851->parent.set_addr = phy_ksz8851_set_addr; ksz8851->parent.get_addr = phy_ksz8851_get_addr; ksz8851->parent.advertise_pause_ability = phy_ksz8851_advertise_pause_ability; + ksz8851->parent.loopback = phy_ksz8851_loopback; ksz8851->parent.del = phy_ksz8851_del; return &(ksz8851->parent); err: diff --git a/components/esp_eth/src/esp_eth_phy_lan87xx.c b/components/esp_eth/src/esp_eth_phy_lan87xx.c index 5dc21b5c9a..e6465501f0 100644 --- a/components/esp_eth/src/esp_eth_phy_lan87xx.c +++ b/components/esp_eth/src/esp_eth_phy_lan87xx.c @@ -465,6 +465,25 @@ err: return ret; } +static esp_err_t lan87xx_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent); + esp_eth_mediator_t *eth = lan87xx->eth; + /* Set Loopback function */ + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + if (enable) { + bmcr.en_loopback = 1; + } else { + bmcr.en_loopback = 0; + } + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); + return ESP_OK; +err: + return ret; +} + static esp_err_t lan87xx_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -528,6 +547,7 @@ esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config) lan87xx->parent.pwrctl = lan87xx_pwrctl; lan87xx->parent.get_addr = lan87xx_get_addr; lan87xx->parent.set_addr = lan87xx_set_addr; + lan87xx->parent.loopback = lan87xx_loopback; lan87xx->parent.advertise_pause_ability = lan87xx_advertise_pause_ability; lan87xx->parent.del = lan87xx_del; diff --git a/components/esp_eth/src/esp_eth_phy_rtl8201.c b/components/esp_eth/src/esp_eth_phy_rtl8201.c index 6733328b80..2f05b000d8 100644 --- a/components/esp_eth/src/esp_eth_phy_rtl8201.c +++ b/components/esp_eth/src/esp_eth_phy_rtl8201.c @@ -302,6 +302,25 @@ err: return ret; } +static esp_err_t rtl8201_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent); + esp_eth_mediator_t *eth = rtl8201->eth; + /* Set Loopback function */ + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + if (enable) { + bmcr.en_loopback = 1; + } else { + bmcr.en_loopback = 0; + } + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed"); + return ESP_OK; +err: + return ret; +} + static esp_err_t rtl8201_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -358,6 +377,7 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config) rtl8201->parent.get_addr = rtl8201_get_addr; rtl8201->parent.set_addr = rtl8201_set_addr; rtl8201->parent.advertise_pause_ability = rtl8201_advertise_pause_ability; + rtl8201->parent.loopback = rtl8201_loopback; rtl8201->parent.del = rtl8201_del; return &(rtl8201->parent); diff --git a/components/esp_eth/src/esp_eth_phy_w5500.c b/components/esp_eth/src/esp_eth_phy_w5500.c index 2ec157c6b4..4296582d97 100644 --- a/components/esp_eth/src/esp_eth_phy_w5500.c +++ b/components/esp_eth/src/esp_eth_phy_w5500.c @@ -197,6 +197,12 @@ static esp_err_t w5500_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t abil return ESP_OK; } +static esp_err_t w5500_loopback(esp_eth_phy_t *phy, bool enable) +{ + // Loopback is not supported for W5500 internal PHY + return ESP_ERR_NOT_SUPPORTED; +} + static esp_err_t w5500_init(esp_eth_phy_t *phy) { esp_err_t ret = ESP_OK; @@ -241,6 +247,7 @@ esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config) w5500->parent.get_addr = w5500_get_addr; w5500->parent.set_addr = w5500_set_addr; w5500->parent.advertise_pause_ability = w5500_advertise_pause_ability; + w5500->parent.loopback = w5500_loopback; w5500->parent.del = w5500_del; return &(w5500->parent); err: diff --git a/examples/ethernet/eth2ap/main/ethernet_example_main.c b/examples/ethernet/eth2ap/main/ethernet_example_main.c index 0595872965..a50991d868 100644 --- a/examples/ethernet/eth2ap/main/ethernet_example_main.c +++ b/examples/ethernet/eth2ap/main/ethernet_example_main.c @@ -242,7 +242,8 @@ static void initialize_ethernet(void) 0x02, 0x00, 0x00, 0x12, 0x34, 0x56 })); #endif - esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, (void *)true); + bool eth_promiscuous = true; + esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, ð_promiscuous); esp_eth_start(s_eth_handle); }