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, ð_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, ð_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(ð_driver->ref_count, 1); 186 atomic_init(ð_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, ð_driver->mediator) == ESP_OK, "set mediator for mac failed", err_mediator, ESP_FAIL); 202 ETH_CHECK(phy->set_mediator(phy, ð_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(ð_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(ð_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(ð_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, ð_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(ð_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, ð_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(ð_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(ð_driver->ref_count, 1); 407 return ESP_OK; 408 err: 409 return ret; 410 }