123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- # 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.
- """Generic USB gadget functionality.
- """
- from __future__ import print_function
- import struct
- import msos20_descriptors
- import usb_constants
- import usb_descriptors
- class Gadget(object):
- """Basic functionality for a USB device.
- Implements standard control requests assuming that a subclass will handle
- class- or vendor-specific requests.
- """
- def __init__(self, device_desc, fs_config_desc, hs_config_desc):
- """Create a USB gadget device.
- Args:
- device_desc: USB device descriptor.
- fs_config_desc: Low/full-speed device descriptor.
- hs_config_desc: High-speed device descriptor.
- """
- self._speed = usb_constants.Speed.UNKNOWN
- self._chip = None
- self._device_desc = device_desc
- self._fs_config_desc = fs_config_desc
- self._hs_config_desc = hs_config_desc
- # dict mapping language codes to a dict mapping indexes to strings
- self._strings = {}
- self._bos_descriptor = None
- # dict mapping interface numbers to a set of endpoint addresses
- self._active_endpoints = {}
- # dict mapping endpoint addresses to interfaces
- self._endpoint_interface_map = {}
- self._ms_vendor_code_v1 = None
- self._ms_vendor_code_v2 = None
- self._ms_compat_ids = {}
- self._ms_os20_config_subset = None
- def GetDeviceDescriptor(self):
- return self._device_desc
- def GetFullSpeedConfigurationDescriptor(self):
- return self._fs_config_desc
- def GetHighSpeedConfigurationDescriptor(self):
- return self._hs_config_desc
- def GetConfigurationDescriptor(self):
- if self._speed == usb_constants.Speed.FULL:
- return self._fs_config_desc
- elif self._speed == usb_constants.Speed.HIGH:
- return self._hs_config_desc
- else:
- raise RuntimeError('Device is not connected.')
- def GetSpeed(self):
- return self._speed
- def AddStringDescriptor(self, index, value, lang=0x0409):
- """Add a string descriptor to this device.
- Args:
- index: String descriptor index (matches 'i' fields in descriptors).
- value: The string.
- lang: Language code (default: English).
- Raises:
- ValueError: The index or language code is invalid.
- """
- if index < 1 or index > 255:
- raise ValueError('String descriptor index out of range.')
- if lang < 0 or lang > 0xffff:
- raise ValueError('String descriptor language code out of range.')
- lang_strings = self._strings.setdefault(lang, {})
- lang_strings[index] = value
- def EnableMicrosoftOSDescriptorsV1(self, vendor_code=0x01):
- if vendor_code < 0 or vendor_code > 255:
- raise ValueError('Vendor code out of range.')
- if vendor_code == self._ms_vendor_code_v1:
- raise ValueError('OS Descriptor v1 vendor code conflicts with v2.')
- self._ms_vendor_code_v1 = vendor_code
- def EnableMicrosoftOSDescriptorsV2(self, vendor_code=0x02):
- if vendor_code < 0 or vendor_code > 255:
- raise ValueError('Vendor code out of range.')
- if vendor_code == self._ms_vendor_code_v1:
- raise ValueError('OS Descriptor v2 vendor code conflicts with v1.')
- self._ms_vendor_code_v2 = vendor_code
- self._ms_os20_descriptor_set = \
- msos20_descriptors.DescriptorSetHeader(dwWindowsVersion=0x06030000)
- # Gadget devices currently only support one configuration. Contrary to
- # Microsoft's documentation the bConfigurationValue field should be set to
- # the index passed to GET_DESCRIPTOR that returned the configuration instead
- # of the configuration's bConfigurationValue field. (i.e. 0 instead of 1).
- #
- # https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/ae64282c-3bc3-49af-8391-4d174479d9e7/microsoft-os-20-descriptors-not-working-on-an-interface-of-a-composite-usb-device
- self._ms_os20_config_subset = msos20_descriptors.ConfigurationSubsetHeader(
- bConfigurationValue=0)
- self._ms_os20_descriptor_set.Add(self._ms_os20_config_subset)
- self._ms_os20_platform_descriptor = \
- msos20_descriptors.PlatformCapabilityDescriptor(
- dwWindowsVersion=0x06030000,
- bMS_VendorCode=self._ms_vendor_code_v2)
- self._ms_os20_platform_descriptor.SetDescriptorSet(
- self._ms_os20_descriptor_set)
- self.AddDeviceCapabilityDescriptor(self._ms_os20_platform_descriptor)
- def SetMicrosoftCompatId(self, interface_number, compat_id, sub_compat_id=''):
- self._ms_compat_ids[interface_number] = (compat_id, sub_compat_id)
- if self._ms_os20_config_subset is not None:
- function_header = msos20_descriptors.FunctionSubsetHeader(
- bFirstInterface=interface_number)
- function_header.Add(msos20_descriptors.CompatibleId(
- CompatibleID=compat_id, SubCompatibleID=sub_compat_id))
- self._ms_os20_config_subset.Add(function_header)
- def AddDeviceCapabilityDescriptor(self, device_capability):
- """Add a device capability descriptor to this device.
- Args:
- device_capability: The Descriptor object.
- """
- if self._bos_descriptor is None:
- self._bos_descriptor = usb_descriptors.BosDescriptor()
- self._bos_descriptor.AddDeviceCapability(device_capability)
- def Connected(self, chip, speed):
- """The device has been connected to a USB host.
- Args:
- chip: USB controller.
- speed: Connection speed.
- """
- self._speed = speed
- self._chip = chip
- def Disconnected(self):
- """The device has been disconnected from the USB host."""
- 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
- def ControlRead(self, request_type, request, value, index, length):
- """Handle a read on the control pipe (endpoint zero).
- Args:
- request_type: bmRequestType field of the setup packet.
- 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.
- """
- assert request_type & usb_constants.Dir.IN
- typ = request_type & usb_constants.Type.MASK
- recipient = request_type & usb_constants.Recipient.MASK
- if typ == usb_constants.Type.STANDARD:
- return self.StandardControlRead(
- recipient, request, value, index, length)
- elif typ == usb_constants.Type.CLASS:
- return self.ClassControlRead(
- recipient, request, value, index, length)
- elif typ == usb_constants.Type.VENDOR:
- return self.VendorControlRead(
- recipient, request, value, index, length)
- def ControlWrite(self, request_type, request, value, index, data):
- """Handle a write to the control pipe (endpoint zero).
- Args:
- request_type: bmRequestType field of the setup packet.
- 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.
- """
- assert not request_type & usb_constants.Dir.IN
- typ = request_type & usb_constants.Type.MASK
- recipient = request_type & usb_constants.Recipient.MASK
- if typ == usb_constants.Type.STANDARD:
- return self.StandardControlWrite(
- recipient, request, value, index, data)
- elif typ == usb_constants.Type.CLASS:
- return self.ClassControlWrite(
- recipient, request, value, index, data)
- elif typ == usb_constants.Type.VENDOR:
- return self.VendorControlWrite(
- recipient, request, value, index, data)
- def SendPacket(self, endpoint, data):
- """Send a data packet on the given endpoint.
- Args:
- endpoint: Endpoint address.
- data: Data buffer.
- Raises:
- ValueError: If the endpoint address is not valid.
- RuntimeError: If the device is not connected.
- """
- if self._chip is None:
- raise RuntimeError('Device is not connected.')
- if not endpoint & usb_constants.Dir.IN:
- raise ValueError('Cannot write to non-input endpoint.')
- self._chip.SendPacket(endpoint, data)
- def ReceivePacket(self, endpoint, data):
- """Handle an incoming data packet on one of the device's active endpoints.
- This method should be overridden by a subclass implementing endpoint-based
- data transfers.
- Args:
- endpoint: Endpoint address.
- data: Data buffer.
- """
- pass
- def HaltEndpoint(self, endpoint):
- """Signals a STALL condition to the host on the given endpoint.
- Args:
- endpoint: Endpoint address.
- """
- self._chip.HaltEndpoint(endpoint)
- def StandardControlRead(self, recipient, request, value, index, length):
- """Handle standard control transfers.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- 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.
- """
- 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))
- 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.
- See Universal Serial Bus Specification Revision 2.0 section 9.4.3.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- typ: Descriptor type.
- index: Descriptor index.
- lang: Descriptor language code.
- length: Maximum amount of data the host expects the device to return.
- Returns:
- The value of the descriptor or None to stall the pipe.
- """
- if typ == usb_constants.DescriptorType.STRING:
- return self.GetStringDescriptor(index, lang, length)
- elif typ == usb_constants.DescriptorType.BOS:
- return self.GetBosDescriptor(length)
- def ClassControlRead(self, recipient, request, value, index, length):
- """Handle class-specific control transfers.
- This function should be overridden by a subclass implementing a particular
- device class.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- 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.
- This function should be overridden by a subclass if implementing a device
- that responds to vendor-specific requests.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- 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.
- """
- if (self._ms_vendor_code_v1 is not None and
- request == self._ms_vendor_code_v1 and
- (recipient == usb_constants.Recipient.DEVICE or
- recipient == usb_constants.Recipient.INTERFACE)):
- return self.GetMicrosoftOSDescriptorV1(recipient, value, index, length)
- if (self._ms_vendor_code_v2 is not None and
- request == self._ms_vendor_code_v2 and
- recipient == usb_constants.Recipient.DEVICE and
- value == 0x0000 and
- index == 0x0007):
- return self.GetMicrosoftOSDescriptorV2(length)
- return None
- def StandardControlWrite(self, recipient, request, value, index, data):
- """Handle standard control transfers.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- 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.
- """
- _ = data
- if request == usb_constants.Request.SET_CONFIGURATION:
- if recipient == usb_constants.Recipient.DEVICE:
- return self.SetConfiguration(value)
- elif request == usb_constants.Request.SET_INTERFACE:
- if recipient == usb_constants.Recipient.INTERFACE:
- return self.SetInterface(index, value)
- def ClassControlWrite(self, recipient, request, value, index, data):
- """Handle class-specific control transfers.
- This function should be overridden by a subclass implementing a particular
- device class.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- 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.
- This function should be overridden by a subclass if implementing a device
- that responds to vendor-specific requests.
- Args:
- recipient: Request recipient (device, interface, endpoint, etc.)
- 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 GetStringDescriptor(self, index, lang, length):
- """Handle a GET_DESCRIPTOR(String) request from the host.
- Descriptor index 0 returns the set of languages supported by the device.
- All other indices return the string descriptors registered with those
- indices.
- See Universal Serial Bus Specification Revision 2.0 section 9.6.7.
- Args:
- index: Descriptor index.
- lang: Descriptor language code.
- length: Maximum amount of data the host expects the device to return.
- Returns:
- The string descriptor or None to stall the pipe if the descriptor is not
- found.
- """
- if index == 0:
- length = 2 + len(self._strings) * 2
- header = struct.pack('<BB', length, usb_constants.DescriptorType.STRING)
- lang_codes = [struct.pack('<H', lang)
- for lang in self._strings.iterkeys()]
- buf = header + ''.join(lang_codes)
- assert len(buf) == length
- return buf[:length]
- if index == 0xEE and lang == 0 and self._ms_vendor_code_v1 is not None:
- # See https://msdn.microsoft.com/en-us/windows/hardware/gg463179 for the
- # definition of this special string descriptor.
- buf = (struct.pack('<BB', 18, usb_constants.DescriptorType.STRING) +
- 'MSFT100'.encode('UTF-16LE') +
- struct.pack('<BB', self._ms_vendor_code_v1, 0))
- assert len(buf) == 18
- return buf[:length]
- elif lang not in self._strings:
- return None
- elif index not in self._strings[lang]:
- return None
- else:
- descriptor = usb_descriptors.StringDescriptor(
- bString=self._strings[lang][index])
- return descriptor.Encode()[:length]
- def GetMicrosoftOSDescriptorV1(self, recipient, value, index, length):
- """Handle a the Microsoft OS 1.0 Descriptor request from the host.
- See https://msdn.microsoft.com/en-us/windows/hardware/gg463179 for the
- format of these descriptors.
- Args:
- recipient: Request recipient (device or interface)
- 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:
- The descriptor or None to stall the pipe if the descriptor is not
- supported.
- """
- _ = recipient, value
- if index == 0x0004:
- return self.GetMicrosoftCompatIds(length)
- def GetMicrosoftCompatIds(self, length):
- interfaces = self.GetConfigurationDescriptor().GetInterfaces()
- max_interface = max([iface.bInterfaceNumber for iface in interfaces])
- header = struct.pack('<IHHBxxxxxxx',
- 16 + 24 * (max_interface + 1),
- 0x0100,
- 0x0004,
- max_interface + 1)
- if length <= len(header):
- return header[:length]
- buf = header
- for interface in xrange(max_interface + 1):
- compat_id, sub_compat_id = self._ms_compat_ids.get(interface, ('', ''))
- buf += struct.pack('<BB8s8sxxxxxx',
- interface, 0x01, compat_id, sub_compat_id)
- return buf[:length]
- def GetMicrosoftOSDescriptorV2(self, length):
- return self._ms_os20_descriptor_set.Encode()[:length]
- def GetBosDescriptor(self, length):
- """Handle a GET_DESCRIPTOR(BOS) request from the host.
- Device capability descriptors can be added to the Binary Device Object Store
- returned by this method by calling AddDeviceCapabilityDescriptor.
- See Universal Serial Bus 3.1 Specification, Revision 1.0 section 9.6.2.
- Args:
- length: Maximum amount of data the host expects the device to return.
- Returns:
- The device's binary object store descriptor or None to stall the pipe if
- no device capability descriptors have been configured.
- """
- if self._bos_descriptor is None:
- return None
- return self._bos_descriptor.Encode()[:length]
- def SetConfiguration(self, index):
- """Handle a SET_CONFIGURATION request from the host.
- See Universal Serial Bus Specification Revision 2.0 section 9.4.7.
- Args:
- index: Configuration index selected.
- Returns:
- True on success, None on error to stall the pipe.
- """
- print('SetConfiguration({})'.format(index))
- for endpoint_addrs in self._active_endpoints.values():
- 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
- # Windows does before suspending the port.
- return True
- elif index != 1:
- return None
- config_desc = self.GetConfigurationDescriptor()
- for interface_desc in config_desc.GetInterfaces():
- if interface_desc.bAlternateSetting != 0:
- continue
- endpoint_addrs = self._active_endpoints.setdefault(
- interface_desc.bInterfaceNumber, set())
- 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):
- """Handle a SET_INTERFACE request from the host.
- See Universal Serial Bus Specification Revision 2.0 section 9.4.10.
- Args:
- interface: Interface number to configure.
- alt_setting: Alternate setting to select.
- Returns:
- True on success, None on error to stall the pipe.
- """
- print('SetInterface({}, {})'.format(interface, alt_setting))
- config_desc = self.GetConfigurationDescriptor()
- interface_desc = None
- for interface_option in config_desc.GetInterfaces():
- if (interface_option.bInterfaceNumber == interface and
- interface_option.bAlternateSetting == alt_setting):
- interface_desc = interface_option
- if interface_desc is None:
- return None
- 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)
|