-
Notifications
You must be signed in to change notification settings - Fork 3
USB and HID
- http://microchipdeveloper.com/usb:what-is-usb
- http://microchipdeveloper.com/usb:how-it-works
- http://www.usbmadesimple.co.uk/
- https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-concepts-for-all-developers
- https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/introduction-to-hid-concepts
- https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/pen-sample-report-descriptors
- http://who-t.blogspot.com/2018/12/understanding-hid-report-descriptors.html
TL;DR
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.
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
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.
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!
]
...
]
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
]
]
- 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
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
]
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.
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
]
...
]
Just use HID
instead of USB
.
open HID://XXXXXXXXXXX
- Microsoft Windows (Windows 7 and later)
- Linux
- macOS (10.11 and later)
You can use this tool to check all the USB descriptors and HID Report descriptor.