ethernet: support flow control for esp32 emac

This commit is contained in:
morris
2020-07-20 20:42:52 +08:00
parent e97e7281b2
commit 4e38aab1b0
17 changed files with 490 additions and 32 deletions

View File

@ -78,6 +78,7 @@ typedef enum {
ETH_STATE_LINK, /*!< Link status changed */
ETH_STATE_SPEED, /*!< Speed updated */
ETH_STATE_DUPLEX, /*!< Duplex updated */
ETH_STATE_PAUSE, /*!< Pause ability updated */
} esp_eth_state_t;
/**
@ -91,6 +92,7 @@ typedef enum {
ETH_CMD_S_PHY_ADDR, /*!< Set PHY address */
ETH_CMD_G_SPEED, /*!< Get Speed */
ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */
ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */
} esp_eth_io_cmd_t;
/**

View File

@ -252,6 +252,31 @@ struct esp_eth_mac_s {
*/
esp_err_t (*set_promiscuous)(esp_eth_mac_t *mac, bool enable);
/**
* @brief Enable flow control on MAC layer or not
*
* @param[in] mac: Ethernet MAC instance
* @param[in] enable: set true to enable flow control; set false to disable flow control
*
* @return
* - ESP_OK: set flow control successfully
* - ESP_FAIL: set flow control failed because some error occurred
*
*/
esp_err_t (*enable_flow_ctrl)(esp_eth_mac_t *mac, bool enable);
/**
* @brief Set the PAUSE ability of peer node
*
* @param[in] mac: Ethernet MAC instance
* @param[in] ability: zero indicates that pause function is supported by link partner; non-zero indicates that pause function is not supported by link partner
*
* @return
* - ESP_OK: set peer pause ability successfully
* - ESP_FAIL: set peer pause ability failed because some error occurred
*/
esp_err_t (*set_peer_pause_ability)(esp_eth_mac_t *mac, uint32_t ability);
/**
* @brief Free memory of Ethernet MAC
*

View File

@ -160,6 +160,19 @@ struct esp_eth_phy_s {
*/
esp_err_t (*get_addr)(esp_eth_phy_t *phy, uint32_t *addr);
/**
* @brief Advertise pause function supported by MAC layer
*
* @param[in] phy: Ethernet PHY instance
* @param[out] addr: Pause ability
*
* @return
* - ESP_OK: Advertise pause ability successfully
* - ESP_ERR_INVALID_ARG: Advertise pause ability failed because of invalid argument
*
*/
esp_err_t (*advertise_pause_ability)(esp_eth_phy_t *phy, uint32_t ability);
/**
* @brief Free memory of Ethernet PHY instance
*

View File

@ -142,6 +142,11 @@ static esp_err_t eth_on_state_changed(esp_eth_mediator_t *eth, esp_eth_state_t s
eth_driver->duplex = duplex;
break;
}
case ETH_STATE_PAUSE: {
uint32_t peer_pause_ability = (uint32_t)args;
ETH_CHECK(mac->set_peer_pause_ability(mac, peer_pause_ability) == ESP_OK, "ethernet mac set peer pause ability failed", err, ESP_FAIL);
break;
}
default:
ETH_CHECK(false, "unknown ethernet state: %d", err, ESP_ERR_INVALID_ARG, state);
break;
@ -369,6 +374,10 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
case ETH_CMD_S_PROMISCUOUS:
ETH_CHECK(mac->set_promiscuous(mac, (bool)data) == ESP_OK, "set promiscuous mode failed", err, ESP_FAIL);
break;
case ETH_CMD_S_FLOW_CTRL:
ETH_CHECK(mac->enable_flow_ctrl(mac, (bool)data) == ESP_OK, "enable mac flow control failed", err, ESP_FAIL);
ETH_CHECK(phy->advertise_pause_ability(phy, (uint32_t)data) == ESP_OK, "phy advertise pause ability failed", err, ESP_FAIL);
break;
default:
ETH_CHECK(false, "unknown io command: %d", err, ESP_ERR_INVALID_ARG, cmd);
break;

View File

@ -63,6 +63,7 @@ typedef struct {
int int_gpio_num;
uint8_t addr[6];
bool packets_remain;
bool flow_ctrl_enabled;
} emac_dm9051_t;
static inline bool dm9051_lock(emac_dm9051_t *emac)
@ -303,12 +304,6 @@ static esp_err_t dm9051_setup_default(emac_dm9051_t *emac)
/* stop receiving, no promiscuous mode, no runt packet(size < 64bytes), not all multicast packets*/
/* discard long packet(size > 1522bytes) and crc error packet, enable watchdog */
MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC) == ESP_OK, "write RCR failed", err, ESP_FAIL);
/* send jam pattern (duration time = 1.15ms) when rx free space < 3k bytes */
MAC_CHECK(dm9051_register_write(emac, DM9051_BPTR, 0x3F) == ESP_OK, "write BPTR failed", err, ESP_FAIL);
/* flow control: high water threshold = 3k bytes, low water threshold = 8k bytes */
MAC_CHECK(dm9051_register_write(emac, DM9051_FCTR, 0x38) == ESP_OK, "write FCTR failed", err, ESP_FAIL);
/* enable flow control */
MAC_CHECK(dm9051_register_write(emac, DM9051_FCR, FCR_FLOW_ENABLE) == ESP_OK, "write FCR failed", err, ESP_FAIL);
/* retry late collision packet, at most two transmit command can be issued before transmit complete */
MAC_CHECK(dm9051_register_write(emac, DM9051_TCR2, TCR2_RLCP) == ESP_OK, "write TCR2 failed", err, ESP_FAIL);
/* enable auto transmit */
@ -337,6 +332,25 @@ err:
return ret;
}
static esp_err_t dm9051_enable_flow_ctrl(emac_dm9051_t *emac, bool enable)
{
esp_err_t ret = ESP_OK;
if (enable) {
/* send jam pattern (duration time = 1.15ms) when rx free space < 3k bytes */
MAC_CHECK(dm9051_register_write(emac, DM9051_BPTR, 0x3F) == ESP_OK, "write BPTR failed", err, ESP_FAIL);
/* flow control: high water threshold = 3k bytes, low water threshold = 8k bytes */
MAC_CHECK(dm9051_register_write(emac, DM9051_FCTR, 0x38) == ESP_OK, "write FCTR failed", err, ESP_FAIL);
/* enable flow control */
MAC_CHECK(dm9051_register_write(emac, DM9051_FCR, FCR_FLOW_ENABLE) == ESP_OK, "write FCR failed", err, ESP_FAIL);
} else {
/* disable flow control */
MAC_CHECK(dm9051_register_write(emac, DM9051_FCR, 0) == ESP_OK, "write FCR failed", err, ESP_FAIL);
}
return ESP_OK;
err:
return ret;
}
/**
* @brief start dm9051: enable interrupt and start receive
*/
@ -604,6 +618,27 @@ err:
return ret;
}
static esp_err_t emac_dm9051_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
{
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
emac->flow_ctrl_enabled = enable;
return ESP_OK;
}
static esp_err_t emac_dm9051_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
{
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
// we want to enable flow control, and peer does support pause function
// then configure the MAC layer to enable flow control feature
if (emac->flow_ctrl_enabled && ability) {
dm9051_enable_flow_ctrl(emac, true);
} else {
dm9051_enable_flow_ctrl(emac, false);
ESP_LOGD(TAG, "Flow control not enabled for the link");
}
return ESP_OK;
}
static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
{
esp_err_t ret = ESP_OK;
@ -751,6 +786,8 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config,
emac->parent.set_duplex = emac_dm9051_set_duplex;
emac->parent.set_link = emac_dm9051_set_link;
emac->parent.set_promiscuous = emac_dm9051_set_promiscuous;
emac->parent.set_peer_pause_ability = emac_dm9051_set_peer_pause_ability;
emac->parent.enable_flow_ctrl = emac_dm9051_enable_flow_ctrl;
emac->parent.transmit = emac_dm9051_transmit;
emac->parent.receive = emac_dm9051_receive;
/* create mutex */

View File

@ -47,6 +47,9 @@ static const char *TAG = "emac_esp32";
#define PHY_OPERATION_TIMEOUT_US (1000)
#define FLOW_CONTROL_LOW_WATER_MARK (CONFIG_ETH_DMA_RX_BUFFER_NUM / 3)
#define FLOW_CONTROL_HIGH_WATER_MARK (FLOW_CONTROL_LOW_WATER_MARK * 2)
typedef struct {
esp_eth_mac_t parent;
esp_eth_mediator_t *eth;
@ -55,12 +58,17 @@ typedef struct {
TaskHandle_t rx_task_hdl;
uint32_t sw_reset_timeout_ms;
uint32_t frames_remain;
uint32_t free_rx_descriptor;
uint32_t flow_control_high_water_mark;
uint32_t flow_control_low_water_mark;
int smi_mdc_gpio_num;
int smi_mdio_gpio_num;
uint8_t addr[6];
uint8_t *rx_buf[CONFIG_ETH_DMA_RX_BUFFER_NUM];
uint8_t *tx_buf[CONFIG_ETH_DMA_TX_BUFFER_NUM];
bool isr_need_yield;
bool flow_ctrl_enabled; // indicates whether the user want to do flow control
bool do_flow_ctrl; // indicates whether we need to do software flow control
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
@ -217,6 +225,29 @@ static esp_err_t emac_esp32_set_promiscuous(esp_eth_mac_t *mac, bool enable)
return ESP_OK;
}
static esp_err_t emac_esp32_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
emac->flow_ctrl_enabled = enable;
return ESP_OK;
}
static esp_err_t emac_esp32_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
// we want to enable flow control, and peer does support pause function
// then configure the MAC layer to enable flow control feature
if (emac->flow_ctrl_enabled && ability) {
emac_hal_enable_flow_ctrl(&emac->hal, true);
emac->do_flow_ctrl = true;
} else {
emac_hal_enable_flow_ctrl(&emac->hal, false);
emac->do_flow_ctrl = false;
ESP_LOGD(TAG, "Flow control not enabled for the link");
}
return ESP_OK;
}
static esp_err_t emac_esp32_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
{
esp_err_t ret = ESP_OK;
@ -234,7 +265,7 @@ static esp_err_t emac_esp32_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *
uint32_t expected_len = *length;
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
MAC_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG);
uint32_t receive_len = emac_hal_receive_frame(&emac->hal, buf, expected_len, &emac->frames_remain);
uint32_t receive_len = emac_hal_receive_frame(&emac->hal, buf, expected_len, &emac->frames_remain, &emac->free_rx_descriptor);
/* we need to check the return value in case the buffer size is not enough */
ESP_LOGD(TAG, "receive len= %d", receive_len);
MAC_CHECK(expected_len >= receive_len, "received buffer longer than expected", err, ESP_ERR_INVALID_SIZE);
@ -268,6 +299,12 @@ static void emac_esp32_rx_task(void *arg)
} else {
free(buffer);
}
// we need to do extra checking of remained frames in case there are no unhandled frames left, but pause frame is still undergoing
if ((emac->free_rx_descriptor < emac->flow_control_low_water_mark) && emac->do_flow_ctrl && emac->frames_remain) {
emac_hal_send_pause_frame(&emac->hal, true);
} else if ((emac->free_rx_descriptor > emac->flow_control_high_water_mark) || !emac->frames_remain) {
emac_hal_send_pause_frame(&emac->hal, false);
}
} while (emac->frames_remain);
}
vTaskDelete(NULL);
@ -428,6 +465,8 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config)
emac->sw_reset_timeout_ms = config->sw_reset_timeout_ms;
emac->smi_mdc_gpio_num = config->smi_mdc_gpio_num;
emac->smi_mdio_gpio_num = config->smi_mdio_gpio_num;
emac->flow_control_high_water_mark = FLOW_CONTROL_HIGH_WATER_MARK;
emac->flow_control_low_water_mark = FLOW_CONTROL_LOW_WATER_MARK;
emac->parent.set_mediator = emac_esp32_set_mediator;
emac->parent.init = emac_esp32_init;
emac->parent.deinit = emac_esp32_deinit;
@ -442,6 +481,8 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config)
emac->parent.set_duplex = emac_esp32_set_duplex;
emac->parent.set_link = emac_esp32_set_link;
emac->parent.set_promiscuous = emac_esp32_set_promiscuous;
emac->parent.set_peer_pause_ability = emac_esp32_set_peer_pause_ability;
emac->parent.enable_flow_ctrl = emac_esp32_enable_flow_ctrl;
emac->parent.transmit = emac_esp32_transmit;
emac->parent.receive = emac_esp32_receive;
/* Interrupt configuration */

View File

@ -231,6 +231,18 @@ static esp_err_t emac_opencores_set_promiscuous(esp_eth_mac_t *mac, bool enable)
return ESP_OK;
}
static esp_err_t emac_opencores_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
{
/* QEMU doesn't emulate flow control function, so accept any value */
return ESP_OK;
}
static esp_err_t emac_opencores_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
{
/* QEMU doesn't emulate PAUSE function, so accept any value */
return ESP_OK;
}
static esp_err_t emac_opencores_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
{
esp_err_t ret = ESP_OK;
@ -390,6 +402,8 @@ esp_eth_mac_t *esp_eth_mac_new_openeth(const eth_mac_config_t *config)
emac->parent.set_duplex = emac_opencores_set_duplex;
emac->parent.set_link = emac_opencores_set_link;
emac->parent.set_promiscuous = emac_opencores_set_promiscuous;
emac->parent.set_peer_pause_ability = emac_opencores_set_peer_pause_ability;
emac->parent.enable_flow_ctrl = emac_opencores_enable_flow_ctrl;
emac->parent.transmit = emac_opencores_transmit;
emac->parent.receive = emac_opencores_receive;

View File

@ -94,8 +94,10 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
esp_eth_mediator_t *eth = dm9051->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
uint32_t peer_pause_ability = false;
bmsr_reg_t bmsr;
dscsr_reg_t dscsr;
anlpar_reg_t anlpar;
// BMSR is a latch low register
// after power up, the first latched value must be 0, which means down
// to speed up power up link speed, double read this register as a workaround
@ -103,6 +105,8 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
"read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
"read ANLPAR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (dm9051->link_status != link) {
@ -124,6 +128,14 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
/* if we're in duplex mode, and peer has the flow control ability */
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
peer_pause_ability = 1;
} else {
peer_pause_ability = 0;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
"change pause ability failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
@ -289,6 +301,28 @@ static esp_err_t dm9051_del(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t dm9051_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
esp_eth_mediator_t *eth = dm9051->eth;
/* Set PAUSE function ability */
anar_reg_t anar;
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
"read ANAR failed", err);
if (ability) {
anar.asymmetric_pause = 1;
anar.symmetric_pause = 1;
} else {
anar.asymmetric_pause = 0;
anar.symmetric_pause = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
"write ANAR failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t dm9051_init(esp_eth_phy_t *phy)
{
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
@ -344,6 +378,7 @@ esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config)
dm9051->parent.pwrctl = dm9051_pwrctl;
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.del = dm9051_del;
return &(dm9051->parent);
err:

View File

@ -100,7 +100,11 @@ static esp_err_t dp83848_update_link_duplex_speed(phy_dp83848_t *dp83848)
esp_eth_mediator_t *eth = dp83848->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
uint32_t peer_pause_ability = false;
anlpar_reg_t anlpar;
physts_reg_t physts;
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
"read ANLPAR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)) == ESP_OK,
"read PHYSTS failed", err);
eth_link_t link = physts.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
@ -122,6 +126,14 @@ static esp_err_t dp83848_update_link_duplex_speed(phy_dp83848_t *dp83848)
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
/* if we're in duplex mode, and peer has the flow control ability */
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
peer_pause_ability = 1;
} else {
peer_pause_ability = 0;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
"change pause ability failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
@ -279,6 +291,28 @@ static esp_err_t dp83848_del(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t dp83848_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
esp_eth_mediator_t *eth = dp83848->eth;
/* Set PAUSE function ability */
anar_reg_t anar;
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
"read ANAR failed", err);
if (ability) {
anar.asymmetric_pause = 1;
anar.symmetric_pause = 1;
} else {
anar.asymmetric_pause = 0;
anar.symmetric_pause = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
"write ANAR failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t dp83848_init(esp_eth_phy_t *phy)
{
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
@ -334,6 +368,7 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config)
dp83848->parent.pwrctl = dp83848_pwrctl;
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.del = dp83848_del;
return &(dp83848->parent);

View File

@ -129,10 +129,14 @@ static esp_err_t ip101_update_link_duplex_speed(phy_ip101_t *ip101)
esp_eth_mediator_t *eth = ip101->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
uint32_t peer_pause_ability = false;
cssr_reg_t cssr;
anlpar_reg_t anlpar;
PHY_CHECK(ip101_page_select(ip101, 16) == ESP_OK, "select page 16 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_CSSR_REG_ADDR, &(cssr.val)) == ESP_OK,
"read CSSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
"read ANLPAR failed", err);
eth_link_t link = cssr.link_up ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (ip101->link_status != link) {
@ -162,6 +166,14 @@ static esp_err_t ip101_update_link_duplex_speed(phy_ip101_t *ip101)
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
/* if we're in duplex mode, and peer has the flow control ability */
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
peer_pause_ability = 1;
} else {
peer_pause_ability = 0;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
"change pause ability failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"chagne link failed", err);
@ -316,6 +328,28 @@ static esp_err_t ip101_del(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t ip101_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
esp_eth_mediator_t *eth = ip101->eth;
/* Set PAUSE function ability */
anar_reg_t anar;
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
"read ANAR failed", err);
if (ability) {
anar.asymmetric_pause = 1;
anar.symmetric_pause = 1;
} else {
anar.asymmetric_pause = 0;
anar.symmetric_pause = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
"write ANAR failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t ip101_init(esp_eth_phy_t *phy)
{
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
@ -368,6 +402,7 @@ esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config)
ip101->parent.pwrctl = ip101_pwrctl;
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.del = ip101_del;
return &(ip101->parent);

View File

@ -75,8 +75,12 @@ static esp_err_t ksz8041_update_link_duplex_speed(phy_ksz8041_t *ksz8041)
esp_eth_mediator_t *eth = ksz8041->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
uint32_t peer_pause_ability = false;
anlpar_reg_t anlpar;
bmsr_reg_t bmsr;
pc2r_reg_t pc2r;
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
"read ANLPAR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
@ -110,6 +114,14 @@ static esp_err_t ksz8041_update_link_duplex_speed(phy_ksz8041_t *ksz8041)
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
/* if we're in duplex mode, and peer has the flow control ability */
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
peer_pause_ability = 1;
} else {
peer_pause_ability = 0;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
"change pause ability failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
@ -266,6 +278,28 @@ static esp_err_t ksz8041_del(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t ksz8041_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
esp_eth_mediator_t *eth = ksz8041->eth;
/* Set PAUSE function ability */
anar_reg_t anar;
PHY_CHECK(eth->phy_reg_read(eth, ksz8041->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
"read ANAR failed", err);
if (ability) {
anar.asymmetric_pause = 1;
anar.symmetric_pause = 1;
} else {
anar.asymmetric_pause = 0;
anar.symmetric_pause = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, ksz8041->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
"write ANAR failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t ksz8041_init(esp_eth_phy_t *phy)
{
phy_ksz8041_t *ksz8041 = __containerof(phy, phy_ksz8041_t, parent);
@ -316,6 +350,7 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config)
ksz8041->parent.pwrctl = ksz8041_pwrctl;
ksz8041->parent.get_addr = ksz8041_get_addr;
ksz8041->parent.set_addr = ksz8041_set_addr;
ksz8041->parent.advertise_pause_ability = ksz8041_advertise_pause_ability;
ksz8041->parent.del = ksz8041_del;
return &(ksz8041->parent);

View File

@ -174,6 +174,10 @@ static esp_err_t lan8720_update_link_duplex_speed(phy_lan8720_t *lan8720)
eth_duplex_t duplex = ETH_DUPLEX_HALF;
bmsr_reg_t bmsr;
pscsr_reg_t pscsr;
uint32_t peer_pause_ability = false;
anlpar_reg_t anlpar;
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
"read ANLPAR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
@ -207,6 +211,14 @@ static esp_err_t lan8720_update_link_duplex_speed(phy_lan8720_t *lan8720)
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
/* if we're in duplex mode, and peer has the flow control ability */
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
peer_pause_ability = 1;
} else {
peer_pause_ability = 0;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
"change pause ability failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
@ -363,6 +375,28 @@ static esp_err_t lan8720_del(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t lan8720_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
esp_eth_mediator_t *eth = lan8720->eth;
/* Set PAUSE function ability */
anar_reg_t anar;
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
"read ANAR failed", err);
if (ability) {
anar.asymmetric_pause = 1;
anar.symmetric_pause = 1;
} else {
anar.asymmetric_pause = 0;
anar.symmetric_pause = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
"write ANAR failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t lan8720_init(esp_eth_phy_t *phy)
{
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
@ -417,6 +451,7 @@ esp_eth_phy_t *esp_eth_phy_new_lan8720(const eth_phy_config_t *config)
lan8720->parent.pwrctl = lan8720_pwrctl;
lan8720->parent.get_addr = lan8720_get_addr;
lan8720->parent.set_addr = lan8720_set_addr;
lan8720->parent.advertise_pause_ability = lan8720_advertise_pause_ability;
lan8720->parent.del = lan8720_del;
return &(lan8720->parent);

View File

@ -92,9 +92,13 @@ static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
eth_duplex_t duplex = ETH_DUPLEX_HALF;
bmcr_reg_t bmcr;
bmsr_reg_t bmsr;
uint32_t peer_pause_ability = false;
anlpar_reg_t anlpar;
PHY_CHECK(rtl8201_page_select(rtl8201, 0) == ESP_OK, "select page 0 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
"read ANLPAR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (rtl8201->link_status != link) {
@ -116,6 +120,14 @@ static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
/* if we're in duplex mode, and peer has the flow control ability */
if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
peer_pause_ability = 1;
} else {
peer_pause_ability = 0;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability) == ESP_OK,
"change pause ability failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
@ -270,6 +282,28 @@ static esp_err_t rtl8201_del(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t rtl8201_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
esp_eth_mediator_t *eth = rtl8201->eth;
/* Set PAUSE function ability */
anar_reg_t anar;
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK,
"read ANAR failed", err);
if (ability) {
anar.asymmetric_pause = 1;
anar.symmetric_pause = 1;
} else {
anar.asymmetric_pause = 0;
anar.symmetric_pause = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK,
"write ANAR failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t rtl8201_init(esp_eth_phy_t *phy)
{
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
@ -325,6 +359,7 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config)
rtl8201->parent.pwrctl = rtl8201_pwrctl;
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.del = rtl8201_del;
return &(rtl8201->parent);

View File

@ -24,7 +24,8 @@ typedef volatile struct {
struct {
uint32_t div_num : 4;
uint32_t h_div_num : 4;
uint32_t reserved8 : 24;
uint32_t dly_num : 2;
uint32_t reserved10 : 22;
};
uint32_t val;
} ex_clkout_conf;
@ -43,18 +44,28 @@ typedef volatile struct {
struct {
uint32_t ext_en : 1;
uint32_t int_en : 1;
uint32_t reserved2 : 1;
uint32_t rx_125_clk_en : 1;
uint32_t mii_clk_tx_en : 1;
uint32_t mii_clk_rx_en : 1;
uint32_t reserved5 : 27;
uint32_t clk_en : 1;
uint32_t reserved6 : 26;
};
uint32_t val;
} ex_clk_ctrl;
union {
struct {
uint32_t reserved0 : 13;
uint32_t int_revmii_rx_clk_sel : 1;
uint32_t ext_revmii_rx_clk_sel : 1;
uint32_t sbd_flowctrl : 1;
uint32_t core_phy_addr : 5;
uint32_t revmii_phy_addr : 5;
uint32_t phy_intf_sel : 3;
uint32_t reserved16 : 16;
uint32_t ss_mode : 1;
uint32_t sbd_clk_gating_en : 1;
uint32_t pmt_ctrl_en : 1;
uint32_t scr_smi_dly_rx_sync : 1;
uint32_t tx_err_out_en : 1;
uint32_t reserved21 : 11;
};
uint32_t val;
} ex_phyinf_conf;
@ -65,6 +76,65 @@ typedef volatile struct {
};
uint32_t val;
} pd_sel;
uint32_t reserved_14;
uint32_t reserved_18;
uint32_t reserved_1c;
uint32_t reserved_20;
uint32_t reserved_24;
uint32_t reserved_28;
uint32_t reserved_2c;
uint32_t reserved_30;
uint32_t reserved_34;
uint32_t reserved_38;
uint32_t reserved_3c;
uint32_t reserved_40;
uint32_t reserved_44;
uint32_t reserved_48;
uint32_t reserved_4c;
uint32_t reserved_50;
uint32_t reserved_54;
uint32_t reserved_58;
uint32_t reserved_5c;
uint32_t reserved_60;
uint32_t reserved_64;
uint32_t reserved_68;
uint32_t reserved_6c;
uint32_t reserved_70;
uint32_t reserved_74;
uint32_t reserved_78;
uint32_t reserved_7c;
uint32_t reserved_80;
uint32_t reserved_84;
uint32_t reserved_88;
uint32_t reserved_8c;
uint32_t reserved_90;
uint32_t reserved_94;
uint32_t reserved_98;
uint32_t reserved_9c;
uint32_t reserved_a0;
uint32_t reserved_a4;
uint32_t reserved_a8;
uint32_t reserved_ac;
uint32_t reserved_b0;
uint32_t reserved_b4;
uint32_t reserved_b8;
uint32_t reserved_bc;
uint32_t reserved_c0;
uint32_t reserved_c4;
uint32_t reserved_c8;
uint32_t reserved_cc;
uint32_t reserved_d0;
uint32_t reserved_d4;
uint32_t reserved_d8;
uint32_t reserved_dc;
uint32_t reserved_e0;
uint32_t reserved_e4;
uint32_t reserved_e8;
uint32_t reserved_ec;
uint32_t reserved_f0;
uint32_t reserved_f4;
uint32_t reserved_f8;
uint32_t ex_date;
} emac_ext_dev_t;
extern emac_ext_dev_t EMAC_EXT;

View File

@ -256,21 +256,28 @@ void emac_hal_init_mac_default(emac_hal_context_t *hal)
/* Disable Promiscuous Mode */
macffr.pmode = EMAC_PROMISCUOUS_DISABLE;
hal->mac_regs->gmacff = macffr;
}
void emac_hal_enable_flow_ctrl(emac_hal_context_t *hal, bool enable)
{
/* MACFCR Configuration */
typeof(hal->mac_regs->gmacfc) macfcr = hal->mac_regs->gmacfc;
/* Pause time */
macfcr.pause_time = EMAC_PAUSE_TIME;
/* Enable generation of Zero-Quanta Pause Control frames */
macfcr.dzpq = EMAC_ZERO_QUANTA_PAUSE_ENABLE;
/* Threshold of the PAUSE to be checked for automatic retransmission of PAUSE Frame */
macfcr.plt = EMAC_PAUSE_LOW_THRESHOLD_MINUS_28;
/* Don't allow MAC detect Pause frames with MAC address0 unicast address and unique multicast address */
macfcr.upfd = EMAC_UNICAST_PAUSE_DETECT_DISABLE;
/* Enable MAC to decode the received Pause frame and disable its transmitter for a specific time */
macfcr.rfce = EMAC_RECEIVE_FLOW_CONTROL_ENABLE;
/* Enable MAC to transmit Pause frames in full duplex mode or the MAC back-pressure operation in half duplex mode */
macfcr.tfce = EMAC_TRANSMIT_FLOW_CONTROL_ENABLE;
if (enable) {
/* Pause time */
macfcr.pause_time = EMAC_PAUSE_TIME;
/* Enable generation of Zero-Quanta Pause Control frames */
macfcr.dzpq = EMAC_ZERO_QUANTA_PAUSE_ENABLE;
/* Threshold of the PAUSE to be checked for automatic retransmission of PAUSE Frame */
macfcr.plt = EMAC_PAUSE_LOW_THRESHOLD_MINUS_28;
/* Don't allow MAC detect Pause frames with MAC address0 unicast address and unique multicast address */
macfcr.upfd = EMAC_UNICAST_PAUSE_DETECT_DISABLE;
/* Enable MAC to decode the received Pause frame and disable its transmitter for a specific time */
macfcr.rfce = EMAC_RECEIVE_FLOW_CONTROL_ENABLE;
/* Enable MAC to transmit Pause frames in full duplex mode or the MAC back-pressure operation in half duplex mode */
macfcr.tfce = EMAC_TRANSMIT_FLOW_CONTROL_ENABLE;
} else {
macfcr.val = 0;
}
hal->mac_regs->gmacfc = macfcr;
}
@ -341,6 +348,15 @@ void emac_hal_set_promiscuous(emac_hal_context_t *hal, bool enable)
}
}
void emac_hal_send_pause_frame(emac_hal_context_t *hal, bool enable)
{
if (enable) {
hal->ext_regs->ex_phyinf_conf.sbd_flowctrl = 1;
} else {
hal->ext_regs->ex_phyinf_conf.sbd_flowctrl = 0;
}
}
bool emac_hal_is_mii_busy(emac_hal_context_t *hal)
{
return hal->mac_regs->emacgmiiaddr.miibusy ? true : false;
@ -491,11 +507,11 @@ err:
return sentout;
}
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain)
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc)
{
eth_dma_rx_descriptor_t *desc_iter = NULL;
eth_dma_rx_descriptor_t *first_desc = NULL;
uint32_t iter = 0;
uint32_t used_descs = 0;
uint32_t seg_count = 0;
uint32_t ret_len = 0;
uint32_t copy_len = 0;
@ -505,8 +521,8 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t
first_desc = hal->rx_desc;
desc_iter = hal->rx_desc;
/* Traverse descriptors owned by CPU */
while ((desc_iter->RDES0.Own != EMAC_DMADESC_OWNER_DMA) && (iter < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) {
iter++;
while ((desc_iter->RDES0.Own != EMAC_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) {
used_descs++;
seg_count++;
/* Last segment in frame */
if (desc_iter->RDES0.LastDescriptor) {
@ -527,8 +543,8 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t
/* there's at least one frame to process */
if (frame_count) {
/* check how many frames left to handle */
while ((desc_iter->RDES0.Own != EMAC_DMADESC_OWNER_DMA) && (iter < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
iter++;
while ((desc_iter->RDES0.Own != EMAC_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
used_descs++;
if (desc_iter->RDES0.LastDescriptor) {
frame_count++;
}
@ -536,7 +552,8 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
}
desc_iter = first_desc;
for (iter = 0; iter < seg_count - 1; iter++) {
for (int i = 0; i < seg_count - 1; i++) {
used_descs--;
write_len = copy_len < CONFIG_ETH_DMA_BUFFER_SIZE ? copy_len : CONFIG_ETH_DMA_BUFFER_SIZE;
/* copy data to buffer */
memcpy(buf, (void *)(desc_iter->Buffer1Addr), write_len);
@ -553,8 +570,10 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t
/* poll rx demand */
hal->dma_regs->dmarxpolldemand = 0;
frame_count--;
used_descs--;
}
*frames_remain = frame_count;
*free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs;
return ret_len;
}

View File

@ -366,6 +366,11 @@ void emac_hal_set_duplex(emac_hal_context_t *hal, uint32_t duplex);
void emac_hal_set_promiscuous(emac_hal_context_t *hal, bool enable);
/**
* @brief Send MAC-CTRL frames to peer (EtherType=0x8808, opcode=0x0001, dest_addr=MAC-specific-ctrl-proto-01 (01:80:c2:00:00:01))
*/
void emac_hal_send_pause_frame(emac_hal_context_t *hal, bool enable);
bool emac_hal_is_mii_busy(emac_hal_context_t *hal);
void emac_hal_set_phy_cmd(emac_hal_context_t *hal, uint32_t phy_addr, uint32_t phy_reg, bool write);
@ -384,7 +389,9 @@ uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal);
uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length);
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain);
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc);
void emac_hal_enable_flow_ctrl(emac_hal_context_t *hal, bool enable);
void emac_hal_isr(void *arg);

View File

@ -340,6 +340,17 @@ The following functions should only be invoked after the Ethernet driver has bee
esp_eth_ioctl(eth_handle, ETH_CMD_G_PHY_ADDR, &phy_addr);
ESP_LOGI(TAG, "Ethernet PHY Address: %d", phy_addr);
.. _flow-control:
Flow control
------------
Ethernet on MCU usually has a limitation in the number of frames it can handle during network congestion, because of the limitation in RAM size. A sending station might be transmitting data faster than the peer end can accept it. Ethernet flow control mechanism allows the receiving node to signal the sender requesting suspension of transmissions until the receiver catches up. The magic behind that is the pause frame, which was defined in IEEE 802.3x.
Pause frame is a special Ethernet frame used to carry the pause command, whose EtherType field is 0x8808, with the Control opcode set to 0x0001. Only stations configured for full-duplex operation may send pause frames. When a station wishes to pause the other end of a link, it sends a pause frame to the 48-bit reserved multicast address of 01-80-C2-00-00-01. The pause frame also includes the period of pause time being requested, in the form of a two-byte integer, ranging from 0 to 65535.
After Ethernet driver installation, the flow control feature is disabled by default. You can enable it by invoking `esp_eth_ioctl(eth_handle, ETH_CMD_S_FLOW_CTRL, true);`. One thing should be kept in mind, is that the pause frame ability will be advertised to peer end by PHY during auto negotiation. Ethernet driver sends pause frame only when both sides of the link support it.
.. -------------------------------- Examples -----------------------------------
Application Example