Commit b6e12a68 authored by reillyg@chromium.org's avatar reillyg@chromium.org

[usb_gadget p01] Functions for encoding HID descriptors.

The hid module defines functions for encoding HID descriptors. These
descriptors are much more free-form than USB descriptors so a different
approach was taken in this module.

BUG=396682
R=rockot@chromium.org,rpaquay@chromium.org,kalman@chromium.org

Review URL: https://codereview.chromium.org/410813002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@285177 0039d316-1c4b-4281-b951-d872f2087c98
parent c6292f80
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""HID constant definitions.
"""
import usb_constants
class DescriptorType(object):
"""Class descriptors.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 7.1.
"""
HID = usb_constants.Type.CLASS | 0x01
REPORT = usb_constants.Type.CLASS | 0x02
PHYSICAL = usb_constants.Type.CLASS | 0x03
class Scope(object):
"""Item scope.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 6.2.2.2.
"""
MAIN = 0
GLOBAL = 1
LOCAL = 2
class CollectionType(object):
"""Collection types.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 6.2.2.4.
"""
PHYSICAL = 0
APPLICATION = 1
LOGICAL = 2
REPORT = 3
NAMED_ARRAY = 4
USAGE_SWITCH = 5
USAGE_MODIFIER = 6
class Request(object):
"""Class specific requests.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 7.2.
"""
GET_REPORT = 1
GET_IDLE = 2
GET_PROTOCOL = 3
SET_REPORT = 9
SET_IDLE = 0x0A
SET_PROTOCOL = 0x0B
class ReportType(object):
"""Report types.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 7.2.1.
"""
INPUT = 1
OUTPUT = 2
FEATURE = 3
class ModifierKey(object):
"""Keyboard modifier key report values.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 8.3 and HID Usage Tables Version 1.1 Table 12.
"""
L_CTRL = 0x01
L_SHIFT = 0x02
L_ALT = 0x04
L_GUI = 0x08
R_CTRL = 0x10
R_SHIFT = 0x20
R_ALT = 0x40
R_GUI = 0x80
class LED(object):
"""Keyboard LED report values.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section B.1 and HID Usage Tables Version 1.1 Table 13.
"""
NUM_LOCK = 0x01
CAPS_LOCK = 0x02
SCROLL_LOCK = 0x04
COMPOSE = 0x08
KANA = 0x10
class Mouse(object):
"""Mouse button report values.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section B.2.
"""
BUTTON_1 = 0x01
BUTTON_2 = 0x02
BUTTON_3 = 0x04
KEY_CODES = {}
for key, code in zip(xrange(ord('a'), ord('z') + 1), xrange(4, 30)):
KEY_CODES[chr(key)] = code
for key, code in zip(xrange(ord('1'), ord('9') + 1), xrange(30, 39)):
KEY_CODES[chr(key)] = code
for key, code in zip(['Enter', 'Esc', 'Backspace', 'Tab', ' '], xrange(40, 45)):
KEY_CODES[key] = code
for key, code in zip('-=[]\\', xrange(45, 50)):
KEY_CODES[key] = code
for key, code in zip(';\'`,./', xrange(51, 57)):
KEY_CODES[key] = code
for key, code in zip(
['CapsLock', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10',
'F11', 'F12', 'PrintScreen', 'ScrollLock', 'Pause', 'Insert', 'Home',
'PageUp', 'PageDown', 'Delete', 'End', 'PageDown', 'RightArrow',
'LeftArrow', 'DownArrow', 'UpArrow', 'NumLock'],
xrange(57, 84)):
KEY_CODES[key] = code
SHIFT_KEY_CODES = {}
for key, code in zip(xrange(ord('A'), ord('Z') + 1), xrange(4, 30)):
SHIFT_KEY_CODES[chr(key)] = code
for key, code in zip('!@#$%^&*()', xrange(30, 40)):
SHIFT_KEY_CODES[key] = code
for key, code in zip('_+{}|', xrange(45, 50)):
SHIFT_KEY_CODES[key] = code
for key, code in zip(':"~<>?', xrange(51, 57)):
SHIFT_KEY_CODES[key] = code
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utility functions for constructing HID report descriptors.
"""
import struct
import hid_constants
def ReportDescriptor(*items):
return ''.join(items)
def _PackItem(tag, typ, value=0, force_length=0):
"""Pack a multibyte value.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 5.8.
Args:
tag: Item tag.
typ: Item type.
value: Item value.
force_length: Force packing to a specific width.
Returns:
Packed string.
"""
if value == 0 and force_length <= 0:
return struct.pack('<B', tag << 4 | typ << 2 | 0)
elif value <= 0xff and force_length <= 1:
return struct.pack('<BB', tag << 4 | typ << 2 | 1, value)
elif value <= 0xffff and force_length <= 2:
return struct.pack('<BH', tag << 4 | typ << 2 | 2, value)
elif value <= 0xffffffff and force_length <= 4:
return struct.pack('<BI', tag << 4 | typ << 2 | 3, value)
else:
raise NotImplementedError('Long items are not implemented.')
def _DefineItem(name, tag, typ):
"""Create a function which encodes a HID item.
Args:
name: Function name.
tag: Item tag.
typ: Item type.
Returns:
A function which encodes a HID item of the given type.
"""
assert tag >= 0 and tag <= 0xF
assert typ >= 0 and typ <= 3
def EncodeItem(value=0, force_length=0):
return _PackItem(tag, typ, value, force_length)
EncodeItem.__name__ = name
return EncodeItem
def _DefineMainItem(name, tag):
"""Create a function which encodes a HID Main item.
See Device Class Definition for Human Interface Devices (HID) Version 1.11
section 6.2.2.4.
Args:
name: Function name.
tag: Item tag.
Returns:
A function which encodes a HID item of the given type.
Raises:
ValueError: If the tag value is out of range.
"""
assert tag >= 0 and tag <= 0xF
def EncodeMainItem(*properties):
value = 0
for bit, is_set in properties:
if is_set:
value |= 1 << bit
return _PackItem(tag, hid_constants.Scope.MAIN, value, force_length=1)
EncodeMainItem.__name__ = name
return EncodeMainItem
Input = _DefineMainItem('Input', 8)
Output = _DefineMainItem('Output', 9)
Feature = _DefineMainItem('Feature', 11)
# Input, Output and Feature Item Properties
#
# See Device Class Definition for Human Interface Devices (HID) Version 1.11
# section 6.2.2.5.
Data = (0, False)
Constant = (0, True)
Array = (1, False)
Variable = (1, True)
Absolute = (2, False)
Relative = (2, True)
NoWrap = (3, False)
Wrap = (3, True)
Linear = (4, False)
NonLinear = (4, True)
PreferredState = (5, False)
NoPreferred = (5, True)
NoNullPosition = (6, False)
NullState = (6, True)
NonVolatile = (7, False)
Volatile = (7, True)
BitField = (8, False)
BufferedBytes = (8, True)
def Collection(typ, *items):
start = struct.pack('<BB', 0xA1, typ)
end = struct.pack('<B', 0xC0)
return start + ''.join(items) + end
# Global Items
#
# See Device Class Definition for Human Interface Devices (HID) Version 1.11
# section 6.2.2.7.
UsagePage = _DefineItem('UsagePage', 0, hid_constants.Scope.GLOBAL)
LogicalMinimum = _DefineItem('LogicalMinimum', 1, hid_constants.Scope.GLOBAL)
LogicalMaximum = _DefineItem('LogicalMaximum', 2, hid_constants.Scope.GLOBAL)
PhysicalMinimum = _DefineItem('PhysicalMinimum', 3, hid_constants.Scope.GLOBAL)
PhysicalMaximum = _DefineItem('PhysicalMaximum', 4, hid_constants.Scope.GLOBAL)
UnitExponent = _DefineItem('UnitExponent', 5, hid_constants.Scope.GLOBAL)
Unit = _DefineItem('Unit', 6, hid_constants.Scope.GLOBAL)
ReportSize = _DefineItem('ReportSize', 7, hid_constants.Scope.GLOBAL)
ReportID = _DefineItem('ReportID', 8, hid_constants.Scope.GLOBAL)
ReportCount = _DefineItem('ReportCount', 9, hid_constants.Scope.GLOBAL)
Push = _DefineItem('Push', 10, hid_constants.Scope.GLOBAL)
Pop = _DefineItem('Pop', 11, hid_constants.Scope.GLOBAL)
# Local Items
#
# See Device Class Definition for Human Interface Devices (HID) Version 1.11
# section 6.2.2.8.
Usage = _DefineItem('Usage', 0, hid_constants.Scope.LOCAL)
UsageMinimum = _DefineItem('UsageMinimum', 1, hid_constants.Scope.LOCAL)
UsageMaximum = _DefineItem('UsageMaximum', 2, hid_constants.Scope.LOCAL)
DesignatorIndex = _DefineItem('DesignatorIndex', 3, hid_constants.Scope.LOCAL)
DesignatorMinimum = _DefineItem('DesignatorMinimum', 4,
hid_constants.Scope.LOCAL)
DesignatorMaximum = _DefineItem('DesignatorMaximum', 5,
hid_constants.Scope.LOCAL)
StringIndex = _DefineItem('StringIndex', 7, hid_constants.Scope.LOCAL)
StringMinimum = _DefineItem('StringMinimum', 8, hid_constants.Scope.LOCAL)
StringMaximum = _DefineItem('StringMaximum', 9, hid_constants.Scope.LOCAL)
Delimiter = _DefineItem('Delimiter', 10, hid_constants.Scope.LOCAL)
#!/usr/bin/python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import unittest
import hid_constants
import hid_descriptors
class HidTest(unittest.TestCase):
def test_keyboard_example(self):
report_desc = hid_descriptors.ReportDescriptor(
hid_descriptors.UsagePage(0x01), # Generic Desktop
hid_descriptors.Usage(0x06), # Keyboard
hid_descriptors.Collection(
hid_constants.CollectionType.APPLICATION,
hid_descriptors.UsagePage(0x07), # Key Codes
hid_descriptors.UsageMinimum(224),
hid_descriptors.UsageMaximum(231),
hid_descriptors.LogicalMinimum(0, force_length=1),
hid_descriptors.LogicalMaximum(1),
hid_descriptors.ReportSize(1),
hid_descriptors.ReportCount(8),
hid_descriptors.Input(hid_descriptors.Data,
hid_descriptors.Variable,
hid_descriptors.Absolute),
hid_descriptors.ReportCount(1),
hid_descriptors.ReportSize(8),
hid_descriptors.Input(hid_descriptors.Constant),
hid_descriptors.ReportCount(5),
hid_descriptors.ReportSize(1),
hid_descriptors.UsagePage(0x08), # LEDs
hid_descriptors.UsageMinimum(1),
hid_descriptors.UsageMaximum(5),
hid_descriptors.Output(hid_descriptors.Data,
hid_descriptors.Variable,
hid_descriptors.Absolute),
hid_descriptors.ReportCount(1),
hid_descriptors.ReportSize(3),
hid_descriptors.Output(hid_descriptors.Constant),
hid_descriptors.ReportCount(6),
hid_descriptors.ReportSize(8),
hid_descriptors.LogicalMinimum(0, force_length=1),
hid_descriptors.LogicalMaximum(101),
hid_descriptors.UsagePage(0x07), # Key Codes
hid_descriptors.UsageMinimum(0, force_length=1),
hid_descriptors.UsageMaximum(101),
hid_descriptors.Input(hid_descriptors.Data, hid_descriptors.Array)
)
)
expected = ''.join(chr(x) for x in [
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29,
0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05,
0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03,
0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05,
0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0
])
self.assertEquals(report_desc, expected)
def test_mouse_example(self):
report_desc = hid_descriptors.ReportDescriptor(
hid_descriptors.UsagePage(0x01), # Generic Desktop
hid_descriptors.Usage(0x02), # Mouse
hid_descriptors.Collection(
hid_constants.CollectionType.APPLICATION,
hid_descriptors.Usage(0x01), # Pointer
hid_descriptors.Collection(
hid_constants.CollectionType.PHYSICAL,
hid_descriptors.UsagePage(0x09), # Buttons
hid_descriptors.UsageMinimum(1),
hid_descriptors.UsageMaximum(3),
hid_descriptors.LogicalMinimum(0, force_length=1),
hid_descriptors.LogicalMaximum(1),
hid_descriptors.ReportCount(3),
hid_descriptors.ReportSize(1),
hid_descriptors.Input(hid_descriptors.Data,
hid_descriptors.Variable,
hid_descriptors.Absolute),
hid_descriptors.ReportCount(1),
hid_descriptors.ReportSize(5),
hid_descriptors.Input(hid_descriptors.Constant),
hid_descriptors.UsagePage(0x01), # Generic Desktop
hid_descriptors.Usage(0x30), # X
hid_descriptors.Usage(0x31), # Y
hid_descriptors.LogicalMinimum(0x81), # -127
hid_descriptors.LogicalMaximum(127),
hid_descriptors.ReportSize(8),
hid_descriptors.ReportCount(2),
hid_descriptors.Input(hid_descriptors.Data,
hid_descriptors.Variable,
hid_descriptors.Relative)
)
)
)
expected = ''.join(chr(x) for x in [
0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, 0x05, 0x09,
0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30,
0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06,
0xC0, 0xC0
])
self.assertEquals(report_desc, expected)
def test_tag(self):
self.assertEquals(hid_descriptors.Push(), '\xa4')
def test_2byte_tag(self):
self.assertEquals(hid_descriptors.LogicalMaximum(0xFF00), '\x26\x00\xFF')
def test_4byte_tag(self):
self.assertEquals(hid_descriptors.LogicalMaximum(0xFF884400),
'\x27\x00\x44\x88\xFF')
def test_long_tag(self):
with self.assertRaises(NotImplementedError):
hid_descriptors.LogicalMaximum(0xFFFFFFFFFFFFFFFF)
if __name__ == '__main__':
unittest.main()
......@@ -9,6 +9,7 @@ Classes to represent and generate USB descriptors.
import struct
import hid_constants
import usb_constants
......@@ -374,7 +375,8 @@ class HidDescriptor(Descriptor):
.format(typ, length) for typ, length in self._descriptors))
HidDescriptor.AddComputedField('bLength', 'B', 'struct_size')
HidDescriptor.AddFixedField('bDescriptorType', 'B', 33)
HidDescriptor.AddFixedField('bDescriptorType', 'B',
hid_constants.DescriptorType.HID)
HidDescriptor.AddField('bcdHID', 'H', default=0x0111, str_fmt='0x{:04X}')
HidDescriptor.AddField('bCountryCode', 'B', default=0)
HidDescriptor.AddComputedField('bNumDescriptors', 'B', 'num_descriptors')
......@@ -5,6 +5,7 @@
import unittest
import hid_constants
import usb_descriptors
......@@ -195,15 +196,15 @@ class TestUsbDescriptors(unittest.TestCase):
def test_encode_hid_descriptor(self):
hid_desc = usb_descriptors.HidDescriptor()
hid_desc.AddDescriptor(0x22, 0x80)
hid_desc.AddDescriptor(0x23, 0x60)
hid_desc.AddDescriptor(hid_constants.DescriptorType.REPORT, 0x80)
hid_desc.AddDescriptor(hid_constants.DescriptorType.PHYSICAL, 0x60)
encoded_desc = '\x0C\x21\x11\x01\x00\x02\x22\x80\x00\x23\x60\x00'
self.assertEquals(hid_desc.Encode(), encoded_desc)
def test_print_hid_descriptor(self):
hid_desc = usb_descriptors.HidDescriptor()
hid_desc.AddDescriptor(0x22, 0x80)
hid_desc.AddDescriptor(0x23, 0x60)
hid_desc.AddDescriptor(hid_constants.DescriptorType.REPORT, 0x80)
hid_desc.AddDescriptor(hid_constants.DescriptorType.PHYSICAL, 0x60)
string = str(hid_desc)
self.assertIn('0x22', string)
self.assertIn('0x23', string)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment