adafruit / adafruit/Adafruit_TinyUSB_Arduino

WinUSB/Bulk transfer (non-webUSB)

Open
#564 1 comment 0 reactions 0 assignees View on GitHub
Feature
Dominant language
C
Stars
654
Forks
183
PR merge metrics
No merged PRs in 30d

Description

**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

```cpp
// Adafruit_USBD_WinUSB.h
#ifndef ADAFRUIT_USBD_WINUSB_H_
#define ADAFRUIT_USBD_WINUSB_H_

#include "Stream.h"
#include "arduino/Adafruit_USBD_Device.h"

class Adafruit_USBD_WinUSB : public Stream, public Adafruit_USBD_Interface {
public:
Adafruit_USBD_WinUSB(void);

bool begin(void);

// Stream API
virtual int available(void);
virtual int peek(void);
virtual int read(void);
size_t read(uint8_t *buffer, size_t size);
virtual void flush(void);
virtual size_t write(uint8_t b);
virtual size_t write(const uint8_t *buffer, size_t size);

// from Adafruit_USBD_Interface
virtual uint16_t getInterfaceDescriptor(uint8_t itfnum_deprecated,
uint8_t *buf, uint16_t bufsize);

private:

};
```

Implementation

```cpp
// 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;
}
}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the proposed Adafruit_USBD_WinUSB.h and Adafruit_USBD_WinUSB.cpp, then compare their endpoint and descriptor handling with the WebUSB and USB HID implementations. Test enumeration on the RP2040 and verify that the new interface supports high-speed bulk reads and writes without breaking existing USB interfaces.

Written by the indexing model from the issue text.

Assessment

Tech stack
arduino, cpp
Domain
embedded-iot
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.