esp_eth: add PHY loopback control via esp_eth_ioctl

Fixed esp_eth_ioctl command's data argument non-standard handling
This commit is contained in:
Ondrej Kosta 2021-09-03 17:24:01 +02:00
parent 502e132e5d
commit 76326e8268
13 changed files with 202 additions and 7 deletions

View File

@ -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);

View File

@ -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;
/**

View File

@ -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
*

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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);

View File

@ -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:

View File

@ -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:

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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);
}