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:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.DS_Store*
|
||||||
|
|
||||||
@@ -103,6 +103,10 @@ if(USB_ITF_CCID)
|
|||||||
message(STATUS "USB WebCCID Interface:\t enabled")
|
message(STATUS "USB WebCCID Interface:\t enabled")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
if(USB_ITF_LWIP)
|
||||||
|
add_compile_definitions(USB_ITF_LWIP=1)
|
||||||
|
message(STATUS "USB LWIP Interface:\t\t enabled")
|
||||||
|
endif()
|
||||||
add_compile_definitions(DEBUG_APDU=${DEBUG_APDU})
|
add_compile_definitions(DEBUG_APDU=${DEBUG_APDU})
|
||||||
if(NOT ESP_PLATFORM)
|
if(NOT ESP_PLATFORM)
|
||||||
add_compile_definitions(MBEDTLS_CONFIG_FILE="${CMAKE_CURRENT_LIST_DIR}/config/mbedtls_config.h")
|
add_compile_definitions(MBEDTLS_CONFIG_FILE="${CMAKE_CURRENT_LIST_DIR}/config/mbedtls_config.h")
|
||||||
@@ -444,6 +448,12 @@ if(PICO_PLATFORM)
|
|||||||
tinyusb_board
|
tinyusb_board
|
||||||
hardware_pio
|
hardware_pio
|
||||||
)
|
)
|
||||||
|
if(USB_ITF_LWIP)
|
||||||
|
list(APPEND LIBRARIES
|
||||||
|
pico_lwip
|
||||||
|
pico_lwip_nosys
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_PQC)
|
if(ENABLE_PQC)
|
||||||
@@ -543,6 +553,7 @@ if(USB_ITF_CCID)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
add_compile_options("-fmacro-prefix-map=${CMAKE_CURRENT_LIST_DIR}/=")
|
add_compile_options("-fmacro-prefix-map=${CMAKE_CURRENT_LIST_DIR}/=")
|
||||||
endif()
|
endif()
|
||||||
@@ -600,6 +611,22 @@ if(PICO_PLATFORM)
|
|||||||
pico_sdk_init()
|
pico_sdk_init()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(USB_ITF_LWIP)
|
||||||
|
list(APPEND PICO_KEYS_SOURCES
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/usb/lwip/lwip.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/usb/lwip/rest_server.c
|
||||||
|
${PICO_TINYUSB_PATH}/lib/networking/dhserver.c
|
||||||
|
${PICO_TINYUSB_PATH}/lib/networking/dnserver.c
|
||||||
|
)
|
||||||
|
list(APPEND INCLUDES
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/usb/lwip
|
||||||
|
${PICO_TINYUSB_PATH}/lib/networking
|
||||||
|
${PICO_LWIP_PATH}/src/include/lwip/apps
|
||||||
|
)
|
||||||
|
message(STATUS "TINYUSB_PATH:\t\t ${PICO_TINYUSB_PATH}")
|
||||||
|
message(STATUS "LWIP_PATH:\t\t ${PICO_LWIP_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(PICO_RP2350)
|
if(PICO_RP2350)
|
||||||
pico_set_uf2_family(${CMAKE_PROJECT_NAME} "rp2350-arm-s")
|
pico_set_uf2_family(${CMAKE_PROJECT_NAME} "rp2350-arm-s")
|
||||||
pico_embed_pt_in_binary(${CMAKE_PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/config/rp2350/pt.json")
|
pico_embed_pt_in_binary(${CMAKE_PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/config/rp2350/pt.json")
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
#define PHY_USB_ITF_WCID 0x2
|
#define PHY_USB_ITF_WCID 0x2
|
||||||
#define PHY_USB_ITF_HID 0x4
|
#define PHY_USB_ITF_HID 0x4
|
||||||
#define PHY_USB_ITF_KB 0x8
|
#define PHY_USB_ITF_KB 0x8
|
||||||
|
#define PHY_USB_ITF_LWIP 0x10
|
||||||
|
|
||||||
#define PHY_LED_DRIVER_PICO 0x1
|
#define PHY_LED_DRIVER_PICO 0x1
|
||||||
#define PHY_LED_DRIVER_PIMORONI 0x2
|
#define PHY_LED_DRIVER_PIMORONI 0x2
|
||||||
|
|||||||
@@ -284,6 +284,9 @@ static void execute_tasks(void)
|
|||||||
{
|
{
|
||||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||||
tud_task(); // tinyusb device task
|
tud_task(); // tinyusb device task
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
service_traffic();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
usb_task();
|
usb_task();
|
||||||
led_blinking_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
|
#else
|
||||||
#define CFG_TUD_VENDOR 0
|
#define CFG_TUD_VENDOR 0
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
#define CFG_TUD_NCM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||||
#define CFG_TUD_HID_EP_BUFSIZE 64
|
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ pthread_t hcore0, hcore1;
|
|||||||
uint8_t ITF_SC_TOTAL = 0;
|
uint8_t ITF_SC_TOTAL = 0;
|
||||||
extern void ccid_init(void);
|
extern void ccid_init(void);
|
||||||
#endif
|
#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;
|
uint8_t ITF_TOTAL = 0;
|
||||||
|
|
||||||
void usb_set_timeout_counter(uint8_t itf, uint32_t v) {
|
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(&card_to_usb_q, sizeof(uint32_t), 64);
|
||||||
queue_init(&usb_to_card_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
|
#ifndef ENABLE_EMULATION
|
||||||
if (phy_data.enabled_usb_itf_present) {
|
if (phy_data.enabled_usb_itf_present) {
|
||||||
enabled_usb_itf = phy_data.enabled_usb_itf;
|
enabled_usb_itf = phy_data.enabled_usb_itf;
|
||||||
@@ -104,6 +110,9 @@ void usb_init(void)
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USB_ITF_CCID
|
#ifdef USB_ITF_CCID
|
||||||
ITF_SC_TOTAL = 0;
|
ITF_SC_TOTAL = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
ITF_LWIP_TOTAL = 0;
|
||||||
#endif
|
#endif
|
||||||
ITF_TOTAL = 0;
|
ITF_TOTAL = 0;
|
||||||
#ifdef USB_ITF_HID
|
#ifdef USB_ITF_HID
|
||||||
@@ -111,14 +120,14 @@ void usb_init(void)
|
|||||||
ITF_HID_CTAP = ITF_HID_TOTAL++;
|
ITF_HID_CTAP = ITF_HID_TOTAL++;
|
||||||
ITF_HID = ITF_TOTAL++;
|
ITF_HID = ITF_TOTAL++;
|
||||||
#ifndef ENABLE_EMULATION
|
#ifndef ENABLE_EMULATION
|
||||||
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[5];
|
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[6];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (enabled_usb_itf & PHY_USB_ITF_KB) {
|
if (enabled_usb_itf & PHY_USB_ITF_KB) {
|
||||||
ITF_HID_KB = ITF_HID_TOTAL++;
|
ITF_HID_KB = ITF_HID_TOTAL++;
|
||||||
ITF_KEYBOARD = ITF_TOTAL++;
|
ITF_KEYBOARD = ITF_TOTAL++;
|
||||||
#ifndef ENABLE_EMULATION
|
#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
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -127,14 +136,23 @@ void usb_init(void)
|
|||||||
ITF_SC_CCID = ITF_SC_TOTAL++;
|
ITF_SC_CCID = ITF_SC_TOTAL++;
|
||||||
ITF_CCID = ITF_TOTAL++;
|
ITF_CCID = ITF_TOTAL++;
|
||||||
#ifndef ENABLE_EMULATION
|
#ifndef ENABLE_EMULATION
|
||||||
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[7];
|
string_desc_itf[ITF_TOTAL - 1] = string_desc_arr[8];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (enabled_usb_itf & PHY_USB_ITF_WCID) {
|
if (enabled_usb_itf & PHY_USB_ITF_WCID) {
|
||||||
ITF_SC_WCID = ITF_SC_TOTAL++;
|
ITF_SC_WCID = ITF_SC_TOTAL++;
|
||||||
ITF_WCID = ITF_TOTAL++;
|
ITF_WCID = ITF_TOTAL++;
|
||||||
#ifndef ENABLE_EMULATION
|
#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
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -152,6 +170,11 @@ void usb_init(void)
|
|||||||
ccid_init();
|
ccid_init();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
if (ITF_LWIP_TOTAL > 0) {
|
||||||
|
lwip_itf_init();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
usb_desc_setup();
|
usb_desc_setup();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ enum { ITF_INVALID = 0xFF };
|
|||||||
extern uint8_t ITF_CCID, ITF_WCID;
|
extern uint8_t ITF_CCID, ITF_WCID;
|
||||||
extern uint8_t ITF_SC_TOTAL;
|
extern uint8_t ITF_SC_TOTAL;
|
||||||
#endif
|
#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;
|
extern uint8_t ITF_TOTAL;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -126,4 +132,9 @@ typedef enum {
|
|||||||
WRITE_SUCCESS,
|
WRITE_SUCCESS,
|
||||||
} write_status_t;
|
} write_status_t;
|
||||||
|
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
extern int lwip_itf_init(void);
|
||||||
|
extern void service_traffic(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ enum {
|
|||||||
#ifdef USB_ITF_CCID
|
#ifdef USB_ITF_CCID
|
||||||
+ TUSB_SMARTCARD_CCID_DESC_LEN + TUSB_SMARTCARD_WCID_DESC_LEN
|
+ TUSB_SMARTCARD_CCID_DESC_LEN + TUSB_SMARTCARD_WCID_DESC_LEN
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
+ TUD_CDC_NCM_DESC_LEN
|
||||||
|
#endif
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,7 +109,8 @@ uint8_t const desc_hid_report_kb[] = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum {
|
enum
|
||||||
|
{
|
||||||
EPNUM_DUMMY = 0,
|
EPNUM_DUMMY = 0,
|
||||||
#ifdef USB_ITF_CCID
|
#ifdef USB_ITF_CCID
|
||||||
EPNUM_CCID,
|
EPNUM_CCID,
|
||||||
@@ -118,6 +122,10 @@ enum {
|
|||||||
#ifdef USB_ITF_HID
|
#ifdef USB_ITF_HID
|
||||||
EPNUM_HID,
|
EPNUM_HID,
|
||||||
EPNUM_HID_KB,
|
EPNUM_HID_KB,
|
||||||
|
#endif
|
||||||
|
#ifdef USB_ITF_LWIP
|
||||||
|
EPNUM_LWIP_NOTIF,
|
||||||
|
EPNUM_LWIP,
|
||||||
#endif
|
#endif
|
||||||
EPNUM_TOTAL
|
EPNUM_TOTAL
|
||||||
};
|
};
|
||||||
@@ -167,13 +175,13 @@ void usb_desc_setup(void) {
|
|||||||
#ifdef USB_ITF_HID
|
#ifdef USB_ITF_HID
|
||||||
if (ITF_HID != ITF_INVALID) {
|
if (ITF_HID != ITF_INVALID) {
|
||||||
TUSB_DESC_TOTAL_LEN += TUD_HID_INOUT_DESC_LEN;
|
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));
|
memcpy(p, desc, sizeof(desc));
|
||||||
p += sizeof(desc);
|
p += sizeof(desc);
|
||||||
}
|
}
|
||||||
if (ITF_KEYBOARD != ITF_INVALID) {
|
if (ITF_KEYBOARD != ITF_INVALID) {
|
||||||
TUSB_DESC_TOTAL_LEN += TUD_HID_DESC_LEN;
|
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));
|
memcpy(p, desc_kb, sizeof(desc_kb));
|
||||||
p += sizeof(desc_kb);
|
p += sizeof(desc_kb);
|
||||||
}
|
}
|
||||||
@@ -181,16 +189,24 @@ void usb_desc_setup(void) {
|
|||||||
#ifdef USB_ITF_CCID
|
#ifdef USB_ITF_CCID
|
||||||
if (ITF_CCID != ITF_INVALID) {
|
if (ITF_CCID != ITF_INVALID) {
|
||||||
TUSB_DESC_TOTAL_LEN += TUSB_SMARTCARD_CCID_DESC_LEN;
|
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));
|
memcpy(p, desc_ccid, sizeof(desc_ccid));
|
||||||
p += sizeof(desc_ccid);
|
p += sizeof(desc_ccid);
|
||||||
}
|
}
|
||||||
if (ITF_WCID != ITF_INVALID) {
|
if (ITF_WCID != ITF_INVALID) {
|
||||||
TUSB_DESC_TOTAL_LEN += TUSB_SMARTCARD_WCID_DESC_LEN;
|
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));
|
memcpy(p, desc_wcid, sizeof(desc_wcid));
|
||||||
p += 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
|
#endif
|
||||||
desc_config[2] = TUSB_DESC_TOTAL_LEN & 0xFF;
|
desc_config[2] = TUSB_DESC_TOTAL_LEN & 0xFF;
|
||||||
desc_config[3] = TUSB_DESC_TOTAL_LEN >> 8;
|
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
|
#ifdef USB_ITF_WCID
|
||||||
|
|
||||||
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
|
#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
|
#define MS_OS_20_DESC_LEN 0xB2
|
||||||
|
#endif
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@@ -224,9 +244,7 @@ const tusb_desc_webusb_url_t desc_url =
|
|||||||
.bScheme = 1, // 0: http, 1: https
|
.bScheme = 1, // 0: http, 1: https
|
||||||
.url = URL
|
.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[] = {
|
uint8_t desc_ms_os_20[] = {
|
||||||
// Set header: length, type, windows version, total length
|
// 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),
|
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
|
// 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),
|
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
|
// 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(MS_OS_20_DESC_LEN-0x0A-0x08),
|
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
|
// 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,
|
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
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
|
||||||
|
|
||||||
// MS OS 2.0 Registry property descriptor: length, type
|
// 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
|
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,
|
'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,
|
'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
|
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,
|
'{', 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,
|
'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, '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
|
'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) {
|
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
|
||||||
// nothing to with DATA & ACK stage
|
// nothing to with DATA & ACK stage
|
||||||
if (stage != CONTROL_STAGE_SETUP)
|
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
|
// Get Microsoft OS 2.0 compatible descriptor
|
||||||
uint16_t total_len;
|
uint16_t total_len;
|
||||||
desc_ms_os_20[22] = ITF_WCID;
|
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);
|
memcpy(&total_len, desc_ms_os_20+8, 2);
|
||||||
return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, total_len);
|
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
|
"Pol Henarejos", // 1: Manufacturer
|
||||||
"Pico Key", // 2: Product
|
"Pico Key", // 2: Product
|
||||||
"11223344", // 3: Serials, should use chip ID
|
"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 Interface"
|
||||||
, "HID Keyboard Interface"
|
, "HID Keyboard Interface"
|
||||||
#ifdef USB_ITF_HID
|
#ifdef USB_ITF_HID
|
||||||
@@ -331,6 +363,7 @@ char const *string_desc_arr [] = {
|
|||||||
, "CCID Interface"
|
, "CCID Interface"
|
||||||
#endif
|
#endif
|
||||||
, "WebCCID Interface"
|
, "WebCCID Interface"
|
||||||
|
, "Network Interface"
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ESP_PLATFORM
|
#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;
|
str = phy_data.usb_product;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (index >= 5 && string_desc_itf[index - 5] != NULL) {
|
else if (index >= 6 && string_desc_itf[index - 6] != NULL) {
|
||||||
str = string_desc_itf[index - 5];
|
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;
|
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];
|
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);
|
uint8_t len = (uint8_t)MIN(strlen(product), buff_avail);
|
||||||
for (size_t ix = 0; ix < len; chr_count++, ix++) {
|
for (size_t ix = 0; ix < len; chr_count++, ix++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user