|
@@ -1,16 +1,8 @@
|
|
|
-// Copyright 2019-2021 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.
|
|
|
|
|
|
|
+/*
|
|
|
|
|
+ * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
|
|
|
|
+ *
|
|
|
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
+ */
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <sys/cdefs.h>
|
|
|
#include <stdatomic.h>
|
|
#include <stdatomic.h>
|
|
@@ -20,7 +12,6 @@
|
|
|
#include "esp_event.h"
|
|
#include "esp_event.h"
|
|
|
#include "esp_heap_caps.h"
|
|
#include "esp_heap_caps.h"
|
|
|
#include "esp_timer.h"
|
|
#include "esp_timer.h"
|
|
|
-#include "soc/soc.h" // TODO: for esp_eth_ioctl API compatibility reasons, will be removed with next major release
|
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
|
#include "freertos/task.h"
|
|
#include "freertos/task.h"
|
|
|
|
|
|
|
@@ -49,6 +40,7 @@ typedef struct {
|
|
|
esp_eth_mac_t *mac;
|
|
esp_eth_mac_t *mac;
|
|
|
esp_timer_handle_t check_link_timer;
|
|
esp_timer_handle_t check_link_timer;
|
|
|
uint32_t check_link_period_ms;
|
|
uint32_t check_link_period_ms;
|
|
|
|
|
+ bool auto_nego_en;
|
|
|
eth_speed_t speed;
|
|
eth_speed_t speed;
|
|
|
eth_duplex_t duplex;
|
|
eth_duplex_t duplex;
|
|
|
eth_link_t link;
|
|
eth_link_t link;
|
|
@@ -221,6 +213,9 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
|
|
|
// init MAC first, so that MAC can generate the correct SMI signals
|
|
// init MAC first, so that MAC can generate the correct SMI signals
|
|
|
ESP_GOTO_ON_ERROR(mac->init(mac), err, TAG, "init mac failed");
|
|
ESP_GOTO_ON_ERROR(mac->init(mac), err, TAG, "init mac failed");
|
|
|
ESP_GOTO_ON_ERROR(phy->init(phy), err, TAG, "init phy failed");
|
|
ESP_GOTO_ON_ERROR(phy->init(phy), err, TAG, "init phy failed");
|
|
|
|
|
+ // get default status of PHY autonegotiation (ultimately may also indicate if it is supported)
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, ð_driver->auto_nego_en),
|
|
|
|
|
+ err, TAG, "get autonegotiation status failed");
|
|
|
ESP_LOGD(TAG, "new ethernet driver @%p", eth_driver);
|
|
ESP_LOGD(TAG, "new ethernet driver @%p", eth_driver);
|
|
|
*out_hdl = eth_driver;
|
|
*out_hdl = eth_driver;
|
|
|
|
|
|
|
@@ -275,7 +270,10 @@ esp_err_t esp_eth_start(esp_eth_handle_t hdl)
|
|
|
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
|
|
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
|
|
|
ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(ð_driver->fsm, &expected_fsm, ESP_ETH_FSM_START),
|
|
ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(ð_driver->fsm, &expected_fsm, ESP_ETH_FSM_START),
|
|
|
ESP_ERR_INVALID_STATE, err, TAG, "driver started already");
|
|
ESP_ERR_INVALID_STATE, err, TAG, "driver started already");
|
|
|
- ESP_GOTO_ON_ERROR(phy->negotiate(phy), err, TAG, "phy negotiation failed");
|
|
|
|
|
|
|
+ // Autonegotiate link speed and duplex mode when enabled
|
|
|
|
|
+ if (eth_driver->auto_nego_en == true) {
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_RESTART, ð_driver->auto_nego_en), err, TAG, "phy negotiation failed");
|
|
|
|
|
+ }
|
|
|
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "start mac failed");
|
|
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "start mac failed");
|
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, ð_driver, sizeof(esp_eth_driver_t *), 0),
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, ð_driver, sizeof(esp_eth_driver_t *), 0),
|
|
|
err, TAG, "send ETHERNET_EVENT_START event failed");
|
|
err, TAG, "send ETHERNET_EVENT_START event failed");
|
|
@@ -298,6 +296,7 @@ esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
|
|
|
ESP_ERR_INVALID_STATE, err, TAG, "driver not started yet");
|
|
ESP_ERR_INVALID_STATE, err, TAG, "driver not started yet");
|
|
|
ESP_GOTO_ON_ERROR(esp_timer_stop(eth_driver->check_link_timer), err, TAG, "stop link timer failed");
|
|
ESP_GOTO_ON_ERROR(esp_timer_stop(eth_driver->check_link_timer), err, TAG, "stop link timer failed");
|
|
|
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "stop mac failed");
|
|
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "stop mac failed");
|
|
|
|
|
+
|
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, ð_driver, sizeof(esp_eth_driver_t *), 0),
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, ð_driver, sizeof(esp_eth_driver_t *), 0),
|
|
|
err, TAG, "send ETHERNET_EVENT_STOP event failed");
|
|
err, TAG, "send ETHERNET_EVENT_STOP event failed");
|
|
|
err:
|
|
err:
|
|
@@ -361,46 +360,61 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
|
|
|
ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed");
|
|
ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed");
|
|
|
break;
|
|
break;
|
|
|
case ETH_CMD_S_PHY_ADDR:
|
|
case ETH_CMD_S_PHY_ADDR:
|
|
|
- if ((uint32_t)data >= SOC_DRAM_LOW) {
|
|
|
|
|
- ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed");
|
|
|
|
|
- } else { // TODO: for API compatibility reasons, will be removed with next major release
|
|
|
|
|
- ESP_GOTO_ON_ERROR(phy->set_addr(phy, (uint32_t)data), err, TAG, "set phy address failed");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed");
|
|
|
break;
|
|
break;
|
|
|
case ETH_CMD_G_PHY_ADDR:
|
|
case ETH_CMD_G_PHY_ADDR:
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store phy addr");
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store phy addr");
|
|
|
ESP_GOTO_ON_ERROR(phy->get_addr(phy, (uint32_t *)data), err, TAG, "get phy address failed");
|
|
ESP_GOTO_ON_ERROR(phy->get_addr(phy, (uint32_t *)data), err, TAG, "get phy address failed");
|
|
|
break;
|
|
break;
|
|
|
|
|
+ case ETH_CMD_S_AUTONEGO:
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set autonegotiation to null");
|
|
|
|
|
+ // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped");
|
|
|
|
|
+ if (*(bool *)data == true) {
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_EN, ð_driver->auto_nego_en), err, TAG, "phy negotiation enable failed");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_DIS, ð_driver->auto_nego_en), err, TAG, "phy negotiation disable failed");
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ETH_CMD_G_AUTONEGO:
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store autonegotiation configuration");
|
|
|
|
|
+ *(bool *)data = eth_driver->auto_nego_en;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ETH_CMD_S_SPEED:
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set speed to null");
|
|
|
|
|
+ // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped");
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(eth_driver->auto_nego_en == false, ESP_ERR_INVALID_STATE, err, TAG, "autonegotiation needs to be disabled to change this parameter");
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->set_speed(phy, *(eth_speed_t *)data), err, TAG, "set speed mode failed");
|
|
|
|
|
+ break;
|
|
|
case ETH_CMD_G_SPEED:
|
|
case ETH_CMD_G_SPEED:
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store speed value");
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store speed value");
|
|
|
*(eth_speed_t *)data = eth_driver->speed;
|
|
*(eth_speed_t *)data = eth_driver->speed;
|
|
|
break;
|
|
break;
|
|
|
case ETH_CMD_S_PROMISCUOUS:
|
|
case ETH_CMD_S_PROMISCUOUS:
|
|
|
- if ((uint32_t)data >= SOC_DRAM_LOW) {
|
|
|
|
|
- ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
|
|
|
|
|
- } else { // TODO: for API compatibility reasons, will be removed with next major release
|
|
|
|
|
- ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, (bool)data), err, TAG, "set promiscuous mode failed");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set promiscuous to null");
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
|
|
|
break;
|
|
break;
|
|
|
case ETH_CMD_S_FLOW_CTRL:
|
|
case ETH_CMD_S_FLOW_CTRL:
|
|
|
- if ((uint32_t)data >= SOC_DRAM_LOW) {
|
|
|
|
|
- ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
|
|
|
|
|
- ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed");
|
|
|
|
|
- } else { // TODO: for API compatibility reasons, will be removed with next major release
|
|
|
|
|
- ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, (bool)data), err, TAG, "enable mac flow control failed");
|
|
|
|
|
- ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, (uint32_t)data), err, TAG, "phy advertise pause ability failed");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set flow ctrl to null");
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed");
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ETH_CMD_S_DUPLEX_MODE:
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set duplex to null");
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(eth_driver->auto_nego_en == false, ESP_ERR_INVALID_STATE, err, TAG, "autonegotiation needs to be disabled to change this parameter");
|
|
|
|
|
+ // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped");
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->set_duplex(phy, *(eth_duplex_t *)data), err, TAG, "set duplex mode failed");
|
|
|
break;
|
|
break;
|
|
|
case ETH_CMD_G_DUPLEX_MODE:
|
|
case ETH_CMD_G_DUPLEX_MODE:
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value");
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value");
|
|
|
*(eth_duplex_t *)data = eth_driver->duplex;
|
|
*(eth_duplex_t *)data = eth_driver->duplex;
|
|
|
break;
|
|
break;
|
|
|
case ETH_CMD_S_PHY_LOOPBACK:
|
|
case ETH_CMD_S_PHY_LOOPBACK:
|
|
|
- if ((uint32_t)data >= SOC_DRAM_LOW) {
|
|
|
|
|
- ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
|
|
|
|
|
- } else { // TODO: for API compatibility reasons, will be removed with next major release
|
|
|
|
|
- ESP_GOTO_ON_ERROR(phy->loopback(phy, (bool)data), err, TAG, "configuration of phy loopback mode failed");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set loopback to null");
|
|
|
|
|
+ ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
|
|
|
|
|
+
|
|
|
break;
|
|
break;
|
|
|
default:
|
|
default:
|
|
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
|
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
|