1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/midi/usb_midi_descriptor_parser.h"
9 #include "base/logging.h"
15 // The constants below are specified in USB spec, USB audio spec
20 TYPE_CONFIGURATION
= 2,
24 TYPE_DEVICE_QUALIFIER
= 6,
25 TYPE_OTHER_SPEED_CONFIGURATION
= 7,
26 TYPE_INTERFACE_POWER
= 8,
28 TYPE_CS_INTERFACE
= 36,
29 TYPE_CS_ENDPOINT
= 37,
32 enum DescriptorSubType
{
33 SUBTYPE_MS_DESCRIPTOR_UNDEFINED
= 0,
34 SUBTYPE_MS_HEADER
= 1,
35 SUBTYPE_MIDI_IN_JACK
= 2,
36 SUBTYPE_MIDI_OUT_JACK
= 3,
41 JACK_TYPE_UNDEFINED
= 0,
42 JACK_TYPE_EMBEDDED
= 1,
43 JACK_TYPE_EXTERNAL
= 2,
46 const uint8 kAudioInterfaceClass
= 1;
47 const uint8 kAudioMidiInterfaceSubclass
= 3;
51 explicit JackMatcher(uint8 id
) : id_(id
) {}
53 bool operator() (const UsbMidiJack
& jack
) const {
54 return jack
.jack_id
== id_
;
63 UsbMidiDescriptorParser::UsbMidiDescriptorParser()
64 : is_parsing_usb_midi_interface_(false),
65 current_endpoint_address_(0),
66 current_cable_number_(0) {}
68 UsbMidiDescriptorParser::~UsbMidiDescriptorParser() {}
70 bool UsbMidiDescriptorParser::Parse(UsbMidiDevice
* device
,
73 std::vector
<UsbMidiJack
>* jacks
) {
75 bool result
= ParseInternal(device
, data
, size
, jacks
);
82 bool UsbMidiDescriptorParser::ParseInternal(UsbMidiDevice
* device
,
85 std::vector
<UsbMidiJack
>* jacks
) {
86 for (const uint8
* current
= data
;
87 current
< data
+ size
;
88 current
+= current
[0]) {
89 uint8 length
= current
[0];
91 DVLOG(1) << "Descriptor Type is not accessible.";
94 if (current
+ length
> data
+ size
) {
95 DVLOG(1) << "The header size is incorrect.";
98 DescriptorType descriptor_type
= static_cast<DescriptorType
>(current
[1]);
99 if (descriptor_type
!= TYPE_INTERFACE
&& !is_parsing_usb_midi_interface_
)
102 switch (descriptor_type
) {
104 if (!ParseInterface(current
, length
))
107 case TYPE_CS_INTERFACE
:
108 // We are assuming that the corresponding INTERFACE precedes
109 // the CS_INTERFACE descriptor, as specified.
110 if (!ParseCSInterface(device
, current
, length
))
114 // We are assuming that endpoints are contained in an interface.
115 if (!ParseEndpoint(current
, length
))
118 case TYPE_CS_ENDPOINT
:
119 // We are assuming that the corresponding ENDPOINT precedes
120 // the CS_ENDPOINT descriptor, as specified.
121 if (!ParseCSEndpoint(current
, length
, jacks
))
125 // Ignore uninteresting types.
132 bool UsbMidiDescriptorParser::ParseInterface(const uint8
* data
, size_t size
) {
134 DVLOG(1) << "INTERFACE header size is incorrect.";
137 incomplete_jacks_
.clear();
139 uint8 interface_class
= data
[5];
140 uint8 interface_subclass
= data
[6];
142 // All descriptors of endpoints contained in this interface
143 // precede the next INTERFACE descriptor.
144 is_parsing_usb_midi_interface_
=
145 interface_class
== kAudioInterfaceClass
&&
146 interface_subclass
== kAudioMidiInterfaceSubclass
;
150 bool UsbMidiDescriptorParser::ParseCSInterface(UsbMidiDevice
* device
,
153 // Descriptor Type and Descriptor Subtype should be accessible.
155 DVLOG(1) << "CS_INTERFACE header size is incorrect.";
159 DescriptorSubType subtype
= static_cast<DescriptorSubType
>(data
[2]);
161 if (subtype
!= SUBTYPE_MIDI_OUT_JACK
&&
162 subtype
!= SUBTYPE_MIDI_IN_JACK
)
166 DVLOG(1) << "CS_INTERFACE (MIDI JACK) header size is incorrect.";
169 uint8 jack_type
= data
[3];
171 if (jack_type
== JACK_TYPE_EMBEDDED
) {
172 // We can't determine the associated endpoint now.
173 incomplete_jacks_
.push_back(UsbMidiJack(device
, id
, 0, 0));
178 bool UsbMidiDescriptorParser::ParseEndpoint(const uint8
* data
, size_t size
) {
180 DVLOG(1) << "ENDPOINT header size is incorrect.";
183 current_endpoint_address_
= data
[2];
184 current_cable_number_
= 0;
188 bool UsbMidiDescriptorParser::ParseCSEndpoint(const uint8
* data
,
190 std::vector
<UsbMidiJack
>* jacks
) {
191 const size_t kSizeForEmptyJacks
= 4;
192 // CS_ENDPOINT must be of size 4 + n where n is the number of associated
194 if (size
< kSizeForEmptyJacks
) {
195 DVLOG(1) << "CS_ENDPOINT header size is incorrect.";
198 uint8 num_jacks
= data
[3];
199 if (size
!= kSizeForEmptyJacks
+ num_jacks
) {
200 DVLOG(1) << "CS_ENDPOINT header size is incorrect.";
204 for (size_t i
= 0; i
< num_jacks
; ++i
) {
205 uint8 jack
= data
[kSizeForEmptyJacks
+ i
];
206 std::vector
<UsbMidiJack
>::iterator it
=
207 std::find_if(incomplete_jacks_
.begin(),
208 incomplete_jacks_
.end(),
210 if (it
== incomplete_jacks_
.end()) {
211 DVLOG(1) << "A non-existing MIDI jack is associated.";
214 if (current_cable_number_
> 0xf) {
215 DVLOG(1) << "Cable number should range from 0x0 to 0xf.";
218 // CS_ENDPOINT follows ENDPOINT and hence we can use the following
220 it
->cable_number
= current_cable_number_
++;
221 it
->endpoint_address
= current_endpoint_address_
;
222 jacks
->push_back(*it
);
223 incomplete_jacks_
.erase(it
);
228 void UsbMidiDescriptorParser::Clear() {
229 is_parsing_usb_midi_interface_
= false;
230 current_endpoint_address_
= 0;
231 current_cable_number_
= 0;
232 incomplete_jacks_
.clear();