summaryrefslogtreecommitdiffstats
path: root/tools/usb_gadget
diff options
context:
space:
mode:
authorreillyg@chromium.org <reillyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-24 16:45:03 +0000
committerreillyg@chromium.org <reillyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-24 16:45:03 +0000
commit3998309c7b0814eefcc33b585e8c1ae0753d61da (patch)
tree204e5f47759d4a096434f7cee29cf53f530ad7e6 /tools/usb_gadget
parent1ab15fe15d73fa8fa933d1314da5922878ef58a2 (diff)
downloadchromium_src-3998309c7b0814eefcc33b585e8c1ae0753d61da.zip
chromium_src-3998309c7b0814eefcc33b585e8c1ae0753d61da.tar.gz
chromium_src-3998309c7b0814eefcc33b585e8c1ae0753d61da.tar.bz2
[usb_gadget p04] Add a HID keyboard gadget.
An example HID gadget implementing the standard HID keyboard protocol. BUG=396682 R=rockot@chromium.org,rpaquay@chromium.org,kalman@chromium.org Review URL: https://codereview.chromium.org/418583003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@285298 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/usb_gadget')
-rwxr-xr-xtools/usb_gadget/hid_descriptors_test.py43
-rw-r--r--tools/usb_gadget/keyboard_gadget.py161
-rwxr-xr-xtools/usb_gadget/keyboard_gadget_test.py63
-rw-r--r--tools/usb_gadget/usb_constants.py5
4 files changed, 231 insertions, 41 deletions
diff --git a/tools/usb_gadget/hid_descriptors_test.py b/tools/usb_gadget/hid_descriptors_test.py
index d2313ca..ce3f0d0 100755
--- a/tools/usb_gadget/hid_descriptors_test.py
+++ b/tools/usb_gadget/hid_descriptors_test.py
@@ -7,51 +7,12 @@ import unittest
import hid_constants
import hid_descriptors
+import keyboard_gadget
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,
@@ -60,7 +21,7 @@ class HidTest(unittest.TestCase):
0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05,
0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0
])
- self.assertEquals(report_desc, expected)
+ self.assertEquals(keyboard_gadget.KeyboardFeature.REPORT_DESC, expected)
def test_mouse_example(self):
report_desc = hid_descriptors.ReportDescriptor(
diff --git a/tools/usb_gadget/keyboard_gadget.py b/tools/usb_gadget/keyboard_gadget.py
new file mode 100644
index 0000000..ce37b44
--- /dev/null
+++ b/tools/usb_gadget/keyboard_gadget.py
@@ -0,0 +1,161 @@
+# 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.
+
+"""Implementation of a USB HID keyboard.
+
+Two classes are provided by this module. The KeyboardFeature class implements
+the core functionality of a HID keyboard and can be included in any HID gadget.
+The KeyboardGadget class implements an example keyboard gadget.
+"""
+
+import struct
+
+import hid_constants
+import hid_descriptors
+import hid_gadget
+import usb_constants
+
+
+class KeyboardFeature(hid_gadget.HidFeature):
+ """HID feature implementation for a keyboard.
+
+ REPORT_DESC provides an example HID report descriptor for a device including
+ this functionality.
+ """
+
+ 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)
+ )
+ )
+
+ def __init__(self):
+ super(KeyboardFeature, self).__init__()
+ self._modifiers = 0
+ self._keys = [0, 0, 0, 0, 0, 0]
+ self._leds = 0
+
+ def ModifierDown(self, modifier):
+ self._modifiers |= modifier
+ if self.IsConnected():
+ self.SendReport(self.GetInputReport())
+
+ def ModifierUp(self, modifier):
+ self._modifiers &= ~modifier
+ if self.IsConnected():
+ self.SendReport(self.GetInputReport())
+
+ def KeyDown(self, keycode):
+ free = self._keys.index(0)
+ self._keys[free] = keycode
+ if self.IsConnected():
+ self.SendReport(self.GetInputReport())
+
+ def KeyUp(self, keycode):
+ free = self._keys.index(keycode)
+ self._keys[free] = 0
+ if self.IsConnected():
+ self.SendReport(self.GetInputReport())
+
+ def GetInputReport(self):
+ """Construct an input report.
+
+ See Device Class Definition for Human Interface Devices (HID) Version 1.11
+ Appendix B.1.
+
+ Returns:
+ A packed input report.
+ """
+ return struct.pack('BBBBBBBB', self._modifiers, 0, *self._keys)
+
+ def GetOutputReport(self):
+ """Construct an output report.
+
+ See Device Class Definition for Human Interface Devices (HID) Version 1.11
+ Appendix B.1.
+
+ Returns:
+ A packed input report.
+ """
+ return struct.pack('B', self._leds)
+
+ def SetOutputReport(self, data):
+ """Handle an output report.
+
+ See Device Class Definition for Human Interface Devices (HID) Version 1.11
+ Appendix B.1.
+
+ Args:
+ data: Report data.
+
+ Returns:
+ True on success, None to stall the pipe.
+ """
+ if len(data) >= 1:
+ self._leds, = struct.unpack('B', data)
+ return True
+
+
+class KeyboardGadget(hid_gadget.HidGadget):
+ """USB gadget implementation of a HID keyboard."""
+
+ def __init__(self, vendor_id=0x18D1, product_id=0xFF02):
+ self._feature = KeyboardFeature()
+ super(KeyboardGadget, self).__init__(
+ report_desc=KeyboardFeature.REPORT_DESC,
+ features={0: self._feature},
+ packet_size=8,
+ interval_ms=1,
+ out_endpoint=False,
+ vendor_id=usb_constants.VendorID.GOOGLE,
+ product_id=usb_constants.ProductID.GOOGLE_KEYBOARD_GADGET,
+ device_version=0x0100)
+ self.AddStringDescriptor(1, 'Google Inc.')
+ self.AddStringDescriptor(2, 'Keyboard Gadget')
+
+ def ModifierDown(self, modifier):
+ self._feature.ModifierDown(modifier)
+
+ def ModifierUp(self, modifier):
+ self._feature.ModifierUp(modifier)
+
+ def KeyDown(self, keycode):
+ self._feature.KeyDown(keycode)
+
+ def KeyUp(self, keycode):
+ self._feature.KeyUp(keycode)
diff --git a/tools/usb_gadget/keyboard_gadget_test.py b/tools/usb_gadget/keyboard_gadget_test.py
new file mode 100755
index 0000000..d3b085f
--- /dev/null
+++ b/tools/usb_gadget/keyboard_gadget_test.py
@@ -0,0 +1,63 @@
+#!/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 mock
+
+import hid_constants
+import keyboard_gadget
+import usb_constants
+
+
+class KeyboardGadgetTest(unittest.TestCase):
+
+ def test_key_press(self):
+ g = keyboard_gadget.KeyboardGadget()
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.FULL)
+ g.KeyDown(0x04)
+ self.assertEqual(g.ControlRead(0xA1, 1, 0x0100, 0, 8),
+ '\x00\x00\x04\x00\x00\x00\x00\x00')
+ g.KeyUp(0x04)
+ self.assertEqual(g.ControlRead(0xA1, 1, 0x0100, 0, 8),
+ '\x00\x00\x00\x00\x00\x00\x00\x00')
+ chip.SendPacket.assert_has_calls([
+ mock.call(0x81, '\x00\x00\x04\x00\x00\x00\x00\x00'),
+ mock.call(0x81, '\x00\x00\x00\x00\x00\x00\x00\x00'),
+ ])
+
+ def test_key_press_with_modifier(self):
+ g = keyboard_gadget.KeyboardGadget()
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.FULL)
+ g.ModifierDown(hid_constants.ModifierKey.L_SHIFT)
+ g.KeyDown(0x04)
+ g.KeyDown(0x05)
+ g.KeyUp(0x04)
+ g.KeyUp(0x05)
+ g.ModifierUp(hid_constants.ModifierKey.L_SHIFT)
+ chip.SendPacket.assert_has_calls([
+ mock.call(0x81, '\x02\x00\x00\x00\x00\x00\x00\x00'),
+ mock.call(0x81, '\x02\x00\x04\x00\x00\x00\x00\x00'),
+ mock.call(0x81, '\x02\x00\x04\x05\x00\x00\x00\x00'),
+ mock.call(0x81, '\x02\x00\x00\x05\x00\x00\x00\x00'),
+ mock.call(0x81, '\x02\x00\x00\x00\x00\x00\x00\x00'),
+ mock.call(0x81, '\x00\x00\x00\x00\x00\x00\x00\x00'),
+ ])
+
+ def test_set_leds(self):
+ g = keyboard_gadget.KeyboardGadget()
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.FULL)
+ self.assertEqual(g.ControlRead(0xA1, 1, 0x0200, 0, 8), '\x00')
+ self.assertTrue(g.ControlWrite(0x21, 9, 0x0200, 0, '\x01'))
+ self.assertEqual(g.ControlRead(0xA1, 1, 0x0200, 0, 8), '\x01')
+ g.ReceivePacket(0x01, '\x03')
+ self.assertFalse(chip.HaltEndpoint.called)
+ self.assertEqual(g.ControlRead(0xA1, 1, 0x0200, 0, 8), '\x03')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/usb_gadget/usb_constants.py b/tools/usb_gadget/usb_constants.py
index fee7fc1..106b37c 100644
--- a/tools/usb_gadget/usb_constants.py
+++ b/tools/usb_gadget/usb_constants.py
@@ -159,3 +159,8 @@ class Speed(object):
class VendorID(object):
GOOGLE = 0x18D1
+
+
+class ProductID(object):
+ # TODO(reillyg): Get officially assigned IDs for these devices.
+ GOOGLE_KEYBOARD_GADGET = 0x2001