-Subproject commit 471f03c2ca55225a600d3783dd277a910bfe80ed
+Subproject commit d915fc93499fd925ae301306a7539b5f13bf6e65
# driver is a public requirement because esp_sleep.h uses gpio_num_t & touch_pad_t
# app_update is added here because cpu_start.c uses esp_ota_get_app_description() function.
set(priv_requires app_trace app_update bootloader_support log mbedtls nvs_flash pthread
- smartconfig_ack spi_flash vfs wpa_supplicant espcoredump esp_common esp_wifi)
+ spi_flash vfs wpa_supplicant espcoredump esp_common esp_wifi)
set(fragments linker.lf ld/esp32_fragments.lf)
idf_component_register(SRCS "${srcs}"
#include "esp_intr_alloc.h"
#include "esp_attr.h"
#include "esp_log.h"
+#include "esp_event.h"
#include "esp_heap_caps.h"
#include "esp_private/wifi_os_adapter.h"
#include "esp_private/wifi.h"
#include "nvs.h"
#include "os.h"
#include "esp_smartconfig.h"
-#include "smartconfig_ack.h"
#include "esp_coexist_internal.h"
#include "esp_coexist_adapter.h"
return (int32_t)(configMAX_PRIORITIES);
}
+static int32_t esp_event_post_wrapper(const char* event_base, int32_t event_id, void* event_data, size_t event_data_size, uint32_t ticks_to_wait)
+{
+ if (ticks_to_wait == OSI_FUNCS_TIME_BLOCKING) {
+ return (int32_t)esp_event_post(event_base, event_id, event_data, event_data_size, portMAX_DELAY);
+ } else {
+ return (int32_t)esp_event_post(event_base, event_id, event_data, event_data_size, ticks_to_wait);
+ }
+}
+
static void IRAM_ATTR timer_arm_wrapper(void *timer, uint32_t tmout, bool repeat)
{
ets_timer_arm(timer, tmout, repeat);
return ptr;
}
-static void sc_ack_send_wrapper(void *param)
-{
- return sc_ack_send((sc_ack_t *)param);
-}
-
static uint32_t coex_status_get_wrapper(void)
{
#if CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE
._task_get_max_priority = task_get_max_priority_wrapper,
._malloc = malloc,
._free = free,
+ ._event_post = esp_event_post_wrapper,
._get_free_heap_size = esp_get_free_heap_size,
._rand = esp_random,
._dport_access_stall_other_cpu_start_wrap = esp_dport_access_stall_other_cpu_start_wrap,
._modem_sleep_exit = esp_modem_sleep_exit,
._modem_sleep_register = esp_modem_sleep_register,
._modem_sleep_deregister = esp_modem_sleep_deregister,
- ._sc_ack_send = sc_ack_send_wrapper,
- ._sc_ack_send_stop = sc_ack_send_stop,
._coex_status_get = coex_status_get_wrapper,
._coex_wifi_request = coex_wifi_request_wrapper,
._coex_wifi_release = coex_wifi_release_wrapper,
list(APPEND esp_eth_srcs "src/esp_eth_mac_esp32.c")
endif()
+if(CONFIG_ETH_SPI_ETHERNET_DM9051)
+ list(APPEND esp_eth_srcs "src/esp_eth_mac_dm9051.c"
+ "src/esp_eth_phy_dm9051.c")
+endif()
+
idf_component_register(SRCS "${esp_eth_srcs}"
INCLUDE_DIRS "include"
LDFRAGMENTS "linker.lf"
menuconfig ETH_USE_ESP32_EMAC
depends on IDF_TARGET_ESP32
- bool "Use ESP32 internal EMAC controller"
+ bool "Support ESP32 internal EMAC controller"
default y
help
ESP32 integrates a 10/100M Ethernet MAC controller.
Number of DMA transmit buffers. Each buffer's size is ETH_DMA_BUFFER_SIZE.
Larger number of buffers could increase throughput somehow.
endif
+
+ menuconfig ETH_USE_SPI_ETHERNET
+ bool "Support SPI to Ethernet Module"
+ default y
+ help
+ ESP-IDF can also support some SPI-Ethernet module.
+
+ if ETH_USE_SPI_ETHERNET
+ menuconfig 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
ifndef CONFIG_IDF_TARGET_ESP32
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_esp32.o
endif
+
+ifndef CONFIG_ETH_SPI_ETHERNET_DM9051
+ COMPONENT_OBJEXCLUDE += src/esp_eth_mac_dm9051.o src/esp_eth_phy_dm9051.o
+endif
#include <stdbool.h>
#include "esp_eth_com.h"
#include "sdkconfig.h"
+#if CONFIG_ETH_USE_SPI_ETHERNET
+#include "driver/gpio.h"
+#include "driver/spi_master.h"
+#endif
+
/**
* @brief Ethernet MAC
* - ESP_OK: read PHY register successfully
* - ESP_ERR_INVALID_ARG: read PHY register failed because of invalid argument
* - ESP_ERR_INVALID_STATE: read PHY register failed because of wrong state of MAC
+ * - ESP_ERR_TIMEOUT: read PHY register failed because of timeout
* - ESP_FAIL: read PHY register failed because some other error occurred
*
*/
* @return
* - ESP_OK: write PHY register successfully
* - ESP_ERR_INVALID_STATE: write PHY register failed because of wrong state of MAC
+ * - ESP_ERR_TIMEOUT: write PHY register failed because of timeout
* - ESP_FAIL: write PHY register failed because some other error occurred
*
*/
uint32_t rx_task_stack_size; /*!< Stack size of the receive task */
uint32_t rx_task_prio; /*!< Priority of the receive task */
uint32_t queue_len; /*!< Length of the transaction queue */
+#if CONFIG_ETH_USE_SPI_ETHERNET
+ spi_device_handle_t spi_hdl; /*!< Handle of spi device */
+#endif
+
} eth_mac_config_t;
/**
.queue_len = 100, \
}
+#if CONFIG_ETH_USE_ESP32_EMAC
/**
* @brief Create ESP32 Ethernet MAC instance
*
* - NULL: create MAC instance failed because some error occurred
*/
esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config);
+#endif
+#if CONFIG_ETH_SPI_ETHERNET_DM9051
+/**
+* @brief Create DM9051 Ethernet MAC instance
+*
+* @param config: Ethernet MAC configuration
+*
+* @return
+* - instance: create MAC instance successfully
+* - NULL: create MAC instance failed because some error occurred
+*/
+esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_mac_config_t *config);
+#endif
#ifdef __cplusplus
}
#endif
*/
esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config);
+#if CONFIG_ETH_SPI_ETHERNET_DM9051
+/**
+* @brief Create a PHY instance of DM9051
+*
+* @param[in] config: configuration of PHY
+*
+* @return
+* - instance: create PHY instance successfully
+* - NULL: create PHY instance failed because some error occurred
+*/
+esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
+#endif
#ifdef __cplusplus
}
#endif
[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)
+ if ETH_SPI_ETHERNET_DM9051 = y:
+ esp_eth_mac_dm9051:dm9051_isr_handler (noflash_text)
--- /dev/null
+// 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.
+#include <string.h>
+#include <stdlib.h>
+#include <sys/cdefs.h>
+#include "driver/gpio.h"
+#include "driver/spi_master.h"
+#include "esp_log.h"
+#include "esp_eth.h"
+#include "esp_system.h"
+#include "esp_intr_alloc.h"
+#include "esp_heap_caps.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include "sdkconfig.h"
+
+static const char *TAG = "emac_dm9051";
+#define MAC_CHECK(a, str, goto_tag, ret_value, ...) \
+ do \
+ { \
+ if (!(a)) \
+ { \
+ ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+ ret = ret_value; \
+ goto goto_tag; \
+ } \
+ } 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;
+ uint8_t length_low;
+ uint8_t length_high;
+} dm9051_rx_header_t;
+
+typedef struct {
+ esp_eth_mac_t parent;
+ esp_eth_mediator_t *eth;
+ spi_device_handle_t spi_hdl;
+ SemaphoreHandle_t spi_lock;
+ TaskHandle_t rx_task_hdl;
+ uint32_t sw_reset_timeout_ms;
+ uint8_t addr[6];
+ bool packets_remain;
+} emac_dm9051_t;
+
+static inline bool dm9051_lock(emac_dm9051_t *emac)
+{
+ return xSemaphoreTake(emac->spi_lock, DM9051_SPI_LOCK_TIMEOUT_MS) == pdTRUE;
+}
+
+static inline bool dm9051_unlock(emac_dm9051_t *emac)
+{
+ return xSemaphoreGive(emac->spi_lock) == pdTRUE;
+}
+
+/**
+ * @brief write value to dm9051 internal register
+ */
+static esp_err_t dm9051_register_write(emac_dm9051_t *emac, uint8_t reg_addr, uint8_t value)
+{
+ esp_err_t ret = ESP_OK;
+ spi_transaction_t trans = {
+ .cmd = DM9051_SPI_WR,
+ .addr = reg_addr,
+ .length = 8,
+ .flags = SPI_TRANS_USE_TXDATA
+ };
+ trans.tx_data[0] = value;
+ 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 value from dm9051 internal register
+ */
+static esp_err_t dm9051_register_read(emac_dm9051_t *emac, uint8_t reg_addr, uint8_t *value)
+{
+ esp_err_t ret = ESP_OK;
+ spi_transaction_t trans = {
+ .cmd = DM9051_SPI_RD,
+ .addr = reg_addr,
+ .length = 8,
+ .flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA
+ };
+ 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;
+ } else {
+ *value = trans.rx_data[0];
+ }
+ dm9051_unlock(emac);
+ } else {
+ ret = ESP_ERR_TIMEOUT;
+ }
+ return ret;
+}
+
+/**
+ * @brief write buffer to dm9051 internal memory
+ */
+static esp_err_t dm9051_memory_write(emac_dm9051_t *emac, uint8_t *buffer, uint32_t len)
+{
+ esp_err_t ret = ESP_OK;
+ spi_transaction_t trans = {
+ .cmd = DM9051_SPI_WR,
+ .addr = DM9051_MWCMD,
+ .length = len * 8,
+ .tx_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 buffer from dm9051 internal memory
+ */
+static esp_err_t dm9051_memory_read(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_MRCMD,
+ .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
+ */
+static esp_err_t dm9051_get_mac_addr(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ for (int i = 0; i < 6; i++) {
+ MAC_CHECK(dm9051_register_read(emac, DM9051_PAR + i, &emac->addr[i]) == ESP_OK, "read PAR failed", err, ESP_FAIL);
+ }
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief set new mac address to internal registers
+ */
+static esp_err_t dm9051_set_mac_addr(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ for (int i = 0; i < 6; i++) {
+ MAC_CHECK(dm9051_register_write(emac, DM9051_PAR + i, emac->addr[i]) == ESP_OK, "write PAR failed", err, ESP_FAIL);
+ }
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief clear multicast hash table
+ */
+static esp_err_t dm9051_clear_multicast_table(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ /* rx broadcast packet control by bit7 of MAC register 1DH */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_BCASTCR, 0x00) == ESP_OK, "write BCASTCR failed", err, ESP_FAIL);
+ for (int i = 0; i < 7; i++) {
+ MAC_CHECK(dm9051_register_write(emac, DM9051_MAR + i, 0x00) == ESP_OK, "write MAR failed", err, ESP_FAIL);
+ }
+ /* enable receive broadcast paclets */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_MAR + 7, 0x80) == ESP_OK, "write MAR failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief software reset dm9051 internal register
+ */
+static esp_err_t dm9051_reset(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ /* power on phy */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_GPR, 0x00) == ESP_OK, "write GPR failed", err, ESP_FAIL);
+ /* mac and phy register won't be accesable within at least 1ms */
+ vTaskDelay(pdMS_TO_TICKS(10));
+ /* software reset */
+ uint8_t ncr = NCR_RST;
+ MAC_CHECK(dm9051_register_write(emac, DM9051_NCR, ncr) == ESP_OK, "write NCR failed", err, ESP_FAIL);
+ uint32_t to = 0;
+ for (to = 0; to < emac->sw_reset_timeout_ms / 10; to++) {
+ MAC_CHECK(dm9051_register_read(emac, DM9051_NCR, &ncr) == ESP_OK, "read NCR failed", err, ESP_FAIL);
+ if (!(ncr & NCR_RST)) {
+ break;
+ }
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+ MAC_CHECK(to < emac->sw_reset_timeout_ms / 10, "reset timeout", err, ESP_ERR_TIMEOUT);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief verify dm9051 chip ID
+ */
+static esp_err_t dm9051_verify_id(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ 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(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);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief default setup for dm9051 internal registers
+ */
+static esp_err_t dm9051_setup_default(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ /* disable wakeup */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_NCR, 0x00) == ESP_OK, "write NCR failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_WCR, 0x00) == ESP_OK, "write WCR failed", err, ESP_FAIL);
+ /* stop transmitting, enable appending pad, crc for packets */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_TCR, 0x00) == ESP_OK, "write TCR failed", err, ESP_FAIL);
+ /* stop receiving, no promiscuous mode, no runt packet(size < 64bytes), not all multicast packets*/
+ /* discard long packet(size > 1522bytes) and crc error packet, enable watchdog */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC) == ESP_OK, "write RCR failed", err, ESP_FAIL);
+ /* send jam pattern (duration time = 1.15ms) when rx free space < 3k bytes */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_BPTR, 0x3F) == ESP_OK, "write BPTR failed", err, ESP_FAIL);
+ /* flow control: high water threshold = 3k bytes, low water threshold = 8k bytes */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_FCTR, 0x38) == ESP_OK, "write FCTR failed", err, ESP_FAIL);
+ /* enable flow control */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_FCR, FCR_FLOW_ENABLE) == ESP_OK, "write FCR failed", err, ESP_FAIL);
+ /* retry late collision packet, at most two transmit command can be issued before transmit complete */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_TCR2, TCR2_RLCP) == ESP_OK, "write TCR2 failed", err, ESP_FAIL);
+ /* enable auto transmit */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_ATCR, ATCR_AUTO_TX) == ESP_OK, "write ATCR failed", err, ESP_FAIL);
+ /* generate checksum for UDP, TCP and IPv4 packets */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_TCSCR, TCSCR_IPCSE | TCSCR_TCPCSE | TCSCR_UDPCSE) == ESP_OK,
+ "write TCSCR failed", err, ESP_FAIL);
+ /* disable check sum for receive packets */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_RCSCSR, 0x00) == ESP_OK, "write RCSCSR failed", err, ESP_FAIL);
+ /* interrupt pin config: push-pull output, active high */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_INTCR, 0x00) == ESP_OK, "write INTCR failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_INTCKCR, 0x00) == ESP_OK, "write INTCKCR failed", err, ESP_FAIL);
+ /* no length limitation for rx packets */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_RLENCR, 0x00) == ESP_OK, "write RLENCR failed", err, ESP_FAIL);
+ /* 3K-byte for TX and 13K-byte for RX */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_MEMSCR, 0x00) == ESP_OK, "write MEMSCR failed", err, ESP_FAIL);
+ /* reset tx and rx memory pointer */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX | MPTRCR_RST_TX) == ESP_OK,
+ "write MPTRCR failed", err, ESP_FAIL);
+ /* clear network status: wakeup event, tx complete */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END) == ESP_OK, "write NSR failed", err, ESP_FAIL);
+ /* clear interrupt status */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_ISR, ISR_CLR_STATUS) == ESP_OK, "write ISR failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief start dm9051: enable interrupt and start receive
+ */
+static esp_err_t dm9051_start(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ /* enable interrupt */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_IMR, IMR_ALL) == ESP_OK, "write IMR failed", err, ESP_FAIL);
+ /* enable rx */
+ uint8_t rcr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_RCR, &rcr) == ESP_OK, "read RCR failed", err, ESP_FAIL);
+ rcr |= RCR_RXEN;
+ MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, rcr) == ESP_OK, "write RCR failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+/**
+ * @brief stop dm9051: disable interrupt and stop receive
+ */
+static esp_err_t dm9051_stop(emac_dm9051_t *emac)
+{
+ esp_err_t ret = ESP_OK;
+ /* disable interrupt */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_IMR, 0x00) == ESP_OK, "write IMR failed", err, ESP_FAIL);
+ /* disable rx */
+ uint8_t rcr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_RCR, &rcr) == ESP_OK, "read RCR failed", err, ESP_FAIL);
+ rcr &= ~RCR_RXEN;
+ MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, rcr) == ESP_OK, "write RCR failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static void dm9051_isr_handler(void *arg)
+{
+ emac_dm9051_t *emac = (emac_dm9051_t *)arg;
+ BaseType_t high_task_wakeup = pdFALSE;
+ /* notify dm9051 task */
+ vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup);
+ if (high_task_wakeup != pdFALSE) {
+ portYIELD_FROM_ISR();
+ }
+}
+
+static void emac_dm9051_task(void *arg)
+{
+ emac_dm9051_t *emac = (emac_dm9051_t *)arg;
+ uint8_t status = 0;
+ 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) */
+ emac->eth->stack_input(emac->eth, buffer, length);
+ } else {
+ free(buffer);
+ }
+ } while (emac->packets_remain);
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+static esp_err_t emac_dm9051_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
+{
+ esp_err_t ret = ESP_OK;
+ MAC_CHECK(eth, "can't set mac's mediator to null", err, ESP_ERR_INVALID_ARG);
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ emac->eth = eth;
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_write_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ /* check if phy access is in progress */
+ uint8_t epcr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL);
+ MAC_CHECK(!(epcr & EPCR_ERRE), "phy is busy", err, ESP_ERR_INVALID_STATE);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_EPAR, (uint8_t)(((phy_addr << 6) & 0xFF) | phy_reg)) == ESP_OK,
+ "write EPAR failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_EPDRL, (uint8_t)(reg_value & 0xFF)) == ESP_OK,
+ "write EPDRL failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_EPDRH, (uint8_t)((reg_value >> 8) & 0xFF)) == ESP_OK,
+ "write EPDRH failed", err, ESP_FAIL);
+ /* select PHY and select write operation */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW) == ESP_OK, "write EPCR failed", err, ESP_FAIL);
+ /* polling the busy flag */
+ uint32_t to = 0;
+ do {
+ ets_delay_us(100);
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL);
+ to += 100;
+ } while ((epcr & EPCR_ERRE) && to < DM9051_PHY_OPERATION_TIMEOUT_US);
+ MAC_CHECK(!(epcr & EPCR_ERRE), "phy is busy", err, ESP_ERR_TIMEOUT);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_read_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value)
+{
+ esp_err_t ret = ESP_OK;
+ MAC_CHECK(reg_value, "can't set reg_value to null", err, ESP_ERR_INVALID_ARG);
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ /* check if phy access is in progress */
+ uint8_t epcr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL);
+ MAC_CHECK(!(epcr & 0x01), "phy is busy", err, ESP_ERR_INVALID_STATE);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_EPAR, (uint8_t)(((phy_addr << 6) & 0xFF) | phy_reg)) == ESP_OK,
+ "write EPAR failed", err, ESP_FAIL);
+ /* Select PHY and select read operation */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_EPCR, 0x0C) == ESP_OK, "write EPCR failed", err, ESP_FAIL);
+ /* polling the busy flag */
+ uint32_t to = 0;
+ do {
+ ets_delay_us(100);
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPCR, &epcr) == ESP_OK, "read EPCR failed", err, ESP_FAIL);
+ to += 100;
+ } while ((epcr & EPCR_ERRE) && to < DM9051_PHY_OPERATION_TIMEOUT_US);
+ MAC_CHECK(!(epcr & EPCR_ERRE), "phy is busy", err, ESP_ERR_TIMEOUT);
+ uint8_t value_h = 0;
+ uint8_t value_l = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPDRH, &value_h) == ESP_OK, "read EPDRH failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPDRL, &value_l) == ESP_OK, "read EPDRL failed", err, ESP_FAIL);
+ *reg_value = (value_h << 8) | value_l;
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_set_addr(esp_eth_mac_t *mac, uint8_t *addr)
+{
+ esp_err_t ret = ESP_OK;
+ MAC_CHECK(addr, "can't set mac addr to null", err, ESP_ERR_INVALID_ARG);
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ memcpy(emac->addr, addr, 6);
+ MAC_CHECK(dm9051_set_mac_addr(emac) == ESP_OK, "set mac address failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_get_addr(esp_eth_mac_t *mac, uint8_t *addr)
+{
+ esp_err_t ret = ESP_OK;
+ MAC_CHECK(addr, "can't set mac addr to null", err, ESP_ERR_INVALID_ARG);
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ memcpy(addr, emac->addr, 6);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_set_link(esp_eth_mac_t *mac, eth_link_t link)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ uint8_t nsr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_NSR, &nsr) == ESP_OK, "read NSR failed", err, ESP_FAIL);
+ switch (link) {
+ case ETH_LINK_UP:
+ MAC_CHECK(nsr & NSR_LINKST, "phy is not link up", err, ESP_ERR_INVALID_STATE);
+ MAC_CHECK(dm9051_start(emac) == ESP_OK, "dm9051 start failed", err, ESP_FAIL);
+ break;
+ case ETH_LINK_DOWN:
+ MAC_CHECK(!(nsr & NSR_LINKST), "phy is not link down", err, ESP_ERR_INVALID_STATE);
+ MAC_CHECK(dm9051_stop(emac) == ESP_OK, "dm9051 stop failed", err, ESP_FAIL);
+ break;
+ default:
+ MAC_CHECK(false, "unknown link status", err, ESP_ERR_INVALID_ARG);
+ break;
+ }
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ uint8_t nsr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_NSR, &nsr) == ESP_OK, "read NSR failed", err, ESP_FAIL);
+ switch (speed) {
+ case ETH_SPEED_10M:
+ MAC_CHECK(nsr & NSR_SPEED, "phy speed is not at 10Mbps", err, ESP_ERR_INVALID_STATE);
+ break;
+ case ETH_SPEED_100M:
+ MAC_CHECK(!(nsr & NSR_SPEED), "phy speed is not at 100Mbps", err, ESP_ERR_INVALID_STATE);
+ break;
+ default:
+ MAC_CHECK(false, "unknown speed", err, ESP_ERR_INVALID_ARG);
+ break;
+ }
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ uint8_t ncr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_NCR, &ncr) == ESP_OK, "read NCR failed", err, ESP_FAIL);
+ switch (duplex) {
+ case ETH_DUPLEX_HALF:
+ MAC_CHECK(!(ncr & NCR_FDX), "phy is not at half duplex", err, ESP_ERR_INVALID_STATE);
+ break;
+ case ETH_DUPLEX_FULL:
+ MAC_CHECK(ncr & NCR_FDX, "phy is not at full duplex", err, ESP_ERR_INVALID_STATE);
+ break;
+ default:
+ MAC_CHECK(false, "unknown duplex", err, ESP_ERR_INVALID_ARG);
+ break;
+ }
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_set_promiscuous(esp_eth_mac_t *mac, bool enable)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ uint8_t rcr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_EPDRL, &rcr) == ESP_OK, "read RCR failed", err, ESP_FAIL);
+ if (enable) {
+ rcr |= RCR_PRMSC;
+ } else {
+ rcr &= ~RCR_PRMSC;
+ }
+ MAC_CHECK(dm9051_register_write(emac, DM9051_RCR, rcr) == ESP_OK, "write RCR failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ MAC_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG);
+ MAC_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG);
+ /* Check if last transmit complete */
+ uint8_t tcr = 0;
+ MAC_CHECK(dm9051_register_read(emac, DM9051_TCR, &tcr) == ESP_OK, "read TCR failed", err, ESP_FAIL);
+ MAC_CHECK(!(tcr & TCR_TXREQ), "last transmit still in progress", err, ESP_ERR_INVALID_STATE);
+ /* set tx length */
+ MAC_CHECK(dm9051_register_write(emac, DM9051_TXPLL, length & 0xFF) == ESP_OK, "write TXPLL failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_write(emac, DM9051_TXPLH, (length >> 8) & 0xFF) == ESP_OK, "write TXPLH failed", err, ESP_FAIL);
+ /* copy data to tx memory */
+ MAC_CHECK(dm9051_memory_write(emac, buf, length) == ESP_OK, "write memory failed", err, ESP_FAIL);
+ /* issue tx polling command */
+ tcr |= TCR_TXREQ;
+ MAC_CHECK(dm9051_register_write(emac, DM9051_TCR, tcr) == ESP_OK, "write TCR failed", err, ESP_FAIL);
+ return ESP_OK;
+err:
+ return ret;
+}
+
+static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
+{
+ esp_err_t ret = ESP_OK;
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ MAC_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG);
+ uint8_t rxbyte = 0;
+ uint16_t rx_len = 0;
+ __attribute__((aligned(4))) dm9051_rx_header_t header; // SPI driver needs the rx buffer 4 byte align
+ emac->packets_remain = false;
+ /* dummy read, get the most updated data */
+ MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
+ /* rxbyte must be 0xFF, 0 or 1 */
+ 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);
+ 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);
+ rx_len = header.length_low + (header.length_high << 8);
+ 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)
+ /* dummy read, get the most updated data */
+ MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
+ MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
+ emac->packets_remain = rxbyte > 0;
+ }
+ return ESP_OK;
+err:
+ return ret;
+}
+
+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);
+ 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);
+ /* verify chip id */
+ MAC_CHECK(dm9051_verify_id(emac) == ESP_OK, "vefiry chip ID failed", err, ESP_FAIL);
+ /* default setup of internal registers */
+ MAC_CHECK(dm9051_setup_default(emac) == ESP_OK, "dm9051 default setup failed", err, ESP_FAIL);
+ /* clear multicast hash table */
+ MAC_CHECK(dm9051_clear_multicast_table(emac) == ESP_OK, "clear multicast table failed", err, ESP_FAIL);
+ /* get emac address from eeprom */
+ 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);
+ eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
+ return ret;
+}
+
+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);
+ eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
+ return ESP_OK;
+}
+
+static esp_err_t emac_dm9051_del(esp_eth_mac_t *mac)
+{
+ emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
+ vTaskDelete(emac->rx_task_hdl);
+ vSemaphoreDelete(emac->spi_lock);
+ free(emac);
+ return ESP_OK;
+}
+
+esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_mac_config_t *config)
+{
+ esp_eth_mac_t *ret = NULL;
+ MAC_CHECK(config, "can't set mac config to null", err, NULL);
+ MAC_CHECK(config->spi_hdl, "can't set spi handle to null", err, NULL);
+ emac_dm9051_t *emac = calloc(1, sizeof(emac_dm9051_t));
+ MAC_CHECK(emac, "calloc emac failed", err, NULL);
+ /* bind methods and attributes */
+ emac->sw_reset_timeout_ms = config->sw_reset_timeout_ms;
+ emac->spi_hdl = config->spi_hdl;
+ emac->parent.set_mediator = emac_dm9051_set_mediator;
+ emac->parent.init = emac_dm9051_init;
+ emac->parent.deinit = emac_dm9051_deinit;
+ emac->parent.del = emac_dm9051_del;
+ emac->parent.write_phy_reg = emac_dm9051_write_phy_reg;
+ emac->parent.read_phy_reg = emac_dm9051_read_phy_reg;
+ emac->parent.set_addr = emac_dm9051_set_addr;
+ emac->parent.get_addr = emac_dm9051_get_addr;
+ emac->parent.set_speed = emac_dm9051_set_speed;
+ emac->parent.set_duplex = emac_dm9051_set_duplex;
+ emac->parent.set_link = emac_dm9051_set_link;
+ emac->parent.set_promiscuous = emac_dm9051_set_promiscuous;
+ emac->parent.transmit = emac_dm9051_transmit;
+ emac->parent.receive = emac_dm9051_receive;
+ /* create mutex */
+ emac->spi_lock = xSemaphoreCreateMutex();
+ MAC_CHECK(emac->spi_lock, "create lock failed", err_lock, NULL);
+ /* create dm9051 task */
+ BaseType_t xReturned = xTaskCreate(emac_dm9051_task, "dm9051_tsk", config->rx_task_stack_size, emac,
+ config->rx_task_prio, &emac->rx_task_hdl);
+ MAC_CHECK(xReturned == pdPASS, "create dm9051 task failed", err_tsk, NULL);
+ return &(emac->parent);
+err_tsk:
+ vSemaphoreDelete(emac->spi_lock);
+err_lock:
+ free(emac);
+err:
+ return ret;
+}
} while (0)
#define RX_QUEUE_WAIT_MS (20)
+#define PHY_OPERATION_TIMEOUT_US (1000)
typedef struct {
esp_eth_mac_t parent;
intr_handle_t intr_hdl;
SemaphoreHandle_t rx_counting_sem;
TaskHandle_t rx_task_hdl;
- const char *name;
uint32_t sw_reset_timeout_ms;
uint32_t frames_remain;
- uint32_t rx_task_stack_size;
- uint32_t rx_task_prio;
- uint32_t queue_len;
uint8_t addr[6];
uint8_t *rx_buf[CONFIG_ETH_DMA_RX_BUFFER_NUM];
uint8_t *tx_buf[CONFIG_ETH_DMA_TX_BUFFER_NUM];
MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE);
emac_hal_set_phy_data(emac->hal, reg_value);
emac_hal_set_phy_cmd(emac->hal, phy_addr, phy_reg, true);
- /* Delay some time and Check for the Busy flag */
- ets_delay_us(1000);
- MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE);
+ /* polling the busy flag */
+ uint32_t to = 0;
+ bool busy = true;
+ do {
+ ets_delay_us(100);
+ busy = emac_hal_is_mii_busy(emac->hal);
+ to += 100;
+ } while (busy && to < PHY_OPERATION_TIMEOUT_US);
+ MAC_CHECK(!busy, "phy is busy", err, ESP_ERR_TIMEOUT);
return ESP_OK;
err:
return ret;
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE);
emac_hal_set_phy_cmd(emac->hal, phy_addr, phy_reg, false);
- /* Delay some time and Check for the Busy flag */
- ets_delay_us(1000);
- MAC_CHECK(!emac_hal_is_mii_busy(emac->hal), "phy is busy", err, ESP_ERR_INVALID_STATE);
+ /* polling the busy flag */
+ uint32_t to = 0;
+ bool busy = true;
+ do {
+ ets_delay_us(100);
+ busy = emac_hal_is_mii_busy(emac->hal);
+ to += 100;
+ } while (busy && to < PHY_OPERATION_TIMEOUT_US);
+ MAC_CHECK(!busy, "phy is busy", err, ESP_ERR_TIMEOUT);
/* Store value */
*reg_value = emac_hal_get_phy_data(emac->hal);
return ESP_OK;
static void emac_esp32_rx_task(void *arg)
{
emac_esp32_t *emac = (emac_esp32_t *)arg;
- esp_eth_mediator_t *eth = emac->eth;
uint8_t *buffer = NULL;
uint32_t length = 0;
-
while (1) {
if (xSemaphoreTake(emac->rx_counting_sem, pdMS_TO_TICKS(RX_QUEUE_WAIT_MS)) == pdTRUE) {
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) */
- eth->stack_input(eth, buffer, length);
+ emac->eth->stack_input(emac->eth, buffer, length);
} else {
free(buffer);
}
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);
- /* Interrupt configuration */
- MAC_CHECK(esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_hal_isr,
- &emac->hal, &(emac->intr_hdl)) == ESP_OK,
- "alloc emac interrupt failed", err, ESP_FAIL);
- /* create counting semaphore */
- emac->rx_counting_sem = xSemaphoreCreateCounting(emac->queue_len, 0);
- MAC_CHECK(emac->rx_counting_sem, "create semaphore failed", err_sem, ESP_FAIL);
- /* create rx task */
- BaseType_t xReturned = xTaskCreate(emac_esp32_rx_task, "emac_rx", emac->rx_task_stack_size, emac,
- emac->rx_task_prio, &emac->rx_task_hdl);
- MAC_CHECK(xReturned == pdPASS, "create emac_rx task failed", err_task, ESP_FAIL);
return ESP_OK;
-err_task:
- vSemaphoreDelete(emac->rx_counting_sem);
-err_sem:
- esp_intr_free(emac->intr_hdl);
err:
- periph_module_disable(PERIPH_EMAC_MODULE);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
+ periph_module_disable(PERIPH_EMAC_MODULE);
return ret;
}
static esp_err_t emac_esp32_deinit(esp_eth_mac_t *mac)
{
- esp_err_t ret = ESP_OK;
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);
#endif
emac_hal_stop(emac->hal);
- MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL) == ESP_OK, "lowlevel deinit failed", err, ESP_FAIL);
- MAC_CHECK(esp_intr_free(emac->intr_hdl) == ESP_OK, "free emac interrupt failed", err, ESP_FAIL);
- vTaskDelete(emac->rx_task_hdl);
- vSemaphoreDelete(emac->rx_counting_sem);
+ eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
periph_module_disable(PERIPH_EMAC_MODULE);
return ESP_OK;
-err:
- return ret;
}
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);
+ vTaskDelete(emac->rx_task_hdl);
+ vSemaphoreDelete(emac->rx_counting_sem);
int i = 0;
for (i = 0; i < CONFIG_ETH_DMA_RX_BUFFER_NUM; i++) {
free(emac->hal->rx_buf[i]);
/* 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->rx_task_stack_size = config->rx_task_stack_size;
- emac->rx_task_prio = config->rx_task_prio;
- emac->queue_len = config->queue_len;
- emac->name = "emac_esp32";
emac->parent.set_mediator = emac_esp32_set_mediator;
emac->parent.init = emac_esp32_init;
emac->parent.deinit = emac_esp32_deinit;
emac->parent.set_promiscuous = emac_esp32_set_promiscuous;
emac->parent.transmit = emac_esp32_transmit;
emac->parent.receive = emac_esp32_receive;
+ /* Interrupt configuration */
+ MAC_CHECK(esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_hal_isr,
+ &emac->hal, &(emac->intr_hdl)) == ESP_OK,
+ "alloc emac interrupt failed", err_intr, NULL);
+ /* create counting semaphore */
+ emac->rx_counting_sem = xSemaphoreCreateCounting(config->queue_len, 0);
+ MAC_CHECK(emac->rx_counting_sem, "create semaphore failed", err_sem, NULL);
+ /* 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);
return &(emac->parent);
+err_task:
+ vSemaphoreDelete(emac->rx_counting_sem);
+err_sem:
+ 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(emac->hal);
err_hal:
--- /dev/null
+// 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.
+#include <string.h>
+#include <stdlib.h>
+#include <sys/cdefs.h>
+#include "esp_log.h"
+#include "esp_eth.h"
+#include "eth_phy_regs_struct.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+static const char *TAG = "dm9051";
+#define PHY_CHECK(a, str, goto_tag, ...) \
+ do \
+ { \
+ if (!(a)) \
+ { \
+ ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+ goto goto_tag; \
+ } \
+ } while (0)
+
+/***************Vendor Specific Register***************/
+
+/**
+ * @brief DSCR(DAVICOM Specified Configuration Register)
+ *
+ */
+typedef union {
+ struct {
+ uint32_t reserved1 : 1; /* Reserved */
+ uint32_t sleep : 1; /* Set 1 to enable PHY into sleep mode */
+ uint32_t mfpsc : 1; /* MII frame preamble suppression control bit */
+ uint32_t smrst : 1; /* Set 1 to reset all state machines of PHY */
+ uint32_t rpdctr_en : 1; /* Set 1 to enable automatic reduced power down */
+ uint32_t reserved2 : 2; /* Reserved */
+ uint32_t flink100 : 1; /* Force Good Link in 100Mbps */
+ uint32_t reserved3 : 2; /* Reserved */
+ uint32_t tx_fx : 1; /* 100BASE-TX or FX Mode Control */
+ uint32_t reserved4 : 1; /* Reserved */
+ uint32_t bp_adpok : 1; /* BYPASS ADPOK */
+ uint32_t bp_align : 1; /* Bypass Symbol Alignment Function */
+ uint32_t bp_scr : 1; /* Bypass Scrambler/Descrambler Function */
+ uint32_t bp_4b5b : 1; /* Bypass 4B5B Encoding and 5B4B Decoding */
+ };
+ uint32_t val;
+} dscr_reg_t;
+#define ETH_PHY_DSCR_REG_ADDR (0x10)
+
+/**
+ * @brief DSCSR(DAVICOM Specified Configuration and Status Register)
+ *
+ */
+typedef union {
+ struct {
+ uint32_t anmb : 4; /* Auto-Negotiation Monitor Bits */
+ uint32_t phy_addr : 5; /* PHY Address */
+ uint32_t reserved : 3; /* Reserved */
+ uint32_t hdx10 : 1; /* 10M Half-Duplex Operation Mode */
+ uint32_t fdx10 : 1; /* 10M Full-Duplex Operation Mode */
+ uint32_t hdx100 : 1; /* 100M Half-Duplex Operation Mode */
+ uint32_t fdx100 : 1; /* 100M Full-Duplex Operation Mode */
+ };
+ uint32_t val;
+} dscsr_reg_t;
+#define ETH_PHY_DSCSR_REG_ADDR (0x11)
+
+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;
+} phy_dm9051_t;
+
+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_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
+ dm9051->eth = eth;
+ return ESP_OK;
+err:
+ return ESP_ERR_INVALID_ARG;
+}
+
+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;
+ }
+ }
+ return ESP_OK;
+err:
+ return ESP_FAIL;
+}
+
+static esp_err_t dm9051_reset(esp_eth_phy_t *phy)
+{
+ phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
+ 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);
+ 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);
+ 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);
+ /* 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);
+ if (!bmcr.reset && !dscr.smrst) {
+ break;
+ }
+ }
+ PHY_CHECK(to < dm9051->reset_timeout_ms / 10, "PHY reset timeout", err);
+ return ESP_OK;
+err:
+ return ESP_FAIL;
+}
+
+static esp_err_t dm9051_negotiate(esp_eth_phy_t *phy)
+{
+ phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
+ esp_eth_mediator_t *eth = dm9051->eth;
+ /* Start 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, 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);
+ if (bmsr.auto_nego_complete && dscsr.anmb & 0x08) {
+ break;
+ }
+ }
+ if (to >= dm9051->autonego_timeout_ms / 10) {
+ 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;
+ }
+ return ESP_OK;
+err:
+ return ESP_FAIL;
+}
+
+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);
+ if (!enable) {
+ /* Enable IEEE Power Down Mode */
+ bmcr.power_down = 1;
+ } else {
+ /* 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);
+ if (!enable) {
+ PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
+ } else {
+ PHY_CHECK(bmcr.power_down == 0, "power up failed", err);
+ }
+ return ESP_OK;
+err:
+ return ESP_FAIL;
+}
+
+static esp_err_t dm9051_set_addr(esp_eth_phy_t *phy, uint32_t addr)
+{
+ phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
+ dm9051->addr = addr;
+ return ESP_OK;
+}
+
+static esp_err_t dm9051_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
+{
+ PHY_CHECK(addr, "get phy address failed", err);
+ phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
+ *addr = dm9051->addr;
+ return ESP_OK;
+err:
+ return ESP_ERR_INVALID_ARG;
+}
+
+static esp_err_t dm9051_del(esp_eth_phy_t *phy)
+{
+ phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
+ free(dm9051);
+ return ESP_OK;
+}
+
+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);
+ /* Reset Ethernet PHY */
+ PHY_CHECK(dm9051_reset(phy) == ESP_OK, "reset Ethernet PHY 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);
+ return ESP_OK;
+err:
+ return ESP_FAIL;
+}
+
+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);
+ return ESP_OK;
+err:
+ return ESP_FAIL;
+}
+
+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";
+ dm9051->addr = config->phy_addr;
+ dm9051->reset_timeout_ms = config->reset_timeout_ms;
+ dm9051->link_status = ETH_LINK_DOWN;
+ dm9051->autonego_timeout_ms = config->autonego_timeout_ms;
+ dm9051->parent.reset = dm9051_reset;
+ dm9051->parent.init = dm9051_init;
+ dm9051->parent.deinit = dm9051_deinit;
+ dm9051->parent.set_mediator = dm9051_set_mediator;
+ dm9051->parent.negotiate = dm9051_negotiate;
+ dm9051->parent.get_link = dm9051_get_link;
+ dm9051->parent.pwrctl = dm9051_pwrctl;
+ 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;
+}
#include "test_utils.h"
#include "esp_eth.h"
#include "esp_log.h"
+#include "sdkconfig.h"
static const char *TAG = "esp_eth_test";
/** Event handler for Ethernet events */
TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle));
vTaskDelay(portMAX_DELAY);
}
+
+#if CONFIG_ETH_USE_SPI_ETHERNET
+TEST_CASE("dm9051 io test", "[ethernet][ignore]")
+{
+ spi_device_handle_t spi_handle = NULL;
+ spi_bus_config_t buscfg = {
+ .miso_io_num = 25,
+ .mosi_io_num = 23,
+ .sclk_io_num = 19,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ };
+ TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, 1));
+ spi_device_interface_config_t devcfg = {
+ .command_bits = 1,
+ .address_bits = 7,
+ .mode = 0,
+ .clock_speed_hz = 20 * 1000 * 1000,
+ .spics_io_num = 22,
+ .queue_size = 20
+ };
+ TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, &spi_handle));
+ gpio_install_isr_service(0);
+ tcpip_adapter_init();
+ 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, ð_event_handler, NULL));
+ TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
+ eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
+ mac_config.spi_hdl = spi_handle;
+ esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&mac_config);
+ eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
+ esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
+ 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, ð_handle));
+ vTaskDelay(pdMS_TO_TICKS(portMAX_DELAY));
+ TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
+ TEST_ESP_OK(phy->del(phy));
+ TEST_ESP_OK(mac->del(mac));
+ TEST_ESP_OK(esp_event_loop_delete_default());
+ TEST_ESP_OK(spi_bus_remove_device(spi_handle));
+ TEST_ESP_OK(spi_bus_free(HSPI_HOST));
+}
+#endif
"src/mesh_event.c"
"src/phy_init.c"
"src/restore.c"
+ "src/smartconfig.c"
"src/wifi_init.c"
INCLUDE_DIRS "include" "${idf_target}/include"
- PRIV_REQUIRES wpa_supplicant nvs_flash
+ PRIV_REQUIRES wpa_supplicant nvs_flash smartconfig_ack
LDFRAGMENTS "${ldfragments}")
idf_build_get_property(build_dir BUILD_DIR)
#include "esp_wifi_types.h"
#include "esp_event.h"
#include "esp_wifi.h"
+#include "esp_smartconfig.h"
#ifdef __cplusplus
extern "C" {
*/
esp_err_t esp_wifi_internal_set_fix_rate(wifi_interface_t ifx, bool en, wifi_phy_rate_t rate);
+/**
+ * @brief Start SmartConfig, config ESP device to connect AP. You need to broadcast information by phone APP.
+ * Device sniffer special packets from the air that containing SSID and password of target AP.
+ *
+ * @attention 1. This API can be called in station or softAP-station mode.
+ * @attention 2. Can not call esp_smartconfig_start twice before it finish, please call
+ * esp_smartconfig_stop first.
+ *
+ * @param config pointer to smartconfig start configure structure
+ *
+ * @return
+ * - ESP_OK: succeed
+ * - others: fail
+ */
+esp_err_t esp_smartconfig_internal_start(const smartconfig_start_config_t *config);
+
+/**
+ * @brief Stop SmartConfig, free the buffer taken by esp_smartconfig_start.
+ *
+ * @attention Whether connect to AP succeed or not, this API should be called to free
+ * memory taken by smartconfig_start.
+ *
+ * @return
+ * - ESP_OK: succeed
+ * - others: fail
+ */
+esp_err_t esp_smartconfig_internal_stop(void);
+
/**
* @brief Check the MD5 values of the OS adapter header files in IDF and WiFi library
*
extern "C" {
#endif
-#define ESP_WIFI_OS_ADAPTER_VERSION 0x00000002
+#define ESP_WIFI_OS_ADAPTER_VERSION 0x00000003
#define ESP_WIFI_OS_ADAPTER_MAGIC 0xDEADBEAF
#define OSI_FUNCS_TIME_BLOCKING 0xffffffff
int32_t (* _task_get_max_priority)(void);
void *(* _malloc)(uint32_t size);
void (* _free)(void *p);
+ int32_t (* _event_post)(const char* event_base, int32_t event_id, void* event_data, size_t event_data_size, uint32_t ticks_to_wait);
uint32_t (* _get_free_heap_size)(void);
uint32_t (* _rand)(void);
void (* _dport_access_stall_other_cpu_start_wrap)(void);
int32_t (* _modem_sleep_exit)(uint32_t module);
int32_t (* _modem_sleep_register)(uint32_t module);
int32_t (* _modem_sleep_deregister)(uint32_t module);
- void (* _sc_ack_send)(void *param);
- void (* _sc_ack_send_stop)(void);
uint32_t (* _coex_status_get)(void);
int32_t (* _coex_wifi_request)(uint32_t event, uint32_t latency, uint32_t duration);
int32_t (* _coex_wifi_release)(uint32_t event);
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
+#include "esp_event_base.h"
#ifdef __cplusplus
extern "C" {
#endif
-typedef enum {
- SC_STATUS_WAIT = 0, /**< Waiting to start connect */
- SC_STATUS_FIND_CHANNEL, /**< Finding target channel */
- SC_STATUS_GETTING_SSID_PSWD, /**< Getting SSID and password of target AP */
- SC_STATUS_LINK, /**< Connecting to target AP */
- SC_STATUS_LINK_OVER, /**< Connected to AP successfully */
-} smartconfig_status_t;
-
typedef enum {
SC_TYPE_ESPTOUCH = 0, /**< protocol: ESPTouch */
SC_TYPE_AIRKISS, /**< protocol: AirKiss */
SC_TYPE_ESPTOUCH_AIRKISS, /**< protocol: ESPTouch and AirKiss */
} smartconfig_type_t;
-/**
- * @brief The callback of SmartConfig, executed when smart-config status changed.
- *
- * @param status Status of SmartConfig:
- * - SC_STATUS_GETTING_SSID_PSWD : pdata is a pointer of smartconfig_type_t, means config type.
- * - SC_STATUS_LINK : pdata is a pointer to wifi_config_t.
- * - SC_STATUS_LINK_OVER : pdata is a pointer of phone's IP address(4 bytes) if pdata unequal NULL.
- * - otherwise : parameter void *pdata is NULL.
- * @param pdata According to the different status have different values.
- *
- */
-typedef void (*sc_callback_t)(smartconfig_status_t status, void *pdata);
+/** Smartconfig event declarations */
+typedef enum {
+ SC_EVENT_SCAN_DONE, /*!< ESP32 station smartconfig has finished to scan for APs */
+ SC_EVENT_FOUND_CHANNEL, /*!< ESP32 station smartconfig has found the channel of the target AP */
+ SC_EVENT_GOT_SSID_PSWD, /*!< ESP32 station smartconfig got the SSID and password */
+ SC_EVENT_SEND_ACK_DONE, /*!< ESP32 station smartconfig has sent ACK to cellphone */
+} smartconfig_event_t;
+
+/** @brief smartconfig event base declaration */
+ESP_EVENT_DECLARE_BASE(SC_EVENT);
+
+/** Argument structure for SC_EVENT_GOT_SSID_PSWD event */
+typedef struct {
+ uint8_t ssid[32]; /**< SSID of the AP. Null terminated string. */
+ uint8_t password[64]; /**< Password of the AP. Null terminated string. */
+ bool bssid_set; /**< whether set MAC address of target AP or not. */
+ uint8_t bssid[6]; /**< MAC address of target AP. */
+ smartconfig_type_t type; /**< Type of smartconfig(ESPTouch or AirKiss). */
+ uint8_t token; /**< Token from cellphone which is used to send ACK to cellphone. */
+ uint8_t cellphone_ip[4]; /**< IP address of cellphone. */
+} smartconfig_event_got_ssid_pswd_t;
+
+/** Configure structure for esp_smartconfig_start */
+typedef struct {
+ bool enable_log; /**< Enable smartconfig logs. */
+} smartconfig_start_config_t;
+
+#define SMARTCONFIG_START_CONFIG_DEFAULT() { \
+ .enable_log = false \
+};
/**
* @brief Get the version of SmartConfig.
* @attention 2. Can not call esp_smartconfig_start twice before it finish, please call
* esp_smartconfig_stop first.
*
- * @param cb SmartConfig callback function.
- * @param ... log 1: UART output logs; 0: UART only outputs the result.
+ * @param config pointer to smartconfig start configure structure
*
* @return
* - ESP_OK: succeed
* - others: fail
*/
-esp_err_t esp_smartconfig_start(sc_callback_t cb, ...);
+esp_err_t esp_smartconfig_start(const smartconfig_start_config_t *config);
/**
* @brief Stop SmartConfig, free the buffer taken by esp_smartconfig_start.
-Subproject commit 6579ef9a7ebdfa4196398056ee5337c300ca74e3
+Subproject commit 7657dd453d4349d03458b77c082acbfe6b6736c4
--- /dev/null
+// 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.
+
+#include <stdint.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_event_base.h"
+#include "esp_private/wifi.h"
+#include "esp_smartconfig.h"
+#include "smartconfig_ack.h"
+
+/* Smartconfig events definitions */
+ESP_EVENT_DEFINE_BASE(SC_EVENT);
+
+static const char *TAG = "smartconfig";
+
+static void handler_got_ssid_passwd(void *arg, esp_event_base_t base, int32_t event_id, void *data)
+{
+ smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)data;
+ uint8_t ssid[33] = { 0 };
+ uint8_t password[65] = { 0 };
+ uint8_t cellphone_ip[4];
+ esp_err_t err = ESP_OK;
+
+ memcpy(ssid, evt->ssid, sizeof(evt->ssid));
+ memcpy(password, evt->password, sizeof(evt->password));
+ memcpy(cellphone_ip, evt->cellphone_ip, sizeof(evt->cellphone_ip));
+
+ ESP_LOGD(TAG, "SSID:%s", ssid);
+ ESP_LOGD(TAG, "PASSWORD:%s", password);
+ ESP_LOGD(TAG, "Phone ip: %d.%d.%d.%d\n", cellphone_ip[0], cellphone_ip[1], cellphone_ip[2], cellphone_ip[3]);
+
+ err = sc_send_ack_start(evt->type, evt->token, evt->cellphone_ip);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Send smartconfig ACK error: %d", err);
+ }
+}
+
+esp_err_t esp_smartconfig_start(const smartconfig_start_config_t *config)
+{
+ esp_err_t err = ESP_OK;
+
+ err = esp_event_handler_register(SC_EVENT, SC_EVENT_GOT_SSID_PSWD, handler_got_ssid_passwd, NULL);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Register smartconfig default event handler fail!");
+ return err;
+ }
+
+ err = esp_smartconfig_internal_start(config);
+ if (err != ESP_OK) {
+ esp_event_handler_unregister(SC_EVENT, SC_EVENT_GOT_SSID_PSWD, handler_got_ssid_passwd);
+ }
+ return err;
+}
+
+esp_err_t esp_smartconfig_stop(void)
+{
+ esp_err_t err = ESP_OK;
+
+ err = esp_smartconfig_internal_stop();
+ if (err == ESP_OK) {
+ sc_send_ack_stop();
+ esp_event_handler_unregister(SC_EVENT, SC_EVENT_GOT_SSID_PSWD, handler_got_ssid_passwd);
+ }
+ return err;
+}
\ No newline at end of file
extern "C" {
#endif
-#define SC_ACK_TASK_PRIORITY 2 /*!< Priority of sending smartconfig ACK task */
-#define SC_ACK_TASK_STACK_SIZE 2048 /*!< Stack size of sending smartconfig ACK task */
-
-#define SC_ACK_TOUCH_SERVER_PORT 18266 /*!< ESP touch UDP port of server on cellphone */
-#define SC_ACK_AIRKISS_SERVER_PORT 10000 /*!< Airkiss UDP port of server on cellphone */
-
-#define SC_ACK_TOUCH_LEN 11 /*!< Length of ESP touch ACK context */
-#define SC_ACK_AIRKISS_LEN 7 /*!< Length of Airkiss ACK context */
-
-#define SC_ACK_MAX_COUNT 30 /*!< Maximum count of sending smartconfig ACK */
-
-/**
- * @brief Smartconfig ACK type.
- */
-typedef enum {
- SC_ACK_TYPE_ESPTOUCH = 0, /*!< ESP touch ACK type */
- SC_ACK_TYPE_AIRKISS, /*!< Airkiss ACK type */
-} sc_ack_type_t;
-
-/**
- * @brief Smartconfig parameters passed to sc_ack_send call.
- */
-typedef struct sc_ack {
- sc_ack_type_t type; /*!< Smartconfig ACK type */
- uint8_t *link_flag; /*!< Smartconfig link flag */
- sc_callback_t cb; /*!< Smartconfig callback function */
- struct {
- uint8_t token; /*!< Smartconfig token to be sent */
- uint8_t mac[6]; /*!< MAC address of station */
- uint8_t ip[4]; /*!< IP address of cellphone */
- } ctx;
-} sc_ack_t;
-
/**
* @brief Send smartconfig ACK to cellphone.
*
- * @attention The API is only used in libsmartconfig.a.
+ * @attention The API can only be used when receiving SC_EVENT_GOT_SSID_PSWD event.
*
- * @param param: smartconfig parameters;
+ * @param type: smartconfig type(ESPTouch or AirKiss);
+ * token: token from the cellphone;
+ * cellphone_ip: IP address of the cellphone;
+ *
+ * @retuen ESP_OK: succeed
+ * others: fail
*/
-void sc_ack_send(sc_ack_t *param);
+esp_err_t sc_send_ack_start(smartconfig_type_t type, uint8_t token, uint8_t *cellphone_ip);
/**
* @brief Stop sending smartconfig ACK to cellphone.
- *
- * @attention The API is only used in libsmartconfig.a.
*/
-void sc_ack_send_stop(void);
+void sc_send_ack_stop(void);
#ifdef __cplusplus
}
#include "tcpip_adapter.h"
#include "esp_log.h"
#include "esp_wifi.h"
+#include "esp_event.h"
#include "esp_smartconfig.h"
#include "smartconfig_ack.h"
+#define SC_ACK_TASK_PRIORITY 2 /*!< Priority of sending smartconfig ACK task */
+#define SC_ACK_TASK_STACK_SIZE 2048 /*!< Stack size of sending smartconfig ACK task */
+
+#define SC_ACK_TOUCH_SERVER_PORT 18266 /*!< ESP touch UDP port of server on cellphone */
+#define SC_ACK_AIRKISS_SERVER_PORT 10000 /*!< Airkiss UDP port of server on cellphone */
+
+#define SC_ACK_TOUCH_LEN 11 /*!< Length of ESP touch ACK context */
+#define SC_ACK_AIRKISS_LEN 7 /*!< Length of Airkiss ACK context */
+
+#define SC_ACK_MAX_COUNT 30 /*!< Maximum count of sending smartconfig ACK */
+
+/**
+ * @brief Smartconfig parameters passed to sc_ack_send call.
+ */
+typedef struct sc_ack {
+ smartconfig_type_t type; /*!< Smartconfig type(ESPTouch or AirKiss) */
+ struct {
+ uint8_t token; /*!< Smartconfig token from the cellphone */
+ uint8_t mac[6]; /*!< MAC address of station */
+ uint8_t ip[4]; /*!< IP address of cellphone */
+ } ctx;
+} sc_ack_t;
+
static const char *TAG = "smartconfig";
/* Flag to indicate sending smartconfig ACK or not. */
tcpip_adapter_ip_info_t local_ip;
uint8_t remote_ip[4];
memcpy(remote_ip, ack->ctx.ip, sizeof(remote_ip));
- int remote_port = (ack->type == SC_ACK_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_SERVER_PORT : SC_ACK_AIRKISS_SERVER_PORT;
+ int remote_port = (ack->type == SC_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_SERVER_PORT : SC_ACK_AIRKISS_SERVER_PORT;
struct sockaddr_in server_addr;
socklen_t sin_size = sizeof(server_addr);
int send_sock = -1;
int optval = 1;
int sendlen;
- int ack_len = (ack->type == SC_ACK_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_LEN : SC_ACK_AIRKISS_LEN;
+ int ack_len = (ack->type == SC_TYPE_ESPTOUCH) ? SC_ACK_TOUCH_LEN : SC_ACK_AIRKISS_LEN;
uint8_t packet_count = 1;
int err;
int ret;
ret = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &local_ip);
if ((ESP_OK == ret) && (local_ip.ip.addr != INADDR_ANY)) {
/* If ESP touch, smartconfig ACK contains local IP address. */
- if (ack->type == SC_ACK_TYPE_ESPTOUCH) {
+ if (ack->type == SC_TYPE_ESPTOUCH) {
memcpy(ack->ctx.ip, &local_ip.ip.addr, 4);
}
if (sendlen > 0) {
/* Totally send 30 smartconfig ACKs. Then smartconfig is successful. */
if (packet_count++ >= SC_ACK_MAX_COUNT) {
- if (ack->link_flag) {
- *ack->link_flag = 1;
- }
- if (ack->cb) {
- ack->cb(SC_STATUS_LINK_OVER, remote_ip);
- }
+ esp_event_post(SC_EVENT, SC_EVENT_SEND_ACK_DONE, NULL, 0, portMAX_DELAY);
goto _end;
}
}
vTaskDelete(NULL);
}
-void sc_ack_send(sc_ack_t *param)
+esp_err_t sc_send_ack_start(smartconfig_type_t type, uint8_t token, uint8_t *cellphone_ip)
{
sc_ack_t *ack = NULL;
- if (param == NULL) {
- ESP_LOGE(TAG, "Smart config ack parameter error");
- return;
+ if (cellphone_ip == NULL) {
+ ESP_LOGE(TAG, "Cellphone IP address is NULL");
+ return ESP_ERR_INVALID_ARG;
}
ack = malloc(sizeof(sc_ack_t));
if (ack == NULL) {
- ESP_LOGE(TAG, "Smart config ack parameter malloc fail");
- return;
+ ESP_LOGE(TAG, "ACK parameter malloc fail");
+ return ESP_ERR_NO_MEM;
}
- memcpy(ack, param, sizeof(sc_ack_t));
+ ack->type = type;
+ ack->ctx.token = token;
+ memcpy(ack->ctx.ip, cellphone_ip, 4);
s_sc_ack_send = true;
if (xTaskCreate(sc_ack_send_task, "sc_ack_send_task", SC_ACK_TASK_STACK_SIZE, ack, SC_ACK_TASK_PRIORITY, NULL) != pdPASS) {
ESP_LOGE(TAG, "Create sending smartconfig ACK task fail");
free(ack);
+ return ESP_ERR_NO_MEM;
}
+
+ return ESP_OK;
}
-void sc_ack_send_stop(void)
+void sc_send_ack_stop(void)
{
s_sc_ack_send = false;
}
hal->tx_desc[i].TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
/* Enable Ethernet DMA Tx Descriptor interrupt */
hal->tx_desc[1].TDES0.InterruptOnComplete = 1;
- /* Set the DMA Tx descriptors checksum insertion */
- hal->tx_desc[i].TDES0.ChecksumInsertControl = EMAC_DMATXDESC_CHECKSUM_TCPUDPICMPFULL;
/* Enable Transmit Timestamp */
hal->tx_desc[i].TDES0.TransmitTimestampEnable = 1;
/* point to the buffer */
menu "Example Connection Configuration"
-
choice EXAMPLE_CONNECT_INTERFACE
prompt "Connect using"
default EXAMPLE_CONNECT_WIFI
endchoice
- config EXAMPLE_WIFI_SSID
- depends on EXAMPLE_CONNECT_WIFI
- string "WiFi SSID"
- default "myssid"
- help
- SSID (network name) for the example to connect to.
-
- config EXAMPLE_WIFI_PASSWORD
- depends on EXAMPLE_CONNECT_WIFI
- string "WiFi Password"
- default "mypassword"
- help
- WiFi password (WPA or WPA2) for the example to use.
- Can be left blank if the network has no security set.
-
- choice EXAMPLE_ETH_PHY_MODEL
- depends on EXAMPLE_CONNECT_ETHERNET
- prompt "Ethernet PHY Device"
- default EXAMPLE_ETH_PHY_IP101
- help
- Select the PHY driver to use for the example.
-
- config EXAMPLE_ETH_PHY_IP101
- bool "IP101"
+ if EXAMPLE_CONNECT_WIFI
+ config EXAMPLE_WIFI_SSID
+ string "WiFi SSID"
+ default "myssid"
help
- IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
- Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+ SSID (network name) for the example to connect to.
- config EXAMPLE_ETH_PHY_RTL8201
- bool "RTL8201/SR8201"
+ config EXAMPLE_WIFI_PASSWORD
+ string "WiFi Password"
+ default "mypassword"
help
- RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
- Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
+ WiFi password (WPA or WPA2) for the example to use.
+ Can be left blank if the network has no security set.
+ endif
- config EXAMPLE_ETH_PHY_LAN8720
- bool "LAN8720"
+ if EXAMPLE_CONNECT_ETHERNET
+ 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
help
- LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
- Goto https://www.microchip.com/LAN8720A for more information about it.
+ Select which kind of Ethernet will be used in the example.
- config EXAMPLE_ETH_PHY_DP83848
- bool "DP83848"
- help
- DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
- Goto http://www.ti.com/product/DP83848J for more information about it.
+ config EXAMPLE_USE_INTERNAL_ETHERNET
+ depends on IDF_TARGET_ESP32
+ select ETH_USE_ESP32_EMAC
+ bool "Internal EMAC"
+ help
+ Select internal Ethernet MAC controller.
- endchoice
+ config EXAMPLE_USE_SPI_ETHERNET
+ bool "SPI Ethernet Module"
+ select ETH_USE_SPI_ETHERNET
+ help
+ Select external SPI-Ethernet module.
+ endchoice
+
+ if EXAMPLE_USE_INTERNAL_ETHERNET
+ choice EXAMPLE_ETH_PHY_MODEL
+ prompt "Ethernet PHY Device"
+ default EXAMPLE_ETH_PHY_IP101
+ help
+ Select the Ethernet PHY device to use in the example.
+
+ config EXAMPLE_ETH_PHY_IP101
+ bool "IP101"
+ help
+ IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
+ Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+
+ config EXAMPLE_ETH_PHY_RTL8201
+ bool "RTL8201/SR8201"
+ help
+ RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
+ Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
+
+ config EXAMPLE_ETH_PHY_LAN8720
+ bool "LAN8720"
+ help
+ LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
+ Goto https://www.microchip.com/LAN8720A for more information about it.
+
+ config EXAMPLE_ETH_PHY_DP83848
+ bool "DP83848"
+ help
+ 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
+ endif
+
+ if EXAMPLE_USE_SPI_ETHERNET
+ config EXAMPLE_ETH_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
+ int "SPI SCLK GPIO number"
+ range 0 33
+ default 19
+ help
+ Set the GPIO number used by SPI SCLK.
+
+ config EXAMPLE_ETH_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
+ int "SPI MISO GPIO number"
+ range 0 33
+ default 25
+ help
+ Set the GPIO number used by SPI MISO.
+
+ config EXAMPLE_ETH_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
+ int "SPI clock speed (MHz)"
+ range 20 80
+ default 20
+ help
+ Set the clock speed (MHz) of SPI interface.
+ endif
+ endif
config EXAMPLE_CONNECT_IPV6
bool "Obtain IPv6 link-local address"
help
By default, examples will wait until IPv4 and IPv6 addresses are obtained.
Disable this option if the network does not support IPv6.
-
endmenu
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL));
#endif
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
- s_mac = esp_eth_mac_new_esp32(&mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
+#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
+ s_mac = esp_eth_mac_new_esp32(&mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
s_phy = esp_eth_phy_new_ip101(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
s_phy = esp_eth_phy_new_lan8720(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
s_phy = esp_eth_phy_new_dp83848(&phy_config);
+#endif
+#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
+ 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,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ };
+ ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_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,
+ .queue_size = 20
+ };
+ ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
+ /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */
+ mac_config.spi_hdl = spi_handle;
+ s_mac = esp_eth_mac_new_dm9051(&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));
s_connection_name = "Ethernet";
}
### 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 four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
+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.
### Project configuration in menuconfig
Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system.
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.
-* 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.
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:
+
+ | Default Example GPIO | RMII Signal | Notes |
+ | -------------------- | ----------- | ------------- |
+ | GPIO23 | MDC | Output to PHY |
+ | GPIO18 | MDIO | Bidirectional |
-* 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:
+ * 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`.
- | Default Example GPIO | RMII Signal | Notes |
- | -------------------- | ----------- | ------------- |
- | GPIO23 | MDC | Output to PHY |
- | GPIO18 | MDIO | Bidirectional |
+ * If `SPI Ethernet Module` is selected:
+ * Set the GPIO number used by interrupt pin under `DM9051 Interrupt GPIO number`.
-* 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`.
### 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
## Example Output
```bash
-I (336) cpu_start: Starting scheduler on PRO CPU.
-I (0) cpu_start: Starting scheduler on APP CPU.
-I (382) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
-I (392) eth_example: Ethernet Started
-I (3922) eth_example: Ethernet Link Up
-I (5862) tcpip_adapter: eth ip: 192.168.2.151, mask: 255.255.255.0, gw: 192.168.2.2
-I (5862) eth_example: Ethernet Got IP Address
-I (5862) eth_example: ~~~~~~~~~~~
-I (5862) eth_example: ETHIP:192.168.2.151
-I (5872) eth_example: ETHMASK:255.255.255.0
-I (5872) eth_example: ETHGW:192.168.2.2
-I (5882) eth_example: ~~~~~~~~~~~
+I (394) eth_example: Ethernet Started
+I (3934) eth_example: Ethernet Link Up
+I (3934) eth_example: Ethernet HW Addr 30:ae:a4:c6:87:5b
+I (5864) tcpip_adapter: eth ip: 192.168.2.151, mask: 255.255.255.0, gw: 192.168.2.2
+I (5864) eth_example: Ethernet Got IP Address
+I (5864) eth_example: ~~~~~~~~~~~
+I (5864) eth_example: ETHIP:192.168.2.151
+I (5874) eth_example: ETHMASK:255.255.255.0
+I (5874) eth_example: ETHGW:192.168.2.2
+I (5884) eth_example: ~~~~~~~~~~~
```
+Now you can ping your ESP32 in the terminal by entering `ping 192.168.2.151` (it depends on the actual IP address you get).
+
## Troubleshooting
* RMII Clock
menu "Example Configuration"
-
- choice EXAMPLE_ETH_PHY_MODEL
- prompt "Ethernet PHY Device"
- default EXAMPLE_ETH_PHY_IP101
+ 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
help
- Select the Ethernet PHY device to use in the example.
+ Select which kind of Ethernet will be used in the example.
- config EXAMPLE_ETH_PHY_IP101
- bool "IP101"
+ config EXAMPLE_USE_INTERNAL_ETHERNET
+ depends on IDF_TARGET_ESP32
+ select ETH_USE_ESP32_EMAC
+ bool "Internal EMAC"
help
- IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
- Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+ Select internal Ethernet MAC controller.
- config EXAMPLE_ETH_PHY_RTL8201
- bool "RTL8201/SR8201"
+ config EXAMPLE_USE_SPI_ETHERNET
+ bool "SPI Ethernet Module"
+ select ETH_USE_SPI_ETHERNET
help
- RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
- Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
+ Select external SPI-Ethernet module.
+ endchoice
- config EXAMPLE_ETH_PHY_LAN8720
- bool "LAN8720"
+ if EXAMPLE_USE_INTERNAL_ETHERNET
+ choice EXAMPLE_ETH_PHY_MODEL
+ prompt "Ethernet PHY Device"
+ default EXAMPLE_ETH_PHY_IP101
help
- LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
- Goto https://www.microchip.com/LAN8720A for more information about it.
+ Select the Ethernet PHY device to use in the example.
+
+ config EXAMPLE_ETH_PHY_IP101
+ bool "IP101"
+ help
+ IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
+ Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+
+ config EXAMPLE_ETH_PHY_RTL8201
+ bool "RTL8201/SR8201"
+ help
+ RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
+ Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
- config EXAMPLE_ETH_PHY_DP83848
- bool "DP83848"
+ config EXAMPLE_ETH_PHY_LAN8720
+ bool "LAN8720"
+ help
+ LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
+ Goto https://www.microchip.com/LAN8720A for more information about it.
+
+ config EXAMPLE_ETH_PHY_DP83848
+ bool "DP83848"
+ help
+ 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
+ endif
+
+ if EXAMPLE_USE_SPI_ETHERNET
+ config EXAMPLE_ETH_SPI_HOST
+ int "SPI Host Number"
+ range 0 2
+ default 1
help
- 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
+ Set the SPI host used to communicate with DM9051.
+
+ config EXAMPLE_ETH_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
+ int "SPI MOSI GPIO number"
+ range 0 33
+ default 23
+ help
+ Set the GPIO number used by SPI MOSI.
+
+ config EXAMPLE_ETH_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
+ 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
+ int "SPI clock speed (MHz)"
+ range 20 80
+ default 20
+ help
+ Set the clock speed (MHz) of SPI interface.
+ endif
endmenu
#include "sdkconfig.h"
static const char *TAG = "eth_example";
-static esp_eth_handle_t s_eth_handle = NULL;
/** Event handler for Ethernet events */
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
+ uint8_t mac_addr[6] = {0};
+ /* we can get the ethernet driver handle from event data */
+ esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
+
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
+ esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
ESP_LOGI(TAG, "Ethernet Link Up");
+ ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
+ mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
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();
+#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
+ 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);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config);
#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
+ 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,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ };
+ ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_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,
+ .queue_size = 20
+ };
+ ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
+ /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */
+ mac_config.spi_hdl = spi_handle;
+ esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&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, &s_eth_handle));
+ esp_eth_handle_t eth_handle = NULL;
+ ESP_ERROR_CHECK(esp_eth_driver_install(&config, ð_handle));
}
### 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 four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
+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.
### Project configuration in menuconfig
Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system.
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.
-* Choose PHY device under `Ethernet PHY Device`, by default, the **ESP32-Ethernet-Kit** has an `IP101` on board.
-* 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 `SPI Ethernet Module` is selected:
+ * Set SPI specific configuration, including GPIO and clock speed.
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:
-* 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:
+ | Default Example GPIO | RMII Signal | Notes |
+ | -------------------- | ----------- | ------------- |
+ | GPIO23 | MDC | Output to PHY |
+ | GPIO18 | MDIO | Bidirectional |
- | Default Example GPIO | RMII Signal | Notes |
- | -------------------- | ----------- | ------------- |
- | GPIO23 | MDC | Output to PHY |
- | GPIO18 | MDIO | Bidirectional |
+ * 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 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.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
menu "Example Configuration"
-
- choice EXAMPLE_ETH_PHY_MODEL
- prompt "Ethernet PHY Device"
- default EXAMPLE_ETH_PHY_IP101
+ 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
help
- Select the Ethernet PHY device to use in the example.
+ Select which kind of Ethernet will be used in the example.
- config EXAMPLE_ETH_PHY_IP101
- bool "IP101"
+ config EXAMPLE_USE_INTERNAL_ETHERNET
+ depends on IDF_TARGET_ESP32
+ select ETH_USE_ESP32_EMAC
+ bool "Internal EMAC"
help
- IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
- Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+ Select internal Ethernet MAC controller.
- config EXAMPLE_ETH_PHY_RTL8201
- bool "RTL8201/SR8201"
+ config EXAMPLE_USE_SPI_ETHERNET
+ bool "SPI Ethernet Module"
+ select ETH_USE_SPI_ETHERNET
help
- RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
- Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
+ Select external SPI-Ethernet module.
+ endchoice
- config EXAMPLE_ETH_PHY_LAN8720
- bool "LAN8720"
+ if EXAMPLE_USE_INTERNAL_ETHERNET
+ choice EXAMPLE_ETH_PHY_MODEL
+ prompt "Ethernet PHY Device"
+ default EXAMPLE_ETH_PHY_IP101
help
- LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
- Goto https://www.microchip.com/LAN8720A for more information about it.
+ Select the Ethernet PHY device to use in the example.
+
+ config EXAMPLE_ETH_PHY_IP101
+ bool "IP101"
+ help
+ IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
+ Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+
+ config EXAMPLE_ETH_PHY_RTL8201
+ bool "RTL8201/SR8201"
+ help
+ RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
+ Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
+
+ config EXAMPLE_ETH_PHY_LAN8720
+ bool "LAN8720"
+ help
+ LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
+ Goto https://www.microchip.com/LAN8720A for more information about it.
- config EXAMPLE_ETH_PHY_DP83848
- bool "DP83848"
+ config EXAMPLE_ETH_PHY_DP83848
+ bool "DP83848"
+ help
+ 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
+ endif
+
+ if EXAMPLE_USE_SPI_ETHERNET
+ config EXAMPLE_ETH_SPI_HOST
+ int "SPI Host Number"
+ range 0 2
+ default 1
help
- 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
+ Set the SPI host used to communicate with DM9051.
+
+ config EXAMPLE_ETH_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
+ int "SPI MOSI GPIO number"
+ range 0 33
+ default 23
+ help
+ Set the GPIO number used by SPI MOSI.
+
+ config EXAMPLE_ETH_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
+ 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
+ int "SPI clock speed (MHz)"
+ range 20 80
+ default 20
+ help
+ Set the clock speed (MHz) of SPI interface.
+ endif
config EXAMPLE_WIFI_SSID
string "Wi-Fi SSID"
{
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();
- esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
+#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
+ 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);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config);
#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
+ 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,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ };
+ ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_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,
+ .queue_size = 20
+ };
+ ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
+ /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */
+ mac_config.spi_hdl = spi_handle;
+ esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&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);
config.stack_input = pkt_eth2wifi;
## How to use example
-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 four Ethernet PHY: `LAN8720`, `IP101`, `DP83848` and `RTL8201`, additional PHY drivers should be implemented by users themselves.
+### Hardware Required
-### Prepare work
+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.
+
+### Other Preparation
1. Install iperf tool on PC
* Debian/Ubuntu: `sudo apt-get install iperf`
Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you' are using CMake based build system.
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.
-* Enable storing history commands in flash under `Store command history in flash`.
-* 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.
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:
-* 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:
+ | Default Example GPIO | RMII Signal | Notes |
+ | -------------------- | ----------- | ------------- |
+ | GPIO23 | MDC | Output to PHY |
+ | GPIO18 | MDIO | Bidirectional |
- | Default Example GPIO | RMII Signal | Notes |
- | -------------------- | ----------- | ------------- |
- | GPIO23 | MDC | Output to PHY |
- | GPIO18 | MDIO | Bidirectional |
+ * 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 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 `make -j4 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.
------------------------------------------------------------
[ 3] local 192.168.2.160 port 5001 connected with 192.168.2.156 port 49154
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
-[ 3] 0.0- 3.0 sec 24.8 MBytes 69.5 Mbits/sec 0.172 ms 1/17721 (0.0056%)
-[ 3] 3.0- 6.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17719 (0%)
-[ 3] 6.0- 9.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17719 (0%)
-[ 3] 9.0-12.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17718 (0%)
-[ 3] 12.0-15.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17717 (0%)
-[ 3] 15.0-18.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17720 (0%)
-[ 3] 18.0-21.0 sec 24.8 MBytes 69.5 Mbits/sec 0.170 ms 0/17721 (0%)
-[ 3] 21.0-24.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17720 (0%)
-[ 3] 24.0-27.0 sec 24.8 MBytes 69.5 Mbits/sec 0.169 ms 0/17723 (0%)
+[ 3] 0.0- 3.0 sec 26.1 MBytes 72.8 Mbits/sec 0.198 ms 1/18585 (0.0054%)
+[ 3] 3.0- 6.0 sec 26.3 MBytes 73.7 Mbits/sec 0.192 ms 0/18792 (0%)
+[ 3] 6.0- 9.0 sec 26.3 MBytes 73.5 Mbits/sec 0.189 ms 0/18741 (0%)
+[ 3] 9.0-12.0 sec 26.2 MBytes 73.3 Mbits/sec 0.191 ms 43/18739 (0.23%)
+[ 3] 12.0-15.0 sec 26.3 MBytes 73.5 Mbits/sec 0.194 ms 0/18739 (0%)
+[ 3] 15.0-18.0 sec 26.3 MBytes 73.5 Mbits/sec 0.191 ms 0/18741 (0%)
+[ 3] 18.0-21.0 sec 26.3 MBytes 73.5 Mbits/sec 0.187 ms 0/18752 (0%)
+[ 3] 21.0-24.0 sec 26.3 MBytes 73.4 Mbits/sec 0.192 ms 0/18737 (0%)
+[ 3] 24.0-27.0 sec 26.3 MBytes 73.5 Mbits/sec 0.188 ms 0/18739 (0%)
```
#### ESP32 output
```bash
mode=udp-client sip=192.168.2.156:5001, dip=192.168.2.160:5001, interval=3, time=30
Interval Bandwidth
- 0- 3 sec 69.34 Mbits/sec
- 3- 6 sec 69.55 Mbits/sec
- 6- 9 sec 69.55 Mbits/sec
- 9- 12 sec 69.55 Mbits/sec
- 12- 15 sec 69.55 Mbits/sec
- 15- 18 sec 69.56 Mbits/sec
- 18- 21 sec 69.56 Mbits/sec
- 21- 24 sec 69.56 Mbits/sec
- 24- 27 sec 69.56 Mbits/sec
- 27- 30 sec 69.56 Mbits/sec
+ 0- 3 sec 72.92 Mbits/sec
+ 3- 6 sec 73.76 Mbits/sec
+ 6- 9 sec 73.56 Mbits/sec
+ 9- 12 sec 73.56 Mbits/sec
+ 12- 15 sec 73.56 Mbits/sec
+ 15- 18 sec 73.56 Mbits/sec
+ 18- 21 sec 73.61 Mbits/sec
+ 21- 24 sec 73.55 Mbits/sec
+ 24- 27 sec 73.56 Mbits/sec
+ 27- 30 sec 73.56 Mbits/sec
+ 0- 30 sec 73.52 Mbits/sec
```
### Test downlink bandwidth
[ 3] 3.0- 6.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 6.0- 9.0 sec 28.6 MBytes 80.0 Mbits/sec
[ 3] 9.0-12.0 sec 28.6 MBytes 80.0 Mbits/sec
-[ 3] 12.0-15.0 sec 28.6 MBytes 79.9 Mbits/sec
-[ 3] 15.0-18.0 sec 28.6 MBytes 80.0 Mbits/sec
-[ 3] 18.0-21.0 sec 28.6 MBytes 80.0 Mbits/sec
-[ 3] 21.0-24.0 sec 28.6 MBytes 80.0 Mbits/sec
+[ 3] 12.0-15.0 sec 28.4 MBytes 79.5 Mbits/sec
+[ 3] 15.0-18.0 sec 28.6 MBytes 79.9 Mbits/sec
+[ 3] 18.0-21.0 sec 28.6 MBytes 79.9 Mbits/sec
+[ 3] 21.0-24.0 sec 28.6 MBytes 79.9 Mbits/sec
[ 3] 24.0-27.0 sec 28.6 MBytes 80.0 Mbits/sec
+[ 3] 27.0-30.0 sec 28.5 MBytes 79.7 Mbits/sec
+[ 3] 0.0-30.0 sec 286 MBytes 79.9 Mbits/sec
```
#### ESP32 output
mode=udp-server sip=192.168.2.156:5001, dip=0.0.0.0:5001, interval=3, time=30
Interval Bandwidth
I (2534456) iperf: want recv=16384
- 0- 30 sec 72.67 Mbits/sec
- 3- 6 sec 74.18 Mbits/sec
- 6- 9 sec 73.14 Mbits/sec
- 9- 12 sec 73.65 Mbits/sec
- 12- 15 sec 72.87 Mbits/sec
- 15- 18 sec 73.29 Mbits/sec
- 18- 21 sec 74.35 Mbits/sec
- 21- 24 sec 72.28 Mbits/sec
- 24- 27 sec 73.39 Mbits/sec
- 27- 30 sec 73.49 Mbits/sec
+ 0- 3 sec 79.36 Mbits/sec
+ 3- 6 sec 79.56 Mbits/sec
+ 6- 9 sec 79.51 Mbits/sec
+ 9- 12 sec 79.24 Mbits/sec
+ 12- 15 sec 77.33 Mbits/sec
+ 15- 18 sec 79.01 Mbits/sec
+ 18- 21 sec 78.58 Mbits/sec
+ 21- 24 sec 78.24 Mbits/sec
+ 24- 27 sec 79.56 Mbits/sec
+ 27- 30 sec 77.20 Mbits/sec
+ 0- 30 sec 78.76 Mbits/sec
```
## Suggestions of getting higher bandwidth
menu "Example Configuration"
-
config EXAMPLE_STORE_HISTORY
bool "Store command history in flash"
default y
command history. If this option is enabled, initalizes a FAT filesystem
and uses it to store command history.
- choice EXAMPLE_ETH_PHY_MODEL
- prompt "Ethernet PHY Device"
- default EXAMPLE_ETH_PHY_IP101
+ 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
help
- Select the Ethernet PHY device to use in the example.
+ Select which kind of Ethernet will be used in the example.
- config EXAMPLE_ETH_PHY_IP101
- bool "IP101"
+ config EXAMPLE_USE_INTERNAL_ETHERNET
+ depends on IDF_TARGET_ESP32
+ select ETH_USE_ESP32_EMAC
+ bool "Internal EMAC"
help
- IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
- Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+ Select internal Ethernet MAC controller.
- config EXAMPLE_ETH_PHY_RTL8201
- bool "RTL8201/SR8201"
+ config EXAMPLE_USE_SPI_ETHERNET
+ bool "SPI Ethernet Module"
+ select ETH_USE_SPI_ETHERNET
help
- RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
- Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
+ Select external SPI-Ethernet module.
+ endchoice
- config EXAMPLE_ETH_PHY_LAN8720
- bool "LAN8720"
+ if EXAMPLE_USE_INTERNAL_ETHERNET
+ choice EXAMPLE_ETH_PHY_MODEL
+ prompt "Ethernet PHY Device"
+ default EXAMPLE_ETH_PHY_IP101
help
- LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
- Goto https://www.microchip.com/LAN8720A for more information about it.
+ Select the Ethernet PHY device to use in the example.
+
+ config EXAMPLE_ETH_PHY_IP101
+ bool "IP101"
+ help
+ IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
+ Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
+
+ config EXAMPLE_ETH_PHY_RTL8201
+ bool "RTL8201/SR8201"
+ help
+ RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
+ Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
- config EXAMPLE_ETH_PHY_DP83848
- bool "DP83848"
+ config EXAMPLE_ETH_PHY_LAN8720
+ bool "LAN8720"
+ help
+ LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
+ Goto https://www.microchip.com/LAN8720A for more information about it.
+
+ config EXAMPLE_ETH_PHY_DP83848
+ bool "DP83848"
+ help
+ 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
+ endif
+
+ if EXAMPLE_USE_SPI_ETHERNET
+ config EXAMPLE_ETH_SPI_HOST
+ int "SPI Host Number"
+ range 0 2
+ default 1
help
- 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
+ Set the SPI host used to communicate with DM9051.
+
+ config EXAMPLE_ETH_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
+ int "SPI MOSI GPIO number"
+ range 0 33
+ default 23
+ help
+ Set the GPIO number used by SPI MOSI.
+
+ config EXAMPLE_ETH_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
+ 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
+ int "SPI clock speed (MHz)"
+ range 20 80
+ default 20
+ help
+ Set the clock speed (MHz) of SPI interface.
+ endif
endmenu
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &event_handler, NULL));
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();
+#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
+ 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);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
esp_eth_phy_t *phy = esp_eth_phy_new_lan8720(&phy_config);
#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
+ 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,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ };
+ ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_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,
+ .queue_size = 20
+ };
+ ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
+ /* dm9051 ethernet driver is based on spi driver, so need to specify the spi handle */
+ mac_config.spi_hdl = spi_handle;
+ esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&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, ð_handle));
/* Initialize the console */
esp_console_config_t console_config = {
- .max_cmdline_args = 8,
+ .max_cmdline_args = 16,
.max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
-static const char *TAG = "sc";
+static const char *TAG = "smartconfig_example";
static void smartconfig_example_task(void * parm);
xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
+ } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
+ ESP_LOGI(TAG, "Scan done");
+ } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
+ ESP_LOGI(TAG, "Found channel");
+ } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
+ ESP_LOGI(TAG, "Got SSID and password");
+
+ smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
+ wifi_config_t wifi_config;
+ uint8_t ssid[33] = { 0 };
+ uint8_t password[65] = { 0 };
+
+ bzero(&wifi_config, sizeof(wifi_config_t));
+ memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
+ memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
+ wifi_config.sta.bssid_set = evt->bssid_set;
+ if (wifi_config.sta.bssid_set == true) {
+ memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
+ }
+
+ memcpy(ssid, evt->ssid, sizeof(evt->ssid));
+ memcpy(password, evt->password, sizeof(evt->password));
+ ESP_LOGI(TAG, "SSID:%s", ssid);
+ ESP_LOGI(TAG, "PASSWORD:%s", password);
+
+ ESP_ERROR_CHECK( esp_wifi_disconnect() );
+ ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+ ESP_ERROR_CHECK( esp_wifi_connect() );
+ } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
+ xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
}
}
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
+ ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
-static void sc_callback(smartconfig_status_t status, void *pdata)
-{
- switch (status) {
- case SC_STATUS_WAIT:
- ESP_LOGI(TAG, "SC_STATUS_WAIT");
- break;
- case SC_STATUS_FIND_CHANNEL:
- ESP_LOGI(TAG, "SC_STATUS_FINDING_CHANNEL");
- break;
- case SC_STATUS_GETTING_SSID_PSWD:
- ESP_LOGI(TAG, "SC_STATUS_GETTING_SSID_PSWD");
- break;
- case SC_STATUS_LINK:
- ESP_LOGI(TAG, "SC_STATUS_LINK");
- wifi_config_t *wifi_config = pdata;
- ESP_LOGI(TAG, "SSID:%s", wifi_config->sta.ssid);
- ESP_LOGI(TAG, "PASSWORD:%s", wifi_config->sta.password);
- ESP_ERROR_CHECK( esp_wifi_disconnect() );
- ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_config) );
- ESP_ERROR_CHECK( esp_wifi_connect() );
- break;
- case SC_STATUS_LINK_OVER:
- ESP_LOGI(TAG, "SC_STATUS_LINK_OVER");
- if (pdata != NULL) {
- uint8_t phone_ip[4] = { 0 };
- memcpy(phone_ip, (uint8_t* )pdata, 4);
- ESP_LOGI(TAG, "Phone ip: %d.%d.%d.%d\n", phone_ip[0], phone_ip[1], phone_ip[2], phone_ip[3]);
- }
- xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
- break;
- default:
- break;
- }
-}
-
static void smartconfig_example_task(void * parm)
{
EventBits_t uxBits;
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
- ESP_ERROR_CHECK( esp_smartconfig_start(sc_callback) );
+ smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
while (1) {
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
if(uxBits & CONNECTED_BIT) {
except (KeyError, TypeError):
pass
# branch name read from IDF
- git_describe = subprocess.check_output(["git", "describe", "--tags", "HEAD"])
- match = IDF_GIT_DESCRIBE_PATTERN.search(git_describe)
- if match:
- major_revision = match.group(1)
- minor_revision = match.group(2)
- # release branch
- candidates.append("release/v{}.{}".format(major_revision, minor_revision))
- # branch to match all major branches, like v3.x or v3
- candidates.append("release/v{}.x".format(major_revision))
- candidates.append("release/v{}".format(major_revision))
+ try:
+ git_describe = subprocess.check_output(["git", "describe", "--tags", "HEAD"])
+ match = IDF_GIT_DESCRIBE_PATTERN.search(git_describe.decode())
+ if match:
+ major_revision = match.group(1)
+ minor_revision = match.group(2)
+ # release branch
+ candidates.append("release/v{}.{}".format(major_revision, minor_revision))
+ # branch to match all major branches, like v3.x or v3
+ candidates.append("release/v{}.x".format(major_revision))
+ candidates.append("release/v{}".format(major_revision))
+ except subprocess.CalledProcessError:
+ # this should not happen as IDF should have describe message
+ pass
return [c for c in candidates if c] # filter out null value
input_text = input_text.encode()
result = subprocess.run(cmd, capture_output=True, check=True, input=input_text)
return result.stdout + result.stderr
- except AttributeError:
+ except (AttributeError, TypeError):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate(input_text)
if p.returncode != 0:
# should have one condition which is true
for min_range, max_range, cond_expr in sym.ranges:
if kconfiglib.expr_value(cond_expr):
- greatest_range = [int(min_range.str_value), int(max_range.str_value)]
+ base = 16 if sym.type == kconfiglib.HEX else 10
+ greatest_range = [int(min_range.str_value, base), int(max_range.str_value, base)]
+ break
new_json = {
"type": kconfiglib.TYPE_TO_STR[sym.type],
range 0 10
default 1
+ config TEST_CONDITIONAL_HEX_RANGES
+ hex "Something with a hex range"
+ range 0x00 0xaf if TEST_BOOL
+ range 0x10 0xaf
+ default 0xa0
+
config SUBMENU_TRIGGER
bool "I enable/disable some submenu items"
default y
* Set TEST_BOOL, showing child items
> { "TEST_BOOL" : true }
-< { "values" : { "TEST_BOOL" : true, "TEST_CHILD_STR" : "OHAI!", "TEST_CHILD_BOOL" : true }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 100]} }
+< { "values" : { "TEST_BOOL" : true, "TEST_CHILD_STR" : "OHAI!", "TEST_CHILD_BOOL" : true }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 100], "TEST_CONDITIONAL_HEX_RANGES": [0, 175]} }
* Set TEST_CHILD_STR
> { "TEST_CHILD_STR" : "Other value" }
* Clear TEST_BOOL, hiding child items
> { "TEST_BOOL" : false }
-< { "values" : { "TEST_BOOL" : false, "TEST_CHILD_STR" : null, "TEST_CHILD_BOOL" : null }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 10]} }
+< { "values" : { "TEST_BOOL" : false, "TEST_CHILD_STR" : null, "TEST_CHILD_BOOL" : null }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 10], "TEST_CONDITIONAL_HEX_RANGES": [16, 175]} }
* Set TEST_CHILD_BOOL, invalid as parent is disabled
> { "TEST_CHILD_BOOL" : false }
* Set TEST_BOOL, showing child items
> { "TEST_BOOL" : true }
-< { "values" : { "TEST_BOOL" : true, "TEST_CHILD_STR" : "OHAI!", "TEST_CHILD_BOOL" : true }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 100]}, "visible": {"TEST_CHILD_BOOL" : true, "TEST_CHILD_STR" : true} }
+< { "values" : { "TEST_BOOL" : true, "TEST_CHILD_STR" : "OHAI!", "TEST_CHILD_BOOL" : true }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 100], "TEST_CONDITIONAL_HEX_RANGES": [0, 175]}, "visible": {"TEST_CHILD_BOOL" : true, "TEST_CHILD_STR" : true} }
* Set TEST_CHILD_STR
> { "TEST_CHILD_STR" : "Other value" }
* Clear TEST_BOOL, hiding child items
> { "TEST_BOOL" : false }
-< { "values" : { "TEST_BOOL" : false }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 10]}, "visible": { "TEST_CHILD_BOOL" : false, "TEST_CHILD_STR" : false } }
+< { "values" : { "TEST_BOOL" : false }, "ranges": {"TEST_CONDITIONAL_RANGES": [0, 10], "TEST_CONDITIONAL_HEX_RANGES": [16, 175]}, "visible": { "TEST_CHILD_BOOL" : false, "TEST_CHILD_STR" : false } }
* Set TEST_CHILD_BOOL, invalid as parent is disabled
> { "TEST_CHILD_BOOL" : false }