summaryrefslogtreecommitdiffstats
path: root/tools/usb_gadget
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2015-07-06 15:15:44 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-06 22:17:19 +0000
commita942b49af49181ff218d4a2c345be96464c23176 (patch)
tree0cfeaf1879aa07ff7bcfd479a98e471f5fc4422d /tools/usb_gadget
parentbbb9e2f6486cccadde4dc2fa077af8f694105eaa (diff)
downloadchromium_src-a942b49af49181ff218d4a2c345be96464c23176.zip
chromium_src-a942b49af49181ff218d4a2c345be96464c23176.tar.gz
chromium_src-a942b49af49181ff218d4a2c345be96464c23176.tar.bz2
usb_gadget: Add infrastructure for creating a composite devices.
This patch follows the example of the HID gadget code to allow a device to be constructed by composing multiple "feature" modules into a single device. As an example of this new capability the HID and echo gadgets have been updated to extend CompositeFeature with simple wrappers to create single-function gadgets and a new composite echo gadget combines the two together into a single device. BUG= Review URL: https://codereview.chromium.org/1225623003 Cr-Commit-Position: refs/heads/master@{#337492}
Diffstat (limited to 'tools/usb_gadget')
-rw-r--r--tools/usb_gadget/__main__.py2
-rw-r--r--tools/usb_gadget/composite_echo_gadget.py61
-rw-r--r--tools/usb_gadget/composite_gadget.py277
-rw-r--r--tools/usb_gadget/echo_gadget.py344
-rwxr-xr-xtools/usb_gadget/echo_gadget_test.py22
-rw-r--r--tools/usb_gadget/gadget.py34
-rw-r--r--tools/usb_gadget/hid_gadget.py135
-rwxr-xr-xtools/usb_gadget/hid_gadget_test.py27
-rw-r--r--tools/usb_gadget/keyboard_gadget.py2
-rwxr-xr-xtools/usb_gadget/keyboard_gadget_test.py1
-rw-r--r--tools/usb_gadget/usb_constants.py1
-rw-r--r--tools/usb_gadget/usb_gadget.gyp2
12 files changed, 682 insertions, 226 deletions
diff --git a/tools/usb_gadget/__main__.py b/tools/usb_gadget/__main__.py
index 0391a34..03341ea 100644
--- a/tools/usb_gadget/__main__.py
+++ b/tools/usb_gadget/__main__.py
@@ -9,6 +9,7 @@ import argparse
import netifaces
from tornado import ioloop
+import composite_echo_gadget
import echo_gadget
import hid_echo_gadget
import keyboard_gadget
@@ -50,6 +51,7 @@ def main():
server.chip = linux_gadgetfs.LinuxGadgetfs(server.hardware)
server.SwitchGadget(server.default)
+ composite_echo_gadget.RegisterHandlers()
echo_gadget.RegisterHandlers()
hid_echo_gadget.RegisterHandlers()
keyboard_gadget.RegisterHandlers()
diff --git a/tools/usb_gadget/composite_echo_gadget.py b/tools/usb_gadget/composite_echo_gadget.py
new file mode 100644
index 0000000..85b3845
--- /dev/null
+++ b/tools/usb_gadget/composite_echo_gadget.py
@@ -0,0 +1,61 @@
+# Copyright 2015 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 uuid
+
+import composite_gadget
+import echo_gadget
+import hid_echo_gadget
+import hid_gadget
+import usb_constants
+import usb_descriptors
+
+
+class CompositeEchoGadget(composite_gadget.CompositeGadget):
+
+ def __init__(self):
+ device_desc = usb_descriptors.DeviceDescriptor(
+ idVendor=usb_constants.VendorID.GOOGLE,
+ idProduct=usb_constants.ProductID.GOOGLE_COMPOSITE_ECHO_GADGET,
+ bcdUSB=0x0200,
+ iManufacturer=1,
+ iProduct=2,
+ iSerialNumber=3,
+ bcdDevice=0x0100)
+
+ echo_feature = echo_gadget.EchoCompositeFeature(
+ endpoints=[(0, 5, 0x81, 0x01), (1, 6, 0x82, 0x02), (2, 7, 0x83, 0x03)])
+
+ hid_echo_feature = hid_echo_gadget.EchoFeature()
+ hid_feature = hid_gadget.HidCompositeFeature(
+ report_desc=hid_echo_gadget.EchoFeature.REPORT_DESC,
+ features={0: hid_echo_feature},
+ interface_number=3,
+ interface_string=4,
+ in_endpoint=0x84, out_endpoint=0x04)
+
+ super(CompositeEchoGadget, self).__init__(
+ device_desc, [echo_feature, hid_feature])
+ self.AddStringDescriptor(1, 'Google Inc.')
+ self.AddStringDescriptor(2, 'Echo Gadget')
+ self.AddStringDescriptor(3, '{:06X}'.format(uuid.getnode()))
+ self.AddStringDescriptor(4, 'HID Echo')
+ self.AddStringDescriptor(5, 'Interrupt Echo')
+ self.AddStringDescriptor(6, 'Bulk Echo')
+ self.AddStringDescriptor(7, 'Isochronous Echo')
+
+def RegisterHandlers():
+ """Registers web request handlers with the application server."""
+
+ import server
+ from tornado import web
+
+ class WebConfigureHandler(web.RequestHandler):
+
+ def post(self):
+ server.SwitchGadget(CompositeEchoGadget())
+
+ server.app.add_handlers('.*$', [
+ (r'/composite_echo/configure', WebConfigureHandler),
+ ])
diff --git a/tools/usb_gadget/composite_gadget.py b/tools/usb_gadget/composite_gadget.py
new file mode 100644
index 0000000..ebf2ea3
--- /dev/null
+++ b/tools/usb_gadget/composite_gadget.py
@@ -0,0 +1,277 @@
+# 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.
+
+"""A composite USB gadget is built from multiple USB features.
+"""
+
+import gadget
+import usb_constants
+import usb_descriptors
+
+
+class CompositeGadget(gadget.Gadget):
+ """Basic functionality for a composite USB device.
+
+ Composes multiple USB features into a single device.
+ """
+
+ def __init__(self, device_desc, features):
+ """Create a USB gadget device.
+
+ Args:
+ device_desc: USB device descriptor.
+ features: USB device features.
+ """
+ # dicts mapping interface numbers to features for FS and HS configurations
+ self._fs_interface_feature_map = {}
+ self._hs_interface_feature_map = {}
+
+ fs_config_desc = usb_descriptors.ConfigurationDescriptor(
+ bmAttributes=0x80,
+ MaxPower=50)
+ hs_config_desc = usb_descriptors.ConfigurationDescriptor(
+ bmAttributes=0x80,
+ MaxPower=50)
+ for feature in features:
+ for fs_interface in feature.GetFullSpeedInterfaces():
+ fs_config_desc.AddInterface(fs_interface)
+ self._fs_interface_feature_map[fs_interface.bInterfaceNumber] = feature
+ for hs_interface in feature.GetHighSpeedInterfaces():
+ hs_config_desc.AddInterface(hs_interface)
+ self._hs_interface_feature_map[hs_interface.bInterfaceNumber] = feature
+
+ super(CompositeGadget, self).__init__(
+ device_desc, fs_config_desc, hs_config_desc)
+ self._features = features
+
+ def Connected(self, chip, speed):
+ super(CompositeGadget, self).Connected(chip, speed)
+ for feature in self._features:
+ feature.Connected(self)
+
+ def Disconnected(self):
+ super(CompositeGadget, self).Disconnected()
+ for feature in self._features:
+ feature.Disconnected()
+
+ def _GetInterfaceFeatureMap(self):
+ if self.GetSpeed() == usb_constants.Speed.FULL:
+ return self._fs_interface_feature_map
+ elif self.GetSpeed() == usb_constants.Speed.HIGH:
+ return self._hs_interface_feature_map
+ else:
+ raise RuntimeError('Device is not connected.')
+
+ def ReceivePacket(self, endpoint, data):
+ interface = self.GetInterfaceForEndpoint(endpoint)
+ feature = self._GetInterfaceFeatureMap()[interface]
+ feature.ReceivePacket(endpoint, data)
+
+ def _GetFeatureForIndex(self, recipient, index):
+ interface = None
+ if recipient == usb_constants.Recipient.INTERFACE:
+ interface = index
+ elif recipient == usb_constants.Recipient.ENDPOINT:
+ interface = self.GetInterfaceForEndpoint(index)
+
+ if interface is not None:
+ return self._GetInterfaceFeatureMap().get(interface)
+ return None
+
+ def StandardControlRead(self, recipient, request, value, index, length):
+ response = super(CompositeGadget, self).StandardControlRead(
+ recipient, request, value, index, length)
+ if response is not None:
+ return response
+
+ feature = self._GetFeatureForIndex(recipient, index)
+ if feature:
+ return feature.StandardControlRead(
+ recipient, request, value, index, length)
+
+ def StandardControlWrite(self, recipient, request, value, index, data):
+ response = super(CompositeGadget, self).StandardControlWrite(
+ recipient, request, value, index, data)
+ if response is not None:
+ return response
+
+ feature = self._GetFeatureForIndex(recipient, index)
+ if feature:
+ return feature.StandardControlWrite(
+ recipient, request, value, index, data)
+
+ def ClassControlRead(self, recipient, request, value, index, length):
+ response = super(CompositeGadget, self).ClassControlRead(
+ recipient, request, value, index, length)
+ if response is not None:
+ return response
+
+ feature = self._GetFeatureForIndex(recipient, index)
+ if feature:
+ return feature.ClassControlRead(recipient, request, value, index, length)
+
+ def ClassControlWrite(self, recipient, request, value, index, data):
+ response = super(CompositeGadget, self).ClassControlWrite(
+ recipient, request, value, index, data)
+ if response is not None:
+ return response
+
+ feature = self._GetFeatureForIndex(recipient, index)
+ if feature:
+ return feature.ClassControlWrite(recipient, request, value, index, data)
+
+ def VendorControlRead(self, recipient, request, value, index, length):
+ response = super(CompositeGadget, self).VendorControlRead(
+ recipient, request, value, index, length)
+ if response is not None:
+ return response
+
+ feature = self._GetFeatureForIndex(recipient, index)
+ if feature:
+ return feature.VendorControlRead(recipient, request, value, index, length)
+
+ def VendorControlWrite(self, recipient, request, value, index, data):
+ response = super(CompositeGadget, self).VendorControlWrite(
+ recipient, request, value, index, data)
+ if response is not None:
+ return response
+
+ feature = self._GetFeatureForIndex(recipient, index)
+ if feature:
+ return feature.VendorControlWrite(recipient, request, value, index, data)
+
+
+class CompositeFeature(object):
+ def __init__(self, fs_interface_descs, hs_interface_descs):
+ self._gadget = None
+ self._fs_interface_descs = fs_interface_descs
+ self._hs_interface_descs = hs_interface_descs
+
+ def GetFullSpeedInterfaces(self):
+ return self._fs_interface_descs
+
+ def GetHighSpeedInterfaces(self):
+ return self._hs_interface_descs
+
+ def Connected(self, my_gadget):
+ self._gadget = my_gadget
+
+ def Disconnected(self):
+ self._gadget = None
+
+ def IsConnected(self):
+ return self._gadget is not None
+
+ def SendPacket(self, endpoint, data):
+ if self._gadget is None:
+ raise RuntimeError('Device is not connected.')
+ self._gadget.SendPacket(endpoint, data)
+
+ def HaltEndpoint(self, endpoint):
+ if self._gadget is None:
+ raise RuntimeError('Device is not connected.')
+ self._gadget.HaltEndpoint(endpoint)
+
+ def GetDescriptor(self, recipient, typ, index, lang, length):
+ _ = recipient, typ, index, lang, length
+ return None
+
+ def StandardControlRead(self, recipient, request, value, index, length):
+ """Handle standard USB control transfers.
+
+ Args:
+ recipient: Request recipient (interface or endpoint)
+ request: bRequest field of the setup packet.
+ value: wValue field of the setup packet.
+ index: wIndex field of the setup packet.
+ length: Maximum amount of data the host expects the device to return.
+
+ Returns:
+ A buffer to return to the USB host with len <= length on success or
+ None to stall the pipe.
+ """
+ _ = recipient, request, value, index, length
+ return None
+
+ def ClassControlRead(self, recipient, request, value, index, length):
+ """Handle class-specific control transfers.
+
+ Args:
+ recipient: Request recipient (interface or endpoint)
+ request: bRequest field of the setup packet.
+ value: wValue field of the setup packet.
+ index: wIndex field of the setup packet.
+ length: Maximum amount of data the host expects the device to return.
+
+ Returns:
+ A buffer to return to the USB host with len <= length on success or
+ None to stall the pipe.
+ """
+ _ = recipient, request, value, index, length
+ return None
+
+ def VendorControlRead(self, recipient, request, value, index, length):
+ """Handle vendor-specific control transfers.
+
+ Args:
+ recipient: Request recipient (interface or endpoint)
+ request: bRequest field of the setup packet.
+ value: wValue field of the setup packet.
+ index: wIndex field of the setup packet.
+ length: Maximum amount of data the host expects the device to return.
+
+ Returns:
+ A buffer to return to the USB host with len <= length on success or
+ None to stall the pipe.
+ """
+ _ = recipient, request, value, index, length
+ return None
+
+ def StandardControlWrite(self, recipient, request, value, index, data):
+ """Handle standard USB control transfers.
+
+ Args:
+ recipient: Request recipient (interface or endpoint)
+ request: bRequest field of the setup packet.
+ value: wValue field of the setup packet.
+ index: wIndex field of the setup packet.
+ data: Data stage of the request.
+
+ Returns:
+ True on success, None to stall the pipe.
+ """
+ _ = recipient, request, value, index, data
+ return None
+
+ def ClassControlWrite(self, recipient, request, value, index, data):
+ """Handle class-specific control transfers.
+
+ Args:
+ recipient: Request recipient (interface or endpoint)
+ request: bRequest field of the setup packet.
+ value: wValue field of the setup packet.
+ index: wIndex field of the setup packet.
+ data: Data stage of the request.
+
+ Returns:
+ True on success, None to stall the pipe.
+ """
+ _ = recipient, request, value, index, data
+ return None
+
+ def VendorControlWrite(self, recipient, request, value, index, data):
+ """Handle vendor-specific control transfers.
+
+ Args:
+ recipient: Request recipient (interface or endpoint)
+ request: bRequest field of the setup packet.
+ value: wValue field of the setup packet.
+ index: wIndex field of the setup packet.
+ data: Data stage of the request.
+
+ Returns:
+ True on success, None to stall the pipe.
+ """
+ _ = recipient, request, value, index, data
+ return None
diff --git a/tools/usb_gadget/echo_gadget.py b/tools/usb_gadget/echo_gadget.py
index b260381..98a6850 100644
--- a/tools/usb_gadget/echo_gadget.py
+++ b/tools/usb_gadget/echo_gadget.py
@@ -7,16 +7,186 @@
This gadget has pairs of IN/OUT endpoints that echo packets back to the host.
"""
-import math
-import struct
import uuid
-import gadget
+import composite_gadget
import usb_constants
import usb_descriptors
-class EchoGadget(gadget.Gadget):
+class EchoCompositeFeature(composite_gadget.CompositeFeature):
+ """Composite device feature that echos data back to the host.
+ """
+
+ def __init__(self, endpoints):
+ """Create an echo gadget.
+ """
+ fs_interfaces = []
+ hs_interfaces = []
+
+ if len(endpoints) >= 1:
+ iface_num, iface_string, in_endpoint, out_endpoint = endpoints[0]
+ fs_intr_interface_desc = usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string,
+ )
+ fs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=out_endpoint,
+ bmAttributes=usb_constants.TransferType.INTERRUPT,
+ wMaxPacketSize=64,
+ bInterval=1 # 1ms
+ ))
+ fs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=in_endpoint,
+ bmAttributes=usb_constants.TransferType.INTERRUPT,
+ wMaxPacketSize=64,
+ bInterval=1 # 1ms
+ ))
+ fs_interfaces.append(fs_intr_interface_desc)
+
+ hs_intr_interface_desc = usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ )
+ hs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=out_endpoint,
+ bmAttributes=usb_constants.TransferType.INTERRUPT,
+ wMaxPacketSize=64,
+ bInterval=4 # 1ms
+ ))
+ hs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=in_endpoint,
+ bmAttributes=usb_constants.TransferType.INTERRUPT,
+ wMaxPacketSize=64,
+ bInterval=4 # 1ms
+ ))
+ hs_interfaces.append(hs_intr_interface_desc)
+
+ if len(endpoints) >= 2:
+ iface_num, iface_string, in_endpoint, out_endpoint = endpoints[1]
+ fs_bulk_interface_desc = usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ )
+ fs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=out_endpoint,
+ bmAttributes=usb_constants.TransferType.BULK,
+ wMaxPacketSize=64,
+ bInterval=0
+ ))
+ fs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=in_endpoint,
+ bmAttributes=usb_constants.TransferType.BULK,
+ wMaxPacketSize=64,
+ bInterval=0
+ ))
+ fs_interfaces.append(fs_bulk_interface_desc)
+
+ hs_bulk_interface_desc = usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ )
+ hs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=out_endpoint,
+ bmAttributes=usb_constants.TransferType.BULK,
+ wMaxPacketSize=512,
+ bInterval=0
+ ))
+ hs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=in_endpoint,
+ bmAttributes=usb_constants.TransferType.BULK,
+ wMaxPacketSize=512,
+ bInterval=0
+ ))
+ hs_interfaces.append(hs_bulk_interface_desc)
+
+ if len(endpoints) >= 3:
+ iface_num, iface_string, in_endpoint, out_endpoint = endpoints[2]
+ fs_interfaces.append(usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ ))
+ fs_isoc_interface_desc = usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bAlternateSetting=1,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ )
+ fs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=out_endpoint,
+ bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
+ wMaxPacketSize=1023,
+ bInterval=1 # 1ms
+ ))
+ fs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=in_endpoint,
+ bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
+ wMaxPacketSize=1023,
+ bInterval=1 # 1ms
+ ))
+ fs_interfaces.append(fs_isoc_interface_desc)
+
+ hs_interfaces.append(usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ ))
+ hs_isoc_interface_desc = usb_descriptors.InterfaceDescriptor(
+ bInterfaceNumber=iface_num,
+ bAlternateSetting=1,
+ bInterfaceClass=usb_constants.DeviceClass.VENDOR,
+ bInterfaceSubClass=0,
+ bInterfaceProtocol=0,
+ iInterface=iface_string
+ )
+ hs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=out_endpoint,
+ bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
+ wMaxPacketSize=512,
+ bInterval=4 # 1ms
+ ))
+ hs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
+ bEndpointAddress=in_endpoint,
+ bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
+ wMaxPacketSize=512,
+ bInterval=4 # 1ms
+ ))
+ hs_interfaces.append(hs_isoc_interface_desc)
+
+ super(EchoCompositeFeature, self).__init__(fs_interfaces, hs_interfaces)
+
+ def ReceivePacket(self, endpoint, data):
+ """Echo a packet back to the host.
+
+ Args:
+ endpoint: Incoming endpoint (must be an OUT pipe).
+ data: Packet data.
+ """
+ assert endpoint & usb_constants.Dir.IN == 0
+
+ self.SendPacket(endpoint | usb_constants.Dir.IN, data)
+
+
+class EchoGadget(composite_gadget.CompositeGadget):
"""Echo gadget.
"""
@@ -32,157 +202,10 @@ class EchoGadget(gadget.Gadget):
iSerialNumber=3,
bcdDevice=0x0100)
- fs_config_desc = usb_descriptors.ConfigurationDescriptor(
- bmAttributes=0x80,
- MaxPower=50)
- fs_intr_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=0,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=4,
- )
- fs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x01,
- bmAttributes=usb_constants.TransferType.INTERRUPT,
- wMaxPacketSize=64,
- bInterval=1 # 1ms
- ))
- fs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x81,
- bmAttributes=usb_constants.TransferType.INTERRUPT,
- wMaxPacketSize=64,
- bInterval=1 # 1ms
- ))
- fs_config_desc.AddInterface(fs_intr_interface_desc)
-
- fs_bulk_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=1,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=5
- )
- fs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x02,
- bmAttributes=usb_constants.TransferType.BULK,
- wMaxPacketSize=64,
- bInterval=0
- ))
- fs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x82,
- bmAttributes=usb_constants.TransferType.BULK,
- wMaxPacketSize=64,
- bInterval=0
- ))
- fs_config_desc.AddInterface(fs_bulk_interface_desc)
-
- fs_config_desc.AddInterface(usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=2,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=6
- ))
- fs_isoc_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=2,
- bAlternateSetting=1,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=6
- )
- fs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x03,
- bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
- wMaxPacketSize=1023,
- bInterval=1 # 1ms
- ))
- fs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x83,
- bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
- wMaxPacketSize=1023,
- bInterval=1 # 1ms
- ))
- fs_config_desc.AddInterface(fs_isoc_interface_desc)
-
- hs_config_desc = usb_descriptors.ConfigurationDescriptor(
- bmAttributes=0x80,
- MaxPower=50)
-
- hs_intr_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=0,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=4
- )
- hs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x01,
- bmAttributes=usb_constants.TransferType.INTERRUPT,
- wMaxPacketSize=64,
- bInterval=4 # 1ms
- ))
- hs_intr_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x81,
- bmAttributes=usb_constants.TransferType.INTERRUPT,
- wMaxPacketSize=64,
- bInterval=4 # 1ms
- ))
- hs_config_desc.AddInterface(hs_intr_interface_desc)
-
- hs_bulk_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=1,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=5
- )
- hs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x02,
- bmAttributes=usb_constants.TransferType.BULK,
- wMaxPacketSize=512,
- bInterval=0
- ))
- hs_bulk_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x82,
- bmAttributes=usb_constants.TransferType.BULK,
- wMaxPacketSize=512,
- bInterval=0
- ))
- hs_config_desc.AddInterface(hs_bulk_interface_desc)
-
- hs_config_desc.AddInterface(usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=2,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=6
- ))
- hs_isoc_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=2,
- bAlternateSetting=1,
- bInterfaceClass=usb_constants.DeviceClass.VENDOR,
- bInterfaceSubClass=0,
- bInterfaceProtocol=0,
- iInterface=6
- )
- hs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x03,
- bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
- wMaxPacketSize=1024,
- bInterval=4 # 1ms
- ))
- hs_isoc_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x83,
- bmAttributes=usb_constants.TransferType.ISOCHRONOUS,
- wMaxPacketSize=1024,
- bInterval=4 # 1ms
- ))
- hs_config_desc.AddInterface(hs_isoc_interface_desc)
-
- super(EchoGadget, self).__init__(
- device_desc, fs_config_desc, hs_config_desc)
+ feature = EchoCompositeFeature(
+ endpoints=[(0, 4, 0x81, 0x01), (1, 5, 0x82, 0x02), (2, 6, 0x83, 0x03)])
+
+ super(EchoGadget, self).__init__(device_desc, [feature])
self.AddStringDescriptor(1, 'Google Inc.')
self.AddStringDescriptor(2, 'Echo Gadget')
self.AddStringDescriptor(3, '{:06X}'.format(uuid.getnode()))
@@ -190,17 +213,6 @@ class EchoGadget(gadget.Gadget):
self.AddStringDescriptor(5, 'Bulk Echo')
self.AddStringDescriptor(6, 'Isochronous Echo')
- def ReceivePacket(self, endpoint, data):
- """Echo a packet back to the host.
-
- Args:
- endpoint: Incoming endpoint (must be an OUT pipe).
- data: Packet data.
- """
- assert endpoint & usb_constants.Dir.IN == 0
-
- self.SendPacket(endpoint | usb_constants.Dir.IN, data)
-
def RegisterHandlers():
"""Registers web request handlers with the application server."""
diff --git a/tools/usb_gadget/echo_gadget_test.py b/tools/usb_gadget/echo_gadget_test.py
new file mode 100755
index 0000000..23370e7
--- /dev/null
+++ b/tools/usb_gadget/echo_gadget_test.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+# Copyright 2015 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 echo_gadget
+import usb_constants
+
+
+class HidGadgetTest(unittest.TestCase):
+
+ def test_bulk_echo(self):
+ g = echo_gadget.EchoGadget()
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
+ g.SetConfiguration(1)
+ g.ReceivePacket(0x02, 'Hello world!')
+ chip.SendPacket.assert_called_once_with(0x82, 'Hello world!')
diff --git a/tools/usb_gadget/gadget.py b/tools/usb_gadget/gadget.py
index fb48a2c..8e8a25d 100644
--- a/tools/usb_gadget/gadget.py
+++ b/tools/usb_gadget/gadget.py
@@ -34,6 +34,8 @@ class Gadget(object):
self._strings = {}
# dict mapping interface numbers to a set of endpoint addresses
self._active_endpoints = {}
+ # dict mapping endpoint addresses to interfaces
+ self._endpoint_interface_map = {}
def GetDeviceDescriptor(self):
return self._device_desc
@@ -89,6 +91,7 @@ class Gadget(object):
self._speed = usb_constants.Speed.UNKNOWN
self._chip = None
self._active_endpoints.clear()
+ self._endpoint_interface_map.clear()
def IsConnected(self):
return self._chip is not None
@@ -197,16 +200,17 @@ class Gadget(object):
A buffer to return to the USB host with len <= length on success or
None to stall the pipe.
"""
- if request == usb_constants.Request.GET_DESCRIPTOR:
- desc_type = value >> 8
- desc_index = value & 0xff
- desc_lang = index
+ if recipient == usb_constants.Recipient.DEVICE:
+ if request == usb_constants.Request.GET_DESCRIPTOR:
+ desc_type = value >> 8
+ desc_index = value & 0xff
+ desc_lang = index
- print 'GetDescriptor(recipient={}, type={}, index={}, lang={})'.format(
- recipient, desc_type, desc_index, desc_lang)
+ print 'GetDescriptor(recipient={}, type={}, index={}, lang={})'.format(
+ recipient, desc_type, desc_index, desc_lang)
- return self.GetDescriptor(recipient, desc_type, desc_index, desc_lang,
- length)
+ return self.GetDescriptor(recipient, desc_type, desc_index, desc_lang,
+ length)
def GetDescriptor(self, recipient, typ, index, lang, length):
"""Handle a standard GET_DESCRIPTOR request.
@@ -223,9 +227,8 @@ class Gadget(object):
Returns:
The value of the descriptor or None to stall the pipe.
"""
- if recipient == usb_constants.Recipient.DEVICE:
- if typ == usb_constants.DescriptorType.STRING:
- return self.GetStringDescriptor(index, lang, length)
+ if typ == usb_constants.DescriptorType.STRING:
+ return self.GetStringDescriptor(index, lang, length)
def ClassControlRead(self, recipient, request, value, index, length):
"""Handle class-specific control transfers.
@@ -381,6 +384,7 @@ class Gadget(object):
for endpoint_addr in endpoint_addrs:
self._chip.StopEndpoint(endpoint_addr)
endpoint_addrs.clear()
+ self._endpoint_interface_map.clear();
if index == 0:
# SET_CONFIGRATION(0) puts the device into the Address state which
@@ -398,6 +402,8 @@ class Gadget(object):
for endpoint_desc in interface_desc.GetEndpoints():
self._chip.StartEndpoint(endpoint_desc)
endpoint_addrs.add(endpoint_desc.bEndpointAddress)
+ self._endpoint_interface_map[endpoint_desc.bEndpointAddress] = \
+ interface_desc.bInterfaceNumber
return True
def SetInterface(self, interface, alt_setting):
@@ -426,7 +432,13 @@ class Gadget(object):
endpoint_addrs = self._active_endpoints.setdefault(interface, set())
for endpoint_addr in endpoint_addrs:
self._chip.StopEndpoint(endpoint_addr)
+ del self._endpoint_interface_map[endpoint_addr]
for endpoint_desc in interface_desc.GetEndpoints():
self._chip.StartEndpoint(endpoint_desc)
endpoint_addrs.add(endpoint_desc.bEndpointAddress)
+ self._endpoint_interface_map[endpoint_desc.bEndpointAddress] = \
+ interface_desc.bInterfaceNumber
return True
+
+ def GetInterfaceForEndpoint(self, endpoint_addr):
+ return self._endpoint_interface_map.get(endpoint_addr)
diff --git a/tools/usb_gadget/hid_gadget.py b/tools/usb_gadget/hid_gadget.py
index 82e4482..48535f1 100644
--- a/tools/usb_gadget/hid_gadget.py
+++ b/tools/usb_gadget/hid_gadget.py
@@ -14,64 +14,50 @@ import math
import struct
import uuid
-import gadget
+import composite_gadget
import hid_constants
import usb_constants
import usb_descriptors
-class HidGadget(gadget.Gadget):
- """Generic HID gadget.
+class HidCompositeFeature(composite_gadget.CompositeFeature):
+ """Generic HID feature for a composite device.
"""
- def __init__(self, report_desc, features, vendor_id, product_id,
- packet_size=64, interval_ms=10, out_endpoint=True,
- device_version=0x0100):
- """Create a HID gadget.
+ def __init__(self, report_desc, features,
+ packet_size=64, interval_ms=10, interface_number=0,
+ interface_string=0,
+ in_endpoint=0x81, out_endpoint=0x01):
+ """Create a composite device feature implementing the HID protocol.
Args:
report_desc: HID report descriptor.
features: Map between Report IDs and HidFeature objects to handle them.
- vendor_id: Device Vendor ID.
- product_id: Device Product ID.
packet_size: Maximum interrupt packet size.
interval_ms: Interrupt transfer interval in milliseconds.
- out_endpoint: Should this device have an interrupt OUT endpoint?
- device_version: Device version number.
+ interface_number: Interface number for this feature (default 0).
+ in_endpoint: Endpoint number for the IN endpoint (defualt 0x81).
+ out_endpoint: Endpoint number for the OUT endpoint or None to disable
+ the endpoint (default 0x01).
Raises:
ValueError: If any of the parameters are out of range.
"""
- device_desc = usb_descriptors.DeviceDescriptor(
- idVendor=vendor_id,
- idProduct=product_id,
- bcdUSB=0x0200,
- iManufacturer=1,
- iProduct=2,
- iSerialNumber=3,
- bcdDevice=device_version)
-
- fs_config_desc = usb_descriptors.ConfigurationDescriptor(
- bmAttributes=0x80,
- MaxPower=50)
fs_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=0,
+ bInterfaceNumber=interface_number,
bInterfaceClass=usb_constants.DeviceClass.HID,
bInterfaceSubClass=0, # Non-bootable.
bInterfaceProtocol=0, # None.
+ iInterface=interface_string,
)
- fs_config_desc.AddInterface(fs_interface_desc)
- hs_config_desc = usb_descriptors.ConfigurationDescriptor(
- bmAttributes=0x80,
- MaxPower=50)
hs_interface_desc = usb_descriptors.InterfaceDescriptor(
- bInterfaceNumber=0,
+ bInterfaceNumber=interface_number,
bInterfaceClass=usb_constants.DeviceClass.HID,
bInterfaceSubClass=0, # Non-bootable.
bInterfaceProtocol=0, # None.
+ iInterface=interface_string,
)
- hs_config_desc.AddInterface(hs_interface_desc)
hid_desc = usb_descriptors.HidDescriptor()
hid_desc.AddDescriptor(hid_constants.DescriptorType.REPORT,
@@ -85,7 +71,7 @@ class HidGadget(gadget.Gadget):
.format(fs_interval, interval_ms))
fs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x81,
+ bEndpointAddress=in_endpoint,
bmAttributes=usb_constants.TransferType.INTERRUPT,
wMaxPacketSize=packet_size,
bInterval=fs_interval
@@ -97,49 +83,53 @@ class HidGadget(gadget.Gadget):
.format(hs_interval, interval_ms))
hs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x81,
+ bEndpointAddress=in_endpoint,
bmAttributes=usb_constants.TransferType.INTERRUPT,
wMaxPacketSize=packet_size,
bInterval=hs_interval
))
- if out_endpoint:
+ if out_endpoint is not None:
fs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x01,
+ bEndpointAddress=out_endpoint,
bmAttributes=usb_constants.TransferType.INTERRUPT,
wMaxPacketSize=packet_size,
bInterval=fs_interval
))
hs_interface_desc.AddEndpoint(usb_descriptors.EndpointDescriptor(
- bEndpointAddress=0x01,
+ bEndpointAddress=out_endpoint,
bmAttributes=usb_constants.TransferType.INTERRUPT,
wMaxPacketSize=packet_size,
bInterval=hs_interval
))
- super(HidGadget, self).__init__(device_desc, fs_config_desc, hs_config_desc)
- self.AddStringDescriptor(3, '{:06X}'.format(uuid.getnode()))
+ super(HidCompositeFeature, self).__init__(
+ [fs_interface_desc], [hs_interface_desc])
self._report_desc = report_desc
self._features = features
+ self._interface_number = interface_number
- def Connected(self, chip, speed):
- super(HidGadget, self).Connected(chip, speed)
+ def Connected(self, gadget):
+ super(HidCompositeFeature, self).Connected(gadget)
for report_id, feature in self._features.iteritems():
feature.Connected(self, report_id)
def Disconnected(self):
- super(HidGadget, self).Disconnected()
+ super(HidCompositeFeature, self).Disconnected()
for feature in self._features.itervalues():
feature.Disconnected()
- def GetDescriptor(self, recipient, typ, index, lang, length):
+ def StandardControlRead(self, recipient, request, value, index, length):
if recipient == usb_constants.Recipient.INTERFACE:
- if typ == hid_constants.DescriptorType.REPORT:
- if index == 0:
- return self._report_desc[:length]
+ if index == self._interface_number:
+ desc_type = value >> 8
+ desc_index = value & 0xff
+ if desc_type == hid_constants.DescriptorType.REPORT:
+ if desc_index == 0:
+ return self._report_desc[:length]
- return super(HidGadget, self).GetDescriptor(recipient, typ, index, lang,
- length)
+ return super(HidCompositeFeature, self).StandardControlRead(
+ recipient, request, value, index, length)
def ClassControlRead(self, recipient, request, value, index, length):
"""Handle class-specific control requests.
@@ -160,7 +150,7 @@ class HidGadget(gadget.Gadget):
"""
if recipient != usb_constants.Recipient.INTERFACE:
return None
- if index != 0:
+ if index != self._interface_number:
return None
if request == hid_constants.Request.GET_REPORT:
@@ -187,7 +177,7 @@ class HidGadget(gadget.Gadget):
"""
if recipient != usb_constants.Recipient.INTERFACE:
return None
- if index != 0:
+ if index != self._interface_number:
return None
if request == hid_constants.Request.SET_REPORT:
@@ -389,3 +379,52 @@ class HidFeature(object):
The feature report or None to stall the pipe.
"""
pass # pragma: no cover
+
+class HidGadget(composite_gadget.CompositeGadget):
+ """Generic HID gadget.
+ """
+
+ def __init__(self, report_desc, features, vendor_id, product_id,
+ packet_size=64, interval_ms=10, out_endpoint=True,
+ device_version=0x0100):
+ """Create a HID gadget.
+
+ Args:
+ report_desc: HID report descriptor.
+ features: Map between Report IDs and HidFeature objects to handle them.
+ vendor_id: Device Vendor ID.
+ product_id: Device Product ID.
+ packet_size: Maximum interrupt packet size.
+ interval_ms: Interrupt transfer interval in milliseconds.
+ out_endpoint: Should this device have an interrupt OUT endpoint?
+ device_version: Device version number.
+
+ Raises:
+ ValueError: If any of the parameters are out of range.
+ """
+ device_desc = usb_descriptors.DeviceDescriptor(
+ idVendor=vendor_id,
+ idProduct=product_id,
+ bcdUSB=0x0200,
+ iManufacturer=1,
+ iProduct=2,
+ iSerialNumber=3,
+ bcdDevice=device_version)
+
+ if out_endpoint:
+ out_endpoint = 0x01
+ else:
+ out_endpoint = None
+
+ self._hid_feature = HidCompositeFeature(
+ report_desc=report_desc,
+ features=features,
+ packet_size=packet_size,
+ interval_ms=interval_ms,
+ out_endpoint=out_endpoint)
+
+ super(HidGadget, self).__init__(device_desc, [self._hid_feature])
+ self.AddStringDescriptor(3, '{:06X}'.format(uuid.getnode()))
+
+ def SendReport(self, report_id, data):
+ self._hid_feature.SendReport(report_id, data)
diff --git a/tools/usb_gadget/hid_gadget_test.py b/tools/usb_gadget/hid_gadget_test.py
index 58d9d98..f0a6a29 100755
--- a/tools/usb_gadget/hid_gadget_test.py
+++ b/tools/usb_gadget/hid_gadget_test.py
@@ -58,6 +58,8 @@ class HidGadgetTest(unittest.TestCase):
def test_get_string_descriptor(self):
g = hid_gadget.HidGadget(report_desc=report_desc, features={},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
g.AddStringDescriptor(2, 'HID Gadget')
desc = g.ControlRead(0x80, 6, 0x0302, 0x0409, 255)
self.assertEquals(desc, '\x16\x03H\0I\0D\0 \0G\0a\0d\0g\0e\0t\0')
@@ -65,17 +67,23 @@ class HidGadgetTest(unittest.TestCase):
def test_get_report_descriptor(self):
g = hid_gadget.HidGadget(report_desc=report_desc, features={},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
desc = g.ControlRead(0x81, 6, 0x2200, 0, 63)
self.assertEquals(desc, report_desc)
def test_set_idle(self):
g = hid_gadget.HidGadget(report_desc=report_desc, features={},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertTrue(g.ControlWrite(0x21, 0x0A, 0, 0, ''))
def test_class_wrong_target(self):
g = hid_gadget.HidGadget(report_desc=report_desc, features={},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertIsNone(g.ControlRead(0xA0, 0, 0, 0, 0)) # Device
self.assertIsNone(g.ControlRead(0xA1, 0, 0, 1, 0)) # Interface 1
self.assertIsNone(g.ControlWrite(0x20, 0, 0, 0, '')) # Device
@@ -147,18 +155,24 @@ class HidFeatureTest(unittest.TestCase):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertIsNone(g.ControlRead(0xA1, 1, 0x0102, 0, 8))
def test_set_bad_report(self):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertIsNone(g.ControlWrite(0x21, 0x09, 0x0102, 0, 'Hello!'))
def test_get_input_report(self):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
report = g.ControlRead(0xA1, 1, 0x0101, 0, 8)
self.assertEquals(report, 'Input re')
@@ -166,6 +180,8 @@ class HidFeatureTest(unittest.TestCase):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertTrue(g.ControlWrite(0x21, 0x09, 0x0101, 0, 'Hello!'))
self.assertEquals(feature.input_report, 'Hello!')
@@ -173,6 +189,8 @@ class HidFeatureTest(unittest.TestCase):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
report = g.ControlRead(0xA1, 1, 0x0201, 0, 8)
self.assertEquals(report, 'Output r')
@@ -180,6 +198,8 @@ class HidFeatureTest(unittest.TestCase):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertTrue(g.ControlWrite(0x21, 0x09, 0x0201, 0, 'Hello!'))
self.assertEquals(feature.output_report, 'Hello!')
@@ -189,6 +209,7 @@ class HidFeatureTest(unittest.TestCase):
vendor_id=0, product_id=0)
chip = mock.Mock()
g.Connected(chip, usb_constants.Speed.HIGH)
+ g.SetConfiguration(1)
g.ReceivePacket(0x01, '\x01Hello!')
self.assertFalse(chip.HaltEndpoint.called)
self.assertEquals(feature.output_report, 'Hello!')
@@ -199,6 +220,7 @@ class HidFeatureTest(unittest.TestCase):
vendor_id=0, product_id=0)
chip = mock.Mock()
g.Connected(chip, usb_constants.Speed.HIGH)
+ g.SetConfiguration(1)
g.ReceivePacket(0x01, 'Hello!')
self.assertFalse(chip.HaltEndpoint.called)
self.assertEquals(feature.output_report, 'Hello!')
@@ -209,6 +231,7 @@ class HidFeatureTest(unittest.TestCase):
vendor_id=0, product_id=0)
chip = mock.Mock()
g.Connected(chip, usb_constants.Speed.HIGH)
+ g.SetConfiguration(1)
g.ReceivePacket(0x01, '\x00Hello!')
chip.HaltEndpoint.assert_called_once_with(0x01)
@@ -216,6 +239,8 @@ class HidFeatureTest(unittest.TestCase):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
report = g.ControlRead(0xA1, 1, 0x0301, 0, 8)
self.assertEquals(report, 'Feature ')
@@ -223,6 +248,8 @@ class HidFeatureTest(unittest.TestCase):
feature = TestFeature()
g = hid_gadget.HidGadget(report_desc, features={1: feature},
vendor_id=0, product_id=0)
+ chip = mock.Mock()
+ g.Connected(chip, usb_constants.Speed.HIGH)
self.assertTrue(g.ControlWrite(0x21, 0x09, 0x0301, 0, 'Hello!'))
self.assertEquals(feature.feature_report, 'Hello!')
diff --git a/tools/usb_gadget/keyboard_gadget.py b/tools/usb_gadget/keyboard_gadget.py
index 915b50e..8f1e3b0 100644
--- a/tools/usb_gadget/keyboard_gadget.py
+++ b/tools/usb_gadget/keyboard_gadget.py
@@ -141,7 +141,7 @@ class KeyboardGadget(hid_gadget.HidGadget):
features={0: self._feature},
packet_size=8,
interval_ms=1,
- out_endpoint=False,
+ out_endpoint=True,
vendor_id=usb_constants.VendorID.GOOGLE,
product_id=usb_constants.ProductID.GOOGLE_KEYBOARD_GADGET,
device_version=0x0100)
diff --git a/tools/usb_gadget/keyboard_gadget_test.py b/tools/usb_gadget/keyboard_gadget_test.py
index d3b085f..cac3e31 100755
--- a/tools/usb_gadget/keyboard_gadget_test.py
+++ b/tools/usb_gadget/keyboard_gadget_test.py
@@ -52,6 +52,7 @@ class KeyboardGadgetTest(unittest.TestCase):
g = keyboard_gadget.KeyboardGadget()
chip = mock.Mock()
g.Connected(chip, usb_constants.Speed.FULL)
+ g.SetConfiguration(1)
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')
diff --git a/tools/usb_gadget/usb_constants.py b/tools/usb_gadget/usb_constants.py
index 442674b..4e8f2c0 100644
--- a/tools/usb_gadget/usb_constants.py
+++ b/tools/usb_gadget/usb_constants.py
@@ -167,3 +167,4 @@ class ProductID(object):
GOOGLE_MOUSE_GADGET = 0x58F2
GOOGLE_HID_ECHO_GADGET = 0x58F3
GOOGLE_ECHO_GADGET = 0x58F4
+ GOOGLE_COMPOSITE_ECHO_GADGET = 0x58F5
diff --git a/tools/usb_gadget/usb_gadget.gyp b/tools/usb_gadget/usb_gadget.gyp
index ed991d9..4a6f84e 100644
--- a/tools/usb_gadget/usb_gadget.gyp
+++ b/tools/usb_gadget/usb_gadget.gyp
@@ -11,6 +11,8 @@
'usb_gadget_files': [
'__init__.py',
'__main__.py',
+ 'composite_echo_gadget.py',
+ 'composite_gadget.py',
'default_gadget.py',
'echo_gadget.py',
'gadget.py',