| @@ -10,16 +10,18 @@ from contextlib import contextmanager | |||
| from .. import emitter_for_format | |||
| from ..descriptor import ComplexDescriptorEmitter | |||
| from ...types import LanguageIDs | |||
| from ...types.descriptors.standard import \ | |||
| DeviceDescriptor, StringDescriptor, EndpointDescriptor, DeviceQualifierDescriptor, \ | |||
| ConfigurationDescriptor, InterfaceDescriptor, StandardDescriptorNumbers | |||
| ConfigurationDescriptor, InterfaceDescriptor, StandardDescriptorNumbers, StringLanguageDescriptor | |||
| # Create our basic emitters... | |||
| DeviceDescriptorEmitter = emitter_for_format(DeviceDescriptor) | |||
| StringDescriptorEmitter = emitter_for_format(StringDescriptor) | |||
| EndpointDescriptorEmitter = emitter_for_format(EndpointDescriptor) | |||
| DeviceQualifierDescriptor = emitter_for_format(DeviceQualifierDescriptor) | |||
| DeviceDescriptorEmitter = emitter_for_format(DeviceDescriptor) | |||
| StringDescriptorEmitter = emitter_for_format(StringDescriptor) | |||
| StringLanguageDescriptorEmitter = emitter_for_format(StringLanguageDescriptor) | |||
| EndpointDescriptorEmitter = emitter_for_format(EndpointDescriptor) | |||
| DeviceQualifierDescriptor = emitter_for_format(DeviceQualifierDescriptor) | |||
| # ... convenience functions ... | |||
| def get_string_descriptor(string): | |||
| @@ -112,7 +114,19 @@ class ConfigurationDescriptorEmitter(ComplexDescriptorEmitter): | |||
| class DeviceDescriptorCollection: | |||
| """ Object that builds a full collection of descriptors related to a given USB device. """ | |||
| def __init__(self): | |||
| # Most systems seem happiest with en_US (ugh), so default to that. | |||
| DEFAULT_SUPPORTED_LANGUAGES = [LanguageIDs.ENGLISH_US] | |||
| def __init__(self, automatic_language_descriptor=True): | |||
| """ | |||
| Parameters: | |||
| automatic_language_descriptor -- If set or not provided, a language descriptor will automatically | |||
| be added if none exists. | |||
| """ | |||
| self._automatic_language_descriptor = automatic_language_descriptor | |||
| # Create our internal descriptor tracker. | |||
| # Keys are a tuple of (type, index). | |||
| @@ -183,6 +197,21 @@ class DeviceDescriptorCollection: | |||
| self._descriptors[identifier] = descriptor | |||
| def add_language_descriptor(self, supported_languages=None): | |||
| """ Adds a language descriptor to the list of device descriptors. | |||
| Parameters: | |||
| supported_languages -- A list of languages supported by the device. | |||
| """ | |||
| if supported_languages is None: | |||
| supported_languages = self.DEFAULT_SUPPORTED_LANGUAGES | |||
| descriptor = StringLanguageDescriptorEmitter() | |||
| descriptor.wLANGID = supported_languages | |||
| self.add_descriptor(descriptor) | |||
| @contextmanager | |||
| def DeviceDescriptor(self): | |||
| """ Context manager that allows addition of a device descriptor. | |||
| @@ -230,6 +259,20 @@ class DeviceDescriptorCollection: | |||
| self.add_descriptor(descriptor) | |||
| def _ensure_has_language_descriptor(self): | |||
| """ Ensures that we have a language descriptor; adding one if necessary.""" | |||
| # If we're not automatically adding a language descriptor, we shouldn't do anything, | |||
| # and we'll just ignore this. | |||
| if not self._ensure_has_language_descriptor: | |||
| return | |||
| # If we don't have a language descriptor, add our default one. | |||
| if not (StandardDescriptorNumbers.STRING, 0) in self._descriptors: | |||
| self.add_language_descriptor() | |||
| def get_descriptor_bytes(self, type_number: int, index: int = 0): | |||
| """ Returns the raw, binary descriptor for a given descriptor type/index. | |||
| @@ -237,11 +280,17 @@ class DeviceDescriptorCollection: | |||
| type_number -- The descriptor type number. | |||
| index -- The index of the relevant descriptor, if relevant. | |||
| """ | |||
| # If this is a request for a language descriptor, return one. | |||
| if (type_number, index) == (StandardDescriptorNumbers.STRING, 0): | |||
| self._ensure_has_language_descriptor() | |||
| return self._descriptors[(type_number, index)] | |||
| def __iter__(self): | |||
| """ Allow iterating over each of our descriptors; yields (index, value, descriptor). """ | |||
| self._ensure_has_language_descriptor() | |||
| return ((number, index, desc) for ((number, index), desc) in self._descriptors.items()) | |||
| @@ -337,17 +386,20 @@ class EmitterTests(unittest.TestCase): | |||
| # We should wind up with four descriptor entries, as our endpoint/interface descriptors are | |||
| # included in our configuration descriptor. | |||
| self.assertEqual(len(results), 4) | |||
| self.assertEqual(len(results), 5) | |||
| # Supported languages string. | |||
| self.assertIn((3, 0, b'\x04\x03\x09\x04'), results) | |||
| # Manufacturer / product string. | |||
| self.assertIn((3, 1, b'\x1a\x03T\x00e\x00s\x00t\x00 \x00C\x00o\x00m\x00p\x00a\x00n\x00y\x00'), results) | |||
| self.assertIn((3, 2, b'\x1a\x03T\x00e\x00s\x00t\x00 \x00P\x00r\x00o\x00d\x00u\x00c\x00t\x00'), results) | |||
| # Device descriptor. | |||
| self.assertIn((1, 0, b'\x0f\x01\x00\x02\x00\x00\x00@\xad\xde\xef\xbe\x00\x00\x01\x02\x00\x01'), results) | |||
| self.assertIn((1, 0, b'\x12\x01\x00\x02\x00\x00\x00@\xad\xde\xef\xbe\x00\x00\x01\x02\x00\x01'), results) | |||
| # Configuration descriptor, with subordinates. | |||
| self.assertIn((2, 0, b'\r\x02 \x00\x01\x01\x00\x80\xfa\t\x04\x01\x00\x02\xff\xff\xff\x00\x07\x05\x81\x02@\x00\xff\x07\x05\x01\x02@\x00\xff'), results) | |||
| self.assertIn((2, 0, b'\t\x02 \x00\x01\x01\x00\x80\xfa\t\x04\x01\x00\x02\xff\xff\xff\x00\x07\x05\x81\x02@\x00\xff\x07\x05\x01\x02@\x00\xff'), results) | |||
| if __name__ == "__main__": | |||
| unittest.main() | |||