Add a tiny REST server.

This commit is contained in:
Pol Henarejos
2026-04-10 20:40:05 +02:00
parent 89d44e8c32
commit f84b6bed93
14 changed files with 1323 additions and 20 deletions

232
src/usb/lwip/lwip.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Peter Lawrence
*
* influenced by lrndis https://github.com/fetisov/lrndis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
/*
this appears as either a RNDIS or CDC-ECM USB virtual network adapter; the OS picks its preference
RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on Linux and macOS hosts
The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server.
*/
/*
Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers,
and likely their manufacturer has not tested such functionality. Some code workarounds could be tried:
The smartphone may only have an ECM driver, but refuse to automatically pick ECM (unlike the OSes above);
try modifying ./examples/devices/net_lwip_webserver/usb_descriptors.c so that CONFIG_ID_ECM is default.
The smartphone may be artificially picky about which Ethernet MAC address to recognize; if this happens,
try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00 (clearing bit 1).
*/
#include "bsp/board_api.h"
#include "tusb.h"
#include "dhserver.h"
#include "dnserver.h"
#include "lwip/ethip6.h"
#include "lwip/init.h"
#include "lwip/timeouts.h"
#include "rest_server.h"
#ifdef INCLUDE_IPERF
#include "lwip/apps/lwiperf.h"
#endif
#define INIT_IP4(a, b, c, d) \
{ PP_HTONL(LWIP_MAKEU32(a, b, c, d)) }
/* lwip context */
static struct netif netif_data;
/* shared between tud_network_recv_cb() and service_traffic() */
static struct pbuf *received_frame;
/* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */
/* ideally speaking, this should be generated from the hardware's unique ID (if available) */
/* it is suggested that the first byte is 0x02 to indicate a link-local address */
uint8_t tud_network_mac_address[6] = {0x02, 0x02, 0x84, 0x6A, 0x96, 0x00};
/* network parameters of this MCU */
static const ip4_addr_t ipaddr = INIT_IP4(192, 168, 7, 1);
static const ip4_addr_t netmask = INIT_IP4(255, 255, 255, 0);
static const ip4_addr_t gateway = INIT_IP4(0, 0, 0, 0);
/* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */
static dhcp_entry_t entries[] = {
/* mac ip address lease time */
{{0}, INIT_IP4(192, 168, 7, 2), 24 * 60 * 60},
{{0}, INIT_IP4(192, 168, 7, 3), 24 * 60 * 60},
{{0}, INIT_IP4(192, 168, 7, 4), 24 * 60 * 60},
};
static const dhcp_config_t dhcp_config = {
.router = INIT_IP4(0, 0, 0, 0), /* router address (if any) */
.port = 67, /* listen port */
.dns = INIT_IP4(192, 168, 7, 1), /* dns server (if any) */
"usb", /* dns suffix */
TU_ARRAY_SIZE(entries), /* num entry */
entries /* entries */
};
static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) {
(void) netif;
for (;;) {
/* if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do */
if (!tud_ready())
return ERR_USE;
/* if the network driver can accept another packet, we make it happen */
if (tud_network_can_xmit(p->tot_len)) {
tud_network_xmit(p, 0 /* unused for this example */);
return ERR_OK;
}
/* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */
tud_task();
}
}
static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr) {
return etharp_output(netif, p, addr);
}
#if LWIP_IPV6
static err_t ip6_output_fn(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr) {
return ethip6_output(netif, p, addr);
}
#endif
static err_t netif_init_cb(struct netif *netif) {
LWIP_ASSERT("netif != NULL", (netif != NULL));
netif->mtu = CFG_TUD_NET_MTU;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
netif->state = NULL;
netif->name[0] = 'E';
netif->name[1] = 'X';
netif->linkoutput = linkoutput_fn;
netif->output = ip4_output_fn;
#if LWIP_IPV6
netif->output_ip6 = ip6_output_fn;
#endif
return ERR_OK;
}
static void init_lwip(void) {
struct netif *netif = &netif_data;
lwip_init();
/* the lwip virtual MAC address must be different from the host's; to ensure this, we toggle the LSbit */
netif->hwaddr_len = sizeof(tud_network_mac_address);
memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address));
netif->hwaddr[5] ^= 0x01;
netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input);
#if LWIP_IPV6
netif_create_ip6_linklocal_address(netif, 1);
#endif
netif_set_default(netif);
}
/* handle any DNS requests from dns-server */
bool dns_query_proc(const char *name, ip4_addr_t *addr) {
if (0 == strcmp(name, "tiny.usb")) {
*addr = ipaddr;
return true;
}
return false;
}
bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
/* this shouldn't happen, but if we get another packet before
parsing the previous, we must signal our inability to accept it */
if (received_frame) return false;
if (size) {
struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL);
if (p) {
/* pbuf_alloc() has already initialized struct; all we need to do is copy the data */
memcpy(p->payload, src, size);
/* store away the pointer for service_traffic() to later handle */
received_frame = p;
}
}
return true;
}
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
struct pbuf *p = (struct pbuf *) ref;
(void) arg; /* unused for this example */
return pbuf_copy_partial(p, dst, p->tot_len, 0);
}
void service_traffic(void) {
/* handle any packet received by tud_network_recv_cb() */
if (received_frame) {
// Surrender ownership of our pbuf unless there was an error
// Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0"
// or steal it from whatever took ownership of it with undefined consequences.
// See: https://savannah.nongnu.org/patch/index.php?10121
if (ethernet_input(received_frame, &netif_data)!=ERR_OK) {
pbuf_free(received_frame);
}
received_frame = NULL;
tud_network_recv_renew();
}
sys_check_timeouts();
}
void tud_network_init_cb(void) {
/* if the network is re-initializing and we have a leftover packet, we must do a cleanup */
if (received_frame) {
pbuf_free(received_frame);
received_frame = NULL;
}
}
int lwip_itf_init(void) {
init_lwip();
while (!netif_is_up(&netif_data));
while (dhserv_init(&dhcp_config) != ERR_OK);
while (dnserv_init(IP_ADDR_ANY, 53, dns_query_proc) != ERR_OK);
while (rest_server_init() != ERR_OK);
#ifdef INCLUDE_IPERF
// test with: iperf -c 192.168.7.1 -e -i 1 -M 5000 -l 8192 -r
lwiperf_start_tcp_server_default(NULL, NULL);
#endif
return 0;
}

73
src/usb/lwip/lwipopts.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt
*
*/
#ifndef LWIPOPTS_H__
#define LWIPOPTS_H__
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
#define NO_SYS 1
#define MEM_ALIGNMENT 4
#define LWIP_RAW 0
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#define LWIP_DHCP 0
#define LWIP_ICMP 1
#define LWIP_UDP 1
#define LWIP_TCP 1
#define LWIP_IPV4 1
#define LWIP_IPV6 0
#define ETH_PAD_SIZE 0
#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67))
#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
#define TCP_SND_BUF (4 * TCP_MSS)
#define TCP_WND (4 * TCP_MSS)
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
#define LWIP_HTTPD_CGI 0
#define LWIP_HTTPD_SSI 0
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define LWIP_HTTPD_CUSTOM_FILES 0
#define LWIP_SINGLE_NETIF 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define PBUF_POOL_SIZE 4
#define HTTPD_USE_CUSTOM_FSDATA 0
#define LWIP_MULTICAST_PING 1
#define LWIP_BROADCAST_PING 1
#define LWIP_IPV6_MLD 0
#define LWIP_IPV6_SEND_ROUTER_SOLICIT 0
#endif /* __LWIPOPTS_H__ */

364
src/usb/lwip/rest_server.c Normal file
View File

@@ -0,0 +1,364 @@
#include "rest_server.h"
#include "lwip/tcp.h"
#include "lwip/def.h"
#include <stdbool.h>
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#define REST_PORT 80
#define REST_MAX_CONNS 4
#define REST_MAX_REQUEST_SIZE 8192
#define REST_MAX_METHOD_SIZE 8
#define REST_MAX_PATH_SIZE 192
typedef struct {
bool in_use;
struct tcp_pcb *pcb;
char request[REST_MAX_REQUEST_SIZE + 1];
size_t request_len;
} rest_conn_t;
static struct tcp_pcb *listener_pcb = NULL;
static rest_conn_t conns[REST_MAX_CONNS];
static rest_conn_t *alloc_conn(struct tcp_pcb *pcb) {
size_t i;
for (i = 0; i < REST_MAX_CONNS; i++) {
if (!conns[i].in_use) {
memset(&conns[i], 0, sizeof(conns[i]));
conns[i].in_use = true;
conns[i].pcb = pcb;
return &conns[i];
}
}
return NULL;
}
static void clear_conn(rest_conn_t *conn) {
if (conn == NULL) {
return;
}
memset(conn, 0, sizeof(*conn));
}
static void close_conn(rest_conn_t *conn) {
err_t err;
if (conn == NULL || conn->pcb == NULL) {
clear_conn(conn);
return;
}
tcp_arg(conn->pcb, NULL);
tcp_recv(conn->pcb, NULL);
tcp_sent(conn->pcb, NULL);
tcp_poll(conn->pcb, NULL, 0);
tcp_err(conn->pcb, NULL);
err = tcp_close(conn->pcb);
if (err != ERR_OK) {
tcp_abort(conn->pcb);
}
clear_conn(conn);
}
static void send_response_and_close(rest_conn_t *conn, int status_code, const char *status_text,
const char *content_type, const char *body, size_t body_len) {
char headers[256];
int header_len;
err_t err;
if (conn == NULL || conn->pcb == NULL) {
return;
}
header_len = snprintf(headers, sizeof(headers),
"HTTP/1.0 %d %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %lu\r\n"
"Connection: close\r\n"
"\r\n",
status_code, status_text, content_type, (unsigned long)body_len);
if (header_len <= 0 || (size_t)header_len >= sizeof(headers)) {
close_conn(conn);
return;
}
err = tcp_write(conn->pcb, headers, (uint16_t)header_len, TCP_WRITE_FLAG_COPY);
if (err == ERR_OK && body_len > 0) {
err = tcp_write(conn->pcb, body, (uint16_t)body_len, TCP_WRITE_FLAG_COPY);
}
if (err == ERR_OK) {
(void)tcp_output(conn->pcb);
}
close_conn(conn);
}
static void send_json_and_close(rest_conn_t *conn, int status_code, const char *status_text, const char *json_body) {
send_response_and_close(conn, status_code, status_text, "application/json", json_body, strlen(json_body));
}
static int parse_request(rest_conn_t *conn,
char *method,
size_t method_size,
char *path,
size_t path_size,
const char **body,
size_t *body_len,
bool *json_content_type) {
char *header_end;
char *line_end;
char *cursor;
size_t headers_size;
unsigned long content_length = 0;
*body = NULL;
*body_len = 0;
*json_content_type = false;
conn->request[conn->request_len] = '\0';
header_end = strstr(conn->request, "\r\n\r\n");
if (header_end == NULL) {
return 0;
}
headers_size = (size_t)(header_end - conn->request) + 4;
line_end = strstr(conn->request, "\r\n");
if (line_end == NULL || line_end > header_end) {
return -1;
}
*line_end = '\0';
if (sscanf(conn->request, "%7s %191s", method, path) != 2) {
return -1;
}
cursor = line_end + 2;
while (cursor < header_end) {
char *next = strstr(cursor, "\r\n");
char *colon;
char *name;
char *value;
char *endptr;
if (next == NULL || next > header_end) {
return -1;
}
if (next == cursor) {
break;
}
*next = '\0';
colon = strchr(cursor, ':');
if (colon != NULL) {
*colon = '\0';
name = cursor;
value = colon + 1;
while (*name != '\0' && isspace((unsigned char)*name)) {
name++;
}
while (*value != '\0' && isspace((unsigned char)*value)) {
value++;
}
while (*name != '\0' && isspace((unsigned char)name[strlen(name) - 1])) {
name[strlen(name) - 1] = '\0';
}
while (*value != '\0' && isspace((unsigned char)value[strlen(value) - 1])) {
value[strlen(value) - 1] = '\0';
}
if (strcasecmp(name, "Content-Length") == 0) {
content_length = strtoul(value, &endptr, 10);
if ((endptr == value) || (*endptr != '\0') || (content_length > REST_MAX_REQUEST_SIZE)) {
return -1;
}
} else if (strcasecmp(name, "Content-Type") == 0) {
if (strncasecmp(value, "application/json", 16) == 0) {
*json_content_type = true;
}
}
}
cursor = next + 2;
}
if (conn->request_len < headers_size + content_length) {
return 0;
}
*body = conn->request + headers_size;
*body_len = (size_t)content_length;
return 1;
}
static void handle_request(rest_conn_t *conn) {
char method[REST_MAX_METHOD_SIZE] = {0};
char path[REST_MAX_PATH_SIZE] = {0};
const char *body;
size_t body_len;
bool is_json;
int parsed;
parsed = parse_request(conn, method, sizeof(method), path, sizeof(path), &body, &body_len, &is_json);
if (parsed <= 0) {
if (parsed < 0) {
send_json_and_close(conn, 400, "Bad Request", "{\"error\":\"bad_request\"}");
}
return;
}
if (strcmp(method, "GET") == 0) {
if (strcmp(path, "/health") == 0) {
send_json_and_close(conn, 200, "OK", "{\"ok\":true}");
return;
}
send_json_and_close(conn, 404, "Not Found", "{\"error\":\"not_found\"}");
return;
}
if (strcmp(method, "POST") == 0 || strcmp(method, "PUT") == 0) {
if (strcmp(path, "/echo") != 0) {
send_json_and_close(conn, 404, "Not Found", "{\"error\":\"not_found\"}");
return;
}
if (!is_json) {
send_json_and_close(conn, 415, "Unsupported Media Type", "{\"error\":\"content_type_must_be_application_json\"}");
return;
}
send_response_and_close(conn, 200, "OK", "application/json", body, body_len);
return;
}
if (strcmp(method, "DELETE") == 0) {
if (strcmp(path, "/echo") == 0) {
send_json_and_close(conn, 200, "OK", "{\"deleted\":true}");
return;
}
send_json_and_close(conn, 404, "Not Found", "{\"error\":\"not_found\"}");
return;
}
send_json_and_close(conn, 405, "Method Not Allowed", "{\"error\":\"method_not_allowed\"}");
}
static err_t rest_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
rest_conn_t *conn = (rest_conn_t *)arg;
LWIP_UNUSED_ARG(pcb);
if (err != ERR_OK) {
if (p != NULL) {
pbuf_free(p);
}
close_conn(conn);
return err;
}
if (p == NULL) {
close_conn(conn);
return ERR_OK;
}
if (conn == NULL) {
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
return ERR_OK;
}
if (conn->request_len + p->tot_len > REST_MAX_REQUEST_SIZE) {
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
send_json_and_close(conn, 413, "Payload Too Large", "{\"error\":\"payload_too_large\"}");
return ERR_OK;
}
pbuf_copy_partial(p, conn->request + conn->request_len, p->tot_len, 0);
conn->request_len += p->tot_len;
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
handle_request(conn);
return ERR_OK;
}
static err_t rest_poll(void *arg, struct tcp_pcb *pcb) {
rest_conn_t *conn = (rest_conn_t *)arg;
LWIP_UNUSED_ARG(pcb);
close_conn(conn);
return ERR_OK;
}
static void rest_err(void *arg, err_t err) {
rest_conn_t *conn = (rest_conn_t *)arg;
LWIP_UNUSED_ARG(err);
clear_conn(conn);
}
static err_t rest_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
rest_conn_t *conn;
LWIP_UNUSED_ARG(arg);
if (err != ERR_OK || newpcb == NULL) {
if (newpcb != NULL) {
tcp_abort(newpcb);
}
return ERR_ABRT;
}
conn = alloc_conn(newpcb);
if (conn == NULL) {
tcp_abort(newpcb);
return ERR_ABRT;
}
tcp_arg(newpcb, conn);
tcp_recv(newpcb, rest_recv);
tcp_poll(newpcb, rest_poll, 8);
tcp_err(newpcb, rest_err);
return ERR_OK;
}
err_t rest_server_init(void) {
err_t err;
if (listener_pcb != NULL) {
return ERR_OK;
}
listener_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
if (listener_pcb == NULL) {
return ERR_MEM;
}
err = tcp_bind(listener_pcb, IP_ANY_TYPE, REST_PORT);
if (err != ERR_OK) {
tcp_abort(listener_pcb);
listener_pcb = NULL;
return err;
}
listener_pcb = tcp_listen_with_backlog(listener_pcb, REST_MAX_CONNS);
if (listener_pcb == NULL) {
return ERR_MEM;
}
tcp_accept(listener_pcb, rest_accept);
return ERR_OK;
}

View File

@@ -0,0 +1,8 @@
#ifndef PICO_KEYS_REST_SERVER_H
#define PICO_KEYS_REST_SERVER_H
#include "lwip/err.h"
err_t rest_server_init(void);
#endif

145
src/usb/lwip/tusb_config.h Normal file
View File

@@ -0,0 +1,145 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef TUSB_CONFIG_H_
#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "lwipopts.h"
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
#endif
// Use different configurations to test all net devices (also due to resource limitations)
#ifndef USE_ECM
#if TU_CHECK_MCU(OPT_MCU_LPC15XX, OPT_MCU_LPC40XX, OPT_MCU_LPC51UXX, OPT_MCU_LPC54)
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML2X)
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F1)
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_MAX32690, OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX78002)
#define USE_ECM 1
#else
#define USE_ECM 0
#define INCLUDE_IPERF
#endif
#endif
//--------------------------------------------------------------------
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
//--------------------------------------------------------------------
// 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
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
// Network class has 2 drivers: ECM/RNDIS and NCM.
// Only one of the drivers can be enabled
#define CFG_TUD_ECM_RNDIS USE_ECM
#define CFG_TUD_NCM (1 - CFG_TUD_ECM_RNDIS)
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,369 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "bsp/board_api.h"
#include "class/net/net_device.h"
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB]
*/
#define PID_MAP(itf, n) ((CFG_TUD_##itf) ? (1 << (n)) : 0)
#define USB_PID \
(0x4000 | PID_MAP(CDC, 0) | PID_MAP(MSC, 1) | PID_MAP(HID, 2) | PID_MAP(MIDI, 3) | PID_MAP(VENDOR, 4) | \
PID_MAP(ECM_RNDIS, 5) | PID_MAP(NCM, 5))
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_INTERFACE,
STRID_MAC,
STRID_COUNT
};
enum {
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
ITF_NUM_TOTAL
};
enum {
#if CFG_TUD_ECM_RNDIS
CONFIG_ID_RNDIS = 0,
CONFIG_ID_ECM = 1,
#else
CONFIG_ID_NCM = 0,
#endif
CONFIG_ID_COUNT
};
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
static const tusb_desc_device_t desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
#if CFG_TUD_NCM
.bcdUSB = 0x0201,
#else
.bcdUSB = 0x0200,
#endif
// Use Interface Association Descriptor (IAD) device class
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0101,
.iManufacturer = STRID_MANUFACTURER,
.iProduct = STRID_PRODUCT,
.iSerialNumber = STRID_SERIAL,
.bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
const uint8_t *tud_descriptor_device_cb(void) {
return (const uint8_t *)&desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN)
#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_NCM_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#elif CFG_TUSB_MCU == OPT_MCU_CXD56
// CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
// 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
#define EPNUM_NET_NOTIF 0x83
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x81
#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x83
#else
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#endif
#if CFG_TUD_ECM_RNDIS
static uint8_t const rndis_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE),
};
static const uint8_t ecm_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// 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_ECM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
};
#else
static uint8_t const ncm_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
// 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(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
};
#endif
// Configuration array: RNDIS and CDC-ECM
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
static const uint8_t *const configuration_arr[CONFIG_ID_COUNT] = {
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = rndis_configuration,
[CONFIG_ID_ECM] = ecm_configuration
#else
[CONFIG_ID_NCM] = ncm_configuration
#endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
}
#if CFG_TUD_NCM
//--------------------------------------------------------------------+
// BOS Descriptor
//--------------------------------------------------------------------+
/* Used to automatically load the NCM driver on Windows 10, otherwise manual driver install is needed.
Associate NCM interface with WINNCM driver. */
/* Microsoft OS 2.0 registry property descriptor
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
device should create DeviceInterfaceGUIDs. It can be done by driver and
in case of real PnP solution device should expose MS "Microsoft OS 2.0
registry property descriptor". Such descriptor can insert any record
into Windows registry per device/configuration/interface. In our case it
will insert "DeviceInterfaceGUIDs" multistring property.
GUID is freshly generated and should be OK to use.
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
(Section Microsoft OS compatibility descriptors)
*/
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
#define MS_OS_20_DESC_LEN 0xB2
// BOS Descriptor is required for webUSB
const uint8_t desc_bos[] = {
// total length, number of device caps
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
// Microsoft OS 2.0 descriptor
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)};
const uint8_t *tud_descriptor_bos_cb(void) {
return desc_bos;
}
const uint8_t desc_ms_os_20[] = {
// Set header: length, type, windows version, total length
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000),
U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
// Configuration subset header: length, type, configuration index, reserved, configuration total length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0,
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
// Function Subset header: length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_CDC, 0,
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'N', 'C', 'M', 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
U16_TO_U8S_LE(0x0007),
U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r',
0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
//bPropertyData: {12345678-0D08-43FD-8B3E-127CA8AFFF9D}
'{', 0x00, '1', 0x00, '2', 0x00, '3', 0x00, '4', 0x00, '5', 0x00, '6', 0x00, '7', 0x00, '8', 0x00, '-', 0x00, '0',
0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00,
'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00, '8', 0x00, 'A',
0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00};
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) {
// nothing to with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP) {
return true;
}
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_VENDOR:
switch (request->bRequest) {
case 1:
if (request->wIndex == 7) {
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len;
memcpy(&total_len, desc_ms_os_20 + 8, 2);
return tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_ms_os_20, total_len);
} else {
return false;
}
default:
break; // nothing to do
}
break;
default:
break; // nothing to do
}
// stall unknown request
return false;
}
#endif
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
static const char *string_desc_arr[STRID_COUNT] = {
[STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409)
[STRID_MANUFACTURER] = "TinyUSB", // Manufacturer
[STRID_PRODUCT] = "TinyUSB Device", // Product
[STRID_SERIAL] = NULL, // Serials will use unique ID if possible
[STRID_INTERFACE] = "TinyUSB Network Interface", // Interface Description
[STRID_MAC] = NULL // STRID_MAC index is handled separately
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void)langid;
unsigned int chr_count = 0;
switch (index) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
case STRID_MAC:
// Convert MAC address into UTF-16
for (unsigned i = 0; i < sizeof(tud_network_mac_address); i++) {
_desc_str[1 + chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
_desc_str[1 + chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
break;
default: {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) {
return NULL;
}
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
if (chr_count > max_count) {
chr_count = max_count;
}
// Convert ASCII string into UTF-16
for (size_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
break;
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -122,6 +122,9 @@ extern "C" {
#else
#define CFG_TUD_VENDOR 0
#endif
#ifdef USB_ITF_LWIP
#define CFG_TUD_NCM 1
#endif
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE 64

View File

@@ -62,6 +62,12 @@ pthread_t hcore0, hcore1;
uint8_t ITF_SC_TOTAL = 0;
extern void ccid_init(void);
#endif
#ifdef USB_ITF_LWIP
uint8_t ITF_LWIP_NET = ITF_INVALID, ITF_LWIP = ITF_INVALID;
uint8_t ITF_LWIP_TOTAL = 0;
extern void lwip_init(void);
#endif
uint8_t ITF_TOTAL = 0;
void usb_set_timeout_counter(uint8_t itf, uint32_t v) {
@@ -92,7 +98,7 @@ void usb_init(void)
queue_init(&card_to_usb_q, sizeof(uint32_t), 64);
queue_init(&usb_to_card_q, sizeof(uint32_t), 64);
uint8_t enabled_usb_itf = PHY_USB_ITF_CCID | PHY_USB_ITF_WCID | PHY_USB_ITF_HID | PHY_USB_ITF_KB;
uint8_t enabled_usb_itf = PHY_USB_ITF_CCID | PHY_USB_ITF_WCID | PHY_USB_ITF_HID | PHY_USB_ITF_KB | PHY_USB_ITF_LWIP;
#ifndef ENABLE_EMULATION
if (phy_data.enabled_usb_itf_present) {
enabled_usb_itf = phy_data.enabled_usb_itf;
@@ -104,6 +110,9 @@ void usb_init(void)
#endif
#ifdef USB_ITF_CCID
ITF_SC_TOTAL = 0;
#endif
#ifdef USB_ITF_LWIP
ITF_LWIP_TOTAL = 0;
#endif
ITF_TOTAL = 0;
#ifdef USB_ITF_HID
@@ -111,14 +120,14 @@ void usb_init(void)
ITF_HID_CTAP = ITF_HID_TOTAL++;
ITF_HID = ITF_TOTAL++;
#ifndef ENABLE_EMULATION
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[5];
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[6];
#endif
}
if (enabled_usb_itf & PHY_USB_ITF_KB) {
ITF_HID_KB = ITF_HID_TOTAL++;
ITF_KEYBOARD = ITF_TOTAL++;
#ifndef ENABLE_EMULATION
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[6];
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[7];
#endif
}
#endif
@@ -127,14 +136,23 @@ void usb_init(void)
ITF_SC_CCID = ITF_SC_TOTAL++;
ITF_CCID = ITF_TOTAL++;
#ifndef ENABLE_EMULATION
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[7];
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[8];
#endif
}
if (enabled_usb_itf & PHY_USB_ITF_WCID) {
ITF_SC_WCID = ITF_SC_TOTAL++;
ITF_WCID = ITF_TOTAL++;
#ifndef ENABLE_EMULATION
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[8];
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[9];
#endif
}
#endif
#ifdef USB_ITF_LWIP
if (enabled_usb_itf & PHY_USB_ITF_LWIP) {
ITF_LWIP_NET = ITF_LWIP_TOTAL++;
ITF_LWIP = ITF_TOTAL++;
#ifndef ENABLE_EMULATION
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[10];
#endif
}
#endif
@@ -152,6 +170,11 @@ void usb_init(void)
ccid_init();
}
#endif
#ifdef USB_ITF_LWIP
if (ITF_LWIP_TOTAL > 0) {
lwip_itf_init();
}
#endif
#ifdef ESP_PLATFORM
usb_desc_setup();
#endif

View File

@@ -61,6 +61,12 @@ enum { ITF_INVALID = 0xFF };
extern uint8_t ITF_CCID, ITF_WCID;
extern uint8_t ITF_SC_TOTAL;
#endif
#ifdef USB_ITF_LWIP
extern uint8_t ITF_LWIP, ITF_LWIP_NET;
extern uint8_t ITF_LWIP_TOTAL;
#endif
extern uint8_t ITF_TOTAL;
enum {
@@ -126,4 +132,9 @@ typedef enum {
WRITE_SUCCESS,
} write_status_t;
#ifdef USB_ITF_LWIP
extern int lwip_itf_init(void);
extern void service_traffic(void);
#endif
#endif

View File

@@ -94,6 +94,9 @@ enum {
#ifdef USB_ITF_CCID
+ TUSB_SMARTCARD_CCID_DESC_LEN + TUSB_SMARTCARD_WCID_DESC_LEN
#endif
#ifdef USB_ITF_LWIP
+ TUD_CDC_NCM_DESC_LEN
#endif
)
};
@@ -106,7 +109,8 @@ uint8_t const desc_hid_report_kb[] = {
};
#endif
enum {
enum
{
EPNUM_DUMMY = 0,
#ifdef USB_ITF_CCID
EPNUM_CCID,
@@ -118,6 +122,10 @@ enum {
#ifdef USB_ITF_HID
EPNUM_HID,
EPNUM_HID_KB,
#endif
#ifdef USB_ITF_LWIP
EPNUM_LWIP_NOTIF,
EPNUM_LWIP,
#endif
EPNUM_TOTAL
};
@@ -167,13 +175,13 @@ void usb_desc_setup(void) {
#ifdef USB_ITF_HID
if (ITF_HID != ITF_INVALID) {
TUSB_DESC_TOTAL_LEN += TUD_HID_INOUT_DESC_LEN;
const uint8_t desc[] = { TUD_HID_INOUT_DESCRIPTOR(ITF_HID, ITF_HID + 5, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, (uint8_t)TUSB_DIR_IN_MASK | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10) };
const uint8_t desc[] = { TUD_HID_INOUT_DESCRIPTOR(ITF_HID, ITF_HID + 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, (uint8_t)TUSB_DIR_IN_MASK | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10) };
memcpy(p, desc, sizeof(desc));
p += sizeof(desc);
}
if (ITF_KEYBOARD != ITF_INVALID) {
TUSB_DESC_TOTAL_LEN += TUD_HID_DESC_LEN;
const uint8_t desc_kb[] = { TUD_HID_DESCRIPTOR(ITF_KEYBOARD, ITF_KEYBOARD + 5, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report_kb), (uint8_t)TUSB_DIR_IN_MASK | EPNUM_HID_KB, 16, 5) };
const uint8_t desc_kb[] = { TUD_HID_DESCRIPTOR(ITF_KEYBOARD, ITF_KEYBOARD + 6, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report_kb), (uint8_t)TUSB_DIR_IN_MASK | EPNUM_HID_KB, 16, 5) };
memcpy(p, desc_kb, sizeof(desc_kb));
p += sizeof(desc_kb);
}
@@ -181,16 +189,24 @@ void usb_desc_setup(void) {
#ifdef USB_ITF_CCID
if (ITF_CCID != ITF_INVALID) {
TUSB_DESC_TOTAL_LEN += TUSB_SMARTCARD_CCID_DESC_LEN;
const uint8_t desc_ccid[] = { TUD_SMARTCARD_DESCRIPTOR(ITF_CCID, ITF_CCID + 5, EPNUM_CCID, TUSB_DIR_IN_MASK | EPNUM_CCID, TUSB_DIR_IN_MASK | EPNUM_CCID_INT, 64) };
const uint8_t desc_ccid[] = { TUD_SMARTCARD_DESCRIPTOR(ITF_CCID, ITF_CCID + 6, EPNUM_CCID, TUSB_DIR_IN_MASK | EPNUM_CCID, TUSB_DIR_IN_MASK | EPNUM_CCID_INT, 64) };
memcpy(p, desc_ccid, sizeof(desc_ccid));
p += sizeof(desc_ccid);
}
if (ITF_WCID != ITF_INVALID) {
TUSB_DESC_TOTAL_LEN += TUSB_SMARTCARD_WCID_DESC_LEN;
const uint8_t desc_wcid[] = { TUD_SMARTCARD_DESCRIPTOR_WEB(ITF_WCID, ITF_WCID + 5, EPNUM_WCID, TUSB_DIR_IN_MASK | EPNUM_WCID, 64) };
const uint8_t desc_wcid[] = { TUD_SMARTCARD_DESCRIPTOR_WEB(ITF_WCID, ITF_WCID + 6, EPNUM_WCID, TUSB_DIR_IN_MASK | EPNUM_WCID, 64) };
memcpy(p, desc_wcid, sizeof(desc_wcid));
p += sizeof(desc_wcid);
}
#endif
#ifdef USB_ITF_LWIP
if (ITF_LWIP != ITF_INVALID) {
TUSB_DESC_TOTAL_LEN += TUD_CDC_NCM_DESC_LEN;
const uint8_t desc_lwip[] = { TUD_CDC_NCM_DESCRIPTOR(ITF_LWIP, ITF_LWIP + 6, 5, EPNUM_LWIP_NOTIF, 64, EPNUM_LWIP, EPNUM_LWIP | TUSB_DIR_IN_MASK, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU) };
memcpy(p, desc_lwip, sizeof(desc_lwip));
p += sizeof(desc_lwip);
}
#endif
desc_config[2] = TUSB_DESC_TOTAL_LEN & 0xFF;
desc_config[3] = TUSB_DESC_TOTAL_LEN >> 8;
@@ -207,7 +223,11 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
#ifdef USB_ITF_WCID
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
#ifdef USB_ITF_LWIP
#define MS_OS_20_DESC_LEN 0xCE
#else
#define MS_OS_20_DESC_LEN 0xB2
#endif
enum
{
@@ -224,9 +244,7 @@ const tusb_desc_webusb_url_t desc_url =
.bScheme = 1, // 0: http, 1: https
.url = URL
};
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
#define MS_OS_20_DESC_LEN 0xB2
uint8_t desc_ms_os_20[] = {
// Set header: length, type, windows version, total length
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
@@ -234,25 +252,35 @@ uint8_t desc_ms_os_20[] = {
// Configuration subset header: length, type, configuration index, reserved, configuration total length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A),
// Function Subset header: length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 0/*ITF_WCID*/, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08),
// Function Subset header for WebCCID (WINUSB): length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 0/*ITF_WCID*/, 0, U16_TO_U8S_LE(0x00A0),
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
U16_TO_U8S_LE(0x0084), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
//bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
//bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
'{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00,
'0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00,
'8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00,
'8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
#ifdef USB_ITF_LWIP
,
// Function Subset header for NCM (WINNCM): length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 0/*ITF_LWIP*/, 0, U16_TO_U8S_LE(0x001C),
// MS OS 2.0 Compatible ID descriptor for NCM
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'N', 'C', 'M', 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#endif
};
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
// nothing to with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP)
@@ -269,6 +297,9 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len;
desc_ms_os_20[22] = ITF_WCID;
#ifdef USB_ITF_LWIP
desc_ms_os_20[182] = ITF_LWIP;
#endif
memcpy(&total_len, desc_ms_os_20+8, 2);
return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, total_len);
}
@@ -322,7 +353,8 @@ char const *string_desc_arr [] = {
"Pol Henarejos", // 1: Manufacturer
"Pico Key", // 2: Product
"11223344", // 3: Serials, should use chip ID
"Config" // 4: Vendor Interface
"Config", // 4: Vendor Interface
"MAC" // 5: MAC address string, handled separately
, "HID Interface"
, "HID Keyboard Interface"
#ifdef USB_ITF_HID
@@ -331,6 +363,7 @@ char const *string_desc_arr [] = {
, "CCID Interface"
#endif
, "WebCCID Interface"
, "Network Interface"
};
#ifdef ESP_PLATFORM
@@ -370,12 +403,21 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
str = phy_data.usb_product;
}
}
else if (index >= 5 && string_desc_itf[index - 5] != NULL) {
str = string_desc_itf[index - 5];
else if (index >= 6 && string_desc_itf[index - 6] != NULL) {
str = string_desc_itf[index - 6];
}
else if (index == 5) {
#ifdef USB_ITF_LWIP
unsigned int chr_count = 0;
for (unsigned i = 0; i < sizeof(tud_network_mac_address); i++) {
_desc_str[1 + chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
_desc_str[1 + chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
#endif
}
uint8_t buff_avail = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1;
if (index >= 4) {
if (index >= 6) {
const char *product = phy_data.usb_product_present ? phy_data.usb_product : string_desc_arr[2];
uint8_t len = (uint8_t)MIN(strlen(product), buff_avail);
for (size_t ix = 0; ix < len; chr_count++, ix++) {