Merge branch 'feature/usb_device/ncm' into 'master'

usb: Add NCM device example

See merge request espressif/esp-idf!22232
pull/11637/head
David Čermák 2023-06-08 13:20:43 +08:00
commit 9f9bd8c033
9 zmienionych plików z 287 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tusb_ncm)

Wyświetl plik

@ -0,0 +1,103 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# TinyUSB Network Control Model Device Example
(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.
In this example, we implemented the ESP development board to transmit WiFi data to the Linux or Windows host via USB, so that the host could access the Internet.
As a USB stack, a TinyUSB component is used.
## How to use example
### 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).
### Configure the project
Open the project configuration menu (`idf.py menuconfig`).
In the `Example Configuration` menu:
* Set the Wi-Fi configuration.
* Set `WiFi SSID`.
* Set `WiFi Password`.
### Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
After the flashing you should see the output at idf monitor:
```
I (399) main_task: Calling app_main()
I (429) USB_NCM: USB NCM device initialization
W (429) TinyUSB: The device's configuration descriptor is not provided by user, using default.
W (429) TinyUSB: The device's string descriptor is not provided by user, using default.
W (439) TinyUSB: The device's device descriptor is not provided by user, using default.
I (449) tusb_desc:
┌─────────────────────────────────┐
│ USB Device Descriptor Summary │
├───────────────────┬─────────────┤
│bDeviceClass │ 239 │
├───────────────────┼─────────────┤
│bDeviceSubClass │ 2 │
├───────────────────┼─────────────┤
│bDeviceProtocol │ 1 │
├───────────────────┼─────────────┤
│bMaxPacketSize0 │ 64 │
├───────────────────┼─────────────┤
│idVendor │ 0x303a │
├───────────────────┼─────────────┤
│idProduct │ 0x4001 │
├───────────────────┼─────────────┤
│bcdDevice │ 0x100 │
├───────────────────┼─────────────┤
│iManufacturer │ 0x1 │
├───────────────────┼─────────────┤
│iProduct │ 0x2 │
├───────────────────┼─────────────┤
│iSerialNumber │ 0x3 │
├───────────────────┼─────────────┤
│bNumConfigurations │ 0x1 │
└───────────────────┴─────────────┘
I (619) TinyUSB: TinyUSB Driver installed
I (619) USB_NCM: WiFi initialization
I (619) pp: pp rom version: e7ae62f
I (629) net80211: net80211 rom version: e7ae62f
I (689) wifi_init: rx ba win: 6
I (699) wifi_init: tcpip mbox: 32
I (699) wifi_init: udp mbox: 6
I (699) wifi_init: tcp mbox: 6
I (709) wifi_init: tcp tx win: 5744
I (709) wifi_init: tcp rx win: 5744
I (719) wifi_init: tcp mss: 1440
I (719) wifi_init: WiFi IRAM OP enabled
I (719) wifi_init: WiFi RX IRAM OP enabled
I (729) phy_init: phy_version 600,8dd0147,Mar 31 2023,16:34:12
I (779) USB_NCM: USB NCM and WiFi initialized and started
I (779) main_task: Returned from app_main()
I (849) USB_NCM: WiFi STA connected
```

Wyświetl plik

@ -0,0 +1,2 @@
idf_component_register(SRCS "tusb_ncm_main.c"
INCLUDE_DIRS "")

Wyświetl plik

@ -0,0 +1,15 @@
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
endmenu

Wyświetl plik

@ -0,0 +1,5 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb:
version: "^1.3.0"
idf: "^5.0"

Wyświetl plik

@ -0,0 +1,127 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* DESCRIPTION:
* This example demonstrates using ESP32-S2/S3 as a USB network device. It initializes WiFi in station mode,
* connects and bridges the WiFi and USB networks, so the USB device acts as a standard network interface that
* acquires an IP address from the AP/router which the WiFi station connects to.
*/
#include <stdio.h>
#include "esp_log.h"
#include "esp_event.h"
#include "esp_check.h"
#include "nvs_flash.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_private/wifi.h"
#include "tinyusb.h"
#include "tinyusb_net.h"
static const char *TAG = "USB_NCM";
static esp_err_t usb_recv_callback(void *buffer, uint16_t len, void *ctx)
{
bool *is_wifi_connected = ctx;
if (*is_wifi_connected) {
esp_wifi_internal_tx(ESP_IF_WIFI_STA, buffer, len);
}
return ESP_OK;
}
static void wifi_pkt_free(void *eb, void *ctx)
{
esp_wifi_internal_free_rx_buffer(eb);
}
static esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb)
{
if (tinyusb_net_send_sync(buffer, len, eb, portMAX_DELAY) != ESP_OK) {
esp_wifi_internal_free_rx_buffer(eb);
}
return ESP_OK;
}
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
bool *is_connected = arg;
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "WiFi STA disconnected");
*is_connected = false;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL);
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
ESP_LOGI(TAG, "WiFi STA connected");
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, pkt_wifi2usb);
*is_connected = true;
}
}
static esp_err_t start_wifi(bool *is_connected)
{
ESP_RETURN_ON_ERROR(esp_event_loop_create_default(), TAG, "Cannot initialize event loop");
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_RETURN_ON_ERROR(esp_wifi_init(&wifi_cfg), TAG, "Failed to initialize WiFi library");
ESP_RETURN_ON_ERROR(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, is_connected),
TAG, "Failed to register handler for wifi events");
ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), TAG, "Failed to set WiFi station mode");
ESP_RETURN_ON_ERROR(esp_wifi_start(), TAG, "Failed to start WiFi library");
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
},
};
ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_STA, &wifi_config), TAG, "Failed to set WiFi config");
return esp_wifi_connect();
}
void app_main(void)
{
static bool s_is_wifi_connected = false; // needs to be static as it's used after we exit app_main()
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "USB NCM device initialization");
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
};
ESP_GOTO_ON_ERROR(tinyusb_driver_install(&tusb_cfg), err, TAG, "Failed to install TinyUSB driver");
tinyusb_net_config_t net_config = {
.on_recv_callback = usb_recv_callback,
.free_tx_buffer = wifi_pkt_free,
.user_context = &s_is_wifi_connected
};
esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA);
uint8_t *mac = net_config.mac_addr;
ESP_LOGI(TAG, "Network interface HW address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
ESP_GOTO_ON_ERROR(tinyusb_net_init(TINYUSB_USBDEV_0, &net_config), err, TAG, "Failed to initialize TinyUSB NCM device class");
ESP_LOGI(TAG, "WiFi initialization");
ESP_GOTO_ON_ERROR(start_wifi(&s_is_wifi_connected), err, TAG, "Failed to init and start WiFi");
ESP_LOGI(TAG, "USB NCM and WiFi initialized and started");
return;
err:
ESP_LOGE(TAG, "USB-WiFi bridge example failed!");
}

Wyświetl plik

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import subprocess
import time
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32s2
@pytest.mark.usb_device
def test_usb_device_ncm_example(dut: Dut) -> None:
netif_mac = dut.expect(r'Network interface HW address: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})')
netif_mac = netif_mac.group(1).decode('utf-8')
dut.expect_exact('USB NCM and WiFi initialized and started')
dut.expect_exact('Returned from app_main()')
time.sleep(1) # Wait 1s for the network interface to appear
out_bytes = subprocess.check_output('ifconfig', shell=True, timeout=5)
out_str = out_bytes.decode('utf-8')
print('expected network interface HW address: ', netif_mac)
print('ifconfig command output:\n', out_str)
if netif_mac in out_str:
print("NCM device's MAC address {} was found in system network interfaces".format(netif_mac))
else:
raise AssertionError('NCM device not found in network interface list')

Wyświetl plik

@ -0,0 +1 @@
CONFIG_TINYUSB_NET_MODE_NCM=y

Wyświetl plik

@ -0,0 +1,3 @@
CONFIG_TINYUSB_TASK_AFFINITY_CPU0=y
CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y
CONFIG_TINYUSB_NET_MODE_NCM=y