Skip to content

Commit 5cc4faf

Browse files
authored
Merge branch 'greatscottgadgets:master' into master
2 parents 6469bca + a983073 commit 5cc4faf

File tree

2 files changed

+69
-8
lines changed

2 files changed

+69
-8
lines changed

usb_protocol/emitters/descriptors/uac2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def _pre_emit(self):
2626
ClockSelectorDescriptorFootEmitter = emitter_for_format(ClockSelectorDescriptorFoot)
2727
InputTerminalDescriptorEmitter = emitter_for_format(InputTerminalDescriptor)
2828
OutputTerminalDescriptorEmitter = emitter_for_format(OutputTerminalDescriptor)
29+
FeatureUnitDescriptorEmitter = emitter_for_format(FeatureUnitDescriptor)
2930
AudioStreamingInterfaceDescriptorEmitter = emitter_for_format(AudioStreamingInterfaceDescriptor)
3031
ClassSpecificAudioStreamingInterfaceDescriptorEmitter = emitter_for_format(ClassSpecificAudioStreamingInterfaceDescriptor)
3132
TypeIFormatTypeDescriptorEmitter = emitter_for_format(TypeIFormatTypeDescriptor)

usb_protocol/types/descriptors/uac2.py

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,9 @@ class SidebandProtocols(IntEnum):
650650
"bCSourceID" / DescriptorField(description="ID of the clock which is connected to this terminal"),
651651
"bNrChannels" / DescriptorField(description="number of logical output channels in the terminal’s output channel cluster"),
652652
"bmChannelConfig" / DescriptorField(description="describes the spatial location of the logical channels", default=0, length=4),
653-
"bmControls" / DescriptorField(description="OR combination of ClockFrequencyControl, CopyProtectControl, ConnectorControl, ClusterControl, UnderflowControl and OverflowControl", default=0, length=2),
654653
"iChannelNames" / DescriptorField(description="string descriptor index of the first logical channel name", default=0),
655-
"iTerminal" / DescriptorField(description="ID of the input terminal string description", default=0)
654+
"bmControls" / DescriptorField(description="OR combination of CopyProtectControl, ConnectorControl, OverloadControl, ClusterControl, UnderflowControl and OverflowControl", default=0, length=2),
655+
"iTerminal" / DescriptorField(description="ID of the input terminal string descriptor", default=0)
656656
)
657657

658658
OutputTerminalDescriptor = DescriptorFormat(
@@ -665,7 +665,19 @@ class SidebandProtocols(IntEnum):
665665
"bSourceID" / DescriptorField(description="ID of the unit or terminal which is connected to this terminal"),
666666
"bCSourceID" / DescriptorField(description="ID of the clock which is connected to this terminal"),
667667
"bmControls" / DescriptorField(description="OR combination of ClockFrequencyControl, CopyProtectControl, ConnectorControl, UnderflowControl>>2 and OverflowControl>>2", default=0, length=2),
668-
"iTerminal" / DescriptorField(description="ID of the input terminal string description", default=0)
668+
"iTerminal" / DescriptorField(description="ID of the input terminal string descriptor", default=0)
669+
)
670+
671+
FeatureUnitDescriptorLength = construct.Rebuild(construct.Int8ul, construct.len_(construct.this.bmaControls) * 4 + 6)
672+
673+
FeatureUnitDescriptor = DescriptorFormat(
674+
"bLength" / FeatureUnitDescriptorLength,
675+
"bDescriptorType" / DescriptorNumber(AudioClassSpecificStandardDescriptorNumbers.CS_INTERFACE),
676+
"bDescriptorSubtype" / DescriptorNumber(AudioClassSpecificACInterfaceDescriptorSubtypes.FEATURE_UNIT),
677+
"bUnitID" / DescriptorField(description="unique identifier for the unit within the audio function."),
678+
"bSourceID" / DescriptorField(description="ID of the unit or terminal which is connected to this terminal"),
679+
"bmaControls" / construct.Array((construct.this.bLength - 6)//4, construct.Int32ul) * "The control bitmap for all channels",
680+
"iFeature" / DescriptorField(description="ID of the feature unit string descriptor", default=0)
669681
)
670682

671683
AudioStreamingInterfaceDescriptor = DescriptorFormat(
@@ -1002,8 +1014,8 @@ def test_parse_input_terminal_descriptor(self):
10021014
0x01, # Clock ID
10031015
0x02, # Number of channels
10041016
0x03, 0x00, 0x00, 0x00, # Channel configuration
1005-
0x00, # First channel name
1006-
0x00, 0x00, # Controls
1017+
0x23, # First channel name
1018+
0x05, 0x00, # Controls
10071019
0x42 # Terminal name
10081020
])
10091021

@@ -1017,7 +1029,8 @@ def test_parse_input_terminal_descriptor(self):
10171029
self.assertEqual(parsed.bCSourceID, 0x01)
10181030
self.assertEqual(parsed.bNrChannels, 0x02)
10191031
self.assertEqual(parsed.bmChannelConfig, 0x0003)
1020-
self.assertEqual(parsed.iChannelNames, 0x00)
1032+
self.assertEqual(parsed.iChannelNames, 0x23)
1033+
self.assertEqual(parsed.bmControls, 5)
10211034
self.assertEqual(parsed.iTerminal, 0x42)
10221035

10231036
def test_build_input_terminal_descriptor(self):
@@ -1028,6 +1041,8 @@ def test_build_input_terminal_descriptor(self):
10281041
'bCSourceID': 1,
10291042
'bNrChannels': 2,
10301043
'bmChannelConfig': 3,
1044+
'iChannelNames': 0x23,
1045+
'bmControls': 5,
10311046
'iTerminal': 0x42,
10321047
})
10331048

@@ -1042,8 +1057,8 @@ def test_build_input_terminal_descriptor(self):
10421057
0x01, # Clock ID
10431058
0x02, # Number of channels
10441059
0x03, 0x00, 0x00, 0x00, # Channel configuration
1045-
0x00, # First channel name
1046-
0x00, 0x00, # Controls
1060+
0x23, # First channel name
1061+
0x05, 0x00, # Controls
10471062
0x42 # Terminal name
10481063
]))
10491064

@@ -1098,6 +1113,51 @@ def test_build_output_terminal_descriptor(self):
10981113
0x42 # Terminal name
10991114
]))
11001115

1116+
def test_parse_feature_unit_descriptor(self):
1117+
# Parse the relevant descriptor ...
1118+
parsed = FeatureUnitDescriptor.parse([
1119+
0x12, # Length
1120+
0x24, # Type
1121+
0x06, # Subtype
1122+
0x06, # Unit ID
1123+
0x09, # Source ID
1124+
0x01, 0x00, 0x00, 0x00, # Controls 0
1125+
0x02, 0x00, 0x00, 0x00, # Controls 1
1126+
0x03, 0x00, 0x00, 0x00, # Controls 2
1127+
0x42 # Unit name
1128+
])
1129+
1130+
# ... and check the descriptor's fields.
1131+
self.assertEqual(parsed.bLength, 18)
1132+
self.assertEqual(parsed.bDescriptorType, AudioClassSpecificStandardDescriptorNumbers.CS_INTERFACE)
1133+
self.assertEqual(parsed.bDescriptorSubtype, AudioClassSpecificACInterfaceDescriptorSubtypes.FEATURE_UNIT)
1134+
self.assertEqual(parsed.bUnitID, 0x06)
1135+
self.assertEqual(parsed.bSourceID, 0x09)
1136+
self.assertEqual(parsed.bmaControls, [0x0001, 0x0002, 0x0003])
1137+
self.assertEqual(parsed.iFeature, 0x42)
1138+
1139+
def test_build_feature_unit_descriptor(self):
1140+
# Build the relevant descriptor
1141+
data = FeatureUnitDescriptor.build({
1142+
'bUnitID': 6,
1143+
'bSourceID': 9,
1144+
'bmaControls': [1, 2, 3],
1145+
'iFeature': 0x42,
1146+
})
1147+
1148+
# ... and check the binary output
1149+
self.assertEqual(data, bytes([
1150+
0x12, # Length
1151+
0x24, # Type
1152+
0x06, # Subtype
1153+
0x06, # Unit ID
1154+
0x09, # Source ID
1155+
0x01, 0x00, 0x00, 0x00, # Controls 0
1156+
0x02, 0x00, 0x00, 0x00, # Controls 1
1157+
0x03, 0x00, 0x00, 0x00, # Controls 2
1158+
0x42 # Unit name
1159+
]))
1160+
11011161
def test_parse_audio_streaming_interface_descriptor(self):
11021162
# Parse the relevant descriptor ...
11031163
parsed = AudioStreamingInterfaceDescriptor.parse([

0 commit comments

Comments
 (0)