1 /*
2  * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <sys/cdefs.h>
8 #include <stdatomic.h>
9 #include "esp_log.h"
10 #include "esp_check.h"
11 #include "esp_eth.h"
12 #include "esp_event.h"
13 #include "esp_heap_caps.h"
14 #include "esp_timer.h"
15 #include "soc/soc.h" // TODO: for esp_eth_ioctl API compatibility reasons, will be removed with next major release
16 #include "freertos/FreeRTOS.h"
17 #include "freertos/task.h"
18 
19 static const char *TAG = "esp_eth";
20 
21 ESP_EVENT_DEFINE_BASE(ETH_EVENT);
22 
23 typedef enum {
24     ESP_ETH_FSM_STOP,
25     ESP_ETH_FSM_START
26 } esp_eth_fsm_t;
27 
28 /**
29  * @brief The Ethernet driver mainly consists of PHY, MAC and
30  * the mediator who will handle the request/response from/to MAC, PHY and Users.
31  * Ethernet driver adopts an OS timer to check the link status periodically.
32  * This structure preserves some important Ethernet attributes (e.g. speed, duplex, link).
33  * Function stack_input is the channel which set by user, it will deliver all received packets.
34  * If stack_input is set to NULL, then all received packets will be passed to tcp/ip stack.
35  * on_lowlevel_init_done and on_lowlevel_deinit_done are callbacks set by user.
36  * In the callback, user can do any low level operations (e.g. enable/disable crystal clock).
37  */
38 typedef struct {
39     esp_eth_mediator_t mediator;
40     esp_eth_phy_t *phy;
41     esp_eth_mac_t *mac;
42     esp_timer_handle_t check_link_timer;
43     uint32_t check_link_period_ms;
44     eth_speed_t speed;
45     eth_duplex_t duplex;
46     eth_link_t link;
47     atomic_int ref_count;
48     void *priv;
49     _Atomic esp_eth_fsm_t fsm;
50     esp_err_t (*stack_input)(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv);
51     esp_err_t (*on_lowlevel_init_done)(esp_eth_handle_t eth_handle);
52     esp_err_t (*on_lowlevel_deinit_done)(esp_eth_handle_t eth_handle);
53     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);
54     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);
55 } esp_eth_driver_t;
56 
57 ////////////////////////////////Mediator Functions////////////////////////////////////////////
58 // Following functions are owned by mediator, which will get invoked by MAC or PHY.
59 // Mediator functions need to find the right actor (MAC, PHY or user) to perform the operation.
60 // So in the head of mediator function, we have to get the esp_eth_driver_t pointer.
61 // With this pointer, we could deliver the task to the real actor (MAC, PHY or user).
62 // This might sound excessive, but is helpful to separate the PHY with MAC (they can not contact with each other directly).
63 // For more details, please refer to WiKi. https://en.wikipedia.org/wiki/Mediator_pattern
64 //////////////////////////////////////////////////////////////////////////////////////////////
65 
eth_phy_reg_read(esp_eth_mediator_t * eth,uint32_t phy_addr,uint32_t phy_reg,uint32_t * reg_value)66 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)
67 {
68     esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
69     // invoking user customized PHY IO function if necessary
70     if (eth_driver->customized_read_phy_reg) {
71         return eth_driver->customized_read_phy_reg(eth_driver, phy_addr, phy_reg, reg_value);
72     }
73     // by default, PHY device is managed by MAC's SMI interface
74     esp_eth_mac_t *mac = eth_driver->mac;
75     return mac->read_phy_reg(mac, phy_addr, phy_reg, reg_value);
76 }
77 
eth_phy_reg_write(esp_eth_mediator_t * eth,uint32_t phy_addr,uint32_t phy_reg,uint32_t reg_value)78 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)
79 {
80     esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
81     // invoking user customized PHY IO function if necessary
82     if (eth_driver->customized_write_phy_reg) {
83         return eth_driver->customized_write_phy_reg(eth_driver, phy_addr, phy_reg, reg_value);
84     }
85     // by default, PHY device is managed by MAC's SMI interface
86     esp_eth_mac_t *mac = eth_driver->mac;
87     return mac->write_phy_reg(mac, phy_addr, phy_reg, reg_value);
88 }
89 
eth_stack_input(esp_eth_mediator_t * eth,uint8_t * buffer,uint32_t length)90 static esp_err_t eth_stack_input(esp_eth_mediator_t *eth, uint8_t *buffer, uint32_t length)
91 {
92     esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
93     if (eth_driver->stack_input) {
94         return eth_driver->stack_input((esp_eth_handle_t)eth_driver, buffer, length, eth_driver->priv);
95     }
96     // No stack input path has been installed, just drop the incoming packets
97     free(buffer);
98     return ESP_OK;
99 }
100 
eth_on_state_changed(esp_eth_mediator_t * eth,esp_eth_state_t state,void * args)101 static esp_err_t eth_on_state_changed(esp_eth_mediator_t *eth, esp_eth_state_t state, void *args)
102 {
103     esp_err_t ret = ESP_OK;
104     esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
105     esp_eth_mac_t *mac = eth_driver->mac;
106     switch (state) {
107     case ETH_STATE_LLINIT: {
108         if (eth_driver->on_lowlevel_init_done) {
109             ESP_GOTO_ON_ERROR(eth_driver->on_lowlevel_init_done(eth_driver), err, TAG, "extra lowlevel init failed");
110         }
111         break;
112     }
113     case ETH_STATE_DEINIT: {
114         if (eth_driver->on_lowlevel_deinit_done) {
115             ESP_GOTO_ON_ERROR(eth_driver->on_lowlevel_deinit_done(eth_driver), err, TAG, "extra lowlevel deinit failed");
116         }
117         break;
118     }
119     case ETH_STATE_LINK: {
120         eth_link_t link = (eth_link_t)args;
121         ESP_GOTO_ON_ERROR(mac->set_link(mac, link), err, TAG, "ethernet mac set link failed");
122         eth_driver->link = link;
123         if (link == ETH_LINK_UP) {
124             ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &eth_driver, sizeof(esp_eth_driver_t *), 0), err,
125                               TAG, "send ETHERNET_EVENT_CONNECTED event failed");
126         } else if (link == ETH_LINK_DOWN) {
127             ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &eth_driver, sizeof(esp_eth_driver_t *), 0), err,
128                               TAG, "send ETHERNET_EVENT_DISCONNECTED event failed");
129         }
130         break;
131     }
132     case ETH_STATE_SPEED: {
133         eth_speed_t speed = (eth_speed_t)args;
134         ESP_GOTO_ON_ERROR(mac->set_speed(mac, speed), err, TAG, "ethernet mac set speed failed");
135         eth_driver->speed = speed;
136         break;
137     }
138     case ETH_STATE_DUPLEX: {
139         eth_duplex_t duplex = (eth_duplex_t)args;
140         ESP_GOTO_ON_ERROR(mac->set_duplex(mac, duplex), err, TAG, "ethernet mac set duplex failed");
141         eth_driver->duplex = duplex;
142         break;
143     }
144     case ETH_STATE_PAUSE: {
145         uint32_t peer_pause_ability = (uint32_t)args;
146         ESP_GOTO_ON_ERROR(mac->set_peer_pause_ability(mac, peer_pause_ability), err, TAG, "ethernet mac set peer pause ability failed");
147         break;
148     }
149     default:
150         ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown ethernet state: %d", state);
151         break;
152     }
153 err:
154     return ret;
155 }
156 
eth_check_link_timer_cb(void * args)157 static void eth_check_link_timer_cb(void *args)
158 {
159     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)args;
160     esp_eth_phy_t *phy = eth_driver->phy;
161     phy->get_link(phy);
162 }
163 
164 ////////////////////////////////User face APIs////////////////////////////////////////////////
165 // User has to pass the handle of Ethernet driver to each API.
166 // Different Ethernet driver instance is identified with a unique handle.
167 // It's helpful for us to support multiple Ethernet port on ESP32.
168 //////////////////////////////////////////////////////////////////////////////////////////////
169 
esp_eth_driver_install(const esp_eth_config_t * config,esp_eth_handle_t * out_hdl)170 esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_t *out_hdl)
171 {
172     esp_err_t ret = ESP_OK;
173     esp_eth_mac_t *mac = NULL;
174     esp_eth_phy_t *phy = NULL;
175     esp_eth_driver_t *eth_driver = NULL;
176     ESP_GOTO_ON_FALSE(config && out_hdl, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
177     mac = config->mac;
178     phy = config->phy;
179     ESP_GOTO_ON_FALSE(mac && phy, ESP_ERR_INVALID_ARG, err, TAG, "can't set eth->mac or eth->phy to null");
180     // eth_driver contains an atomic variable, which should not be put in PSRAM
181     eth_driver = heap_caps_calloc(1, sizeof(esp_eth_driver_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
182     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_NO_MEM, err, TAG, "no mem for eth_driver");
183     const esp_timer_create_args_t check_link_timer_args = {
184         .callback = eth_check_link_timer_cb,
185         .name = "eth_link_timer",
186         .arg = eth_driver,
187         .skip_unhandled_events = true
188     };
189     ESP_GOTO_ON_ERROR(esp_timer_create(&check_link_timer_args, &eth_driver->check_link_timer), err, TAG, "create link timer failed");
190     atomic_init(&eth_driver->ref_count, 1);
191     atomic_init(&eth_driver->fsm, ESP_ETH_FSM_STOP);
192     eth_driver->mac = mac;
193     eth_driver->phy = phy;
194     eth_driver->link = ETH_LINK_DOWN;
195     eth_driver->duplex = ETH_DUPLEX_HALF;
196     eth_driver->speed = ETH_SPEED_10M;
197     eth_driver->stack_input = config->stack_input;
198     eth_driver->on_lowlevel_init_done = config->on_lowlevel_init_done;
199     eth_driver->on_lowlevel_deinit_done = config->on_lowlevel_deinit_done;
200     eth_driver->check_link_period_ms = config->check_link_period_ms;
201     eth_driver->customized_read_phy_reg = config->read_phy_reg;
202     eth_driver->customized_write_phy_reg = config->write_phy_reg;
203     eth_driver->mediator.phy_reg_read = eth_phy_reg_read;
204     eth_driver->mediator.phy_reg_write = eth_phy_reg_write;
205     eth_driver->mediator.stack_input = eth_stack_input;
206     eth_driver->mediator.on_state_changed = eth_on_state_changed;
207     // set mediator for both mac and phy object, so that mac and phy are connected to each other via mediator
208     mac->set_mediator(mac, &eth_driver->mediator);
209     phy->set_mediator(phy, &eth_driver->mediator);
210     // for PHY whose internal PLL has been configured to generate RMII clock, but is put in reset state during power up,
211     // we need to deasseert the reset GPIO of PHY device first, ensure the RMII is clocked out from PHY
212     phy->reset_hw(phy);
213     // init MAC first, so that MAC can generate the correct SMI signals
214     ESP_GOTO_ON_ERROR(mac->init(mac), err, TAG, "init mac failed");
215     ESP_GOTO_ON_ERROR(phy->init(phy), err, TAG, "init phy failed");
216     ESP_LOGD(TAG, "new ethernet driver @%p", eth_driver);
217     *out_hdl = eth_driver;
218 
219     // for backward compatible to 4.0, and will get removed in 5.0
220 #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
221     extern esp_err_t tcpip_adapter_compat_start_eth(void *eth_driver);
222     tcpip_adapter_compat_start_eth(eth_driver);
223 #endif
224 
225     return ESP_OK;
226 err:
227     if (eth_driver) {
228         if (eth_driver->check_link_timer) {
229             esp_timer_delete(eth_driver->check_link_timer);
230         }
231         free(eth_driver);
232     }
233     return ret;
234 }
235 
esp_eth_driver_uninstall(esp_eth_handle_t hdl)236 esp_err_t esp_eth_driver_uninstall(esp_eth_handle_t hdl)
237 {
238     esp_err_t ret = ESP_OK;
239     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
240     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
241     // check if driver has stopped
242     esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
243     ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP),
244                       ESP_ERR_INVALID_STATE, err, TAG, "driver not stopped yet");
245     // don't uninstall driver unless there's only one reference
246     int expected_ref_count = 1;
247     ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->ref_count, &expected_ref_count, 0),
248                       ESP_ERR_INVALID_STATE, err, TAG, "%d ethernet reference in use", expected_ref_count);
249     esp_eth_mac_t *mac = eth_driver->mac;
250     esp_eth_phy_t *phy = eth_driver->phy;
251     ESP_GOTO_ON_ERROR(esp_timer_delete(eth_driver->check_link_timer), err, TAG, "delete link timer failed");
252     ESP_GOTO_ON_ERROR(phy->deinit(phy), err, TAG, "deinit phy failed");
253     ESP_GOTO_ON_ERROR(mac->deinit(mac), err, TAG, "deinit mac failed");
254     free(eth_driver);
255 err:
256     return ret;
257 }
258 
esp_eth_start(esp_eth_handle_t hdl)259 esp_err_t esp_eth_start(esp_eth_handle_t hdl)
260 {
261     esp_err_t ret = ESP_OK;
262     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
263     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
264     esp_eth_phy_t *phy = eth_driver->phy;
265     // check if driver has stopped
266     esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
267     ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_START),
268                       ESP_ERR_INVALID_STATE, err, TAG, "driver started already");
269     ESP_GOTO_ON_ERROR(phy->negotiate(phy), err, TAG, "phy negotiation failed");
270     ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(esp_eth_driver_t *), 0),
271                       err, TAG, "send ETHERNET_EVENT_START event failed");
272     ESP_GOTO_ON_ERROR(phy->get_link(phy), err, TAG, "phy get link status failed");
273     ESP_GOTO_ON_ERROR(esp_timer_start_periodic(eth_driver->check_link_timer, eth_driver->check_link_period_ms * 1000),
274                       err, TAG, "start link timer failed");
275 err:
276     return ret;
277 }
278 
esp_eth_stop(esp_eth_handle_t hdl)279 esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
280 {
281     esp_err_t ret = ESP_OK;
282     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
283     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
284     esp_eth_mac_t *mac = eth_driver->mac;
285     // check if driver has started
286     esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_START;
287     ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP),
288                       ESP_ERR_INVALID_STATE, err, TAG, "driver not started yet");
289     ESP_GOTO_ON_ERROR(esp_timer_stop(eth_driver->check_link_timer), err, TAG, "stop link timer failed");
290     ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "stop mac failed");
291     ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, &eth_driver, sizeof(esp_eth_driver_t *), 0),
292                       err, TAG, "send ETHERNET_EVENT_STOP event failed");
293 err:
294     return ret;
295 }
296 
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)297 esp_err_t esp_eth_update_input_path(
298     esp_eth_handle_t hdl,
299     esp_err_t (*stack_input)(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv),
300     void *priv)
301 {
302     esp_err_t ret = ESP_OK;
303     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
304     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
305     eth_driver->priv = priv;
306     eth_driver->stack_input = stack_input;
307 err:
308     return ret;
309 }
310 
esp_eth_transmit(esp_eth_handle_t hdl,void * buf,size_t length)311 esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length)
312 {
313     esp_err_t ret = ESP_OK;
314     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
315 
316     if (atomic_load(&eth_driver->fsm) != ESP_ETH_FSM_START) {
317         ret = ESP_ERR_INVALID_STATE;
318         ESP_LOGD(TAG, "Ethernet is not started");
319         goto err;
320     }
321 
322     ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "can't set buf to null");
323     ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "buf length can't be zero");
324     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
325     esp_eth_mac_t *mac = eth_driver->mac;
326     ret = mac->transmit(mac, buf, length);
327 err:
328     return ret;
329 }
330 
esp_eth_receive(esp_eth_handle_t hdl,uint8_t * buf,uint32_t * length)331 esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length)
332 {
333     esp_err_t ret = ESP_OK;
334     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
335     ESP_GOTO_ON_FALSE(buf && length, ESP_ERR_INVALID_ARG, err, TAG, "can't set buf and length to null");
336     ESP_GOTO_ON_FALSE(*length > 60, ESP_ERR_INVALID_ARG, err, TAG, "length can't be less than 60");
337     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
338     esp_eth_mac_t *mac = eth_driver->mac;
339     ret = mac->receive(mac, buf, length);
340 err:
341     return ret;
342 }
343 
esp_eth_ioctl(esp_eth_handle_t hdl,esp_eth_io_cmd_t cmd,void * data)344 esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
345 {
346     esp_err_t ret = ESP_OK;
347     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
348     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
349     esp_eth_mac_t *mac = eth_driver->mac;
350     esp_eth_phy_t *phy = eth_driver->phy;
351     switch (cmd) {
352     case ETH_CMD_S_MAC_ADDR:
353         ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
354         ESP_GOTO_ON_ERROR(mac->set_addr(mac, (uint8_t *)data), err, TAG, "set mac address failed");
355         break;
356     case ETH_CMD_G_MAC_ADDR:
357         ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store mac addr");
358         ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed");
359         break;
360     case ETH_CMD_S_PHY_ADDR:
361         if ((uint32_t)data >= SOC_DRAM_LOW) {
362             ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed");
363         } else { // TODO: for API compatibility reasons, will be removed with next major release
364             ESP_GOTO_ON_ERROR(phy->set_addr(phy, (uint32_t)data), err, TAG, "set phy address failed");
365         }
366         break;
367     case ETH_CMD_G_PHY_ADDR:
368         ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store phy addr");
369         ESP_GOTO_ON_ERROR(phy->get_addr(phy, (uint32_t *)data), err, TAG, "get phy address failed");
370         break;
371     case ETH_CMD_G_SPEED:
372         ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store speed value");
373         *(eth_speed_t *)data = eth_driver->speed;
374         break;
375     case ETH_CMD_S_PROMISCUOUS:
376         if ((uint32_t)data >= SOC_DRAM_LOW) {
377             ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
378         } else { // TODO: for API compatibility reasons, will be removed with next major release
379             ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, (bool)data), err, TAG, "set promiscuous mode failed");
380         }
381         break;
382     case ETH_CMD_S_FLOW_CTRL:
383         if ((uint32_t)data >= SOC_DRAM_LOW) {
384             ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
385             ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed");
386         } else { // TODO: for API compatibility reasons, will be removed with next major release
387             ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, (bool)data), err, TAG, "enable mac flow control failed");
388             ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, (uint32_t)data), err, TAG, "phy advertise pause ability failed");
389         }
390         break;
391     case ETH_CMD_G_DUPLEX_MODE:
392         ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value");
393         *(eth_duplex_t *)data = eth_driver->duplex;
394         break;
395     case ETH_CMD_S_PHY_LOOPBACK:
396         if ((uint32_t)data >= SOC_DRAM_LOW) {
397             ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
398         } else { // TODO: for API compatibility reasons, will be removed with next major release
399             ESP_GOTO_ON_ERROR(phy->loopback(phy, (bool)data), err, TAG, "configuration of phy loopback mode failed");
400         }
401         break;
402     default:
403         ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
404         break;
405     }
406 err:
407     return ret;
408 }
409 
esp_eth_increase_reference(esp_eth_handle_t hdl)410 esp_err_t esp_eth_increase_reference(esp_eth_handle_t hdl)
411 {
412     esp_err_t ret = ESP_OK;
413     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
414     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
415     atomic_fetch_add(&eth_driver->ref_count, 1);
416 err:
417     return ret;
418 }
419 
esp_eth_decrease_reference(esp_eth_handle_t hdl)420 esp_err_t esp_eth_decrease_reference(esp_eth_handle_t hdl)
421 {
422     esp_err_t ret = ESP_OK;
423     esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
424     ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
425     atomic_fetch_sub(&eth_driver->ref_count, 1);
426 err:
427     return ret;
428 }
429