Skip to content
Qingtian edited this page Apr 18, 2019 · 11 revisions

USB Basic Concepts

TL;DR

What makes a USB system?

A USB system is a unidirectional communications hierarchy consisting of of a single USB HOST and anywhere from 1 to 127 USB Devices. If more than one device is present one or more USB HUBs must be used. When used, a Hub will be considered one of the 127 Devices. USB System

Standard USB descriptors

A Universal Serial Bus (USB) device defines its capabilities and features through configurations, interfaces, alternate settings, and endpoints.

  • A USB device can have one or more USB configurations.
  • A USB configuration defines the capabilities and features of a device, mainly its power capabilities and interfaces.
  • A configuration can have one or more USB interfaces that define the functionality of the device.
  • Each interface contains one or more endpoints, which are used to transfer data to and from the device. In addition, the interface contains alternate settings that define the bandwidth requirements of the function associated with the interface.
  • An endpoint is a buffer on a USB device. The host can send and receive data to or from that buffer.
  • Devices which have multiple interfaces are referred to as Composite Devices.

A USB device provides information about itself in data structures called USB descriptors. The most commonly used descriptors include:

  • Device Descriptor
  • Configuration Descriptor
  • Interface Descriptor
  • Endpoint Descriptor
  • String Descriptor Descriptor table

USB Port!

A USB device is represented as a USB port! in Red. You can perform standard USB operations (such as configuring the device, sending control requests, and transferring data to or from the device) on it.

Enumerate Devices

Get all the USB devices connect to to Host. Use query usb://, this will give you a block of USB devices information. Each interface is treated as a logical device.

device: query usb://
== [
      [  ;-- Device information block
         name:          User friendly name (string!)
         class:         Class defined in Device descriptor or Interface descriptor (word!)
         vendor-id:     Device Vendor ID (integer!)
         product-id:    Device Product ID (integer!)
         serial-number: Serial Number (string!)
         usage-page:    Usage Page (HID only) (integer!)
         handle:        Native handle! of the device, can be used to open the device (handle!)
      ]
      ...
   ]

A Sample:

devices: query usb://
== [
        [
            name: TREZOR Interface
            class: vendor            ;-- 0xFF
            vendor-id: 1209h
            product-id: 53C1h
            serial-number: "1687DBC3F859CCFFC36D5F71"
            usage-page: 0
            handle: handle!
        ]
        [
             name: TREZOR U2F Interface
             class: HID
             vendor-id: 1209h
             product-id: 53C1h
             serial-number: "1687DBC3F859CCFFC36D5F71"
             usage-page: 61904       ;-- F1D0
             handle: handle!
        ]
        [
             name: USB Optical Mouse
             class: HID
             vendor-id: 1BCFh
             product-id: 0005h
             serial-number: 0
             usage-page: 1           ;-- Generic Desktop Controls
             handle: handle!
        ]
        ...
   ]

Open USB devices

You can open USB devices directly by using a USB URL! or the USB handle! in the device information returned by query.

open USB://XXXXXXXXXXX

The open action always returns a block. It would be an empty block if no devices be found, or a block of USB port!s.

The format of the USB URL! is not decided yet. Here is a proposal:

The key and value are separated by =. The key-value pairs are separated by &.

USB://VID=xxxx&PID=xxxx&SN=xxxx&MI=xx&CONFIG=xx

Keys are supported:
VID:    vendor id (integer!, mandatory)
PID:    product id (integer!, mandatory)
SN:     serial number (string!, optional)
MI:     manufacturing interface (integer!, optional) For composite device, specify the interface. 
CONFIG: configuration (integer!, optional)

Use `VID`, `PID` and `SN` should be able to identify a device uniquely.

A sample:

open USB://VID=1209&PID=53C1
== [
    make port! [
        spec: make object! [
            scheme: 'usb
            user-info: none
            host: "VID=1209&PID=53C1"
            port: none
            path: none
            target: none
            query: none
            fragment: none
            ref: USB://VID=1209&PID=53C1
        ]
        scheme: make object! [
            open: func [port [port!]][...]
            insert: func [
                port [port!]
                data [binary! string!]
            ][...]
            copy: func [port [port!]][...]
            close: func [port [port!]][...]
        ]
        actor: none
        awake: none
        state: make object! [
            state: 'init
            sub: none
            error: none
            closed?: false
            info: none
            configuration: 0
            interface: 0
            transfer-type: 'INTERRUPT
            endpoint: 1
            iso-packets: 0    ;-- Number of isochronous packets. For isochronous transfers only
            ;-- TBD, add more if needed
        ]
        data: none
    ]
]

Communicate with USB device

  • Open a USB device
dev: first open USB://VID=1209&PID=53C1
== make port! [
    spec: make object! [
        scheme: 'usb
        user-info: none
        host: "VID=1209&PID=53C1"
        port: none
        path: none
        target: none
        query: none
        fragment: none
        ref: USB://VID=1209&PID=53C1
    ]
    scheme: make object! [
        open: func [port [port!]][...]
        insert: func [
            port [port!]
            data [binary! string!]
        ][...]
        copy: func [port [port!]][...]
        close: func [port [port!]][...]
    ]
    actor: none
    awake: none
    state: make object! [
        state: 'init
        sub: none
        error: none
        closed?: false
        info: none
        configuration: 0
        interface: 0
        endpoint: 1
        transfer-type: 'INTERRUPT
        iso-packets: 0    ;-- Number of isochronous packets. For isochronous transfers only
        ;-- TBD, add more if needed
    ]
    data: none
]
  • Write data to the USB device
insert dev "Hello world"
insert dev #{1234abcd}
  • Read data from the USB device
copy dev
  • Config the device

If the device has multiple configurations or multiple interfaces. You can change it:

dev/state/configuration: 1
dev/state/interface: 1

NOTE 1: When changing the interface, Red will try to detect the class of the interface and take care of it if possible. For example, A USB device has two interfaces. One is vendor specific class, another one is usbHID class. When switch from the vendor specific interface to usbHID interface, Red will use HID protocol to perform I/O operations automatically.

NOTE 2: In some rare cases, you want to perform raw USB operations on a HID interface. You can open the interface directly by using a USB URL! to force it. e.g. open USB://VID=1209&PID=53C1&MI=1.

Change the transfer type and endpoint is also allowed:

dev/state/transfer-type: 'BULK
dev/state/interface: 2

Get USB Descriptors

You can get all the descriptors by using reflect on the USB port!.

reflect port 'device
reflect port 'configuration
reflect port 'interface
reflect port 'endpoint
reflect port 'HID               ;-- HID Descriptor
reflect port 'HID-Report        ;-- HID Report Descriptor

A sample:

reflect dev 'device
== [
    USBVersion:         2.10
    DeviceClass:         0
    DeviceSubClass:      0
    DeviceProtocol:      0
    MaxPacketSize:       64
    Vendor:              1209h
    Product:             53C1h
    ProductVersion:      1.0
    Manufacturer:        "SatoshiLabs"   ;-- Original value: 0x01, We read the string form String descriptor 1
    Product:             "TREZOR"        ;-- Original value: 0x02
    SerialNumber:        "1687DBC3F859CCFFC36D5F71"
    NumConfigurations:   1
]

reflect dev 'HID
== [
    HIDVersion: 1.10 
    Country: 0           ;-- NotDefined
    NumDescriptors: 1
    DescriptorType: 34   ;-- HID REPORT descriptor
    DescriptorLength: 75
]

reflect dev 'HID-Report
== [
    ;TBD
]

HID Port!

The definition of HID started as a device class over USB. It is well designed and flexible. Today, HID has a standard protocol over multiple transports, e.g USB, Bluetooth, Bluetooth LE, I²C etc.

The usage of HID devices in Red is similar to USB devices. The main differences are listed below.

Enumerate Devices

Get all the HID devices connect to to Host. Use query HID://.

device: query HID://
== [
      [  ;-- Device information block
         name: User friendly name
         vendor-id: Device Vendor ID
         product-id: Device Product ID
         serial-number: Serial Number
         usage-page: Usage Page
         usage: Usage ID
         handle: Native handle! of the device, can be used to open the device
      ]
      ...
   ]

Open HID devices

Just use HID instead of USB.

open HID://XXXXXXXXXXX

Supported platforms

  • Microsoft Windows (Windows 7 and later)
  • Linux
  • macOS (10.11 and later)

Tools

You can use this tool to check all the USB descriptors and HID Report descriptor.

Clone this wiki locally