From 13d00d8780282291358caa4fee949e1148fd97d6 Mon Sep 17 00:00:00 2001 From: Bogdan Kolendovskyy Date: Mon, 12 Feb 2024 16:06:46 +0100 Subject: [PATCH] Add driver for LAN867x Initial version of a driver for Microchip LAN867x. Implements all main functions for lan867x driver, as well as example utilizing esp-netif and lwIP. --- .github/workflows/build.yml | 6 +- .github/workflows/upload_component.yml | 2 +- .gitignore | 6 +- README.md | 1 + ethernet_init/Kconfig.projbuild | 6 + ethernet_init/ethernet_init.c | 7 +- .../simple-ethernet/main/idf_component.yml | 1 - ethernet_init/idf_component.yml | 4 +- lan867x/CMakeLists.txt | 3 + lan867x/LICENSE | 202 ++++++++++ lan867x/README.md | 48 +++ .../lan867x_tcp_sockets/client/CMakeLists.txt | 8 + .../client/main/CMakeLists.txt | 3 + .../lan867x_tcp_sockets/client/main/client.c | 72 ++++ .../client/main/idf_component.yml | 7 + .../lan867x_tcp_sockets/server/CMakeLists.txt | 8 + .../server/main/CMakeLists.txt | 3 + .../server/main/idf_component.yml | 7 + .../lan867x_tcp_sockets/server/main/server.c | 94 +++++ lan867x/idf_component.yml | 7 + lan867x/include/esp_eth_phy_lan867x.h | 44 +++ lan867x/src/esp_eth_phy_lan867x.c | 346 ++++++++++++++++++ 22 files changed, 877 insertions(+), 8 deletions(-) create mode 100644 lan867x/CMakeLists.txt create mode 100644 lan867x/LICENSE create mode 100644 lan867x/README.md create mode 100644 lan867x/examples/lan867x_tcp_sockets/client/CMakeLists.txt create mode 100644 lan867x/examples/lan867x_tcp_sockets/client/main/CMakeLists.txt create mode 100644 lan867x/examples/lan867x_tcp_sockets/client/main/client.c create mode 100644 lan867x/examples/lan867x_tcp_sockets/client/main/idf_component.yml create mode 100644 lan867x/examples/lan867x_tcp_sockets/server/CMakeLists.txt create mode 100644 lan867x/examples/lan867x_tcp_sockets/server/main/CMakeLists.txt create mode 100644 lan867x/examples/lan867x_tcp_sockets/server/main/idf_component.yml create mode 100644 lan867x/examples/lan867x_tcp_sockets/server/main/server.c create mode 100644 lan867x/idf_component.yml create mode 100644 lan867x/include/esp_eth_phy_lan867x.h create mode 100644 lan867x/src/esp_eth_phy_lan867x.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65d9f88..a55a3d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: idf_ver: ["latest"] - example: ["simple_switch", "switch_mode", "two_ports_mode"] + example: ["ksz8863/examples/simple_switch", "ksz8863/examples/switch_mode", "ksz8863/examples/two_ports_mode", "lan867x/examples/lan867x_tcp_sockets/client", "lan867x/examples/lan867x_tcp_sockets/server"] idf_target: ["esp32"] runs-on: ubuntu-20.04 @@ -23,5 +23,5 @@ jobs: shell: bash run: | . ${IDF_PATH}/export.sh - cd $GITHUB_WORKSPACE/esp_eth_drivers/ksz8863/examples/${{ matrix.example }} - idf.py build + cd $GITHUB_WORKSPACE/esp_eth_drivers/${{ matrix.example }} + idf.py build \ No newline at end of file diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 61c59ba..68ab195 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -14,6 +14,6 @@ jobs: - name: Upload components to the component registry uses: espressif/github-actions/upload_components@master with: - directories: "ksz8863;adin1200;ethernet_init" + directories: "ksz8863;adin1200;lan867x;ethernet_init" namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index ab727bd..1b8630d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ GPATH **/examples/**/sdkconfig **/examples/**/sdkconfig.old **/examples/**/build +**/examples/**/managed_components # Doc build artifacts docs/_build/ @@ -76,4 +77,7 @@ build dependencies.lock # ignore generated docs -docs/html \ No newline at end of file +docs/html + +# component hash file generated by idf +**/.component_hash \ No newline at end of file diff --git a/README.md b/README.md index cba948a..93b9551 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ List of currently supported chips: - [KSZ8863](ksz8863/README.md) - [ADIN1200](adin1200/README.md) +- [LAN867X](lan867x/README.md) ## Resources diff --git a/ethernet_init/Kconfig.projbuild b/ethernet_init/Kconfig.projbuild index c468d00..6a12a21 100644 --- a/ethernet_init/Kconfig.projbuild +++ b/ethernet_init/Kconfig.projbuild @@ -62,6 +62,12 @@ menu "Ethernet Configuration" The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041, KSZ8051, KSZ8061, KSZ8081, KSZ8091 Goto https://www.microchip.com for more information about them. + + config ETHERNET_PHY_LAN867X + bool "LAN867x" + help + The LAN8670/1/2 is a high-performance 10BASE-T1S single-pair Ethernet PHY transceiver for 10 Mbit/s half-duplex networking over a single pair of conductors + Goto https://www.microchip.com for more information about them. endchoice # ETHERNET_PHY_MODEL config ETHERNET_MDC_GPIO diff --git a/ethernet_init/ethernet_init.c b/ethernet_init/ethernet_init.c index f61f413..b013a87 100644 --- a/ethernet_init/ethernet_init.c +++ b/ethernet_init/ethernet_init.c @@ -15,7 +15,9 @@ #if CONFIG_ETH_USE_SPI_ETHERNET #include "driver/spi_master.h" #endif // CONFIG_ETH_USE_SPI_ETHERNET -#include "ethernet_init.h" +#if CONFIG_ETHERNET_PHY_LAN867X +#include "esp_eth_phy_lan867x.h" +#endif // CONFIG_ETHERNET_PHY_LAN867X #if CONFIG_ETHERNET_SPI_NUMBER @@ -172,6 +174,9 @@ static esp_eth_handle_t eth_init_internal(eth_device *dev_out) #elif CONFIG_ETHERNET_PHY_KSZ80XX dev_out->phy = esp_eth_phy_new_ksz80xx(&phy_config); sprintf(dev_out->dev_info.name, "KSZ80XX"); +#elif CONFIG_ETHERNET_PHY_LAN867X + dev_out->phy = esp_eth_phy_new_lan867x(&phy_config); + sprintf(dev_out->dev_info.name, "LAN867x"); #endif // Init Ethernet driver to default and install it diff --git a/ethernet_init/examples/simple-ethernet/main/idf_component.yml b/ethernet_init/examples/simple-ethernet/main/idf_component.yml index 468e4cc..62a3d86 100644 --- a/ethernet_init/examples/simple-ethernet/main/idf_component.yml +++ b/ethernet_init/examples/simple-ethernet/main/idf_component.yml @@ -3,6 +3,5 @@ dependencies: version: '>=5.0' ethernet_init: version: '*' - # For local development use the local copy of the component override_path: '../../../' \ No newline at end of file diff --git a/ethernet_init/idf_component.yml b/ethernet_init/idf_component.yml index 4c2affd..b439dee 100644 --- a/ethernet_init/idf_component.yml +++ b/ethernet_init/idf_component.yml @@ -1,6 +1,8 @@ dependencies: idf: version: '>=5.0' + lan867x: + override_path: "../lan867x" description: This component initializes Ethernet driver based on Espressif IoT Development Framework Configuration. url: https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init -version: 0.0.8 +version: 0.1.0 diff --git a/lan867x/CMakeLists.txt b/lan867x/CMakeLists.txt new file mode 100644 index 0000000..7aed9ab --- /dev/null +++ b/lan867x/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "src/esp_eth_phy_lan867x.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES log esp_eth) \ No newline at end of file diff --git a/lan867x/LICENSE b/lan867x/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/lan867x/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/lan867x/README.md b/lan867x/README.md new file mode 100644 index 0000000..2dc4060 --- /dev/null +++ b/lan867x/README.md @@ -0,0 +1,48 @@ +# Microchip LAN867x Ethernet PHY Driver + +## Overview + +The LAN8670/1/2 is a high-performance 10BASE-T1S single-pair Ethernet PHY transceiver for 10 Mbit/s half-duplex networking over a single pair of conductors. +More information about the chip can be found on the product page: [LAN8670](https://www.microchip.com/en-us/product/lan8670), [LAN8671](https://www.microchip.com/en-us/product/lan8671). + +## ESP-IDF Usage + +Add this component from [IDF Component Manager](https://components.espressif.com/) to your project using `idf.py add-dependency` and include `esp_eth_phy_lan867x.h`, + +```c +#include "esp_eth_phy_lan867x.h" +``` + +create a `phy` driver instance by calling `esp_eth_phy_new_lan867x()` + +```c +eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + +// Update PHY config based on board specific configuration +phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR; +phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO; +esp_eth_phy_t *phy = esp_eth_phy_new_lan867x(&phy_config); +``` + +#### You may need to setup the PLCA + +Depending on the task you are doing some additional setup may be required. +PLCA configuration is done using `esp_eth_ioctl` function with one of following commands. + +| Command | Argument | Action | +|------------------------------------|------------------|-----------------------------------------------------------------------------------------------------------| +| LAN867X_ETH_CMD_S_EN_PLCA | bool* enable | Enable (if true) or disable PLCA | +| LAN867X_ETH_CMD_G_EN_PLCA | bool* enable | Write PLCA status (true if enabled) to the location via pointer | +| LAN867X_ETH_CMD_S_PLCA_NCNT | uint8_t* count | Set node count to the value passed through the pointer | +| LAN867X_ETH_CMD_G_PLCA_NCNT | uint8_t* count | Write node count configured in the PLCA to the location via pointer | +| LAN867X_ETH_CMD_S_PLCA_ID | uint8_t* id | Set ID to the value passed through the pointer | +| LAN867X_ETH_CMD_G_PLCA_ID | uint8_t* id | Write ID configured in the PLCA to the location via pointer | +| LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY | uint8_t* node_id | Add additional transmit opportunity for chosen node | +| LAN867X_ETH_CMD_RM_TX_OPPORTUNITY | uint8_t* node_id | Remove additional transmit opportunity for chosen node | +| LAN867X_ETH_CMD_S_MAX_BURST_COUNT | uint8_t* maxcnt | Set max count of additonal packets transmitted during one frame, or 0 to disable PLCA burst | +| LAN867X_ETH_CMD_G_MAX_BURST_COUNT | uint8_t* maxcnt | Write max count of additonal packets transmitted during one frame to the location via pointer | +| LAN867X_ETH_CMD_S_BURST_TIMER | uint8_t* time | Set time during which additional packets can be sent in incriments of 100ns | +| LAN867X_ETH_CMD_G_BURST_TIMER | uint8_t* time | Write time during which additional packets can be sent in increments of 100ns to the location via pointer | +| LAN768X_ETH_CMD_PLCA_RST | | Perform reset of the PLCA | + +and you are ready to use the Ethernet driver as you are used to. For more information of how to use ESP-IDF Ethernet driver, visit [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html). diff --git a/lan867x/examples/lan867x_tcp_sockets/client/CMakeLists.txt b/lan867x/examples/lan867x_tcp_sockets/client/CMakeLists.txt new file mode 100644 index 0000000..fed1ec6 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/client/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(client) diff --git a/lan867x/examples/lan867x_tcp_sockets/client/main/CMakeLists.txt b/lan867x/examples/lan867x_tcp_sockets/client/main/CMakeLists.txt new file mode 100644 index 0000000..00f0d16 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/client/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "client.c" + PRIV_REQUIRES esp_eth esp_netif + INCLUDE_DIRS ".") diff --git a/lan867x/examples/lan867x_tcp_sockets/client/main/client.c b/lan867x/examples/lan867x_tcp_sockets/client/main/client.c new file mode 100644 index 0000000..a28945c --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/client/main/client.c @@ -0,0 +1,72 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "ethernet_init.h" +#include "lwip/sockets.h" + +#define SOCKET_ADDRESS "192.168.1.1" +#define SOCKET_PORT 5000 +#define SOCKET_MAX_LENGTH 128 + +static const char *TAG = "lan867x_client"; +static SemaphoreHandle_t xGotIpSemaphore; + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *) data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + xSemaphoreGive(xGotIpSemaphore); +} + +void app_main(void) +{ + // Create default event loop that running in background + ESP_ERROR_CHECK(esp_event_loop_create_default()); + // Initialize semaphore + xGotIpSemaphore = xSemaphoreCreateBinary(); + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + // Initialize TCP/IP network interface aka the esp-netif (should be called only once in application) + ESP_ERROR_CHECK(esp_netif_init()); + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(&cfg); + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + // Register user defined event handers + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + // Start Ethernet driver + esp_eth_start(eth_handles[0]); + // Initialize Berkley socket + char txbuffer[SOCKET_MAX_LENGTH] = {0}; + int client_fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + inet_pton(AF_INET, SOCKET_ADDRESS, &serv_addr.sin_addr); + serv_addr.sin_port = htons(SOCKET_PORT); + // Wait until IP address is assigned to this device + xSemaphoreTake(xGotIpSemaphore, portMAX_DELAY); + int transmission_cnt = 0; + while (1) { + connect(client_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 via LAN867x", ++transmission_cnt); + int bytesSent = send(client_fd, txbuffer, strlen(txbuffer), 0); + ESP_LOGI(TAG, "Sent tranmission #%d which was %d bytes long.", transmission_cnt, bytesSent); + vTaskDelay(pdMS_TO_TICKS(500)); + } + close(client_fd); +} diff --git a/lan867x/examples/lan867x_tcp_sockets/client/main/idf_component.yml b/lan867x/examples/lan867x_tcp_sockets/client/main/idf_component.yml new file mode 100644 index 0000000..3841543 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/client/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + lan867x: + override_path: "../../../../../lan867x" + ethernet_init: + override_path: "../../../../../ethernet_init" \ No newline at end of file diff --git a/lan867x/examples/lan867x_tcp_sockets/server/CMakeLists.txt b/lan867x/examples/lan867x_tcp_sockets/server/CMakeLists.txt new file mode 100644 index 0000000..5938f05 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/server/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(server) diff --git a/lan867x/examples/lan867x_tcp_sockets/server/main/CMakeLists.txt b/lan867x/examples/lan867x_tcp_sockets/server/main/CMakeLists.txt new file mode 100644 index 0000000..b4d0285 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/server/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "server.c" + PRIV_REQUIRES esp_eth esp_netif + INCLUDE_DIRS ".") diff --git a/lan867x/examples/lan867x_tcp_sockets/server/main/idf_component.yml b/lan867x/examples/lan867x_tcp_sockets/server/main/idf_component.yml new file mode 100644 index 0000000..3841543 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/server/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + lan867x: + override_path: "../../../../../lan867x" + ethernet_init: + override_path: "../../../../../ethernet_init" \ No newline at end of file diff --git a/lan867x/examples/lan867x_tcp_sockets/server/main/server.c b/lan867x/examples/lan867x_tcp_sockets/server/main/server.c new file mode 100644 index 0000000..eb6d370 --- /dev/null +++ b/lan867x/examples/lan867x_tcp_sockets/server/main/server.c @@ -0,0 +1,94 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_netif.h" +#include "esp_netif_net_stack.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "ethernet_init.h" +#include "lwip/sockets.h" + +#define SOCKET_PORT 5000 +#define LISTENER_MAX_QUEUE 1 +#define SOCKET_MAX_LENGTH 128 + +static const char *TAG = "lan867x_server"; + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *) data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); +} + +static void my_event_connected_handler(void *esp_netif, esp_event_base_t base, int32_t event_id, void *data) +{ + esp_netif_dhcps_start(esp_netif); +} + +void app_main(void) +{ + // Create default event loop that running in background + ESP_ERROR_CHECK(esp_event_loop_create_default()); + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + // Initialize TCP/IP network interface aka the esp-netif (should be called only once in application) + ESP_ERROR_CHECK(esp_netif_init()); + esp_netif_ip_info_t ip_info = { + .ip = {.addr = ESP_IP4TOADDR(192, 168, 1, 1)}, + .netmask = {.addr = ESP_IP4TOADDR(255, 255, 255, 0)}, + .gw = {.addr = ESP_IP4TOADDR(192, 168, 1, 255)} + }; + const esp_netif_inherent_config_t eth_behav_cfg = { + .get_ip_event = IP_EVENT_ETH_GOT_IP, + .lost_ip_event = 0, + .flags = ESP_NETIF_DHCP_SERVER, + .ip_info = &ip_info, + .if_key = "ETH_DHCPS", + .if_desc = "eth", + .route_prio = 50 + }; + esp_netif_config_t eth_as_dhcps_cfg = { .base = ð_behav_cfg, .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH }; + esp_netif_t *eth_netif = esp_netif_new(ð_as_dhcps_cfg); + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + // Register user defined event handers + esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, my_event_connected_handler, eth_netif); + esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler, NULL); + // Stop dhcp client and set a static IP address + esp_netif_dhcpc_stop(eth_netif); + esp_netif_set_ip_info(eth_netif, &ip_info); + // Start Ethernet driver state machine + ESP_ERROR_CHECK(esp_eth_start(eth_handles[0])); + // Initialize Berkley socket which will listen on port SOCKET_PORT for transmission from client + char rxbuffer[SOCKET_MAX_LENGTH] = {0}; + int server_fd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in address; + socklen_t addrlen = sizeof(address); + int opt = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(SOCKET_PORT); + bind(server_fd, (struct sockaddr *)&address, sizeof(address)); + // listen and wait for transmission to come + listen(server_fd, LISTENER_MAX_QUEUE); + int new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen); + while (1) { + // when transmission comes - print it + recv(new_socket, rxbuffer, SOCKET_MAX_LENGTH, 0); + printf("Received: \"%s\" from %s.\n", rxbuffer, inet_ntoa(address.sin_addr)); + } + close(new_socket); +} \ No newline at end of file diff --git a/lan867x/idf_component.yml b/lan867x/idf_component.yml new file mode 100644 index 0000000..a8f9daa --- /dev/null +++ b/lan867x/idf_component.yml @@ -0,0 +1,7 @@ +version: "0.9.0" +targets: + - esp32 +description: LAN867x Ethernet PHY Driver +url: https://github.com/espressif/esp-eth-drivers/tree/master/lan867x +dependencies: + idf: ">=5.3" \ No newline at end of file diff --git a/lan867x/include/esp_eth_phy_lan867x.h b/lan867x/include/esp_eth_phy_lan867x.h new file mode 100644 index 0000000..8effc80 --- /dev/null +++ b/lan867x/include/esp_eth_phy_lan867x.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_eth_phy.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LAN867X_ETH_CMD_S_EN_PLCA = ETH_CMD_CUSTOM_PHY_CMDS, /*!< Enable or disable PLCA */ + LAN867X_ETH_CMD_G_EN_PLCA, /*!< Get status if PLCA is disabled or enabled*/ + LAN867X_ETH_CMD_S_PLCA_NCNT, /*!< Set PLCA node count */ + LAN867X_ETH_CMD_G_PLCA_NCNT, /*!< Get PLCA node count */ + LAN867X_ETH_CMD_S_PLCA_ID, /*!< Set PLCA ID */ + LAN867X_ETH_CMD_G_PLCA_ID, /*!< Get PLCA ID */ + LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY, /*!< Add additional transmit opportunity for chosen node */ + LAN867X_ETH_CMD_RM_TX_OPPORTUNITY, /*!< Remove additional transmit opportunity for chosen node */ + LAN867X_ETH_CMD_S_MAX_BURST_COUNT, /*!< Set max count of additional packets, set to 0 to disable */ + LAN867X_ETH_CMD_G_MAX_BURST_COUNT, /*!< Get max count of additional packets, set to 0 to disable */ + LAN867X_ETH_CMD_S_BURST_TIMER, /*!< Set time after transmission during which node is allowed to transmit more packets in incriments of 100ns */ + LAN867X_ETH_CMD_G_BURST_TIMER, /*!< Get time after transmission during which node is allowed to transmit more packets in incriments of 100ns */ + LAN768X_ETH_CMD_PLCA_RST /*!< Reset PLCA*/ +} phy_lan867x_custom_io_cmd_t; + +/** +* @brief Create a PHY instance of LAN867x +* +* @param[in] config: configuration of PHY +* +* @return +* - instance: create PHY instance successfully +* - NULL: create PHY instance failed because some error occurred +*/ +esp_eth_phy_t *esp_eth_phy_new_lan867x(const eth_phy_config_t *config); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lan867x/src/esp_eth_phy_lan867x.c b/lan867x/src/esp_eth_phy_lan867x.c new file mode 100644 index 0000000..331811c --- /dev/null +++ b/lan867x/src/esp_eth_phy_lan867x.c @@ -0,0 +1,346 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_eth_phy_802_3.h" +#include "esp_eth_driver.h" +#include "esp_eth_phy_lan867x.h" + +static const char *TAG = "lan867x"; + +/***************List of Supported Models***************/ +#define LAN867X_MODEL_NUM 0x16 +#define LAN867X_OUI 0xC0001C + +static const uint8_t supported_models[] = { + LAN867X_MODEL_NUM, +}; + +/***************Vendor Specific Register***************/ +typedef union { + struct { + uint32_t oui_bits_2_9 : 8; /*!< Organizationally Unique Identifier(OUI) bits 3 to 10 */ + uint32_t oui_bits_10_17 : 8; /*!< Organizationally Unique Identifier(OUI) bits 11 to 18 */ + }; + uint32_t val; +} lan867x_phyidr1_reg_t; +#define ETH_PHY_IDR1_REG_ADDR (0x02) + +typedef union { + struct { + uint32_t model_revision : 4; /*!< Model revision number */ + uint32_t vendor_model : 6; /*!< Vendor model number */ + uint32_t oui_bits_18_23 : 6; /*!< Organizationally Unique Identifier(OUI) bits 19 to 24 */ + }; + uint32_t val; +} lan867x_phyidr2_reg_t; +#define ETH_PHY_IDR2_REG_ADDR (0x03) + +typedef union { + struct { + uint32_t reserved1 : 14; // Reserved + uint32_t rst : 1; // PLCA Reset + uint32_t en : 1; // PLCA Enable + }; + uint32_t val; +} lan867x_plca_ctrl0_reg_t; +#define ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR (0xCA01) + +typedef union { + struct { + uint8_t id; // PLCA ID + uint8_t ncnt; // Node count + }; + uint32_t val; +} lan867x_plca_ctrl1_reg_t; +#define ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR (0xCA02) + +typedef union { + struct { + uint8_t maxbc; // Maximum burst count + uint8_t btmr; // Burst timer + }; + uint32_t val; +} lan867x_plca_burst_reg_t; +#define ETH_PHY_PLCA_BURST_REG_MMD_ADDR (0xCA05) + +typedef union { + struct { + uint8_t entries[2]; + }; + uint32_t val; +} lan867x_plca_multiple_id_reg_t; +#define ETH_PHY_PLCA_MULTID_BASE_MMD_ADDR (0x0030) + +typedef struct { + phy_802_3_t phy_802_3; +} phy_lan867x_t; + +#define MISC_REGISTERS_DEVICE 0x1f + +/***********Custom functions implementations***********/ +esp_err_t esp_eth_phy_lan867x_read_oui(phy_802_3_t *phy_802_3, uint32_t *oui) +{ + esp_err_t ret = ESP_OK; + lan867x_phyidr1_reg_t id1; + lan867x_phyidr2_reg_t id2; + esp_eth_mediator_t *eth = phy_802_3->eth; + + ESP_GOTO_ON_FALSE(oui != NULL, ESP_ERR_INVALID_ARG, err, TAG, "oui can't be null"); + + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed"); + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed"); + + *oui = (id2.oui_bits_18_23 << 18) + ((id1.oui_bits_10_17 << 10) + (id1.oui_bits_2_9 << 2)); + return ESP_OK; +err: + return ret; +} + +static esp_err_t lan867x_update_link_duplex_speed(phy_lan867x_t *lan867x) +{ + esp_err_t ret = ESP_OK; + esp_eth_mediator_t *eth = lan867x->phy_802_3.eth; + uint32_t addr = lan867x->phy_802_3.addr; + bmcr_reg_t bmcr; + bmsr_reg_t bmsr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed"); + eth_speed_t speed = bmcr.speed_select ? ETH_SPEED_100M : ETH_SPEED_10M; + eth_duplex_t duplex = bmcr.duplex_mode ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF; + eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + // This will be ran exactly once, when everything is setting up + if (lan867x->phy_802_3.link_status != link) { + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed), err, TAG, "change speed failed"); + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex), err, TAG, "change duplex failed"); + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed"); + lan867x->phy_802_3.link_status = link; + } + return ESP_OK; +err: + return ret; +} + +static esp_err_t lan867x_get_link(esp_eth_phy_t *phy) +{ + esp_err_t ret = ESP_OK; + phy_lan867x_t *lan867x = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_lan867x_t, phy_802_3); + + /* Update information about link, speed, duplex */ + ESP_GOTO_ON_ERROR(lan867x_update_link_duplex_speed(lan867x), err, TAG, "update link duplex speed failed"); + return ESP_OK; +err: + return ret; +} + +static esp_err_t lan867x_init(esp_eth_phy_t *phy) +{ + esp_err_t ret = ESP_OK; + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); + + /* Basic PHY init */ + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY"); + /* Check PHY ID */ + uint32_t oui; + uint8_t model; + ESP_GOTO_ON_ERROR(esp_eth_phy_lan867x_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed"); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_manufac_info(phy_802_3, &model, NULL), err, TAG, "read manufacturer's info failed"); + ESP_GOTO_ON_FALSE(oui == LAN867X_OUI, ESP_FAIL, err, TAG, "wrong chip OUI %lx (expected %x)", oui, LAN867X_OUI); + + bool supported_model = false; + for (unsigned int i = 0; i < sizeof(supported_models); i++) { + if (model == supported_models[i]) { + supported_model = true; + break; + } + } + ESP_GOTO_ON_FALSE(supported_model, ESP_FAIL, err, TAG, "unsupported chip model %x", model); + return ESP_OK; +err: + return ret; +} + +static esp_err_t lan867x_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat) +{ + switch (cmd) { + case ESP_ETH_PHY_AUTONEGO_RESTART: + // Fallthrough + case ESP_ETH_PHY_AUTONEGO_EN: + // Fallthrough + case ESP_ETH_PHY_AUTONEGO_DIS: + return ESP_ERR_NOT_SUPPORTED; // no autonegotiation operations are supported + case ESP_ETH_PHY_AUTONEGO_G_STAT: + // since autonegotiation is not supported it is always indicated disabled + *autonego_en_stat = false; + break; + default: + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +static esp_err_t lan867x_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t lan867x_set_speed(esp_eth_phy_t *phy, eth_speed_t speed) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t lan867x_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t lan867x_custom_ioctl(esp_eth_phy_t *phy, uint32_t cmd, void *data) +{ + esp_err_t ret; + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); + esp_eth_mediator_t *eth = phy_802_3->eth; + lan867x_plca_ctrl0_reg_t plca_ctrl0; + lan867x_plca_ctrl1_reg_t plca_ctrl1; + lan867x_plca_burst_reg_t plca_burst_reg; + lan867x_plca_multiple_id_reg_t plca_multiple_id_reg; + switch (cmd) { + case LAN867X_ETH_CMD_S_EN_PLCA: + // check if loopback is enabled if user wants to enable PLCA + bool plca_en = *(bool *)data; + if (plca_en) { + bmcr_reg_t bmcr; + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); + ESP_GOTO_ON_FALSE(bmcr.en_loopback == false, ESP_ERR_INVALID_STATE, err, TAG, "PLCA can't be enabled at the same time as loopback"); + } + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, &plca_ctrl0.val), err, TAG, "read PLCA_CTRL0 failed"); + plca_ctrl0.en = (*(bool *)data != false); // anything but 0 will be regarded as true + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, plca_ctrl0.val), err, TAG, "write PLCA_CTRL0 failed"); + break; + case LAN867X_ETH_CMD_G_EN_PLCA: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, &plca_ctrl0.val), err, TAG, "read PLCA_CTRL0 failed"); + *((bool *)data) = plca_ctrl0.en; + break; + case LAN867X_ETH_CMD_S_PLCA_NCNT: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, &plca_ctrl1.val), err, TAG, "read PLCA_CTRL1 failed"); + plca_ctrl1.ncnt = *((uint8_t *) data); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, plca_ctrl1.val), err, TAG, "write PLCA_CTRL1 failed"); + break; + case LAN867X_ETH_CMD_G_PLCA_NCNT: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, &plca_ctrl1.val), err, TAG, "read PLCA_CTRL1 failed"); + *((uint8_t *) data) = plca_ctrl1.ncnt; + break; + case LAN867X_ETH_CMD_S_PLCA_ID: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, &plca_ctrl1.val), err, TAG, "read PLCA_CTRL1 failed"); + plca_ctrl1.id = *((uint8_t *) data); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, plca_ctrl1.val), err, TAG, "write PLCA_CTRL1 failed"); + break; + case LAN867X_ETH_CMD_G_PLCA_ID: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL1_REG_MMD_ADDR, &plca_ctrl1.val), err, TAG, "read PLCA_CTRL1 failed"); + *((uint8_t *) data) = plca_ctrl1.id; + break; + case LAN768X_ETH_CMD_PLCA_RST: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, &plca_ctrl0.val), err, TAG, "read PLCA_CTRL0 failed"); + plca_ctrl0.rst = true; + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_CTRL0_REG_MMD_ADDR, plca_ctrl0.val), err, TAG, "write PLCA_CTRL0 failed"); + break; + case LAN867X_ETH_CMD_ADD_TX_OPPORTUNITY: + // Transmit opportunities are stored in four registers + // Additional transmit opportunity is assigned if value is not 0x00 or 0xff + // So the algorithm is to find first 0x00 or 0xff and replace with id + for (uint16_t i = 0; i < 4; i++) { + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_MULTID_BASE_MMD_ADDR + i, &plca_multiple_id_reg.val), err, TAG, "read MULTID%d failed", i); + for (uint8_t j = 0; j < 2; j++) { + if (plca_multiple_id_reg.entries[j] == 0x00 || plca_multiple_id_reg.entries[j] == 0xff) { + plca_multiple_id_reg.entries[j] = *((uint8_t *) data); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_MULTID_BASE_MMD_ADDR + i, plca_multiple_id_reg.val), err, TAG, "write MULTID%d failed", i); + return ESP_OK; + } + } + } + ESP_GOTO_ON_FALSE(false, ESP_ERR_NO_MEM, err, TAG, "Unable to add additional transmit opportunity for 0x%02x. Maximum amount (8) reached.", *((uint8_t *) data)); + break; + case LAN867X_ETH_CMD_RM_TX_OPPORTUNITY: + // Look for the first occurance of id and replace it with 0x00 + for (uint16_t i = 0; i < 4; i++) { + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_MULTID_BASE_MMD_ADDR + i, &plca_multiple_id_reg.val), err, TAG, "read MULTID%d failed", i); + for (uint8_t j = 0; j < 2; j++) { + if (plca_multiple_id_reg.entries[j] == *((uint8_t *) data)) { + plca_multiple_id_reg.entries[j] = 0x00; + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_MULTID_BASE_MMD_ADDR + i, plca_multiple_id_reg.val), err, TAG, "write MULTID%d failed", i); + return ESP_OK; + } + } + } + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_FOUND, err, TAG, "Unable to remove additional transmit opportunity for 0x%02x since it doesn't have one already.", *((uint8_t *) data)); + break; + case LAN867X_ETH_CMD_S_MAX_BURST_COUNT: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_BURST_REG_MMD_ADDR, &plca_burst_reg.val), err, TAG, "read PLCA_BURST failed"); + plca_burst_reg.maxbc = *((uint8_t *) data); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_BURST_REG_MMD_ADDR, plca_burst_reg.val), err, TAG, "write PLCA_BURST failed"); + break; + case LAN867X_ETH_CMD_G_MAX_BURST_COUNT: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_BURST_REG_MMD_ADDR, &plca_burst_reg.val), err, TAG, "read PLCA_BURST failed"); + *((uint8_t *) data) = plca_burst_reg.maxbc; + break; + case LAN867X_ETH_CMD_S_BURST_TIMER: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_BURST_REG_MMD_ADDR, &plca_burst_reg.val), err, TAG, "read PLCA_BURST failed"); + plca_burst_reg.btmr = *((uint8_t *) data); + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_write_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_BURST_REG_MMD_ADDR, plca_burst_reg.val), err, TAG, "write PLCA_BURST failed"); + break; + case LAN867X_ETH_CMD_G_BURST_TIMER: + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_mmd_register(phy_802_3, MISC_REGISTERS_DEVICE, ETH_PHY_PLCA_BURST_REG_MMD_ADDR, &plca_burst_reg.val), err, TAG, "read PLCA_BURST failed"); + *((uint8_t *) data) = plca_burst_reg.btmr; + break; + default: + ret = ESP_ERR_INVALID_ARG; + break; + } + return ESP_OK; +err: + return ret; +} + +static esp_err_t lan867x_loopback(esp_eth_phy_t *phy, bool enable) +{ + esp_err_t ret = ESP_OK; + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); + // For loopback to work PLCA must be disabled + bool plca_status = false; + lan867x_custom_ioctl(phy, LAN867X_ETH_CMD_G_EN_PLCA, &plca_status); + ESP_GOTO_ON_FALSE(plca_status == false, ESP_ERR_INVALID_STATE, err, TAG, "Unable to set loopback while PLCA is enabled. Disable it to use loopback"); + return esp_eth_phy_802_3_loopback(phy_802_3, enable); +err: + return ret; +} + +esp_eth_phy_t *esp_eth_phy_new_lan867x(const eth_phy_config_t *config) +{ + esp_eth_phy_t *ret = NULL; + phy_lan867x_t *lan867x = calloc(1, sizeof(phy_lan867x_t)); + ESP_GOTO_ON_FALSE(lan867x, NULL, err, TAG, "calloc lan867x failed"); + ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&lan867x->phy_802_3, config) == ESP_OK, + NULL, err, TAG, "configuration initialization of PHY 802.3 failed"); + + // redefine functions which need to be customized for sake of LAN867x + lan867x->phy_802_3.parent.init = lan867x_init; + lan867x->phy_802_3.parent.get_link = lan867x_get_link; + lan867x->phy_802_3.parent.autonego_ctrl = lan867x_autonego_ctrl; + lan867x->phy_802_3.parent.set_speed = lan867x_set_speed; + lan867x->phy_802_3.parent.set_duplex = lan867x_set_duplex; + lan867x->phy_802_3.parent.loopback = lan867x_loopback; + lan867x->phy_802_3.parent.custom_ioctl = lan867x_custom_ioctl; + lan867x->phy_802_3.parent.advertise_pause_ability = lan867x_advertise_pause_ability; + + return &lan867x->phy_802_3.parent; +err: + if (lan867x != NULL) { + free(lan867x); + } + return ret; +}