2021-11-04 04:10:19 -04:00
/*
2022-01-26 03:55:36 -05:00
* SPDX - FileCopyrightText : 2019 - 2022 Espressif Systems ( Shanghai ) CO LTD
2021-11-04 04:10:19 -04:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
2021-04-19 03:05:29 -04:00
2019-04-10 04:24:50 -04:00
# 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"
2021-04-19 03:05:29 -04:00
# include "esp_heap_caps.h"
# include "esp_timer.h"
2019-04-10 04:24:50 -04:00
# include "freertos/FreeRTOS.h"
# include "freertos/task.h"
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
/**
* @ brief Transmit timeout when multiple accesses to network driver
*/
# define ESP_ETH_TX_TIMEOUT_MS 250
# endif
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 ;
2021-04-19 03:05:29 -04:00
esp_timer_handle_t check_link_timer ;
uint32_t check_link_period_ms ;
2021-11-04 04:10:19 -04:00
bool auto_nego_en ;
2019-04-10 04:24:50 -04:00
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 ;
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
SemaphoreHandle_t transmit_mutex ;
# endif // CONFIG_ETH_TRANSMIT_MUTEX
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
}
2021-04-19 03:05:29 -04:00
// No stack input path has been installed, just drop the incoming packets
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 , & eth_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 , & eth_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 ;
}
err :
return ret ;
}
2021-04-19 03:05:29 -04:00
static void eth_check_link_timer_cb ( void * args )
2019-04-10 04:24:50 -04:00
{
2021-04-19 03:05:29 -04:00
esp_eth_driver_t * eth_driver = ( esp_eth_driver_t * ) args ;
2019-04-10 04:24:50 -04:00
esp_eth_phy_t * phy = eth_driver - > phy ;
phy - > get_link ( phy ) ;
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-19 03:05:29 -04:00
esp_eth_mac_t * mac = NULL ;
esp_eth_phy_t * phy = NULL ;
esp_eth_driver_t * eth_driver = NULL ;
ESP_GOTO_ON_FALSE ( config & & out_hdl , ESP_ERR_INVALID_ARG , err , TAG , " invalid argument " ) ;
mac = config - > mac ;
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
2021-04-19 03:05:29 -04:00
eth_driver = heap_caps_calloc ( 1 , sizeof ( esp_eth_driver_t ) , MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT ) ;
ESP_GOTO_ON_FALSE ( eth_driver , ESP_ERR_NO_MEM , err , TAG , " no mem for eth_driver " ) ;
const esp_timer_create_args_t check_link_timer_args = {
. callback = eth_check_link_timer_cb ,
. name = " eth_link_timer " ,
. arg = eth_driver ,
. skip_unhandled_events = true
} ;
ESP_GOTO_ON_ERROR ( esp_timer_create ( & check_link_timer_args , & eth_driver - > check_link_timer ) , err , TAG , " create link timer failed " ) ;
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
eth_driver - > transmit_mutex = xSemaphoreCreateMutex ( ) ;
ESP_GOTO_ON_FALSE ( eth_driver - > transmit_mutex , ESP_ERR_NO_MEM , err , TAG , " Failed to create transmit mutex " ) ;
# endif // CONFIG_ETH_TRANSMIT_MUTEX
2019-11-26 04:48:38 -05:00
atomic_init ( & eth_driver - > ref_count , 1 ) ;
2020-05-09 00:49:29 -04:00
atomic_init ( & eth_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-19 03:05:29 -04:00
eth_driver - > check_link_period_ms = config - > check_link_period_ms ;
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 ;
2021-04-19 03:05:29 -04:00
// set mediator for both mac and phy object, so that mac and phy are connected to each other via mediator
mac - > set_mediator ( mac , & eth_driver - > mediator ) ;
phy - > set_mediator ( phy , & eth_driver - > mediator ) ;
// for PHY whose internal PLL has been configured to generate RMII clock, but is put in reset state during power up,
// we need to deasseert the reset GPIO of PHY device first, ensure the RMII is clocked out from PHY
2019-11-13 23:03:14 -05:00
phy - > reset_hw ( phy ) ;
2021-04-19 03:05:29 -04:00
// 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 ( phy - > init ( phy ) , err , TAG , " init phy failed " ) ;
2021-11-04 04:10:19 -04:00
// 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 , & eth_driver - > auto_nego_en ) ,
err , TAG , " get autonegotiation status failed " ) ;
2021-04-19 03:05:29 -04:00
ESP_LOGD ( TAG , " new ethernet driver @%p " , eth_driver ) ;
* out_hdl = eth_driver ;
2019-11-26 04:48:38 -05:00
2019-04-10 04:24:50 -04:00
return ESP_OK ;
err :
2021-04-19 03:05:29 -04:00
if ( eth_driver ) {
if ( eth_driver - > check_link_timer ) {
esp_timer_delete ( eth_driver - > check_link_timer ) ;
}
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
if ( eth_driver - > transmit_mutex ) {
vSemaphoreDelete ( eth_driver - > transmit_mutex ) ;
}
# endif // CONFIG_ETH_TRANSMIT_MUTEX
2021-04-19 03:05:29 -04:00
free ( eth_driver ) ;
}
2019-04-10 04:24:50 -04:00
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-19 03:05:29 -04:00
ESP_GOTO_ON_FALSE ( eth_driver , ESP_ERR_INVALID_ARG , err , TAG , " invalid argument " ) ;
// check if driver has stopped
2020-05-09 00:49:29 -04:00
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP ;
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_FALSE ( atomic_compare_exchange_strong ( & eth_driver - > fsm , & expected_fsm , ESP_ETH_FSM_STOP ) ,
ESP_ERR_INVALID_STATE , err , TAG , " driver not stopped yet " ) ;
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 ;
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_FALSE ( atomic_compare_exchange_strong ( & eth_driver - > ref_count , & expected_ref_count , 0 ) ,
ESP_ERR_INVALID_STATE , err , TAG , " %d ethernet reference in use " , expected_ref_count ) ;
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-19 03:05:29 -04:00
ESP_GOTO_ON_ERROR ( esp_timer_delete ( eth_driver - > check_link_timer ) , err , TAG , " delete link timer failed " ) ;
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
vSemaphoreDelete ( eth_driver - > transmit_mutex ) ;
# endif // CONFIG_ETH_TRANSMIT_MUTEX
2021-04-01 08:00:54 -04:00
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 ) ;
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 " ) ;
2021-04-19 03:05:29 -04:00
esp_eth_phy_t * phy = eth_driver - > phy ;
// check if driver has stopped
2020-05-09 00:49:29 -04:00
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP ;
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_FALSE ( atomic_compare_exchange_strong ( & eth_driver - > fsm , & expected_fsm , ESP_ETH_FSM_START ) ,
ESP_ERR_INVALID_STATE , err , TAG , " driver started already " ) ;
2021-11-04 04:10:19 -04:00
// 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 , & eth_driver - > auto_nego_en ) , err , TAG , " phy negotiation failed " ) ;
}
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_ERROR ( esp_event_post ( ETH_EVENT , ETHERNET_EVENT_START , & eth_driver , sizeof ( esp_eth_driver_t * ) , 0 ) ,
err , TAG , " send ETHERNET_EVENT_START event failed " ) ;
2021-05-06 08:16:12 -04:00
ESP_GOTO_ON_ERROR ( phy - > get_link ( phy ) , err , TAG , " phy get link status failed " ) ;
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_ERROR ( esp_timer_start_periodic ( eth_driver - > check_link_timer , eth_driver - > check_link_period_ms * 1000 ) ,
err , TAG , " start link timer failed " ) ;
2019-11-26 04:48:38 -05:00
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 " ) ;
2021-04-19 03:05:29 -04:00
esp_eth_mac_t * mac = eth_driver - > mac ;
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 ;
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_FALSE ( atomic_compare_exchange_strong ( & eth_driver - > fsm , & expected_fsm , ESP_ETH_FSM_STOP ) ,
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 " ) ;
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_ERROR ( mac - > stop ( mac ) , err , TAG , " stop mac failed " ) ;
2021-11-04 04:10:19 -04:00
2021-04-19 03:05:29 -04:00
ESP_GOTO_ON_ERROR ( esp_event_post ( ETH_EVENT , ETHERNET_EVENT_STOP , & eth_driver , sizeof ( esp_eth_driver_t * ) , 0 ) ,
err , TAG , " send ETHERNET_EVENT_STOP event failed " ) ;
2019-11-26 04:48:38 -05:00
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 ;
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-12-20 06:45:24 -05:00
if ( atomic_load ( & eth_driver - > fsm ) ! = ESP_ETH_FSM_START ) {
ret = ESP_ERR_INVALID_STATE ;
ESP_LOGD ( TAG , " Ethernet is not started " ) ;
goto err ;
}
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 ;
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
if ( xSemaphoreTake ( eth_driver - > transmit_mutex , pdMS_TO_TICKS ( ESP_ETH_TX_TIMEOUT_MS ) ) = = pdFALSE ) {
return ESP_ERR_TIMEOUT ;
}
# endif // CONFIG_ETH_TRANSMIT_MUTEX
2021-04-19 03:05:29 -04:00
ret = mac - > transmit ( mac , buf , length ) ;
2021-12-01 03:53:25 -05:00
# if CONFIG_ETH_TRANSMIT_MUTEX
xSemaphoreGive ( eth_driver - > transmit_mutex ) ;
# endif // CONFIG_ETH_TRANSMIT_MUTEX
err :
return ret ;
}
esp_err_t esp_eth_transmit_vargs ( esp_eth_handle_t hdl , uint32_t argc , . . . )
{
esp_err_t ret = ESP_OK ;
esp_eth_driver_t * eth_driver = ( esp_eth_driver_t * ) hdl ;
if ( atomic_load ( & eth_driver - > fsm ) ! = ESP_ETH_FSM_START ) {
ret = ESP_ERR_INVALID_STATE ;
ESP_LOGD ( TAG , " Ethernet is not started " ) ;
goto err ;
}
va_list args ;
esp_eth_mac_t * mac = eth_driver - > mac ;
# if CONFIG_ETH_TRANSMIT_MUTEX
if ( xSemaphoreTake ( eth_driver - > transmit_mutex , pdMS_TO_TICKS ( ESP_ETH_TX_TIMEOUT_MS ) ) = = pdFALSE ) {
return ESP_ERR_TIMEOUT ;
}
# endif // CONFIG_ETH_TRANSMIT_MUTEX
va_start ( args , argc ) ;
ret = mac - > transmit_vargs ( mac , argc , args ) ;
# if CONFIG_ETH_TRANSMIT_MUTEX
xSemaphoreGive ( eth_driver - > transmit_mutex ) ;
# endif // CONFIG_ETH_TRANSMIT_MUTEX
va_end ( args ) ;
2019-04-10 04:24:50 -04:00
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 ;
2021-04-19 03:05:29 -04:00
ret = mac - > receive ( mac , buf , length ) ;
2019-04-10 04:24:50 -04:00
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-11-04 04:10:19 -04:00
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 ;
2021-11-04 04:10:19 -04:00
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 ( & eth_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 , & eth_driver - > auto_nego_en ) , err , TAG , " phy negotiation enable failed " ) ;
} else {
ESP_GOTO_ON_ERROR ( phy - > autonego_ctrl ( phy , ESP_ETH_PHY_AUTONEGO_DIS , & eth_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 ( & eth_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 ;
2019-04-10 04:24:50 -04:00
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-11-04 04:10:19 -04:00
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 " ) ;
2019-04-10 04:24:50 -04:00
break ;
2020-07-20 08:42:52 -04:00
case ETH_CMD_S_FLOW_CTRL :
2021-11-04 04:10:19 -04:00
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 ( & eth_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 " ) ;
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 ;
2021-09-03 11:24:01 -04:00
case ETH_CMD_S_PHY_LOOPBACK :
2021-11-04 04:10:19 -04:00
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 " ) ;
2021-09-03 11:24:01 -04:00
break ;
2019-04-10 04:24:50 -04:00
default :
2021-12-01 03:53:25 -05:00
if ( phy - > custom_ioctl ! = NULL & & cmd > = ETH_CMD_CUSTOM_PHY_CMDS ) {
ret = phy - > custom_ioctl ( phy , cmd , data ) ;
} else if ( mac - > custom_ioctl ! = NULL & & cmd > = ETH_CMD_CUSTOM_MAC_CMDS ) {
ret = mac - > custom_ioctl ( mac , cmd , data ) ;
} else {
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 ;
}
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 ( & eth_driver - > ref_count , 1 ) ;
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 ( & eth_driver - > ref_count , 1 ) ;
err :
2019-06-28 10:47:34 -04:00
return ret ;
}