esp_eth.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <sys/cdefs.h>
  15. #include <stdatomic.h>
  16. #include "esp_log.h"
  17. #include "esp_eth.h"
  18. #include "esp_event.h"
  19. #include "freertos/FreeRTOS.h"
  20. #include "freertos/task.h"
  21. #include "freertos/timers.h"
  22. #include "esp_heap_caps.h"
  23. static const char *TAG = "esp_eth";
  24. #define ETH_CHECK(a, str, goto_tag, ret_value, ...) \
  25. do \
  26. { \
  27. if (!(a)) \
  28. { \
  29. ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
  30. ret = ret_value; \
  31. goto goto_tag; \
  32. } \
  33. } while (0)
  34. ESP_EVENT_DEFINE_BASE(ETH_EVENT);
  35. typedef enum {
  36. ESP_ETH_FSM_STOP,
  37. ESP_ETH_FSM_START
  38. } esp_eth_fsm_t;
  39. /**
  40. * @brief The Ethernet driver mainly consists of PHY, MAC and
  41. * the mediator who will handle the request/response from/to MAC, PHY and Users.
  42. * Ethernet driver adopts an OS timer to check the link status periodically.
  43. * This structure preserves some important Ethernet attributes (e.g. speed, duplex, link).
  44. * Function stack_input is the channel which set by user, it will deliver all received packets.
  45. * If stack_input is set to NULL, then all received packets will be passed to tcp/ip stack.
  46. * on_lowlevel_init_done and on_lowlevel_deinit_done are callbacks set by user.
  47. * In the callback, user can do any low level operations (e.g. enable/disable crystal clock).
  48. */
  49. typedef struct {
  50. esp_eth_mediator_t mediator;
  51. esp_eth_phy_t *phy;
  52. esp_eth_mac_t *mac;
  53. TimerHandle_t check_link_timer;
  54. eth_speed_t speed;
  55. eth_duplex_t duplex;
  56. eth_link_t link;
  57. atomic_int ref_count;
  58. void *priv;
  59. _Atomic esp_eth_fsm_t fsm;
  60. esp_err_t (*stack_input)(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv);
  61. esp_err_t (*on_lowlevel_init_done)(esp_eth_handle_t eth_handle);
  62. esp_err_t (*on_lowlevel_deinit_done)(esp_eth_handle_t eth_handle);
  63. } esp_eth_driver_t;
  64. ////////////////////////////////Mediator Functions////////////////////////////////////////////
  65. // Following functions are owned by mediator, which will get invoked by MAC or PHY.
  66. // Mediator functions need to find the right actor (MAC, PHY or user) to perform the operation.
  67. // So in the head of mediator function, we have to get the esp_eth_driver_t pointer.
  68. // With this pointer, we could deliver the task to the real actor (MAC, PHY or user).
  69. // This might sound excessive, but is helpful to separate the PHY with MAC (they can not contact with each other directly).
  70. // For more details, please refer to WiKi. https://en.wikipedia.org/wiki/Mediator_pattern
  71. //////////////////////////////////////////////////////////////////////////////////////////////
  72. 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)
  73. {
  74. esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
  75. esp_eth_mac_t *mac = eth_driver->mac;
  76. return mac->read_phy_reg(mac, phy_addr, phy_reg, reg_value);
  77. }
  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. esp_eth_mac_t *mac = eth_driver->mac;
  82. return mac->write_phy_reg(mac, phy_addr, phy_reg, reg_value);
  83. }
  84. static esp_err_t eth_stack_input(esp_eth_mediator_t *eth, uint8_t *buffer, uint32_t length)
  85. {
  86. esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
  87. if (eth_driver->stack_input) {
  88. return eth_driver->stack_input((esp_eth_handle_t)eth_driver, buffer, length, eth_driver->priv);
  89. } else {
  90. free(buffer);
  91. return ESP_OK;
  92. }
  93. }
  94. static esp_err_t eth_on_state_changed(esp_eth_mediator_t *eth, esp_eth_state_t state, void *args)
  95. {
  96. esp_err_t ret = ESP_OK;
  97. esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
  98. esp_eth_mac_t *mac = eth_driver->mac;
  99. switch (state) {
  100. case ETH_STATE_LLINIT: {
  101. if (eth_driver->on_lowlevel_init_done) {
  102. ETH_CHECK(eth_driver->on_lowlevel_init_done(eth_driver) == ESP_OK, "extra lowlevel init failed", err, ESP_FAIL);
  103. }
  104. break;
  105. }
  106. case ETH_STATE_DEINIT: {
  107. if (eth_driver->on_lowlevel_deinit_done) {
  108. ETH_CHECK(eth_driver->on_lowlevel_deinit_done(eth_driver) == ESP_OK, "extra lowlevel deinit failed", err, ESP_FAIL);
  109. }
  110. break;
  111. }
  112. case ETH_STATE_LINK: {
  113. eth_link_t link = (eth_link_t)args;
  114. ETH_CHECK(mac->set_link(mac, link) == ESP_OK, "ethernet mac set link failed", err, ESP_FAIL);
  115. eth_driver->link = link;
  116. if (link == ETH_LINK_UP) {
  117. ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
  118. "send ETHERNET_EVENT_CONNECTED event failed", err, ESP_FAIL);
  119. } else if (link == ETH_LINK_DOWN) {
  120. ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
  121. "send ETHERNET_EVENT_DISCONNECTED event failed", err, ESP_FAIL);
  122. }
  123. break;
  124. }
  125. case ETH_STATE_SPEED: {
  126. eth_speed_t speed = (eth_speed_t)args;
  127. ETH_CHECK(mac->set_speed(mac, speed) == ESP_OK, "ethernet mac set speed failed", err, ESP_FAIL);
  128. eth_driver->speed = speed;
  129. break;
  130. }
  131. case ETH_STATE_DUPLEX: {
  132. eth_duplex_t duplex = (eth_duplex_t)args;
  133. ETH_CHECK(mac->set_duplex(mac, duplex) == ESP_OK, "ethernet mac set duplex failed", err, ESP_FAIL);
  134. eth_driver->duplex = duplex;
  135. break;
  136. }
  137. case ETH_STATE_PAUSE: {
  138. uint32_t peer_pause_ability = (uint32_t)args;
  139. ETH_CHECK(mac->set_peer_pause_ability(mac, peer_pause_ability) == ESP_OK, "ethernet mac set peer pause ability failed", err, ESP_FAIL);
  140. break;
  141. }
  142. default:
  143. ETH_CHECK(false, "unknown ethernet state: %d", err, ESP_ERR_INVALID_ARG, state);
  144. break;
  145. }
  146. return ESP_OK;
  147. err:
  148. return ret;
  149. }
  150. static void eth_check_link_timer_cb(TimerHandle_t xTimer)
  151. {
  152. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)pvTimerGetTimerID(xTimer);
  153. esp_eth_phy_t *phy = eth_driver->phy;
  154. esp_eth_increase_reference(eth_driver);
  155. phy->get_link(phy);
  156. esp_eth_decrease_reference(eth_driver);
  157. }
  158. ////////////////////////////////User face APIs////////////////////////////////////////////////
  159. // User has to pass the handle of Ethernet driver to each API.
  160. // Different Ethernet driver instance is identified with a unique handle.
  161. // It's helpful for us to support multiple Ethernet port on ESP32.
  162. //////////////////////////////////////////////////////////////////////////////////////////////
  163. esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_t *out_hdl)
  164. {
  165. esp_err_t ret = ESP_OK;
  166. ETH_CHECK(config, "eth config can't be null", err, ESP_ERR_INVALID_ARG);
  167. ETH_CHECK(out_hdl, "eth handle can't be null", err, ESP_ERR_INVALID_ARG);
  168. esp_eth_mac_t *mac = config->mac;
  169. esp_eth_phy_t *phy = config->phy;
  170. ETH_CHECK(mac && phy, "can't set eth->mac or eth->phy to null", err, ESP_ERR_INVALID_ARG);
  171. // eth_driver contains an atomic variable, which should not be put in PSRAM
  172. esp_eth_driver_t *eth_driver = heap_caps_calloc(1, sizeof(esp_eth_driver_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  173. ETH_CHECK(eth_driver, "request memory for eth_driver failed", err, ESP_ERR_NO_MEM);
  174. atomic_init(&eth_driver->ref_count, 1);
  175. atomic_init(&eth_driver->fsm, ESP_ETH_FSM_STOP);
  176. eth_driver->mac = mac;
  177. eth_driver->phy = phy;
  178. eth_driver->link = ETH_LINK_DOWN;
  179. eth_driver->duplex = ETH_DUPLEX_HALF;
  180. eth_driver->speed = ETH_SPEED_10M;
  181. eth_driver->stack_input = config->stack_input;
  182. eth_driver->on_lowlevel_init_done = config->on_lowlevel_init_done;
  183. eth_driver->on_lowlevel_deinit_done = config->on_lowlevel_deinit_done;
  184. eth_driver->mediator.phy_reg_read = eth_phy_reg_read;
  185. eth_driver->mediator.phy_reg_write = eth_phy_reg_write;
  186. eth_driver->mediator.stack_input = eth_stack_input;
  187. eth_driver->mediator.on_state_changed = eth_on_state_changed;
  188. /* some PHY can't output RMII clock if in reset state, so hardware reset PHY chip firstly */
  189. phy->reset_hw(phy);
  190. ETH_CHECK(mac->set_mediator(mac, &eth_driver->mediator) == ESP_OK, "set mediator for mac failed", err_mediator, ESP_FAIL);
  191. ETH_CHECK(phy->set_mediator(phy, &eth_driver->mediator) == ESP_OK, "set mediator for phy failed", err_mediator, ESP_FAIL);
  192. ETH_CHECK(mac->init(mac) == ESP_OK, "init mac failed", err_init_mac, ESP_FAIL);
  193. ETH_CHECK(phy->init(phy) == ESP_OK, "init phy failed", err_init_phy, ESP_FAIL);
  194. eth_driver->check_link_timer = xTimerCreate("eth_link_timer", pdMS_TO_TICKS(config->check_link_period_ms), pdTRUE,
  195. eth_driver, eth_check_link_timer_cb);
  196. ETH_CHECK(eth_driver->check_link_timer, "create eth_link_timer failed", err_create_timer, ESP_FAIL);
  197. *out_hdl = (esp_eth_handle_t)eth_driver;
  198. // for backward compatible to 4.0, and will get removed in 5.0
  199. #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
  200. extern esp_err_t tcpip_adapter_compat_start_eth(void *eth_driver);
  201. tcpip_adapter_compat_start_eth(eth_driver);
  202. #endif
  203. return ESP_OK;
  204. err_create_timer:
  205. phy->deinit(phy);
  206. err_init_phy:
  207. mac->deinit(mac);
  208. err_init_mac:
  209. err_mediator:
  210. free(eth_driver);
  211. err:
  212. return ret;
  213. }
  214. esp_err_t esp_eth_driver_uninstall(esp_eth_handle_t hdl)
  215. {
  216. esp_err_t ret = ESP_OK;
  217. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  218. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  219. // check if driver has started
  220. esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
  221. if (!atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP)) {
  222. ESP_LOGW(TAG, "driver not stopped yet");
  223. ret = ESP_ERR_INVALID_STATE;
  224. goto err;
  225. }
  226. // don't uninstall driver unless there's only one reference
  227. int expected_ref_count = 1;
  228. if (!atomic_compare_exchange_strong(&eth_driver->ref_count, &expected_ref_count, 0)) {
  229. ESP_LOGE(TAG, "%d ethernet reference in use", expected_ref_count);
  230. ret = ESP_ERR_INVALID_STATE;
  231. goto err;
  232. }
  233. esp_eth_mac_t *mac = eth_driver->mac;
  234. esp_eth_phy_t *phy = eth_driver->phy;
  235. ETH_CHECK(xTimerDelete(eth_driver->check_link_timer, 0) == pdPASS, "delete eth_link_timer failed", err, ESP_FAIL);
  236. ETH_CHECK(phy->deinit(phy) == ESP_OK, "deinit phy failed", err, ESP_FAIL);
  237. ETH_CHECK(mac->deinit(mac) == ESP_OK, "deinit mac failed", err, ESP_FAIL);
  238. free(eth_driver);
  239. return ESP_OK;
  240. err:
  241. return ret;
  242. }
  243. esp_err_t esp_eth_start(esp_eth_handle_t hdl)
  244. {
  245. esp_err_t ret = ESP_OK;
  246. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  247. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  248. // check if driver has started
  249. esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
  250. if (!atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_START)) {
  251. ESP_LOGW(TAG, "driver started already");
  252. ret = ESP_ERR_INVALID_STATE;
  253. goto err;
  254. }
  255. ETH_CHECK(eth_driver->phy->reset(eth_driver->phy) == ESP_OK, "reset phy failed", err, ESP_FAIL);
  256. ETH_CHECK(xTimerStart(eth_driver->check_link_timer, 0) == pdPASS,
  257. "start eth_link_timer failed", err, ESP_FAIL);
  258. ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
  259. "send ETHERNET_EVENT_START event failed", err_event, ESP_FAIL);
  260. return ESP_OK;
  261. err_event:
  262. xTimerStop(eth_driver->check_link_timer, 0);
  263. err:
  264. return ret;
  265. }
  266. esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
  267. {
  268. esp_err_t ret = ESP_OK;
  269. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  270. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  271. // check if driver has started
  272. esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_START;
  273. if (!atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP)) {
  274. ESP_LOGW(TAG, "driver not started yet");
  275. ret = ESP_ERR_INVALID_STATE;
  276. goto err;
  277. }
  278. esp_eth_mac_t *mac = eth_driver->mac;
  279. ETH_CHECK(mac->stop(mac) == ESP_OK, "stop mac failed", err, ESP_FAIL);
  280. ETH_CHECK(xTimerStop(eth_driver->check_link_timer, 0) == pdPASS,
  281. "stop eth_link_timer failed", err, ESP_FAIL);
  282. ETH_CHECK(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, &eth_driver, sizeof(eth_driver), 0) == ESP_OK,
  283. "send ETHERNET_EVENT_STOP event failed", err, ESP_FAIL);
  284. return ESP_OK;
  285. err:
  286. return ret;
  287. }
  288. esp_err_t esp_eth_update_input_path(
  289. esp_eth_handle_t hdl,
  290. esp_err_t (*stack_input)(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv),
  291. void *priv)
  292. {
  293. esp_err_t ret = ESP_OK;
  294. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  295. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  296. eth_driver->priv = priv;
  297. eth_driver->stack_input = stack_input;
  298. return ESP_OK;
  299. err:
  300. return ret;
  301. }
  302. esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length)
  303. {
  304. esp_err_t ret = ESP_OK;
  305. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  306. ETH_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG);
  307. ETH_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG);
  308. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  309. esp_eth_mac_t *mac = eth_driver->mac;
  310. return mac->transmit(mac, buf, length);
  311. err:
  312. return ret;
  313. }
  314. esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length)
  315. {
  316. esp_err_t ret = ESP_OK;
  317. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  318. ETH_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG);
  319. ETH_CHECK(*length > 60, "length can't be less than 60", err, ESP_ERR_INVALID_ARG);
  320. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  321. esp_eth_mac_t *mac = eth_driver->mac;
  322. return mac->receive(mac, buf, length);
  323. err:
  324. return ret;
  325. }
  326. esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
  327. {
  328. esp_err_t ret = ESP_OK;
  329. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  330. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  331. esp_eth_mac_t *mac = eth_driver->mac;
  332. esp_eth_phy_t *phy = eth_driver->phy;
  333. switch (cmd) {
  334. case ETH_CMD_S_MAC_ADDR:
  335. ETH_CHECK(data, "can't set mac addr to null", err, ESP_ERR_INVALID_ARG);
  336. ETH_CHECK(mac->set_addr(mac, (uint8_t *)data) == ESP_OK, "set mac address failed", err, ESP_FAIL);
  337. break;
  338. case ETH_CMD_G_MAC_ADDR:
  339. ETH_CHECK(data, "no mem to store mac addr", err, ESP_ERR_INVALID_ARG);
  340. ETH_CHECK(mac->get_addr(mac, (uint8_t *)data) == ESP_OK, "get mac address failed", err, ESP_FAIL);
  341. break;
  342. case ETH_CMD_S_PHY_ADDR:
  343. ETH_CHECK(data, "can't set phy addr to null", err, ESP_ERR_INVALID_ARG);
  344. ETH_CHECK(phy->set_addr(phy, (uint32_t)data) == ESP_OK, "set phy address failed", err, ESP_FAIL);
  345. break;
  346. case ETH_CMD_G_PHY_ADDR:
  347. ETH_CHECK(data, "no mem to store phy addr", err, ESP_ERR_INVALID_ARG);
  348. ETH_CHECK(phy->get_addr(phy, (uint32_t *)data) == ESP_OK, "get phy address failed", err, ESP_FAIL);
  349. break;
  350. case ETH_CMD_G_SPEED:
  351. ETH_CHECK(data, "no mem to store speed value", err, ESP_ERR_INVALID_ARG);
  352. *(eth_speed_t *)data = eth_driver->speed;
  353. break;
  354. case ETH_CMD_S_PROMISCUOUS:
  355. ETH_CHECK(mac->set_promiscuous(mac, (bool)data) == ESP_OK, "set promiscuous mode failed", err, ESP_FAIL);
  356. break;
  357. case ETH_CMD_S_FLOW_CTRL:
  358. ETH_CHECK(mac->enable_flow_ctrl(mac, (bool)data) == ESP_OK, "enable mac flow control failed", err, ESP_FAIL);
  359. ETH_CHECK(phy->advertise_pause_ability(phy, (uint32_t)data) == ESP_OK, "phy advertise pause ability failed", err, ESP_FAIL);
  360. break;
  361. case ETH_CMD_G_DUPLEX_MODE:
  362. ETH_CHECK(data, "no mem to store duplex value", err, ESP_ERR_INVALID_ARG);
  363. *(eth_duplex_t *)data = eth_driver->duplex;
  364. break;
  365. default:
  366. ETH_CHECK(false, "unknown io command: %d", err, ESP_ERR_INVALID_ARG, cmd);
  367. break;
  368. }
  369. return ESP_OK;
  370. err:
  371. return ret;
  372. }
  373. esp_err_t esp_eth_increase_reference(esp_eth_handle_t hdl)
  374. {
  375. esp_err_t ret = ESP_OK;
  376. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  377. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  378. atomic_fetch_add(&eth_driver->ref_count, 1);
  379. return ESP_OK;
  380. err:
  381. return ret;
  382. }
  383. esp_err_t esp_eth_decrease_reference(esp_eth_handle_t hdl)
  384. {
  385. esp_err_t ret = ESP_OK;
  386. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  387. ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG);
  388. atomic_fetch_sub(&eth_driver->ref_count, 1);
  389. return ESP_OK;
  390. err:
  391. return ret;
  392. }