Merge pull request #4126 from FiriaCTO/webusb
WebUSB serial support (compile-time option, currently defaulted to OFF)
This commit is contained in:
commit
0a55cfbde8
94
WEBUSB_README.md
Normal file
94
WEBUSB_README.md
Normal file
@ -0,0 +1,94 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
|
||||
# WebUSB Serial Support
|
||||
|
||||
To date, this has only been tested on one port (esp32s2), on one board (espressif_kaluga_1).
|
||||
|
||||
## What it does
|
||||
|
||||
If you have ever used CircuitPython on a platform with a graphical LCD display, you have probably
|
||||
already seen multiple "consoles" in use (although the LCD console is "output only").
|
||||
|
||||
New compile-time option CIRCUITPY_USB_VENDOR enables an additional "console" that can be used in
|
||||
parallel with the original (CDC) serial console.
|
||||
|
||||
Web pages that support the WebUSB standard can connect to the "vendor" interface and activate
|
||||
this WebUSB serial console at any time.
|
||||
|
||||
You can type into either console, and CircuitPython output is sent to all active consoles.
|
||||
|
||||
One example of a web page you can use to test drive this feature can be found at:
|
||||
|
||||
https://adafruit.github.io/Adafruit_TinyUSB_Arduino/examples/webusb-serial/index.html
|
||||
|
||||
## How to enable
|
||||
|
||||
Update your platform's mpconfigboard.mk file to enable and disable specific types of USB interfaces.
|
||||
|
||||
CIRCUITPY_USB_HID = xxx
|
||||
CIRCUITPY_USB_MIDI = xxx
|
||||
CIRCUITPY_USB_VENDOR = xxx
|
||||
|
||||
On at least some of the hardware platforms, the maximum number of USB endpoints is fixed.
|
||||
For example, on the ESP32S2, you must pick only one of the above 3 interfaces to be enabled.
|
||||
|
||||
Original espressif_kaluga_1 mpconfigboard.mk settings:
|
||||
|
||||
CIRCUITPY_USB_HID = 1
|
||||
CIRCUITPY_USB_MIDI = 0
|
||||
CIRCUITPY_USB_VENDOR = 0
|
||||
|
||||
Settings to enable WebUSB instead:
|
||||
|
||||
CIRCUITPY_USB_HID = 0
|
||||
CIRCUITPY_USB_MIDI = 0
|
||||
CIRCUITPY_USB_VENDOR = 1
|
||||
|
||||
Notice that to enable VENDOR on ESP32-S2, we had to give up HID. There may be platforms that can have both, or even all three.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
CircuitPython uses the tinyusb library.
|
||||
|
||||
The tinyusb library already has support for WebUSB serial.
|
||||
The tinyusb examples already include a "WebUSB serial" example.
|
||||
|
||||
Sidenote - The use of the term "vendor" instead of "WebUSB" was done to match tinyusb.
|
||||
|
||||
Basically, this feature was ported into CircuitPython by pulling code snippets out of the
|
||||
tinyusb example, and putting them where they best belonged in the CircuitPython codebase.
|
||||
|
||||
There was one complication:
|
||||
|
||||
tinyusb uses C preprocessor macros to define things like USB descriptors.
|
||||
|
||||
CircuitPython uses a Python program (tools/gen_usb_descriptor.py) to create USB descriptors (etc.)
|
||||
using "helper objects" from another repo (adafruit_usb_descriptor). This means some of the example
|
||||
code had to be adapted to the new programing model, and gen_usb_descriptor gained new command-line
|
||||
options to control the generated code.
|
||||
|
||||
The generated files go into the "build" directory, look for autogen_usb_descriptor.c and
|
||||
genhdr/autogen_usb_descriptor.h.
|
||||
|
||||
|
||||
Also worth pointing out - the re-use of the CDC connect/disconnect mechanism is not actually part
|
||||
of the WebUSB standard, it's more of "common idiom". We make use of it here because we need to know
|
||||
when we should be paying attention to the WebUSB serial interface, and when we should ignore it..
|
||||
|
||||
## Possible future work areas
|
||||
|
||||
The current code uses the existing Python infrastructure to create the Interface descriptor, but
|
||||
simply outputs the code snippets from the original tinyusb demo code to create the WEBUSB_URL,
|
||||
BOS, and MS_OS_20 descriptors. I suppose additional work could be done to add these to the
|
||||
adafruit_usb_descriptor project, and then gen_usb_descriptor.py could be modified to make use
|
||||
of them.
|
||||
|
||||
Program gen_usb_descriptor.py creates objects for most interface types, regardless of whether or
|
||||
not they are actually enabled. This increases the size of a generated string table. I made the
|
||||
new vendor-interface-related code not do this (because some of the ARM platforms would no longer
|
||||
build), but I did not go back and do this for the other interface types (CDC, MIDI, HID, etc.)
|
||||
Some FLASH savings are probably possible if this is done.
|
@ -46,6 +46,7 @@ Full Table of Contents
|
||||
../BUILDING
|
||||
../CODE_OF_CONDUCT
|
||||
../license.rst
|
||||
../WEBUSB_README
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
@ -164,7 +164,9 @@ LIBS += -lm
|
||||
endif
|
||||
|
||||
# TinyUSB defines
|
||||
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32S2 -DCFG_TUSB_OS=OPT_OS_FREERTOS -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_CDC_TX_BUFSIZE=1024 -DCFG_TUD_MSC_BUFSIZE=4096 -DCFG_TUD_MIDI_RX_BUFSIZE=128 -DCFG_TUD_MIDI_TX_BUFSIZE=128
|
||||
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32S2 -DCFG_TUSB_OS=OPT_OS_FREERTOS -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_CDC_TX_BUFSIZE=1024
|
||||
CFLAGS += -DCFG_TUD_MSC_BUFSIZE=4096 -DCFG_TUD_MIDI_RX_BUFSIZE=128 -DCFG_TUD_MIDI_TX_BUFSIZE=128
|
||||
CFLAGS += -DCFG_TUD_VENDOR_RX_BUFSIZE=128 -DCFG_TUD_VENDOR_TX_BUFSIZE=128
|
||||
|
||||
|
||||
######################################
|
||||
|
@ -14,4 +14,10 @@ CIRCUITPY_ESP_FLASH_MODE=dio
|
||||
CIRCUITPY_ESP_FLASH_FREQ=80m
|
||||
CIRCUITPY_ESP_FLASH_SIZE=4MB
|
||||
|
||||
# We only have enough endpoints available in hardware to
|
||||
# enable ONE of these at a time.
|
||||
CIRCUITPY_USB_MIDI = 1
|
||||
CIRCUITPY_USB_HID = 0
|
||||
CIRCUITPY_USB_VENDOR = 0
|
||||
|
||||
CIRCUITPY_MODULE=wrover
|
||||
|
@ -29,7 +29,10 @@ CIRCUITPY_I2CPERIPHERAL = 0
|
||||
CIRCUITPY_ROTARYIO = 1
|
||||
CIRCUITPY_NVM = 1
|
||||
# We don't have enough endpoints to include MIDI.
|
||||
CIRCUITPY_USB_MIDI = 0
|
||||
CIRCUITPY_USB_MIDI ?= 0
|
||||
CIRCUITPY_USB_HID ?= 1
|
||||
# We have borrowed the VENDOR nomenclature from tinyusb. VENDOR AKA WEBUSB
|
||||
CIRCUITPY_USB_VENDOR ?= 0
|
||||
CIRCUITPY_WIFI = 1
|
||||
CIRCUITPY_WATCHDOG ?= 1
|
||||
CIRCUITPY_ESPIDF = 1
|
||||
|
@ -292,6 +292,9 @@ endif
|
||||
ifeq ($(CIRCUITPY_USB_MIDI),1)
|
||||
SRC_PATTERNS += usb_midi/%
|
||||
endif
|
||||
ifeq ($(CIRCUITPY_USB_VENDOR),1)
|
||||
SRC_PATTERNS += usb_vendor/%
|
||||
endif
|
||||
ifeq ($(CIRCUITPY_USTACK),1)
|
||||
SRC_PATTERNS += ustack/%
|
||||
endif
|
||||
|
@ -288,6 +288,12 @@ CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID)
|
||||
CIRCUITPY_USB_MIDI ?= 1
|
||||
CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI)
|
||||
|
||||
# Defaulting this to OFF initially because it has only been tested on a
|
||||
# limited number of platforms, and the other platforms do not have this
|
||||
# setting in their mpconfigport.mk and/or mpconfigboard.mk files yet.
|
||||
CIRCUITPY_USB_VENDOR ?= 0
|
||||
CFLAGS += -DCIRCUITPY_USB_VENDOR=$(CIRCUITPY_USB_VENDOR)
|
||||
|
||||
CIRCUITPY_PEW ?= 0
|
||||
CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW)
|
||||
|
||||
|
@ -47,6 +47,10 @@ busio_uart_obj_t debug_uart;
|
||||
byte buf_array[64];
|
||||
#endif
|
||||
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
bool tud_vendor_connected(void);
|
||||
#endif
|
||||
|
||||
void serial_early_init(void) {
|
||||
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||
debug_uart.base.type = &busio_uart_type;
|
||||
@ -66,6 +70,12 @@ void serial_init(void) {
|
||||
}
|
||||
|
||||
bool serial_connected(void) {
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
if (tud_vendor_connected()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||
return true;
|
||||
#else
|
||||
@ -74,6 +84,14 @@ bool serial_connected(void) {
|
||||
}
|
||||
|
||||
char serial_read(void) {
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
if (tud_vendor_connected() && tud_vendor_available() > 0) {
|
||||
char tiny_buffer;
|
||||
tud_vendor_read(&tiny_buffer, 1);
|
||||
return tiny_buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||
if (tud_cdc_connected() && tud_cdc_available() > 0) {
|
||||
return (char) tud_cdc_read_char();
|
||||
@ -88,6 +106,12 @@ char serial_read(void) {
|
||||
}
|
||||
|
||||
bool serial_bytes_available(void) {
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
if (tud_vendor_connected() && tud_vendor_available() > 0) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||
return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0);
|
||||
#else
|
||||
@ -104,6 +128,12 @@ void serial_write_substring(const char* text, uint32_t length) {
|
||||
common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t*) text, length, &errcode);
|
||||
#endif
|
||||
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
if (tud_vendor_connected()) {
|
||||
tud_vendor_write(text, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t count = 0;
|
||||
while (count < length && tud_cdc_connected()) {
|
||||
count += tud_cdc_write(text + count, length - count);
|
||||
|
@ -68,6 +68,7 @@
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_HID CIRCUITPY_USB_HID
|
||||
#define CFG_TUD_MIDI CIRCUITPY_USB_MIDI
|
||||
#define CFG_TUD_VENDOR CIRCUITPY_USB_VENDOR
|
||||
#define CFG_TUD_CUSTOM_CLASS 0
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
|
@ -37,6 +37,19 @@
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
#include "genhdr/autogen_usb_descriptor.h"
|
||||
|
||||
// The WebUSB support being conditionally added to this file is based on the
|
||||
// tinyusb demo examples/device/webusb_serial.
|
||||
|
||||
extern const tusb_desc_webusb_url_t desc_webusb_url;
|
||||
|
||||
static bool web_serial_connected = false;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Serial number as hex characters. This writes directly to the USB
|
||||
// descriptor.
|
||||
extern uint16_t usb_serial_number[1 + COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2];
|
||||
@ -145,6 +158,62 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
}
|
||||
}
|
||||
|
||||
#if CIRCUITPY_USB_VENDOR
|
||||
//--------------------------------------------------------------------+
|
||||
// WebUSB use vendor class
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tud_vendor_connected(void)
|
||||
{
|
||||
return web_serial_connected;
|
||||
}
|
||||
|
||||
// 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, tusb_control_request_t const * request)
|
||||
{
|
||||
// nothing to with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP ) return true;
|
||||
|
||||
switch (request->bRequest)
|
||||
{
|
||||
case VENDOR_REQUEST_WEBUSB:
|
||||
// match vendor request in BOS descriptor
|
||||
// Get landing page url
|
||||
return tud_control_xfer(rhport, request, (void*) &desc_webusb_url, desc_webusb_url.bLength);
|
||||
|
||||
case VENDOR_REQUEST_MICROSOFT:
|
||||
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*) desc_ms_os_20, total_len);
|
||||
} else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
case 0x22:
|
||||
// Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to
|
||||
// connect and disconnect.
|
||||
web_serial_connected = (request->wValue != 0);
|
||||
|
||||
// response with status OK
|
||||
return tud_control_status(rhport, request);
|
||||
|
||||
default:
|
||||
// stall unknown request
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif CIRCUITPY_USB_VENDOR
|
||||
|
||||
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
|
||||
/**
|
||||
|
@ -102,6 +102,11 @@ else
|
||||
shared-module/usb_midi/PortOut.c
|
||||
endif
|
||||
|
||||
ifeq ($(CIRCUITPY_USB_VENDOR), 1)
|
||||
SRC_SUPERVISOR += \
|
||||
lib/tinyusb/src/class/vendor/vendor_device.c
|
||||
endif
|
||||
|
||||
CFLAGS += -DUSB_AVAILABLE
|
||||
endif
|
||||
|
||||
@ -119,6 +124,12 @@ ifndef USB_INTERFACE_NAME
|
||||
USB_INTERFACE_NAME = "CircuitPython"
|
||||
endif
|
||||
|
||||
# In the following URL, don't include the https:// prefix.
|
||||
# It gets added automatically.
|
||||
ifndef USB_WEBUSB_URL
|
||||
USB_WEBUSB_URL = "circuitpython.org"
|
||||
endif
|
||||
|
||||
USB_DEVICES_COMPUTED := CDC,MSC
|
||||
ifeq ($(CIRCUITPY_USB_MIDI),1)
|
||||
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),AUDIO
|
||||
@ -126,6 +137,9 @@ endif
|
||||
ifeq ($(CIRCUITPY_USB_HID),1)
|
||||
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),HID
|
||||
endif
|
||||
ifeq ($(CIRCUITPY_USB_VENDOR),1)
|
||||
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),VENDOR
|
||||
endif
|
||||
USB_DEVICES ?= "$(USB_DEVICES_COMPUTED)"
|
||||
|
||||
ifndef USB_HID_DEVICES
|
||||
@ -198,6 +212,12 @@ USB_DESCRIPTOR_ARGS = \
|
||||
--output_c_file $(BUILD)/autogen_usb_descriptor.c\
|
||||
--output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h
|
||||
|
||||
ifeq ($(CIRCUITPY_USB_VENDOR), 1)
|
||||
USB_DESCRIPTOR_ARGS += \
|
||||
--vendor_ep_num_out 0 --vendor_ep_num_in 0 \
|
||||
--webusb_url $(USB_WEBUSB_URL)
|
||||
endif
|
||||
|
||||
ifeq ($(USB_RENUMBER_ENDPOINTS), 0)
|
||||
USB_DESCRIPTOR_ARGS += --no-renumber_endpoints
|
||||
endif
|
||||
|
@ -13,7 +13,7 @@ from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standar
|
||||
import hid_report_descriptors
|
||||
|
||||
DEFAULT_INTERFACE_NAME = 'CircuitPython'
|
||||
ALL_DEVICES='CDC,MSC,AUDIO,HID'
|
||||
ALL_DEVICES='CDC,MSC,AUDIO,HID,VENDOR'
|
||||
ALL_DEVICES_SET=frozenset(ALL_DEVICES.split(','))
|
||||
DEFAULT_DEVICES='CDC,MSC,AUDIO,HID'
|
||||
|
||||
@ -22,6 +22,9 @@ ALL_HID_DEVICES_SET=frozenset(ALL_HID_DEVICES.split(','))
|
||||
# Digitizer works on Linux but conflicts with mouse, so omit it.
|
||||
DEFAULT_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,GAMEPAD'
|
||||
|
||||
# In the following URL, don't include the https:// because that prefix gets added automatically
|
||||
DEFAULT_WEBUSB_URL = 'circuitpython.org' # In the future, this may become a specific landing page
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate USB descriptors.')
|
||||
parser.add_argument('--highspeed', default=False, action='store_true',
|
||||
help='descriptor for highspeed device')
|
||||
@ -62,6 +65,13 @@ parser.add_argument('--midi_ep_num_out', type=int, default=0,
|
||||
help='endpoint number of MIDI OUT')
|
||||
parser.add_argument('--midi_ep_num_in', type=int, default=0,
|
||||
help='endpoint number of MIDI IN')
|
||||
parser.add_argument('--webusb_url', type=str,
|
||||
help='The URL to include in the WebUSB URL Descriptor',
|
||||
default=DEFAULT_WEBUSB_URL)
|
||||
parser.add_argument('--vendor_ep_num_out', type=int, default=0,
|
||||
help='endpoint number of VENDOR OUT')
|
||||
parser.add_argument('--vendor_ep_num_in', type=int, default=0,
|
||||
help='endpoint number of VENDOR IN')
|
||||
parser.add_argument('--max_ep', type=int, default=0,
|
||||
help='total number of endpoints available')
|
||||
parser.add_argument('--output_c_file', type=argparse.FileType('w', encoding='UTF-8'), required=True)
|
||||
@ -89,21 +99,27 @@ if not args.renumber_endpoints:
|
||||
if 'MSC' in args.devices:
|
||||
if args.msc_ep_num_out == 0:
|
||||
raise ValueError("MSC endpoint OUT number must not be 0")
|
||||
elif args.msc_ep_num_in == 0:
|
||||
elif args.msc_ep_num_in == 0:
|
||||
raise ValueError("MSC endpoint IN number must not be 0")
|
||||
|
||||
if 'HID' in args.devices:
|
||||
if args.args.hid_ep_num_out == 0:
|
||||
raise ValueError("HID endpoint OUT number must not be 0")
|
||||
elif args.hid_ep_num_in == 0:
|
||||
elif args.hid_ep_num_in == 0:
|
||||
raise ValueError("HID endpoint IN number must not be 0")
|
||||
|
||||
if 'AUDIO' in args.devices:
|
||||
if args.args.midi_ep_num_out == 0:
|
||||
raise ValueError("MIDI endpoint OUT number must not be 0")
|
||||
elif args.midi_ep_num_in == 0:
|
||||
elif args.midi_ep_num_in == 0:
|
||||
raise ValueError("MIDI endpoint IN number must not be 0")
|
||||
|
||||
if 'VENDOR' in args.devices:
|
||||
if args.vendor_ep_num_out == 0:
|
||||
raise ValueError("VENDOR endpoint OUT number must not be 0")
|
||||
elif args.vendor_ep_num_in == 0:
|
||||
raise ValueError("VENDOR endpoint IN number must not be 0")
|
||||
|
||||
class StringIndex:
|
||||
"""Assign a monotonically increasing index to each unique string. Start with 0."""
|
||||
string_to_index = {}
|
||||
@ -359,6 +375,35 @@ audio_control_interface = standard.InterfaceDescriptor(
|
||||
# Audio streaming interfaces must occur before MIDI ones.
|
||||
audio_interfaces = [audio_control_interface] + cs_ac_interface.audio_streaming_interfaces + cs_ac_interface.midi_streaming_interfaces
|
||||
|
||||
# Vendor-specific interface, for example WebUSB
|
||||
vendor_endpoint_in_descriptor = standard.EndpointDescriptor(
|
||||
description="VENDOR in",
|
||||
bEndpointAddress=args.vendor_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN,
|
||||
bmAttributes=standard.EndpointDescriptor.TYPE_BULK,
|
||||
bInterval=16)
|
||||
|
||||
vendor_endpoint_out_descriptor = standard.EndpointDescriptor(
|
||||
description="VENDOR out",
|
||||
bEndpointAddress=args.vendor_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT,
|
||||
bmAttributes=standard.EndpointDescriptor.TYPE_BULK,
|
||||
bInterval=16)
|
||||
|
||||
# We do the following conditionally to avoid adding unused entries to the StringIndex table
|
||||
if 'VENDOR' in args.devices:
|
||||
vendor_interface = standard.InterfaceDescriptor(
|
||||
description="VENDOR",
|
||||
bInterfaceClass=0xff, # Vendor-specific
|
||||
bInterfaceSubClass=0x00,
|
||||
bInterfaceProtocol=0x00,
|
||||
iInterface=StringIndex.index("{} VENDOR".format(args.interface_name)),
|
||||
subdescriptors=[
|
||||
vendor_endpoint_in_descriptor,
|
||||
vendor_endpoint_out_descriptor,
|
||||
]
|
||||
)
|
||||
|
||||
vendor_interfaces = [vendor_interface]
|
||||
|
||||
interfaces_to_join = []
|
||||
|
||||
if 'CDC' in args.devices:
|
||||
@ -373,6 +418,9 @@ if 'HID' in args.devices:
|
||||
if 'AUDIO' in args.devices:
|
||||
interfaces_to_join.append(audio_interfaces)
|
||||
|
||||
if 'VENDOR' in args.devices:
|
||||
interfaces_to_join.append(vendor_interfaces)
|
||||
|
||||
# util.join_interfaces() will renumber the endpoints to make them unique across descriptors,
|
||||
# and renumber the interfaces in order. But we still need to fix up certain
|
||||
# interface cross-references.
|
||||
@ -425,6 +473,9 @@ if 'AUDIO' in args.devices:
|
||||
# correct ordering.
|
||||
descriptor_list.append(audio_control_interface)
|
||||
|
||||
if 'VENDOR' in args.devices:
|
||||
descriptor_list.extend(vendor_interfaces)
|
||||
|
||||
# Finally, build the composite descriptor.
|
||||
|
||||
configuration = standard.ConfigurationDescriptor(
|
||||
@ -444,6 +495,7 @@ h_file = args.output_h_file
|
||||
c_file.write("""\
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "py/objtuple.h"
|
||||
#include "shared-bindings/usb_hid/Device.h"
|
||||
#include "{H_FILE_NAME}"
|
||||
@ -550,7 +602,7 @@ c_file.write("\n")
|
||||
|
||||
hid_descriptor_length = len(bytes(combined_hid_report_descriptor))
|
||||
|
||||
# Now we values we need for the .h file.
|
||||
# Now the values we need for the .h file.
|
||||
h_file.write("""\
|
||||
#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
|
||||
#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
|
||||
@ -586,7 +638,27 @@ extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}];
|
||||
msc_vendor=args.manufacturer[:8],
|
||||
msc_product=args.product[:16]))
|
||||
|
||||
if 'VENDOR' in args.devices:
|
||||
h_file.write("""\
|
||||
enum
|
||||
{
|
||||
VENDOR_REQUEST_WEBUSB = 1,
|
||||
VENDOR_REQUEST_MICROSOFT = 2
|
||||
};
|
||||
|
||||
extern uint8_t const desc_ms_os_20[];
|
||||
|
||||
// Currently getting compile-time errors in files like tusb_fifo.c
|
||||
// if we try do define this here (TODO figure this out!)
|
||||
//extern const tusb_desc_webusb_url_t desc_webusb_url;
|
||||
|
||||
""")
|
||||
|
||||
h_file.write("""\
|
||||
#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
|
||||
""")
|
||||
# Write out the report descriptor and info
|
||||
|
||||
c_file.write("""\
|
||||
const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{
|
||||
""".format(HID_DESCRIPTOR_LENGTH=hid_descriptor_length))
|
||||
@ -655,6 +727,96 @@ c_file.write("""\
|
||||
};
|
||||
""")
|
||||
|
||||
h_file.write("""\
|
||||
#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
|
||||
""")
|
||||
if 'VENDOR' in args.devices:
|
||||
# Mimic what the tinyusb webusb demo does in it's main.c file
|
||||
c_file.write("""
|
||||
#define URL "{webusb_url}"
|
||||
|
||||
const tusb_desc_webusb_url_t desc_webusb_url =
|
||||
{{
|
||||
.bLength = 3 + sizeof(URL) - 1,
|
||||
.bDescriptorType = 3, // WEBUSB URL type
|
||||
.bScheme = 1, // 0: http, 1: https, 255: ""
|
||||
.url = URL
|
||||
}};
|
||||
|
||||
// These next two hardcoded descriptors were pulled from the usb_descriptor.c file
|
||||
// of the tinyusb webusb_serial demo. TODO - this is probably something else to
|
||||
// integrate into the adafruit_usb_descriptors project...
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BOS Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 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_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
|
||||
|
||||
#define MS_OS_20_DESC_LEN 0xB2
|
||||
|
||||
// BOS Descriptor is required for webUSB
|
||||
uint8_t const desc_bos[] =
|
||||
{{
|
||||
// total length, number of device caps
|
||||
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
|
||||
|
||||
// Vendor Code, iLandingPage
|
||||
TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
|
||||
|
||||
// Microsoft OS 2.0 descriptor
|
||||
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
|
||||
}};
|
||||
|
||||
uint8_t const * tud_descriptor_bos_cb(void)
|
||||
{{
|
||||
return desc_bos;
|
||||
}}
|
||||
|
||||
|
||||
#define ITF_NUM_VENDOR {webusb_interface} // used in this next descriptor
|
||||
|
||||
uint8_t const 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_VENDOR, 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', '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(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}}”.
|
||||
'{{', 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
|
||||
}};
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||
|
||||
// End of section about desc_ms_os_20
|
||||
|
||||
""".format(webusb_url=args.webusb_url, webusb_interface=vendor_interface.bInterfaceNumber))
|
||||
|
Loading…
x
Reference in New Issue
Block a user