@@ -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
641641OutputTerminalDescriptor = 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
654666AudioStreamingInterfaceDescriptor = 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