mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-19 14:26:01 -04:00
Examples/network: Add Ethernet iface to sta-2-wired bridge
* adds description about that it's not a bridge, but more like an 1:1 forwarder on L2 * add and describe mac spoofing for Ethernet interface * describe virtual networking for USB-NCM interface
This commit is contained in:
parent
08c5e6e07c
commit
6ac17b5020
@ -11,3 +11,6 @@ examples/network/simple_sniffer:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3", "esp32s3"]
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
examples/network/sta_to_eth:
|
||||
disable:
|
||||
- if: SOC_WIFI_SUPPORTED != 1
|
||||
|
@ -3,7 +3,8 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# This example needs a DNS server: let's use the simple DNS server implementation from captive portal example
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/http_server/captive_portal/components/dns_server)
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/http_server/captive_portal/components/dns_server
|
||||
$ENV{IDF_PATH}/examples/ethernet/basic/components/ethernet_init)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(tusb_ncm)
|
||||
project(wifi_to_wired)
|
@ -1,30 +1,26 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# TinyUSB Network Control Model Device Example
|
||||
# WiFi station to "Wired" interface L2 forwarder
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
Network Control Model (NCM) is a sub-class of Communication Device Class (CDC) USB Device for Ethernet-over-USB applications.
|
||||
This example aims to demonstrate 1-1 bridge using WiFi station and one of these interfaces (so called *wired* in this example)
|
||||
- Ethernet (supported for all targets)
|
||||
- USB acting as NCM device (supported for ESP32-S2 and ESP32-S3)
|
||||
|
||||
In this example, we implemented the ESP development board to transmit WiFi data to the Linux host via USB, so that the Linux host could access the Internet.
|
||||
|
||||
As a USB stack, a TinyUSB component is used.
|
||||
It also allows for reconfiguring WiFi settings using a virtual network in the Ethernet. The reconfiguration mode is initialized if the WiFi settings are not available, connection fails or manually by long pressing the Boot button (GPIO0).
|
||||
It is possible to configure WiFi settings (SSID and password) in a browser on an address `"wifi.settings"` or using unified provisioning.
|
||||
|
||||
## How to use example
|
||||
|
||||
This example demonstrate usage of USB NCM device as USB-WiFi bridge. It also allows for reconfiguring WiFi settings using a virtual network in NCM device. The reconfiguration mode is initialized if the WiFi settings are not available, connection fails or manually by long pressing the Boot button (GPIO0).
|
||||
It is possible to configure WiFi settings (SSID and password) in a browser on an address `"wifi.settings"` or using unified provisioning.
|
||||
This example could be used to *bring* wireless connectivity to devices that support only Ethernet (or USB Ethernet implemented as NCM device).
|
||||
This example also supports runtime configuration of WiFi settings by means of a webpage or unified provisioning.
|
||||
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Any ESP board that have USB-OTG supported.
|
||||
|
||||
#### Pin Assignment
|
||||
|
||||
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below.
|
||||
|
||||
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
|
||||
Any board with either Ethernet of USB-OTG supported.
|
||||
|
||||
### Configure the project
|
||||
|
||||
@ -36,7 +32,7 @@ In the `Example Configuration` menu choose the provisioning method:
|
||||
|
||||
To provision the device using IDF provisioning tools (if `EXAMPLE_WIFI_CONFIGURATION_PROVISIONING` is selected) you can use idf provisioning utility with transport set to `softap`:
|
||||
```bash
|
||||
esp-idf/tools/esp_prov$ python esp_prov.py --transport softap ...
|
||||
esp-idf/tools/esp_prov$ python esp_prov.py --transport httpd ...
|
||||
```
|
||||
Please refer to the provisioning documentation and `esp_prov` script [documentation](../../../../../tools/esp_prov/README.md) for more details.
|
||||
|
||||
@ -58,15 +54,15 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
|
||||
|
||||
After the flashing you should see the output at idf monitor:
|
||||
|
||||
(note that this is the output of USB configuration)
|
||||
```
|
||||
I (725) usb_net: Wi-Fi STA connected
|
||||
I (735) usb_net: CONNECTED_BIT
|
||||
|
||||
I (735) usb_net: connect success
|
||||
|
||||
I (735) wifi:BcnInt:102400, DTIM:1
|
||||
I (745) usb_net: USB net initialization
|
||||
I (745) tusb_desc:
|
||||
I (1740) example_sta2wired: Wi-Fi STA connected
|
||||
I (1740) example_sta2wired: WiFi station connected successfully
|
||||
W (1750) TinyUSB: The device's configuration descriptor is not provided by user, using default.
|
||||
W (1760) TinyUSB: The device's string descriptor is not provided by user, using default.
|
||||
W (1770) TinyUSB: The device's device descriptor is not provided by user, using default.
|
||||
I (1770) wifi:AP's beacon interval = 102400 us, DTIM period = 1
|
||||
I (1780) tusb_desc:
|
||||
┌─────────────────────────────────┐
|
||||
│ USB Device Descriptor Summary │
|
||||
├───────────────────┬─────────────┤
|
||||
@ -93,5 +89,4 @@ I (745) tusb_desc:
|
||||
│bNumConfigurations │ 0x1 │
|
||||
└───────────────────┴─────────────┘
|
||||
I (915) TinyUSB: TinyUSB Driver installed
|
||||
I (925) usb_net: USB NCM initialization DONE
|
||||
```
|
@ -4,6 +4,13 @@ else()
|
||||
set(config_method provisioning.c scheme_generic_httpd.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS tusb_ncm_main.c
|
||||
if(CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET)
|
||||
set(wired_iface ethernet_iface.c)
|
||||
else()
|
||||
set(wired_iface usb_ncm_iface.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS sta2wired_main.c
|
||||
${wired_iface}
|
||||
${config_method}
|
||||
INCLUDE_DIRS "")
|
42
examples/network/sta_to_eth/main/Kconfig.projbuild
Normal file
42
examples/network/sta_to_eth/main/Kconfig.projbuild
Normal file
@ -0,0 +1,42 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_WIFI_CONFIGURATION
|
||||
prompt "WiFi configuration"
|
||||
default EXAMPLE_WIFI_CONFIGURATION_MANUAL
|
||||
help
|
||||
Choose how the WiFi settings should be configured.
|
||||
|
||||
config EXAMPLE_WIFI_CONFIGURATION_MANUAL
|
||||
bool
|
||||
prompt "Manual configuration via http server"
|
||||
config EXAMPLE_WIFI_CONFIGURATION_PROVISIONING
|
||||
bool
|
||||
prompt "Using unified provisioning"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_WIRED_INTERFACE
|
||||
prompt "Choose the Wired interface"
|
||||
default EXAMPLE_WIRED_INTERFACE_IS_ETHERNET
|
||||
help
|
||||
Choose how the WiFi settings should be configured.
|
||||
|
||||
config EXAMPLE_WIRED_INTERFACE_IS_ETHERNET
|
||||
bool
|
||||
prompt "Ethernet"
|
||||
config EXAMPLE_WIRED_INTERFACE_IS_USB
|
||||
bool
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
prompt "USB NCM"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_RECONFIGURE_BUTTON
|
||||
int "Button for switching to reconfigure mode"
|
||||
range 0 46
|
||||
default 2 if EXAMPLE_WIRED_INTERFACE_IS_ETHERNET
|
||||
default 0
|
||||
help
|
||||
The button on this GPIO is used to reset the board to
|
||||
the reconfiguration mode, i.e. to restart provisioning
|
||||
or manual configuration of Wi-Fi settings (ssid, password)
|
||||
|
||||
endmenu
|
318
examples/network/sta_to_eth/main/ethernet_iface.c
Normal file
318
examples/network/sta_to_eth/main/ethernet_iface.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "wired_iface.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
#include "esp_mac.h"
|
||||
#include "ethernet_init.h"
|
||||
|
||||
/**
|
||||
* Disable promiscuous mode on Ethernet interface by setting this macro to 0
|
||||
* if disabled, we'd have to rewrite MAC addressed in frames with the actual Eth interface MAC address
|
||||
* - this results in better throughput
|
||||
* - might cause ARP conflicts if the PC is also connected to the same AP with another NIC
|
||||
*/
|
||||
#define ETH_BRIDGE_PROMISCUOUS 0
|
||||
|
||||
static const char *TAG = "example_wired_ethernet";
|
||||
static esp_netif_t *s_netif = NULL;
|
||||
static esp_eth_handle_t s_eth_handle = NULL;
|
||||
static bool s_ethernet_is_connected = false;
|
||||
static uint8_t s_eth_mac[6];
|
||||
static wired_rx_cb_t s_rx_cb = NULL;
|
||||
static wired_free_cb_t s_free_cb = NULL;
|
||||
|
||||
/**
|
||||
* @brief Event handler for Ethernet events
|
||||
*/
|
||||
void eth_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
uint8_t mac_addr[6] = {0};
|
||||
/* we can get the ethernet driver handle from event data */
|
||||
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
|
||||
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
s_ethernet_is_connected = true;
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Down");
|
||||
s_ethernet_is_connected = false;
|
||||
break;
|
||||
case ETHERNET_EVENT_START:
|
||||
ESP_LOGI(TAG, "Ethernet Started");
|
||||
break;
|
||||
case ETHERNET_EVENT_STOP:
|
||||
ESP_LOGI(TAG, "Ethernet Stopped");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Default Event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In this scenario of WiFi station to Ethernet bridge mode, we have this configuration
|
||||
*
|
||||
* (ISP) router ESP32 PC
|
||||
* [ AP ] <-> [ sta -- eth ] <-> [ eth-NIC ]
|
||||
*
|
||||
* From the PC's NIC perspective the L2 forwarding should be transparent and resemble this configuration:
|
||||
*
|
||||
* (ISP) router PC
|
||||
* [ AP ] <----------> [ virtual wifi-NIC ]
|
||||
*
|
||||
* In order for the ESP32 to act as L2 bridge it needs to accept all frames on the interface
|
||||
* - For Ethernet we just enable `PROMISCUOUS` mode
|
||||
* - For Wifi we could also enable the promiscuous mode, but in that case we'd receive encoded frames
|
||||
* from 802.11 and we'd have to decode it and process (using wpa-supplicant).
|
||||
* The easier option (in this scenario of only one client -- eth-NIC) we could simply "pretend"
|
||||
* that we have the HW mac address of eth-NIC and receive only ethernet frames for "us" from esp_wifi API
|
||||
* (we could use the same technique for Ethernet and yield better throughput, see ETH_BRIDGE_PROMISCUOUS flag)
|
||||
*
|
||||
* This API updates Ethernet frames to swap mac addresses of ESP32 interfaces with those of eth-NIC and AP.
|
||||
* For that we'd have to parse initial DHCP packets (manually) to record the HW addresses of the AP and eth-NIC
|
||||
* (note, that it is possible to simply spoof the MAC addresses, but that's not recommended technique)
|
||||
*/
|
||||
#define IP_V4 0x40
|
||||
#define IP_PROTO_UDP 0x11
|
||||
#define DHCP_PORT_IN 0x43
|
||||
#define DHCP_PORT_OUT 0x44
|
||||
#define DHCP_MACIG_COOKIE_OFFSET (8 + 236)
|
||||
#define MIN_DHCP_PACKET_SIZE (285)
|
||||
#define IP_HEADER_SIZE (20)
|
||||
#define DHCP_DISCOVER 1
|
||||
#define DHCP_OFFER 2
|
||||
#define DHCP_COOKIE_WITH_PKT_TYPE(type) {0x63, 0x82, 0x53, 0x63, 0x35, 1, type};
|
||||
|
||||
void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, uint8_t own_mac[6])
|
||||
{
|
||||
if (!s_ethernet_is_connected) {
|
||||
return;
|
||||
}
|
||||
static uint8_t eth_nic_mac[6] = {};
|
||||
static bool eth_nic_mac_found = false;
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
static uint8_t ap_mac[6] = {};
|
||||
static bool ap_mac_found = false;
|
||||
#endif
|
||||
uint8_t *dest_mac = buffer;
|
||||
uint8_t *src_mac = buffer + 6;
|
||||
uint8_t *eth_type = buffer + 12;
|
||||
if (eth_type[0] == 0x08) { // support only IPv4
|
||||
// try to find NIC HW address (look for DHCP discovery packet)
|
||||
if (!eth_nic_mac_found && direction == FROM_WIRED && eth_type[1] == 0x00) { // ETH IP4
|
||||
uint8_t *ip_header = eth_type + 2;
|
||||
if (len > MIN_DHCP_PACKET_SIZE && (ip_header[0] & 0xF0) == IP_V4 && ip_header[9] == IP_PROTO_UDP) {
|
||||
uint8_t *udp_header = ip_header + IP_HEADER_SIZE;
|
||||
const uint8_t dhcp_ports[] = {0, DHCP_PORT_OUT, 0, DHCP_PORT_IN};
|
||||
if (memcmp(udp_header, dhcp_ports, sizeof(dhcp_ports)) == 0) {
|
||||
uint8_t *dhcp_magic = udp_header + DHCP_MACIG_COOKIE_OFFSET;
|
||||
const uint8_t dhcp_type[] = DHCP_COOKIE_WITH_PKT_TYPE(DHCP_DISCOVER);
|
||||
if (memcmp(dhcp_magic, dhcp_type, sizeof(dhcp_type)) == 0) {
|
||||
eth_nic_mac_found = true;
|
||||
memcpy(eth_nic_mac, src_mac, 6);
|
||||
}
|
||||
} // DHCP
|
||||
} // UDP/IP
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
// try to find AP HW address (look for DHCP offer packet)
|
||||
} else if (!ap_mac_found && direction == TO_WIRED && eth_type[1] == 0x00) { // ETH IP4
|
||||
uint8_t *ip_header = eth_type + 2;
|
||||
if (len > MIN_DHCP_PACKET_SIZE && (ip_header[0] & 0xF0) == IP_V4 && ip_header[9] == IP_PROTO_UDP) {
|
||||
uint8_t *udp_header = ip_header + IP_HEADER_SIZE;
|
||||
const uint8_t dhcp_ports[] = {0, DHCP_PORT_IN, 0, DHCP_PORT_OUT};
|
||||
if (memcmp(udp_header, dhcp_ports, sizeof(dhcp_ports)) == 0) {
|
||||
uint8_t *dhcp_magic = udp_header + DHCP_MACIG_COOKIE_OFFSET;
|
||||
const uint8_t dhcp_type[] = DHCP_COOKIE_WITH_PKT_TYPE(DHCP_OFFER);
|
||||
if (memcmp(dhcp_magic, dhcp_type, sizeof(dhcp_type)) == 0) {
|
||||
ap_mac_found = true;
|
||||
memcpy(ap_mac, src_mac, 6);
|
||||
}
|
||||
} // DHCP
|
||||
} // UDP/IP
|
||||
#endif // !ETH_BRIDGE_PROMISCUOUS
|
||||
}
|
||||
|
||||
// swap addresses in ARP probes
|
||||
if (eth_type[1] == 0x06) { // ARP
|
||||
uint8_t *arp = eth_type + 2 + 8; // points to sender's HW address
|
||||
if (eth_nic_mac_found && direction == FROM_WIRED && memcmp(arp, eth_nic_mac, 6) == 0) {
|
||||
/* updates senders HW address to our wireless */
|
||||
memcpy(arp, own_mac, 6);
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
} else if (ap_mac_found && direction == TO_WIRED && memcmp(arp, ap_mac, 6) == 0) {
|
||||
/* updates senders HW address to our wired */
|
||||
memcpy(arp, s_eth_mac, 6);
|
||||
#endif // !ETH_BRIDGE_PROMISCUOUS
|
||||
}
|
||||
}
|
||||
// swap HW addresses in ETH frames
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
if (ap_mac_found && direction == FROM_WIRED && memcmp(dest_mac, s_eth_mac, 6) == 0) {
|
||||
memcpy(dest_mac, ap_mac, 6);
|
||||
}
|
||||
if (ap_mac_found && direction == TO_WIRED && memcmp(src_mac, ap_mac, 6) == 0) {
|
||||
memcpy(src_mac, s_eth_mac, 6);
|
||||
}
|
||||
#endif // !ETH_BRIDGE_PROMISCUOUS
|
||||
if (eth_nic_mac_found && direction == FROM_WIRED && memcmp(src_mac, eth_nic_mac, 6) == 0) {
|
||||
memcpy(src_mac, own_mac, 6);
|
||||
}
|
||||
if (eth_nic_mac_found && direction == TO_WIRED && memcmp(dest_mac, own_mac, 6) == 0) {
|
||||
memcpy(dest_mac, eth_nic_mac, 6);
|
||||
}
|
||||
} // IP4 section of eth-type (0x08) both ETH-IP4 and ETHARP
|
||||
}
|
||||
|
||||
static esp_err_t wired_recv(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t len, void *priv)
|
||||
{
|
||||
esp_err_t ret = s_rx_cb(buffer,len, buffer);
|
||||
free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb)
|
||||
{
|
||||
uint8_t eth_port_cnt = 0;
|
||||
esp_eth_handle_t *eth_handles;
|
||||
ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt));
|
||||
|
||||
// Check or multiple ethernet interface
|
||||
if (1 < eth_port_cnt) {
|
||||
ESP_LOGW(TAG, "Multiple Ethernet Interface detected: Only the first initialized interface is going to be used.");
|
||||
}
|
||||
s_eth_handle = eth_handles[0];
|
||||
free(eth_handles);
|
||||
ESP_ERROR_CHECK(esp_eth_update_input_path(s_eth_handle, wired_recv, NULL));
|
||||
#if ETH_BRIDGE_PROMISCUOUS
|
||||
bool eth_promiscuous = true;
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, ð_promiscuous));
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, &s_eth_mac));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
|
||||
s_rx_cb = rx_cb;
|
||||
s_free_cb = free_cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t wired_send(void *buffer, uint16_t len, void *buff_free_arg)
|
||||
{
|
||||
if (s_ethernet_is_connected) {
|
||||
if (esp_eth_transmit(s_eth_handle, buffer, len) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Ethernet send packet failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (s_free_cb) {
|
||||
s_free_cb(buff_free_arg, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* In this scenario of configuring WiFi, we setup Ethernet to create a network and run DHCP server,
|
||||
* so it could assign an IP address to the PC
|
||||
*
|
||||
* ESP32 PC
|
||||
* | <DHCP server> eth | <-> [ eth-NIC ]
|
||||
* | <HTTP server> |
|
||||
* | (wifi-provisioning) |
|
||||
*
|
||||
* From the PC's NIC perspective the board acts as a separate network with it's own IP and MAC address
|
||||
* (this network's MAC address is the native ESP32's Ethernet interface MAC)
|
||||
*/
|
||||
static void l2_free(void *h, void* buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t netif_transmit (void *h, void *buffer, size_t len)
|
||||
{
|
||||
if (wired_send(buffer, len, buffer) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send buffer to USB!");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t netif_recv_callback(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t len, void *priv)
|
||||
{
|
||||
if (s_netif) {
|
||||
void *buf_copy = malloc(len);
|
||||
if (!buf_copy) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(buf_copy, buffer, len);
|
||||
return esp_netif_receive(s_netif, buf_copy, len, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t wired_netif_init(void)
|
||||
{
|
||||
uint8_t eth_port_cnt = 0;
|
||||
esp_eth_handle_t *eth_handles;
|
||||
ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt));
|
||||
|
||||
// Check or multiple ethernet interface
|
||||
if (1 < eth_port_cnt) {
|
||||
ESP_LOGW(TAG, "Multiple Ethernet Interface detected: Only the first initialized interface is going to be used.");
|
||||
}
|
||||
s_eth_handle = eth_handles[0];
|
||||
free(eth_handles);
|
||||
ESP_ERROR_CHECK(esp_eth_update_input_path(s_eth_handle, netif_recv_callback, NULL));
|
||||
|
||||
// 1) Derive the base config from the default AP (using DHCP server)
|
||||
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP();
|
||||
base_cfg.if_key = "wired";
|
||||
base_cfg.if_desc = "ethernet config device";
|
||||
// 2) Use static config for driver's config pointing only to static transmit and free functions
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = (void*)1, // will be replaced by the driver pointer only tinyusb_net supports ti
|
||||
.transmit = netif_transmit,
|
||||
.driver_free_rx_buffer = l2_free
|
||||
};
|
||||
|
||||
// Config the esp-netif with:
|
||||
// 1) inherent config (behavioural settings of an interface)
|
||||
// 2) driver's config (connection to IO functions -- usb)
|
||||
// 3) stack config (using lwip IO functions -- derive from eth)
|
||||
esp_netif_config_t cfg = {
|
||||
.base = &base_cfg,
|
||||
.driver = &driver_cfg,
|
||||
// 3) use ethernet style of lwip netif settings
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
|
||||
};
|
||||
|
||||
s_netif = esp_netif_new(&cfg);
|
||||
if (s_netif == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t mac[6];
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, &mac));
|
||||
esp_netif_set_mac(s_netif, mac);
|
||||
|
||||
// set the minimum lease time
|
||||
uint32_t lease_opt = 1;
|
||||
esp_netif_dhcps_option(s_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_opt, sizeof(lease_opt));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
|
||||
|
||||
// start the interface manually (as the driver has been started already)
|
||||
esp_netif_action_start(s_netif, 0, 0, 0);
|
||||
return ESP_OK;
|
||||
}
|
9
examples/network/sta_to_eth/main/idf_component.yml
Normal file
9
examples/network/sta_to_eth/main/idf_component.yml
Normal file
@ -0,0 +1,9 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "^1.3.0"
|
||||
rules:
|
||||
- if: "idf_version >=4.4"
|
||||
- if: "target in [esp32s2, esp32s3]"
|
||||
|
||||
idf: "^5.0"
|
@ -58,8 +58,15 @@ static esp_err_t http_get_handler(httpd_req_t *req)
|
||||
|
||||
if (strlen((char*)wifi_cfg.sta.ssid) > 0 && strlen((char*)wifi_cfg.sta.password)) {
|
||||
const char wifi_configured[] = "<h1>Connecting...</h1>";
|
||||
ESP_LOGI(TAG, "WiFi settings accepted!");
|
||||
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) == ESP_OK &&
|
||||
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "WiFi settings accepted!");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to set WiFi config to flash");
|
||||
}
|
||||
//
|
||||
// esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
|
||||
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_send(req, wifi_configured, strlen(wifi_configured));
|
||||
@ -104,7 +111,7 @@ esp_err_t start_provisioning(EventGroupHandle_t *flags, int success_bit, int fai
|
||||
{
|
||||
start_webserver();
|
||||
// Start the DNS server that will reply to "wifi.settings" with "usb" network interface address
|
||||
dns_server_config_t config = DNS_SERVER_CONFIG_SINGLE("wifi.settings" /* name */, "usb" /* USB netif ID */);
|
||||
dns_server_config_t config = DNS_SERVER_CONFIG_SINGLE("wifi.settings" /* name */, "wired" /* USB netif ID */);
|
||||
start_dns_server(&config);
|
||||
|
||||
s_flags = flags;
|
@ -66,7 +66,7 @@ extern const wifi_prov_scheme_t wifi_prov_scheme_httpd;
|
||||
esp_err_t start_provisioning(EventGroupHandle_t *flags, int success_bit, int fail_bit)
|
||||
{
|
||||
// Start the DNS server that will reply to "wifi.settings" with "usb" network interface address
|
||||
dns_server_config_t dns_config = DNS_SERVER_CONFIG_SINGLE("wifi.settings" /* name */, "usb" /* USB netif ID */);
|
||||
dns_server_config_t dns_config = DNS_SERVER_CONFIG_SINGLE("wifi.settings" /* name */, "wired" /* wired netif ID */);
|
||||
start_dns_server(&dns_config);
|
||||
struct events *handler_args = malloc(sizeof(struct events));
|
||||
handler_args->flags = flags;
|
@ -4,11 +4,7 @@
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* DESCRIPTION:
|
||||
* This example contains code to make ESP32-S2/S3 as a USB network Device.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@ -20,18 +16,15 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_private/wifi.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
#include "esp_mac.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "tinyusb.h"
|
||||
#include "tinyusb_net.h"
|
||||
#include "provisioning.h"
|
||||
#include "wired_iface.h"
|
||||
|
||||
static const char *TAG = "USB_NCM";
|
||||
static const char *TAG = "example_sta2wired";
|
||||
|
||||
static EventGroupHandle_t s_event_flags;
|
||||
static bool s_wifi_is_connected = false;
|
||||
static uint8_t s_sta_mac[6];
|
||||
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
const int DISCONNECTED_BIT = BIT1;
|
||||
@ -39,15 +32,13 @@ const int RECONFIGURE_BIT = BIT2;
|
||||
const int PROV_SUCCESS_BIT = BIT3;
|
||||
const int PROV_FAIL_BIT = BIT4;
|
||||
|
||||
static esp_netif_t *s_netif = NULL;
|
||||
|
||||
/**
|
||||
* WiFi -- USB bridge functionality
|
||||
* WiFi -- Wired packet path
|
||||
*/
|
||||
|
||||
static esp_err_t usb_recv_callback(void *buffer, uint16_t len, void* ctx)
|
||||
static esp_err_t wired_recv_callback(void *buffer, uint16_t len, void* ctx)
|
||||
{
|
||||
if (s_wifi_is_connected) {
|
||||
mac_spoof(FROM_WIRED, buffer, len, s_sta_mac);
|
||||
if (esp_wifi_internal_tx(ESP_IF_WIFI_STA, buffer, len) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to send packet to WiFi!");
|
||||
}
|
||||
@ -60,9 +51,10 @@ static void wifi_buff_free(void* buffer, void* ctx)
|
||||
esp_wifi_internal_free_rx_buffer(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb)
|
||||
static esp_err_t wifi_recv_callback(void *buffer, uint16_t len, void *eb)
|
||||
{
|
||||
if (tinyusb_net_send_sync(buffer, len, eb, pdMS_TO_TICKS(100)) != ESP_OK) {
|
||||
mac_spoof(TO_WIRED, buffer, len, s_sta_mac);
|
||||
if (wired_send(buffer, len, eb) != ESP_OK) {
|
||||
esp_wifi_internal_free_rx_buffer(eb);
|
||||
ESP_LOGD(TAG, "Failed to send packet to USB!");
|
||||
}
|
||||
@ -77,11 +69,12 @@ static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
s_wifi_is_connected = false;
|
||||
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL);
|
||||
esp_wifi_connect();
|
||||
|
||||
xEventGroupClearBits(s_event_flags, CONNECTED_BIT);
|
||||
xEventGroupSetBits(s_event_flags, DISCONNECTED_BIT);
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
|
||||
ESP_LOGI(TAG, "Wi-Fi STA connected");
|
||||
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, pkt_wifi2usb);
|
||||
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, wifi_recv_callback);
|
||||
s_wifi_is_connected = true;
|
||||
xEventGroupClearBits(s_event_flags, DISCONNECTED_BIT);
|
||||
xEventGroupSetBits(s_event_flags, CONNECTED_BIT);
|
||||
@ -109,137 +102,16 @@ static esp_err_t connect_wifi(void)
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
static void on_usb_net_init(void *ctx)
|
||||
{
|
||||
ESP_LOGE(TAG, "USB NET device has been initialized!");
|
||||
}
|
||||
|
||||
static esp_err_t usb_ncm_wifi_bridge(void)
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
tinyusb_net_config_t net_config = {
|
||||
.on_recv_callback = usb_recv_callback,
|
||||
.free_tx_buffer = wifi_buff_free,
|
||||
.on_init_callback = on_usb_net_init
|
||||
};
|
||||
|
||||
esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA);
|
||||
|
||||
esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB net init but not connect wifi");
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* USB internal network functionality
|
||||
*/
|
||||
|
||||
esp_err_t netif_recv_callback(void *buffer, uint16_t len, void* ctx)
|
||||
{
|
||||
if (s_netif) {
|
||||
void *buf_copy = malloc(len);
|
||||
if (!buf_copy) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(buf_copy, buffer, len);
|
||||
return esp_netif_receive(s_netif, buf_copy, len, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t netif_transmit (void *h, void *buffer, size_t len)
|
||||
{
|
||||
if (tinyusb_net_send_sync(buffer, len, NULL, pdMS_TO_TICKS(100)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send buffer to USB!");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void l2_free(void *h, void* buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t usb_ncm_with_network(void)
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
const tinyusb_net_config_t net_config = {
|
||||
// locally administrated address for the ncm device as it's going to be used internally
|
||||
// for configuration only
|
||||
.mac_addr = {0x02, 0x02, 0x11, 0x22, 0x33, 0x01},
|
||||
.on_recv_callback = netif_recv_callback,
|
||||
};
|
||||
|
||||
esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Cannot initialize USB Net device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// with OUI range MAC to create a virtual netif running http server
|
||||
// this needs to be different to usb_interface_mac (==client)
|
||||
uint8_t lwip_addr[6]= {0x02, 0x02, 0x11, 0x22, 0x33, 0x02};
|
||||
|
||||
|
||||
// 1) Derive the base config from the default AP (using DHCP server)
|
||||
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP();
|
||||
base_cfg.if_key = "usb";
|
||||
base_cfg.if_desc = "usb ncm config device";
|
||||
// 2) Use static config for driver's config pointing only to static transmit and free functions
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = (void*)1, // will be replaced by the driver pointer only tinyusb_net supports ti
|
||||
.transmit = netif_transmit,
|
||||
.driver_free_rx_buffer = l2_free
|
||||
};
|
||||
|
||||
// Config the esp-netif with:
|
||||
// 1) inherent config (behavioural settings of an interface)
|
||||
// 2) driver's config (connection to IO functions -- usb)
|
||||
// 3) stack config (using lwip IO functions -- derive from eth)
|
||||
esp_netif_config_t cfg = {
|
||||
.base = &base_cfg,
|
||||
.driver = &driver_cfg,
|
||||
// 3) use ethernet style of lwip netif settings
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
|
||||
};
|
||||
|
||||
s_netif = esp_netif_new(&cfg);
|
||||
if (s_netif == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_netif_set_mac(s_netif, lwip_addr);
|
||||
|
||||
// set the minimum lease time
|
||||
uint32_t lease_opt = 1;
|
||||
esp_netif_dhcps_option(s_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_opt, sizeof(lease_opt));
|
||||
|
||||
// start the interface manually (as the driver has been started already)
|
||||
esp_netif_action_start(s_netif, 0, 0, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* GPIO button functionality
|
||||
*/
|
||||
|
||||
#define GPIO_INPUT_IO_0 0
|
||||
#define GPIO_INPUT CONFIG_EXAMPLE_RECONFIGURE_BUTTON
|
||||
#define GPIO_LONG_PUSH_US 2000000 /* push for 2 seconds to reconfigure */
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void* arg)
|
||||
{
|
||||
static int64_t last_pushed = -1;
|
||||
if (gpio_get_level(GPIO_INPUT_IO_0) == 0) {
|
||||
if (gpio_get_level(GPIO_INPUT) == 0) {
|
||||
last_pushed = esp_timer_get_time();
|
||||
} else {
|
||||
uint64_t now = esp_timer_get_time();
|
||||
@ -257,19 +129,19 @@ static void IRAM_ATTR gpio_isr_handler(void* arg)
|
||||
static void gpio_init(void)
|
||||
{
|
||||
gpio_config_t io_conf = { .intr_type = GPIO_INTR_ANYEDGE,
|
||||
.pin_bit_mask = (1ULL<<GPIO_INPUT_IO_0),
|
||||
.pin_bit_mask = (1ULL<<GPIO_INPUT),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1 };
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(0);
|
||||
//hook isr handler for specific gpio pin
|
||||
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, NULL);
|
||||
|
||||
gpio_isr_handler_add(GPIO_INPUT, gpio_isr_handler, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Application
|
||||
*/
|
||||
#include "esp_mac.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
@ -279,7 +151,7 @@ void app_main(void)
|
||||
/* Check reset reason and decide if we should re-provision */
|
||||
bool do_provision = false;
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
ESP_LOGW(TAG, "After restart! %d", reason);
|
||||
ESP_LOGD(TAG, "After restart! %d", reason);
|
||||
if (reason != ESP_RST_SW) {
|
||||
s_reconfigure_requested = 0;
|
||||
} else if (s_reconfigure_requested == RECONFIGURE_REQUEST) {
|
||||
@ -302,6 +174,7 @@ void app_main(void)
|
||||
|
||||
/* Init the re-provisioning button (long-press with initiate provisioning restart) */
|
||||
gpio_init();
|
||||
esp_read_mac(s_sta_mac, ESP_MAC_WIFI_STA);
|
||||
|
||||
/* Start the application in configuration mode (to perform provisioning)
|
||||
* or in a bridge mode (already provisioned) */
|
||||
@ -312,8 +185,8 @@ void app_main(void)
|
||||
// needed to complete provisioning with getting a valid IP event
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
// starts usb ncm with virtual network used to configure/provision the example
|
||||
usb_ncm_with_network();
|
||||
// starts the wired interface with virtual network used to configure/provision the example
|
||||
wired_netif_init();
|
||||
start_provisioning(&s_event_flags, PROV_SUCCESS_BIT, PROV_FAIL_BIT);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Starting USB-WiFi bridge");
|
||||
@ -321,7 +194,8 @@ void app_main(void)
|
||||
// if we cannot connect to WiFi we just try to re-provision
|
||||
xEventGroupSetBits(s_event_flags, RECONFIGURE_BIT);
|
||||
} else {
|
||||
usb_ncm_wifi_bridge();
|
||||
// start the wired interface in the bridge mode
|
||||
wired_bridge_init(wired_recv_callback, wifi_buff_free);
|
||||
}
|
||||
}
|
||||
|
173
examples/network/sta_to_eth/main/usb_ncm_iface.c
Normal file
173
examples/network/sta_to_eth/main/usb_ncm_iface.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* DESCRIPTION:
|
||||
* This example contains code to make ESP32-S2/S3 as a USB network Device.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tinyusb_net.h"
|
||||
#include "wired_iface.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
#include "esp_mac.h"
|
||||
|
||||
static const char *TAG = "example_wired_tusb_ncm";
|
||||
static esp_netif_t *s_netif = NULL;
|
||||
|
||||
/**
|
||||
* In this scenario of WiFi station to Ethernet bridge mode, we have this configuration
|
||||
*
|
||||
* (ISP) router ESP32 PC
|
||||
* [ AP ] <-> [ sta -- USB ] <-> [ USB-NCM device acting as eth-NIC ]
|
||||
*
|
||||
* From the PC's NIC perspective the L2 forwarding should be transparent and resemble this configuration:
|
||||
*
|
||||
* (ISP) router PC
|
||||
* [ AP ] <----------> [ virtual wifi-NIC ]
|
||||
*
|
||||
* In order for the ESP32 to act as L2 bridge it needs to accept the frames for the NCM device,
|
||||
* which we have fully under control, we can modify it's MAC address, as well as the WiFi station
|
||||
* MAC address, which need to be the same so the AP would see one device (virtual eth-NIC).
|
||||
* No need to modify the ethernet frames here, as we can set the station's MAC to the USB NCM device.
|
||||
*/
|
||||
void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, uint8_t own_mac[6])
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb)
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
tinyusb_net_config_t net_config = {
|
||||
.on_recv_callback = rx_cb,
|
||||
.free_tx_buffer = free_cb,
|
||||
};
|
||||
|
||||
esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA);
|
||||
|
||||
esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB net init but not connect wifi");
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t wired_send(void *buffer, uint16_t len, void *buff_free_arg)
|
||||
{
|
||||
return tinyusb_net_send_sync(buffer, len, buff_free_arg, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
static void l2_free(void *h, void* buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t netif_transmit (void *h, void *buffer, size_t len)
|
||||
{
|
||||
if (wired_send(buffer, len, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send buffer to USB!");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t netif_recv_callback(void *buffer, uint16_t len, void* ctx)
|
||||
{
|
||||
if (s_netif) {
|
||||
void *buf_copy = malloc(len);
|
||||
if (!buf_copy) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(buf_copy, buffer, len);
|
||||
return esp_netif_receive(s_netif, buf_copy, len, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* In this scenario of configuring WiFi, we setup USB-Ethernet to create a virtual network and run DHCP server,
|
||||
* so it could assign an IP address to the PC
|
||||
*
|
||||
* ESP32 PC
|
||||
* | lwip MAC=...01 | eth NIC MAC=...02
|
||||
* | <DHCP server> usb | <-> [ USB-NCM device acting as eth-NIC ]
|
||||
* | <HTTP server> |
|
||||
* | (wifi-provisioning) |
|
||||
*
|
||||
* From the PC's NIC perspective the board acts as a separate network with it's own IP and MAC address,
|
||||
* but the virtual ethernet NIC has also it's own IP and MAC address (configured via tinyusb_net_init()).
|
||||
* That's why we need to create the virtual network with *different* MAC address.
|
||||
* Here, we use two different OUI range MAC addresses.
|
||||
*/
|
||||
esp_err_t wired_netif_init(void)
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
const tinyusb_net_config_t net_config = {
|
||||
// locally administrated address for the ncm device as it's going to be used internally
|
||||
// for configuration only
|
||||
.mac_addr = {0x02, 0x02, 0x11, 0x22, 0x33, 0x01},
|
||||
.on_recv_callback = netif_recv_callback,
|
||||
};
|
||||
|
||||
esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Cannot initialize USB Net device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// with OUI range MAC to create a virtual netif running http server
|
||||
// this needs to be different to usb_interface_mac (==client)
|
||||
uint8_t lwip_addr[6]= {0x02, 0x02, 0x11, 0x22, 0x33, 0x02};
|
||||
|
||||
|
||||
// 1) Derive the base config from the default AP (using DHCP server)
|
||||
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP();
|
||||
base_cfg.if_key = "wired";
|
||||
base_cfg.if_desc = "usb ncm config device";
|
||||
// 2) Use static config for driver's config pointing only to static transmit and free functions
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = (void*)1, // will be replaced by the driver pointer only tinyusb_net supports ti
|
||||
.transmit = netif_transmit,
|
||||
.driver_free_rx_buffer = l2_free
|
||||
};
|
||||
|
||||
// Config the esp-netif with:
|
||||
// 1) inherent config (behavioural settings of an interface)
|
||||
// 2) driver's config (connection to IO functions -- usb)
|
||||
// 3) stack config (using lwip IO functions -- derive from eth)
|
||||
esp_netif_config_t cfg = {
|
||||
.base = &base_cfg,
|
||||
.driver = &driver_cfg,
|
||||
// 3) use ethernet style of lwip netif settings
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
|
||||
};
|
||||
|
||||
s_netif = esp_netif_new(&cfg);
|
||||
if (s_netif == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_netif_set_mac(s_netif, lwip_addr);
|
||||
|
||||
// set the minimum lease time
|
||||
uint32_t lease_opt = 1;
|
||||
esp_netif_dhcps_option(s_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_opt, sizeof(lease_opt));
|
||||
|
||||
// start the interface manually (as the driver has been started already)
|
||||
esp_netif_action_start(s_netif, 0, 0, 0);
|
||||
return ESP_OK;
|
||||
}
|
24
examples/network/sta_to_eth/main/wired_iface.h
Normal file
24
examples/network/sta_to_eth/main/wired_iface.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef esp_err_t (*wired_rx_cb_t)(void *buffer, uint16_t len, void *ctx);
|
||||
|
||||
typedef void (*wired_free_cb_t)(void *buffer, void *ctx);
|
||||
|
||||
typedef enum {
|
||||
FROM_WIRED,
|
||||
TO_WIRED
|
||||
} mac_spoof_direction_t;
|
||||
|
||||
void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, uint8_t own_mac[6]);
|
||||
|
||||
esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb);
|
||||
|
||||
esp_err_t wired_send(void *buffer, uint16_t len, void *buff_free_arg);
|
||||
|
||||
esp_err_t wired_netif_init(void);
|
1
examples/network/sta_to_eth/sdkconfig.defaults
Normal file
1
examples/network/sta_to_eth/sdkconfig.defaults
Normal file
@ -0,0 +1 @@
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=y
|
3
examples/network/sta_to_eth/sdkconfig.defaults.esp32s2
Normal file
3
examples/network/sta_to_eth/sdkconfig.defaults.esp32s2
Normal file
@ -0,0 +1,3 @@
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_USB=y
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=n
|
||||
CONFIG_TINYUSB_NET_MODE_NCM=y
|
5
examples/network/sta_to_eth/sdkconfig.defaults.esp32s3
Normal file
5
examples/network/sta_to_eth/sdkconfig.defaults.esp32s3
Normal file
@ -0,0 +1,5 @@
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_USB=y
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=n
|
||||
CONFIG_TINYUSB_TASK_AFFINITY_CPU0=y
|
||||
CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y
|
||||
CONFIG_TINYUSB_NET_MODE_NCM=y
|
@ -1,17 +0,0 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_WIFI_CONFIGURATION
|
||||
prompt "WiFi configuration"
|
||||
default EXAMPLE_WIFI_CONFIGURATION_MANUAL
|
||||
help
|
||||
Choose how the WiFi settings should be configured.
|
||||
|
||||
config EXAMPLE_WIFI_CONFIGURATION_MANUAL
|
||||
bool
|
||||
prompt "Manual configuration via http server"
|
||||
config EXAMPLE_WIFI_CONFIGURATION_PROVISIONING
|
||||
bool
|
||||
prompt "Using unified provisioning"
|
||||
endchoice
|
||||
|
||||
endmenu
|
@ -1,5 +0,0 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "^1.0.1"
|
||||
idf: "^5.0"
|
@ -1,6 +0,0 @@
|
||||
|
||||
#
|
||||
# USB Network Class (NCM)
|
||||
#
|
||||
CONFIG_TINYUSB_NCM_ENABLE=y
|
||||
# end of USB Network Class (NCM)
|
Loading…
Reference in New Issue
Block a user