// Adafruit_USBD_WinUSB.cpp
#include "Adafruit_USBD_WinUSB.h"
#include "Arduino.h"
#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define MS_OS_20_DESC_LEN 0xB2
// Microsoft OS 2.0 descriptor for WinUSB
static uint8_t desc_ms_os_20[] = {
// Set header: length, type, windows version, total length
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(0x0000),
U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
// Configuration subset header
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(0x0001),
0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
// Function Subset header
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(0x0002),
0 /*itf num*/, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
// Compatible ID descriptor: "WINUSB"
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(0x0003), 'W', 'I', 'N', 'U', 'S', 'B',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Registry property descriptor
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14),
U16_TO_U8S_LE(0x0004), U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A),
'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),
'{', 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
};
// BOS descriptor with Microsoft OS 2.0 capability
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
static uint8_t const desc_bos[] = {
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)
};
static Adafruit_USBD_WinUSB* _winusb_dev = nullptr;
Adafruit_USBD_WinUSB::Adafruit_USBD_WinUSB(void) {
}
bool Adafruit_USBD_WinUSB::begin(void) {
if (!TinyUSBDevice.addInterface(*this)) {
return false;
}
// Microsoft OS 2.0 descriptors require USB 2.1+
TinyUSBDevice.setVersion(0x0210);
_winusb_dev = this;
return true;
}
uint16_t Adafruit_USBD_WinUSB::getInterfaceDescriptor(uint8_t itfnum_deprecated,
uint8_t *buf,
uint16_t bufsize) {
(void)itfnum_deprecated;
if (!buf) {
return TUD_VENDOR_DESC_LEN;
}
uint8_t const itfnum = TinyUSBDevice.allocInterface(1);
uint8_t const ep_in = TinyUSBDevice.allocEndpoint(TUSB_DIR_IN);
uint8_t const ep_out = TinyUSBDevice.allocEndpoint(TUSB_DIR_OUT);
uint8_t desc[] = {
TUD_VENDOR_DESCRIPTOR(itfnum, _strid, ep_out, ep_in, BULK_PACKET_SIZE)};
uint16_t const len = sizeof(desc);
if (bufsize < len) {
return 0;
}
memcpy(buf, desc, len);
// Update interface number in Microsoft OS 2.0 descriptor
desc_ms_os_20[0x0a + 0x08 + 4] = itfnum;
return len;
}
// Stream implementation
int Adafruit_USBD_WinUSB::available(void) {
return tud_vendor_available();
}
int Adafruit_USBD_WinUSB::peek(void) {
uint8_t ch;
return tud_vendor_peek(&ch) ? (int)ch : -1;
}
int Adafruit_USBD_WinUSB::read(void) {
uint8_t ch;
return tud_vendor_read(&ch, 1) ? (int)ch : -1;
}
size_t Adafruit_USBD_WinUSB::read(uint8_t *buffer, size_t size) {
return tud_vendor_read(buffer, size);
}
void Adafruit_USBD_WinUSB::flush(void) {
tud_vendor_flush();
}
size_t Adafruit_USBD_WinUSB::write(uint8_t b) {
return this->write(&b, 1);
}
size_t Adafruit_USBD_WinUSB::write(const uint8_t *buffer, size_t size) {
size_t remain = size;
while (remain) {
size_t wrcount = tud_vendor_write(buffer, remain);
if (wrcount == 0) break;
remain -= wrcount;
buffer += wrcount;
if (remain) {
yield();
}
}
return size - remain;
}
// Critical callbacks for WinUSB support
extern "C" {
uint8_t const *tud_descriptor_bos_cb(void) {
return desc_bos;
}
bool
tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request) {
if (!_winusb_dev) {
return false;
}
if (stage != CONTROL_STAGE_SETUP) {
return true;
}
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR &&
request->bRequest == 1 &&
request->wIndex == 7) {
// Return Microsoft OS 2.0 descriptor for WinUSB
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);
}
return false;
}
}
Is your feature request related to a problem? Please describe.
I would like to use a bulk transfer endpoint (high speed, full 480Mbit/s goodness) for a project. I've attempted to implement it multiple times and failed.
Describe the solution you'd like
Akin to the WebUSB endpoint, a USB Bulk Transfer that works like the USB HID inout.
Describe alternatives you've considered
USB HID -- Too slow, reports don't show up fast enough for what I want.
Additional context
My attempt thus far has been unsuccessful; when I use it on an RP2040, it fails to enumerate properly.
Header
Implementation