You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.2 KiB

  1. #
  2. # This file is part of usb_protocol.
  3. #
  4. """ Convenience emitters for simple, standard descriptors. """
  5. import unittest
  6. import functools
  7. from contextlib import contextmanager
  8. from .. import emitter_for_format
  9. from ..descriptor import ComplexDescriptorEmitter
  10. from ...types.descriptors.standard import \
  11. DeviceDescriptor, StringDescriptor, EndpointDescriptor, DeviceQualifierDescriptor, \
  12. ConfigurationDescriptor, InterfaceDescriptor, StandardDescriptorNumbers
  13. # Create our basic emitters...
  14. DeviceDescriptorEmitter = emitter_for_format(DeviceDescriptor)
  15. StringDescriptorEmitter = emitter_for_format(StringDescriptor)
  16. EndpointDescriptorEmitter = emitter_for_format(EndpointDescriptor)
  17. DeviceQualifierDescriptor = emitter_for_format(DeviceQualifierDescriptor)
  18. # ... convenience functions ...
  19. def get_string_descriptor(string):
  20. """ Generates a string descriptor for the relevant string. """
  21. emitter = StringDescriptorEmitter()
  22. emitter.bString = string
  23. return emitter.emit()
  24. # ... and complex emitters.
  25. class InterfaceDescriptorEmitter(ComplexDescriptorEmitter):
  26. """ Emitter that creates an InterfaceDescriptor. """
  27. DESCRIPTOR_FORMAT = InterfaceDescriptor
  28. @contextmanager
  29. def EndpointDescriptor(self):
  30. """ Context manager that allows addition of a subordinate endpoint descriptor.
  31. It can be used with a `with` statement; and yields an EndpointDesriptorEmitter
  32. that can be populated:
  33. with interface.EndpointDescriptor() as d:
  34. d.bEndpointAddress = 0x01
  35. d.bmAttributes = 0x80
  36. d.wMaxPacketSize = 64
  37. d.bInterval = 0
  38. This adds the relevant descriptor, automatically.
  39. """
  40. descriptor = EndpointDescriptorEmitter()
  41. yield descriptor
  42. self.add_subordinate_descriptor(descriptor)
  43. def emit(self, include_subordinates=True):
  44. # Count our endpoints, and then call our parent emitter.
  45. self.bNumEndpoints = self._type_counts[StandardDescriptorNumbers.ENDPOINT]
  46. return super().emit(include_subordinates=include_subordinates)
  47. class ConfigurationDescriptorEmitter(ComplexDescriptorEmitter):
  48. """ Emitter that creates a configuration descriptor. """
  49. DESCRIPTOR_FORMAT = ConfigurationDescriptor
  50. @contextmanager
  51. def InterfaceDescriptor(self):
  52. """ Context manager that allows addition of a subordinate interface descriptor.
  53. It can be used with a `with` statement; and yields an InterfaceDescriptorEmitter
  54. that can be populated:
  55. with interface.InterfaceDescriptor() as d:
  56. d.bInterfaceNumber = 0x01
  57. [snip]
  58. This adds the relevant descriptor, automatically. Note that populating derived
  59. fields such as bNumEndpoints aren't necessary; they'll be populated automatically.
  60. """
  61. descriptor = InterfaceDescriptorEmitter()
  62. yield descriptor
  63. self.add_subordinate_descriptor(descriptor)
  64. def emit(self, include_subordinates=True):
  65. # Count our interfaces...
  66. self.bNumInterfaces = self._type_counts[StandardDescriptorNumbers.INTERFACE]
  67. # ... and figure out our total length.
  68. subordinate_length = sum(len(sub) for sub in self._subordinates)
  69. self.wTotalLength = subordinate_length + self.DESCRIPTOR_FORMAT.sizeof()
  70. # Finally, emit our whole descriptor.
  71. return super().emit(include_subordinates=include_subordinates)
  72. class EmitterTests(unittest.TestCase):
  73. def test_string_emitter(self):
  74. emitter = StringDescriptorEmitter()
  75. emitter.bString = "Hello"
  76. self.assertEqual(emitter.emit(), b"\x0C\x03H\0e\0l\0l\0o\0")
  77. def test_string_emitter_function(self):
  78. self.assertEqual(get_string_descriptor("Hello"), b"\x0C\x03H\0e\0l\0l\0o\0")
  79. def test_configuration_emitter(self):
  80. descriptor = bytes([
  81. # config descriptor
  82. 12, # length
  83. 2, # type
  84. 25, 00, # total length
  85. 1, # num interfaces
  86. 1, # configuration number
  87. 0, # config string
  88. 0x80, # attributes
  89. 250, # max power
  90. # interface descriptor
  91. 9, # length
  92. 4, # type
  93. 0, # number
  94. 0, # alternate
  95. 1, # num endpoints
  96. 0xff, # class
  97. 0xff, # subclass
  98. 0xff, # protocol
  99. 0, # string
  100. # endpoint descriptor
  101. 7, # length
  102. 5, # type
  103. 0x01, # address
  104. 2, # attributes
  105. 64, 0, # max packet size
  106. 255, # interval
  107. ])
  108. # Create a trivial configuration descriptor...
  109. emitter = ConfigurationDescriptorEmitter()
  110. with emitter.InterfaceDescriptor() as interface:
  111. interface.bInterfaceNumber = 0
  112. with interface.EndpointDescriptor() as endpoint:
  113. endpoint.bEndpointAddress = 1
  114. # ... and validate that it maches our reference descriptor.
  115. binary = emitter.emit()
  116. self.assertEqual(len(binary), len(descriptor))
  117. if __name__ == "__main__":
  118. unittest.main()