esp_eth.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <sys/cdefs.h>
  7. #include <stdatomic.h>
  8. #include "esp_log.h"
  9. #include "esp_check.h"
  10. #include "esp_eth_driver.h"
  11. #include "esp_event.h"
  12. #include "esp_heap_caps.h"
  13. #include "esp_timer.h"
  14. #include "freertos/FreeRTOS.h"
  15. #include "freertos/task.h"
  16. #if CONFIG_ETH_TRANSMIT_MUTEX
  17. /**
  18. * @brief Transmit timeout when multiple accesses to network driver
  19. */
  20. #define ESP_ETH_TX_TIMEOUT_MS 250
  21. #endif
  22. static const char *TAG = "esp_eth";
  23. ESP_EVENT_DEFINE_BASE(ETH_EVENT);
  24. typedef enum {
  25. ESP_ETH_FSM_STOP,
  26. ESP_ETH_FSM_START
  27. } esp_eth_fsm_t;
  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. bool auto_nego_en;
  45. eth_speed_t speed;
  46. eth_duplex_t duplex;
  47. _Atomic eth_link_t link;
  48. atomic_int ref_count;
  49. void *priv;
  50. _Atomic esp_eth_fsm_t fsm;
  51. #if CONFIG_ETH_TRANSMIT_MUTEX
  52. SemaphoreHandle_t transmit_mutex;
  53. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  54. esp_err_t (*stack_input)(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv);
  55. esp_err_t (*on_lowlevel_init_done)(esp_eth_handle_t eth_handle);
  56. esp_err_t (*on_lowlevel_deinit_done)(esp_eth_handle_t eth_handle);
  57. 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);
  58. 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);
  59. } esp_eth_driver_t;
  60. ////////////////////////////////Mediator Functions////////////////////////////////////////////
  61. // Following functions are owned by mediator, which will get invoked by MAC or PHY.
  62. // Mediator functions need to find the right actor (MAC, PHY or user) to perform the operation.
  63. // So in the head of mediator function, we have to get the esp_eth_driver_t pointer.
  64. // With this pointer, we could deliver the task to the real actor (MAC, PHY or user).
  65. // This might sound excessive, but is helpful to separate the PHY with MAC (they can not contact with each other directly).
  66. // For more details, please refer to WiKi. https://en.wikipedia.org/wiki/Mediator_pattern
  67. //////////////////////////////////////////////////////////////////////////////////////////////
  68. 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)
  69. {
  70. esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
  71. // invoking user customized PHY IO function if necessary
  72. if (eth_driver->customized_read_phy_reg) {
  73. return eth_driver->customized_read_phy_reg(eth_driver, phy_addr, phy_reg, reg_value);
  74. }
  75. // by default, PHY device is managed by MAC's SMI interface
  76. esp_eth_mac_t *mac = eth_driver->mac;
  77. return mac->read_phy_reg(mac, phy_addr, phy_reg, reg_value);
  78. }
  79. 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)
  80. {
  81. esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
  82. // invoking user customized PHY IO function if necessary
  83. if (eth_driver->customized_write_phy_reg) {
  84. return eth_driver->customized_write_phy_reg(eth_driver, phy_addr, phy_reg, reg_value);
  85. }
  86. // by default, PHY device is managed by MAC's SMI interface
  87. esp_eth_mac_t *mac = eth_driver->mac;
  88. return mac->write_phy_reg(mac, phy_addr, phy_reg, reg_value);
  89. }
  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. static esp_err_t eth_on_state_changed(esp_eth_mediator_t *eth, esp_eth_state_t state, void *args)
  101. {
  102. esp_err_t ret = ESP_OK;
  103. esp_eth_driver_t *eth_driver = __containerof(eth, esp_eth_driver_t, mediator);
  104. esp_eth_mac_t *mac = eth_driver->mac;
  105. switch (state) {
  106. case ETH_STATE_LLINIT: {
  107. if (eth_driver->on_lowlevel_init_done) {
  108. ESP_GOTO_ON_ERROR(eth_driver->on_lowlevel_init_done(eth_driver), err, TAG, "extra lowlevel init failed");
  109. }
  110. break;
  111. }
  112. case ETH_STATE_DEINIT: {
  113. if (eth_driver->on_lowlevel_deinit_done) {
  114. ESP_GOTO_ON_ERROR(eth_driver->on_lowlevel_deinit_done(eth_driver), err, TAG, "extra lowlevel deinit failed");
  115. }
  116. break;
  117. }
  118. case ETH_STATE_LINK: {
  119. eth_link_t link = (eth_link_t)args;
  120. ESP_GOTO_ON_ERROR(mac->set_link(mac, link), err, TAG, "ethernet mac set link failed");
  121. atomic_store(&eth_driver->link, link);
  122. if (link == ETH_LINK_UP) {
  123. ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &eth_driver, sizeof(esp_eth_driver_t *), 0), err,
  124. TAG, "send ETHERNET_EVENT_CONNECTED event failed");
  125. } else if (link == ETH_LINK_DOWN) {
  126. ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &eth_driver, sizeof(esp_eth_driver_t *), 0), err,
  127. TAG, "send ETHERNET_EVENT_DISCONNECTED event failed");
  128. }
  129. break;
  130. }
  131. case ETH_STATE_SPEED: {
  132. eth_speed_t speed = (eth_speed_t)args;
  133. ESP_GOTO_ON_ERROR(mac->set_speed(mac, speed), err, TAG, "ethernet mac set speed failed");
  134. eth_driver->speed = speed;
  135. break;
  136. }
  137. case ETH_STATE_DUPLEX: {
  138. eth_duplex_t duplex = (eth_duplex_t)args;
  139. ESP_GOTO_ON_ERROR(mac->set_duplex(mac, duplex), err, TAG, "ethernet mac set duplex failed");
  140. eth_driver->duplex = duplex;
  141. break;
  142. }
  143. case ETH_STATE_PAUSE: {
  144. uint32_t peer_pause_ability = (uint32_t)args;
  145. ESP_GOTO_ON_ERROR(mac->set_peer_pause_ability(mac, peer_pause_ability), err, TAG, "ethernet mac set peer pause ability failed");
  146. break;
  147. }
  148. default:
  149. ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown ethernet state: %d", state);
  150. break;
  151. }
  152. err:
  153. return ret;
  154. }
  155. static void eth_check_link_timer_cb(void *args)
  156. {
  157. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)args;
  158. esp_eth_phy_t *phy = eth_driver->phy;
  159. phy->get_link(phy);
  160. }
  161. ////////////////////////////////User face APIs////////////////////////////////////////////////
  162. // User has to pass the handle of Ethernet driver to each API.
  163. // Different Ethernet driver instance is identified with a unique handle.
  164. // It's helpful for us to support multiple Ethernet port on ESP32.
  165. //////////////////////////////////////////////////////////////////////////////////////////////
  166. esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_t *out_hdl)
  167. {
  168. esp_err_t ret = ESP_OK;
  169. esp_eth_mac_t *mac = NULL;
  170. esp_eth_phy_t *phy = NULL;
  171. esp_eth_driver_t *eth_driver = NULL;
  172. ESP_GOTO_ON_FALSE(config && out_hdl, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
  173. mac = config->mac;
  174. phy = config->phy;
  175. ESP_GOTO_ON_FALSE(mac && phy, ESP_ERR_INVALID_ARG, err, TAG, "can't set eth->mac or eth->phy to null");
  176. // eth_driver contains an atomic variable, which should not be put in PSRAM
  177. eth_driver = heap_caps_calloc(1, sizeof(esp_eth_driver_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  178. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_NO_MEM, err, TAG, "no mem for eth_driver");
  179. const esp_timer_create_args_t check_link_timer_args = {
  180. .callback = eth_check_link_timer_cb,
  181. .name = "eth_link_timer",
  182. .arg = eth_driver,
  183. .skip_unhandled_events = true
  184. };
  185. ESP_GOTO_ON_ERROR(esp_timer_create(&check_link_timer_args, &eth_driver->check_link_timer), err, TAG, "create link timer failed");
  186. #if CONFIG_ETH_TRANSMIT_MUTEX
  187. eth_driver->transmit_mutex = xSemaphoreCreateMutex();
  188. ESP_GOTO_ON_FALSE(eth_driver->transmit_mutex, ESP_ERR_NO_MEM, err, TAG, "Failed to create transmit mutex");
  189. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  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. atomic_init(&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. // get default status of PHY autonegotiation (ultimately may also indicate if it is supported)
  217. ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, &eth_driver->auto_nego_en),
  218. err, TAG, "get autonegotiation status failed");
  219. ESP_LOGD(TAG, "new ethernet driver @%p", eth_driver);
  220. *out_hdl = eth_driver;
  221. return ESP_OK;
  222. err:
  223. if (eth_driver) {
  224. if (eth_driver->check_link_timer) {
  225. esp_timer_delete(eth_driver->check_link_timer);
  226. }
  227. #if CONFIG_ETH_TRANSMIT_MUTEX
  228. if (eth_driver->transmit_mutex) {
  229. vSemaphoreDelete(eth_driver->transmit_mutex);
  230. }
  231. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  232. free(eth_driver);
  233. }
  234. return ret;
  235. }
  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. #if CONFIG_ETH_TRANSMIT_MUTEX
  253. vSemaphoreDelete(eth_driver->transmit_mutex);
  254. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  255. ESP_GOTO_ON_ERROR(phy->deinit(phy), err, TAG, "deinit phy failed");
  256. ESP_GOTO_ON_ERROR(mac->deinit(mac), err, TAG, "deinit mac failed");
  257. free(eth_driver);
  258. err:
  259. return ret;
  260. }
  261. esp_err_t esp_eth_start(esp_eth_handle_t hdl)
  262. {
  263. esp_err_t ret = ESP_OK;
  264. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  265. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  266. esp_eth_phy_t *phy = eth_driver->phy;
  267. // check if driver has stopped
  268. esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
  269. ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_START),
  270. ESP_ERR_INVALID_STATE, err, TAG, "driver started already");
  271. // Autonegotiate link speed and duplex mode when enabled
  272. if (eth_driver->auto_nego_en == true) {
  273. ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_RESTART, &eth_driver->auto_nego_en), err, TAG, "phy negotiation failed");
  274. }
  275. ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(esp_eth_driver_t *), 0),
  276. err, TAG, "send ETHERNET_EVENT_START event failed");
  277. ESP_GOTO_ON_ERROR(phy->get_link(phy), err, TAG, "phy get link status failed");
  278. ESP_GOTO_ON_ERROR(esp_timer_start_periodic(eth_driver->check_link_timer, eth_driver->check_link_period_ms * 1000),
  279. err, TAG, "start link timer failed");
  280. err:
  281. return ret;
  282. }
  283. esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
  284. {
  285. esp_err_t ret = ESP_OK;
  286. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  287. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  288. esp_eth_mac_t *mac = eth_driver->mac;
  289. // check if driver has started
  290. esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_START;
  291. ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_STOP),
  292. ESP_ERR_INVALID_STATE, err, TAG, "driver not started yet");
  293. ESP_GOTO_ON_ERROR(esp_timer_stop(eth_driver->check_link_timer), err, TAG, "stop link timer failed");
  294. eth_link_t expected_link = ETH_LINK_UP;
  295. if (atomic_compare_exchange_strong(&eth_driver->link, &expected_link, ETH_LINK_DOWN)){
  296. // MAC is stopped by setting link down
  297. ESP_GOTO_ON_ERROR(mac->set_link(mac, ETH_LINK_DOWN), err, TAG, "ethernet mac set link failed");
  298. ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &eth_driver, sizeof(esp_eth_driver_t *), 0), err,
  299. TAG, "send ETHERNET_EVENT_DISCONNECTED event failed");
  300. }
  301. ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, &eth_driver, sizeof(esp_eth_driver_t *), 0),
  302. err, TAG, "send ETHERNET_EVENT_STOP event failed");
  303. err:
  304. return ret;
  305. }
  306. esp_err_t esp_eth_update_input_path(
  307. esp_eth_handle_t hdl,
  308. esp_err_t (*stack_input)(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv),
  309. void *priv)
  310. {
  311. esp_err_t ret = ESP_OK;
  312. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  313. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  314. eth_driver->priv = priv;
  315. eth_driver->stack_input = stack_input;
  316. err:
  317. return ret;
  318. }
  319. esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length)
  320. {
  321. esp_err_t ret = ESP_OK;
  322. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  323. if (atomic_load(&eth_driver->link) != ETH_LINK_UP) {
  324. ret = ESP_ERR_INVALID_STATE;
  325. ESP_LOGD(TAG, "Ethernet link is not up, can't transmit");
  326. goto err;
  327. }
  328. ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "can't set buf to null");
  329. ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "buf length can't be zero");
  330. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  331. esp_eth_mac_t *mac = eth_driver->mac;
  332. #if CONFIG_ETH_TRANSMIT_MUTEX
  333. if (xSemaphoreTake(eth_driver->transmit_mutex, pdMS_TO_TICKS(ESP_ETH_TX_TIMEOUT_MS)) == pdFALSE) {
  334. return ESP_ERR_TIMEOUT;
  335. }
  336. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  337. ret = mac->transmit(mac, buf, length);
  338. #if CONFIG_ETH_TRANSMIT_MUTEX
  339. xSemaphoreGive(eth_driver->transmit_mutex);
  340. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  341. err:
  342. return ret;
  343. }
  344. esp_err_t esp_eth_transmit_vargs(esp_eth_handle_t hdl, uint32_t argc, ...)
  345. {
  346. esp_err_t ret = ESP_OK;
  347. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  348. if (atomic_load(&eth_driver->link) != ETH_LINK_UP) {
  349. ret = ESP_ERR_INVALID_STATE;
  350. ESP_LOGD(TAG, "Ethernet link is not up, can't transmit");
  351. goto err;
  352. }
  353. va_list args;
  354. esp_eth_mac_t *mac = eth_driver->mac;
  355. #if CONFIG_ETH_TRANSMIT_MUTEX
  356. if (xSemaphoreTake(eth_driver->transmit_mutex, pdMS_TO_TICKS(ESP_ETH_TX_TIMEOUT_MS)) == pdFALSE) {
  357. return ESP_ERR_TIMEOUT;
  358. }
  359. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  360. va_start(args, argc);
  361. ret = mac->transmit_vargs(mac, argc, args);
  362. #if CONFIG_ETH_TRANSMIT_MUTEX
  363. xSemaphoreGive(eth_driver->transmit_mutex);
  364. #endif // CONFIG_ETH_TRANSMIT_MUTEX
  365. va_end(args);
  366. err:
  367. return ret;
  368. }
  369. esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
  370. {
  371. esp_err_t ret = ESP_OK;
  372. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  373. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  374. esp_eth_mac_t *mac = eth_driver->mac;
  375. esp_eth_phy_t *phy = eth_driver->phy;
  376. switch (cmd) {
  377. case ETH_CMD_S_MAC_ADDR:
  378. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
  379. ESP_GOTO_ON_ERROR(mac->set_addr(mac, (uint8_t *)data), err, TAG, "set mac address failed");
  380. break;
  381. case ETH_CMD_G_MAC_ADDR:
  382. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store mac addr");
  383. ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed");
  384. break;
  385. case ETH_CMD_S_PHY_ADDR:
  386. ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed");
  387. break;
  388. case ETH_CMD_G_PHY_ADDR:
  389. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store phy addr");
  390. ESP_GOTO_ON_ERROR(phy->get_addr(phy, (uint32_t *)data), err, TAG, "get phy address failed");
  391. break;
  392. case ETH_CMD_S_AUTONEGO:
  393. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set autonegotiation to null");
  394. // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
  395. 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");
  396. if (*(bool *)data == true) {
  397. ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_EN, &eth_driver->auto_nego_en), err, TAG, "phy negotiation enable failed");
  398. } else {
  399. ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_DIS, &eth_driver->auto_nego_en), err, TAG, "phy negotiation disable failed");
  400. }
  401. break;
  402. case ETH_CMD_G_AUTONEGO:
  403. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store autonegotiation configuration");
  404. *(bool *)data = eth_driver->auto_nego_en;
  405. break;
  406. case ETH_CMD_S_SPEED:
  407. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set speed to null");
  408. // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
  409. 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");
  410. 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");
  411. ESP_GOTO_ON_ERROR(phy->set_speed(phy, *(eth_speed_t *)data), err, TAG, "set speed mode failed");
  412. break;
  413. case ETH_CMD_G_SPEED:
  414. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store speed value");
  415. *(eth_speed_t *)data = eth_driver->speed;
  416. break;
  417. case ETH_CMD_S_PROMISCUOUS:
  418. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set promiscuous to null");
  419. ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
  420. break;
  421. case ETH_CMD_S_FLOW_CTRL:
  422. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set flow ctrl to null");
  423. ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
  424. ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed");
  425. break;
  426. case ETH_CMD_S_DUPLEX_MODE:
  427. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set duplex to null");
  428. 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");
  429. // check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
  430. 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");
  431. ESP_GOTO_ON_ERROR(phy->set_duplex(phy, *(eth_duplex_t *)data), err, TAG, "set duplex mode failed");
  432. break;
  433. case ETH_CMD_G_DUPLEX_MODE:
  434. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value");
  435. *(eth_duplex_t *)data = eth_driver->duplex;
  436. break;
  437. case ETH_CMD_S_PHY_LOOPBACK:
  438. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set loopback to null");
  439. ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
  440. break;
  441. case ETH_CMD_READ_PHY_REG:
  442. uint32_t phy_addr;
  443. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "invalid register read/write info");
  444. esp_eth_phy_reg_rw_data_t *phy_r_data = (esp_eth_phy_reg_rw_data_t *)data;
  445. ESP_GOTO_ON_FALSE(phy_r_data->reg_value_p, ESP_ERR_INVALID_ARG, err, TAG, "can't read registers to null");
  446. ESP_GOTO_ON_ERROR(phy->get_addr(phy, &phy_addr), err, TAG, "get phy address failed");
  447. ESP_GOTO_ON_ERROR(eth_driver->mediator.phy_reg_read(&eth_driver->mediator,
  448. phy_addr, phy_r_data->reg_addr, phy_r_data->reg_value_p), err, TAG, "failed to read PHY register");
  449. break;
  450. case ETH_CMD_WRITE_PHY_REG:
  451. ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "invalid register read/write info");
  452. esp_eth_phy_reg_rw_data_t *phy_w_data = (esp_eth_phy_reg_rw_data_t *)data;
  453. ESP_GOTO_ON_FALSE(phy_w_data->reg_value_p, ESP_ERR_INVALID_ARG, err, TAG, "can't write registers from null");
  454. ESP_GOTO_ON_ERROR(phy->get_addr(phy, &phy_addr), err, TAG, "get phy address failed");
  455. ESP_GOTO_ON_ERROR(eth_driver->mediator.phy_reg_write(&eth_driver->mediator,
  456. phy_addr, phy_w_data->reg_addr, *(phy_w_data->reg_value_p)), err, TAG, "failed to write PHY register");
  457. break;
  458. default:
  459. if (phy->custom_ioctl != NULL && cmd >= ETH_CMD_CUSTOM_PHY_CMDS) {
  460. ret = phy->custom_ioctl(phy, cmd, data);
  461. } else if (mac->custom_ioctl != NULL && cmd >= ETH_CMD_CUSTOM_MAC_CMDS) {
  462. ret = mac->custom_ioctl(mac, cmd, data);
  463. } else {
  464. ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
  465. }
  466. break;
  467. }
  468. err:
  469. return ret;
  470. }
  471. esp_err_t esp_eth_increase_reference(esp_eth_handle_t hdl)
  472. {
  473. esp_err_t ret = ESP_OK;
  474. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  475. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  476. atomic_fetch_add(&eth_driver->ref_count, 1);
  477. err:
  478. return ret;
  479. }
  480. esp_err_t esp_eth_decrease_reference(esp_eth_handle_t hdl)
  481. {
  482. esp_err_t ret = ESP_OK;
  483. esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
  484. ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
  485. atomic_fetch_sub(&eth_driver->ref_count, 1);
  486. err:
  487. return ret;
  488. }