فهرست منبع

Merge branch 'feature/set_phy_loopback' into 'master'

esp_eth: add PHY loopback control via esp_eth_ioctl

Closes IDF-3865

See merge request espressif/esp-idf!15096
Ondrej Kosta 4 سال پیش
والد
کامیت
61455ddc19

+ 13 - 1
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);
 

+ 1 - 0
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;
 
 /**

+ 13 - 0
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
     *

+ 25 - 5
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;

+ 20 - 0
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:

+ 20 - 0
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:

+ 20 - 0
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);

+ 20 - 0
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:

+ 21 - 0
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:

+ 20 - 0
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;
 

+ 20 - 0
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);

+ 7 - 0
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:

+ 2 - 1
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, &eth_promiscuous);
     esp_eth_start(s_eth_handle);
 }