Skip to content

Commit a983073

Browse files
authored
Merge pull request #22 from twam/uac2-fixes
Uac2 fixes
2 parents e597022 + 4da76c4 commit a983073

File tree

2 files changed

+71
-10
lines changed

2 files changed

+71
-10
lines changed

usb_protocol/emitters/descriptors/uac2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def _pre_emit(self):
2424
ClockSourceDescriptorEmitter = emitter_for_format(ClockSourceDescriptor)
2525
InputTerminalDescriptorEmitter = emitter_for_format(InputTerminalDescriptor)
2626
OutputTerminalDescriptorEmitter = emitter_for_format(OutputTerminalDescriptor)
27+
FeatureUnitDescriptorEmitter = emitter_for_format(FeatureUnitDescriptor)
2728
AudioStreamingInterfaceDescriptorEmitter = emitter_for_format(AudioStreamingInterfaceDescriptor)
2829
ClassSpecificAudioStreamingInterfaceDescriptorEmitter = emitter_for_format(ClassSpecificAudioStreamingInterfaceDescriptor)
2930
TypeIFormatTypeDescriptorEmitter = emitter_for_format(TypeIFormatTypeDescriptor)

usb_protocol/types/descriptors/uac2.py

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,9 @@ class SidebandProtocols(IntEnum):
633633
"bCSourceID" / DescriptorField(description="ID of the clock which is connected to this terminal"),
634634
"bNrChannels" / DescriptorField(description="number of logical output channels in the terminal’s output channel cluster"),
635635
"bmChannelConfig" / DescriptorField(description="describes the spatial location of the logical channels", default=0, length=4),
636-
"bmControls" / DescriptorField(description="OR combination of ClockFrequencyControl, CopyProtectControl, ConnectorControl, ClusterControl, UnderflowControl and OverflowControl", default=0, length=2),
637636
"iChannelNames" / DescriptorField(description="string descriptor index of the first logical channel name", default=0),
638-
"iTerminal" / DescriptorField(description="ID of the input terminal string description", default=0)
637+
"bmControls" / DescriptorField(description="OR combination of CopyProtectControl, ConnectorControl, OverloadControl, ClusterControl, UnderflowControl and OverflowControl", default=0, length=2),
638+
"iTerminal" / DescriptorField(description="ID of the input terminal string descriptor", default=0)
639639
)
640640

641641
OutputTerminalDescriptor = DescriptorFormat(
@@ -648,7 +648,19 @@ class SidebandProtocols(IntEnum):
648648
"bSourceID" / DescriptorField(description="ID of the unit or terminal which is connected to this terminal"),
649649
"bCSourceID" / DescriptorField(description="ID of the clock which is connected to this terminal"),
650650
"bmControls" / DescriptorField(description="OR combination of ClockFrequencyControl, CopyProtectControl, ConnectorControl, UnderflowControl>>2 and OverflowControl>>2", default=0, length=2),
651-
"iTerminal" / DescriptorField(description="ID of the input terminal string description", default=0)
651+
"iTerminal" / DescriptorField(description="ID of the input terminal string descriptor", default=0)
652+
)
653+
654+
FeatureUnitDescriptorLength = construct.Rebuild(construct.Int8ul, construct.len_(construct.this.bmaControls) * 4 + 6)
655+
656+
FeatureUnitDescriptor = DescriptorFormat(
657+
"bLength" / FeatureUnitDescriptorLength,
658+
"bDescriptorType" / DescriptorNumber(AudioClassSpecificStandardDescriptorNumbers.CS_INTERFACE),
659+
"bDescriptorSubtype" / DescriptorNumber(AudioClassSpecificACInterfaceDescriptorSubtypes.FEATURE_UNIT),
660+
"bUnitID" / DescriptorField(description="unique identifier for the unit within the audio function."),
661+
"bSourceID" / DescriptorField(description="ID of the unit or terminal which is connected to this terminal"),
662+
"bmaControls" / construct.Array((construct.this.bLength - 6)//4, construct.Int32ul) * "The control bitmap for all channels",
663+
"iFeature" / DescriptorField(description="ID of the feature unit string descriptor", default=0)
652664
)
653665

654666
AudioStreamingInterfaceDescriptor = DescriptorFormat(
@@ -884,7 +896,7 @@ def test_parse_clock_source_descriptor(self):
884896
parsed = ClockSourceDescriptor.parse([
885897
0x08, # Length
886898
0x24, # Type
887-
0x0B, # Subtype
899+
0x0A, # Subtype
888900
0x01, # Clock ID
889901
0x01, # Attributes
890902
0x01, # Controls
@@ -916,7 +928,7 @@ def test_build_clock_source_descriptor(self):
916928
self.assertEqual(data, bytes([
917929
0x08, # Length
918930
0x24, # Type
919-
0x0B, # Subtype
931+
0x0A, # Subtype
920932
0x01, # Clock ID
921933
0x01, # Attributes
922934
0x01, # Controls
@@ -936,8 +948,8 @@ def test_parse_input_terminal_descriptor(self):
936948
0x01, # Clock ID
937949
0x02, # Number of channels
938950
0x03, 0x00, 0x00, 0x00, # Channel configuration
939-
0x00, # First channel name
940-
0x00, 0x00, # Controls
951+
0x23, # First channel name
952+
0x05, 0x00, # Controls
941953
0x42 # Terminal name
942954
])
943955

@@ -951,7 +963,8 @@ def test_parse_input_terminal_descriptor(self):
951963
self.assertEqual(parsed.bCSourceID, 0x01)
952964
self.assertEqual(parsed.bNrChannels, 0x02)
953965
self.assertEqual(parsed.bmChannelConfig, 0x0003)
954-
self.assertEqual(parsed.iChannelNames, 0x00)
966+
self.assertEqual(parsed.iChannelNames, 0x23)
967+
self.assertEqual(parsed.bmControls, 5)
955968
self.assertEqual(parsed.iTerminal, 0x42)
956969

957970
def test_build_input_terminal_descriptor(self):
@@ -962,6 +975,8 @@ def test_build_input_terminal_descriptor(self):
962975
'bCSourceID': 1,
963976
'bNrChannels': 2,
964977
'bmChannelConfig': 3,
978+
'iChannelNames': 0x23,
979+
'bmControls': 5,
965980
'iTerminal': 0x42,
966981
})
967982

@@ -976,8 +991,8 @@ def test_build_input_terminal_descriptor(self):
976991
0x01, # Clock ID
977992
0x02, # Number of channels
978993
0x03, 0x00, 0x00, 0x00, # Channel configuration
979-
0x00, # First channel name
980-
0x00, 0x00, # Controls
994+
0x23, # First channel name
995+
0x05, 0x00, # Controls
981996
0x42 # Terminal name
982997
]))
983998

@@ -1032,6 +1047,51 @@ def test_build_output_terminal_descriptor(self):
10321047
0x42 # Terminal name
10331048
]))
10341049

1050+
def test_parse_feature_unit_descriptor(self):
1051+
# Parse the relevant descriptor ...
1052+
parsed = FeatureUnitDescriptor.parse([
1053+
0x12, # Length
1054+
0x24, # Type
1055+
0x06, # Subtype
1056+
0x06, # Unit ID
1057+
0x09, # Source ID
1058+
0x01, 0x00, 0x00, 0x00, # Controls 0
1059+
0x02, 0x00, 0x00, 0x00, # Controls 1
1060+
0x03, 0x00, 0x00, 0x00, # Controls 2
1061+
0x42 # Unit name
1062+
])
1063+
1064+
# ... and check the descriptor's fields.
1065+
self.assertEqual(parsed.bLength, 18)
1066+
self.assertEqual(parsed.bDescriptorType, AudioClassSpecificStandardDescriptorNumbers.CS_INTERFACE)
1067+
self.assertEqual(parsed.bDescriptorSubtype, AudioClassSpecificACInterfaceDescriptorSubtypes.FEATURE_UNIT)
1068+
self.assertEqual(parsed.bUnitID, 0x06)
1069+
self.assertEqual(parsed.bSourceID, 0x09)
1070+
self.assertEqual(parsed.bmaControls, [0x0001, 0x0002, 0x0003])
1071+
self.assertEqual(parsed.iFeature, 0x42)
1072+
1073+
def test_build_feature_unit_descriptor(self):
1074+
# Build the relevant descriptor
1075+
data = FeatureUnitDescriptor.build({
1076+
'bUnitID': 6,
1077+
'bSourceID': 9,
1078+
'bmaControls': [1, 2, 3],
1079+
'iFeature': 0x42,
1080+
})
1081+
1082+
# ... and check the binary output
1083+
self.assertEqual(data, bytes([
1084+
0x12, # Length
1085+
0x24, # Type
1086+
0x06, # Subtype
1087+
0x06, # Unit ID
1088+
0x09, # Source ID
1089+
0x01, 0x00, 0x00, 0x00, # Controls 0
1090+
0x02, 0x00, 0x00, 0x00, # Controls 1
1091+
0x03, 0x00, 0x00, 0x00, # Controls 2
1092+
0x42 # Unit name
1093+
]))
1094+
10351095
def test_parse_audio_streaming_interface_descriptor(self):
10361096
# Parse the relevant descriptor ...
10371097
parsed = AudioStreamingInterfaceDescriptor.parse([

0 commit comments

Comments
 (0)