@@ -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
658658OutputTerminalDescriptor = 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
671683AudioStreamingInterfaceDescriptor = 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