diff options
author | reillyg <reillyg@chromium.org> | 2015-07-06 15:15:44 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-06 22:17:19 +0000 |
commit | a942b49af49181ff218d4a2c345be96464c23176 (patch) | |
tree | 0cfeaf1879aa07ff7bcfd479a98e471f5fc4422d /tools/usb_gadget | |
parent | bbb9e2f6486cccadde4dc2fa077af8f694105eaa (diff) | |
download | chromium_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__.py | 2 | ||||
-rw-r--r-- | tools/usb_gadget/composite_echo_gadget.py | 61 | ||||
-rw-r--r-- | tools/usb_gadget/composite_gadget.py | 277 | ||||
-rw-r--r-- | tools/usb_gadget/echo_gadget.py | 344 | ||||
-rwxr-xr-x | tools/usb_gadget/echo_gadget_test.py | 22 | ||||
-rw-r--r-- | tools/usb_gadget/gadget.py | 34 | ||||
-rw-r--r-- | tools/usb_gadget/hid_gadget.py | 135 | ||||
-rwxr-xr-x | tools/usb_gadget/hid_gadget_test.py | 27 | ||||
-rw-r--r-- | tools/usb_gadget/keyboard_gadget.py | 2 | ||||
-rwxr-xr-x | tools/usb_gadget/keyboard_gadget_test.py | 1 | ||||
-rw-r--r-- | tools/usb_gadget/usb_constants.py | 1 | ||||
-rw-r--r-- | tools/usb_gadget/usb_gadget.gyp | 2 |
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', |