Skip to content

WinUSB/Bulk transfer (non-webUSB) #564

@indrora

Description

@indrora

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions