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