Most of the code we need has been pulled in from the tinyusb webusb_serial demo. Still LOTS to do regarding descriptors.
This commit is contained in:
parent
9ce33a5771
commit
fbfb7b68cc
|
@ -164,7 +164,9 @@ LIBS += -lm
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# TinyUSB defines
|
# 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
|
||||||
|
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
|
|
|
@ -30,6 +30,8 @@ CIRCUITPY_ROTARYIO = 1
|
||||||
CIRCUITPY_NVM = 1
|
CIRCUITPY_NVM = 1
|
||||||
# We don't have enough endpoints to include MIDI.
|
# We don't have enough endpoints to include MIDI.
|
||||||
CIRCUITPY_USB_MIDI = 0
|
CIRCUITPY_USB_MIDI = 0
|
||||||
|
# We have borrowed the VENDOR nomenclature from tinyusb. VENDOR AKA WEBUSB
|
||||||
|
CIRCUITPY_USB_VENDOR = 1
|
||||||
CIRCUITPY_WIFI = 1
|
CIRCUITPY_WIFI = 1
|
||||||
CIRCUITPY_WATCHDOG ?= 1
|
CIRCUITPY_WATCHDOG ?= 1
|
||||||
CIRCUITPY_ESPIDF = 1
|
CIRCUITPY_ESPIDF = 1
|
||||||
|
|
|
@ -288,6 +288,9 @@ endif
|
||||||
ifeq ($(CIRCUITPY_USB_MIDI),1)
|
ifeq ($(CIRCUITPY_USB_MIDI),1)
|
||||||
SRC_PATTERNS += usb_midi/%
|
SRC_PATTERNS += usb_midi/%
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(CIRCUITPY_USB_VENDOR),1)
|
||||||
|
SRC_PATTERNS += usb_vendor/%
|
||||||
|
endif
|
||||||
ifeq ($(CIRCUITPY_USTACK),1)
|
ifeq ($(CIRCUITPY_USTACK),1)
|
||||||
SRC_PATTERNS += ustack/%
|
SRC_PATTERNS += ustack/%
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -270,6 +270,9 @@ CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID)
|
||||||
CIRCUITPY_USB_MIDI ?= 1
|
CIRCUITPY_USB_MIDI ?= 1
|
||||||
CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI)
|
CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI)
|
||||||
|
|
||||||
|
CIRCUITPY_USB_VENDOR ?= 1
|
||||||
|
CFLAGS += -DCIRCUITPY_USB_VENDOR=$(CIRCUITPY_USB_VENDOR)
|
||||||
|
|
||||||
CIRCUITPY_PEW ?= 0
|
CIRCUITPY_PEW ?= 0
|
||||||
CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW)
|
CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW)
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,10 @@ busio_uart_obj_t debug_uart;
|
||||||
byte buf_array[64];
|
byte buf_array[64];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CIRCUITPY_USB_VENDOR
|
||||||
|
bool tud_vendor_connected(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
void serial_early_init(void) {
|
void serial_early_init(void) {
|
||||||
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||||
debug_uart.base.type = &busio_uart_type;
|
debug_uart.base.type = &busio_uart_type;
|
||||||
|
@ -66,6 +70,12 @@ void serial_init(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool serial_connected(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)
|
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
@ -74,6 +84,14 @@ bool serial_connected(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char serial_read(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 defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||||
if (tud_cdc_connected() && tud_cdc_available() > 0) {
|
if (tud_cdc_connected() && tud_cdc_available() > 0) {
|
||||||
return (char) tud_cdc_read_char();
|
return (char) tud_cdc_read_char();
|
||||||
|
@ -88,6 +106,12 @@ char serial_read(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool serial_bytes_available(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)
|
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
|
||||||
return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0);
|
return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0);
|
||||||
#else
|
#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);
|
common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t*) text, length, &errcode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CIRCUITPY_USB_VENDOR
|
||||||
|
if (tud_vendor_connected()) {
|
||||||
|
tud_vendor_write(text, length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
while (count < length && tud_cdc_connected()) {
|
while (count < length && tud_cdc_connected()) {
|
||||||
count += tud_cdc_write(text + count, length - count);
|
count += tud_cdc_write(text + count, length - count);
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
#define CFG_TUD_MSC 1
|
#define CFG_TUD_MSC 1
|
||||||
#define CFG_TUD_HID CIRCUITPY_USB_HID
|
#define CFG_TUD_HID CIRCUITPY_USB_HID
|
||||||
#define CFG_TUD_MIDI CIRCUITPY_USB_MIDI
|
#define CFG_TUD_MIDI CIRCUITPY_USB_MIDI
|
||||||
|
#define CFG_TUD_VENDOR CIRCUITPY_USB_VENDOR
|
||||||
#define CFG_TUD_CUSTOM_CLASS 0
|
#define CFG_TUD_CUSTOM_CLASS 0
|
||||||
|
|
||||||
/*------------------------------------------------------------------*/
|
/*------------------------------------------------------------------*/
|
||||||
|
|
|
@ -37,6 +37,26 @@
|
||||||
|
|
||||||
#include "tusb.h"
|
#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.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
VENDOR_REQUEST_WEBUSB = 1,
|
||||||
|
VENDOR_REQUEST_MICROSOFT = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
extern uint8_t const desc_ms_os_20[];
|
||||||
|
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
|
// Serial number as hex characters. This writes directly to the USB
|
||||||
// descriptor.
|
// descriptor.
|
||||||
extern uint16_t usb_serial_number[1 + COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2];
|
extern uint16_t usb_serial_number[1 + COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2];
|
||||||
|
@ -141,6 +161,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
|
#if MICROPY_KBD_EXCEPTION
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -102,6 +102,11 @@ else
|
||||||
shared-module/usb_midi/PortOut.c
|
shared-module/usb_midi/PortOut.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CIRCUITPY_USB_VENDOR), 1)
|
||||||
|
SRC_SUPERVISOR += \
|
||||||
|
lib/tinyusb/src/class/vendor/vendor_device.c
|
||||||
|
endif
|
||||||
|
|
||||||
CFLAGS += -DUSB_AVAILABLE
|
CFLAGS += -DUSB_AVAILABLE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -119,6 +124,10 @@ ifndef USB_INTERFACE_NAME
|
||||||
USB_INTERFACE_NAME = "CircuitPython"
|
USB_INTERFACE_NAME = "CircuitPython"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifndef USB_WEBUSB_URL
|
||||||
|
USB_WEBUSB_URL = "www.circuitpython.org"
|
||||||
|
endif
|
||||||
|
|
||||||
USB_DEVICES_COMPUTED := CDC,MSC
|
USB_DEVICES_COMPUTED := CDC,MSC
|
||||||
ifeq ($(CIRCUITPY_USB_MIDI),1)
|
ifeq ($(CIRCUITPY_USB_MIDI),1)
|
||||||
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),AUDIO
|
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),AUDIO
|
||||||
|
@ -126,6 +135,9 @@ endif
|
||||||
ifeq ($(CIRCUITPY_USB_HID),1)
|
ifeq ($(CIRCUITPY_USB_HID),1)
|
||||||
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),HID
|
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),HID
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(CIRCUITPY_USB_VENDOR),1)
|
||||||
|
USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),VENDOR
|
||||||
|
endif
|
||||||
USB_DEVICES ?= "$(USB_DEVICES_COMPUTED)"
|
USB_DEVICES ?= "$(USB_DEVICES_COMPUTED)"
|
||||||
|
|
||||||
ifndef USB_HID_DEVICES
|
ifndef USB_HID_DEVICES
|
||||||
|
@ -198,6 +210,12 @@ USB_DESCRIPTOR_ARGS = \
|
||||||
--output_c_file $(BUILD)/autogen_usb_descriptor.c\
|
--output_c_file $(BUILD)/autogen_usb_descriptor.c\
|
||||||
--output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h
|
--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)
|
ifeq ($(USB_RENUMBER_ENDPOINTS), 0)
|
||||||
USB_DESCRIPTOR_ARGS += --no-renumber_endpoints
|
USB_DESCRIPTOR_ARGS += --no-renumber_endpoints
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -484,6 +484,7 @@ h_file = args.output_h_file
|
||||||
c_file.write("""\
|
c_file.write("""\
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
#include "py/objtuple.h"
|
#include "py/objtuple.h"
|
||||||
#include "shared-bindings/usb_hid/Device.h"
|
#include "shared-bindings/usb_hid/Device.h"
|
||||||
#include "{H_FILE_NAME}"
|
#include "{H_FILE_NAME}"
|
||||||
|
@ -628,11 +629,13 @@ extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}];
|
||||||
msc_vendor=args.manufacturer[:8],
|
msc_vendor=args.manufacturer[:8],
|
||||||
msc_product=args.product[:16]))
|
msc_product=args.product[:16]))
|
||||||
|
|
||||||
if 'VENDOR' in args.devices:
|
# Currently getting compile-time errors in files like tusb_fifo.c
|
||||||
h_file.write("""\
|
# if we try do define this here (TODO figure this out!)
|
||||||
extern const tusb_desc_webusb_url_t desc_webusb_url;
|
#if 'VENDOR' in args.devices:
|
||||||
|
# h_file.write("""\
|
||||||
""")
|
#extern const tusb_desc_webusb_url_t desc_webusb_url;
|
||||||
|
#
|
||||||
|
#""")
|
||||||
|
|
||||||
h_file.write("""\
|
h_file.write("""\
|
||||||
#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
|
#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H
|
||||||
|
@ -719,4 +722,44 @@ const tusb_desc_webusb_url_t desc_webusb_url =
|
||||||
.bScheme = 1, // 0: http, 1: https, 255: ""
|
.bScheme = 1, // 0: http, 1: https, 255: ""
|
||||||
.url = URL
|
.url = URL
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
// This next hardcoded descriptor was 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, especially with this next #define..
|
||||||
|
#define ITF_NUM_VENDOR 6 // SWAG for now.
|
||||||
|
|
||||||
|
#define MS_OS_20_DESC_LEN 0xB2
|
||||||
|
|
||||||
|
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))
|
""".format(webusb_url=args.webusb_url))
|
||||||
|
|
Loading…
Reference in New Issue