USB Human Interface Devices: Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
m (→‎Parsing: More subtypes)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(4 intermediate revisions by 2 users not shown)
Line 512:
=== Parsing ===
The report descriptor's items are parsed in a sequential manner. The parser is a state machine. A complete report descriptor may look something like this:
<sourcesyntaxhighlight lang="C">
static const uint8_t hidReportDescriptor [] =
{
Line 548:
0xC0, // EndCollection()
};
</syntaxhighlight>
</source>
Recall the descriptor types.
 
'''Main''' - This is added to the logical tree. There are 5 subtypes:
* ''Input''
* ''Output''
* ''Feature''
* ''Collection''
* ''End Collection''
 
'''Global''' - Adjusts the global state machine. New main items will inherit this state. This is useful where multiple axes are similar, for instance.
* ''Usage Page''
* ''Logical Minimum''
* ''Logical Maximum''
* ''Physical Minimum''
* ''Physical Maximum''
* ''Unit Exponent''
* ''Unit''
* ''Report Size''
* ''Report ID''
|-
|1
|1
|Caps Lock.
|-
|2
|1
|Scroll Lock.
|-
|3
|1
|Compose.
|-
|4
|1
|Kana.
|-
|5
|3
|Reserved, must be zero.
|}
 
== USB mouse ==
USB mice, just like any other HID device, communicate with the software using reports, which are sent via interrupt endpoints or can be manually requested with the "GetReport" request. USB mice have a protocol value of 2 in the interface descriptor.
 
=== Report format ===
This report must be requested by the host using interrupt transfers once every interval milliseconds. Interval is defined in the interrupt IN descriptor of the USB mouse device. Only the first three bytes of the USB mouse report are defined. The remaining bytes, if existed, may be used for device-specific features. Software may request only three bytes in an interrupt transfer, and this will not cause an error even if the actual packet is larger. The table below defines the report format for USB mice operating using the boot protocol.
{| width="70%" border="1"
|-
|'''Offset'''
|'''Size'''
|'''Description'''
|-
|0
|Byte
|Button status.
|-
|1
|Byte
|X movement.
|-
|2
|Byte
|Y movement.
|}
 
 
'''Button status:''' This byte is a bitfield, in which the lowest three bits are standard format. The remaining 5 bits may be used for device-specific purposes.
 
{| width="70%" border="1"
|-
|'''Bit'''
|'''Bit Length'''
|'''Description'''
|-
|0
|1
|When set to 1, indicates the left mouse button is being clicked.
|-
|1
|1
|When set to 1, indicates the right mouse button is being clicked.
|-
|2
|1
|When set to 1, indicates the middle mouse button is being clicked.
|-
|3
|5
|These bits are reserved for device-specific features.
|}
 
 
'''X movement:''' This is a signed 8-bit integer that represents the X movement. Bit 7 (value 0x80) determines the sign of the value. When this value is negative, the mouse is being moved to the left. When this value is positive, the mouse is being moved to the right. Notice that unlike PS/2 mice, the movement values for USB mice are 8-bit signed integers and not 9-bit integers.
 
'''Y movement:''' This is also a signed 8-bit integer that represents the Y movement. When this value is negative, the mouse is being moved up. When the value is positive, the mouse is being moved down (towards the user.)
 
== USB Report Protocol ==
This is the complicated one supported by all HID class devices. Mouses, keyboards, joysticks...
 
The interface descriptor will contain an HID descriptor alongside the endpoint descriptors:
{| width="70%" border="1"
!Offset
!Field
!Size
!Type
!Description
|- valign="top"
|align="center" |0
|bLength
|align="center" |1
|align="center" |Number
|Size of this descriptor in bytes
|- valign="top"
|align="center" |1
|bDescriptorType
|align="center" |1
|align="center" |Constant
|HID Descriptor Type (0x22)
|- valign="top"
|align="center" |2
|bcdHID
|align="center" |2
|align="center" |BCD
|HID Class Specification Release Number in Binary-Coded Decimal (i.e, 1.10 is expressed as 110h).
|- valign="top"
|align="center" |4
|bCountryCode
|align="center" |1
|align="center" |ID
|Country code of localised hardware (0 if irrelevant. Mainly for keyboards).
|- valign="top"
|align="center" |5
|bNumDescriptors
|align="center" |1
|align="center" |Number
|Number of descriptors >= 1
|- valign="top"
|align="center" |3i+6
|bDescriptorType
|align="center" |1
|align="center" |Number
|Type of HID descriptor
|- valign="top"
|align="center" |3i+7
|wDescriptorLength
|align="center" |2
|align="center" |Number
|Length of HID Descriptor
|}
There is always one descriptor, a Report Descriptor, followed by n Optional Descriptors. The types and lengths only are in an array at the end of the overarching HID Descriptor.
 
'''HID Descriptor Types'''
{| border="1" cellpadding="2"
!Value
!Description
|-
|align="center" |21h
|HID Descriptor
|-
|align="center" |22h
|Report Descriptor
|-
|align="center" |23h
|Physical Descriptor
|}
 
To get the details of these descriptors, use the standard USB [[USB#GET_DESCRIPTOR|GET_DESCRIPTOR]], but with an interface recipient.
{| align="center" border="1" cellpadding="5"
!bmRequestType
!bRequest
!colspan="2" |wValue
!wIndex
!wLength
|- align="center"
|10000001b
|GET_DESCRIPTOR<br />6
|Descriptor Type
|Descriptor Index
|Interface Number
|Descriptor<br />Length
|}
Descriptor Index is 0 except for Physical Descriptors, where Index 0 will enumerate descriptor sets and their sizes.
 
===Country Codes===
{| border="1" cellpadding="2"
!Value
!Country
!Value
!Country
!Value
!Country
!Value
!Country
!Value
!Country
|-
|align="center" |00h
|''Not Supported''
|align="center" |08h
|French
|align="center" |10h
|Korean
|align="center" |18h
|Slovakia
|align="center" |20h
|UK
|-
|align="center" |01h
|Arabic
|align="center" |09h
|German
|align="center" |11h
|Latin American
|align="center" |19h
|Spanish
|align="center" |21h
|US
|-
|align="center" |02h
|Belgian
|align="center" |0Ah
|Greek
|align="center" |12h
|Netherlands
|align="center" |1Ah
|Swedish
|align="center" |22h
|Yugoslavia
|-
|align="center" |03h
|Canadian-Bilingual
|align="center" |0Bh
|Hebrew
|align="center" |13h
|Norwegian
|align="center" |1Bh
|Swiss/French
|align="center" |23h
|Turkish-F
|-
|align="center" |04h
|Canadian-French
|align="center" |0Ch
|Hungary
|align="center" |14h
|Persian
|align="center" |1Ch
|Swiss/German
|-
|align="center" |05h
|Czechia
|align="center" |0Dh
|International (ISO)
|align="center" |15h
|Poland
|align="center" |1Dh
|Switzerland
|-
|align="center" |06h
|Danish
|align="center" |0Eh
|Italian
|align="center" |16h
|Portuguese
|align="center" |1Eh
|Taiwan
|-
|align="center" |07h
|Finnish
|align="center" |0Fh
|Japan (Katakana)
|align="center" |17h
|Russia
|align="center" |1Fh
|Turkish-Q
|}
 
=== Report Descriptor ===
This is not a value table, length and content vary as required.
 
It's a sequential list of items. Items come in two basic types, short and long.
==== Short Item====
{| width="70%" border="1"
!Offset
!Field
!Size
!Type
!Description
|- valign="top"
|align="center" |0:0
|bSize
|align="center" |2 bits
|align="center" |Enum
|Size of optional data in bytes
|- valign="top"
|align="center" |0:2
|bType
|align="center" |2 bits
|align="center" |Enum
|Type of this descriptor
|- valign="top"
|align="center" |0:4
|bTag
|align="center" |4 bits
|align="center" |Number
|Function of the item
|- valign="top"
|align="center" |1
|Data
|align="center" |bSize
|align="center" |Data
|Item data
|}
Note that as a special case a bSize of 3 corresponds to 4 bytes.
 
'''bType''':
{| border="1" cellpadding="2"
!Value
!Description
|-
|align="center" |00h
|Main
|-
|align="center" |01h
|Global
|-
|align="center" |02h
|Local
|-
|align="center" |03h
|Reserved
|}
==== Long Item====
{| width="70%" border="1"
!Offset
!Field
!Size
!Type
!Description
|- valign="top"
|align="center" |0:0
|bSize
|align="center" |2 bits
|align="center" |Enum
|Size = 2
|- valign="top"
|align="center" |0:2
|bType
|align="center" |2 bits
|align="center" |Enum
|Type = 3 (Reserved)
|- valign="top"
|align="center" |0:4
|bTag
|align="center" |4 bits
|align="center" |Number
|1111b - Long
|- valign="top"
|align="center" |1
|bDataSize
|align="center" |1
|align="center" |Number
|Byte count of data
|- valign="top"
|align="center" |2
|bLongItemTag
|align="center" |1
|align="center" |Number
|Long item tag
|- valign="top"
|align="center" |3
|Data
|align="center" |bDataSize
|align="center" |Number
|Data
|}
 
=== Parsing ===
The report descriptor's items are parsed in a sequential manner. The parser is a state machine. A complete report descriptor may look something like this:
<source lang="C">
static const uint8_t hidReportDescriptor [] =
{
0x05, 0x01, // UsagePage(Generic Desktop[1])
0x09, 0x04, // UsageId(Joystick[4])
0xA1, 0x01, // Collection(Application)
0x85, 0x01, // ReportId(1)
0x09, 0x01, // UsageId(Pointer[1])
0xA1, 0x00, // Collection(Physical)
0x09, 0x30, // UsageId(X[48])
0x09, 0x31, // UsageId(Y[49])
0x15, 0x80, // LogicalMinimum(-128)
0x25, 0x7F, // LogicalMaximum(127)
0x95, 0x02, // ReportCount(2)
0x75, 0x08, // ReportSize(8)
0x81, 0x02, // Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)
0x05, 0x09, // UsagePage(Button[9])
0x19, 0x01, // UsageIdMin(Button 1[1])
0x29, 0x03, // UsageIdMax(Button 3[3])
0x15, 0x00, // LogicalMinimum(0)
0x25, 0x01, // LogicalMaximum(1)
0x95, 0x03, // ReportCount(3)
0x75, 0x01, // ReportSize(1)
0x81, 0x02, // Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)
0xC0, // EndCollection()
0x05, 0x02, // UsagePage(Simulation Controls[2])
0x09, 0xBB, // UsageId(Throttle[187])
0x15, 0x80, // LogicalMinimum(-128)
0x25, 0x7F, // LogicalMaximum(127)
0x95, 0x01, // ReportCount(1)
0x75, 0x08, // ReportSize(8)
0x81, 0x02, // Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)
0x75, 0x05, // ReportSize(5)
0x81, 0x03, // Input(Constant, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField)
0xC0, // EndCollection()
};
</source>
Recall the descriptor types.
 
Line 1,017 ⟶ 601:
 
[[Category:USB]]
[[Category:Human Interface Device]]