Skip to content

Commit 514f118

Browse files
committed
feat(rndis): add usb host rndis
1 parent 341749f commit 514f118

38 files changed

+1079
-555
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(SRCS "usb_host_rndis.c"
2+
INCLUDE_DIRS "include"
3+
REQUIRES esp_netif esp_ringbuf
4+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: "0.1.0"
2+
description: USB Host ETH Driver
3+
dependencies:
4+
idf: ">=4.4"
5+
espressif/usb_host_cdc_acm:
6+
version: "*"
7+
targets:
8+
- esp32s2
9+
- esp32s3
10+
- esp32p4
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#pragma once
7+
8+
#include "esp_err.h"
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/**
15+
* @brief USB Host Ethernet ECM Configuration
16+
*/
17+
typedef struct {
18+
uint16_t vid; /*!< USB device vendor ID */
19+
uint16_t pid; /*!< USB device product ID */
20+
uint16_t out_buffer_size; /*!< Size of the USB OUT buffer */
21+
uint16_t in_buffer_size; /*!< Size of the USB IN buffer */
22+
uint32_t connection_timeout_ms; /*!< Connection timeout in milliseconds */
23+
void (*on_connection_cb)(bool connected); /*!< Connection status change callback */
24+
} usb_host_rndis_config_t;
25+
26+
esp_err_t usbh_rndis_init(const usb_host_rndis_config_t *config);
27+
28+
esp_err_t usbh_rndis_create(void);
29+
30+
esp_err_t usbh_rndis_keepalive(void);
31+
32+
esp_err_t usbh_rndis_open(void);
33+
34+
esp_err_t usbh_rndis_close(void);
35+
36+
esp_err_t usbh_rndis_eth_output(void *h, void *buffer, size_t buflen);
37+
38+
void usbh_rndis_rx_thread(void *arg);
39+
40+
uint8_t *usbh_rndis_get_mac(void);
41+
42+
#ifdef __cplusplus
43+
}
44+
#endif

examples/usb/host/usb_rndis_4g_module/main/usbh_rndis.c renamed to components/usb/usb_host_rndis/usb_host_rndis.c

Lines changed: 145 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
#include "freertos/queue.h"
1414
#include "freertos/ringbuf.h"
1515
#include "usbh_rndis_protocol.h"
16-
#include "usb/usb_host.h"
1716
#include "usb/cdc_acm_host.h"
1817
#include "esp_netif.h"
18+
#include "esp_mac.h"
19+
#include "esp_event.h"
20+
#include "usb_host_rndis.h"
1921

2022
static const char *TAG = "usbh_rndis";
2123

@@ -31,6 +33,7 @@ static uint8_t g_rndis_rx_buffer[CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE];
3133
static uint8_t g_rndis_tx_buffer[CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE];
3234

3335
typedef struct {
36+
esp_netif_t *netif;
3437
cdc_acm_dev_hdl_t cdc_dev;
3538
uint8_t minor;
3639
uint32_t request_id;
@@ -44,53 +47,19 @@ typedef struct {
4447
size_t rndis_msg_buf_len;
4548
RingbufHandle_t in_ringbuf_handle; /*!< in ringbuffer handle of corresponding interface */
4649
QueueHandle_t in_queue_handle; /*!< in queue handle of corresponding interface */
50+
QueueHandle_t tx_queue_handle; /*!< in queue handle of corresponding interface */
4751
size_t in_ringbuf_size;
4852

4953
void *user_data;
5054
} usbh_rndis_t;
5155

52-
static usbh_rndis_t *rndis = NULL;
53-
54-
static void usb_lib_task(void *arg)
55-
{
56-
// Install USB Host driver. Should only be called once in entire application
57-
const usb_host_config_t host_config = {
58-
.skip_phy_setup = false,
59-
.intr_flags = ESP_INTR_FLAG_LEVEL1,
60-
};
61-
ESP_ERROR_CHECK(usb_host_install(&host_config));
62-
63-
//Signalize the usbh_cdc_driver_install, the USB host library has been installed
64-
xTaskNotifyGive(arg);
65-
66-
bool has_clients = true;
67-
bool has_devices = false;
68-
while (has_clients) {
69-
uint32_t event_flags;
70-
ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags));
71-
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
72-
ESP_LOGI(TAG, "Get FLAGS_NO_CLIENTS");
73-
if (ESP_OK == usb_host_device_free_all()) {
74-
ESP_LOGI(TAG, "All devices marked as free, no need to wait FLAGS_ALL_FREE event");
75-
has_clients = false;
76-
} else {
77-
ESP_LOGI(TAG, "Wait for the FLAGS_ALL_FREE");
78-
has_devices = true;
79-
}
80-
}
81-
if (has_devices && event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
82-
ESP_LOGI(TAG, "Get FLAGS_ALL_FREE");
83-
has_clients = false;
84-
}
85-
}
86-
ESP_LOGI(TAG, "No more clients and devices, uninstall USB Host library");
56+
// Structure for TX packets
57+
typedef struct {
58+
void *buffer;
59+
uint32_t length;
60+
} usb_ecm_tx_packet_t;
8761

88-
// Clean up USB Host
89-
vTaskDelay(100); // Short delay to allow clients clean-up
90-
usb_host_uninstall();
91-
ESP_LOGD(TAG, "USB Host library is uninstalled");
92-
vTaskDelete(NULL);
93-
}
62+
static usbh_rndis_t *rndis = NULL;
9463

9564
/*--------------------------------- CDC Buffer Handle Code --------------------------------------*/
9665
static size_t _get_ringbuf_len(RingbufHandle_t ringbuf_hdl)
@@ -157,13 +126,51 @@ static void _ring_buffer_flush(RingbufHandle_t ringbuf_hdl)
157126
}
158127
}
159128

129+
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
130+
int32_t event_id, void *event_data)
131+
{
132+
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
133+
const esp_netif_ip_info_t *ip_info = &event->ip_info;
134+
// s_got_ip = true;
135+
ESP_LOGI(TAG, "Ethernet Got IP Address");
136+
ESP_LOGI(TAG, "~~~~~~~~~~~");
137+
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
138+
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
139+
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
140+
ESP_LOGI(TAG, "~~~~~~~~~~~");
141+
}
142+
143+
static void usb_rndis_free_rx_buffer(void *h, void *buffer)
144+
{
145+
if (buffer) {
146+
free(buffer - 44);
147+
}
148+
}
149+
160150
#define USB_RNDIS_MSG_BUF_SIZE 512
161151
#define USB_RNDIS_IN_RINGBUF_SIZE 2048*2
162152

163-
esp_err_t usbh_rndis_init(void)
153+
esp_err_t usbh_rndis_init(const usb_host_rndis_config_t *config)
164154
{
165-
esp_err_t ret = ESP_OK;
166-
ESP_LOGI(TAG, "Installing USB Host");
155+
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
156+
157+
// Init TCP/IP network interface (should be called only once in application)
158+
esp_err_t ret = esp_netif_init();
159+
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { // Already initialized is OK
160+
ESP_LOGE(TAG, "Failed to initialize TCP/IP stack");
161+
return ret;
162+
}
163+
164+
// Create default event loop that runs in background if not already created
165+
ret = esp_event_loop_create_default();
166+
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { // Already initialized is OK
167+
ESP_LOGE(TAG, "Failed to create event loop");
168+
return ret;
169+
}
170+
171+
// Register event handlers
172+
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
173+
167174
rndis = calloc(1, sizeof(usbh_rndis_t));
168175
ESP_RETURN_ON_FALSE(rndis != NULL, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for rndis");
169176

@@ -177,20 +184,46 @@ esp_err_t usbh_rndis_init(void)
177184
rndis->in_queue_handle = xQueueCreate(10, sizeof(size_t));
178185
ESP_RETURN_ON_FALSE(rndis->in_queue_handle != NULL, ESP_ERR_NO_MEM, TAG, "Failed to create queue");
179186

180-
// Create a task that will handle USB library events
181-
BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), 5, NULL);
182-
assert(task_created == pdTRUE); // Task should always be created
187+
rndis->tx_queue_handle = xQueueCreate(10, sizeof(usb_ecm_tx_packet_t));
188+
ESP_RETURN_ON_FALSE(rndis->tx_queue_handle != NULL, ESP_ERR_NO_MEM, TAG, "Failed to create queue");
183189

184-
// Wait unit the USB host library is installed
185-
uint32_t notify_value = ulTaskNotifyTake(false, pdMS_TO_TICKS(1000));
186-
if (notify_value == 0) {
187-
ESP_LOGE(TAG, "USB host library not installed");
188-
return ESP_FAIL;
190+
// Create network interface for USB ECM with default config
191+
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
192+
rndis->netif = esp_netif_new(&netif_cfg);
193+
if (!rndis->netif) {
194+
ESP_LOGE(TAG, "Failed to create netif");
195+
return ESP_ERR_NO_MEM;
196+
}
197+
198+
esp_netif_driver_ifconfig_t driver_cfg = {
199+
.handle = rndis->netif, // not using an instance, USB-NCM is a static singleton (must be != NULL)
200+
.transmit = usbh_rndis_eth_output, // point to static Tx function
201+
.driver_free_rx_buffer = usb_rndis_free_rx_buffer, // point to Free Rx buffer function
202+
};
203+
204+
// Set the driver configuration for the netif
205+
ret = esp_netif_set_driver_config(rndis->netif, &driver_cfg);
206+
if (ret != ESP_OK) {
207+
esp_netif_destroy(rndis->netif);
208+
rndis->netif = NULL;
209+
return ret;
189210
}
190211

212+
// Generate a MAC address for the interface
213+
uint8_t mac_addr[6];
214+
// uint8_t mac_addr[6] = { 0x01, 0x01, 0x5E, 0x01, 0x01, 0x01 };
215+
216+
esp_read_mac(mac_addr, ESP_MAC_ETH);
217+
mac_addr[5] ^= 0x01; // Make it unique from the default Ethernet MAC
218+
ESP_ERROR_CHECK(esp_netif_set_mac(rndis->netif, mac_addr));
219+
220+
ESP_LOGI(TAG, "USB RNDIS MAC: %02x:%02x:%02x:%02x:%02x:%02x",
221+
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
222+
223+
ESP_LOGI(TAG, "USB RNDIS network interface initialized");
191224
ESP_LOGI(TAG, "Installing CDC-ACM driver");
192225
ESP_ERROR_CHECK(cdc_acm_host_install(NULL));
193-
return ret;
226+
return ESP_OK;
194227
}
195228

196229
/**
@@ -208,7 +241,6 @@ static bool handle_rx(const uint8_t *data, size_t data_len, void *arg)
208241
usbh_rndis_t *rndis = (usbh_rndis_t *)arg;
209242
if (_ringbuf_push(rndis->in_ringbuf_handle, data, data_len, pdMS_TO_TICKS(1000)) == ESP_OK) {
210243
xQueueSend(rndis->in_queue_handle, &data_len, pdMS_TO_TICKS(1000));
211-
printf("!!!!Received %d bytes\n", data_len);
212244
}
213245

214246
return true;
@@ -237,17 +269,45 @@ static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_
237269
ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val);
238270
break;
239271
case CDC_ACM_HOST_NETWORK_CONNECTION:
272+
break;
240273
default:
241274
ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type);
242275
break;
243276
}
244277
}
245278

279+
// USB ECM transmit task
280+
static void usb_rndis_tx_task(void *arg)
281+
{
282+
usb_ecm_tx_packet_t tx_packet;
283+
printf("usb_rndis_tx_task started\n");
284+
while (1) {
285+
if (xQueueReceive(rndis->tx_queue_handle, &tx_packet, portMAX_DELAY) == pdTRUE) {
286+
// if (!s_cdc_dev) {
287+
// // Device disconnected, free buffer and continue
288+
// free(tx_packet.buffer);
289+
// continue;
290+
// }
291+
292+
// Send packet through USB ECM interface
293+
ESP_LOGD(TAG, "Transmitting packet, len=%lu", tx_packet.length);
294+
esp_err_t ret = cdc_acm_host_data_tx_blocking(rndis->cdc_dev, tx_packet.buffer,
295+
tx_packet.length, 1000);
296+
if (ret != ESP_OK) {
297+
ESP_LOGE(TAG, "Failed to transmit packet: %s", esp_err_to_name(ret));
298+
}
299+
300+
// Free the buffer after transmitting
301+
free(tx_packet.buffer);
302+
}
303+
}
304+
}
305+
246306
esp_err_t usbh_rndis_create(void)
247307
{
248308
const cdc_acm_host_device_config_t dev_config = {
249309
.connection_timeout_ms = 1000,
250-
.out_buffer_size = 512,
310+
.out_buffer_size = 2048,
251311
// TODO: make this configurable
252312
.in_buffer_size = 2048,
253313
.event_cb = handle_event,
@@ -267,6 +327,7 @@ esp_err_t usbh_rndis_create(void)
267327
break;
268328
}
269329
cdc_acm_host_desc_print(rndis->cdc_dev);
330+
xTaskCreate(usb_rndis_tx_task, "usb_rndis_tx_task", 4096, rndis, 10, NULL);
270331
return ESP_OK;
271332
}
272333

@@ -507,6 +568,11 @@ esp_err_t usbh_rndis_open(void)
507568

508569
ESP_LOGI(TAG, "Register RNDIS success");
509570
// usbh_rndis_run(rndis);
571+
572+
xTaskCreate(usbh_rndis_rx_thread, "usbh_rndis_rx_thread", 4096, rndis->netif, 5, NULL);
573+
574+
esp_netif_action_start(rndis->netif, NULL, 0, NULL);
575+
esp_netif_action_connected(rndis->netif, NULL, 0, NULL);
510576
return ret;
511577
query_errorout:
512578
ESP_LOGE(TAG, "rndis query iod:%08x error", oid);
@@ -557,7 +623,7 @@ void usbh_rndis_rx_thread(void *arg)
557623
pmg_offset = 0;
558624
uint32_t total_len = rx_length;
559625
while (rx_length > 0) {
560-
ESP_LOGI(TAG, "rndis rx thread rx_length %d\r\n", rx_length);
626+
ESP_LOGD(TAG, "rndis rx thread rx_length %d\r\n", rx_length);
561627
size_t read_len = 0;
562628
// TODO: if can get read_len < sizeof(rndis_query_cmplt_t)
563629
ret = _ringbuf_pop(rndis->in_ringbuf_handle, data_buffer, rx_length, &read_len, pdMS_TO_TICKS(1000));
@@ -617,24 +683,37 @@ esp_err_t usbh_rndis_eth_output(void *h, void *buffer, size_t buflen)
617683
return ESP_ERR_INVALID_STATE;
618684
}
619685

620-
hdr = (rndis_data_packet_t *)g_rndis_tx_buffer;
621-
memset(hdr, 0, sizeof(rndis_data_packet_t));
686+
hdr = (rndis_data_packet_t *)malloc(sizeof(rndis_data_packet_t) + buflen);
687+
// (rndis_data_packet_t *)g_rndis_tx_buffer;
688+
// memset(hdr, 0, sizeof(rndis_data_packet_t));
622689

623690
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
624691
hdr->MessageLength = sizeof(rndis_data_packet_t) + buflen;
625692
hdr->DataOffset = sizeof(rndis_data_packet_t) - sizeof(rndis_generic_msg_t);
626693
hdr->DataLength = buflen;
627694

628695
len = hdr->MessageLength;
629-
ESP_LOGI(TAG, "rndis tx length %"PRIu32"", len);
630-
memcpy(g_rndis_tx_buffer + sizeof(rndis_data_packet_t), buffer, buflen);
696+
ESP_LOGD(TAG, "rndis tx length %"PRIu32"", len);
697+
memcpy((uint8_t *)hdr + sizeof(rndis_data_packet_t), buffer, buflen);
631698
// ESP_LOG_BUFFER_HEX("rndis tx data", g_rndis_tx_buffer, len);
632-
if (len > CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE) {
633-
ESP_LOGE(TAG, "rndis tx length %"PRIu32" is too large", len);
634-
return ESP_ERR_INVALID_SIZE;
635-
}
699+
// if (len > CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE) {
700+
// ESP_LOGE(TAG, "rndis tx length %"PRIu32" is too large", len);
701+
// return ESP_ERR_INVALID_SIZE;
702+
// }
703+
704+
// Create packet structure
705+
usb_ecm_tx_packet_t tx_packet = {
706+
.buffer = hdr,
707+
.length = len
708+
};
636709

637-
cdc_acm_host_data_tx_blocking(rndis->cdc_dev, g_rndis_tx_buffer, len, pdMS_TO_TICKS(1000));
710+
// Send packet to transmit queue
711+
if (xQueueSend(rndis->tx_queue_handle, &tx_packet, pdMS_TO_TICKS(100)) != pdTRUE) {
712+
ESP_LOGE(TAG, "TX queue full, dropping packet");
713+
free(hdr);
714+
return ESP_ERR_TIMEOUT;
715+
}
716+
// cdc_acm_host_data_tx_blocking(rndis->cdc_dev, g_rndis_tx_buffer, len, pdMS_TO_TICKS(1000));
638717
return ESP_OK;
639718
}
640719

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2f9ae2ee3e46609a7d19edc2e0b23e3d7d807607d53d3b0c54a7ab7f7b7b1527
1+
f76e1283e7ef0e7970e8a708c4ee443ff0aa112572e25319d30b7ec555cc0aee

examples/usb/host/usb_rndis_4g_module/components/espressif__usb_host_cdc_acm/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 2.0.6
2+
3+
- Fixed device opening for devices with CDC class defined in Device descriptor https://github.com/espressif/esp-usb/pull/89
4+
- Fixed driver installation from a task with priority >= CDC driver priority
5+
16
## 2.0.5
27

38
- Fixed CDC descriptor parsing logic, when some CDC devices could not be opened

0 commit comments

Comments
 (0)