Merge branch 'bugfix/ethernet_add_reference_counter_v4.0' into 'release/v4.0'

ethernet: add reference counter v4.0 (v4.0)

See merge request espressif/esp-idf!6780
This commit is contained in:
Angus Gratton
2019-12-04 14:59:45 +08:00
34 changed files with 1437 additions and 877 deletions

View File

@ -15,6 +15,8 @@ endif()
idf_component_register(SRCS "${esp_eth_srcs}"
INCLUDE_DIRS "include"
LDFRAGMENTS "linker.lf"
REQUIRES "esp_event"
PRIV_REQUIRES "tcpip_adapter" "driver" "log")
# uses C11 atomic feature
set_source_files_properties(src/esp_eth.c PROPERTIES COMPILE_FLAGS -std=gnu11)

View File

@ -75,36 +75,6 @@ menu "Ethernet"
endif
endif
config ETH_SMI_MDC_GPIO
int "SMI MDC GPIO number"
default 23
range 0 33
help
Set the GPIO number used by SMI MDC.
config ETH_SMI_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
range 0 33
help
Set the GPIO number used by SMI MDIO.
config ETH_PHY_USE_RST
bool "Use Reset Pin of PHY Chip"
default y
help
Set this option to true if you want to control PHY chip's reset using a GPIO.
Check the schematic of you board to make sure if it's necessary to use this feature.
if ETH_PHY_USE_RST
config ETH_PHY_RST_GPIO
int "PHY RST GPIO number"
default 5
range 0 33
help
Set the GPIO number used by the PHY chip's RST pin.
endif
config ETH_DMA_BUFFER_SIZE
int "Ethernet DMA buffer size (Byte)"
range 256 1600
@ -136,21 +106,12 @@ menu "Ethernet"
ESP-IDF can also support some SPI-Ethernet module.
if ETH_USE_SPI_ETHERNET
menuconfig ETH_SPI_ETHERNET_DM9051
config ETH_SPI_ETHERNET_DM9051
bool "Use DM9051"
default y
help
DM9051 is a fast Ethernet controller with an SPI interface.
It's also integrated with a 10/100M PHY and MAC.
Set true to enable DM9051 driver.
if ETH_SPI_ETHERNET_DM9051
config ETH_DM9051_INT_GPIO
int "DM9051 Interrupt GPIO number"
default 4
range 0 33
help
Set the GPIO number used by DM9051's Interrupt pin.
endif
endif
endmenu

View File

@ -3,7 +3,6 @@
#
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src
COMPONENT_ADD_LDFRAGMENTS += linker.lf
ifndef CONFIG_ETH_USE_ESP32_EMAC
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_esp32.o
@ -12,3 +11,6 @@ endif
ifndef CONFIG_ETH_SPI_ETHERNET_DM9051
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_dm9051.o src/esp_eth_phy_dm9051.o
endif
# uses C11 atomic feature
src/esp_eth.o: CFLAGS += -std=gnu11

View File

@ -117,16 +117,49 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
/**
* @brief Uninstall Ethernet driver
* @note It's not recommended to uninstall Ethernet driver unless it won't get used any more in application code.
* To uninstall Ethernet driver, you have to make sure, all references to the driver are released.
* Ethernet driver can only be uninstalled successfully when reference counter equals to one.
*
* @param[in] hdl: handle of Ethernet driver
*
* @return
* - ESP_OK: uninstall esp_eth driver successfully
* - ESP_ERR_INVALID_ARG: uninstall esp_eth driver failed because of some invalid argument
* - ESP_ERR_INVALID_STATE: uninstall esp_eth driver failed because it has more than one reference
* - ESP_FAIL: uninstall esp_eth driver failed because some other error occurred
*/
esp_err_t esp_eth_driver_uninstall(esp_eth_handle_t hdl);
/**
* @brief Start Ethernet driver
*
* @note This API will start driver state machine and internal software timer (for checking link status).
*
* @param[in] hdl handle of Ethernet driver
*
* @return
* - ESP_OK: start esp_eth driver successfully
* - ESP_ERR_INVALID_ARG: start esp_eth driver failed because of some invalid argument
* - ESP_ERR_INVALID_STATE: start esp_eth driver failed because driver has started already
* - ESP_FAIL: start esp_eth driver failed because some other error occurred
*/
esp_err_t esp_eth_start(esp_eth_handle_t hdl);
/**
* @brief Stop Ethernet driver
*
* @note This function does the oppsite operation of `esp_eth_start`.
*
* @param[in] hdl handle of Ethernet driver
* @return
* - ESP_OK: stop esp_eth driver successfully
* - ESP_ERR_INVALID_ARG: stop esp_eth driver failed because of some invalid argument
* - ESP_ERR_INVALID_STATE: stop esp_eth driver failed because driver has not started yet
* - ESP_FAIL: stop esp_eth driver failed because some other error occurred
*/
esp_err_t esp_eth_stop(esp_eth_handle_t hdl);
/**
* @brief General Transmit
*
@ -169,6 +202,31 @@ esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length);
*/
esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data);
/**
* @brief Increase Ethernet driver reference
* @note Ethernet driver handle can be obtained by os timer, netif, etc.
* It's dangerous when thread A is using Ethernet but thread B uninstall the driver.
* Using reference counter can prevent such risk, but care should be taken, when you obtain Ethernet driver,
* this API must be invoked so that the driver won't be uninstalled during your using time.
*
*
* @param[in] hdl: handle of Ethernet driver
* @return
* - ESP_OK: increase reference successfully
* - ESP_ERR_INVALID_ARG: increase reference failed because of some invalid argument
*/
esp_err_t esp_eth_increase_reference(esp_eth_handle_t hdl);
/**
* @brief Decrease Ethernet driver reference
*
* @param[in] hdl: handle of Ethernet driver
* @return
* - ESP_OK: increase reference successfully
* - ESP_ERR_INVALID_ARG: increase reference failed because of some invalid argument
*/
esp_err_t esp_eth_decrease_reference(esp_eth_handle_t hdl);
#ifdef __cplusplus
}
#endif

View File

@ -21,7 +21,6 @@ extern "C" {
#include "esp_eth_com.h"
#include "sdkconfig.h"
#if CONFIG_ETH_USE_SPI_ETHERNET
#include "driver/gpio.h"
#include "driver/spi_master.h"
#endif
@ -247,6 +246,8 @@ typedef struct {
uint32_t sw_reset_timeout_ms; /*!< Software reset timeout value (Unit: ms) */
uint32_t rx_task_stack_size; /*!< Stack size of the receive task */
uint32_t rx_task_prio; /*!< Priority of the receive task */
int smi_mdc_gpio_num; /*!< SMI MDC GPIO number */
int smi_mdio_gpio_num; /*!< SMI MDIO GPIO number */
} eth_mac_config_t;
/**
@ -258,6 +259,8 @@ typedef struct {
.sw_reset_timeout_ms = 100, \
.rx_task_stack_size = 4096, \
.rx_task_prio = 15, \
.smi_mdc_gpio_num = 23, \
.smi_mdio_gpio_num = 18, \
}
#if CONFIG_ETH_USE_ESP32_EMAC
@ -280,6 +283,7 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config);
*/
typedef struct {
spi_device_handle_t spi_hdl; /*!< Handle of SPI device driver */
int int_gpio_num; /*!< Interrupt GPIO number */
} eth_dm9051_config_t;
/**
@ -289,6 +293,7 @@ typedef struct {
#define ETH_DM9051_DEFAULT_CONFIG(spi_device) \
{ \
.spi_hdl = spi_device, \
.int_gpio_num = 4, \
}
/**

View File

@ -46,7 +46,7 @@ struct esp_eth_phy_s {
esp_err_t (*set_mediator)(esp_eth_phy_t *phy, esp_eth_mediator_t *mediator);
/**
* @brief Reset Ethernet PHY
* @brief Software Reset Ethernet PHY
*
* @param[in] phy: Ethernet PHY instance
*
@ -57,6 +57,20 @@ struct esp_eth_phy_s {
*/
esp_err_t (*reset)(esp_eth_phy_t *phy);
/**
* @brief Hardware Reset Ethernet PHY
*
* @note Hardware reset is mostly done by pull down and up PHY's nRST pin
*
* @param[in] phy: Ethernet PHY instance
*
* @return
* - ESP_OK: reset Ethernet PHY successfully
* - ESP_FAIL: reset Ethernet PHY failed because some error occurred
*
*/
esp_err_t (*reset_hw)(esp_eth_phy_t *phy);
/**
* @brief Initialize Ethernet PHY
*
@ -165,17 +179,19 @@ typedef struct {
uint32_t phy_addr; /*!< PHY address */
uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */
uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */
int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */
} eth_phy_config_t;
/**
* @brief Default configuration for Ethernet PHY object
*
*/
#define ETH_PHY_DEFAULT_CONFIG() \
{ \
.phy_addr = 1, \
.reset_timeout_ms = 100, \
.autonego_timeout_ms = 4000 \
#define ETH_PHY_DEFAULT_CONFIG() \
{ \
.phy_addr = 1, \
.reset_timeout_ms = 100, \
.autonego_timeout_ms = 4000, \
.reset_gpio_num = 5, \
}
/**

View File

@ -1,12 +0,0 @@
[mapping:esp_eth]
archive: libesp_eth.a
entries:
if ETH_USE_ESP32_EMAC = y:
esp_eth_mac_esp32:emac_hal_tx_complete_cb (noflash_text)
esp_eth_mac_esp32:emac_hal_tx_unavail_cb (noflash_text)
esp_eth_mac_esp32:emac_hal_rx_complete_cb (noflash_text)
esp_eth_mac_esp32:emac_hal_rx_early_cb (noflash_text)
esp_eth_mac_esp32:emac_hal_rx_unavail_cb (noflash_text)
esp_eth_mac_esp32:emac_esp32_isr_handler (noflash_text)
if ETH_SPI_ETHERNET_DM9051 = y:
esp_eth_mac_dm9051:dm9051_isr_handler (noflash_text)

View File

@ -0,0 +1,162 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Registers in DM9051
*
*/
#define DM9051_NCR (0x00) // Network Control Register
#define DM9051_NSR (0x01) // Network Status Register
#define DM9051_TCR (0x02) // Tx Control Register
#define DM9051_TSR1 (0x03) // Tx Status Register I
#define DM9051_TSR2 (0x04) // Tx Status Register II
#define DM9051_RCR (0x05) // Rx Control Register
#define DM9051_RSR (0x06) // Rx Status Register
#define DM9051_ROCR (0x07) // Receive Overflow Counter Register
#define DM9051_BPTR (0x08) // Back Pressure Threshold Register
#define DM9051_FCTR (0x09) // Flow Control Threshold Register
#define DM9051_FCR (0x0A) // Rx/Tx Flow Control Register
#define DM9051_EPCR (0x0B) // EEPROM & PHY Control Register
#define DM9051_EPAR (0x0C) // EEPROM & PHY Address Register
#define DM9051_EPDRL (0x0D) // EEPROM & PHY Data Register Low
#define DM9051_EPDRH (0x0E) // EEPROM & PHY Data Register High
#define DM9051_WCR (0x0F) // Wake Up Control Register
#define DM9051_PAR (0x10) // Physical Address Register
#define DM9051_MAR (0x16) // Multicast Address Hash Table Register
#define DM9051_GPCR (0x1E) // General Purpose Control Register
#define DM9051_GPR (0x1F) // General Purpose Register
#define DM9051_TRPAL (0x22) // Tx Memory Read Pointer Address Low Byte
#define DM9051_TRPAH (0x23) // Tx Memory Read Pointer Address High Byte
#define DM9051_RWPAL (0x24) // Rx Memory Read Pointer Address Low Byte
#define DM9051_RWPAH (0x25) // Rx Memory Read Pointer Address High Byte
#define DM9051_VIDL (0x28) // Vendor ID Low Byte
#define DM9051_VIDH (0x29) // Vendor ID High Byte
#define DM9051_PIDL (0x2A) // Product ID Low Byte
#define DM9051_PIDH (0x2B) // Product ID High Byte
#define DM9051_CHIPR (0x2C) // CHIP Revision
#define DM9051_TCR2 (0x2D) // Transmit Control Register 2
#define DM9051_ATCR (0x30) // Auto-Transmit Control Register
#define DM9051_TCSCR (0x31) // Transmit Check Sum Control Register
#define DM9051_RCSCSR (0x32) // Receive Check Sum Control Status Register
#define DM9051_SBCR (0x38) // SPI Bus Control Register
#define DM9051_INTCR (0x39) // INT Pin Control Register
#define DM9051_PPCSR (0x3D) // Pause Packet Control Status Register
#define DM9051_EEE_IN (0x3E) // IEEE 802.3az Enter Counter Register
#define DM9051_EEE_OUT (0x3F) // IEEE 802.3az Leave Counter Register
#define DM9051_ALNCR (0x4A) // SPI Byte Align Error Counter Register
#define DM9051_RLENCR (0x52) // Rx Packet Length Control Register
#define DM9051_BCASTCR (0x53) // RX Broadcast Control Register
#define DM9051_INTCKCR (0x54) // INT Pin Clock Output Control Register
#define DM9051_MPTRCR (0x55) // Memory Pointer Control Register
#define DM9051_MLEDCR (0x57) // More LED Control Register
#define DM9051_MEMSCR (0x59) // Memory Control Register
#define DM9051_TMEMR (0x5A) // Transmit Memory Size Register
#define DM9051_MBSR (0x5D) // Memory BIST Status Register
#define DM9051_MRCMDX (0x70) // Memory Data Pre-Fetch Read Command Without Address Increment Register
#define DM9051_MRCMDX1 (0x71) // Memory Read Command Without Pre-Fetch and Without Address Increment Register
#define DM9051_MRCMD (0x72) // Memory Data Read Command With Address Increment Register
#define DM9051_SDR_DLY (0x73) // SPI Data Read Delay Counter Register
#define DM9051_MRRL (0x74) // Memory Data Read Address Register Low Byte
#define DM9051_MRRH (0x75) // Memory Data Read Address Register High Byte
#define DM9051_MWCMDX (0x76) // Memory Data Write Command Without Address Increment Register
#define DM9051_MWCMD (0x78) // Memory Data Write Command With Address Increment Register
#define DM9051_MWRL (0x7A) // Memory Data Write Address Register Low Byte
#define DM9051_MWRH (0x7B) // Memory Data Write Address Register High Byte
#define DM9051_TXPLL (0x7C) // TX Packet Length Low Byte Register
#define DM9051_TXPLH (0x7D) // TX Packet Length High Byte Register
#define DM9051_ISR (0x7E) // Interrupt Status Register
#define DM9051_IMR (0x7F) // Interrupt Mask Register
/**
* @brief status and flag of DM9051 specific registers
*
*/
#define DM9051_SPI_RD (0) // Burst Read Command
#define DM9051_SPI_WR (1) // Burst Write Command
#define NCR_WAKEEN (1 << 6) // Enable Wakeup Function
#define NCR_FDX (1 << 3) // Duplex Mode of the Internal PHY
#define NCR_RST (1 << 0) // Software Reset and Auto-Clear after 10us
#define NSR_SPEED (1 << 7) // Speed of Internal PHY
#define NSR_LINKST (1 << 6) // Link Status of Internal PHY
#define NSR_WAKEST (1 << 5) // Wakeup Event Status
#define NSR_TX2END (1 << 3) // TX Packet Index II Complete Status
#define NSR_TX1END (1 << 2) // TX Packet Index I Complete Status
#define NSR_RXOV (1 << 1) // RX Memory Overflow Status
#define NSR_RXRDY (1 << 0) // RX Packet Ready
#define TCR_TXREQ (1 << 0) // TX Request. Auto-Clear after Sending Completely
#define RCR_WTDIS (1 << 6) // Watchdog Timer Disable
#define RCR_DIS_LONG (1 << 5) // Discard Long Packet
#define RCR_DIS_CRC (1 << 4) // Discard CRC Error Packet
#define RCR_ALL (1 << 3) // Receive All Multicast
#define RCR_RUNT (1 << 2) // Receive Runt Packet
#define RCR_PRMSC (1 << 1) // Promiscuous Mode
#define RCR_RXEN (1 << 0) // RX Enable
#define RSR_RF (1 << 7) // Runt Frame
#define RSR_MF (1 << 6) // Multicast Frame
#define RSR_LCS (1 << 5) // Late Collision Seen
#define RSR_RWTO (1 << 4) // Receive Watchdog Time-Out
#define RSR_PLE (1 << 3) // Physical Layer Error
#define RSR_AE (1 << 2) // Alignment Error
#define RSR_CE (1 << 1) // CRC Error
#define RSR_FOE (1 << 0) // RX Memory Overflow Error
#define FCR_FLOW_ENABLE (0x39) // Enable Flow Control
#define EPCR_REEP (1 << 5) // Reload EEPROM
#define EPCR_WEP (1 << 4) // Write EEPROM Enable
#define EPCR_EPOS (1 << 3) // EEPROM or PHY Operation Select
#define EPCR_ERPRR (1 << 2) // EEPROM Read or PHY Register Read Command
#define EPCR_ERPRW (1 << 1) // EEPROM Write or PHY Register Write Command
#define EPCR_ERRE (1 << 0) // EEPROM Access Status or PHY Access Status
#define TCR2_RLCP (1 << 6) // Retry Late Collision Packet
#define ATCR_AUTO_TX (1 << 7) // Auto-Transmit Control
#define TCSCR_UDPCSE (1 << 2) // UDP CheckSum Generation
#define TCSCR_TCPCSE (1 << 1) // TCP CheckSum Generation
#define TCSCR_IPCSE (1 << 0) // IPv4 CheckSum Generation
#define MPTRCR_RST_TX (1 << 1) // Reset TX Memory Pointer
#define MPTRCR_RST_RX (1 << 0) // Reset RX Memory Pointer
#define ISR_LNKCHGS (1 << 5) // Link Status Change
#define ISR_ROO (1 << 3) // Receive Overflow Counter Overflow
#define ISR_ROS (1 << 2) // Receive Overflow
#define ISR_PT (1 << 1) // Packet Transmitted
#define ISR_PR (1 << 0) // Packet Received
#define ISR_CLR_STATUS (ISR_LNKCHGS | ISR_ROO | ISR_ROS | ISR_PT | ISR_PR)
#define IMR_PAR (1 << 7) // Pointer Auto-Return Mode
#define IMR_LNKCHGI (1 << 5) // Enable Link Status Change Interrupt
#define IMR_ROOI (1 << 3) // Enable Receive Overflow Counter Overflow Interrupt
#define IMR_ROI (1 << 2) // Enable Receive Overflow Interrupt
#define IMR_PTI (1 << 1) // Enable Packet Transmitted Interrupt
#define IMR_PRI (1 << 0) // Enable Packet Received Interrupt
#define IMR_ALL (IMR_PAR | IMR_LNKCHGI | IMR_ROOI | IMR_ROI | IMR_PTI | IMR_PRI)
#ifdef __cplusplus
}
#endif

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sys/cdefs.h>
#include <stdatomic.h>
#include "esp_log.h"
#include "esp_eth.h"
#include "esp_event.h"
@ -33,6 +34,8 @@ static const char *TAG = "esp_eth";
ESP_EVENT_DEFINE_BASE(ETH_EVENT);
#define ESP_ETH_FLAGS_STARTED (1<<0)
/**
* @brief The Ethernet driver mainly consists of PHY, MAC and
* the mediator who will handle the request/response from/to MAC, PHY and Users.
@ -51,6 +54,8 @@ typedef struct {
eth_speed_t speed;
eth_duplex_t duplex;
eth_link_t link;
atomic_int ref_count;
uint32_t flags;
esp_err_t (*stack_input)(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length);
esp_err_t (*on_lowlevel_init_done)(esp_eth_handle_t eth_handle);
esp_err_t (*on_lowlevel_deinit_done)(esp_eth_handle_t eth_handle);
@ -145,7 +150,9 @@ static void eth_check_link_timer_cb(TimerHandle_t xTimer)
{
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)pvTimerGetTimerID(xTimer);
esp_eth_phy_t *phy = eth_driver->phy;
esp_eth_increase_reference(eth_driver);
phy->get_link(phy);
esp_eth_decrease_reference(eth_driver);
}
////////////////////////////////User face APIs////////////////////////////////////////////////
@ -164,6 +171,7 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
ETH_CHECK(mac && phy, "can't set eth->mac or eth->phy to null", err, ESP_ERR_INVALID_ARG);
esp_eth_driver_t *eth_driver = calloc(1, sizeof(esp_eth_driver_t));
ETH_CHECK(eth_driver, "request memory for eth_driver failed", err, ESP_ERR_NO_MEM);
atomic_init(&eth_driver->ref_count, 1);
eth_driver->mac = mac;
eth_driver->phy = phy;
eth_driver->link = ETH_LINK_DOWN;
@ -176,6 +184,8 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
eth_driver->mediator.phy_reg_write = eth_phy_reg_write;
eth_driver->mediator.stack_input = eth_stack_input;
eth_driver->mediator.on_state_changed = eth_on_state_changed;
/* some PHY can't output RMII clock if in reset state, so hardware reset PHY chip firstly */
phy->reset_hw(phy);
ETH_CHECK(mac->set_mediator(mac, &eth_driver->mediator) == ESP_OK, "set mediator for mac failed", err_mediator, ESP_FAIL);
ETH_CHECK(phy->set_mediator(phy, &eth_driver->mediator) == ESP_OK, "set mediator for phy failed", err_mediator, ESP_FAIL);
ETH_CHECK(mac->init(mac) == ESP_OK, "init mac failed", err_init_mac, ESP_FAIL);
@ -183,15 +193,8 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
eth_driver->check_link_timer = xTimerCreate("eth_link_timer", pdMS_TO_TICKS(config->check_link_period_ms), pdTRUE,
eth_driver, eth_check_link_timer_cb);
ETH_CHECK(eth_driver->check_link_timer, "create eth_link_timer failed", err_create_timer, ESP_FAIL);
ETH_CHECK(xTimerStart(eth_driver->check_link_timer, 0) == pdPASS, "start eth_link_timer failed", err_start_timer, ESP_FAIL);
ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
"send ETHERNET_EVENT_START event failed", err_event, ESP_FAIL);
*out_hdl = (esp_eth_handle_t)eth_driver;
return ESP_OK;
err_event:
xTimerStop(eth_driver->check_link_timer, 0);
err_start_timer:
xTimerDelete(eth_driver->check_link_timer, 0);
err_create_timer:
phy->deinit(phy);
err_init_phy:
@ -208,11 +211,12 @@ esp_err_t esp_eth_driver_uninstall(esp_eth_handle_t hdl)
esp_err_t ret = ESP_OK;
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
// don't uninstall driver unless there's only one reference
ETH_CHECK(atomic_load(&eth_driver->ref_count) == 1,
"more than one reference to ethernet driver", err, ESP_ERR_INVALID_STATE);
esp_eth_mac_t *mac = eth_driver->mac;
esp_eth_phy_t *phy = eth_driver->phy;
ETH_CHECK(xTimerDelete(eth_driver->check_link_timer, 0) == pdPASS, "delete eth_link_timer failed", err, ESP_FAIL);
ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
"send ETHERNET_EVENT_STOP event failed", err, ESP_FAIL);
ETH_CHECK(phy->deinit(phy) == ESP_OK, "deinit phy failed", err, ESP_FAIL);
ETH_CHECK(mac->deinit(mac) == ESP_OK, "deinit mac failed", err, ESP_FAIL);
free(eth_driver);
@ -221,6 +225,52 @@ err:
return ret;
}
esp_err_t esp_eth_start(esp_eth_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
// check if driver has started
if (eth_driver->flags & ESP_ETH_FLAGS_STARTED) {
ESP_LOGW(TAG, "driver started already");
ret = ESP_ERR_INVALID_STATE;
goto err;
}
eth_driver->flags |= ESP_ETH_FLAGS_STARTED;
ETH_CHECK(eth_driver->phy->reset(eth_driver->phy) == ESP_OK, "reset phy failed", err, ESP_FAIL);
ETH_CHECK(xTimerStart(eth_driver->check_link_timer, 0) == pdPASS,
"start eth_link_timer failed", err, ESP_FAIL);
ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
"send ETHERNET_EVENT_START event failed", err_event, ESP_FAIL);
return ESP_OK;
err_event:
xTimerStop(eth_driver->check_link_timer, 0);
err:
return ret;
}
esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
// check if driver has started
if (eth_driver->flags & ESP_ETH_FLAGS_STARTED) {
eth_driver->flags &= ~ESP_ETH_FLAGS_STARTED;
ETH_CHECK(xTimerStop(eth_driver->check_link_timer, 0) == pdPASS,
"stop eth_link_timer failed", err, ESP_FAIL);
ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
"send ETHERNET_EVENT_STOP event failed", err, ESP_FAIL);
} else {
ESP_LOGW(TAG, "driver not started yet");
ret = ESP_ERR_INVALID_STATE;
goto err;
}
return ESP_OK;
err:
return ret;
}
esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, uint8_t *buf, uint32_t length)
{
esp_err_t ret = ESP_OK;
@ -282,3 +332,25 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
err:
return ret;
}
esp_err_t esp_eth_increase_reference(esp_eth_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
atomic_fetch_add(&eth_driver->ref_count, 1);
return ESP_OK;
err:
return ret;
}
esp_err_t esp_eth_decrease_reference(esp_eth_handle_t hdl)
{
esp_err_t ret = ESP_OK;
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
atomic_fetch_sub(&eth_driver->ref_count, 1);
return ESP_OK;
err:
return ret;
}

View File

@ -16,6 +16,7 @@
#include <sys/cdefs.h>
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "esp_system.h"
@ -24,6 +25,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "dm9051.h"
#include "sdkconfig.h"
static const char *TAG = "emac_dm9051";
@ -38,149 +40,9 @@ static const char *TAG = "emac_dm9051";
} \
} while (0)
#define RX_QUEUE_WAIT_MS (100)
#define DM9051_SPI_LOCK_TIMEOUT_MS (50)
#define DM9051_PHY_OPERATION_TIMEOUT_US (1000)
/**
* @brief Registers in DM9051
*
*/
#define DM9051_NCR (0x00) // Network Control Register
#define DM9051_NSR (0x01) // Network Status Register
#define DM9051_TCR (0x02) // Tx Control Register
#define DM9051_TSR1 (0x03) // Tx Status Register I
#define DM9051_TSR2 (0x04) // Tx Status Register II
#define DM9051_RCR (0x05) // Rx Control Register
#define DM9051_RSR (0x06) // Rx Status Register
#define DM9051_ROCR (0x07) // Receive Overflow Counter Register
#define DM9051_BPTR (0x08) // Back Pressure Threshold Register
#define DM9051_FCTR (0x09) // Flow Control Threshold Register
#define DM9051_FCR (0x0A) // Rx/Tx Flow Control Register
#define DM9051_EPCR (0x0B) // EEPROM & PHY Control Register
#define DM9051_EPAR (0x0C) // EEPROM & PHY Address Register
#define DM9051_EPDRL (0x0D) // EEPROM & PHY Data Register Low
#define DM9051_EPDRH (0x0E) // EEPROM & PHY Data Register High
#define DM9051_WCR (0x0F) // Wake Up Control Register
#define DM9051_PAR (0x10) // Physical Address Register
#define DM9051_MAR (0x16) // Multicast Address Hash Table Register
#define DM9051_GPCR (0x1E) // General Purpose Control Register
#define DM9051_GPR (0x1F) // General Purpose Register
#define DM9051_TRPAL (0x22) // Tx Memory Read Pointer Address Low Byte
#define DM9051_TRPAH (0x23) // Tx Memory Read Pointer Address High Byte
#define DM9051_RWPAL (0x24) // Rx Memory Read Pointer Address Low Byte
#define DM9051_RWPAH (0x25) // Rx Memory Read Pointer Address High Byte
#define DM9051_VIDL (0x28) // Vendor ID Low Byte
#define DM9051_VIDH (0x29) // Vendor ID High Byte
#define DM9051_PIDL (0x2A) // Product ID Low Byte
#define DM9051_PIDH (0x2B) // Product ID High Byte
#define DM9051_CHIPR (0x2C) // CHIP Revision
#define DM9051_TCR2 (0x2D) // Transmit Control Register 2
#define DM9051_ATCR (0x30) // Auto-Transmit Control Register
#define DM9051_TCSCR (0x31) // Transmit Check Sum Control Register
#define DM9051_RCSCSR (0x32) // Receive Check Sum Control Status Register
#define DM9051_SBCR (0x38) // SPI Bus Control Register
#define DM9051_INTCR (0x39) // INT Pin Control Register
#define DM9051_PPCSR (0x3D) // Pause Packet Control Status Register
#define DM9051_EEE_IN (0x3E) // IEEE 802.3az Enter Counter Register
#define DM9051_EEE_OUT (0x3F) // IEEE 802.3az Leave Counter Register
#define DM9051_ALNCR (0x4A) // SPI Byte Align Error Counter Register
#define DM9051_RLENCR (0x52) // Rx Packet Length Control Register
#define DM9051_BCASTCR (0x53) // RX Broadcast Control Register
#define DM9051_INTCKCR (0x54) // INT Pin Clock Output Control Register
#define DM9051_MPTRCR (0x55) // Memory Pointer Control Register
#define DM9051_MLEDCR (0x57) // More LED Control Register
#define DM9051_MEMSCR (0x59) // Memory Control Register
#define DM9051_TMEMR (0x5A) // Transmit Memory Size Register
#define DM9051_MBSR (0x5D) // Memory BIST Status Register
#define DM9051_MRCMDX (0x70) // Memory Data Pre-Fetch Read Command Without Address Increment Register
#define DM9051_MRCMDX1 (0x71) // Memory Read Command Without Pre-Fetch and Without Address Increment Register
#define DM9051_MRCMD (0x72) // Memory Data Read Command With Address Increment Register
#define DM9051_SDR_DLY (0x73) // SPI Data Read Delay Counter Register
#define DM9051_MRRL (0x74) // Memory Data Read Address Register Low Byte
#define DM9051_MRRH (0x75) // Memory Data Read Address Register High Byte
#define DM9051_MWCMDX (0x76) // Memory Data Write Command Without Address Increment Register
#define DM9051_MWCMD (0x78) // Memory Data Write Command With Address Increment Register
#define DM9051_MWRL (0x7A) // Memory Data Write Address Register Low Byte
#define DM9051_MWRH (0x7B) // Memory Data Write Address Register High Byte
#define DM9051_TXPLL (0x7C) // TX Packet Length Low Byte Register
#define DM9051_TXPLH (0x7D) // TX Packet Length High Byte Register
#define DM9051_ISR (0x7E) // Interrupt Status Register
#define DM9051_IMR (0x7F) // Interrupt Mask Register
/**
* @brief status and flag of DM9051 specific registers
*
*/
#define DM9051_SPI_RD (0) // Burst Read Command
#define DM9051_SPI_WR (1) // Burst Write Command
#define NCR_WAKEEN (1 << 6) // Enable Wakeup Function
#define NCR_FDX (1 << 3) // Duplex Mode of the Internal PHY
#define NCR_RST (1 << 0) // Software Reset and Auto-Clear after 10us
#define NSR_SPEED (1 << 7) // Speed of Internal PHY
#define NSR_LINKST (1 << 6) // Link Status of Internal PHY
#define NSR_WAKEST (1 << 5) // Wakeup Event Status
#define NSR_TX2END (1 << 3) // TX Packet Index II Complete Status
#define NSR_TX1END (1 << 2) // TX Packet Index I Complete Status
#define NSR_RXOV (1 << 1) // RX Memory Overflow Status
#define NSR_RXRDY (1 << 0) // RX Packet Ready
#define TCR_TXREQ (1 << 0) // TX Request. Auto-Clear after Sending Completely
#define RCR_WTDIS (1 << 6) // Watchdog Timer Disable
#define RCR_DIS_LONG (1 << 5) // Discard Long Packet
#define RCR_DIS_CRC (1 << 4) // Discard CRC Error Packet
#define RCR_ALL (1 << 3) // Receive All Multicast
#define RCR_RUNT (1 << 2) // Receive Runt Packet
#define RCR_PRMSC (1 << 1) // Promiscuous Mode
#define RCR_RXEN (1 << 0) // RX Enable
#define RSR_RF (1 << 7) // Runt Frame
#define RSR_MF (1 << 6) // Multicast Frame
#define RSR_LCS (1 << 5) // Late Collision Seen
#define RSR_RWTO (1 << 4) // Receive Watchdog Time-Out
#define RSR_PLE (1 << 3) // Physical Layer Error
#define RSR_AE (1 << 2) // Alignment Error
#define RSR_CE (1 << 1) // CRC Error
#define RSR_FOE (1 << 0) // RX Memory Overflow Error
#define FCR_FLOW_ENABLE (0x39) // Enable Flow Control
#define EPCR_REEP (1 << 5) // Reload EEPROM
#define EPCR_WEP (1 << 4) // Write EEPROM Enable
#define EPCR_EPOS (1 << 3) // EEPROM or PHY Operation Select
#define EPCR_ERPRR (1 << 2) // EEPROM Read or PHY Register Read Command
#define EPCR_ERPRW (1 << 1) // EEPROM Write or PHY Register Write Command
#define EPCR_ERRE (1 << 0) // EEPROM Access Status or PHY Access Status
#define TCR2_RLCP (1 << 6) // Retry Late Collision Packet
#define ATCR_AUTO_TX (1 << 7) // Auto-Transmit Control
#define TCSCR_UDPCSE (1 << 2) // UDP CheckSum Generation
#define TCSCR_TCPCSE (1 << 1) // TCP CheckSum Generation
#define TCSCR_IPCSE (1 << 0) // IPv4 CheckSum Generation
#define MPTRCR_RST_TX (1 << 1) // Reset TX Memory Pointer
#define MPTRCR_RST_RX (1 << 0) // Reset RX Memory Pointer
#define ISR_LNKCHGS (1 << 5) // Link Status Change
#define ISR_ROO (1 << 3) // Receive Overflow Counter Overflow
#define ISR_ROS (1 << 2) // Receive Overflow
#define ISR_PT (1 << 1) // Packet Transmitted
#define ISR_PR (1 << 0) // Packet Received
#define ISR_CLR_STATUS (ISR_LNKCHGS | ISR_ROO | ISR_ROS | ISR_PT | ISR_PR)
#define IMR_PAR (1 << 7) // Pointer Auto-Return Mode
#define IMR_LNKCHGI (1 << 5) // Enable Link Status Change Interrupt
#define IMR_ROOI (1 << 3) // Enable Receive Overflow Counter Overflow Interrupt
#define IMR_ROI (1 << 2) // Enable Receive Overflow Interrupt
#define IMR_PTI (1 << 1) // Enable Packet Transmitted Interrupt
#define IMR_PRI (1 << 0) // Enable Packet Received Interrupt
#define IMR_ALL (IMR_PAR | IMR_LNKCHGI | IMR_ROOI | IMR_ROI | IMR_PTI | IMR_PRI)
typedef struct {
uint8_t flag;
uint8_t status;
@ -195,6 +57,7 @@ typedef struct {
SemaphoreHandle_t spi_lock;
TaskHandle_t rx_task_hdl;
uint32_t sw_reset_timeout_ms;
int int_gpio_num;
uint8_t addr[6];
bool packets_remain;
} emac_dm9051_t;
@ -308,6 +171,30 @@ static esp_err_t dm9051_memory_read(emac_dm9051_t *emac, uint8_t *buffer, uint32
return ret;
}
/**
* @brief peek buffer from dm9051 internal memory (without internal cursor moved)
*/
static esp_err_t dm9051_memory_peek(emac_dm9051_t *emac, uint8_t *buffer, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = DM9051_SPI_RD,
.addr = DM9051_MRCMDX1,
.length = len * 8,
.rx_buffer = buffer
};
if (dm9051_lock(emac)) {
if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
dm9051_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
/**
* @brief read mac address from internal registers
*/
@ -390,10 +277,10 @@ static esp_err_t dm9051_verify_id(emac_dm9051_t *emac)
uint8_t id[2];
MAC_CHECK(dm9051_register_read(emac, DM9051_VIDL, &id[0]) == ESP_OK, "read VIDL failed", err, ESP_FAIL);
MAC_CHECK(dm9051_register_read(emac, DM9051_VIDH, &id[1]) == ESP_OK, "read VIDH failed", err, ESP_FAIL);
MAC_CHECK(0x0A46 == *(uint16_t *)id, "wrong Vendor ID", err, ESP_ERR_INVALID_VERSION);
MAC_CHECK(0x0A == id[1] && 0x46 == id[0], "wrong Vendor ID", err, ESP_ERR_INVALID_VERSION);
MAC_CHECK(dm9051_register_read(emac, DM9051_PIDL, &id[0]) == ESP_OK, "read PIDL failed", err, ESP_FAIL);
MAC_CHECK(dm9051_register_read(emac, DM9051_PIDH, &id[1]) == ESP_OK, "read PIDH failed", err, ESP_FAIL);
MAC_CHECK(0x9051 == *(uint16_t *)id, "wrong Product ID", err, ESP_ERR_INVALID_VERSION);
MAC_CHECK(0x90 == id[1] && 0x51 == id[0], "wrong Product ID", err, ESP_ERR_INVALID_VERSION);
return ESP_OK;
err:
return ret;
@ -483,7 +370,7 @@ err:
return ret;
}
static void dm9051_isr_handler(void *arg)
IRAM_ATTR static void dm9051_isr_handler(void *arg)
{
emac_dm9051_t *emac = (emac_dm9051_t *)arg;
BaseType_t high_task_wakeup = pdFALSE;
@ -501,26 +388,27 @@ static void emac_dm9051_task(void *arg)
uint8_t *buffer = NULL;
uint32_t length = 0;
while (1) {
if (ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(RX_QUEUE_WAIT_MS))) {
/* clear interrupt status */
dm9051_register_read(emac, DM9051_ISR, &status);
dm9051_register_write(emac, DM9051_ISR, status);
/* packet received */
if (status & ISR_PR) {
do {
buffer = (uint8_t *)heap_caps_malloc(ETH_MAX_PACKET_SIZE, MALLOC_CAP_DMA);
if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
/* pass the buffer to stack (e.g. TCP/IP layer) */
if (length) {
emac->eth->stack_input(emac->eth, buffer, length);
} else {
free(buffer);
}
// block indefinitely until some task notifies me
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
/* clear interrupt status */
dm9051_register_read(emac, DM9051_ISR, &status);
dm9051_register_write(emac, DM9051_ISR, status);
/* packet received */
if (status & ISR_PR) {
do {
length = ETH_MAX_PACKET_SIZE;
buffer = (uint8_t *)heap_caps_malloc(length, MALLOC_CAP_DMA);
if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) {
/* pass the buffer to stack (e.g. TCP/IP layer) */
if (length) {
emac->eth->stack_input(emac->eth, buffer, length);
} else {
free(buffer);
}
} while (emac->packets_remain);
}
} else {
free(buffer);
}
} while (emac->packets_remain);
}
}
vTaskDelete(NULL);
@ -744,13 +632,26 @@ static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
if (rxbyte > 1) {
MAC_CHECK(dm9051_stop(emac) == ESP_OK, "stop dm9051 failed", err, ESP_FAIL);
/* reset rx fifo pointer */
MAC_CHECK(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX) == ESP_OK, "write MPTRCR failed", err, ESP_FAIL);
MAC_CHECK(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX) == ESP_OK,
"write MPTRCR failed", err, ESP_FAIL);
ets_delay_us(10);
MAC_CHECK(dm9051_start(emac) == ESP_OK, "start dm9051 failed", err, ESP_FAIL);
MAC_CHECK(false, "reset rx fifo pointer", err, ESP_FAIL);
} else if (rxbyte) {
MAC_CHECK(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK, "read rx header failed", err, ESP_FAIL);
MAC_CHECK(dm9051_memory_peek(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK,
"peek rx header failed", err, ESP_FAIL);
rx_len = header.length_low + (header.length_high << 8);
/* check if the buffer can hold all the incoming data */
if (*length < rx_len - 4) {
ESP_LOGE(TAG, "buffer size too small");
/* tell upper layer the size we need */
*length = rx_len - 4;
ret = ESP_ERR_INVALID_SIZE;
goto err;
}
MAC_CHECK(*length >= rx_len - 4, "buffer size too small", err, ESP_ERR_INVALID_SIZE);
MAC_CHECK(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK,
"read rx header failed", err, ESP_FAIL);
MAC_CHECK(dm9051_memory_read(emac, buf, rx_len) == ESP_OK, "read rx data failed", err, ESP_FAIL);
MAC_CHECK(!(header.status & 0xBF), "receive status error: %xH", err, ESP_FAIL, header.status);
*length = rx_len - 4; // substract the CRC length (4Bytes)
@ -769,13 +670,12 @@ static esp_err_t emac_dm9051_init(esp_eth_mac_t *mac)
esp_err_t ret = ESP_OK;
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
esp_eth_mediator_t *eth = emac->eth;
/* init gpio used by spi-ethernet interrupt */
gpio_pad_select_gpio(CONFIG_ETH_DM9051_INT_GPIO);
gpio_set_direction(CONFIG_ETH_DM9051_INT_GPIO, GPIO_MODE_INPUT);
gpio_set_pull_mode(CONFIG_ETH_DM9051_INT_GPIO, GPIO_PULLDOWN_ONLY);
gpio_set_intr_type(CONFIG_ETH_DM9051_INT_GPIO, GPIO_INTR_POSEDGE);
gpio_intr_enable(CONFIG_ETH_DM9051_INT_GPIO);
gpio_isr_handler_add(CONFIG_ETH_DM9051_INT_GPIO, dm9051_isr_handler, emac);
gpio_pad_select_gpio(emac->int_gpio_num);
gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT);
gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLDOWN_ONLY);
gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_POSEDGE);
gpio_intr_enable(emac->int_gpio_num);
gpio_isr_handler_add(emac->int_gpio_num, dm9051_isr_handler, emac);
MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, "lowlevel init failed", err, ESP_FAIL);
/* reset dm9051 */
MAC_CHECK(dm9051_reset(emac) == ESP_OK, "reset dm9051 failed", err, ESP_FAIL);
@ -789,8 +689,8 @@ static esp_err_t emac_dm9051_init(esp_eth_mac_t *mac)
MAC_CHECK(dm9051_get_mac_addr(emac) == ESP_OK, "fetch ethernet mac address failed", err, ESP_FAIL);
return ESP_OK;
err:
gpio_isr_handler_remove(CONFIG_ETH_DM9051_INT_GPIO);
gpio_reset_pin(CONFIG_ETH_DM9051_INT_GPIO);
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ret;
}
@ -800,8 +700,8 @@ static esp_err_t emac_dm9051_deinit(esp_eth_mac_t *mac)
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
esp_eth_mediator_t *eth = emac->eth;
dm9051_stop(emac);
gpio_isr_handler_remove(CONFIG_ETH_DM9051_INT_GPIO);
gpio_reset_pin(CONFIG_ETH_DM9051_INT_GPIO);
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ESP_OK;
}
@ -818,12 +718,16 @@ static esp_err_t emac_dm9051_del(esp_eth_mac_t *mac)
esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, const eth_mac_config_t *mac_config)
{
esp_eth_mac_t *ret = NULL;
emac_dm9051_t *emac = NULL;
MAC_CHECK(dm9051_config, "can't set dm9051 specific config to null", err, NULL);
MAC_CHECK(mac_config, "can't set mac config to null", err, NULL);
emac_dm9051_t *emac = calloc(1, sizeof(emac_dm9051_t));
emac = calloc(1, sizeof(emac_dm9051_t));
MAC_CHECK(emac, "calloc emac failed", err, NULL);
/* dm9051 receive is driven by interrupt only for now*/
MAC_CHECK(dm9051_config->int_gpio_num >= 0, "error interrupt gpio number", err, NULL);
/* bind methods and attributes */
emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms;
emac->int_gpio_num = dm9051_config->int_gpio_num;
emac->spi_hdl = dm9051_config->spi_hdl;
emac->parent.set_mediator = emac_dm9051_set_mediator;
emac->parent.init = emac_dm9051_init;
@ -841,16 +745,22 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config,
emac->parent.receive = emac_dm9051_receive;
/* create mutex */
emac->spi_lock = xSemaphoreCreateMutex();
MAC_CHECK(emac->spi_lock, "create lock failed", err_lock, NULL);
MAC_CHECK(emac->spi_lock, "create lock failed", err, NULL);
/* create dm9051 task */
BaseType_t xReturned = xTaskCreate(emac_dm9051_task, "dm9051_tsk", mac_config->rx_task_stack_size, emac,
mac_config->rx_task_prio, &emac->rx_task_hdl);
MAC_CHECK(xReturned == pdPASS, "create dm9051 task failed", err_tsk, NULL);
MAC_CHECK(xReturned == pdPASS, "create dm9051 task failed", err, NULL);
return &(emac->parent);
err_tsk:
vSemaphoreDelete(emac->spi_lock);
err_lock:
free(emac);
err:
if (emac) {
if (emac->rx_task_hdl) {
vTaskDelete(emac->rx_task_hdl);
}
if (emac->spi_lock) {
vSemaphoreDelete(emac->spi_lock);
}
free(emac);
}
return ret;
}

View File

@ -16,8 +16,10 @@
#include <sys/cdefs.h>
#include "driver/periph_ctrl.h"
#include "driver/gpio.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "esp_pm.h"
#include "esp_system.h"
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
@ -40,7 +42,6 @@ static const char *TAG = "emac_esp32";
} \
} while (0)
#define RX_QUEUE_WAIT_MS (20)
#define PHY_OPERATION_TIMEOUT_US (1000)
typedef struct {
@ -51,10 +52,15 @@ typedef struct {
TaskHandle_t rx_task_hdl;
uint32_t sw_reset_timeout_ms;
uint32_t frames_remain;
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;
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
} emac_esp32_t;
static esp_err_t emac_esp32_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
@ -224,7 +230,16 @@ static esp_err_t emac_esp32_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *
esp_err_t ret = ESP_OK;
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);
*length = emac_hal_receive_frame(&emac->hal, buf, &emac->frames_remain);
uint32_t receive_len = emac_hal_receive_frame(&emac->hal, buf, *length, &emac->frames_remain);
/* we need to check the return value in case the buffer size is not enough */
if (*length < receive_len) {
ESP_LOGE(TAG, "buffer size too small");
/* tell upper layer the size we need */
*length = receive_len;
ret = ESP_ERR_INVALID_SIZE;
goto err;
}
*length = receive_len;
return ESP_OK;
err:
return ret;
@ -236,36 +251,37 @@ static void emac_esp32_rx_task(void *arg)
uint8_t *buffer = NULL;
uint32_t length = 0;
while (1) {
if (ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(RX_QUEUE_WAIT_MS))) {
do {
buffer = (uint8_t *)malloc(ETH_MAX_PACKET_SIZE);
if (emac_esp32_receive(&emac->parent, buffer, &length) == ESP_OK) {
/* pass the buffer to stack (e.g. TCP/IP layer) */
if (length) {
emac->eth->stack_input(emac->eth, buffer, length);
} else {
free(buffer);
}
// block indefinitely until some task notifies me
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
do {
length = ETH_MAX_PACKET_SIZE;
buffer = (uint8_t *)malloc(length);
if (emac_esp32_receive(&emac->parent, buffer, &length) == ESP_OK) {
/* pass the buffer to stack (e.g. TCP/IP layer) */
if (length) {
emac->eth->stack_input(emac->eth, buffer, length);
} else {
free(buffer);
}
} while (emac->frames_remain);
}
} else {
free(buffer);
}
} while (emac->frames_remain);
}
vTaskDelete(NULL);
}
static void emac_esp32_init_smi_gpio(void)
static void emac_esp32_init_smi_gpio(emac_esp32_t *emac)
{
/* Setup SMI MDC GPIO */
gpio_set_direction(CONFIG_ETH_SMI_MDC_GPIO, GPIO_MODE_OUTPUT);
gpio_matrix_out(CONFIG_ETH_SMI_MDC_GPIO, EMAC_MDC_O_IDX, false, false);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CONFIG_ETH_SMI_MDC_GPIO], PIN_FUNC_GPIO);
gpio_set_direction(emac->smi_mdc_gpio_num, GPIO_MODE_OUTPUT);
gpio_matrix_out(emac->smi_mdc_gpio_num, EMAC_MDC_O_IDX, false, false);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[emac->smi_mdc_gpio_num], PIN_FUNC_GPIO);
/* Setup SMI MDIO GPIO */
gpio_set_direction(CONFIG_ETH_SMI_MDIO_GPIO, GPIO_MODE_INPUT_OUTPUT);
gpio_matrix_out(CONFIG_ETH_SMI_MDIO_GPIO, EMAC_MDO_O_IDX, false, false);
gpio_matrix_in(CONFIG_ETH_SMI_MDIO_GPIO, EMAC_MDI_I_IDX, false);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CONFIG_ETH_SMI_MDIO_GPIO], PIN_FUNC_GPIO);
gpio_set_direction(emac->smi_mdio_gpio_num, GPIO_MODE_INPUT_OUTPUT);
gpio_matrix_out(emac->smi_mdio_gpio_num, EMAC_MDO_O_IDX, false, false);
gpio_matrix_in(emac->smi_mdio_gpio_num, EMAC_MDI_I_IDX, false);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[emac->smi_mdio_gpio_num], PIN_FUNC_GPIO);
}
static esp_err_t emac_esp32_init(esp_eth_mac_t *mac)
@ -278,12 +294,7 @@ static esp_err_t emac_esp32_init(esp_eth_mac_t *mac)
/* enable clock, config gpio, etc */
emac_hal_lowlevel_init(&emac->hal);
/* init gpio used by gpio */
emac_esp32_init_smi_gpio();
#if CONFIG_ETH_PHY_USE_RST
gpio_pad_select_gpio(CONFIG_ETH_PHY_RST_GPIO);
gpio_set_direction(CONFIG_ETH_PHY_RST_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(CONFIG_ETH_PHY_RST_GPIO, 1);
#endif
emac_esp32_init_smi_gpio(emac);
MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, "lowlevel init failed", err, ESP_FAIL);
/* software reset */
emac_hal_reset(&emac->hal);
@ -307,6 +318,9 @@ static esp_err_t emac_esp32_init(esp_eth_mac_t *mac)
MAC_CHECK(esp_read_mac(emac->addr, ESP_MAC_ETH) == ESP_OK, "fetch ethernet mac address failed", err, ESP_FAIL);
/* set MAC address to emac register */
emac_hal_set_address(&emac->hal, emac->addr);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(emac->pm_lock);
#endif
return ESP_OK;
err:
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
@ -318,8 +332,8 @@ static esp_err_t emac_esp32_deinit(esp_eth_mac_t *mac)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
esp_eth_mediator_t *eth = emac->eth;
#if CONFIG_ETH_PHY_USE_RST
gpio_set_level(CONFIG_ETH_PHY_RST_GPIO, 0);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(emac->pm_lock);
#endif
emac_hal_stop(&emac->hal);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
@ -331,6 +345,11 @@ static esp_err_t emac_esp32_del(esp_eth_mac_t *mac)
{
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
esp_intr_free(emac->intr_hdl);
#ifdef CONFIG_PM_ENABLE
if (emac->pm_lock) {
esp_pm_lock_delete(emac->pm_lock);
}
#endif
vTaskDelete(emac->rx_task_hdl);
int i = 0;
for (i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
@ -344,7 +363,7 @@ static esp_err_t emac_esp32_del(esp_eth_mac_t *mac)
return ESP_OK;
}
void emac_esp32_isr_handler(void *args)
IRAM_ATTR void emac_esp32_isr_handler(void *args)
{
emac_hal_context_t *hal = (emac_hal_context_t *)args;
emac_esp32_t *emac = __containerof(hal, emac_esp32_t, hal);
@ -358,46 +377,35 @@ void emac_esp32_isr_handler(void *args)
esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config)
{
esp_eth_mac_t *ret = NULL;
void *descriptors = NULL;
emac_esp32_t *emac = NULL;
MAC_CHECK(config, "can't set mac config to null", err, NULL);
emac_esp32_t *emac = calloc(1, sizeof(emac_esp32_t));
emac = calloc(1, sizeof(emac_esp32_t));
MAC_CHECK(emac, "calloc emac failed", err, NULL);
/* alloc memory for ethernet dma descriptor */
uint32_t desc_size = CONFIG_ETH_DMA_RX_BUFFER_NUM * sizeof(eth_dma_rx_descriptor_t) +
CONFIG_ETH_DMA_TX_BUFFER_NUM * sizeof(eth_dma_tx_descriptor_t);
void *descriptors = heap_caps_calloc(1, desc_size, MALLOC_CAP_DMA);
MAC_CHECK(descriptors, "calloc descriptors failed", err_desc, NULL);
descriptors = heap_caps_calloc(1, desc_size, MALLOC_CAP_DMA);
MAC_CHECK(descriptors, "calloc descriptors failed", err, NULL);
int i = 0;
/* alloc memory for ethernet dma buffer */
for (i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
emac->rx_buf[i] = heap_caps_calloc(1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA);
if (!(emac->rx_buf[i])) {
break;
goto err;
}
}
if (i != CONFIG_ETH_DMA_RX_BUFFER_NUM) {
for (--i; i >= 0; i--) {
free(emac->rx_buf[i]);
}
goto err_buffer;
}
for (i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
emac->tx_buf[i] = heap_caps_calloc(1, CONFIG_ETH_DMA_BUFFER_SIZE, MALLOC_CAP_DMA);
if (!(emac->tx_buf[i])) {
break;
goto err;
}
}
if (i != CONFIG_ETH_DMA_TX_BUFFER_NUM) {
for (--i; i >= 0; i--) {
free(emac->tx_buf[i]);
}
for (i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
free(emac->rx_buf[i]);
}
goto err_buffer;
}
/* initialize hal layer driver */
emac_hal_init(&emac->hal, descriptors, emac->rx_buf, emac->tx_buf);
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->parent.set_mediator = emac_esp32_set_mediator;
emac->parent.init = emac_esp32_init;
emac->parent.deinit = emac_esp32_deinit;
@ -415,30 +423,45 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config)
/* Interrupt configuration */
MAC_CHECK(esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_esp32_isr_handler,
&emac->hal, &(emac->intr_hdl)) == ESP_OK,
"alloc emac interrupt failed", err_intr, NULL);
"alloc emac interrupt failed", err, NULL);
#ifdef CONFIG_PM_ENABLE
MAC_CHECK(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "emac_esp32", &emac->pm_lock) == ESP_OK,
"create pm lock failed", err, NULL);
#endif
/* create rx task */
BaseType_t xReturned = xTaskCreate(emac_esp32_rx_task, "emac_rx", config->rx_task_stack_size, emac,
config->rx_task_prio, &emac->rx_task_hdl);
MAC_CHECK(xReturned == pdPASS, "create emac_rx task failed", err_task, NULL);
MAC_CHECK(xReturned == pdPASS, "create emac_rx task failed", err, NULL);
return &(emac->parent);
err_task:
esp_intr_free(emac->intr_hdl);
err_intr:
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
free(emac->tx_buf[i]);
}
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
free(emac->rx_buf[i]);
}
err_buffer:
free(descriptors);
err_desc:
free(emac);
err:
if (emac) {
if (emac->rx_task_hdl) {
vTaskDelete(emac->rx_task_hdl);
}
if (emac->intr_hdl) {
esp_intr_free(emac->intr_hdl);
}
for (int i = 0; i < CONFIG_ETH_DMA_TX_BUFFER_NUM; i++) {
free(emac->tx_buf[i]);
}
for (int i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
free(emac->rx_buf[i]);
}
#ifdef CONFIG_PM_ENABLE
if (emac->pm_lock) {
esp_pm_lock_delete(emac->pm_lock);
}
#endif
free(emac);
}
if (descriptors) {
free(descriptors);
}
return ret;
}
void emac_hal_rx_complete_cb(void *arg)
IRAM_ATTR void emac_hal_rx_complete_cb(void *arg)
{
emac_hal_context_t *hal = (emac_hal_context_t *)arg;
emac_esp32_t *emac = __containerof(hal, emac_esp32_t, hal);
@ -450,7 +473,7 @@ void emac_hal_rx_complete_cb(void *arg)
}
}
void emac_hal_rx_unavail_cb(void *arg)
IRAM_ATTR void emac_hal_rx_unavail_cb(void *arg)
{
emac_hal_context_t *hal = (emac_hal_context_t *)arg;
emac_esp32_t *emac = __containerof(hal, emac_esp32_t, hal);

View File

@ -19,6 +19,7 @@
#include "eth_phy_regs_struct.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
static const char *TAG = "dm9051";
#define PHY_CHECK(a, str, goto_tag, ...) \
@ -79,16 +80,56 @@ typedef union {
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
const char *name;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_dm9051_t;
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;
bmsr_reg_t bmsr;
dscsr_reg_t dscsr;
PHY_CHECK(eth->phy_reg_read(eth, dm9051->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;
/* check if link status changed */
if (dm9051->link_status != link) {
/* when link up, read negotiation result */
if (link == ETH_LINK_UP) {
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCSR_REG_ADDR, &(dscsr.val)) == ESP_OK,
"read DSCSR failed", err);
if (dscsr.fdx100 || dscsr.hdx100) {
speed = ETH_SPEED_100M;
} else {
speed = ETH_SPEED_10M;
}
if (dscsr.fdx100 || dscsr.fdx10) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
dm9051->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t dm9051_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator for dm9051 to null", err);
PHY_CHECK(eth, "can't set mediator to null", err);
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
dm9051->eth = eth;
return ESP_OK;
@ -99,19 +140,8 @@ err:
static esp_err_t dm9051_get_link(esp_eth_phy_t *phy)
{
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
esp_eth_mediator_t *eth = dm9051->eth;
bmsr_reg_t bmsr;
PHY_CHECK(eth->phy_reg_read(eth, dm9051->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;
if (dm9051->link_status != link) {
if (link == ETH_LINK_UP) {
phy->negotiate(phy);
} else {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
dm9051->link_status = link;
}
}
/* Updata information about link, speed, duplex */
PHY_CHECK(dm9051_update_link_duplex_speed(dm9051) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -120,19 +150,25 @@ err:
static esp_err_t dm9051_reset(esp_eth_phy_t *phy)
{
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
dm9051->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = dm9051->eth;
dscr_reg_t dscr;
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)) == ESP_OK, "read DSCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)) == ESP_OK,
"read DSCR failed", err);
dscr.smrst = 1;
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, dscr.val) == ESP_OK, "write DSCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, dscr.val) == ESP_OK,
"write DSCR failed", err);
bmcr_reg_t bmcr = {.reset = 1};
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for reset complete */
uint32_t to = 0;
for (to = 0; to < dm9051->reset_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)) == ESP_OK, "read DSCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)) == ESP_OK,
"read DSCR failed", err);
if (!bmcr.reset && !dscr.smrst) {
break;
}
@ -143,6 +179,19 @@ err:
return ESP_FAIL;
}
static esp_err_t dm9051_reset_hw(esp_eth_phy_t *phy)
{
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
// set reset_gpio_num minus zero can skip hardware reset phy chip
if (dm9051->reset_gpio_num >= 0) {
gpio_pad_select_gpio(dm9051->reset_gpio_num);
gpio_set_direction(dm9051->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(dm9051->reset_gpio_num, 0);
gpio_set_level(dm9051->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t dm9051_negotiate(esp_eth_phy_t *phy)
{
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
@ -154,15 +203,18 @@ static esp_err_t dm9051_negotiate(esp_eth_phy_t *phy)
.en_auto_nego = 1, /* Auto Negotiation */
.restart_auto_nego = 1 /* Restart Auto Negotiation */
};
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for auto negotiation complete */
bmsr_reg_t bmsr;
dscsr_reg_t dscsr;
uint32_t to = 0;
for (to = 0; to < dm9051->autonego_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
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_DSCSR_REG_ADDR, &(dscsr.val)) == ESP_OK, "read DSCSR 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_DSCSR_REG_ADDR, &(dscsr.val)) == ESP_OK,
"read DSCSR failed", err);
if (bmsr.auto_nego_complete && dscsr.anmb & 0x08) {
break;
}
@ -171,27 +223,7 @@ static esp_err_t dm9051_negotiate(esp_eth_phy_t *phy)
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
}
/* Updata information about link, speed, duplex */
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_DSCSR_REG_ADDR, &(dscsr.val)) == ESP_OK, "read DSCSR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
if (dscsr.fdx100 || dscsr.hdx100) {
speed = ETH_SPEED_100M;
} else {
speed = ETH_SPEED_10M;
}
if (dscsr.fdx100 || dscsr.fdx10) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
if (dm9051->link_status != link) {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
dm9051->link_status = link;
}
PHY_CHECK(dm9051_update_link_duplex_speed(dm9051) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -202,7 +234,8 @@ static esp_err_t dm9051_pwrctl(esp_eth_phy_t *phy, bool enable)
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
esp_eth_mediator_t *eth = dm9051->eth;
bmcr_reg_t bmcr;
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
/* Enable IEEE Power Down Mode */
bmcr.power_down = 1;
@ -210,8 +243,10 @@ static esp_err_t dm9051_pwrctl(esp_eth_phy_t *phy, bool enable)
/* Disable IEEE Power Down Mode */
bmcr.power_down = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
} else {
@ -231,7 +266,7 @@ static esp_err_t dm9051_set_addr(esp_eth_phy_t *phy, uint32_t addr)
static esp_err_t dm9051_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "get phy address failed", err);
PHY_CHECK(addr, "addr can't be null", err);
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
*addr = dm9051->addr;
return ESP_OK;
@ -251,15 +286,18 @@ static esp_err_t dm9051_init(esp_eth_phy_t *phy)
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
esp_eth_mediator_t *eth = dm9051->eth;
/* Power on Ethernet PHY */
PHY_CHECK(dm9051_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err);
PHY_CHECK(dm9051_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(dm9051_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
PHY_CHECK(dm9051_reset(phy) == ESP_OK, "reset failed", err);
/* Check PHY ID */
phyidr1_reg_t id1;
phyidr2_reg_t id2;
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x0181 && id2.oui_lsb == 0x2E && id2.vendor_model == 0x0A, "wrong PHY chip ID", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
"read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
"read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x0181 && id2.oui_lsb == 0x2E && id2.vendor_model == 0x0A,
"wrong chip ID", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -268,7 +306,7 @@ err:
static esp_err_t dm9051_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(dm9051_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err);
PHY_CHECK(dm9051_pwrctl(phy, false) == ESP_OK, "power control failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -279,13 +317,14 @@ esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config)
PHY_CHECK(config, "can't set phy config to null", err);
PHY_CHECK(config->phy_addr == 1, "dm9051's phy address can only set to 1", err);
phy_dm9051_t *dm9051 = calloc(1, sizeof(phy_dm9051_t));
PHY_CHECK(dm9051, "calloc dm9051 object failed", err);
dm9051->name = "dm9051";
PHY_CHECK(dm9051, "calloc dm9051 failed", err);
dm9051->addr = config->phy_addr;
dm9051->reset_timeout_ms = config->reset_timeout_ms;
dm9051->reset_gpio_num = config->reset_gpio_num;
dm9051->link_status = ETH_LINK_DOWN;
dm9051->autonego_timeout_ms = config->autonego_timeout_ms;
dm9051->parent.reset = dm9051_reset;
dm9051->parent.reset_hw = dm9051_reset_hw;
dm9051->parent.init = dm9051_init;
dm9051->parent.deinit = dm9051_deinit;
dm9051->parent.set_mediator = dm9051_set_mediator;
@ -295,7 +334,6 @@ 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.del = dm9051_del;
return &(dm9051->parent);
err:
return NULL;

View File

@ -19,6 +19,7 @@
#include "eth_phy_regs_struct.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
static const char *TAG = "dp83848";
#define PHY_CHECK(a, str, goto_tag, ...) \
@ -85,16 +86,56 @@ typedef union {
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
const char *name;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_dp83848_t;
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;
bmsr_reg_t bmsr;
physts_reg_t physts;
PHY_CHECK(eth->phy_reg_read(eth, dp83848->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;
/* check if link status changed */
if (dp83848->link_status != link) {
/* when link up, read negotiation result */
if (link == ETH_LINK_UP) {
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)) == ESP_OK,
"read PHYSTS failed", err);
if (physts.speed_status) {
speed = ETH_SPEED_10M;
} else {
speed = ETH_SPEED_100M;
}
if (physts.duplex_status) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
dp83848->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t dp83848_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator for dp83848 to null", err);
PHY_CHECK(eth, "can't set mediator to null", err);
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
dp83848->eth = eth;
return ESP_OK;
@ -105,19 +146,8 @@ err:
static esp_err_t dp83848_get_link(esp_eth_phy_t *phy)
{
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
esp_eth_mediator_t *eth = dp83848->eth;
bmsr_reg_t bmsr;
PHY_CHECK(eth->phy_reg_read(eth, dp83848->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;
if (dp83848->link_status != link) {
if (link == ETH_LINK_UP) {
phy->negotiate(phy);
} else {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
dp83848->link_status = link;
}
}
/* Updata information about link, speed, duplex */
PHY_CHECK(dp83848_update_link_duplex_speed(dp83848) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -126,24 +156,39 @@ err:
static esp_err_t dp83848_reset(esp_eth_phy_t *phy)
{
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
dp83848->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = dp83848->eth;
bmcr_reg_t bmcr = {.reset = 1};
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for reset complete */
uint32_t to = 0;
for (to = 0; to < dp83848->reset_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!bmcr.reset) {
break;
}
}
PHY_CHECK(to < dp83848->reset_timeout_ms / 10, "PHY reset timeout", err);
PHY_CHECK(to < dp83848->reset_timeout_ms / 10, "reset timeout", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t dp83848_reset_hw(esp_eth_phy_t *phy)
{
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
if (dp83848->reset_gpio_num >= 0) {
gpio_pad_select_gpio(dp83848->reset_gpio_num);
gpio_set_direction(dp83848->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(dp83848->reset_gpio_num, 0);
gpio_set_level(dp83848->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t dp83848_negotiate(esp_eth_phy_t *phy)
{
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
@ -155,45 +200,28 @@ static esp_err_t dp83848_negotiate(esp_eth_phy_t *phy)
.en_auto_nego = 1, /* Auto Negotiation */
.restart_auto_nego = 1 /* Restart Auto Negotiation */
};
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for auto negotiation complete */
bmsr_reg_t bmsr;
physts_reg_t physts;
uint32_t to = 0;
for (to = 0; to < dp83848->autonego_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)) == ESP_OK, "read PHYSTS failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)) == ESP_OK,
"read PHYSTS failed", err);
if (bmsr.auto_nego_complete && physts.auto_nego_complete) {
break;
}
}
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
if (to >= dp83848->autonego_timeout_ms / 10) {
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
ESP_LOGW(TAG, "auto negotiation timeout");
}
/* Updata information about link, speed, duplex */
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR 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 = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
if (physts.speed_status) {
speed = ETH_SPEED_10M;
} else {
speed = ETH_SPEED_100M;
}
if (physts.duplex_status) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
if (dp83848->link_status != link) {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
dp83848->link_status = link;
}
PHY_CHECK(dp83848_update_link_duplex_speed(dp83848) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -204,7 +232,8 @@ static esp_err_t dp83848_pwrctl(esp_eth_phy_t *phy, bool enable)
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
esp_eth_mediator_t *eth = dp83848->eth;
bmcr_reg_t bmcr;
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
/* Enable IEEE Power Down Mode */
bmcr.power_down = 1;
@ -212,8 +241,10 @@ static esp_err_t dp83848_pwrctl(esp_eth_phy_t *phy, bool enable)
/* Disable IEEE Power Down Mode */
bmcr.power_down = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
} else {
@ -233,7 +264,7 @@ static esp_err_t dp83848_set_addr(esp_eth_phy_t *phy, uint32_t addr)
static esp_err_t dp83848_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "get phy address failed", err);
PHY_CHECK(addr, "addr can't be null", err);
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
*addr = dp83848->addr;
return ESP_OK;
@ -253,15 +284,18 @@ static esp_err_t dp83848_init(esp_eth_phy_t *phy)
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
esp_eth_mediator_t *eth = dp83848->eth;
/* Power on Ethernet PHY */
PHY_CHECK(dp83848_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err);
PHY_CHECK(dp83848_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(dp83848_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
PHY_CHECK(dp83848_reset(phy) == ESP_OK, "reset failed", err);
/* Check PHY ID */
phyidr1_reg_t id1;
phyidr2_reg_t id2;
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x2000 && id2.oui_lsb == 0x17 && id2.vendor_model == 0x09, "wrong PHY chip ID", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
"read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
"read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x2000 && id2.oui_lsb == 0x17 && id2.vendor_model == 0x09,
"wrong chip ID", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -270,7 +304,7 @@ err:
static esp_err_t dp83848_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(dp83848_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err);
PHY_CHECK(dp83848_pwrctl(phy, false) == ESP_OK, "power control failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -280,13 +314,14 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config)
{
PHY_CHECK(config, "can't set phy config to null", err);
phy_dp83848_t *dp83848 = calloc(1, sizeof(phy_dp83848_t));
PHY_CHECK(dp83848, "calloc dp83848 object failed", err);
dp83848->name = "dp83848";
PHY_CHECK(dp83848, "calloc dp83848 failed", err);
dp83848->addr = config->phy_addr;
dp83848->reset_timeout_ms = config->reset_timeout_ms;
dp83848->link_status = ETH_LINK_DOWN;
dp83848->reset_gpio_num = config->reset_gpio_num;
dp83848->autonego_timeout_ms = config->autonego_timeout_ms;
dp83848->parent.reset = dp83848_reset;
dp83848->parent.reset_hw = dp83848_reset_hw;
dp83848->parent.init = dp83848_init;
dp83848->parent.deinit = dp83848_deinit;
dp83848->parent.set_mediator = dp83848_set_mediator;

View File

@ -19,6 +19,7 @@
#include "eth_phy_regs_struct.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
static const char *TAG = "ip101";
#define PHY_CHECK(a, str, goto_tag, ...) \
@ -102,11 +103,11 @@ typedef union {
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
const char *name;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_ip101_t;
static esp_err_t ip101_page_select(phy_ip101_t *ip101, uint32_t page)
@ -121,9 +122,62 @@ err:
return ESP_FAIL;
}
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;
cssr_reg_t cssr;
bmsr_reg_t bmsr;
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_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->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;
/* check if link status changed */
if (ip101->link_status != link) {
/* when link up, read negotiation result */
if (link == ETH_LINK_UP) {
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_CSSR_REG_ADDR, &(cssr.val)) == ESP_OK,
"read CSSR failed", err);
switch (cssr.op_mode) {
case 1: //10M Half
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_HALF;
break;
case 2: //100M Half
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_HALF;
break;
case 5: //10M Full
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_FULL;
break;
case 6: //100M Full
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_FULL;
break;
default:
break;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"chagne link failed", err);
ip101->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t ip101_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator for ip101 to null", err);
PHY_CHECK(eth, "can't set mediator to null", err);
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
ip101->eth = eth;
return ESP_OK;
@ -134,19 +188,8 @@ err:
static esp_err_t ip101_get_link(esp_eth_phy_t *phy)
{
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
esp_eth_mediator_t *eth = ip101->eth;
bmsr_reg_t bmsr;
PHY_CHECK(eth->phy_reg_read(eth, ip101->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;
if (ip101->link_status != link) {
if (link == ETH_LINK_UP) {
phy->negotiate(phy);
} else {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
ip101->link_status = link;
}
}
/* Updata information about link, speed, duplex */
PHY_CHECK(ip101_update_link_duplex_speed(ip101) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -155,87 +198,69 @@ err:
static esp_err_t ip101_reset(esp_eth_phy_t *phy)
{
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
ip101->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = ip101->eth;
bmcr_reg_t bmcr = {.reset = 1};
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* wait for reset complete */
uint32_t to = 0;
for (to = 0; to < ip101->reset_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!bmcr.reset) {
break;
}
}
PHY_CHECK(to < ip101->reset_timeout_ms / 10, "PHY reset timeout", err);
PHY_CHECK(to < ip101->reset_timeout_ms / 10, "reset timeout", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t ip101_reset_hw(esp_eth_phy_t *phy)
{
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
if (ip101->reset_gpio_num >= 0) {
gpio_pad_select_gpio(ip101->reset_gpio_num);
gpio_set_direction(ip101->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(ip101->reset_gpio_num, 0);
gpio_set_level(ip101->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t ip101_negotiate(esp_eth_phy_t *phy)
{
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
esp_eth_mediator_t *eth = ip101->eth;
/* Start auto negotiation */
/* Restart auto negotiation */
bmcr_reg_t bmcr = {
.speed_select = 1, /* 100Mbps */
.duplex_mode = 1, /* Full Duplex */
.en_auto_nego = 1, /* Auto Negotiation */
.restart_auto_nego = 1 /* Restart Auto Negotiation */
};
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for auto negotiation complete */
bmsr_reg_t bmsr;
uint32_t to = 0;
for (to = 0; to < ip101->autonego_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
if (bmsr.auto_nego_complete) {
break;
}
}
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
if (to >= ip101->autonego_timeout_ms / 10) {
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
ESP_LOGW(TAG, "auto negotiation timeout");
}
PHY_CHECK(ip101_page_select(ip101, 16) == ESP_OK, "select page failed", err);
/* Updata information about link, speed, duplex */
cssr_reg_t cssr;
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_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
switch (cssr.op_mode) {
case 0: //Link off
link = ETH_LINK_DOWN;
break;
case 1: //10M Half
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_HALF;
break;
case 2: //100M Half
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_HALF;
break;
case 5: //10M Full
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_FULL;
break;
case 6: //100M Full
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_FULL;
break;
default:
break;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
if (ip101->link_status != link) {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
ip101->link_status = link;
}
PHY_CHECK(ip101_update_link_duplex_speed(ip101) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -246,7 +271,8 @@ static esp_err_t ip101_pwrctl(esp_eth_phy_t *phy, bool enable)
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
esp_eth_mediator_t *eth = ip101->eth;
bmcr_reg_t bmcr;
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
/* Enable IEEE Power Down Mode */
bmcr.power_down = 1;
@ -254,8 +280,10 @@ static esp_err_t ip101_pwrctl(esp_eth_phy_t *phy, bool enable)
/* Disable IEEE Power Down Mode */
bmcr.power_down = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
} else {
@ -275,7 +303,7 @@ static esp_err_t ip101_set_addr(esp_eth_phy_t *phy, uint32_t addr)
static esp_err_t ip101_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "get phy address failed", err);
PHY_CHECK(addr, "addr can't be null", err);
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
*addr = ip101->addr;
return ESP_OK;
@ -295,15 +323,15 @@ static esp_err_t ip101_init(esp_eth_phy_t *phy)
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
esp_eth_mediator_t *eth = ip101->eth;
/* Power on Ethernet PHY */
PHY_CHECK(ip101_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err);
PHY_CHECK(ip101_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(ip101_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
PHY_CHECK(ip101_reset(phy) == ESP_OK, "reset failed", err);
/* Check PHY ID */
phyidr1_reg_t id1;
phyidr2_reg_t id2;
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x243 && id2.oui_lsb == 0x3 && id2.vendor_model == 0x5, "wrong PHY chip ID", err);
PHY_CHECK(id1.oui_msb == 0x243 && id2.oui_lsb == 0x3 && id2.vendor_model == 0x5, "wrong chip ID", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -312,7 +340,7 @@ err:
static esp_err_t ip101_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(ip101_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err);
PHY_CHECK(ip101_pwrctl(phy, false) == ESP_OK, "power control failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -322,13 +350,14 @@ esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config)
{
PHY_CHECK(config, "can't set phy config to null", err);
phy_ip101_t *ip101 = calloc(1, sizeof(phy_ip101_t));
PHY_CHECK(ip101, "calloc ip101 object failed", err);
ip101->name = "ip101";
PHY_CHECK(ip101, "calloc ip101 failed", err);
ip101->addr = config->phy_addr;
ip101->reset_timeout_ms = config->reset_timeout_ms;
ip101->reset_gpio_num = config->reset_gpio_num;
ip101->link_status = ETH_LINK_DOWN;
ip101->autonego_timeout_ms = config->autonego_timeout_ms;
ip101->parent.reset = ip101_reset;
ip101->parent.reset_hw = ip101_reset_hw;
ip101->parent.init = ip101_init;
ip101->parent.deinit = ip101_deinit;
ip101->parent.set_mediator = ip101_set_mediator;

View File

@ -19,6 +19,7 @@
#include "eth_phy_regs_struct.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
static const char *TAG = "lan8720";
#define PHY_CHECK(a, str, goto_tag, ...) \
@ -157,16 +158,66 @@ typedef union {
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
const char *name;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_lan8720_t;
static esp_err_t lan8720_update_link_duplex_speed(phy_lan8720_t *lan8720)
{
esp_eth_mediator_t *eth = lan8720->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
bmsr_reg_t bmsr;
pscsr_reg_t pscsr;
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;
/* check if link status changed */
if (lan8720->link_status != link) {
/* when link up, read negotiation result */
if (link == ETH_LINK_UP) {
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)) == ESP_OK,
"read PSCSR failed", err);
switch (pscsr.speed_indication) {
case 1: //10Base-T half-duplex
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_HALF;
break;
case 2: //100Base-TX half-duplex
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_HALF;
break;
case 5: //10Base-T full-duplex
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_FULL;
break;
case 6: //100Base-TX full-duplex
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_FULL;
break;
default:
break;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
lan8720->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t lan8720_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator for lan8720 to null", err);
PHY_CHECK(eth, "can't set mediator to null", err);
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
lan8720->eth = eth;
return ESP_OK;
@ -177,19 +228,8 @@ err:
static esp_err_t lan8720_get_link(esp_eth_phy_t *phy)
{
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
esp_eth_mediator_t *eth = lan8720->eth;
bmsr_reg_t bmsr;
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;
if (lan8720->link_status != link) {
if (link == ETH_LINK_UP) {
phy->negotiate(phy);
} else {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
lan8720->link_status = link;
}
}
/* Updata information about link, speed, duplex */
PHY_CHECK(lan8720_update_link_duplex_speed(lan8720) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -198,29 +238,44 @@ err:
static esp_err_t lan8720_reset(esp_eth_phy_t *phy)
{
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
lan8720->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = lan8720->eth;
bmcr_reg_t bmcr = {.reset = 1};
PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* wait for reset complete */
uint32_t to = 0;
for (to = 0; to < lan8720->reset_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!bmcr.reset) {
break;
}
}
PHY_CHECK(to < lan8720->reset_timeout_ms / 10, "PHY reset timeout", err);
PHY_CHECK(to < lan8720->reset_timeout_ms / 10, "reset timeout", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t lan8720_reset_hw(esp_eth_phy_t *phy)
{
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
if (lan8720->reset_gpio_num >= 0) {
gpio_pad_select_gpio(lan8720->reset_gpio_num);
gpio_set_direction(lan8720->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(lan8720->reset_gpio_num, 0);
gpio_set_level(lan8720->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t lan8720_negotiate(esp_eth_phy_t *phy)
{
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
esp_eth_mediator_t *eth = lan8720->eth;
/* Start auto negotiation */
/* Restart auto negotiation */
bmcr_reg_t bmcr = {
.speed_select = 1, /* 100Mbps */
.duplex_mode = 1, /* Full Duplex */
@ -234,48 +289,20 @@ static esp_err_t lan8720_negotiate(esp_eth_phy_t *phy)
int32_t to = 0;
for (to = 0; to < lan8720->autonego_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)) == ESP_OK, "read PSCSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)) == ESP_OK,
"read PSCSR failed", err);
if (bmsr.auto_nego_complete && pscsr.auto_nego_done) {
break;
}
}
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
if (to >= lan8720->autonego_timeout_ms / 10) {
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
ESP_LOGW(TAG, "auto negotiation timeout");
}
/* Updata information about link, speed, duplex */
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)) == ESP_OK, "read PSCSR failed", err);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
switch (pscsr.speed_indication) {
case 1: //10Base-T half-duplex
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_HALF;
break;
case 2: //100Base-TX half-duplex
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_HALF;
break;
case 5: //10Base-T full-duplex
speed = ETH_SPEED_10M;
duplex = ETH_DUPLEX_FULL;
break;
case 6: //100Base-TX full-duplex
speed = ETH_SPEED_100M;
duplex = ETH_DUPLEX_FULL;
break;
default:
break;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
if (lan8720->link_status != link) {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
lan8720->link_status = link;
}
PHY_CHECK(lan8720_update_link_duplex_speed(lan8720) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -286,7 +313,8 @@ static esp_err_t lan8720_pwrctl(esp_eth_phy_t *phy, bool enable)
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
esp_eth_mediator_t *eth = lan8720->eth;
bmcr_reg_t bmcr;
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
/* General Power Down Mode */
bmcr.power_down = 1;
@ -294,8 +322,10 @@ static esp_err_t lan8720_pwrctl(esp_eth_phy_t *phy, bool enable)
/* Normal operation Mode */
bmcr.power_down = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
} else {
@ -315,7 +345,7 @@ static esp_err_t lan8720_set_addr(esp_eth_phy_t *phy, uint32_t addr)
static esp_err_t lan8720_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "get phy address failed", err);
PHY_CHECK(addr, "addr can't be null", err);
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
*addr = lan8720->addr;
return ESP_OK;
@ -335,15 +365,17 @@ static esp_err_t lan8720_init(esp_eth_phy_t *phy)
phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent);
esp_eth_mediator_t *eth = lan8720->eth;
/* Power on Ethernet PHY */
PHY_CHECK(lan8720_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err);
PHY_CHECK(lan8720_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(lan8720_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
PHY_CHECK(lan8720_reset(phy) == ESP_OK, "reset failed", err);
/* Check PHY ID */
phyidr1_reg_t id1;
phyidr2_reg_t id2;
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x7 && id2.oui_lsb == 0x30 && id2.vendor_model == 0xF, "wrong PHY chip ID", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
"read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, lan8720->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
"read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x7 && id2.oui_lsb == 0x30 && id2.vendor_model == 0xF, "wrong chip ID", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -352,7 +384,7 @@ err:
static esp_err_t lan8720_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(lan8720_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err);
PHY_CHECK(lan8720_pwrctl(phy, false) == ESP_OK, "power control failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -362,13 +394,14 @@ esp_eth_phy_t *esp_eth_phy_new_lan8720(const eth_phy_config_t *config)
{
PHY_CHECK(config, "can't set phy config to null", err);
phy_lan8720_t *lan8720 = calloc(1, sizeof(phy_lan8720_t));
PHY_CHECK(lan8720, "calloc lan8720 object failed", err);
lan8720->name = "lan8720";
PHY_CHECK(lan8720, "calloc lan8720 failed", err);
lan8720->addr = config->phy_addr;
lan8720->reset_gpio_num = config->reset_gpio_num;
lan8720->reset_timeout_ms = config->reset_timeout_ms;
lan8720->link_status = ETH_LINK_DOWN;
lan8720->autonego_timeout_ms = config->autonego_timeout_ms;
lan8720->parent.reset = lan8720_reset;
lan8720->parent.reset_hw = lan8720_reset_hw;
lan8720->parent.init = lan8720_init;
lan8720->parent.deinit = lan8720_deinit;
lan8720->parent.set_mediator = lan8720_set_mediator;

View File

@ -20,6 +20,7 @@
#include "eth_phy_regs_struct.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
static const char *TAG = "rtl8201";
#define PHY_CHECK(a, str, goto_tag, ...) \
@ -63,11 +64,11 @@ typedef union {
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
const char *name;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_rtl8201_t;
static esp_err_t rtl8201_page_select(phy_rtl8201_t *rtl8201, uint32_t page)
@ -82,9 +83,50 @@ err:
return ESP_FAIL;
}
static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
{
esp_eth_mediator_t *eth = rtl8201->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
bmcr_reg_t bmcr;
bmsr_reg_t bmsr;
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);
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (rtl8201->link_status != link) {
/* when link up, read negotiation result */
if (link == ETH_LINK_UP) {
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (bmcr.speed_select) {
speed = ETH_SPEED_100M;
} else {
speed = ETH_SPEED_10M;
}
if (bmcr.duplex_mode) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
rtl8201->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t rtl8201_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator for rtl8201 to null", err);
PHY_CHECK(eth, "can't set mediator to null", err);
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
rtl8201->eth = eth;
return ESP_OK;
@ -95,19 +137,8 @@ err:
static esp_err_t rtl8201_get_link(esp_eth_phy_t *phy)
{
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
esp_eth_mediator_t *eth = rtl8201->eth;
bmsr_reg_t bmsr;
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->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;
if (rtl8201->link_status != link) {
if (link == ETH_LINK_UP) {
phy->negotiate(phy);
} else {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
rtl8201->link_status = link;
}
}
/* Updata information about link, speed, duplex */
PHY_CHECK(rtl8201_update_link_duplex_speed(rtl8201) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -116,73 +147,69 @@ err:
static esp_err_t rtl8201_reset(esp_eth_phy_t *phy)
{
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
rtl8201->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = rtl8201->eth;
bmcr_reg_t bmcr = {.reset = 1};
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for reset complete */
uint32_t to = 0;
for (to = 0; to < rtl8201->reset_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!bmcr.reset) {
break;
}
}
PHY_CHECK(to < rtl8201->reset_timeout_ms / 10, "PHY reset timeout", err);
PHY_CHECK(to < rtl8201->reset_timeout_ms / 10, "reset timeout", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t rtl8201_reset_hw(esp_eth_phy_t *phy)
{
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
if (rtl8201->reset_gpio_num >= 0) {
gpio_pad_select_gpio(rtl8201->reset_gpio_num);
gpio_set_direction(rtl8201->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(rtl8201->reset_gpio_num, 0);
gpio_set_level(rtl8201->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t rtl8201_negotiate(esp_eth_phy_t *phy)
{
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
esp_eth_mediator_t *eth = rtl8201->eth;
/* Start auto negotiation */
/* Restart auto negotiation */
bmcr_reg_t bmcr = {
.speed_select = 1, /* 100Mbps */
.duplex_mode = 1, /* Full Duplex */
.en_auto_nego = 1, /* Auto Negotiation */
.restart_auto_nego = 1 /* Restart Auto Negotiation */
};
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
/* Wait for auto negotiation complete */
bmsr_reg_t bmsr;
uint32_t to = 0;
for (to = 0; to < rtl8201->autonego_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
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_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK,
"read BMSR failed", err);
if (bmsr.auto_nego_complete) {
break;
}
}
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
if (to >= rtl8201->autonego_timeout_ms / 10) {
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
ESP_LOGW(TAG, "auto negotiation timeout");
}
PHY_CHECK(rtl8201_page_select(rtl8201, 0) == ESP_OK, "select page failed", err);
/* Updata information about link, speed, duplex */
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->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;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
if (bmcr.speed_select) {
speed = ETH_SPEED_100M;
} else {
speed = ETH_SPEED_10M;
}
if (bmcr.duplex_mode) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
if (rtl8201->link_status != link) {
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
rtl8201->link_status = link;
}
PHY_CHECK(rtl8201_update_link_duplex_speed(rtl8201) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -193,7 +220,8 @@ static esp_err_t rtl8201_pwrctl(esp_eth_phy_t *phy, bool enable)
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
esp_eth_mediator_t *eth = rtl8201->eth;
bmcr_reg_t bmcr;
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
/* Enable IEEE Power Down Mode */
bmcr.power_down = 1;
@ -201,8 +229,10 @@ static esp_err_t rtl8201_pwrctl(esp_eth_phy_t *phy, bool enable)
/* Disable IEEE Power Down Mode */
bmcr.power_down = 0;
}
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed", err);
PHY_CHECK(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK,
"write BMCR failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK,
"read BMCR failed", err);
if (!enable) {
PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
} else {
@ -222,7 +252,7 @@ static esp_err_t rtl8201_set_addr(esp_eth_phy_t *phy, uint32_t addr)
static esp_err_t rtl8201_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "get phy address failed", err);
PHY_CHECK(addr, "addr can't be null", err);
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
*addr = rtl8201->addr;
return ESP_OK;
@ -242,15 +272,18 @@ static esp_err_t rtl8201_init(esp_eth_phy_t *phy)
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
esp_eth_mediator_t *eth = rtl8201->eth;
/* Power on Ethernet PHY */
PHY_CHECK(rtl8201_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err);
PHY_CHECK(rtl8201_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(rtl8201_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
PHY_CHECK(rtl8201_reset(phy) == ESP_OK, "reset failed", err);
/* Check PHY ID */
phyidr1_reg_t id1;
phyidr2_reg_t id2;
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x1C && id2.oui_lsb == 0x32 && id2.vendor_model == 0x1, "wrong PHY chip ID", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK,
"read ID1 failed", err);
PHY_CHECK(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK,
"read ID2 failed", err);
PHY_CHECK(id1.oui_msb == 0x1C && id2.oui_lsb == 0x32 && id2.vendor_model == 0x1,
"wrong chip ID", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -259,7 +292,7 @@ err:
static esp_err_t rtl8201_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(rtl8201_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err);
PHY_CHECK(rtl8201_pwrctl(phy, false) == ESP_OK, "power control failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -269,13 +302,14 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config)
{
PHY_CHECK(config, "can't set phy config to null", err);
phy_rtl8201_t *rtl8201 = calloc(1, sizeof(phy_rtl8201_t));
PHY_CHECK(rtl8201, "calloc rtl8201 object failed", err);
rtl8201->name = "rtl8201";
PHY_CHECK(rtl8201, "calloc rtl8201 failed", err);
rtl8201->addr = config->phy_addr;
rtl8201->reset_gpio_num = config->reset_gpio_num;
rtl8201->reset_timeout_ms = config->reset_timeout_ms;
rtl8201->link_status = ETH_LINK_DOWN;
rtl8201->autonego_timeout_ms = config->autonego_timeout_ms;
rtl8201->parent.reset = rtl8201_reset;
rtl8201->parent.reset_hw = rtl8201_reset_hw;
rtl8201->parent.init = rtl8201_init;
rtl8201->parent.deinit = rtl8201_deinit;
rtl8201->parent.set_mediator = rtl8201_set_mediator;

View File

@ -10,6 +10,7 @@
#include "esp_event.h"
#include "esp_eth.h"
#include "esp_log.h"
#include "driver/gpio.h"
static const char *TAG = "esp_eth_test";
@ -66,9 +67,26 @@ static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
}
static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_wait)
{
int i = 0;
ms_to_wait += 100;
for (i = 0; i < ms_to_wait / 100; i++) {
vTaskDelay(pdMS_TO_TICKS(100));
if (esp_eth_driver_uninstall(eth_hdl) == ESP_OK) {
break;
}
}
if (i < ms_to_wait / 10) {
return ESP_OK;
} else {
return ESP_FAIL;
}
}
TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]")
{
TEST_ESP_OK(esp_event_loop_create_default());
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
@ -76,7 +94,6 @@ TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]")
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
vTaskDelay(pdMS_TO_TICKS(1000));
/* get MAC address */
uint8_t mac_addr[6];
memset(mac_addr, 0, sizeof(mac_addr));
@ -89,10 +106,10 @@ TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]")
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_PHY_ADDR, &phy_addr));
ESP_LOGI(TAG, "Ethernet PHY Address: %d", phy_addr);
TEST_ASSERT(phy_addr >= 0 && phy_addr <= 31);
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
/* driver should be uninstalled within 2 seconds */
TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_loop_delete_default());
}
TEST_CASE("esp32 ethernet event test", "[ethernet][test_env=UT_T2_Ethernet]")
@ -109,18 +126,20 @@ TEST_CASE("esp32 ethernet event test", "[ethernet][test_env=UT_T2_Ethernet]")
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
TEST_ESP_OK(esp_eth_start(eth_handle));
/* wait for connection start */
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
/* wait for connection establish */
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
/* wait for connection stop */
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
// "check link timer callback" might owned the reference of phy object, make sure it has release it
vTaskDelay(pdMS_TO_TICKS(2000));
/* driver should be uninstalled within 2 seconds */
TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
@ -145,15 +164,58 @@ TEST_CASE("esp32 ethernet dhcp test", "[ethernet][test_env=UT_T2_Ethernet]")
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
TEST_ESP_OK(esp_eth_start(eth_handle));
/* wait for IP lease */
bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT);
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
/* wait for connection stop */
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
// "check link timer callback" might owned the reference of phy object, make sure it has release it
vTaskDelay(pdMS_TO_TICKS(2000));
/* driver should be uninstalled within 2 seconds */
TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(tcpip_adapter_clear_default_eth_handlers());
TEST_ESP_OK(esp_event_loop_delete_default());
vEventGroupDelete(eth_event_group);
}
TEST_CASE("esp32 ethernet start/stop stress test", "[ethernet][test_env=UT_T2_Ethernet][timeout=240]")
{
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
test_case_uses_tcpip();
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(tcpip_adapter_set_default_eth_handlers());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group));
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
for (int i = 0; i < 10; i++) {
TEST_ESP_OK(esp_eth_start(eth_handle));
/* wait for IP lease */
bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT);
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
/* wait for connection stop */
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
}
/* driver should be uninstalled within 2 seconds */
TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
@ -185,7 +247,7 @@ TEST_CASE("dm9051 io test", "[ethernet][ignore]")
};
TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, &spi_handle));
gpio_install_isr_service(0);
tcpip_adapter_init();
test_case_uses_tcpip();
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(tcpip_adapter_set_default_eth_handlers());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
@ -198,8 +260,12 @@ TEST_CASE("dm9051 io test", "[ethernet][ignore]")
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle));
TEST_ESP_OK(esp_eth_start(eth_handle));
vTaskDelay(pdMS_TO_TICKS(portMAX_DELAY));
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
/* driver should be uninstalled within 2 seconds */
TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_loop_delete_default());

View File

@ -482,7 +482,7 @@ void emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t len
hal->dma_regs->dmatxpolldemand = 0;
}
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, 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)
{
eth_dma_rx_descriptor_t *desc_iter = NULL;
eth_dma_rx_descriptor_t *first_desc = NULL;
@ -501,6 +501,12 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t
if (desc_iter->RDES0.LastDescriptor) {
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH;
/* check if the buffer can store the whole frame */
if (len > size) {
/* return the real size that we want */
/* user need to compare the return value to the size they prepared when this function returned */
return len;
}
/* update unhandled frame count */
frame_count++;
}
@ -544,7 +550,7 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t
return len;
}
void emac_hal_isr(void *arg)
IRAM_ATTR void emac_hal_isr(void *arg)
{
emac_hal_context_t *hal = (emac_hal_context_t *)arg;
typeof(hal->dma_regs->dmastatus) dma_status = hal->dma_regs->dmastatus;
@ -615,7 +621,7 @@ void emac_hal_isr(void *arg)
}
}
__attribute__((weak)) void emac_hal_tx_complete_cb(void *arg)
IRAM_ATTR __attribute__((weak)) void emac_hal_tx_complete_cb(void *arg)
{
// This is a weak function, do nothing by default
// Upper code can rewrite this function
@ -623,7 +629,7 @@ __attribute__((weak)) void emac_hal_tx_complete_cb(void *arg)
return;
}
__attribute__((weak)) void emac_hal_tx_unavail_cb(void *arg)
IRAM_ATTR __attribute__((weak)) void emac_hal_tx_unavail_cb(void *arg)
{
// This is a weak function, do nothing by default
// Upper code can rewrite this function
@ -631,7 +637,7 @@ __attribute__((weak)) void emac_hal_tx_unavail_cb(void *arg)
return;
}
__attribute__((weak)) void emac_hal_rx_complete_cb(void *arg)
IRAM_ATTR __attribute__((weak)) void emac_hal_rx_complete_cb(void *arg)
{
// This is a weak function, do nothing by default
// Upper code can rewrite this function
@ -639,7 +645,7 @@ __attribute__((weak)) void emac_hal_rx_complete_cb(void *arg)
return;
}
__attribute__((weak)) void emac_hal_rx_early_cb(void *arg)
IRAM_ATTR __attribute__((weak)) void emac_hal_rx_early_cb(void *arg)
{
// This is a weak function, do nothing by default
// Upper code can rewrite this function
@ -647,7 +653,7 @@ __attribute__((weak)) void emac_hal_rx_early_cb(void *arg)
return;
}
__attribute__((weak)) void emac_hal_rx_unavail_cb(void *arg)
IRAM_ATTR __attribute__((weak)) void emac_hal_rx_unavail_cb(void *arg)
{
// This is a weak function, do nothing by default
// Upper code can rewrite this function

View File

@ -380,7 +380,7 @@ uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal);
void 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 *frames_remain);
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain);
void emac_hal_isr(void *arg);

View File

@ -14,10 +14,3 @@ entries:
spi_slave_hal_iram (noflash_text)
spi_flash_hal_iram (noflash)
lldesc (noflash_text)
if ETH_USE_ESP32_EMAC = y:
emac_hal:emac_hal_isr (noflash_text)
emac_hal:emac_hal_tx_complete_cb (noflash_text)
emac_hal:emac_hal_tx_unavail_cb (noflash_text)
emac_hal:emac_hal_rx_complete_cb (noflash_text)
emac_hal:emac_hal_rx_early_cb (noflash_text)
emac_hal:emac_hal_rx_unavail_cb (noflash_text)

View File

@ -125,7 +125,7 @@ Currently, the following peripheral drivers are aware of DFS and will use the ``
The following drivers will hold the ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled:
- **SPI slave**: between calls to :cpp:func:`spi_slave_initialize` and :cpp:func:`spi_slave_free`.
- **Ethernet**: between calls to :cpp:func:`esp_eth_enable` and :cpp:func:`esp_eth_disable`.
- **Ethernet**: between calls to :cpp:func:`esp_eth_driver_install` and :cpp:func:`esp_eth_driver_uninstall`.
- **WiFi**: between calls to :cpp:func:`esp_wifi_start` and :cpp:func:`esp_wifi_stop`. If modem sleep is enabled, the lock will be released for the periods of time when radio is disabled.
- **Bluetooth**: between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`. If Bluetooth modem sleep is enabled, the ``ESP_PM_APB_FREQ_MAX`` lock will be released for the periods of time when radio is disabled. However the ``ESP_PM_NO_LIGHT_SLEEP`` lock will still be held, unless :ref:`CONFIG_BTDM_LOW_POWER_CLOCK` option is set to "External 32kHz crystal".
- **CAN**: between calls to :cpp:func:`can_driver_install` and :cpp:func:`can_driver_uninstall`.

View File

@ -111,7 +111,7 @@ ESP32 支持下表中所述的三种电源管理锁。
启用以下驱动程序时,将占用 ``ESP_PM_APB_FREQ_MAX`` 锁:
- **SPI slave**:从调用 :cpp:func:`spi_slave_initialize`:cpp:func:`spi_slave_free` 期间。
- **Ethernet**:从调用 :cpp:func:`esp_eth_enable`:cpp:func:`esp_eth_disable` 期间。
- **Ethernet**:从调用 :cpp:func:`esp_eth_driver_install`:cpp:func:`esp_eth_driver_uninstall` 期间。
- **WiFi**:从调用 :cpp:func:`esp_wifi_start`:cpp:func:`esp_wifi_stop` 期间。如果启用了调制解调器睡眠模式,广播关闭时将释放此管理锁。
- **Bluetooth**:从调用 :cpp:func:`esp_bt_controller_enable`:cpp:func:`esp_bt_controller_disable` 期间。如果启用了蓝牙调制解调器,广播关闭时将释放此管理锁。但依然占用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁。
- **CAN**:从调用 :cpp:func:`can_driver_install`:cpp:func:`can_driver_uninstall` 期间。

View File

@ -33,7 +33,7 @@ menu "Example Connection Configuration"
choice EXAMPLE_USE_ETHERNET
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32
default EXAMPLE_USE_DM9051 if !IDF_TARGET_ESP32
help
Select which kind of Ethernet will be used in the example.
@ -44,9 +44,10 @@ menu "Example Connection Configuration"
help
Select internal Ethernet MAC controller.
config EXAMPLE_USE_SPI_ETHERNET
bool "SPI Ethernet Module"
config EXAMPLE_USE_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module.
endchoice
@ -82,51 +83,84 @@ menu "Example Connection Configuration"
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
Goto http://www.ti.com/product/DP83848J for more information about it.
endchoice
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_ETH_SPI_HOST
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
config EXAMPLE_ETH_SCLK_GPIO
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ETH_MOSI_GPIO
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ETH_MISO_GPIO
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ETH_CS_GPIO
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_ETH_SPI_CLOCK_MHZ
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
endif
config EXAMPLE_CONNECT_IPV6

View File

@ -15,6 +15,7 @@
#include "esp_eth.h"
#include "esp_log.h"
#include "tcpip_adapter.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
@ -188,7 +189,11 @@ static void start()
#endif
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
s_mac = esp_eth_mac_new_esp32(&mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
s_phy = esp_eth_phy_new_ip101(&phy_config);
@ -199,33 +204,35 @@ static void start()
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
s_phy = esp_eth_phy_new_dp83848(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
#elif CONFIG_EXAMPLE_USE_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO,
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
s_mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
s_phy = esp_eth_phy_new_dm9051(&phy_config);
#endif
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle));
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
s_connection_name = "Ethernet";
}
@ -236,6 +243,7 @@ static void stop()
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6));
ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event));
#endif
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle));
ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle));
ESP_ERROR_CHECK(s_phy->del(s_phy));
ESP_ERROR_CHECK(s_mac->del(s_mac));

View File

@ -1,3 +1,66 @@
# Ethernet Examples
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
## Common Pin Assignments
### Using ESP32 internal MAC
* RMII PHY wiring is fixed and can not be changed through either IOMUX or GPIO Matrix. By default, they're connected as follows:
| GPIO | RMII Signal | Notes |
| ------ | ----------- | ------------ |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
* SMI (Serial Management Interface) wiring is not fixed. You may need to changed it according to your board schematic. By default they're connected as follows:
| GPIO | SMI Signal | Notes |
| ------ | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
* PHY chip has a reset pin, if want to do a hardware reset during initialization, then you have to connect it with one GPIO on ESP32. See more information from [here](#configure-the-project). The default GPIO used for resetting PHY chip is GPIO5.
### Using DM9051
* DM9051 Ethernet module consumes one SPI interface plus an interrupt and reset GPIO. By default they're connected as follows:
| GPIO | DM9051 |
| ------ | ----------- |
| GPIO19 | SPI_CLK |
| GPIO23 | SPI_MOSI |
| GPIO25 | SPI_MISO |
| GPIO22 | SPI_CS |
| GPIO4 | Interrupt |
| GPIO5 | Reset |
## Common Configurations
1. In the `Example Configuration` menu:
* Choose the kind of Ethernet under `Ethernet Type`.
* If `Internal EMAC` is selected:
* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board.
* Set GPIO number used by SMI signal under `SMI MDC GPIO number` and `SMI MDIO GPIO number` respectively.
* If `DM9051 Module` is selected:
* Set SPI specific configuration, including SPI host number, GPIO number and clock rate.
* Set GPIO number used by PHY chip reset under `PHY Reset GPIO number`, you may have to change the default value according to your board schematic. **PHY hardware reset can be disabled by set this value to -1**.
* Set PHY address under `PHY Address`, you may have to change the default value according to your board schematic.
2. In the `Component config > Ethernet` menu:
* Under `Support ESP32 internal EMAC controller` sub-menu:
* In the `PHY interface`, select `Reduced Media Independent Interface (RMII)`, ESP-IDF currently only support RMII mode.
* In the `RMII clock mode`, select one of the source that RMII clock (50MHz) comes from: `Input RMII clock from external` or `Output RMII clock from internal`.
* If `Output RMII clock from internal` is enabled, you also have to set the GPIO number that used to output the RMII clock, under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17.
* If `Output RMII clock from GPIO0 (Experimental!)` is also enabled, then you have no choice but GPIO0 to output the RMII clock.
* In `Amount of Ethernet DMA Rx buffers` and `Amount of Ethernet DMA Tx buffers`, you can set the amount of DMA buffers used for Tx and Rx.
* Under `Support SPI to Ethernet Module` sub-menu, select the SPI module that you used for this example. Currently ESP-IDF only supports `DM9051`.
## Common Troubleshooting
* The data panel between ESP32's MAC and PHY needs a fixed 50MHz clock to do synchronization, which also called RMII clock. It can either be provided by an external oscillator or generated from internal APLL. The signal integrity of RMII clock is strict, so keep the trace as short as possible!
* If the RMII clock is generated from internal APLL, then APLL can't be used for other purpose (e.g. I2S).

View File

@ -17,49 +17,29 @@ If you have a new Ethernet application to go (for example, connect to IoT cloud
To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
`esp_eth` component not only supports ESP32 internal Ethernet MAC controller, but also can drive third-party Ethernet module which integrates MAC and PHY and provides SPI interface. This example also take the **DM9051** as an example, illustrating how to install the Ethernet driver with only a little different configuration.
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the **DM9051** as an example, illustrating how to install the Ethernet driver in the same manner.
### Project configuration in menuconfig
#### Pin Assignment
Enter `idf.py menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system.
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
1. In the `Example Configuration` menu:
* Choose the kind of Ethernet this example will run on under `Ethernet Type`.
* If `Internal EMAC` is selected:
* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board.
### Configure the project
* If `SPI Ethernet Module` is selected:
* Set SPI specific configuration, including GPIO and clock speed.
```
idf.py menuconfig
```
2. In the `Component config > Ethernet` menu:
* If `Internal EMAC` is selected:
* Enable `Use ESP32 internal EMAC controller`, and then go into this menu.
* In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins.
* In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`.
* Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17.
* Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock.
* Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below:
See common configurations for Ethernet examples from [upper level](../README.md#common-configurations).
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
### Build, Flash, and Run
* If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`.
Build the project and flash it to the board, then run monitor tool to view serial output:
* If `SPI Ethernet Module` is selected:
* Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`.
```
idf.py -p PORT build flash monitor
```
### Extra configuration in the code (Optional)
* By default Ethernet driver will assume the PHY address to `1`, but you can alway reconfigure this value after `eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();`. The actual PHY address should depend on the hardware you use, so make sure to consult the schematic and datasheet.
**Note:** DM9051 has a fixed PHY address `1`, which cannot be modified.
### Build and Flash
Enter `idf.py -p PORT flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you' are using CMake based build system.
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
@ -84,20 +64,6 @@ Now you can ping your ESP32 in the terminal by entering `ping 192.168.2.151` (it
## Troubleshooting
* RMII Clock
* ESP32's MAC and the external PHY device need a common 50MHz reference clock (aka RMII clock). This clock can either be provided by an externally oscillator or generated from internal APLL. The signal integrity of RMII clock is strict, so it is highly recommended to add a 33Ω resistor in series to reduce possible ringing.
* ESP32 can generate a 50MHz clock using internal APLL. But if the APLL is already used for other purposes (e.g. I2S peripheral), then you have no choice but use an external RMII clock.
* GPIO connections
* RMII PHY wiring is fixed and can not be changed through either IOMUX or GPIO Matrix. They're described as below:
| GPIO | RMII Signal | ESP32 EMAC Function |
| ------ | ----------- | ------------------- |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@ -2,7 +2,7 @@ menu "Example Configuration"
choice EXAMPLE_USE_ETHERNET
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32
default EXAMPLE_USE_DM9051 if !IDF_TARGET_ESP32
help
Select which kind of Ethernet will be used in the example.
@ -13,9 +13,10 @@ menu "Example Configuration"
help
Select internal Ethernet MAC controller.
config EXAMPLE_USE_SPI_ETHERNET
bool "SPI Ethernet Module"
config EXAMPLE_USE_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module.
endchoice
@ -51,49 +52,82 @@ menu "Example Configuration"
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
Goto http://www.ti.com/product/DP83848J for more information about it.
endchoice
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_ETH_SPI_HOST
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
config EXAMPLE_ETH_SCLK_GPIO
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ETH_MOSI_GPIO
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ETH_MISO_GPIO
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ETH_CS_GPIO
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_ETH_SPI_CLOCK_MHZ
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
endmenu

View File

@ -14,6 +14,7 @@
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
static const char *TAG = "eth_example";
@ -73,7 +74,11 @@ void app_main()
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
@ -84,32 +89,34 @@ void app_main()
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
#elif CONFIG_EXAMPLE_USE_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO,
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
#endif
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
}

View File

@ -10,56 +10,39 @@ The similarities on MAC layer between Ethernet and Wi-Fi make it easy to forward
**Note:** In this example, ESP32 works like a *bridge* between Ethernet and Wi-Fi, and it won't perform any actions on Layer3 and higher layer, which means there's no need to initialize the TCP/IP stack.
## How to use this example
## How to use example
### Hardware Required
To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
`esp_eth` component not only supports ESP32 internal Ethernet MAC controller, but also can drive third-party Ethernet module which integrates MAC and PHY and provides SPI interface. This example also take the **DM9051** as an example, illustrating how to install the Ethernet driver with only a little different configuration.
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the **DM9051** as an example, illustrating how to install the Ethernet driver in the same manner.
### Project configuration in menuconfig
#### Pin Assignment
Open the project configuration menu (`idf.py menuconfig`).
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
1. In the `Example Configuration` menu:
* Set the SSID and password for Wi-Fi ap interface under `Wi-Fi SSID` and `Wi-Fi Password`.
* Set the maximum connection number under `Maximum STA connections`.
* Choose the kind of Ethernet this example will run on under `Ethernet Type`.
* If `Internal EMAC` is selected:
* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board.
### Configure the project
* If `SPI Ethernet Module` is selected:
* Set SPI specific configuration, including GPIO and clock speed.
```
idf.py menuconfig
```
2. In the `Component config > Ethernet` menu:
* If `Internal EMAC` is selected:
* Enable `Use ESP32 internal EMAC controller`, and then go into this menu.
* In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins.
* In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`.
* Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17.
* Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock.
* Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below:
In addition to the common configurations for Ethernet examples from [upper level](../README.md#common-configurations), you might also need to update the default value of following configurations:
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
In the `Example Configuration` menu:
* Set the SSID and password for Wi-Fi ap interface under `Wi-Fi SSID` and `Wi-Fi Password`.
* Set the maximum connection number under `Maximum STA connections`.
* If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`.
### Build, Flash, and Run
* If `SPI Ethernet Module` is selected:
* Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`.
Build the project and flash it to the board, then run monitor tool to view serial output:
### Extra configuration in the code (Optional)
```
idf.py -p PORT build flash monitor
```
* By default Ethernet driver will assume the PHY address to `1`, but you can alway reconfigure this value after `eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();`. The actual PHY address should depend on the hardware you use, so make sure to consult the schematic and datasheet.peripheral (e.g. I²S), you'd better choose the external clock.
**Note:** DM9051 has a fixed PHY address `1`, which cannot be modified.
### Build and Flash
To build and flash the example, enter `idf.py -p PORT flash monitor`.
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
@ -118,29 +101,11 @@ Now your mobile phone should get access to the Internet.
## Troubleshooting
* RMII Clock
* ESP32's MAC and the external PHY device need a common 50MHz reference clock (aka RMII clock). This clock can either be provided by an externally oscillator or generated from internal APLL. The signal integrity of RMII clock is strict, so it is highly recommended to add a 33Ω resistor in series to reduce possible ringing.
* ESP32 can generate a 50MHz clock using internal APLL. But if the APLL is already used for other purposes (e.g. I2S peripheral), then you have no choice but use an external RMII clock.
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
* GPIO connections
* RMII PHY wiring is fixed and can not be changed through either IOMUX or GPIO Matrix. They're described as below:
| GPIO | RMII Signal | ESP32 EMAC Function |
| ------ | ----------- | ------------------- |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
* Got error message `WiFi send packet failed` when running the example.
* Ethernet process packets faster than Wi-Fi on ESP32, so have a try to enlarge the value of `FLOW_CONTROL_WIFI_SEND_DELAY_MS`.
* Got error message `send flow control message failed or timeout` when running the example.
* Enlarge the length of `FLOW_CONTROL_QUEUE_LENGTH`.
* Wi-Fi station doesn't receive any IP via DHCP.
* If you got error message like `WiFi send packet failed` when running the example, you may need to enlarge the value of `FLOW_CONTROL_WIFI_SEND_DELAY_MS` in "ethernet_example_main.c", because Ethernet process packets faster than Wi-Fi on ESP32.
* If you got error message like `send flow control message failed or timeout` when running the example, you may need to enlarge the value of `FLOW_CONTROL_QUEUE_LENGTH` in "ethernet_example_main".
* Wi-Fi station doesn't receive any IP via DHCP?
* All Layer 3 (TCP/IP functions) on the ESP32 are disabled, including the SoftAP DHCP server. This means that devices must be able to access another DHCP server (for example on a Wi-Fi router connected via ethernet) or should use statically assigned IP addresses.
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@ -2,7 +2,7 @@ menu "Example Configuration"
choice EXAMPLE_USE_ETHERNET
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32
default EXAMPLE_USE_DM9051 if !IDF_TARGET_ESP32
help
Select which kind of Ethernet will be used in the example.
@ -13,9 +13,10 @@ menu "Example Configuration"
help
Select internal Ethernet MAC controller.
config EXAMPLE_USE_SPI_ETHERNET
bool "SPI Ethernet Module"
config EXAMPLE_USE_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module.
endchoice
@ -51,52 +52,85 @@ menu "Example Configuration"
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
Goto http://www.ti.com/product/DP83848J for more information about it.
endchoice
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_ETH_SPI_HOST
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
config EXAMPLE_ETH_SCLK_GPIO
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ETH_MOSI_GPIO
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ETH_MISO_GPIO
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ETH_CS_GPIO
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_ETH_SPI_CLOCK_MHZ
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
config EXAMPLE_WIFI_SSID
string "Wi-Fi SSID"
default "eth2ap"

View File

@ -146,7 +146,11 @@ static void initialize_ethernet(void)
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
@ -157,28 +161,29 @@ static void initialize_ethernet(void)
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
#elif CONFIG_EXAMPLE_USE_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO,
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
#endif
@ -186,6 +191,7 @@ static void initialize_ethernet(void)
config.stack_input = pkt_eth2wifi;
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle));
esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, (void *)true);
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
}
static void initialize_wifi(void)

View File

@ -14,56 +14,39 @@ The cli environment in the example is based on the [console component](https://d
To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). This example should also work for 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chip. Up until now, ESP-IDF supports up to four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
`esp_eth` component not only supports ESP32 internal Ethernet MAC controller, but also can drive third-party Ethernet module which integrates MAC and PHY and provides SPI interface. This example also take the **DM9051** as an example, illustrating how to install the Ethernet driver with only a little different configuration.
Besides that, `esp_eth` component can drive third-party Ethernet module which integrates MAC and PHY and provides common communication interface (e.g. SPI, USB, etc). This example will take the **DM9051** as an example, illustrating how to install the Ethernet driver in the same manner.
### Other Preparation
#### Pin Assignment
See common pin assignments for Ethernet examples from [upper level](../README.md#common-pin-assignments).
### Software Tools Preparation
1. Install iperf tool on PC
* Debian/Ubuntu: `sudo apt-get install iperf`
* macOS: `brew install iperf`(if using Homebrew) or `sudo port install iperf`(if using MacPorts)
* Windows(MSYS2): Downloads binaries from [here]( https://iperf.fr/iperf-download.php#windows)
### Project configuration in menuconfig
### Configure the project
Enter `idf.py menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system.
```
idf.py menuconfig
```
In addition to the common configurations for Ethernet examples from [upper level](../README.md#common-configurations), you might also need to update the default value of following configurations:
1. In the `Example Configuration` menu:
* Enable storing history commands in flash under `Store command history in flash`.
* Choose the kind of Ethernet this example will run on under `Ethernet Type`.
* If `Internal EMAC` is selected:
* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board.
* If `SPI Ethernet Module` is selected:
* Set SPI specific configuration, including GPIO and clock speed.
### Build, Flash, and Run
2. In the `Component config > Ethernet` menu:
* If `Internal EMAC` is selected:
* Enable `Use ESP32 internal EMAC controller`, and then go into this menu.
* In the `PHY interface`, it's highly recommended that you choose `Reduced Media Independent Interface (RMII)` which will cost fewer pins.
* In the `RMII clock mode`, you can choose the source of RMII clock (50MHz): `Input RMII clock from external` or `Output RMII clock from internal`.
* Once `Output RMII clock from internal` is enabled, you also have to set the number of the GPIO used for outputting the RMII clock under `RMII clock GPIO number`. In this case, you can set the GPIO number to 16 or 17.
* Once `Output RMII clock from GPIO0 (Experimental!)` is enabled, then you have no choice but GPIO0 to output the RMII clock.
* Set SMI MDC/MDIO GPIO number according to board schematic, by default these two GPIOs are set as below:
Build the project and flash it to the board, then run monitor tool to view serial output:
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| GPIO23 | MDC | Output to PHY |
| GPIO18 | MDIO | Bidirectional |
```
idf.py -p PORT build flash monitor
```
* If you have connect a GPIO to the PHY chip's RST pin, then you need to enable `Use Reset Pin of PHY Chip` and set the GPIO number under `PHY RST GPIO number`.
* If `SPI Ethernet Module` is selected:
* Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`.
### Extra configuration in the code (Optional)
* By default Ethernet driver will assume the PHY address to `1`, but you can alway reconfigure this value after `eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();`. The actual PHY address should depend on the hardware you use, so make sure to consult the schematic and datasheet.
**Note:** DM9051 has a fixed PHY address `1`, which cannot be modified.
### Build and Flash
Enter `idf.py -p PORT flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you' are using CMake based build system.
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
@ -168,20 +151,6 @@ I (2534456) iperf: want recv=16384
## Troubleshooting
* RMII Clock
* ESP32's MAC and the external PHY device need a common 50MHz reference clock (aka RMII clock). This clock can either be provided by an externally oscillator or generated from internal APLL. The signal integrity of RMII clock is strict, so it is highly recommended to add a 33Ω resistor in series to reduce possible ringing.
* ESP32 can generate a 50MHz clock using internal APLL. But if the APLL is already used for other purposes (e.g. I2S peripheral), then you have no choice but use an external RMII clock.
* GPIO connections
* RMII PHY wiring is fixed and can not be changed through either IOMUX or GPIO Matrix. They're described as below:
| GPIO | RMII Signal | ESP32 EMAC Function |
| ------ | ----------- | ------------------- |
| GPIO21 | TX_EN | EMAC_TX_EN |
| GPIO19 | TX0 | EMAC_TXD0 |
| GPIO22 | TX1 | EMAC_TXD1 |
| GPIO25 | RX0 | EMAC_RXD0 |
| GPIO26 | RX1 | EMAC_RXD1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV |
See common troubleshooting for Ethernet examples from [upper level](../README.md#common-troubleshooting).
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@ -10,7 +10,7 @@ menu "Example Configuration"
choice EXAMPLE_USE_ETHERNET
prompt "Ethernet Type"
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
default EXAMPLE_USE_SPI_ETHERNET if !IDF_TARGET_ESP32
default EXAMPLE_USE_DM9051 if !IDF_TARGET_ESP32
help
Select which kind of Ethernet will be used in the example.
@ -21,9 +21,10 @@ menu "Example Configuration"
help
Select internal Ethernet MAC controller.
config EXAMPLE_USE_SPI_ETHERNET
bool "SPI Ethernet Module"
config EXAMPLE_USE_DM9051
bool "DM9051 Module"
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module.
endchoice
@ -59,49 +60,82 @@ menu "Example Configuration"
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
Goto http://www.ti.com/product/DP83848J for more information about it.
endchoice
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
default 18
help
Set the GPIO number used by SMI MDIO.
endif
if EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_ETH_SPI_HOST
if EXAMPLE_USE_DM9051
config EXAMPLE_DM9051_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with DM9051.
config EXAMPLE_ETH_SCLK_GPIO
config EXAMPLE_DM9051_SCLK_GPIO
int "SPI SCLK GPIO number"
range 0 33
default 19
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ETH_MOSI_GPIO
config EXAMPLE_DM9051_MOSI_GPIO
int "SPI MOSI GPIO number"
range 0 33
default 23
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ETH_MISO_GPIO
config EXAMPLE_DM9051_MISO_GPIO
int "SPI MISO GPIO number"
range 0 33
default 25
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ETH_CS_GPIO
config EXAMPLE_DM9051_CS_GPIO
int "SPI CS GPIO number"
range 0 33
default 22
help
Set the GPIO number used by SPI CS.
config EXAMPLE_ETH_SPI_CLOCK_MHZ
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 20 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_DM9051_INT_GPIO
int "Interrupt GPIO number"
default 4
help
Set the GPIO number used by DM9051 interrupt.
endif
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
range 1 1 if !EXAMPLE_USE_INTERNAL_ETHERNET
default 1
help
Set PHY address according your board schematic.
endmenu

View File

@ -15,6 +15,7 @@
#include "esp_console.h"
#include "esp_event.h"
#include "esp_eth.h"
#include "driver/gpio.h"
#include "argtable3/argtable3.h"
#include "iperf.h"
#include "sdkconfig.h"
@ -184,7 +185,11 @@ void register_ethernet()
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
mac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
mac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
@ -195,33 +200,35 @@ void register_ethernet()
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config);
#endif
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
#elif CONFIG_EXAMPLE_USE_DM9051
gpio_install_isr_service(0);
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SCLK_GPIO,
.miso_io_num = CONFIG_EXAMPLE_DM9051_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_DM9051_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_DM9051_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_DM9051_SPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO,
.clock_speed_hz = CONFIG_EXAMPLE_DM9051_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_DM9051_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_DM9051_SPI_HOST, &devcfg, &spi_handle));
/* dm9051 ethernet driver is based on spi driver */
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
#endif
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
eth_control_args.control = arg_str1(NULL, NULL, "<info>", "Get info of Ethernet");
eth_control_args.end = arg_end(1);