2019-04-10 04:24:50 -04:00
|
|
|
// 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 <sys/cdefs.h>
|
2019-11-26 04:48:38 -05:00
|
|
|
#include <stdatomic.h>
|
2019-04-10 04:24:50 -04:00
|
|
|
#include "esp_log.h"
|
2021-04-01 08:00:54 -04:00
|
|
|
#include "esp_check.h"
|
2019-04-10 04:24:50 -04:00
|
|
|
#include "esp_eth.h"
|
|
|
|
#include "esp_event.h"
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "freertos/task.h"
|
|
|
|
#include "freertos/timers.h"
|
2020-11-05 23:00:07 -05:00
|
|
|
#include "esp_heap_caps.h"
|
2019-04-10 04:24:50 -04:00
|
|
|
|
|
|
|
static const char *TAG = "esp_eth";
|
|
|
|
|
|
|
|
ESP_EVENT_DEFINE_BASE(ETH_EVENT);
|
|
|
|
|
2020-05-09 00:49:29 -04:00
|
|
|
typedef enum {
|
|
|
|
ESP_ETH_FSM_STOP,
|
|
|
|
ESP_ETH_FSM_START
|
|
|
|
} esp_eth_fsm_t;
|
2019-12-03 02:37:34 -05:00
|
|
|
|
2019-04-10 04:24:50 -04:00
|
|
|
/**
|
|
|
|
* @brief The Ethernet driver mainly consists of PHY, MAC and
|
|
|
|
* the mediator who will handle the request/response from/to MAC, PHY and Users.
|
|
|
|
* Ethernet driver adopts an OS timer to check the link status periodically.
|
|
|
|
* This structure preserves some important Ethernet attributes (e.g. speed, duplex, link).
|
|
|
|
* Function stack_input is the channel which set by user, it will deliver all received packets.
|
|
|
|
* If stack_input is set to NULL, then all received packets will be passed to tcp/ip stack.
|
|
|
|
* on_lowlevel_init_done and on_lowlevel_deinit_done are callbacks set by user.
|
|
|
|
* In the callback, user can do any low level operations (e.g. enable/disable crystal clock).
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
esp_eth_mediator_t mediator;
|
|
|
|
esp_eth_phy_t *phy;
|
|
|
|
esp_eth_mac_t *mac;
|
|
|
|
TimerHandle_t check_link_timer;
|
|
|
|
eth_speed_t speed;
|
|
|
|
eth_duplex_t duplex;
|
|
|
|
eth_link_t link;
|
2019-11-26 04:48:38 -05:00
|
|
|
atomic_int ref_count;
|
|
|
|
void *priv;
|
2020-05-09 00:49:29 -04:00
|
|
|
_Atomic esp_eth_fsm_t fsm;
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_err_t (*stack_input)(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv);
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_err_t (*on_lowlevel_init_done)(esp_eth_handle_t eth_handle);
|
|
|
|
esp_err_t (*on_lowlevel_deinit_done)(esp_eth_handle_t eth_handle);
|
2021-04-15 04:17:08 -04:00
|
|
|
esp_err_t (*customized_read_phy_reg)(esp_eth_handle_t eth_handle, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value);
|
|
|
|
esp_err_t (*customized_write_phy_reg)(esp_eth_handle_t eth_handle, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value);
|
2019-04-10 04:24:50 -04:00
|
|
|
} esp_eth_driver_t;
|
|
|
|
|
|
|
|
////////////////////////////////Mediator Functions////////////////////////////////////////////
|
|
|
|
// Following functions are owned by mediator, which will get invoked by MAC or PHY.
|
|
|
|
// Mediator functions need to find the right actor (MAC, PHY or user) to perform the operation.
|
|
|
|
// So in the head of mediator function, we have to get the esp_eth_driver_t pointer.
|
|
|
|
// With this pointer, we could deliver the task to the real actor (MAC, PHY or user).
|
|
|
|
// This might sound excessive, but is helpful to separate the PHY with MAC (they can not contact with each other directly).
|
|
|
|
// For more details, please refer to WiKi. https://en.wikipedia.org/wiki/Mediator_pattern
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static esp_err_t eth_phy_reg_read(esp_eth_mediator_t *eth, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value)
|
|
|
|
{
|
|
|
|
esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
|
2021-04-15 04:17:08 -04:00
|
|
|
// invoking user customized PHY IO function if necessary
|
|
|
|
if (eth_driver->customized_read_phy_reg) {
|
|
|
|
return eth_driver->customized_read_phy_reg(eth_driver, phy_addr, phy_reg, reg_value);
|
|
|
|
}
|
|
|
|
// by default, PHY device is managed by MAC's SMI interface
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
return mac->read_phy_reg(mac, phy_addr, phy_reg, reg_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static esp_err_t eth_phy_reg_write(esp_eth_mediator_t *eth, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value)
|
|
|
|
{
|
|
|
|
esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
|
2021-04-15 04:17:08 -04:00
|
|
|
// invoking user customized PHY IO function if necessary
|
|
|
|
if (eth_driver->customized_write_phy_reg) {
|
|
|
|
return eth_driver->customized_write_phy_reg(eth_driver, phy_addr, phy_reg, reg_value);
|
|
|
|
}
|
|
|
|
// by default, PHY device is managed by MAC's SMI interface
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
return mac->write_phy_reg(mac, phy_addr, phy_reg, reg_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static esp_err_t eth_stack_input(esp_eth_mediator_t *eth, uint8_t *buffer, uint32_t length)
|
|
|
|
{
|
|
|
|
esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
|
2019-11-26 04:48:38 -05:00
|
|
|
if (eth_driver->stack_input) {
|
|
|
|
return eth_driver->stack_input((esp_eth_handle_t)eth_driver, buffer, length, eth_driver->priv);
|
2019-04-10 04:24:50 -04:00
|
|
|
} else {
|
2019-11-26 04:48:38 -05:00
|
|
|
free(buffer);
|
|
|
|
return ESP_OK;
|
2019-04-10 04:24:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static esp_err_t eth_on_state_changed(esp_eth_mediator_t *eth, esp_eth_state_t state, void *args)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
|
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
switch (state) {
|
|
|
|
case ETH_STATE_LLINIT: {
|
|
|
|
if (eth_driver->on_lowlevel_init_done) {
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(eth_driver->on_lowlevel_init_done(eth_driver), err, TAG, "extra lowlevel init failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ETH_STATE_DEINIT: {
|
|
|
|
if (eth_driver->on_lowlevel_deinit_done) {
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(eth_driver->on_lowlevel_deinit_done(eth_driver), err, TAG, "extra lowlevel deinit failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ETH_STATE_LINK: {
|
|
|
|
eth_link_t link = (eth_link_t)args;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->set_link(mac, link), err, TAG, "ethernet mac set link failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
eth_driver->link = link;
|
|
|
|
if (link == ETH_LINK_UP) {
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_CONNECTED, ð_driver, sizeof(esp_eth_driver_t *), 0), err,
|
|
|
|
TAG, "send ETHERNET_EVENT_CONNECTED event failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
} else if (link == ETH_LINK_DOWN) {
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, ð_driver, sizeof(esp_eth_driver_t *), 0), err,
|
|
|
|
TAG, "send ETHERNET_EVENT_DISCONNECTED event failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ETH_STATE_SPEED: {
|
|
|
|
eth_speed_t speed = (eth_speed_t)args;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->set_speed(mac, speed), err, TAG, "ethernet mac set speed failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
eth_driver->speed = speed;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ETH_STATE_DUPLEX: {
|
|
|
|
eth_duplex_t duplex = (eth_duplex_t)args;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->set_duplex(mac, duplex), err, TAG, "ethernet mac set duplex failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
eth_driver->duplex = duplex;
|
|
|
|
break;
|
|
|
|
}
|
2020-07-20 08:42:52 -04:00
|
|
|
case ETH_STATE_PAUSE: {
|
|
|
|
uint32_t peer_pause_ability = (uint32_t)args;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->set_peer_pause_ability(mac, peer_pause_ability), err, TAG, "ethernet mac set peer pause ability failed");
|
2020-07-20 08:42:52 -04:00
|
|
|
break;
|
|
|
|
}
|
2019-04-10 04:24:50 -04:00
|
|
|
default:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown ethernet state: %d", state);
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void eth_check_link_timer_cb(TimerHandle_t xTimer)
|
|
|
|
{
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)pvTimerGetTimerID(xTimer);
|
|
|
|
esp_eth_phy_t *phy = eth_driver->phy;
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_eth_increase_reference(eth_driver);
|
2019-04-10 04:24:50 -04:00
|
|
|
phy->get_link(phy);
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_eth_decrease_reference(eth_driver);
|
2019-09-13 09:08:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////User face APIs////////////////////////////////////////////////
|
|
|
|
// User has to pass the handle of Ethernet driver to each API.
|
|
|
|
// Different Ethernet driver instance is identified with a unique handle.
|
|
|
|
// It's helpful for us to support multiple Ethernet port on ESP32.
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
2019-06-28 10:47:34 -04:00
|
|
|
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_t *out_hdl)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(config, ESP_ERR_INVALID_ARG, err, TAG, "eth config can't be null");
|
|
|
|
ESP_GOTO_ON_FALSE(out_hdl, ESP_ERR_INVALID_ARG, err, TAG, "eth handle can't be null");
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = config->mac;
|
|
|
|
esp_eth_phy_t *phy = config->phy;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(mac && phy, ESP_ERR_INVALID_ARG, err, TAG, "can't set eth->mac or eth->phy to null");
|
2020-01-17 00:40:44 -05:00
|
|
|
// eth_driver contains an atomic variable, which should not be put in PSRAM
|
|
|
|
esp_eth_driver_t *eth_driver = heap_caps_calloc(1, sizeof(esp_eth_driver_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_NO_MEM, err, TAG, "request memory for eth_driver failed");
|
2019-11-26 04:48:38 -05:00
|
|
|
atomic_init(ð_driver->ref_count, 1);
|
2020-05-09 00:49:29 -04:00
|
|
|
atomic_init(ð_driver->fsm, ESP_ETH_FSM_STOP);
|
2019-04-10 04:24:50 -04:00
|
|
|
eth_driver->mac = mac;
|
|
|
|
eth_driver->phy = phy;
|
|
|
|
eth_driver->link = ETH_LINK_DOWN;
|
|
|
|
eth_driver->duplex = ETH_DUPLEX_HALF;
|
|
|
|
eth_driver->speed = ETH_SPEED_10M;
|
|
|
|
eth_driver->stack_input = config->stack_input;
|
|
|
|
eth_driver->on_lowlevel_init_done = config->on_lowlevel_init_done;
|
|
|
|
eth_driver->on_lowlevel_deinit_done = config->on_lowlevel_deinit_done;
|
2021-04-15 04:17:08 -04:00
|
|
|
eth_driver->customized_read_phy_reg = config->read_phy_reg;
|
|
|
|
eth_driver->customized_write_phy_reg = config->write_phy_reg;
|
2019-04-10 04:24:50 -04:00
|
|
|
eth_driver->mediator.phy_reg_read = eth_phy_reg_read;
|
|
|
|
eth_driver->mediator.phy_reg_write = eth_phy_reg_write;
|
|
|
|
eth_driver->mediator.stack_input = eth_stack_input;
|
|
|
|
eth_driver->mediator.on_state_changed = eth_on_state_changed;
|
2019-11-13 23:03:14 -05:00
|
|
|
/* some PHY can't output RMII clock if in reset state, so hardware reset PHY chip firstly */
|
|
|
|
phy->reset_hw(phy);
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->set_mediator(mac, ð_driver->mediator), err_mediator, TAG, "set mediator for mac failed");
|
|
|
|
ESP_GOTO_ON_ERROR(phy->set_mediator(phy, ð_driver->mediator), err_mediator, TAG, "set mediator for phy failed");
|
|
|
|
ESP_GOTO_ON_ERROR(mac->init(mac), err_init_mac, TAG, "init mac failed");
|
|
|
|
ESP_GOTO_ON_ERROR(phy->init(phy), err_init_phy, TAG, "init phy failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
eth_driver->check_link_timer = xTimerCreate("eth_link_timer", pdMS_TO_TICKS(config->check_link_period_ms), pdTRUE,
|
|
|
|
eth_driver, eth_check_link_timer_cb);
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver->check_link_timer, ESP_FAIL, err_create_timer, TAG, "create eth_link_timer failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
*out_hdl = (esp_eth_handle_t)eth_driver;
|
2019-11-26 04:48:38 -05:00
|
|
|
|
|
|
|
// for backward compatible to 4.0, and will get removed in 5.0
|
|
|
|
#if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
|
|
|
|
extern esp_err_t tcpip_adapter_compat_start_eth(void *eth_driver);
|
2019-10-23 10:47:16 -04:00
|
|
|
tcpip_adapter_compat_start_eth(eth_driver);
|
2019-11-26 04:48:38 -05:00
|
|
|
#endif
|
|
|
|
|
2019-04-10 04:24:50 -04:00
|
|
|
return ESP_OK;
|
|
|
|
err_create_timer:
|
|
|
|
phy->deinit(phy);
|
|
|
|
err_init_phy:
|
|
|
|
mac->deinit(mac);
|
|
|
|
err_init_mac:
|
|
|
|
err_mediator:
|
|
|
|
free(eth_driver);
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t esp_eth_driver_uninstall(esp_eth_handle_t hdl)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2020-05-09 00:49:29 -04:00
|
|
|
// check if driver has started
|
|
|
|
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
|
|
|
|
if (!atomic_compare_exchange_strong(ð_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP)) {
|
|
|
|
ESP_LOGW(TAG, "driver not stopped yet");
|
|
|
|
ret = ESP_ERR_INVALID_STATE;
|
|
|
|
goto err;
|
|
|
|
}
|
2019-11-26 04:48:38 -05:00
|
|
|
// don't uninstall driver unless there's only one reference
|
2020-05-09 00:49:29 -04:00
|
|
|
int expected_ref_count = 1;
|
|
|
|
if (!atomic_compare_exchange_strong(ð_driver->ref_count, &expected_ref_count, 0)) {
|
|
|
|
ESP_LOGE(TAG, "%d ethernet reference in use", expected_ref_count);
|
|
|
|
ret = ESP_ERR_INVALID_STATE;
|
|
|
|
goto err;
|
|
|
|
}
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
esp_eth_phy_t *phy = eth_driver->phy;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(xTimerDelete(eth_driver->check_link_timer, 0) == pdPASS, ESP_FAIL, err, TAG, "delete eth_link_timer failed");
|
|
|
|
ESP_GOTO_ON_ERROR(phy->deinit(phy), err, TAG, "deinit phy failed");
|
|
|
|
ESP_GOTO_ON_ERROR(mac->deinit(mac), err, TAG, "deinit mac failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
free(eth_driver);
|
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_err_t esp_eth_start(esp_eth_handle_t hdl)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-12-03 02:37:34 -05:00
|
|
|
// check if driver has started
|
2020-05-09 00:49:29 -04:00
|
|
|
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
|
|
|
|
if (!atomic_compare_exchange_strong(ð_driver->fsm, &expected_fsm, ESP_ETH_FSM_START)) {
|
2019-12-03 02:37:34 -05:00
|
|
|
ESP_LOGW(TAG, "driver started already");
|
|
|
|
ret = ESP_ERR_INVALID_STATE;
|
|
|
|
goto err;
|
|
|
|
}
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(eth_driver->phy->reset(eth_driver->phy), err, TAG, "reset phy failed");
|
|
|
|
ESP_GOTO_ON_FALSE(xTimerStart(eth_driver->check_link_timer, 0), ESP_FAIL, err, TAG, "start eth_link_timer failed");
|
|
|
|
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, ð_driver, sizeof(esp_eth_driver_t *), 0), err_event, TAG, "send ETHERNET_EVENT_START event failed");
|
2019-11-26 04:48:38 -05:00
|
|
|
return ESP_OK;
|
|
|
|
err_event:
|
|
|
|
xTimerStop(eth_driver->check_link_timer, 0);
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-12-03 02:37:34 -05:00
|
|
|
// check if driver has started
|
2020-05-09 00:49:29 -04:00
|
|
|
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_START;
|
|
|
|
if (!atomic_compare_exchange_strong(ð_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP)) {
|
2019-12-03 02:37:34 -05:00
|
|
|
ESP_LOGW(TAG, "driver not started yet");
|
|
|
|
ret = ESP_ERR_INVALID_STATE;
|
|
|
|
goto err;
|
|
|
|
}
|
2020-05-09 00:49:29 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "stop mac failed");
|
|
|
|
ESP_GOTO_ON_FALSE(xTimerStop(eth_driver->check_link_timer, 0), ESP_FAIL, err, TAG, "stop eth_link_timer failed");
|
|
|
|
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");
|
2019-11-26 04:48:38 -05:00
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t esp_eth_update_input_path(
|
|
|
|
esp_eth_handle_t hdl,
|
|
|
|
esp_err_t (*stack_input)(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv),
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2020-03-05 07:20:59 -05:00
|
|
|
eth_driver->priv = priv;
|
2019-11-26 04:48:38 -05:00
|
|
|
eth_driver->stack_input = stack_input;
|
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-11-05 23:00:07 -05:00
|
|
|
esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length)
|
2019-04-10 04:24:50 -04:00
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "can't set buf to null");
|
|
|
|
ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "buf length can't be zero");
|
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
return mac->transmit(mac, buf, length);
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(buf && length, ESP_ERR_INVALID_ARG, err, TAG, "can't set buf and length to null");
|
|
|
|
ESP_GOTO_ON_FALSE(*length > 60, ESP_ERR_INVALID_ARG, err, TAG, "length can't be less than 60");
|
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
return mac->receive(mac, buf, length);
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
|
|
|
|
{
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-04-10 04:24:50 -04:00
|
|
|
esp_eth_mac_t *mac = eth_driver->mac;
|
|
|
|
esp_eth_phy_t *phy = eth_driver->phy;
|
|
|
|
switch (cmd) {
|
|
|
|
case ETH_CMD_S_MAC_ADDR:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
|
|
|
|
ESP_GOTO_ON_ERROR(mac->set_addr(mac, (uint8_t *)data), err, TAG, "set mac address failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
|
|
|
case ETH_CMD_G_MAC_ADDR:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store mac addr");
|
|
|
|
ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
|
|
|
case ETH_CMD_S_PHY_ADDR:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set phy addr to null");
|
|
|
|
ESP_GOTO_ON_ERROR(phy->set_addr(phy, (uint32_t)data), err, TAG, "set phy address failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
|
|
|
case ETH_CMD_G_PHY_ADDR:
|
2021-04-01 08:00:54 -04:00
|
|
|
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");
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
|
|
|
case ETH_CMD_G_SPEED:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store speed value");
|
2019-04-10 04:24:50 -04:00
|
|
|
*(eth_speed_t *)data = eth_driver->speed;
|
|
|
|
break;
|
|
|
|
case ETH_CMD_S_PROMISCUOUS:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, (bool)data), err, TAG, "set promiscuous mode failed");
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
2020-07-20 08:42:52 -04:00
|
|
|
case ETH_CMD_S_FLOW_CTRL:
|
2021-04-01 08:00:54 -04:00
|
|
|
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");
|
2020-07-20 08:42:52 -04:00
|
|
|
break;
|
2021-01-20 05:02:23 -05:00
|
|
|
case ETH_CMD_G_DUPLEX_MODE:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value");
|
2021-01-20 05:02:23 -05:00
|
|
|
*(eth_duplex_t *)data = eth_driver->duplex;
|
|
|
|
break;
|
2019-04-10 04:24:50 -04:00
|
|
|
default:
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
|
2019-04-10 04:24:50 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
2019-06-28 10:47:34 -04:00
|
|
|
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_err_t esp_eth_increase_reference(esp_eth_handle_t hdl)
|
2019-06-28 10:47:34 -04:00
|
|
|
{
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-11-26 04:48:38 -05:00
|
|
|
atomic_fetch_add(ð_driver->ref_count, 1);
|
2019-06-28 10:47:34 -04:00
|
|
|
return ESP_OK;
|
2019-11-26 04:48:38 -05:00
|
|
|
err:
|
2019-06-28 10:47:34 -04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_err_t esp_eth_decrease_reference(esp_eth_handle_t hdl)
|
2019-06-28 10:47:34 -04:00
|
|
|
{
|
2019-11-26 04:48:38 -05:00
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
2021-04-01 08:00:54 -04:00
|
|
|
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
2019-11-26 04:48:38 -05:00
|
|
|
atomic_fetch_sub(ð_driver->ref_count, 1);
|
2019-06-28 10:47:34 -04:00
|
|
|
return ESP_OK;
|
2019-11-26 04:48:38 -05:00
|
|
|
err:
|
2019-06-28 10:47:34 -04:00
|
|
|
return ret;
|
|
|
|
}
|