├── examples ├── pio_example │ ├── .gitignore │ ├── test │ │ └── README │ ├── platformio.ini │ ├── lib │ │ └── README │ ├── include │ │ └── README │ └── src │ │ └── main.cpp ├── FOTA │ ├── bin │ │ ├── firmware.bin │ │ ├── firmware.gz │ │ ├── firmware.zz │ │ ├── firmware.gz.json │ │ ├── firmware.json │ │ └── firmware.zz.json │ └── FOTA.ino └── ETH_ENC28J60 │ └── ETH_ENC28J60.ino ├── extras └── logo.png ├── .gitignore ├── library.properties ├── .github └── workflows │ └── lint.yml ├── README.md ├── license └── src ├── extmod ├── esp_eth_spi_enc28j60.c ├── esp_eth_enc28j60.h ├── emac_ext_struct.h ├── enc28j60.h ├── esp_eth_phy_enc28j60.c └── esp_eth_mac_enc28j60.c ├── ESP32-ENC28J60.h └── ESP32-ENC28J60.cpp /examples/pio_example/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | -------------------------------------------------------------------------------- /extras/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-ENC28J60/HEAD/extras/logo.png -------------------------------------------------------------------------------- /examples/FOTA/bin/firmware.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-ENC28J60/HEAD/examples/FOTA/bin/firmware.bin -------------------------------------------------------------------------------- /examples/FOTA/bin/firmware.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-ENC28J60/HEAD/examples/FOTA/bin/firmware.gz -------------------------------------------------------------------------------- /examples/FOTA/bin/firmware.zz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-ENC28J60/HEAD/examples/FOTA/bin/firmware.zz -------------------------------------------------------------------------------- /examples/FOTA/bin/firmware.gz.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "esp32-fota-http", 3 | "version": "1.1", 4 | "host": "github.com", 5 | "port": 443, 6 | "bin": "/tobozo/ESP32-ENC28J60/blob/main/examples/FOTA/bin/firmware.gz?raw=true" 7 | } 8 | -------------------------------------------------------------------------------- /examples/FOTA/bin/firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "esp32-fota-http", 3 | "version": "1.1", 4 | "host": "github.com", 5 | "port": 443, 6 | "bin": "/tobozo/ESP32-ENC28J60/blob/main/examples/FOTA/bin/firmware.bin?raw=true" 7 | } 8 | 9 | -------------------------------------------------------------------------------- /examples/FOTA/bin/firmware.zz.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "esp32-fota-http", 3 | "version": "1.1", 4 | "host": "github.com", 5 | "port": 443, 6 | "bin": "/tobozo/ESP32-ENC28J60/blob/main/examples/FOTA/bin/firmware.zz?raw=true" 7 | } 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /.vs 6 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32-ENC28J60 2 | version=2.0.1 3 | author=Tobozo 4 | maintainer=tobozo@noreply.github.com 5 | sentence=Enables network connection (local and Internet) with ENC28J60 using the ESP32 Ethernet. 6 | paragraph=With this library you can instantiate Servers, Clients and send/receive UDP packets through Ethernet. The IP address can be assigned statically or through a DHCP. The library can also manage DNS. 7 | category=Communication 8 | url=https://github.com/tobozo/ESP32-ENC28J60 9 | architectures=esp32 10 | includes=ESP32-ENC28J60.h 11 | -------------------------------------------------------------------------------- /examples/pio_example/test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Test Runner and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/en/latest/advanced/unit-testing/index.html 12 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.ino' 7 | - '**.ini' 8 | - '**.cpp' 9 | - '**.hpp' 10 | - '**.h' 11 | - '**.c' 12 | - '**lint.yml' 13 | 14 | pull_request: 15 | branches: 16 | - main 17 | 18 | workflow_dispatch: 19 | 20 | jobs: 21 | 22 | lint: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: arduino/arduino-lint-action@v1 27 | with: 28 | project-type: library 29 | library-manager: false 30 | recursive: true 31 | -------------------------------------------------------------------------------- /examples/pio_example/platformio.ini: -------------------------------------------------------------------------------- 1 | [platformio] 2 | description = ESP32-ENC28J60 test sketch for platformio 3 | default_envs = esp32_2_0_5 4 | 5 | [env] 6 | framework = arduino 7 | lib_deps = ESP32-ENC28J60 8 | 9 | 10 | [env:esp32_2_0_5] 11 | board = esp32dev 12 | ; using tasmota platform instead of the official to ensure all latest patches are applied 13 | platform = https://github.com/tasmota/platform-espressif32 14 | ; using package 2.0.5 as eth_* support is instable before this version 15 | platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/releases/download/2.0.5/esp32-2.0.5.zip 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-ENC28J60 2 | 3 | This Ethernet Library is a copy of the official [esp32-arduino Ethernet Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/Ethernet) with added support for ENC28J60 boards and all other board types removed. 4 | 5 | ![image](https://user-images.githubusercontent.com/1893754/192626089-dc36f546-d212-4f6b-b0bb-90da6538e291.png) 6 | 7 | Low level code comes from the [eth_enc28j60 component](https://github.com/espressif/esp-idf/tree/master/examples/ethernet/enc28j60/components/eth_enc28j60) found in the esp-idf examples folder. 8 | 9 | For a more thorough implementation see [@khoih-prog](https://github.com/khoih-prog)'s [WebServer_ESP32_ENC](https://github.com/khoih-prog/WebServer_ESP32_ENC) and [AsyncWebServer_ESP32_ENC](https://github.com/khoih-prog/AsyncWebServer_ESP32_ENC) 10 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 tobozo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/pio_example/lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /examples/pio_example/include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /src/extmod/esp_eth_spi_enc28j60.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sdkconfig.h" 4 | #include "freertos/FreeRTOS.h" 5 | #include "freertos/task.h" 6 | #include "esp_netif.h" 7 | #include "esp_eth.h" 8 | #include "esp_event.h" 9 | #include "driver/gpio.h" 10 | #include "esp_eth_enc28j60.h" 11 | #include "driver/spi_master.h" 12 | 13 | 14 | 15 | esp_eth_mac_t* enc28j60_new_mac(int SPI_HOST, spi_device_interface_config_t* spi_devcfg, int INT_GPIO ) 16 | { 17 | eth_enc28j60_config_t enc28j60_config = ETH_ENC28J60_DEFAULT_CONFIG(SPI_HOST, spi_devcfg); 18 | enc28j60_config.int_gpio_num = INT_GPIO; 19 | 20 | eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); 21 | //mac_config.smi_mdc_gpio_num = -1; // ENC28J60 doesn't have SMI interface 22 | //mac_config.smi_mdio_gpio_num = -1; 23 | // mac_config.rx_task_prio = 1; 24 | return esp_eth_mac_new_enc28j60( &enc28j60_config, &mac_config ); 25 | } 26 | 27 | 28 | esp_eth_mac_t* enc28j60_begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, int SPI_HOST) 29 | { 30 | if(ESP_OK !=gpio_install_isr_service(0)) return NULL; 31 | /* ENC28J60 ethernet driver is based on spi driver */ 32 | spi_bus_config_t buscfg = 33 | { 34 | .miso_io_num = MISO_GPIO, 35 | .mosi_io_num = MOSI_GPIO, 36 | .sclk_io_num = SCLK_GPIO, 37 | .quadwp_io_num = -1, 38 | .quadhd_io_num = -1, 39 | }; 40 | if( ESP_OK != spi_bus_initialize( SPI_HOST, &buscfg, SPI_DMA_CH_AUTO )) return NULL; 41 | 42 | spi_device_interface_config_t devcfg = 43 | { 44 | .mode = 0, 45 | .clock_speed_hz = SPI_CLOCK_MHZ * 1000 * 1000, 46 | .spics_io_num = CS_GPIO, 47 | .queue_size = 1, 48 | .cs_ena_posttrans = enc28j60_cal_spi_cs_hold_time(SPI_CLOCK_MHZ), 49 | }; 50 | 51 | return enc28j60_new_mac( SPI_HOST, &devcfg, INT_GPIO ); 52 | } 53 | -------------------------------------------------------------------------------- /src/ESP32-ENC28J60.h: -------------------------------------------------------------------------------- 1 | /* 2 | ENC28J60-ETH.h - ETH PHY support for ENC28J60 3 | Based on ETH.h from Arduino-esp32 and ENC28J60 component from esp-idf. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #ifndef _ENC28J60_ESP32_ETH_H_ 21 | #define _ENC28J60_ESP32_ETH_H_ 22 | 23 | #include "Network.h" 24 | #include "WiFi.h" 25 | #include "esp_system.h" 26 | #include "esp_eth.h" 27 | //******************** 28 | //#include "esp_netif.h"//esp_eth_driver.h"//*********** 29 | static uint8_t ENC28J60_Default_Mac[] = { 0xFE, 0xED, 0xDE, 0xAD, 0xBE, 0xEF }; 30 | #if ESP_IDF_VERSION_MAJOR < 4 || ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,4,0) 31 | #error "This version of arduino-esp32 core is too old" 32 | #endif 33 | 34 | 35 | class ENC28J60Class { 36 | private: 37 | bool initialized; 38 | bool staticIP; 39 | uint8_t mac_eth[6] = { 0xFE, 0xED, 0xDE, 0xAD, 0xBE, 0xEF }; 40 | esp_eth_handle_t eth_handle; 41 | 42 | protected: 43 | bool started; 44 | eth_link_t eth_link; 45 | static void eth_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); 46 | 47 | public: 48 | ENC28J60Class(); 49 | ~ENC28J60Class(); 50 | 51 | bool begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, int SPI_HOST, uint8_t* ENC28J60_Mac = ENC28J60_Default_Mac); 52 | 53 | bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); 54 | 55 | const char* getHostname(); 56 | bool setHostname(const char* hostname); 57 | 58 | bool fullDuplex(); 59 | bool linkUp(); 60 | uint8_t linkSpeed(); 61 | 62 | bool enableIpV6(); 63 | IPAddress localIPv6(); 64 | 65 | IPAddress localIP(); 66 | IPAddress subnetMask(); 67 | IPAddress gatewayIP(); 68 | IPAddress dnsIP(uint8_t dns_no = 0); 69 | 70 | IPAddress broadcastIP(); 71 | IPAddress networkID(); 72 | uint8_t subnetCIDR(); 73 | 74 | uint8_t* macAddress(uint8_t* mac); 75 | String macAddress(); 76 | 77 | friend class NetworkClient; 78 | friend class NetworkServer; 79 | }; 80 | 81 | extern ENC28J60Class ETH; 82 | 83 | #endif /* _ETH_H_ */ 84 | -------------------------------------------------------------------------------- /examples/pio_example/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch shows the Ethernet event usage for ENC28J60 3 | 4 | */ 5 | 6 | #include 7 | 8 | #define SPI_HOST 1 9 | #define SPI_CLOCK_MHZ 8 10 | #define INT_GPIO 4 11 | // 12 | #if CONFIG_IDF_TARGET_ESP32 13 | #define MISO_GPIO 12 14 | #define MOSI_GPIO 13 15 | #define SCLK_GPIO 14 16 | #define CS_GPIO 15 17 | #elif CONFIG_IDF_TARGET_ESP32C3 18 | #define MISO_GPIO 2 19 | #define MOSI_GPIO 7 20 | #define SCLK_GPIO 6 21 | #define CS_GPIO 10 22 | #else 23 | #define MISO_GPIO 13 24 | #define MOSI_GPIO 11 25 | #define SCLK_GPIO 12 26 | #define CS_GPIO 10 27 | #endif 28 | 29 | 30 | static bool eth_connected = false; 31 | 32 | 33 | void WiFiEvent(WiFiEvent_t event) 34 | { 35 | switch (event) { 36 | case ARDUINO_EVENT_ETH_START: 37 | Serial.println("ETH Started"); 38 | //set eth hostname here 39 | ETH.setHostname("esp32-ethernet"); 40 | break; 41 | case ARDUINO_EVENT_ETH_CONNECTED: 42 | Serial.println("ETH Connected"); 43 | break; 44 | case ARDUINO_EVENT_ETH_GOT_IP: 45 | Serial.print("ETH MAC: "); 46 | Serial.print(ETH.macAddress()); 47 | Serial.print(", IPv4: "); 48 | Serial.print(ETH.localIP()); 49 | if (ETH.fullDuplex()) { 50 | Serial.print(", FULL_DUPLEX"); 51 | } 52 | Serial.print(", "); 53 | Serial.print(ETH.linkSpeed()); 54 | Serial.println("Mbps"); 55 | eth_connected = true; 56 | break; 57 | case ARDUINO_EVENT_ETH_DISCONNECTED: 58 | Serial.println("ETH Disconnected"); 59 | eth_connected = false; 60 | break; 61 | case ARDUINO_EVENT_ETH_STOP: 62 | Serial.println("ETH Stopped"); 63 | eth_connected = false; 64 | break; 65 | default: 66 | break; 67 | } 68 | } 69 | 70 | #include 71 | #include 72 | 73 | HTTPClient http; 74 | WiFiClientSecure client; 75 | 76 | 77 | void testClient(const char * url) 78 | { 79 | Serial.printf("\nConnecting to %s\n", url); 80 | 81 | http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); 82 | 83 | if( String(url).startsWith("https") ) { 84 | client.setInsecure(); 85 | http.begin( client, url ); 86 | } else { 87 | http.begin( url ); 88 | } 89 | 90 | int httpCode = http.GET(); 91 | 92 | if( httpCode != HTTP_CODE_OK && httpCode != HTTP_CODE_MOVED_PERMANENTLY ) { 93 | Serial.printf("Error: Status %i\n", httpCode ); 94 | } 95 | 96 | Stream* stream = http.getStreamPtr(); 97 | 98 | while (stream->available()) { 99 | Serial.write(stream->read()); 100 | } 101 | 102 | Serial.println("closing connection\n"); 103 | http.end(); 104 | } 105 | 106 | 107 | void setup() 108 | { 109 | Serial.begin( 115200 ); 110 | WiFi.onEvent( WiFiEvent ); 111 | ETH.begin( MISO_GPIO, MOSI_GPIO, SCLK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); 112 | 113 | while( !eth_connected) { 114 | Serial.println("Connecting..."); 115 | delay( 1000 ); 116 | } 117 | 118 | testClient("https://google.com"); 119 | 120 | } 121 | 122 | 123 | void loop() 124 | { 125 | } 126 | -------------------------------------------------------------------------------- /examples/ETH_ENC28J60/ETH_ENC28J60.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch shows the Ethernet event usage for ENC28J60 3 | 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define ENC_SPI_HOST 1 12 | #define SPI_CLOCK_MHZ 8 13 | #define INT_GPIO 4 14 | // 15 | #if CONFIG_IDF_TARGET_ESP32 16 | #define MISO_GPIO 12 17 | #define MOSI_GPIO 13 18 | #define SCLK_GPIO 14 19 | #define CS_GPIO 15 20 | #elif CONFIG_IDF_TARGET_ESP32C3 21 | #define MISO_GPIO 2 22 | #define MOSI_GPIO 7 23 | #define SCLK_GPIO 6 24 | #define CS_GPIO 10 25 | #else 26 | #define MISO_GPIO 13 27 | #define MOSI_GPIO 11 28 | #define SCLK_GPIO 12 29 | #define CS_GPIO 10 30 | #endif 31 | 32 | 33 | static bool eth_connected = false; 34 | 35 | 36 | void WiFiEvent(WiFiEvent_t event) 37 | { 38 | switch (event) { 39 | case ARDUINO_EVENT_ETH_START: 40 | Serial.println("ETH Started"); 41 | //set eth hostname here 42 | ETH.setHostname("esp32-ethernet"); 43 | break; 44 | case ARDUINO_EVENT_ETH_CONNECTED: 45 | Serial.println("ETH Connected"); 46 | break; 47 | case ARDUINO_EVENT_ETH_GOT_IP: 48 | Serial.print("ETH MAC: "); 49 | Serial.print(ETH.macAddress()); 50 | Serial.print(", IPv4: "); 51 | Serial.print(ETH.localIP()); 52 | if (ETH.fullDuplex()) { 53 | Serial.print(", FULL_DUPLEX"); 54 | } 55 | Serial.print(", "); 56 | Serial.print(ETH.linkSpeed()); 57 | Serial.println("Mbps"); 58 | eth_connected = true; 59 | break; 60 | case ARDUINO_EVENT_ETH_DISCONNECTED: 61 | Serial.println("ETH Disconnected"); 62 | eth_connected = false; 63 | break; 64 | case ARDUINO_EVENT_ETH_STOP: 65 | Serial.println("ETH Stopped"); 66 | eth_connected = false; 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | 74 | 75 | HTTPClient http; 76 | NetworkClientSecure client; 77 | 78 | 79 | void testClient(const char * url) 80 | { 81 | Serial.printf("\nConnecting to %s\n", url); 82 | 83 | http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); 84 | 85 | if( String(url).startsWith("https") ) { 86 | client.setInsecure(); 87 | http.begin( client, url ); 88 | } else { 89 | http.begin( url ); 90 | } 91 | 92 | int httpCode = http.GET(); 93 | 94 | if( httpCode != HTTP_CODE_OK && httpCode != HTTP_CODE_MOVED_PERMANENTLY ) { 95 | Serial.printf("Error: Status %i\n", httpCode ); 96 | } 97 | 98 | Stream* stream = http.getStreamPtr(); 99 | 100 | while (stream->available()) { 101 | Serial.write(stream->read()); 102 | } 103 | 104 | Serial.println("closing connection\n"); 105 | http.end(); 106 | } 107 | 108 | 109 | void setup() 110 | { 111 | Serial.begin( 115200 ); 112 | WiFi.onEvent( WiFiEvent ); 113 | ETH.begin( MISO_GPIO, MOSI_GPIO, SCLK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, ENC_SPI_HOST ); 114 | 115 | while( !eth_connected) { 116 | Serial.println("Connecting..."); 117 | delay( 1000 ); 118 | } 119 | 120 | testClient("https://google.com"); 121 | 122 | } 123 | 124 | 125 | void loop() 126 | { 127 | } 128 | -------------------------------------------------------------------------------- /src/extmod/esp_eth_enc28j60.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include "esp_eth_phy.h" 14 | #include "esp_eth_mac.h" 15 | #include "driver/spi_master.h" 16 | 17 | #define CS_HOLD_TIME_MIN_NS 210 18 | 19 | /** 20 | * @brief ENC28J60 specific configuration 21 | * 22 | */ 23 | typedef struct { 24 | spi_host_device_t spi_host_id; /*!< SPI peripheral */ 25 | spi_device_interface_config_t* spi_devcfg; /*!< SPI device configuration */ 26 | int int_gpio_num; /*!< Interrupt GPIO number */ 27 | } eth_enc28j60_config_t; 28 | 29 | /** 30 | * @brief ENC28J60 Supported Revisions 31 | * 32 | */ 33 | typedef enum { 34 | ENC28J60_REV_B1 = 0b00000010, 35 | ENC28J60_REV_B4 = 0b00000100, 36 | ENC28J60_REV_B5 = 0b00000101, 37 | ENC28J60_REV_B7 = 0b00000110 38 | } eth_enc28j60_rev_t; 39 | 40 | /** 41 | * @brief Default ENC28J60 specific configuration 42 | * 43 | */ 44 | #define ETH_ENC28J60_DEFAULT_CONFIG(spi_host, spi_devcfg_p) \ 45 | { \ 46 | .spi_host_id = spi_host, \ 47 | .spi_devcfg = spi_devcfg_p, \ 48 | .int_gpio_num = 4, \ 49 | } 50 | 51 | /** 52 | * @brief Compute amount of SPI bit-cycles the CS should stay active after the transmission 53 | * to meet ENC28J60 CS Hold Time specification. 54 | * 55 | * @param clock_speed_mhz SPI Clock frequency in MHz (valid range is <1, 20>) 56 | * @return uint8_t 57 | */ 58 | static inline uint8_t enc28j60_cal_spi_cs_hold_time(int clock_speed_mhz) 59 | { 60 | if (clock_speed_mhz <= 0 || clock_speed_mhz > 20) { 61 | return 0; 62 | } 63 | int temp = clock_speed_mhz * CS_HOLD_TIME_MIN_NS; 64 | uint8_t cs_posttrans = temp / 1000; 65 | if (temp % 1000) { 66 | cs_posttrans += 1; 67 | } 68 | 69 | return cs_posttrans; 70 | } 71 | 72 | /** 73 | * @brief Create ENC28J60 Ethernet MAC instance 74 | * 75 | * @param[in] enc28j60_config: ENC28J60 specific configuration 76 | * @param[in] mac_config: Ethernet MAC configuration 77 | * 78 | * @return 79 | * - instance: create MAC instance successfully 80 | * - NULL: create MAC instance failed because some error occurred 81 | */ 82 | esp_eth_mac_t* esp_eth_mac_new_enc28j60(const eth_enc28j60_config_t* enc28j60_config, const eth_mac_config_t* mac_config); 83 | 84 | /** 85 | * @brief Create a PHY instance of ENC28J60 86 | * 87 | * @param[in] config: configuration of PHY 88 | * 89 | * @return 90 | * - instance: create PHY instance successfully 91 | * - NULL: create PHY instance failed because some error occurred 92 | */ 93 | esp_eth_phy_t* esp_eth_phy_new_enc28j60(const eth_phy_config_t* config); 94 | 95 | /** 96 | * @brief Get ENC28J60 silicon revision ID 97 | * 98 | * @param mac ENC28J60 MAC Handle 99 | * @return eth_enc28j60_rev_t 100 | * - returns silicon revision ID read during initialization 101 | */ 102 | eth_enc28j60_rev_t emac_enc28j60_get_chip_info(esp_eth_mac_t* mac); 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif -------------------------------------------------------------------------------- /examples/FOTA/FOTA.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch shows the Ethernet event usage for ENC28J60 3 | 4 | */ 5 | 6 | #include 7 | 8 | #define SPI_HOST 1 9 | #define SPI_CLOCK_MHZ 20 10 | #define INT_GPIO 4 11 | // 12 | #if CONFIG_IDF_TARGET_ESP32 13 | #define MISO_GPIO 12 14 | #define MOSI_GPIO 13 15 | #define SCLK_GPIO 14 16 | #define CS_GPIO 15 17 | #elif CONFIG_IDF_TARGET_ESP32C3 18 | #define MISO_GPIO 2 19 | #define MOSI_GPIO 7 20 | #define SCLK_GPIO 6 21 | #define CS_GPIO 10 22 | #else 23 | #define MISO_GPIO 13 24 | #define MOSI_GPIO 11 25 | #define SCLK_GPIO 12 26 | #define CS_GPIO 10 27 | #endif 28 | 29 | 30 | // test regular firmware (no compression) 31 | // #define FOTA_URL "https://github.com/tobozo/ESP32-ENC28J60/raw/main/examples/FOTA/bin/firmware.json" 32 | 33 | // test with gzip compression 34 | #include // https://github.com/tobozo/ESP32-targz.h 35 | #define FOTA_URL "https://github.com/tobozo/ESP32-ENC28J60/raw/main/examples/FOTA/bin/firmware.gz.json" 36 | 37 | 38 | // test with zlib compression 39 | //#include // https://github.com/vortigont/esp32-flashz 40 | //#define FOTA_URL "https://github.com/tobozo/ESP32-ENC28J60/raw/main/examples/FOTA/bin/firmware.zz.json" 41 | 42 | 43 | #include // https://github.com/chrisjoyce911/esp32FOTA 44 | #include 45 | 46 | // esp32fota settings 47 | int firmware_version_major = 0; 48 | int firmware_version_minor = 1; 49 | int firmware_version_patch = 0; 50 | const char* firmware_name = "esp32-fota-http"; 51 | const bool check_signature = false; 52 | const bool disable_security = true; 53 | // for debug only 54 | const char* title = "0.1"; 55 | const char* description = "Basic Ethernet example with no security and no filesystem"; 56 | 57 | 58 | esp32FOTA FOTA; 59 | 60 | 61 | static bool eth_connected = false; 62 | 63 | 64 | static bool EthernetConnected() 65 | { 66 | return eth_connected; 67 | } 68 | 69 | 70 | // using WiFiEvent to handle Ethernet events :-) 71 | void WiFiEvent(WiFiEvent_t event) 72 | { 73 | switch (event) { 74 | case ARDUINO_EVENT_ETH_START: 75 | Serial.println("ETH Started"); 76 | ETH.setHostname("esp32-ethernet"); 77 | break; 78 | case ARDUINO_EVENT_ETH_CONNECTED: 79 | Serial.println("ETH Connected"); 80 | break; 81 | case ARDUINO_EVENT_ETH_GOT_IP: 82 | Serial.printf("ETH MAC: %s, IPv4: %s%s, %dMbps\n", ETH.macAddress().c_str(), ETH.localIP().toString().c_str(), ETH.fullDuplex()?", FULL_DUPLEX":"", ETH.linkSpeed() ); 83 | eth_connected = true; 84 | break; 85 | case ARDUINO_EVENT_ETH_DISCONNECTED: 86 | Serial.println("ETH Disconnected"); 87 | eth_connected = false; 88 | break; 89 | case ARDUINO_EVENT_ETH_STOP: 90 | Serial.println("ETH Stopped"); 91 | eth_connected = false; 92 | break; 93 | default: 94 | break; 95 | } 96 | } 97 | 98 | 99 | void setup() 100 | { 101 | Serial.begin(115200); 102 | 103 | PrintFOTAInfo(); 104 | 105 | { 106 | auto cfg = FOTA.getConfig(); 107 | cfg.name = firmware_name; 108 | cfg.manifest_url = FOTA_URL; 109 | cfg.sem = SemverClass( firmware_version_major, firmware_version_minor, firmware_version_patch ); 110 | cfg.check_sig = check_signature; 111 | cfg.unsafe = disable_security; 112 | //cfg.root_ca = MyRootCA; 113 | //cfg.pub_key = MyRSAKey; 114 | FOTA.setConfig( cfg ); 115 | FOTA.setStatusChecker( EthernetConnected ); 116 | //GzUpdateClass::getInstance().useDict( true ); 117 | } 118 | 119 | WiFi.onEvent( WiFiEvent ); 120 | ETH.begin( MISO_GPIO, MOSI_GPIO, SCLK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST ); 121 | 122 | } 123 | 124 | 125 | void loop() 126 | { 127 | if( !eth_connected) { 128 | Serial.println("Connecting..."); 129 | delay(1000); 130 | return; 131 | } 132 | FOTA.handle(); 133 | delay(20000); 134 | } 135 | 136 | -------------------------------------------------------------------------------- /src/extmod/emac_ext_struct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined SOC_HAS_NO_EMAC 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | #include 10 | 11 | typedef struct emac_ext_dev_s { 12 | volatile union { 13 | struct { 14 | uint32_t div_num : 4; 15 | uint32_t h_div_num : 4; 16 | uint32_t dly_num : 2; 17 | uint32_t reserved10 : 22; 18 | }; 19 | uint32_t val; 20 | } ex_clkout_conf; 21 | volatile union { 22 | struct { 23 | uint32_t div_num_10m : 6; 24 | uint32_t h_div_num_10m : 6; 25 | uint32_t div_num_100m : 6; 26 | uint32_t h_div_num_100m : 6; 27 | uint32_t clk_sel : 1; 28 | uint32_t reserved25 : 7; 29 | }; 30 | uint32_t val; 31 | } ex_oscclk_conf; 32 | volatile union { 33 | struct { 34 | uint32_t ext_en : 1; 35 | uint32_t int_en : 1; 36 | uint32_t rx_125_clk_en : 1; 37 | uint32_t mii_clk_tx_en : 1; 38 | uint32_t mii_clk_rx_en : 1; 39 | uint32_t clk_en : 1; 40 | uint32_t reserved6 : 26; 41 | }; 42 | uint32_t val; 43 | } ex_clk_ctrl; 44 | volatile union { 45 | struct { 46 | uint32_t int_revmii_rx_clk_sel : 1; 47 | uint32_t ext_revmii_rx_clk_sel : 1; 48 | uint32_t sbd_flowctrl : 1; 49 | uint32_t core_phy_addr : 5; 50 | uint32_t revmii_phy_addr : 5; 51 | uint32_t phy_intf_sel : 3; 52 | uint32_t ss_mode : 1; 53 | uint32_t sbd_clk_gating_en : 1; 54 | uint32_t pmt_ctrl_en : 1; 55 | uint32_t scr_smi_dly_rx_sync : 1; 56 | uint32_t tx_err_out_en : 1; 57 | uint32_t reserved21 : 11; 58 | }; 59 | uint32_t val; 60 | } ex_phyinf_conf; 61 | volatile union { 62 | struct { 63 | uint32_t ram_pd_en : 2; 64 | uint32_t reserved2 : 30; 65 | }; 66 | uint32_t val; 67 | } pd_sel; 68 | uint32_t reserved_14; 69 | uint32_t reserved_18; 70 | uint32_t reserved_1c; 71 | uint32_t reserved_20; 72 | uint32_t reserved_24; 73 | uint32_t reserved_28; 74 | uint32_t reserved_2c; 75 | uint32_t reserved_30; 76 | uint32_t reserved_34; 77 | uint32_t reserved_38; 78 | uint32_t reserved_3c; 79 | uint32_t reserved_40; 80 | uint32_t reserved_44; 81 | uint32_t reserved_48; 82 | uint32_t reserved_4c; 83 | uint32_t reserved_50; 84 | uint32_t reserved_54; 85 | uint32_t reserved_58; 86 | uint32_t reserved_5c; 87 | uint32_t reserved_60; 88 | uint32_t reserved_64; 89 | uint32_t reserved_68; 90 | uint32_t reserved_6c; 91 | uint32_t reserved_70; 92 | uint32_t reserved_74; 93 | uint32_t reserved_78; 94 | uint32_t reserved_7c; 95 | uint32_t reserved_80; 96 | uint32_t reserved_84; 97 | uint32_t reserved_88; 98 | uint32_t reserved_8c; 99 | uint32_t reserved_90; 100 | uint32_t reserved_94; 101 | uint32_t reserved_98; 102 | uint32_t reserved_9c; 103 | uint32_t reserved_a0; 104 | uint32_t reserved_a4; 105 | uint32_t reserved_a8; 106 | uint32_t reserved_ac; 107 | uint32_t reserved_b0; 108 | uint32_t reserved_b4; 109 | uint32_t reserved_b8; 110 | uint32_t reserved_bc; 111 | uint32_t reserved_c0; 112 | uint32_t reserved_c4; 113 | uint32_t reserved_c8; 114 | uint32_t reserved_cc; 115 | uint32_t reserved_d0; 116 | uint32_t reserved_d4; 117 | uint32_t reserved_d8; 118 | uint32_t reserved_dc; 119 | uint32_t reserved_e0; 120 | uint32_t reserved_e4; 121 | uint32_t reserved_e8; 122 | uint32_t reserved_ec; 123 | uint32_t reserved_f0; 124 | uint32_t reserved_f4; 125 | uint32_t reserved_f8; 126 | uint32_t ex_date; 127 | } emac_ext_dev_t; 128 | 129 | extern emac_ext_dev_t EMAC_EXT; 130 | 131 | #ifdef __cplusplus 132 | } 133 | #endif 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/ESP32-ENC28J60.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESP32-ENC28J60.h - ETH PHY support for ENC28J60 3 | Based on ETH.h from Arduino-esp32 and ENC28J60 component from esp-idf. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include "ESP32-ENC28J60.h" 21 | 22 | #include "esp_netif.h" 23 | #include "esp_eth.h" 24 | #include "esp_event.h" 25 | #include "esp_mac.h" 26 | 27 | #if __has_include("soc/emac_ext_struct.h") 28 | #include "soc/emac_ext_struct.h" 29 | #else 30 | #define SOC_HAS_NO_EMAC 31 | #include "extmod/emac_ext_struct.h" 32 | #endif 33 | 34 | #include "soc/rtc.h" 35 | extern "C" 36 | { 37 | esp_eth_mac_t* enc28j60_begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, int SPI_HOST); 38 | #include "extmod/esp_eth_enc28j60.h" 39 | } 40 | 41 | 42 | #include "lwip/err.h" 43 | #include "lwip/dns.h" 44 | 45 | extern void tcpipInit(); 46 | 47 | /** 48 | * @brief Callback function invoked when lowlevel initialization is finished 49 | * 50 | * @param[in] eth_handle: handle of Ethernet driver 51 | * 52 | * @return 53 | * - ESP_OK: process extra lowlevel initialization successfully 54 | * - ESP_FAIL: error occurred when processing extra lowlevel initialization 55 | */ 56 | 57 | //static eth_clock_mode_t eth_clock_mode = ETH_CLK_MODE; 58 | 59 | /** 60 | * @brief Callback function invoked when lowlevel deinitialization is finished 61 | * 62 | * @param[in] eth_handle: handle of Ethernet driver 63 | * 64 | * @return 65 | * - ESP_OK: process extra lowlevel deinitialization successfully 66 | * - ESP_FAIL: error occurred when processing extra lowlevel deinitialization 67 | */ 68 | //static esp_err_t on_lowlevel_deinit_done(esp_eth_handle_t eth_handle){ 69 | // return ESP_OK; 70 | //} 71 | 72 | 73 | 74 | 75 | 76 | ENC28J60Class::ENC28J60Class() 77 | : initialized(false), staticIP(false), eth_handle(NULL), started(false), eth_link(ETH_LINK_DOWN) { 78 | } 79 | 80 | ENC28J60Class::~ENC28J60Class() {} 81 | esp_netif_t *eth_netif{nullptr}; 82 | //bool ENC28J60Class::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_type_t type, eth_clock_mode_t clock_mode, bool use_mac_from_efuse) 83 | bool ENC28J60Class::begin(int MISO_GPIO, int MOSI_GPIO, int SCLK_GPIO, int CS_GPIO, int INT_GPIO, int SPI_CLOCK_MHZ, int SPI_HOST, uint8_t* ENC28J60_Mac) { 84 | Network.begin(); 85 | 86 | //uint8_t ENC28J60_Default_Mac[6] = { 0x02, 0x00, 0x00, 0x12, 0x34, 0x56 }; 87 | 88 | if (esp_read_mac(mac_eth, ESP_MAC_ETH) == ESP_OK) { 89 | char macStr[18] = { 0 }; 90 | 91 | sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac_eth[0], mac_eth[1], mac_eth[2], 92 | mac_eth[3], mac_eth[4], mac_eth[5]); 93 | 94 | log_i("Using built-in mac_eth = %s", macStr); 95 | 96 | esp_base_mac_addr_set(mac_eth); 97 | } else { 98 | log_i("Using user mac_eth"); 99 | memcpy(mac_eth, ENC28J60_Mac, sizeof(mac_eth)); 100 | 101 | esp_base_mac_addr_set(ENC28J60_Mac); 102 | } 103 | 104 | 105 | esp_netif_init(); // Initialize TCP/IP network interface (should be called only once in application) 106 | esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); // apply default network interface configuration for Ethernet 107 | eth_netif = esp_netif_new(&cfg); // create network interface for Ethernet driver 108 | 109 | esp_eth_mac_t* eth_mac = enc28j60_begin(MISO_GPIO, MOSI_GPIO, SCLK_GPIO, CS_GPIO, INT_GPIO, SPI_CLOCK_MHZ, SPI_HOST); 110 | 111 | if (eth_mac == NULL) { 112 | log_e("esp_eth_mac_new_esp32 failed"); 113 | return false; 114 | } 115 | 116 | eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); 117 | phy_config.autonego_timeout_ms = 0; // ENC28J60 doesn't support auto-negotiation 118 | phy_config.reset_gpio_num = -1; // ENC28J60 doesn't have a pin to reset internal PHY 119 | esp_eth_phy_t* eth_phy = esp_eth_phy_new_enc28j60(&phy_config); 120 | 121 | if (eth_phy == NULL) { 122 | log_e("esp_eth_phy_new failed"); 123 | return false; 124 | } 125 | 126 | eth_handle = NULL; 127 | esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy); 128 | //eth_config.on_lowlevel_init_done = on_lowlevel_init_done; 129 | //eth_config.on_lowlevel_deinit_done = on_lowlevel_deinit_done; 130 | if (esp_eth_driver_install(ð_config, ð_handle) != ESP_OK || eth_handle == NULL) { 131 | log_e("esp_eth_driver_install failed"); 132 | return false; 133 | } 134 | 135 | /* ENC28J60 doesn't burn any factory MAC address, we need to set it manually. 136 | 02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control. 137 | */ 138 | //eth_mac->set_addr(eth_mac, ENC28J60_Default_Mac); 139 | eth_mac->set_addr(eth_mac, mac_eth); 140 | // ENC28J60 Errata #1 check 141 | if (emac_enc28j60_get_chip_info(eth_mac) < ENC28J60_REV_B5 && SPI_CLOCK_MHZ < 8) { 142 | log_e("SPI frequency must be at least 8 MHz for chip revision less than 5"); 143 | ESP_ERROR_CHECK(ESP_FAIL); 144 | } 145 | 146 | /* attach Ethernet driver to TCP/IP stack */ 147 | if (esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)) != ESP_OK) { 148 | log_e("esp_netif_attach failed"); 149 | return false; 150 | } 151 | 152 | if (esp_eth_start(eth_handle) != ESP_OK) { 153 | log_e("esp_eth_start failed"); 154 | return false; 155 | } 156 | 157 | // holds a few microseconds to let DHCP start and enter into a good state 158 | // FIX ME -- adresses issue https://github.com/espressif/arduino-esp32/issues/5733 159 | delay(50); 160 | 161 | return true; 162 | } 163 | 164 | bool ENC28J60Class::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) { 165 | esp_err_t err = ESP_OK; 166 | esp_netif_ip_info_t info; 167 | 168 | if (static_cast(local_ip) != 0) { 169 | info.ip.addr = static_cast(local_ip); 170 | info.gw.addr = static_cast(gateway); 171 | info.netmask.addr = static_cast(subnet); 172 | } else { 173 | info.ip.addr = 0; 174 | info.gw.addr = 0; 175 | info.netmask.addr = 0; 176 | } 177 | 178 | err = esp_netif_dhcpc_stop(eth_netif); 179 | if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) { 180 | log_e("DHCP could not be stopped! Error: %d", err); 181 | return false; 182 | } 183 | 184 | err = esp_netif_set_ip_info(eth_netif, &info); 185 | if (err != ERR_OK) { 186 | log_e("STA IP could not be configured! Error: %d", err); 187 | return false; 188 | } 189 | 190 | if (info.ip.addr) { 191 | staticIP = true; 192 | } else { 193 | err = esp_netif_dhcpc_start(eth_netif); 194 | if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { 195 | log_w("DHCP could not be started! Error: %d", err); 196 | return false; 197 | } 198 | staticIP = false; 199 | } 200 | 201 | ip_addr_t d; 202 | d.type = IPADDR_TYPE_V4; 203 | 204 | if (static_cast(dns1) != 0) { 205 | // Set DNS1-Server 206 | d.u_addr.ip4.addr = static_cast(dns1); 207 | dns_setserver(0, &d); 208 | } 209 | 210 | if (static_cast(dns2) != 0) { 211 | // Set DNS2-Server 212 | d.u_addr.ip4.addr = static_cast(dns2); 213 | dns_setserver(1, &d); 214 | } 215 | 216 | return true; 217 | } 218 | 219 | IPAddress ENC28J60Class::localIP() { 220 | esp_netif_ip_info_t ip; 221 | if (esp_netif_get_ip_info(eth_netif, &ip)) { 222 | return IPAddress(); 223 | } 224 | return IPAddress(ip.ip.addr); 225 | } 226 | 227 | IPAddress ENC28J60Class::subnetMask() { 228 | esp_netif_ip_info_t ip; 229 | if (esp_netif_get_ip_info(eth_netif, &ip)) { 230 | return IPAddress(); 231 | } 232 | return IPAddress(ip.netmask.addr); 233 | } 234 | 235 | IPAddress ENC28J60Class::gatewayIP() { 236 | esp_netif_ip_info_t ip; 237 | if (esp_netif_get_ip_info(eth_netif, &ip)) { 238 | return IPAddress(); 239 | } 240 | return IPAddress(ip.gw.addr); 241 | } 242 | 243 | IPAddress ENC28J60Class::dnsIP(uint8_t dns_no) { 244 | const ip_addr_t* dns_ip = dns_getserver(dns_no); 245 | return IPAddress(dns_ip->u_addr.ip4.addr); 246 | } 247 | 248 | IPAddress ENC28J60Class::broadcastIP() { 249 | esp_netif_ip_info_t ip; 250 | if (esp_netif_get_ip_info(eth_netif, &ip)) { 251 | return IPAddress(); 252 | } 253 | return WiFiGenericClass::calculateBroadcast(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr)); 254 | } 255 | 256 | IPAddress ENC28J60Class::networkID() { 257 | esp_netif_ip_info_t ip; 258 | if (esp_netif_get_ip_info(eth_netif, &ip)) { 259 | return IPAddress(); 260 | } 261 | return WiFiGenericClass::calculateNetworkID(IPAddress(ip.gw.addr), IPAddress(ip.netmask.addr)); 262 | } 263 | 264 | uint8_t ENC28J60Class::subnetCIDR() { 265 | esp_netif_ip_info_t ip; 266 | if (esp_netif_get_ip_info(eth_netif, &ip)) { 267 | return (uint8_t)0; 268 | } 269 | return WiFiGenericClass::calculateSubnetCIDR(IPAddress(ip.netmask.addr)); 270 | } 271 | 272 | const char* ENC28J60Class::getHostname() { 273 | const char* hostname; 274 | if (esp_netif_get_hostname(eth_netif, &hostname)) { 275 | return NULL; 276 | } 277 | return hostname; 278 | } 279 | 280 | bool ENC28J60Class::setHostname(const char* hostname) { 281 | return esp_netif_set_hostname(eth_netif, hostname) == 0; 282 | } 283 | 284 | bool ENC28J60Class::fullDuplex() { 285 | return true; //todo: do not see an API for this 286 | } 287 | 288 | bool ENC28J60Class::linkUp() { 289 | return eth_link == ETH_LINK_UP; 290 | } 291 | 292 | uint8_t ENC28J60Class::linkSpeed() { 293 | eth_speed_t link_speed; 294 | esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &link_speed); 295 | return (link_speed == ETH_SPEED_10M) ? 10 : 100; 296 | } 297 | 298 | bool ENC28J60Class::enableIpV6() { 299 | return esp_netif_create_ip6_linklocal(eth_netif) == 0; 300 | } 301 | 302 | IPAddress ENC28J60Class::localIPv6() { 303 | static esp_ip6_addr_t addr; 304 | if (esp_netif_get_ip6_linklocal(eth_netif, &addr)) { 305 | return IPAddress(IPv6); 306 | } 307 | return IPAddress(IPv6, (const uint8_t*)addr.addr, addr.zone); 308 | } 309 | 310 | uint8_t* ENC28J60Class::macAddress(uint8_t* mac) { 311 | if (!mac) { 312 | return NULL; 313 | } 314 | esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac); 315 | return mac; 316 | } 317 | 318 | String ENC28J60Class::macAddress(void) { 319 | uint8_t mac[6] = { 0, 0, 0, 0, 0, 0 }; 320 | char macStr[18] = { 0 }; 321 | macAddress(mac); 322 | sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 323 | return String(macStr); 324 | } 325 | 326 | ENC28J60Class ETH; 327 | -------------------------------------------------------------------------------- /src/extmod/enc28j60.h: -------------------------------------------------------------------------------- 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 | 15 | #pragma once 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | * @brief SPI Instruction Set 23 | * 24 | */ 25 | #define ENC28J60_SPI_CMD_RCR (0x00) // Read Control Register 26 | #define ENC28J60_SPI_CMD_RBM (0x01) // Read Buffer Memory 27 | #define ENC28J60_SPI_CMD_WCR (0x02) // Write Control Register 28 | #define ENC28J60_SPI_CMD_WBM (0x03) // Write Buffer Memory 29 | #define ENC28J60_SPI_CMD_BFS (0x04) // Bit Field Set 30 | #define ENC28J60_SPI_CMD_BFC (0x05) // Bit Field Clear 31 | #define ENC28J60_SPI_CMD_SRC (0x07) // Soft Reset 32 | 33 | /** 34 | * @brief Shared Registers in ENC28J60 (accessible on each bank) 35 | * 36 | */ 37 | #define ENC28J60_EIE (0x1B) // Ethernet Interrupt Enable 38 | #define ENC28J60_EIR (0x1C) // Ethernet Interrupt flags 39 | #define ENC28J60_ESTAT (0x1D) // Ethernet Status 40 | #define ENC28J60_ECON2 (0x1E) // Ethernet Control Register2 41 | #define ENC28J60_ECON1 (0x1F) // Ethernet Control Register1 42 | 43 | /** 44 | * @brief Per-bank Registers in ENC28J60 45 | * @note Address[15:12]: Register Type, 0 -> ETH, 1 -> MII/MAC 46 | * Address[11:8] : Bank address 47 | * Address[7:0] : Register Index 48 | */ 49 | // Bank 0 Registers 50 | #define ENC28J60_ERDPTL (0x0000) // Read Pointer Low Byte ERDPT<7:0>) 51 | #define ENC28J60_ERDPTH (0x0001) // Read Pointer High Byte (ERDPT<12:8>) 52 | #define ENC28J60_EWRPTL (0x0002) // Write Pointer Low Byte (EWRPT<7:0>) 53 | #define ENC28J60_EWRPTH (0x0003) // Write Pointer High Byte (EWRPT<12:8>) 54 | #define ENC28J60_ETXSTL (0x0004) // TX Start Low Byte (ETXST<7:0>) 55 | #define ENC28J60_ETXSTH (0x0005) // TX Start High Byte (ETXST<12:8>) 56 | #define ENC28J60_ETXNDL (0x0006) // TX End Low Byte (ETXND<7:0>) 57 | #define ENC28J60_ETXNDH (0x0007) // TX End High Byte (ETXND<12:8>) 58 | #define ENC28J60_ERXSTL (0x0008) // RX Start Low Byte (ERXST<7:0>) 59 | #define ENC28J60_ERXSTH (0x0009) // RX Start High Byte (ERXST<12:8>) 60 | #define ENC28J60_ERXNDL (0x000A) // RX End Low Byte (ERXND<7:0>) 61 | #define ENC28J60_ERXNDH (0x000B) // RX End High Byte (ERXND<12:8>) 62 | #define ENC28J60_ERXRDPTL (0x000C) // RX RD Pointer Low Byte (ERXRDPT<7:0>) 63 | #define ENC28J60_ERXRDPTH (0x000D) // RX RD Pointer High Byte (ERXRDPT<12:8>) 64 | #define ENC28J60_ERXWRPTL (0x000E) // RX WR Pointer Low Byte (ERXWRPT<7:0>) 65 | #define ENC28J60_ERXWRPTH (0x000F) // RX WR Pointer High Byte (ERXWRPT<12:8>) 66 | #define ENC28J60_EDMASTL (0x0010) // DMA Start Low Byte (EDMAST<7:0>) 67 | #define ENC28J60_EDMASTH (0x0011) // DMA Start High Byte (EDMAST<12:8>) 68 | #define ENC28J60_EDMANDL (0x0012) // DMA End Low Byte (EDMAND<7:0>) 69 | #define ENC28J60_EDMANDH (0x0013) // DMA End High Byte (EDMAND<12:8>) 70 | #define ENC28J60_EDMADSTL (0x0014) // DMA Destination Low Byte (EDMADST<7:0>) 71 | #define ENC28J60_EDMADSTH (0x0015) // DMA Destination High Byte (EDMADST<12:8>) 72 | #define ENC28J60_EDMACSL (0x0016) // DMA Checksum Low Byte (EDMACS<7:0>) 73 | #define ENC28J60_EDMACSH (0x0017) // DMA Checksum High Byte (EDMACS<15:8>) 74 | 75 | // Bank 1 Registers 76 | #define ENC28J60_EHT0 (0x0100) // Hash Table Byte 0 (EHT<7:0>) 77 | #define ENC28J60_EHT1 (0x0101) // Hash Table Byte 1 (EHT<15:8>) 78 | #define ENC28J60_EHT2 (0x0102) // Hash Table Byte 2 (EHT<23:16>) 79 | #define ENC28J60_EHT3 (0x0103) // Hash Table Byte 3 (EHT<31:24>) 80 | #define ENC28J60_EHT4 (0x0104) // Hash Table Byte 4 (EHT<39:32>) 81 | #define ENC28J60_EHT5 (0x0105) // Hash Table Byte 5 (EHT<47:40>) 82 | #define ENC28J60_EHT6 (0x0106) // Hash Table Byte 6 (EHT<55:48>) 83 | #define ENC28J60_EHT7 (0x0107) // Hash Table Byte 7 (EHT<63:56>) 84 | #define ENC28J60_EPMM0 (0x0108) // Pattern Match Mask Byte 0 (EPMM<7:0>) 85 | #define ENC28J60_EPMM1 (0x0109) // Pattern Match Mask Byte 1 (EPMM<15:8>) 86 | #define ENC28J60_EPMM2 (0x010A) // Pattern Match Mask Byte 2 (EPMM<23:16>) 87 | #define ENC28J60_EPMM3 (0x010B) // Pattern Match Mask Byte 3 (EPMM<31:24>) 88 | #define ENC28J60_EPMM4 (0x010C) // Pattern Match Mask Byte 4 (EPMM<39:32>) 89 | #define ENC28J60_EPMM5 (0x010D) // Pattern Match Mask Byte 5 (EPMM<47:40>) 90 | #define ENC28J60_EPMM6 (0x010E) // Pattern Match Mask Byte 6 (EPMM<55:48>) 91 | #define ENC28J60_EPMM7 (0x010F) // Pattern Match Mask Byte 7 (EPMM<63:56>) 92 | #define ENC28J60_EPMCSL (0x0110) // Pattern Match Checksum Low Byte (EPMCS<7:0>) 93 | #define ENC28J60_EPMCSH (0x0111) // Pattern Match Checksum High Byte (EPMCS<15:0>) 94 | #define ENC28J60_EPMOL (0x0114) // Pattern Match Offset Low Byte (EPMO<7:0>) 95 | #define ENC28J60_EPMOH (0x0115) // Pattern Match Offset High Byte (EPMO<12:8>) 96 | #define ENC28J60_ERXFCON (0x0118) // Receive Fileter Control 97 | #define ENC28J60_EPKTCNT (0x0119) // Ethernet Packet Count 98 | 99 | // Bank 2 Register 100 | #define ENC28J60_MACON1 (0x1200) // MAC Control Register 1 101 | #define ENC28J60_MACON2 (0x1201) // MAC Control Register 2 102 | #define ENC28J60_MACON3 (0x1202) // MAC Control Register 3 103 | #define ENC28J60_MACON4 (0x1203) // MAC Control Register 4 104 | #define ENC28J60_MABBIPG (0x1204) // Back-to-Back Inter-Packet Gap (BBIPG<6:0>) 105 | #define ENC28J60_MAIPGL (0x1206) // Non-Back-to-Back Inter-Packet Gap Low Byte (MAIPGL<6:0>) 106 | #define ENC28J60_MAIPGH (0x1207) // Non-Back-to-Back Inter-Packet Gap High Byte (MAIPGH<6:0>) 107 | #define ENC28J60_MACLCON1 (0x1208) // Retransmission Maximum (RETMAX<3:0>) 108 | #define ENC28J60_MACLCON2 (0x1209) // Collision Window (COLWIN<5:0>) 109 | #define ENC28J60_MAMXFLL (0x120A) // Maximum Frame Length Low Byte (MAMXFL<7:0>) 110 | #define ENC28J60_MAMXFLH (0x120B) // Maximum Frame Length High Byte (MAMXFL<15:8>) 111 | #define ENC28J60_MICMD (0x1212) // MII Command Register 112 | #define ENC28J60_MIREGADR (0x1214) // MII Register Address (MIREGADR<4:0>) 113 | #define ENC28J60_MIWRL (0x1216) // MII Write Data Low Byte (MIWR<7:0>) 114 | #define ENC28J60_MIWRH (0x1217) // MII Write Data High Byte (MIWR<15:8>) 115 | #define ENC28J60_MIRDL (0x1218) // MII Read Data Low Byte (MIRD<7:0>) 116 | #define ENC28J60_MIRDH (0x1219) // MII Read Data High Byte(MIRD<15:8>) 117 | 118 | // Bank 3 Registers 119 | #define ENC28J60_MAADR5 (0x1300) // MAC Address Byte 5 (MAADR<15:8>) 120 | #define ENC28J60_MAADR6 (0x1301) // MAC Address Byte 6 (MAADR<7:0>) 121 | #define ENC28J60_MAADR3 (0x1302) // MAC Address Byte 3 (MAADR<31:24>), OUI Byte 3 122 | #define ENC28J60_MAADR4 (0x1303) // MAC Address Byte 4 (MAADR<23:16>) 123 | #define ENC28J60_MAADR1 (0x1304) // MAC Address Byte 1 (MAADR<47:40>), OUI Byte 1 124 | #define ENC28J60_MAADR2 (0x1305) // MAC Address Byte 2 (MAADR<39:32>), OUI Byte 2 125 | #define ENC28J60_EBSTSD (0x0306) // Built-in Self-Test Fill Seed (EBSTSD<7:0>) 126 | #define ENC28J60_EBSTCON (0x0307) // Built-in Self-Test Control 127 | #define ENC28J60_EBSTCSL (0x0308) // Built-in Self-Test Checksum Low Byte (EBSTCS<7:0>) 128 | #define ENC28J60_EBSTCSH (0x0309) // Built-in Self-Test Checksum High Byte (EBSTCS<15:8>) 129 | #define ENC28J60_MISTAT (0x130A) // MII Status Register 130 | #define ENC28J60_EREVID (0x0312) // Ethernet Revision ID (EREVID<4:0>) 131 | #define ENC28J60_ECOCON (0x0315) // Clock Output Control Register 132 | #define ENC28J60_EFLOCON (0x0317) // Ethernet Flow Control 133 | #define ENC28J60_EPAUSL (0x0318) // Pause Timer Value Low Byte (EPAUS<7:0>) 134 | #define ENC28J60_EPAUSH (0x0319) // Pause Timer Value High Byte (EPAUS<15:8>) 135 | 136 | /** 137 | * @brief status and flag of ENC28J60 specific registers 138 | * 139 | */ 140 | // EIE bit definitions 141 | #define EIE_INTIE (1<<7) // Global INT Interrupt Enable 142 | #define EIE_PKTIE (1<<6) // Receive Packet Pending Interrupt Enable 143 | #define EIE_DMAIE (1<<5) // DMA Interrupt Enable 144 | #define EIE_LINKIE (1<<4) // Link Status Change Interrupt Enable 145 | #define EIE_TXIE (1<<3) // Transmit Enable 146 | #define EIE_TXERIE (1<<1) // Transmit Error Interrupt Enable 147 | #define EIE_RXERIE (1<<0) // Receive Error Interrupt Enable 148 | 149 | // EIR bit definitions 150 | #define EIR_PKTIF (1<<6) // Receive Packet Pending Interrupt Flag 151 | #define EIR_DMAIF (1<<5) // DMA Interrupt Flag 152 | #define EIR_LINKIF (1<<4) // Link Change Interrupt Flag 153 | #define EIR_TXIF (1<<3) // Transmit Interrupt Flag 154 | #define EIR_TXERIF (1<<1) // Transmit Error Interrupt Flag 155 | #define EIR_RXERIF (1<<0) // Receive Error Interrupt Flag 156 | 157 | // ESTAT bit definitions 158 | #define ESTAT_INT (1<<7) // INT Interrupt Flag 159 | #define ESTAT_BUFER (1<<6) // Buffer Error Status 160 | #define ESTAT_LATECOL (1<<4) // Late Collision Error 161 | #define ESTAT_RXBUSY (1<<2) // Receive Busy 162 | #define ESTAT_TXABRT (1<<1) // Transmit Abort Error 163 | #define ESTAT_CLKRDY (1<<0) // Clock Ready 164 | 165 | // ECON2 bit definitions 166 | #define ECON2_AUTOINC (1<<7) // Automatic Buffer Pointer Increment Enable 167 | #define ECON2_PKTDEC (1<<6) // Packet Decrement 168 | #define ECON2_PWRSV (1<<5) // Power Save Enable 169 | #define ECON2_VRPS (1<<3) // Voltage Regulator Power Save Enable 170 | 171 | // ECON1 bit definitions 172 | #define ECON1_TXRST (1<<7) // Transmit Logic Reset 173 | #define ECON1_RXRST (1<<6) // Receive Logic Reset 174 | #define ECON1_DMAST (1<<5) // DMA Start and Busy Status 175 | #define ECON1_CSUMEN (1<<4) // DMA Checksum Enable 176 | #define ECON1_TXRTS (1<<3) // Transmit Request to Send 177 | #define ECON1_RXEN (1<<2) // Receive Enable 178 | #define ECON1_BSEL1 (1<<1) // Bank Select1 179 | #define ECON1_BSEL0 (1<<0) // Bank Select0 180 | 181 | // ERXFCON bit definitions 182 | #define ERXFCON_UCEN (1<<7) // Unicast Filter Enable 183 | #define ERXFCON_ANDOR (1<<6) // AND/OR Filter Select 184 | #define ERXFCON_CRCEN (1<<5) // Post-Filter CRC Check Enable 185 | #define ERXFCON_PMEN (1<<4) // Pattern Match Filter Enable 186 | #define ERXFCON_MPEN (1<<3) // Magic Packet Filter Enable 187 | #define ERXFCON_HTEN (1<<2) // Hash Table Filter Enable 188 | #define ERXFCON_MCEN (1<<1) // Multicast Filter Enable 189 | #define ERXFCON_BCEN (1<<0) // Broadcast Filter Enable 190 | 191 | // MACON1 bit definitions 192 | #define MACON1_TXPAUS (1<<3) // Pause Control Frame Transmission Enable 193 | #define MACON1_RXPAUS (1<<2) // Pause Control Frame Reception Enable 194 | #define MACON1_PASSALL (1<<1) // Pass All Received Frames Enable 195 | #define MACON1_MARXEN (1<<0) // MAC Receive Enable 196 | 197 | // MACON3 bit definitions 198 | #define MACON3_PADCFG2 (1<<7) // Automatic Pad and CRC Configuration bit 2 199 | #define MACON3_PADCFG1 (1<<6) // Automatic Pad and CRC Configuration bit 1 200 | #define MACON3_PADCFG0 (1<<5) // Automatic Pad and CRC Configuration bit 0 201 | #define MACON3_TXCRCEN (1<<4) // Transmit CRC Enable 202 | #define MACON3_PHDRLEN (1<<3) // Proprietary Header Enable 203 | #define MACON3_HFRMLEN (1<<2) // Huge Frame Enable 204 | #define MACON3_FRMLNEN (1<<1) // Frame Length Checking Enable 205 | #define MACON3_FULDPX (1<<0) // MAC Full-Duplex Enable 206 | 207 | // MACON4 bit definitions 208 | #define MACON4_DEFER (1<<6) // Defer Transmission Enable 209 | #define MACON4_BPEN (1<<5) // No Backoff During Backpressure Enable 210 | #define MACON4_NOBKFF (1<<4) // No Backoff Enable 211 | 212 | // MICMD bit definitions 213 | #define MICMD_MIISCAN (1<<1) // MII Scan Enable 214 | #define MICMD_MIIRD (1<<0) // MII Read Enable 215 | 216 | // EBSTCON bit definitions 217 | #define EBSTCON_PSV2 (1<<7) // Pattern Shift Value 2 218 | #define EBSTCON_PSV1 (1<<6) // Pattern Shift Value 1 219 | #define EBSTCON_PSV0 (1<<5) // Pattern Shift Value 0 220 | #define EBSTCON_PSEL (1<<4) // Port Select 221 | #define EBSTCON_TMSEL1 (1<<3) // Test Mode Select 1 222 | #define EBSTCON_TMSEL0 (1<<2) // Test Mode Select 0 223 | #define EBSTCON_TME (1<<1) // Test Mode Enable 224 | #define EBSTCON_BISTST (1<<0) // Built-in Self-Test Start/Busy 225 | 226 | // MISTAT bit definitions 227 | #define MISTAT_NVALID (1<<2) // MII Management Read Data Not Valid 228 | #define MISTAT_SCAN (1<<1) // MII Management Scan Operation in Progress 229 | #define MISTAT_BUSY (1<<0) // MII Management Busy 230 | 231 | // EFLOCON bit definitions 232 | #define EFLOCON_FULDPXS (1<<2) // Full-Duplex Shadown 233 | #define EFLOCON_FCEN1 (1<<1) // Flow Control Enable 1 234 | #define EFLOCON_FCEN0 (1<<0) // Flow Control Enable 0 235 | 236 | #ifdef __cplusplus 237 | } 238 | #endif -------------------------------------------------------------------------------- /src/extmod/esp_eth_phy_enc28j60.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include "esp_log.h" 10 | #include "esp_eth.h" 11 | #include "eth_phy_802_3_regs.h" 12 | #include "esp_eth_enc28j60.h" 13 | #include "freertos/FreeRTOS.h" 14 | #include "freertos/task.h" 15 | #include "driver/gpio.h" 16 | 17 | static const char* TAG = "enc28j60"; 18 | #define PHY_CHECK(a, str, goto_tag, ...) \ 19 | do \ 20 | { \ 21 | if (!(a)) \ 22 | { \ 23 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ 24 | goto goto_tag; \ 25 | } \ 26 | } while (0) 27 | 28 | /***************Vendor Specific Register***************/ 29 | 30 | /** 31 | * @brief PHCON2(PHY Control Register 2) 32 | * 33 | */ 34 | typedef union { 35 | struct { 36 | uint32_t reserved_7_0 : 8; // Reserved 37 | uint32_t pdpxmd : 1; // PHY Duplex Mode bit 38 | uint32_t reserved_10_9 : 2; // Reserved 39 | uint32_t ppwrsv : 1; // PHY Power-Down bit 40 | uint32_t reserved_13_12 : 2; // Reserved 41 | uint32_t ploopbk : 1; // PHY Loopback bit 42 | uint32_t prst : 1; // PHY Software Reset bit 43 | }; 44 | uint32_t val; 45 | } phcon1_reg_t; 46 | #define ETH_PHY_PHCON1_REG_ADDR (0x00) 47 | 48 | /** 49 | * @brief PHCON2(PHY Control Register 2) 50 | * 51 | */ 52 | typedef union { 53 | struct { 54 | uint32_t reserved_7_0 : 8; // Reserved 55 | uint32_t hdldis : 1; // Half-Duplex Loopback Disable 56 | uint32_t reserved_9 : 1; // Reserved 57 | uint32_t jabber : 1; // Disable Jabber Correction 58 | uint32_t reserved_12_11 : 2; // Reserved 59 | uint32_t txdis : 1; // Disable Twist-Pair Transmitter 60 | uint32_t frclnk : 1; // Force Linkup 61 | uint32_t reserved_15 : 1; //Reserved 62 | }; 63 | uint32_t val; 64 | } phcon2_reg_t; 65 | #define ETH_PHY_PHCON2_REG_ADDR (0x10) 66 | 67 | /** 68 | * @brief PHSTAT2(PHY Status Register 2) 69 | * 70 | */ 71 | typedef union { 72 | struct { 73 | uint32_t reserved_4_0 : 5; // Reserved 74 | uint32_t plrity : 1; // Polarity Status 75 | uint32_t reserved_8_6 : 3; // Reserved 76 | uint32_t dpxstat : 1; // PHY Duplex Status 77 | uint32_t lstat : 1; // PHY Link Status (non-latching) 78 | uint32_t colstat : 1; // PHY Collision Status 79 | uint32_t rxstat : 1; // PHY Receive Status 80 | uint32_t txstat : 1; // PHY Transmit Status 81 | uint32_t reserved_15_14 : 2; // Reserved 82 | }; 83 | uint32_t val; 84 | } phstat2_reg_t; 85 | #define ETH_PHY_PHSTAT2_REG_ADDR (0x11) 86 | 87 | typedef struct { 88 | esp_eth_phy_t parent; 89 | esp_eth_mediator_t* eth; 90 | uint32_t addr; 91 | uint32_t reset_timeout_ms; 92 | eth_link_t link_status; 93 | int reset_gpio_num; 94 | } phy_enc28j60_t; 95 | 96 | static esp_err_t enc28j60_update_link_duplex_speed(phy_enc28j60_t* enc28j60) 97 | { 98 | esp_eth_mediator_t* eth = enc28j60->eth; 99 | eth_speed_t speed = ETH_SPEED_10M; // enc28j60 speed is fixed to 10Mbps 100 | eth_duplex_t duplex = ETH_DUPLEX_HALF; 101 | phstat2_reg_t phstat; 102 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_PHSTAT2_REG_ADDR, &(phstat.val)) == ESP_OK, 103 | "read PHSTAT2 failed", err); 104 | eth_link_t link = phstat.lstat ? ETH_LINK_UP : ETH_LINK_DOWN; 105 | /* check if link status changed */ 106 | if (enc28j60->link_status != link) { 107 | /* when link up, read result */ 108 | if (link == ETH_LINK_UP) { 109 | if (phstat.dpxstat) { 110 | duplex = ETH_DUPLEX_FULL; 111 | } 112 | else { 113 | duplex = ETH_DUPLEX_HALF; 114 | } 115 | PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void*)speed) == ESP_OK, 116 | "change speed failed", err); 117 | PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void*)duplex) == ESP_OK, 118 | "change duplex failed", err); 119 | } 120 | PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void*)link) == ESP_OK, 121 | "change link failed", err); 122 | enc28j60->link_status = link; 123 | } 124 | return ESP_OK; 125 | err: 126 | return ESP_FAIL; 127 | } 128 | 129 | static esp_err_t enc28j60_set_mediator(esp_eth_phy_t* phy, esp_eth_mediator_t* eth) 130 | { 131 | PHY_CHECK(eth, "can't set mediator for enc28j60 to null", err); 132 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 133 | enc28j60->eth = eth; 134 | return ESP_OK; 135 | err: 136 | return ESP_ERR_INVALID_ARG; 137 | } 138 | 139 | static esp_err_t enc28j60_get_link(esp_eth_phy_t* phy) 140 | { 141 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 142 | /* Updata information about link, speed, duplex */ 143 | PHY_CHECK(enc28j60_update_link_duplex_speed(enc28j60) == ESP_OK, "update link duplex speed failed", err); 144 | return ESP_OK; 145 | err: 146 | return ESP_FAIL; 147 | } 148 | 149 | static esp_err_t enc28j60_reset(esp_eth_phy_t* phy) 150 | { 151 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 152 | enc28j60->link_status = ETH_LINK_DOWN; 153 | esp_eth_mediator_t* eth = enc28j60->eth; 154 | bmcr_reg_t bmcr = { .reset = 1 }; 155 | PHY_CHECK(eth->phy_reg_write(eth, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, 156 | "write BMCR failed", err); 157 | /* Wait for reset complete */ 158 | uint32_t to = 0; 159 | for (to = 0; to < enc28j60->reset_timeout_ms / 10; to++) { 160 | vTaskDelay(pdMS_TO_TICKS(10)); 161 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, 162 | "read BMCR failed", err); 163 | if (!bmcr.reset) { 164 | break; 165 | } 166 | } 167 | PHY_CHECK(to < enc28j60->reset_timeout_ms / 10, "PHY reset timeout", err); 168 | return ESP_OK; 169 | err: 170 | return ESP_FAIL; 171 | } 172 | 173 | static esp_err_t enc28j60_reset_hw(esp_eth_phy_t* phy) 174 | { 175 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 176 | // set reset_gpio_num minus zero can skip hardware reset phy chip 177 | if (enc28j60->reset_gpio_num >= 0) { 178 | gpio_reset_pin(enc28j60->reset_gpio_num); 179 | gpio_set_direction(enc28j60->reset_gpio_num, GPIO_MODE_OUTPUT); 180 | gpio_set_level(enc28j60->reset_gpio_num, 0); 181 | gpio_set_level(enc28j60->reset_gpio_num, 1); 182 | } 183 | return ESP_OK; 184 | } 185 | 186 | static esp_err_t enc28j60_autonego_ctrl(esp_eth_phy_t* phy, eth_phy_autoneg_cmd_t cmd, bool* autoneg_en_stat) 187 | { 188 | /** 189 | * ENC28J60 does not support automatic duplex negotiation. 190 | * If it is connected to an automatic duplex negotiation enabled network switch, 191 | * ENC28J60 will be detected as a half-duplex device. 192 | * To communicate in Full-Duplex mode, ENC28J60 and the remote node 193 | * must be manually configured for full-duplex operation. 194 | */ 195 | 196 | switch (cmd) 197 | { 198 | case ESP_ETH_PHY_AUTONEGO_RESTART: 199 | /* Fallthrough */ 200 | case ESP_ETH_PHY_AUTONEGO_EN: 201 | return ESP_ERR_NOT_SUPPORTED; 202 | case ESP_ETH_PHY_AUTONEGO_DIS: 203 | /* Fallthrough */ 204 | case ESP_ETH_PHY_AUTONEGO_G_STAT: 205 | *autoneg_en_stat = false; 206 | break; 207 | default: 208 | return ESP_ERR_INVALID_ARG; 209 | } 210 | return ESP_OK; 211 | } 212 | 213 | esp_err_t enc28j60_set_speed(esp_eth_phy_t* phy, eth_speed_t speed) 214 | { 215 | /* ENC28J60 supports only 10Mbps */ 216 | if (speed == ETH_SPEED_10M) { 217 | return ESP_OK; 218 | } 219 | return ESP_ERR_NOT_SUPPORTED; 220 | } 221 | 222 | esp_err_t enc28j60_set_duplex(esp_eth_phy_t* phy, eth_duplex_t duplex) 223 | { 224 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 225 | esp_eth_mediator_t* eth = enc28j60->eth; 226 | phcon1_reg_t phcon1; 227 | 228 | /* Since the link is going to be reconfigured, consider it down to be status updated once the driver re-started */ 229 | enc28j60->link_status = ETH_LINK_DOWN; 230 | 231 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, 0, &phcon1.val) == ESP_OK, 232 | "read PHCON1 failed", err); 233 | switch (duplex) { 234 | case ETH_DUPLEX_HALF: 235 | phcon1.pdpxmd = 0; 236 | break; 237 | case ETH_DUPLEX_FULL: 238 | phcon1.pdpxmd = 1; 239 | break; 240 | default: 241 | PHY_CHECK(false, "unknown duplex", err); 242 | break; 243 | } 244 | 245 | PHY_CHECK(eth->phy_reg_write(eth, enc28j60->addr, 0, phcon1.val) == ESP_OK, 246 | "write PHCON1 failed", err); 247 | 248 | return ESP_OK; 249 | err: 250 | return ESP_FAIL; 251 | } 252 | 253 | static esp_err_t enc28j60_pwrctl(esp_eth_phy_t* phy, bool enable) 254 | { 255 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 256 | esp_eth_mediator_t* eth = enc28j60->eth; 257 | bmcr_reg_t bmcr; 258 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, 259 | "read BMCR failed", err); 260 | if (!enable) { 261 | /* Enable IEEE Power Down Mode */ 262 | bmcr.power_down = 1; 263 | } 264 | else { 265 | /* Disable IEEE Power Down Mode */ 266 | bmcr.power_down = 0; 267 | } 268 | PHY_CHECK(eth->phy_reg_write(eth, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, 269 | "write BMCR failed", err); 270 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, 271 | "read BMCR failed", err); 272 | if (!enable) { 273 | PHY_CHECK(bmcr.power_down == 1, "power down failed", err); 274 | } 275 | else { 276 | PHY_CHECK(bmcr.power_down == 0, "power up failed", err); 277 | } 278 | return ESP_OK; 279 | err: 280 | return ESP_FAIL; 281 | } 282 | 283 | static esp_err_t enc28j60_set_addr(esp_eth_phy_t* phy, uint32_t addr) 284 | { 285 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 286 | enc28j60->addr = addr; 287 | return ESP_OK; 288 | } 289 | 290 | static esp_err_t enc28j60_get_addr(esp_eth_phy_t* phy, uint32_t* addr) 291 | { 292 | PHY_CHECK(addr, "addr can't be null", err); 293 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 294 | *addr = enc28j60->addr; 295 | return ESP_OK; 296 | err: 297 | return ESP_ERR_INVALID_ARG; 298 | } 299 | 300 | static esp_err_t enc28j60_del(esp_eth_phy_t* phy) 301 | { 302 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 303 | free(enc28j60); 304 | return ESP_OK; 305 | } 306 | 307 | static esp_err_t enc28j60_init(esp_eth_phy_t* phy) 308 | { 309 | phy_enc28j60_t* enc28j60 = __containerof(phy, phy_enc28j60_t, parent); 310 | esp_eth_mediator_t* eth = enc28j60->eth; 311 | /* Power on Ethernet PHY */ 312 | PHY_CHECK(enc28j60_pwrctl(phy, true) == ESP_OK, "power control failed", err); 313 | /* Reset Ethernet PHY */ 314 | PHY_CHECK(enc28j60_reset(phy) == ESP_OK, "reset failed", err); 315 | /* Check PHY ID */ 316 | phyidr1_reg_t id1; 317 | phyidr2_reg_t id2; 318 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, 319 | "read ID1 failed", err); 320 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, 321 | "read ID2 failed", err); 322 | PHY_CHECK(id1.oui_msb == 0x0083 && id2.oui_lsb == 0x05 && id2.vendor_model == 0x00, 323 | "wrong chip ID", err); 324 | /* Disable half duplex loopback */ 325 | phcon2_reg_t phcon2; 326 | PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, ETH_PHY_PHCON2_REG_ADDR, &(phcon2.val)) == ESP_OK, 327 | "read PHCON2 failed", err); 328 | phcon2.hdldis = 1; 329 | PHY_CHECK(eth->phy_reg_write(eth, enc28j60->addr, ETH_PHY_PHCON2_REG_ADDR, phcon2.val) == ESP_OK, 330 | "write PHCON2 failed", err); 331 | return ESP_OK; 332 | err: 333 | return ESP_FAIL; 334 | } 335 | 336 | static esp_err_t enc28j60_deinit(esp_eth_phy_t* phy) 337 | { 338 | /* Power off Ethernet PHY */ 339 | PHY_CHECK(enc28j60_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err); 340 | return ESP_OK; 341 | err: 342 | return ESP_FAIL; 343 | } 344 | 345 | esp_eth_phy_t* esp_eth_phy_new_enc28j60(const eth_phy_config_t* config) 346 | { 347 | PHY_CHECK(config, "can't set phy config to null", err); 348 | phy_enc28j60_t* enc28j60 = calloc(1, sizeof(phy_enc28j60_t)); 349 | PHY_CHECK(enc28j60, "calloc enc28j60 failed", err); 350 | enc28j60->addr = config->phy_addr; // although PHY addr is meaningless to ENC28J60 351 | enc28j60->reset_timeout_ms = config->reset_timeout_ms; 352 | enc28j60->reset_gpio_num = config->reset_gpio_num; 353 | enc28j60->link_status = ETH_LINK_DOWN; 354 | enc28j60->parent.reset = enc28j60_reset; 355 | enc28j60->parent.reset_hw = enc28j60_reset_hw; 356 | enc28j60->parent.init = enc28j60_init; 357 | enc28j60->parent.deinit = enc28j60_deinit; 358 | enc28j60->parent.set_mediator = enc28j60_set_mediator; 359 | enc28j60->parent.autonego_ctrl = enc28j60_autonego_ctrl; 360 | enc28j60->parent.get_link = enc28j60_get_link; 361 | enc28j60->parent.pwrctl = enc28j60_pwrctl; 362 | enc28j60->parent.get_addr = enc28j60_get_addr; 363 | enc28j60->parent.set_addr = enc28j60_set_addr; 364 | enc28j60->parent.set_speed = enc28j60_set_speed; 365 | enc28j60->parent.set_duplex = enc28j60_set_duplex; 366 | enc28j60->parent.del = enc28j60_del; 367 | return &(enc28j60->parent); 368 | err: 369 | return NULL; 370 | } -------------------------------------------------------------------------------- /src/extmod/esp_eth_mac_enc28j60.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include "driver/gpio.h" 10 | #include "esp_attr.h" 11 | #include "esp_log.h" 12 | #include "esp_eth.h" 13 | #include "esp_system.h" 14 | #include "esp_cpu.h" 15 | #include "esp_intr_alloc.h" 16 | #include "esp_heap_caps.h" 17 | #include "esp_rom_sys.h" 18 | #include "freertos/FreeRTOS.h" 19 | #include "freertos/task.h" 20 | #include "freertos/semphr.h" 21 | #include "esp_eth_enc28j60.h" 22 | #include "enc28j60.h" 23 | #include "sdkconfig.h" 24 | 25 | static const char* TAG = "enc28j60"; 26 | #define MAC_CHECK(a, str, goto_tag, ret_value, ...) \ 27 | do \ 28 | { \ 29 | if (!(a)) \ 30 | { \ 31 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ 32 | ret = ret_value; \ 33 | goto goto_tag; \ 34 | } \ 35 | } while (0) 36 | 37 | #define MAC_CHECK_NO_RET(a, str, goto_tag, ...) \ 38 | do \ 39 | { \ 40 | if (!(a)) \ 41 | { \ 42 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ 43 | goto goto_tag; \ 44 | } \ 45 | } while (0) 46 | 47 | #define ENC28J60_SPI_LOCK_TIMEOUT_MS (50) 48 | #define ENC28J60_REG_TRANS_LOCK_TIMEOUT_MS (150) 49 | #define ENC28J60_PHY_OPERATION_TIMEOUT_US (150) 50 | #define ENC28J60_SYSTEM_RESET_ADDITION_TIME_US (1000) 51 | #define ENC28J60_TX_READY_TIMEOUT_MS (2000) 52 | 53 | #define ENC28J60_BUFFER_SIZE (0x2000) // 8KB built-in buffer 54 | /** 55 | * ______ 56 | * |__TX__| TX: 2 KB : [0x1800, 0x2000) 57 | * | | 58 | * | RX | RX: 6 KB : [0x0000, 0x1800) 59 | * |______| 60 | * 61 | */ 62 | #define ENC28J60_BUF_RX_START (0) 63 | #define ENC28J60_BUF_RX_END (ENC28J60_BUF_TX_START - 1) 64 | #define ENC28J60_BUF_TX_START ((ENC28J60_BUFFER_SIZE / 4) * 3) 65 | #define ENC28J60_BUF_TX_END (ENC28J60_BUFFER_SIZE - 1) 66 | 67 | #define ENC28J60_RSV_SIZE (6) // Receive Status Vector Size 68 | #define ENC28J60_TSV_SIZE (6) // Transmit Status Vector Size 69 | 70 | typedef struct { 71 | uint8_t next_packet_low; 72 | uint8_t next_packet_high; 73 | uint8_t length_low; 74 | uint8_t length_high; 75 | uint8_t status_low; 76 | uint8_t status_high; 77 | } enc28j60_rx_header_t; 78 | 79 | typedef struct { 80 | uint16_t byte_cnt; 81 | 82 | uint8_t collision_cnt : 4; 83 | uint8_t crc_err : 1; 84 | uint8_t len_check_err : 1; 85 | uint8_t len_out_range : 1; 86 | uint8_t tx_done : 1; 87 | 88 | uint8_t multicast : 1; 89 | uint8_t broadcast : 1; 90 | uint8_t pkt_defer : 1; 91 | uint8_t excessive_defer : 1; 92 | uint8_t excessive_collision : 1; 93 | uint8_t late_collision : 1; 94 | uint8_t giant : 1; 95 | uint8_t underrun : 1; 96 | 97 | uint16_t bytes_on_wire; 98 | 99 | uint8_t ctrl_frame : 1; 100 | uint8_t pause_ctrl_frame : 1; 101 | uint8_t backpressure_app : 1; 102 | uint8_t vlan_frame : 1; 103 | } enc28j60_tsv_t; 104 | 105 | typedef struct { 106 | esp_eth_mac_t parent; 107 | esp_eth_mediator_t* eth; 108 | spi_device_handle_t spi_hdl; 109 | SemaphoreHandle_t spi_lock; 110 | SemaphoreHandle_t reg_trans_lock; 111 | SemaphoreHandle_t tx_ready_sem; 112 | TaskHandle_t rx_task_hdl; 113 | uint32_t sw_reset_timeout_ms; 114 | uint32_t next_packet_ptr; 115 | uint32_t last_tsv_addr; 116 | int int_gpio_num; 117 | uint8_t addr[6]; 118 | uint8_t last_bank; 119 | bool packets_remain; 120 | eth_enc28j60_rev_t revision; 121 | } emac_enc28j60_t; 122 | 123 | static inline bool enc28j60_spi_lock(emac_enc28j60_t* emac) 124 | { 125 | return xSemaphoreTake(emac->spi_lock, pdMS_TO_TICKS(ENC28J60_SPI_LOCK_TIMEOUT_MS)) == pdTRUE; 126 | } 127 | 128 | static inline bool enc28j60_spi_unlock(emac_enc28j60_t* emac) 129 | { 130 | return xSemaphoreGive(emac->spi_lock) == pdTRUE; 131 | } 132 | 133 | static inline bool enc28j60_reg_trans_lock(emac_enc28j60_t* emac) 134 | { 135 | return xSemaphoreTake(emac->reg_trans_lock, pdMS_TO_TICKS(ENC28J60_REG_TRANS_LOCK_TIMEOUT_MS)) == pdTRUE; 136 | } 137 | 138 | static inline bool enc28j60_reg_trans_unlock(emac_enc28j60_t* emac) 139 | { 140 | return xSemaphoreGive(emac->reg_trans_lock) == pdTRUE; 141 | } 142 | 143 | /** 144 | * @brief ERXRDPT need to be set always at odd addresses 145 | */ 146 | static inline uint32_t enc28j60_next_ptr_align_odd(uint32_t next_packet_ptr, uint32_t start, uint32_t end) 147 | { 148 | uint32_t erxrdpt; 149 | 150 | if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end)) { 151 | erxrdpt = end; 152 | } 153 | else { 154 | erxrdpt = next_packet_ptr - 1; 155 | } 156 | 157 | return erxrdpt; 158 | } 159 | 160 | /** 161 | * @brief Calculate wrap around when reading beyond the end of the RX buffer 162 | */ 163 | static inline uint32_t enc28j60_rx_packet_start(uint32_t start_addr, uint32_t off) 164 | { 165 | if (start_addr + off > ENC28J60_BUF_RX_END) { 166 | return (start_addr + off) - (ENC28J60_BUF_RX_END - ENC28J60_BUF_RX_START + 1); 167 | } 168 | else { 169 | return start_addr + off; 170 | } 171 | } 172 | 173 | /** 174 | * @brief SPI operation wrapper for writing ENC28J60 internal register 175 | */ 176 | static esp_err_t enc28j60_do_register_write(emac_enc28j60_t* emac, uint8_t reg_addr, uint8_t value) 177 | { 178 | esp_err_t ret = ESP_OK; 179 | spi_transaction_t trans = { 180 | .cmd = ENC28J60_SPI_CMD_WCR, // Write control register 181 | .addr = reg_addr, 182 | .length = 8, 183 | .flags = SPI_TRANS_USE_TXDATA, 184 | .tx_data = { 185 | [0] = value 186 | } 187 | }; 188 | if (enc28j60_spi_lock(emac)) { 189 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 190 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 191 | ret = ESP_FAIL; 192 | } 193 | enc28j60_spi_unlock(emac); 194 | } 195 | else { 196 | ret = ESP_ERR_TIMEOUT; 197 | } 198 | return ret; 199 | } 200 | 201 | /** 202 | * @brief SPI operation wrapper for reading ENC28J60 internal register 203 | */ 204 | static esp_err_t enc28j60_do_register_read(emac_enc28j60_t* emac, bool is_eth_reg, uint8_t reg_addr, uint8_t* value) 205 | { 206 | esp_err_t ret = ESP_OK; 207 | spi_transaction_t trans = { 208 | .cmd = ENC28J60_SPI_CMD_RCR, // Read control register 209 | .addr = reg_addr, 210 | .length = is_eth_reg ? 8 : 16, // read operation is different for ETH register and non-ETH register 211 | .flags = SPI_TRANS_USE_RXDATA 212 | }; 213 | if (enc28j60_spi_lock(emac)) { 214 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 215 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 216 | ret = ESP_FAIL; 217 | } 218 | else { 219 | *value = is_eth_reg ? trans.rx_data[0] : trans.rx_data[1]; 220 | } 221 | enc28j60_spi_unlock(emac); 222 | } 223 | else { 224 | ret = ESP_ERR_TIMEOUT; 225 | } 226 | return ret; 227 | } 228 | 229 | /** 230 | * @brief SPI operation wrapper for bitwise setting ENC28J60 internal register 231 | * @note can only be used for ETH registers 232 | */ 233 | static esp_err_t enc28j60_do_bitwise_set(emac_enc28j60_t* emac, uint8_t reg_addr, uint8_t mask) 234 | { 235 | esp_err_t ret = ESP_OK; 236 | spi_transaction_t trans = { 237 | .cmd = ENC28J60_SPI_CMD_BFS, // Bit field set 238 | .addr = reg_addr, 239 | .length = 8, 240 | .flags = SPI_TRANS_USE_TXDATA, 241 | .tx_data = { 242 | [0] = mask 243 | } 244 | }; 245 | if (enc28j60_spi_lock(emac)) { 246 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 247 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 248 | ret = ESP_FAIL; 249 | } 250 | enc28j60_spi_unlock(emac); 251 | } 252 | else { 253 | ret = ESP_ERR_TIMEOUT; 254 | } 255 | return ret; 256 | } 257 | 258 | /** 259 | * @brief SPI operation wrapper for bitwise clearing ENC28J60 internal register 260 | * @note can only be used for ETH registers 261 | */ 262 | static esp_err_t enc28j60_do_bitwise_clr(emac_enc28j60_t* emac, uint8_t reg_addr, uint8_t mask) 263 | { 264 | esp_err_t ret = ESP_OK; 265 | spi_transaction_t trans = { 266 | .cmd = ENC28J60_SPI_CMD_BFC, // Bit field clear 267 | .addr = reg_addr, 268 | .length = 8, 269 | .flags = SPI_TRANS_USE_TXDATA, 270 | .tx_data = { 271 | [0] = mask 272 | } 273 | }; 274 | if (enc28j60_spi_lock(emac)) { 275 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 276 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 277 | ret = ESP_FAIL; 278 | } 279 | enc28j60_spi_unlock(emac); 280 | } 281 | else { 282 | ret = ESP_ERR_TIMEOUT; 283 | } 284 | return ret; 285 | } 286 | 287 | /** 288 | * @brief SPI operation wrapper for writing ENC28J60 internal memory 289 | */ 290 | static esp_err_t enc28j60_do_memory_write(emac_enc28j60_t* emac, uint8_t* buffer, uint32_t len) 291 | { 292 | esp_err_t ret = ESP_OK; 293 | spi_transaction_t trans = { 294 | .cmd = ENC28J60_SPI_CMD_WBM, // Write buffer memory 295 | .addr = 0x1A, 296 | .length = len * 8, 297 | .tx_buffer = buffer 298 | }; 299 | if (enc28j60_spi_lock(emac)) { 300 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 301 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 302 | ret = ESP_FAIL; 303 | } 304 | enc28j60_spi_unlock(emac); 305 | } 306 | else { 307 | ret = ESP_ERR_TIMEOUT; 308 | } 309 | return ret; 310 | } 311 | 312 | /** 313 | * @brief SPI operation wrapper for reading ENC28J60 internal memory 314 | */ 315 | static esp_err_t enc28j60_do_memory_read(emac_enc28j60_t* emac, uint8_t* buffer, uint32_t len) 316 | { 317 | esp_err_t ret = ESP_OK; 318 | spi_transaction_t trans = { 319 | .cmd = ENC28J60_SPI_CMD_RBM, // Read buffer memory 320 | .addr = 0x1A, 321 | .length = len * 8, 322 | .rx_buffer = buffer 323 | }; 324 | 325 | if (enc28j60_spi_lock(emac)) { 326 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 327 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 328 | ret = ESP_FAIL; 329 | } 330 | enc28j60_spi_unlock(emac); 331 | } 332 | else { 333 | ret = ESP_ERR_TIMEOUT; 334 | } 335 | return ret; 336 | } 337 | 338 | /** 339 | * @brief SPI operation wrapper for resetting ENC28J60 340 | */ 341 | static esp_err_t enc28j60_do_reset(emac_enc28j60_t* emac) 342 | { 343 | esp_err_t ret = ESP_OK; 344 | spi_transaction_t trans = { 345 | .cmd = ENC28J60_SPI_CMD_SRC, // Soft reset 346 | .addr = 0x1F, 347 | }; 348 | if (enc28j60_spi_lock(emac)) { 349 | if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) { 350 | ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); 351 | ret = ESP_FAIL; 352 | } 353 | enc28j60_spi_unlock(emac); 354 | } 355 | else { 356 | ret = ESP_ERR_TIMEOUT; 357 | } 358 | 359 | // After reset, wait at least 1ms for the device to be ready 360 | esp_rom_delay_us(ENC28J60_SYSTEM_RESET_ADDITION_TIME_US); 361 | 362 | return ret; 363 | } 364 | 365 | /** 366 | * @brief Switch ENC28J60 register bank 367 | */ 368 | static esp_err_t enc28j60_switch_register_bank(emac_enc28j60_t* emac, uint8_t bank) 369 | { 370 | esp_err_t ret = ESP_OK; 371 | if (bank != emac->last_bank) { 372 | MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_ECON1, 0x03) == ESP_OK, 373 | "clear ECON1[1:0] failed", out, ESP_FAIL); 374 | MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, bank & 0x03) == ESP_OK, 375 | "set ECON1[1:0] failed", out, ESP_FAIL); 376 | emac->last_bank = bank; 377 | } 378 | out: 379 | return ret; 380 | } 381 | 382 | /** 383 | * @brief Write ENC28J60 register 384 | */ 385 | static esp_err_t enc28j60_register_write(emac_enc28j60_t* emac, uint16_t reg_addr, uint8_t value) 386 | { 387 | esp_err_t ret = ESP_OK; 388 | if (enc28j60_reg_trans_lock(emac)) { 389 | MAC_CHECK(enc28j60_switch_register_bank(emac, (reg_addr & 0xF00) >> 8) == ESP_OK, 390 | "switch bank failed", out, ESP_FAIL); 391 | MAC_CHECK(enc28j60_do_register_write(emac, reg_addr & 0xFF, value) == ESP_OK, 392 | "write register failed", out, ESP_FAIL); 393 | enc28j60_reg_trans_unlock(emac); 394 | } 395 | else { 396 | ret = ESP_ERR_TIMEOUT; 397 | } 398 | return ret; 399 | out: 400 | enc28j60_reg_trans_unlock(emac); 401 | return ret; 402 | } 403 | 404 | /** 405 | * @brief Read ENC28J60 register 406 | */ 407 | static esp_err_t enc28j60_register_read(emac_enc28j60_t* emac, uint16_t reg_addr, uint8_t* value) 408 | { 409 | esp_err_t ret = ESP_OK; 410 | if (enc28j60_reg_trans_lock(emac)) { 411 | MAC_CHECK(enc28j60_switch_register_bank(emac, (reg_addr & 0xF00) >> 8) == ESP_OK, 412 | "switch bank failed", out, ESP_FAIL); 413 | MAC_CHECK(enc28j60_do_register_read(emac, !(reg_addr & 0xF000), reg_addr & 0xFF, value) == ESP_OK, 414 | "read register failed", out, ESP_FAIL); 415 | enc28j60_reg_trans_unlock(emac); 416 | } 417 | else { 418 | ret = ESP_ERR_TIMEOUT; 419 | } 420 | return ret; 421 | out: 422 | enc28j60_reg_trans_unlock(emac); 423 | return ret; 424 | } 425 | 426 | /** 427 | * @brief Read ENC28J60 internal memroy 428 | */ 429 | static esp_err_t enc28j60_read_packet(emac_enc28j60_t* emac, uint32_t addr, uint8_t* packet, uint32_t len) 430 | { 431 | esp_err_t ret = ESP_OK; 432 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTL, addr & 0xFF) == ESP_OK, 433 | "write ERDPTL failed", out, ESP_FAIL); 434 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTH, (addr & 0xFF00) >> 8) == ESP_OK, 435 | "write ERDPTH failed", out, ESP_FAIL); 436 | MAC_CHECK(enc28j60_do_memory_read(emac, packet, len) == ESP_OK, 437 | "read memory failed", out, ESP_FAIL); 438 | out: 439 | return ret; 440 | } 441 | 442 | /** 443 | * @brief Write ENC28J60 internal PHY register 444 | */ 445 | static esp_err_t emac_enc28j60_write_phy_reg(esp_eth_mac_t* mac, uint32_t phy_addr, 446 | uint32_t phy_reg, uint32_t reg_value) 447 | { 448 | esp_err_t ret = ESP_OK; 449 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 450 | uint8_t mii_status; 451 | 452 | /* check if phy access is in progress */ 453 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK, 454 | "read MISTAT failed", out, ESP_FAIL); 455 | MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_INVALID_STATE); 456 | 457 | /* tell the PHY address to write */ 458 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIREGADR, phy_reg & 0xFF) == ESP_OK, 459 | "write MIREGADR failed", out, ESP_FAIL); 460 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIWRL, reg_value & 0xFF) == ESP_OK, 461 | "write MIWRL failed", out, ESP_FAIL); 462 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIWRH, (reg_value & 0xFF00) >> 8) == ESP_OK, 463 | "write MIWRH failed", out, ESP_FAIL); 464 | 465 | /* polling the busy flag */ 466 | uint32_t to = 0; 467 | do { 468 | esp_rom_delay_us(15); 469 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK, 470 | "read MISTAT failed", out, ESP_FAIL); 471 | to += 15; 472 | } while ((mii_status & MISTAT_BUSY) && to < ENC28J60_PHY_OPERATION_TIMEOUT_US); 473 | MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_TIMEOUT); 474 | out: 475 | return ret; 476 | } 477 | 478 | /** 479 | * @brief Read ENC28J60 internal PHY register 480 | */ 481 | static esp_err_t emac_enc28j60_read_phy_reg(esp_eth_mac_t* mac, uint32_t phy_addr, 482 | uint32_t phy_reg, uint32_t* reg_value) 483 | { 484 | esp_err_t ret = ESP_OK; 485 | MAC_CHECK(reg_value, "can't set reg_value to null", out, ESP_ERR_INVALID_ARG); 486 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 487 | uint8_t mii_status; 488 | uint8_t mii_cmd; 489 | 490 | /* check if phy access is in progress */ 491 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK, 492 | "read MISTAT failed", out, ESP_FAIL); 493 | MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_INVALID_STATE); 494 | 495 | /* tell the PHY address to read */ 496 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MIREGADR, phy_reg & 0xFF) == ESP_OK, 497 | "write MIREGADR failed", out, ESP_FAIL); 498 | mii_cmd = MICMD_MIIRD; 499 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MICMD, mii_cmd) == ESP_OK, 500 | "write MICMD failed", out, ESP_FAIL); 501 | 502 | /* polling the busy flag */ 503 | uint32_t to = 0; 504 | do { 505 | esp_rom_delay_us(15); 506 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MISTAT, &mii_status) == ESP_OK, 507 | "read MISTAT failed", out, ESP_FAIL); 508 | to += 15; 509 | } while ((mii_status & MISTAT_BUSY) && to < ENC28J60_PHY_OPERATION_TIMEOUT_US); 510 | MAC_CHECK(!(mii_status & MISTAT_BUSY), "phy is busy", out, ESP_ERR_TIMEOUT); 511 | 512 | mii_cmd &= (~MICMD_MIIRD); 513 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MICMD, mii_cmd) == ESP_OK, 514 | "write MICMD failed", out, ESP_FAIL); 515 | 516 | uint8_t value_l = 0; 517 | uint8_t value_h = 0; 518 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MIRDL, &value_l) == ESP_OK, 519 | "read MIRDL failed", out, ESP_FAIL); 520 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MIRDH, &value_h) == ESP_OK, 521 | "read MIRDH failed", out, ESP_FAIL); 522 | *reg_value = (value_h << 8) | value_l; 523 | out: 524 | return ret; 525 | } 526 | 527 | /** 528 | * @brief Set mediator for Ethernet MAC 529 | */ 530 | static esp_err_t emac_enc28j60_set_mediator(esp_eth_mac_t* mac, esp_eth_mediator_t* eth) 531 | { 532 | esp_err_t ret = ESP_OK; 533 | MAC_CHECK(eth, "can't set mac's mediator to null", out, ESP_ERR_INVALID_ARG); 534 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 535 | emac->eth = eth; 536 | out: 537 | return ret; 538 | } 539 | 540 | /** 541 | * @brief Verify chip revision ID 542 | */ 543 | static esp_err_t enc28j60_verify_id(emac_enc28j60_t* emac) 544 | { 545 | esp_err_t ret = ESP_OK; 546 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_EREVID, (uint8_t*)&emac->revision) == ESP_OK, 547 | "read EREVID failed", out, ESP_FAIL); 548 | ESP_LOGI(TAG, "revision: %d", emac->revision); 549 | MAC_CHECK(emac->revision >= ENC28J60_REV_B1 && emac->revision <= ENC28J60_REV_B7, "wrong chip ID", out, ESP_ERR_INVALID_VERSION); 550 | out: 551 | return ret; 552 | } 553 | 554 | /** 555 | * @brief Write mac address to internal registers 556 | */ 557 | static esp_err_t enc28j60_set_mac_addr(emac_enc28j60_t* emac) 558 | { 559 | esp_err_t ret = ESP_OK; 560 | 561 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR6, emac->addr[5]) == ESP_OK, 562 | "write MAADR6 failed", out, ESP_FAIL); 563 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR5, emac->addr[4]) == ESP_OK, 564 | "write MAADR5 failed", out, ESP_FAIL); 565 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR4, emac->addr[3]) == ESP_OK, 566 | "write MAADR4 failed", out, ESP_FAIL); 567 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR3, emac->addr[2]) == ESP_OK, 568 | "write MAADR3 failed", out, ESP_FAIL); 569 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR2, emac->addr[1]) == ESP_OK, 570 | "write MAADR2 failed", out, ESP_FAIL); 571 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAADR1, emac->addr[0]) == ESP_OK, 572 | "write MAADR1 failed", out, ESP_FAIL); 573 | out: 574 | return ret; 575 | } 576 | 577 | /** 578 | * @brief Clear multicast hash table 579 | */ 580 | static esp_err_t enc28j60_clear_multicast_table(emac_enc28j60_t* emac) 581 | { 582 | esp_err_t ret = ESP_OK; 583 | 584 | for (int i = 0; i < 7; i++) { 585 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_EHT0 + i, 0x00) == ESP_OK, 586 | "write ENC28J60_EHT%d failed", out, ESP_FAIL, i); 587 | } 588 | out: 589 | return ret; 590 | } 591 | 592 | /** 593 | * @brief Default setup for ENC28J60 internal registers 594 | */ 595 | static esp_err_t enc28j60_setup_default(emac_enc28j60_t* emac) 596 | { 597 | esp_err_t ret = ESP_OK; 598 | 599 | // set up receive buffer start + end 600 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXSTL, ENC28J60_BUF_RX_START & 0xFF) == ESP_OK, 601 | "write ERXSTL failed", out, ESP_FAIL); 602 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXSTH, (ENC28J60_BUF_RX_START & 0xFF00) >> 8) == ESP_OK, 603 | "write ERXSTH failed", out, ESP_FAIL); 604 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXNDL, ENC28J60_BUF_RX_END & 0xFF) == ESP_OK, 605 | "write ERXNDL failed", out, ESP_FAIL); 606 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXNDH, (ENC28J60_BUF_RX_END & 0xFF00) >> 8) == ESP_OK, 607 | "write ERXNDH failed", out, ESP_FAIL); 608 | uint32_t erxrdpt = enc28j60_next_ptr_align_odd(ENC28J60_BUF_RX_START, ENC28J60_BUF_RX_START, ENC28J60_BUF_RX_END); 609 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTL, erxrdpt & 0xFF) == ESP_OK, 610 | "write ERXRDPTL failed", out, ESP_FAIL); 611 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTH, (erxrdpt & 0xFF00) >> 8) == ESP_OK, 612 | "write ERXRDPTH failed", out, ESP_FAIL); 613 | 614 | // set up transmit buffer start + end 615 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXSTL, ENC28J60_BUF_TX_START & 0xFF) == ESP_OK, 616 | "write ETXSTL failed", out, ESP_FAIL); 617 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXSTH, (ENC28J60_BUF_TX_START & 0xFF00) >> 8) == ESP_OK, 618 | "write ETXSTH failed", out, ESP_FAIL); 619 | 620 | // set up default filter mode: (unicast OR broadcast OR multicast) AND crc valid 621 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN | ERXFCON_MCEN) == ESP_OK, 622 | "write ERXFCON failed", out, ESP_FAIL); 623 | 624 | // enable MAC receive, enable pause control frame on Tx and Rx path 625 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON1, MACON1_MARXEN | MACON1_RXPAUS | MACON1_TXPAUS) == ESP_OK, 626 | "write MACON1 failed", out, ESP_FAIL); 627 | // enable automatic padding, append CRC, check frame length, half duplex by default (can update at runtime) 628 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN) == ESP_OK, "write MACON3 failed", out, ESP_FAIL); 629 | // enable defer transmission (effective only in half duplex) 630 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON4, MACON4_DEFER) == ESP_OK, 631 | "write MACON4 failed", out, ESP_FAIL); 632 | // set inter-frame gap (back-to-back) 633 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MABBIPG, 0x12) == ESP_OK, 634 | "write MABBIPG failed", out, ESP_FAIL); 635 | // set inter-frame gap (non-back-to-back) 636 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAIPGL, 0x12) == ESP_OK, 637 | "write MAIPGL failed", out, ESP_FAIL); 638 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MAIPGH, 0x0C) == ESP_OK, 639 | "write MAIPGH failed", out, ESP_FAIL); 640 | 641 | out: 642 | return ret; 643 | } 644 | 645 | /** 646 | * @brief Start enc28j60: enable interrupt and start receive 647 | */ 648 | static esp_err_t emac_enc28j60_start(esp_eth_mac_t* mac) 649 | { 650 | esp_err_t ret = ESP_OK; 651 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 652 | /* enable interrupt */ 653 | MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_EIR, 0xFF) == ESP_OK, 654 | "clear EIR failed", out, ESP_FAIL); 655 | MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_EIE, EIE_PKTIE | EIE_INTIE | EIE_TXERIE) == ESP_OK, 656 | "set EIE.[PKTIE|INTIE] failed", out, ESP_FAIL); 657 | /* enable rx logic */ 658 | MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, ECON1_RXEN) == ESP_OK, 659 | "set ECON1.RXEN failed", out, ESP_FAIL); 660 | 661 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTL, 0x00) == ESP_OK, 662 | "write ERDPTL failed", out, ESP_FAIL); 663 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERDPTH, 0x00) == ESP_OK, 664 | "write ERDPTH failed", out, ESP_FAIL); 665 | out: 666 | return ret; 667 | } 668 | 669 | /** 670 | * @brief Stop enc28j60: disable interrupt and stop receiving packets 671 | */ 672 | static esp_err_t emac_enc28j60_stop(esp_eth_mac_t* mac) 673 | { 674 | esp_err_t ret = ESP_OK; 675 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 676 | /* disable interrupt */ 677 | MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_EIE, 0xFF) == ESP_OK, 678 | "clear EIE failed", out, ESP_FAIL); 679 | /* disable rx */ 680 | MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_ECON1, ECON1_RXEN) == ESP_OK, 681 | "clear ECON1.RXEN failed", out, ESP_FAIL); 682 | out: 683 | return ret; 684 | } 685 | 686 | static esp_err_t emac_enc28j60_set_addr(esp_eth_mac_t* mac, uint8_t* addr) 687 | { 688 | esp_err_t ret = ESP_OK; 689 | MAC_CHECK(addr, "can't set mac addr to null", out, ESP_ERR_INVALID_ARG); 690 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 691 | memcpy(emac->addr, addr, 6); 692 | MAC_CHECK(enc28j60_set_mac_addr(emac) == ESP_OK, "set mac address failed", out, ESP_FAIL); 693 | out: 694 | return ret; 695 | } 696 | 697 | static esp_err_t emac_enc28j60_get_addr(esp_eth_mac_t* mac, uint8_t* addr) 698 | { 699 | esp_err_t ret = ESP_OK; 700 | MAC_CHECK(addr, "can't set mac addr to null", out, ESP_ERR_INVALID_ARG); 701 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 702 | memcpy(addr, emac->addr, 6); 703 | out: 704 | return ret; 705 | } 706 | 707 | static inline esp_err_t emac_enc28j60_get_tsv(emac_enc28j60_t* emac, enc28j60_tsv_t* tsv) 708 | { 709 | return enc28j60_read_packet(emac, emac->last_tsv_addr, (uint8_t*)tsv, ENC28J60_TSV_SIZE); 710 | } 711 | 712 | static void enc28j60_isr_handler(void* arg) 713 | { 714 | emac_enc28j60_t* emac = (emac_enc28j60_t*)arg; 715 | BaseType_t high_task_wakeup = pdFALSE; 716 | /* notify enc28j60 task */ 717 | vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup); 718 | if (high_task_wakeup != pdFALSE) { 719 | portYIELD_FROM_ISR(); 720 | } 721 | } 722 | 723 | /** 724 | * @brief Main ENC28J60 Task. Mainly used for Rx processing. However, it also handles other interrupts. 725 | * 726 | */ 727 | static void emac_enc28j60_task(void* arg) 728 | { 729 | emac_enc28j60_t* emac = (emac_enc28j60_t*)arg; 730 | uint8_t status = 0; 731 | uint8_t mask = 0; 732 | uint8_t* buffer = NULL; 733 | uint32_t length = 0; 734 | 735 | while (1) { 736 | loop_start: 737 | // block until some task notifies me or check the gpio by myself 738 | if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... 739 | gpio_get_level(emac->int_gpio_num) != 0) { // ...and no interrupt asserted 740 | continue; // -> just continue to check again 741 | } 742 | // the host controller should clear the global enable bit for the interrupt pin before servicing the interrupt 743 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_clr(emac, ENC28J60_EIE, EIE_INTIE) == ESP_OK, 744 | "clear EIE_INTIE failed", loop_start); 745 | // read interrupt status 746 | MAC_CHECK_NO_RET(enc28j60_do_register_read(emac, true, ENC28J60_EIR, &status) == ESP_OK, 747 | "read EIR failed", loop_end); 748 | MAC_CHECK_NO_RET(enc28j60_do_register_read(emac, true, ENC28J60_EIE, &mask) == ESP_OK, 749 | "read EIE failed", loop_end); 750 | status &= mask; 751 | 752 | // When source of interrupt is unknown, try to check if there is packet waiting (Errata #6 workaround) 753 | if (status == 0) { 754 | uint8_t pk_counter; 755 | MAC_CHECK_NO_RET(enc28j60_register_read(emac, ENC28J60_EPKTCNT, &pk_counter) == ESP_OK, 756 | "read EPKTCNT failed", loop_end); 757 | if (pk_counter > 0) { 758 | status = EIR_PKTIF; 759 | } 760 | else { 761 | goto loop_end; 762 | } 763 | } 764 | 765 | // packet received 766 | if (status & EIR_PKTIF) { 767 | do { 768 | length = ETH_MAX_PACKET_SIZE; 769 | buffer = heap_caps_malloc(length, MALLOC_CAP_DMA); 770 | if (!buffer) { 771 | ESP_LOGE(TAG, "no mem for receive buffer"); 772 | } 773 | else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) { 774 | /* pass the buffer to stack (e.g. TCP/IP layer) */ 775 | if (length) { 776 | emac->eth->stack_input(emac->eth, buffer, length); 777 | } 778 | else { 779 | free(buffer); 780 | } 781 | } 782 | else { 783 | free(buffer); 784 | } 785 | } while (emac->packets_remain); 786 | } 787 | 788 | // transmit error 789 | if (status & EIR_TXERIF) { 790 | // Errata #12/#13 workaround - reset Tx state machine 791 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, ECON1_TXRST) == ESP_OK, 792 | "set TXRST failed", loop_end); 793 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_clr(emac, ENC28J60_ECON1, ECON1_TXRST) == ESP_OK, 794 | "clear TXRST failed", loop_end); 795 | 796 | // Clear Tx Error Interrupt Flag 797 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_clr(emac, ENC28J60_EIR, EIR_TXERIF) == ESP_OK, 798 | "clear TXERIF failed", loop_end); 799 | 800 | // Errata #13 workaround (applicable only to B5 and B7 revisions) 801 | if (emac->revision == ENC28J60_REV_B5 || emac->revision == ENC28J60_REV_B7) { 802 | __attribute__((aligned(4))) enc28j60_tsv_t tx_status; // SPI driver needs the rx buffer 4 byte align 803 | MAC_CHECK_NO_RET(emac_enc28j60_get_tsv(emac, &tx_status) == ESP_OK, 804 | "get Tx Status Vector failed", loop_end); 805 | // Try to retransmit when late collision is indicated 806 | if (tx_status.late_collision) { 807 | // Clear Tx Interrupt status Flag (it was set along with the error) 808 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_clr(emac, ENC28J60_EIR, EIR_TXIF) == ESP_OK, 809 | "clear TXIF failed", loop_end); 810 | // Enable global interrupt flag and try to retransmit 811 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_set(emac, ENC28J60_EIE, EIE_INTIE) == ESP_OK, 812 | "set INTIE failed", loop_end); 813 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, ECON1_TXRTS) == ESP_OK, 814 | "set TXRTS failed", loop_end); 815 | continue; // no need to handle Tx ready interrupt nor to enable global interrupt at this point 816 | } 817 | } 818 | } 819 | 820 | // transmit ready 821 | if (status & EIR_TXIF) { 822 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_clr(emac, ENC28J60_EIR, EIR_TXIF) == ESP_OK, 823 | "clear TXIF failed", loop_end); 824 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_clr(emac, ENC28J60_EIE, EIE_TXIE) == ESP_OK, 825 | "clear TXIE failed", loop_end); 826 | 827 | xSemaphoreGive(emac->tx_ready_sem); 828 | } 829 | loop_end: 830 | // restore global enable interrupt bit 831 | MAC_CHECK_NO_RET(enc28j60_do_bitwise_set(emac, ENC28J60_EIE, EIE_INTIE) == ESP_OK, 832 | "clear INTIE failed", loop_start); 833 | // Note: Interrupt flag PKTIF is cleared when PKTDEC is set (in receive function) 834 | } 835 | vTaskDelete(NULL); 836 | } 837 | 838 | static esp_err_t emac_enc28j60_set_link(esp_eth_mac_t* mac, eth_link_t link) 839 | { 840 | esp_err_t ret = ESP_OK; 841 | switch (link) { 842 | case ETH_LINK_UP: 843 | MAC_CHECK(mac->start(mac) == ESP_OK, "enc28j60 start failed", out, ESP_FAIL); 844 | break; 845 | case ETH_LINK_DOWN: 846 | MAC_CHECK(mac->stop(mac) == ESP_OK, "enc28j60 stop failed", out, ESP_FAIL); 847 | break; 848 | default: 849 | MAC_CHECK(false, "unknown link status", out, ESP_ERR_INVALID_ARG); 850 | break; 851 | } 852 | out: 853 | return ret; 854 | } 855 | 856 | static esp_err_t emac_enc28j60_set_speed(esp_eth_mac_t* mac, eth_speed_t speed) 857 | { 858 | esp_err_t ret = ESP_OK; 859 | switch (speed) { 860 | case ETH_SPEED_10M: 861 | ESP_LOGI(TAG, "working in 10Mbps"); 862 | break; 863 | default: 864 | MAC_CHECK(false, "100Mbps unsupported", out, ESP_ERR_NOT_SUPPORTED); 865 | break; 866 | } 867 | out: 868 | return ret; 869 | } 870 | 871 | static esp_err_t emac_enc28j60_set_duplex(esp_eth_mac_t* mac, eth_duplex_t duplex) 872 | { 873 | esp_err_t ret = ESP_OK; 874 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 875 | uint8_t mac3 = 0; 876 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_MACON3, &mac3) == ESP_OK, 877 | "read MACON3 failed", out, ESP_FAIL); 878 | switch (duplex) { 879 | case ETH_DUPLEX_HALF: 880 | mac3 &= ~MACON3_FULDPX; 881 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MABBIPG, 0x12) == ESP_OK, 882 | "write MABBIPG failed", out, ESP_FAIL); 883 | ESP_LOGI(TAG, "working in half duplex"); 884 | break; 885 | case ETH_DUPLEX_FULL: 886 | mac3 |= MACON3_FULDPX; 887 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MABBIPG, 0x15) == ESP_OK, 888 | "write MABBIPG failed", out, ESP_FAIL); 889 | ESP_LOGI(TAG, "working in full duplex"); 890 | break; 891 | default: 892 | MAC_CHECK(false, "unknown duplex", out, ESP_ERR_INVALID_ARG); 893 | break; 894 | } 895 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_MACON3, mac3) == ESP_OK, 896 | "write MACON3 failed", out, ESP_FAIL); 897 | out: 898 | return ret; 899 | } 900 | 901 | static esp_err_t emac_enc28j60_set_promiscuous(esp_eth_mac_t* mac, bool enable) 902 | { 903 | esp_err_t ret = ESP_OK; 904 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 905 | if (enable) { 906 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXFCON, 0x00) == ESP_OK, 907 | "write ERXFCON failed", out, ESP_FAIL); 908 | } 909 | out: 910 | return ret; 911 | } 912 | 913 | static esp_err_t emac_enc28j60_transmit(esp_eth_mac_t* mac, uint8_t* buf, uint32_t length) 914 | { 915 | esp_err_t ret = ESP_OK; 916 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 917 | uint8_t econ1 = 0; 918 | 919 | /* ENC28J60 may be a bottle neck in Eth communication. Hence we need to check if it is ready. */ 920 | if (xSemaphoreTake(emac->tx_ready_sem, pdMS_TO_TICKS(ENC28J60_TX_READY_TIMEOUT_MS)) == pdFALSE) { 921 | ESP_LOGW(TAG, "tx_ready_sem expired"); 922 | } 923 | MAC_CHECK(enc28j60_do_register_read(emac, true, ENC28J60_ECON1, &econ1) == ESP_OK, 924 | "read ECON1 failed", out, ESP_FAIL); 925 | MAC_CHECK(!(econ1 & ECON1_TXRTS), "last transmit still in progress", out, ESP_ERR_INVALID_STATE); 926 | 927 | /* Set the write pointer to start of transmit buffer area */ 928 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_EWRPTL, ENC28J60_BUF_TX_START & 0xFF) == ESP_OK, 929 | "write EWRPTL failed", out, ESP_FAIL); 930 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_EWRPTH, (ENC28J60_BUF_TX_START & 0xFF00) >> 8) == ESP_OK, 931 | "write EWRPTH failed", out, ESP_FAIL); 932 | 933 | /* Set the end pointer to correspond to the packet size given */ 934 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXNDL, (ENC28J60_BUF_TX_START + length) & 0xFF) == ESP_OK, 935 | "write ETXNDL failed", out, ESP_FAIL); 936 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ETXNDH, ((ENC28J60_BUF_TX_START + length) & 0xFF00) >> 8) == ESP_OK, 937 | "write ETXNDH failed", out, ESP_FAIL); 938 | 939 | /* copy data to tx memory */ 940 | uint8_t per_pkt_control = 0; // MACON3 will be used to determine how the packet will be transmitted 941 | MAC_CHECK(enc28j60_do_memory_write(emac, &per_pkt_control, 1) == ESP_OK, 942 | "write packet control byte failed", out, ESP_FAIL); 943 | MAC_CHECK(enc28j60_do_memory_write(emac, buf, length) == ESP_OK, 944 | "buffer memory write failed", out, ESP_FAIL); 945 | emac->last_tsv_addr = ENC28J60_BUF_TX_START + length + 1; 946 | 947 | /* enable Tx Interrupt to indicate next Tx ready state */ 948 | MAC_CHECK(enc28j60_do_bitwise_clr(emac, ENC28J60_EIR, EIR_TXIF) == ESP_OK, 949 | "set EIR_TXIF failed", out, ESP_FAIL); 950 | MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_EIE, EIE_TXIE) == ESP_OK, 951 | "set EIE_TXIE failed", out, ESP_FAIL); 952 | 953 | /* issue tx polling command */ 954 | MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON1, ECON1_TXRTS) == ESP_OK, 955 | "set ECON1.TXRTS failed", out, ESP_FAIL); 956 | out: 957 | return ret; 958 | } 959 | 960 | static esp_err_t emac_enc28j60_receive(esp_eth_mac_t* mac, uint8_t* buf, uint32_t* length) 961 | { 962 | esp_err_t ret = ESP_OK; 963 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 964 | uint8_t pk_counter = 0; 965 | uint16_t rx_len = 0; 966 | uint32_t next_packet_addr = 0; 967 | __attribute__((aligned(4))) enc28j60_rx_header_t header; // SPI driver needs the rx buffer 4 byte align 968 | 969 | // read packet header 970 | MAC_CHECK(enc28j60_read_packet(emac, emac->next_packet_ptr, (uint8_t*)&header, sizeof(header)) == ESP_OK, 971 | "read header failed", out, ESP_FAIL); 972 | 973 | // get packets' length, address 974 | rx_len = header.length_low + (header.length_high << 8); 975 | next_packet_addr = header.next_packet_low + (header.next_packet_high << 8); 976 | 977 | // read packet content 978 | MAC_CHECK(enc28j60_read_packet(emac, enc28j60_rx_packet_start(emac->next_packet_ptr, ENC28J60_RSV_SIZE), buf, rx_len) == ESP_OK, 979 | "read packet content failed", out, ESP_FAIL); 980 | 981 | // free receive buffer space 982 | uint32_t erxrdpt = enc28j60_next_ptr_align_odd(next_packet_addr, ENC28J60_BUF_RX_START, ENC28J60_BUF_RX_END); 983 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTL, (erxrdpt & 0xFF)) == ESP_OK, 984 | "write ERXRDPTL failed", out, ESP_FAIL); 985 | MAC_CHECK(enc28j60_register_write(emac, ENC28J60_ERXRDPTH, (erxrdpt & 0xFF00) >> 8) == ESP_OK, 986 | "write ERXRDPTH failed", out, ESP_FAIL); 987 | emac->next_packet_ptr = next_packet_addr; 988 | 989 | MAC_CHECK(enc28j60_do_bitwise_set(emac, ENC28J60_ECON2, ECON2_PKTDEC) == ESP_OK, 990 | "set ECON2.PKTDEC failed", out, ESP_FAIL); 991 | MAC_CHECK(enc28j60_register_read(emac, ENC28J60_EPKTCNT, &pk_counter) == ESP_OK, 992 | "read EPKTCNT failed", out, ESP_FAIL); 993 | 994 | *length = rx_len - 4; // substract the CRC length 995 | emac->packets_remain = pk_counter > 0; 996 | out: 997 | return ret; 998 | } 999 | 1000 | /** 1001 | * @brief Get chip info 1002 | */ 1003 | eth_enc28j60_rev_t emac_enc28j60_get_chip_info(esp_eth_mac_t* mac) 1004 | { 1005 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 1006 | return emac->revision; 1007 | } 1008 | 1009 | static esp_err_t emac_enc28j60_init(esp_eth_mac_t* mac) 1010 | { 1011 | esp_err_t ret = ESP_OK; 1012 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 1013 | esp_eth_mediator_t* eth = emac->eth; 1014 | 1015 | /* init gpio used for reporting enc28j60 interrupt */ 1016 | gpio_reset_pin(emac->int_gpio_num); 1017 | gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT); 1018 | gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY); 1019 | gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); 1020 | gpio_intr_enable(emac->int_gpio_num); 1021 | gpio_isr_handler_add(emac->int_gpio_num, enc28j60_isr_handler, emac); 1022 | MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, 1023 | "lowlevel init failed", out, ESP_FAIL); 1024 | 1025 | /* reset enc28j60 */ 1026 | MAC_CHECK(enc28j60_do_reset(emac) == ESP_OK, "reset enc28j60 failed", out, ESP_FAIL); 1027 | /* verify chip id */ 1028 | MAC_CHECK(enc28j60_verify_id(emac) == ESP_OK, "vefiry chip ID failed", out, ESP_FAIL); 1029 | /* default setup of internal registers */ 1030 | MAC_CHECK(enc28j60_setup_default(emac) == ESP_OK, "enc28j60 default setup failed", out, ESP_FAIL); 1031 | /* clear multicast hash table */ 1032 | MAC_CHECK(enc28j60_clear_multicast_table(emac) == ESP_OK, "clear multicast table failed", out, ESP_FAIL); 1033 | 1034 | return ESP_OK; 1035 | out: 1036 | gpio_isr_handler_remove(emac->int_gpio_num); 1037 | gpio_reset_pin(emac->int_gpio_num); 1038 | eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); 1039 | return ret; 1040 | } 1041 | 1042 | static esp_err_t emac_enc28j60_deinit(esp_eth_mac_t* mac) 1043 | { 1044 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 1045 | esp_eth_mediator_t* eth = emac->eth; 1046 | mac->stop(mac); 1047 | gpio_isr_handler_remove(emac->int_gpio_num); 1048 | gpio_reset_pin(emac->int_gpio_num); 1049 | eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL); 1050 | return ESP_OK; 1051 | } 1052 | 1053 | static esp_err_t emac_enc28j60_del(esp_eth_mac_t* mac) 1054 | { 1055 | emac_enc28j60_t* emac = __containerof(mac, emac_enc28j60_t, parent); 1056 | vTaskDelete(emac->rx_task_hdl); 1057 | spi_bus_remove_device(emac->spi_hdl); 1058 | vSemaphoreDelete(emac->spi_lock); 1059 | vSemaphoreDelete(emac->reg_trans_lock); 1060 | vSemaphoreDelete(emac->tx_ready_sem); 1061 | free(emac); 1062 | return ESP_OK; 1063 | } 1064 | 1065 | esp_eth_mac_t* esp_eth_mac_new_enc28j60(const eth_enc28j60_config_t* enc28j60_config, const eth_mac_config_t* mac_config) 1066 | { 1067 | esp_eth_mac_t* ret = NULL; 1068 | emac_enc28j60_t* emac = NULL; 1069 | MAC_CHECK(enc28j60_config, "can't set enc28j60 specific config to null", err, NULL); 1070 | MAC_CHECK(mac_config, "can't set mac config to null", err, NULL); 1071 | emac = calloc(1, sizeof(emac_enc28j60_t)); 1072 | MAC_CHECK(emac, "calloc emac failed", err, NULL); 1073 | /* enc28j60 driver is interrupt driven */ 1074 | MAC_CHECK(enc28j60_config->int_gpio_num >= 0, "error interrupt gpio number", err, NULL); 1075 | /* SPI device init */ 1076 | spi_device_interface_config_t spi_devcfg; 1077 | memcpy(&spi_devcfg, enc28j60_config->spi_devcfg, sizeof(spi_device_interface_config_t)); 1078 | if (enc28j60_config->spi_devcfg->command_bits == 0 && enc28j60_config->spi_devcfg->address_bits == 0) { 1079 | /* configure default SPI frame format */ 1080 | spi_devcfg.command_bits = 3; 1081 | spi_devcfg.address_bits = 5; 1082 | } 1083 | else { 1084 | MAC_CHECK(enc28j60_config->spi_devcfg->command_bits == 3 || enc28j60_config->spi_devcfg->address_bits == 5, 1085 | "incorrect SPI frame format (command_bits/address_bits)", err, NULL); 1086 | } 1087 | MAC_CHECK(spi_bus_add_device(enc28j60_config->spi_host_id, &spi_devcfg, &emac->spi_hdl) == ESP_OK, 1088 | "adding device to SPI host #%d failed", err, NULL, enc28j60_config->spi_host_id + 1); 1089 | 1090 | emac->last_bank = 0xFF; 1091 | emac->next_packet_ptr = ENC28J60_BUF_RX_START; 1092 | /* bind methods and attributes */ 1093 | emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms; 1094 | emac->int_gpio_num = enc28j60_config->int_gpio_num; 1095 | emac->parent.set_mediator = emac_enc28j60_set_mediator; 1096 | emac->parent.init = emac_enc28j60_init; 1097 | emac->parent.deinit = emac_enc28j60_deinit; 1098 | emac->parent.start = emac_enc28j60_start; 1099 | emac->parent.stop = emac_enc28j60_stop; 1100 | emac->parent.del = emac_enc28j60_del; 1101 | emac->parent.write_phy_reg = emac_enc28j60_write_phy_reg; 1102 | emac->parent.read_phy_reg = emac_enc28j60_read_phy_reg; 1103 | emac->parent.set_addr = emac_enc28j60_set_addr; 1104 | emac->parent.get_addr = emac_enc28j60_get_addr; 1105 | emac->parent.set_speed = emac_enc28j60_set_speed; 1106 | emac->parent.set_duplex = emac_enc28j60_set_duplex; 1107 | emac->parent.set_link = emac_enc28j60_set_link; 1108 | emac->parent.set_promiscuous = emac_enc28j60_set_promiscuous; 1109 | emac->parent.transmit = emac_enc28j60_transmit; 1110 | emac->parent.receive = emac_enc28j60_receive; 1111 | /* create mutex */ 1112 | emac->spi_lock = xSemaphoreCreateMutex(); 1113 | MAC_CHECK(emac->spi_lock, "create spi lock failed", err, NULL); 1114 | emac->reg_trans_lock = xSemaphoreCreateMutex(); 1115 | MAC_CHECK(emac->reg_trans_lock, "create register transaction lock failed", err, NULL); 1116 | emac->tx_ready_sem = xSemaphoreCreateBinary(); 1117 | MAC_CHECK(emac->tx_ready_sem, "create pkt transmit ready semaphore failed", err, NULL); 1118 | xSemaphoreGive(emac->tx_ready_sem); // ensures the first transmit is performed without waiting 1119 | /* create enc28j60 task */ 1120 | BaseType_t core_num = tskNO_AFFINITY; 1121 | if (mac_config->flags & ETH_MAC_FLAG_PIN_TO_CORE) { 1122 | core_num = esp_cpu_get_core_id(); 1123 | } 1124 | BaseType_t xReturned = xTaskCreatePinnedToCore(emac_enc28j60_task, "enc28j60_tsk", mac_config->rx_task_stack_size, emac, 1125 | mac_config->rx_task_prio, &emac->rx_task_hdl, core_num); 1126 | MAC_CHECK(xReturned == pdPASS, "create enc28j60 task failed", err, NULL); 1127 | 1128 | return &(emac->parent); 1129 | err: 1130 | if (emac) { 1131 | if (emac->rx_task_hdl) { 1132 | vTaskDelete(emac->rx_task_hdl); 1133 | } 1134 | if (emac->spi_lock) { 1135 | vSemaphoreDelete(emac->spi_lock); 1136 | } 1137 | if (emac->reg_trans_lock) { 1138 | vSemaphoreDelete(emac->reg_trans_lock); 1139 | } 1140 | if (emac->tx_ready_sem) { 1141 | vSemaphoreDelete(emac->tx_ready_sem); 1142 | } 1143 | free(emac); 1144 | } 1145 | return ret; 1146 | } --------------------------------------------------------------------------------