diff --git a/cores/rp2040/USB.cpp b/cores/rp2040/USB.cpp index 52aa4e073..6b399e2b6 100644 --- a/cores/rp2040/USB.cpp +++ b/cores/rp2040/USB.cpp @@ -610,7 +610,12 @@ extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t pr product_rev[0] = 0; } - +extern "C" bool tud_network_recv_cb(const uint8_t *src, uint16_t size) __attribute__((weak)); +extern "C" bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { + (void) src; + (void) size; + return false; +} #ifdef ENABLE_PICOTOOL_USB // Support for Microsoft OS 2.0 descriptor diff --git a/include/tusb_config.h b/include/tusb_config.h index e867b2074..87a29f27e 100644 --- a/include/tusb_config.h +++ b/include/tusb_config.h @@ -75,6 +75,7 @@ #define CFG_TUD_MSC (1) #define CFG_TUD_MIDI (1) #define CFG_TUD_VENDOR (0) +#define CFG_TUD_NCM (1) #define CFG_TUD_CDC_RX_BUFSIZE (256) #define CFG_TUD_CDC_TX_BUFSIZE (256) @@ -84,10 +85,33 @@ // HID buffer size Should be sufficient to hold ID (if any) + Data #define CFG_TUD_HID_EP_BUFSIZE (64) + // MIDI #define CFG_TUD_MIDI_RX_BUFSIZE (64) #define CFG_TUD_MIDI_TX_BUFSIZE (64) +//-------------------------------------------------------------------- +// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING +//-------------------------------------------------------------------- +#include "lwipopts.h" +// Must be >> MTU +// Can be set to 2048 without impact +#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * TCP_MSS + 100) + +// Must be >> MTU +// Can be set to smaller values if wNtbOutMaxDatagrams==1 +#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100) + +// Number of NCM transfer blocks for reception side +#ifndef CFG_TUD_NCM_OUT_NTB_N +#define CFG_TUD_NCM_OUT_NTB_N 1 +#endif + +// Number of NCM transfer blocks for transmission side +#ifndef CFG_TUD_NCM_IN_NTB_N +#define CFG_TUD_NCM_IN_NTB_N 1 +#endif + #ifdef __cplusplus } #endif diff --git a/lib/rp2040/liblwip-bt.a b/lib/rp2040/liblwip-bt.a index 6c0e63425..df598f324 100644 Binary files a/lib/rp2040/liblwip-bt.a and b/lib/rp2040/liblwip-bt.a differ diff --git a/lib/rp2040/liblwip.a b/lib/rp2040/liblwip.a index b6b044ee9..3c767cbd6 100644 Binary files a/lib/rp2040/liblwip.a and b/lib/rp2040/liblwip.a differ diff --git a/lib/rp2040/libpico.a b/lib/rp2040/libpico.a index 872dce81c..327d17a7d 100644 Binary files a/lib/rp2040/libpico.a and b/lib/rp2040/libpico.a differ diff --git a/lib/rp2350-riscv/liblwip-bt.a b/lib/rp2350-riscv/liblwip-bt.a index f972f7ec6..ae99fe849 100644 Binary files a/lib/rp2350-riscv/liblwip-bt.a and b/lib/rp2350-riscv/liblwip-bt.a differ diff --git a/lib/rp2350-riscv/liblwip.a b/lib/rp2350-riscv/liblwip.a index 9b19a2957..aa5080e09 100644 Binary files a/lib/rp2350-riscv/liblwip.a and b/lib/rp2350-riscv/liblwip.a differ diff --git a/lib/rp2350-riscv/libpico.a b/lib/rp2350-riscv/libpico.a index 59fbefd87..480cc8505 100644 Binary files a/lib/rp2350-riscv/libpico.a and b/lib/rp2350-riscv/libpico.a differ diff --git a/lib/rp2350/liblwip-bt.a b/lib/rp2350/liblwip-bt.a index 2cefd22ad..9f67aa8dc 100644 Binary files a/lib/rp2350/liblwip-bt.a and b/lib/rp2350/liblwip-bt.a differ diff --git a/lib/rp2350/liblwip.a b/lib/rp2350/liblwip.a index c84fdae7e..3173428a9 100644 Binary files a/lib/rp2350/liblwip.a and b/lib/rp2350/liblwip.a differ diff --git a/lib/rp2350/libpico.a b/lib/rp2350/libpico.a index dfc5348b6..774b1c324 100644 Binary files a/lib/rp2350/libpico.a and b/lib/rp2350/libpico.a differ diff --git a/libraries/lwIP_USB_NCM/.gitignore b/libraries/lwIP_USB_NCM/.gitignore new file mode 100644 index 000000000..cf5b839ea --- /dev/null +++ b/libraries/lwIP_USB_NCM/.gitignore @@ -0,0 +1,2 @@ +**/.pio/ +**/.vscode/ diff --git a/libraries/lwIP_USB_NCM/.gitkeep b/libraries/lwIP_USB_NCM/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/.gitignore b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/.gitignore new file mode 100644 index 000000000..9fccbae7c --- /dev/null +++ b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/.gitignore @@ -0,0 +1,7 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +*/README + diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/include/.gitkeep b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/include/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/lib/.gitkeep b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/lib/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/platformio.ini b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/platformio.ini new file mode 100644 index 000000000..950e85670 --- /dev/null +++ b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/platformio.ini @@ -0,0 +1,31 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +default_envs = pico + +[env:pico] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = rpipico +framework = arduino +board_build.core = earlephilhower +platform_packages = + framework-arduinopico@symlink://path/to/arduino-pico + +;build_flags = -DLWIP_DEBUG=1 -DDEBUG_RP2040_PORT=Serial1 + +[env:pico2] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = rpipico2 +framework = arduino +board_build.core = earlephilhower +platform_packages = + framework-arduinopico@symlink://path/to/arduino-pico + diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/src/main.cpp b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/src/main.cpp new file mode 100644 index 000000000..fe6e3be0b --- /dev/null +++ b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/src/main.cpp @@ -0,0 +1,136 @@ +/* + This sketch establishes a TCP connection to a "quote of the day" service. + It sends a "hello" message, and then prints received data. +*/ + +#include +#include + +const char* host = "djxmmx.net"; +const uint16_t port = 17; + +NCMEthernetlwIP eth; +IPAddress my_static_ip_addr(192, 168, 137, 100); +IPAddress my_static_gateway_and_dns_addr(192, 168, 137, 1); + +#define USE_REAL_UART + +#if defined(USE_REAL_UART) +#define SER Serial1 +#else +#define SER Serial +#endif + +void setup() { + // enable Serial1 so it can be used by USE_REAL_UART or by DEBUG_RP2040_PORT + Serial1.end(); + Serial1.setTX(16); + Serial1.setRX(17); + Serial1.begin(115200); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + Serial.begin(115200); + delay(3000); + SER.println(); + SER.println(); + SER.println("Starting NCM Ethernet port"); + + + //optional static config + // eth.config(my_static_ip_addr, my_static_gateway_and_dns_addr, IPAddress(255, 255, 255, 0), my_static_gateway_and_dns_addr); + + // Start the Ethernet port + // This starts DHCP in case config() was not called before + bool ok = eth.begin(); + delay(1000); + if (!ok) { + while (1) { + SER.println("Failed to initialize NCM Ethernet."); + delay(1000); + } + } else { + SER.println("NCM Ethernet started successfully."); + } + +} + +void loop() { + static unsigned long next_msg = 0; + static bool led_on = false; + if(millis() > next_msg) { + SER.println("."); + next_msg = millis() + 1000; + digitalWrite(LED_BUILTIN, led_on); + led_on ^=1; + } + + static bool connected = false; + if(!eth.connected()) { + connected = false; + return; + } else if(!connected){ + SER.println(""); + SER.println("Ethernet connected"); + SER.println("IP address: "); + SER.println(eth.localIP()); +#if LWIP_IPV6 + for(int i=0;iip6_addr[i]); + if(!address.isSet()) { + continue; + } + SER.println(address); + } +#endif + connected = true; + } + + static bool wait = false; + + SER.printf("connecting to %s:%i\n", host, port); + + // Use WiFiClient class to create TCP connections + WiFiClient client; + if (!client.connect(host, port)) { + SER.println("connection failed"); + delay(5000); + return; + } + + // This will send a string to the server + SER.println("sending data to server"); + if (client.connected()) { + client.println("hello from RP2040"); + } + + // wait for data to be available + unsigned long timeout = millis(); + while (client.available() == 0) { + if (millis() - timeout > 5000) { + SER.println(">>> Client Timeout !"); + client.stop(); + delay(60000); + return; + } + } + + // Read all the lines of the reply from server and print them to Serial + SER.println("receiving from remote server"); + // not testing 'client.connected()' since we do not need to send data here + while (client.available()) { + char ch = static_cast(client.read()); + SER.print(ch); + } + + // Close the connection + SER.println(); + SER.println("closing connection"); + client.stop(); + + if (wait) { + delay(300000); // execute once every 5 minutes, don't flood remote service + } + wait = true; +} diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/test/.gitkeep b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/test/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet/WiFiClient-NCMEthernet.ino b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet/WiFiClient-NCMEthernet.ino new file mode 100644 index 000000000..1a08ad9e5 --- /dev/null +++ b/libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet/WiFiClient-NCMEthernet.ino @@ -0,0 +1,135 @@ +/* + This sketch establishes a TCP connection to a "quote of the day" service. + It sends a "hello" message, and then prints received data. +*/ + +#include + +const char* host = "djxmmx.net"; +const uint16_t port = 17; + +NCMEthernetlwIP eth; +IPAddress my_static_ip_addr(192, 168, 137, 100); +IPAddress my_static_gateway_and_dns_addr(192, 168, 137, 1); + +#define USE_REAL_UART + +#if defined(USE_REAL_UART) +#define SER Serial1 +#else +#define SER Serial +#endif + +void setup() { + // enable Serial1 so it can be used by USE_REAL_UART or by DEBUG_RP2040_PORT + Serial1.end(); + Serial1.setTX(16); + Serial1.setRX(17); + Serial1.begin(115200); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + Serial.begin(115200); + delay(3000); + SER.println(); + SER.println(); + SER.println("Starting NCM Ethernet port"); + + + //optional static config + // eth.config(my_static_ip_addr, my_static_gateway_and_dns_addr, IPAddress(255, 255, 255, 0), my_static_gateway_and_dns_addr); + + // Start the Ethernet port + // This starts DHCP in case config() was not called before + bool ok = eth.begin(); + delay(1000); + if (!ok) { + while (1) { + SER.println("Failed to initialize NCM Ethernet."); + delay(1000); + } + } else { + SER.println("NCM Ethernet started successfully."); + } + +} + +void loop() { + static unsigned long next_msg = 0; + static bool led_on = false; + if(millis() > next_msg) { + SER.println("."); + next_msg = millis() + 1000; + digitalWrite(LED_BUILTIN, led_on); + led_on ^=1; + } + + static bool connected = false; + if(!eth.connected()) { + connected = false; + return; + } else if(!connected){ + SER.println(""); + SER.println("Ethernet connected"); + SER.println("IP address: "); + SER.println(eth.localIP()); +#if LWIP_IPV6 + for(int i=0;iip6_addr[i]); + if(!address.isSet()) { + continue; + } + SER.println(address); + } +#endif + connected = true; + } + + static bool wait = false; + + SER.printf("connecting to %s:%i\n", host, port); + + // Use WiFiClient class to create TCP connections + WiFiClient client; + if (!client.connect(host, port)) { + SER.println("connection failed"); + delay(5000); + return; + } + + // This will send a string to the server + SER.println("sending data to server"); + if (client.connected()) { + client.println("hello from RP2040"); + } + + // wait for data to be available + unsigned long timeout = millis(); + while (client.available() == 0) { + if (millis() - timeout > 5000) { + SER.println(">>> Client Timeout !"); + client.stop(); + delay(60000); + return; + } + } + + // Read all the lines of the reply from server and print them to Serial + SER.println("receiving from remote server"); + // not testing 'client.connected()' since we do not need to send data here + while (client.available()) { + char ch = static_cast(client.read()); + SER.print(ch); + } + + // Close the connection + SER.println(); + SER.println("closing connection"); + client.stop(); + + if (wait) { + delay(300000); // execute once every 5 minutes, don't flood remote service + } + wait = true; +} diff --git a/libraries/lwIP_USB_NCM/keywords.txt b/libraries/lwIP_USB_NCM/keywords.txt new file mode 100644 index 000000000..adf07ba4b --- /dev/null +++ b/libraries/lwIP_USB_NCM/keywords.txt @@ -0,0 +1,18 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Library (KEYWORD1) +####################################### + +USB_NCM_lwIP KEYWORD1 +NCMEthernet KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/lwIP_USB_NCM/library.properties b/libraries/lwIP_USB_NCM/library.properties new file mode 100644 index 000000000..d7415dda9 --- /dev/null +++ b/libraries/lwIP_USB_NCM/library.properties @@ -0,0 +1,10 @@ +name=lwIP_USB_NCM +version=1 +author=functionpointer +maintainer=functionpointer +sentence=Ethernet over USB driver based on tinyUSB implementation of CDC-NCM (Network Control Model) +paragraph=Ethernet over USB driver based on tinyUSB implementation of CDC-NCM (Network Control Model) +category=Communication +url=https://github.com/functionpointer/arduino-pico +architectures=rp2040 +dot_a_linkage=true diff --git a/libraries/lwIP_USB_NCM/src/NCMEthernetlwIP.cpp b/libraries/lwIP_USB_NCM/src/NCMEthernetlwIP.cpp new file mode 100644 index 000000000..5aea70153 --- /dev/null +++ b/libraries/lwIP_USB_NCM/src/NCMEthernetlwIP.cpp @@ -0,0 +1,18 @@ +#include "NCMEthernetlwIP.h" +#include +#include +#include +#include + +NCMEthernetlwIP::NCMEthernetlwIP() { +} + +bool NCMEthernetlwIP::begin(const uint8_t *macAddress, const uint16_t mtu) { + // super call + return LwipIntfDev::begin(macAddress, mtu); +} + +void NCMEthernetlwIP::packetReceivedIRQWorker(NCMEthernet *instance) { + NCMEthernetlwIP *d = static_cast(instance); + d->_irq(instance); +} diff --git a/libraries/lwIP_USB_NCM/src/NCMEthernetlwIP.h b/libraries/lwIP_USB_NCM/src/NCMEthernetlwIP.h new file mode 100644 index 000000000..b0e453517 --- /dev/null +++ b/libraries/lwIP_USB_NCM/src/NCMEthernetlwIP.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +class NCMEthernetlwIP: public LwipIntfDev { +public: + NCMEthernetlwIP(); + + bool begin(const uint8_t* macAddress = nullptr, const uint16_t mtu = DEFAULT_MTU); + void packetReceivedIRQWorker(NCMEthernet *instance) override; + +}; diff --git a/libraries/lwIP_USB_NCM/src/utility/NCMEthernet.cpp b/libraries/lwIP_USB_NCM/src/utility/NCMEthernet.cpp new file mode 100644 index 000000000..2106883cc --- /dev/null +++ b/libraries/lwIP_USB_NCM/src/utility/NCMEthernet.cpp @@ -0,0 +1,187 @@ +/* + Copyright (c) 2024 functionpointer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "NCMEthernet.h" +#include +#include +#include "USB.h" + +#define USBD_NCM_EPSIZE 64 + +NCMEthernet::NCMEthernet(int8_t cs, arduino::SPIClass &spi, int8_t intrpin) { + (void) cs; + (void) spi; + (void) intrpin; +} + +bool NCMEthernet::begin(const uint8_t* mac_address, netif *net) { + (void) net; + memcpy(tud_network_mac_address, mac_address, 6); + + if (!critical_section_is_initialized(&this->_recv_critical_section)) { + critical_section_init(&this->_recv_critical_section); + critical_section_enter_blocking(&this->_recv_critical_section); + this->_recv_pkg.size = 0; + this->_recv_pkg.src = nullptr; + critical_section_exit(&this->_recv_critical_section); + } + + async_context_threadsafe_background_config_t config = async_context_threadsafe_background_default_config(); + if (!async_context_threadsafe_background_init(&this->_async_context, &config)) { + return false; + } + + this->_recv_irq_worker.user_data = this; + this->_recv_irq_worker.do_work = &NCMEthernet::_recv_irq_work; + async_context_add_when_pending_worker(&this->_async_context.core, &this->_recv_irq_worker); + + _ncm_ethernet_instance = this; + + USB.disconnect(); + + _epIn = USB.registerEndpointIn(); + _epOut = USB.registerEndpointOut(); + _epNotif = USB.registerEndpointIn(); + _strID = USB.registerString("Pico NCM"); + + // mac address we give to the PC + // must be different than our own + uint8_t len = 0; + for (unsigned i=0; i<6; i++) { + uint8_t mac_byte = tud_network_mac_address[i]; + if(i==5) { // invert last byte + mac_byte^=0xFF; + } + macAddrStr[len++] = "0123456789ABCDEF"[(mac_byte >> 4) & 0xf]; + macAddrStr[len++] = "0123456789ABCDEF"[(mac_byte >> 0) & 0xf]; + } + _strMac = USB.registerString(macAddrStr); + + _id = USB.registerInterface(2, _usb_interface_cb, (void *)this, TUD_CDC_NCM_DESC_LEN, 3, 0); + + USB.connect(); + + return true; +} + +void NCMEthernet::end() { + USB.disconnect(); + USB.unregisterInterface(_id); + USB.unregisterEndpointIn(_epIn); + USB.unregisterEndpointIn(_epNotif); + USB.unregisterEndpointOut(_epOut); + USB.connect(); +} + +// Need to define here so we don't have to include tusb.h in global header (causes problemw w/BT redefining things) +void NCMEthernet::usbInterfaceCB(int itf, uint8_t *dst, int len) { + uint8_t desc[TUD_CDC_NCM_DESC_LEN] = { + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_NCM_DESCRIPTOR((uint8_t)itf, _strID, _strMac, _epNotif, USBD_NCM_EPSIZE, _epOut, _epIn, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU) + }; + memcpy(dst, desc, len); +} + +uint16_t NCMEthernet::readFrame(uint8_t* buffer, uint16_t bufsize) { + uint16_t data_len = this->readFrameSize(); + + if (data_len == 0) { + return 0; + } + + if (data_len > bufsize) { + // Packet is bigger than buffer - drop the packet + discardFrame(data_len); + return 0; + } + + return readFrameData(buffer, data_len); +} + +uint16_t NCMEthernet::readFrameSize() { + return this->_recv_pkg.size; +} + +uint16_t NCMEthernet::readFrameData(uint8_t* buffer, uint16_t framesize) { + critical_section_enter_blocking(&this->_recv_critical_section); + + size_t size = this->_recv_pkg.size; + memcpy(buffer, (const void*)this->_recv_pkg.src, size); + this->_recv_pkg.size = 0; + + critical_section_exit(&this->_recv_critical_section); + tud_network_recv_renew(); + return size; +} + +uint16_t NCMEthernet::sendFrame(const uint8_t* buf, uint16_t len) { + // this is basically linkoutput_fn + + for (;;) { + /* if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do */ + if (!tud_ready()) + return 0; + + /* if the network driver can accept another packet, we make it happen */ + if (tud_network_can_xmit(len)) { + tud_network_xmit((void*)const_cast(buf), len); + return len; + } + + /* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */ + tud_task(); + } +} + +extern "C" { + // data transfer between tinyUSB callbacks and NCMEthernet class + NCMEthernet *_ncm_ethernet_instance = nullptr; + + /* + * Interface to tinyUSB. + */ + uint8_t tud_network_mac_address[6] = {0}; + + void tud_network_init_cb(void) { + } + + bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { + if(_ncm_ethernet_instance == nullptr || _ncm_ethernet_instance->_recv_pkg.size > 0) { + return false; + } + + critical_section_enter_blocking(&_ncm_ethernet_instance->_recv_critical_section); + _ncm_ethernet_instance->_recv_pkg.src = src; + _ncm_ethernet_instance->_recv_pkg.size = size; + critical_section_exit(&_ncm_ethernet_instance->_recv_critical_section); + + async_context_set_work_pending(&_ncm_ethernet_instance->_async_context.core, &_ncm_ethernet_instance->_recv_irq_worker); + + return true; + } + + uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { + // this is called by tud_network_xmit, which is called by NCMEthernet::sendFrame, + // which is called by LwipIntfDev::linkoutput_s + // linkoutput_s gives us pbuf->payload and pbuf->len + + memcpy(dst, ref, arg); + return arg; + } +} \ No newline at end of file diff --git a/libraries/lwIP_USB_NCM/src/utility/NCMEthernet.h b/libraries/lwIP_USB_NCM/src/utility/NCMEthernet.h new file mode 100644 index 000000000..946afb79d --- /dev/null +++ b/libraries/lwIP_USB_NCM/src/utility/NCMEthernet.h @@ -0,0 +1,116 @@ +/* + Copyright (c) 2024 functionpointer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef NCM_ETHERNET_H +#define NCM_ETHERNET_H + +#include +#include +#include "pico/util/queue.h" +#include +#include +#include +#include + +extern "C" { + typedef struct _ncmethernet_packet_t { + const uint8_t *src; + uint16_t size; + } ncmethernet_packet_t; +} + +/** +* incoming packet flow: +* tinyUSB calls tud_network_recv_cb +* that stores the packet in _ncmethernet_pkg and sets _ncm_ethernet_recv_irq_worker pending +* _ncm_ethernet_recv_irq_worker, in different execution context, calls _recv_irq_work +* _recv_irq_work uses _ncm_ethernet_instance to call packetReceivedIRQWorker +* in NCMEthernetlwIP packetReceivedIRQWorker is overridden to call LwipIntfDev::_irq() +* LwipIntfDev::_irq() calls readFrameSize() and readFrameData() and _netif.input +* +* outgoing packet flow: +* LwipIntfDev calls sendFrame() +*/ + +class NCMEthernet { +public: + // constructor and methods as required by LwipIntfDev + + NCMEthernet(int8_t cs, arduino::SPIClass &spi, int8_t intrpin); + + bool begin(const uint8_t *address, netif *netif); + void end(); + + uint16_t sendFrame(const uint8_t *data, uint16_t datalen); + + uint16_t readFrameSize(); + + uint16_t readFrameData(uint8_t *buffer, uint16_t bufsize); + + uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); + + void discardFrame(uint16_t ign) { + (void) ign; + } + + bool interruptIsPossible() { + return false; + } + + PinStatus interruptMode() { + return HIGH; + } + + constexpr bool needsSPI() const { + return false; + } + + void usbInterfaceCB(int itf, uint8_t *dst, int len); + + virtual void packetReceivedIRQWorker(NCMEthernet *instance) {}; + + async_context_threadsafe_background_t _async_context; + async_when_pending_worker_t _recv_irq_worker; + + critical_section_t _recv_critical_section; + ncmethernet_packet_t _recv_pkg; +protected: + netif *_netif; + uint8_t _id; + uint8_t _epIn; + uint8_t _epNotif; + uint8_t _epOut; + uint8_t _strID; + uint8_t _strMac; + char macAddrStr[6*2+2] = {0}; + + static void _usb_interface_cb(int itf, uint8_t *dst, int len, void *param) { + ((NCMEthernet *)param)->usbInterfaceCB(itf, dst, len); + } + static void _recv_irq_work(async_context_t *context, async_when_pending_worker_t *worker) { + NCMEthernet *d = static_cast(worker->user_data); + d->packetReceivedIRQWorker(d); + } +}; + +extern "C" { + extern NCMEthernet *_ncm_ethernet_instance; +} + +#endif // NCM_ETHERNET_H