mirror of
https://github.com/polhenarejos/pico-keys-sdk
synced 2026-05-28 17:11:23 +02:00
Add a tiny REST server.
This commit is contained in:
@@ -51,6 +51,7 @@
|
||||
#define PHY_USB_ITF_WCID 0x2
|
||||
#define PHY_USB_ITF_HID 0x4
|
||||
#define PHY_USB_ITF_KB 0x8
|
||||
#define PHY_USB_ITF_LWIP 0x10
|
||||
|
||||
#define PHY_LED_DRIVER_PICO 0x1
|
||||
#define PHY_LED_DRIVER_PIMORONI 0x2
|
||||
|
||||
@@ -284,6 +284,9 @@ static void execute_tasks(void)
|
||||
{
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
tud_task(); // tinyusb device task
|
||||
#ifdef USB_ITF_LWIP
|
||||
service_traffic();
|
||||
#endif
|
||||
#endif
|
||||
usb_task();
|
||||
led_blinking_task();
|
||||
|
||||
232
src/usb/lwip/lwip.c
Normal file
232
src/usb/lwip/lwip.c
Normal 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
73
src/usb/lwip/lwipopts.h
Normal 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
364
src/usb/lwip/rest_server.c
Normal 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;
|
||||
}
|
||||
8
src/usb/lwip/rest_server.h
Normal file
8
src/usb/lwip/rest_server.h
Normal 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
145
src/usb/lwip/tusb_config.h
Normal 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_ */
|
||||
|
||||
369
src/usb/lwip/usb_descriptors.c
Normal file
369
src/usb/lwip/usb_descriptors.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
Reference in New Issue
Block a user