Skip to content

WinUSB/Bulk transfer (non-webUSB) #564

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions