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"
10 #include "base/strings/stringprintf.h"
17 // The constants below are specified in USB spec, USB audio spec
22 TYPE_CONFIGURATION
= 2,
26 TYPE_DEVICE_QUALIFIER
= 6,
27 TYPE_OTHER_SPEED_CONFIGURATION
= 7,
28 TYPE_INTERFACE_POWER
= 8,
30 TYPE_CS_INTERFACE
= 36,
31 TYPE_CS_ENDPOINT
= 37,
34 enum DescriptorSubType
{
35 SUBTYPE_MS_DESCRIPTOR_UNDEFINED
= 0,
36 SUBTYPE_MS_HEADER
= 1,
37 SUBTYPE_MIDI_IN_JACK
= 2,
38 SUBTYPE_MIDI_OUT_JACK
= 3,
43 JACK_TYPE_UNDEFINED
= 0,
44 JACK_TYPE_EMBEDDED
= 1,
45 JACK_TYPE_EXTERNAL
= 2,
48 const uint8 kAudioInterfaceClass
= 1;
49 const uint8 kAudioMidiInterfaceSubclass
= 3;
53 explicit JackMatcher(uint8 id
) : id_(id
) {}
55 bool operator() (const UsbMidiJack
& jack
) const {
56 return jack
.jack_id
== id_
;
63 int DecodeBcd(uint8 byte
) {
64 DCHECK_LT((byte
& 0xf0) >> 4, 0xa);
65 DCHECK_LT(byte
& 0x0f, 0xa);
66 return ((byte
& 0xf0) >> 4) * 10 + (byte
& 0x0f);
71 std::string
UsbMidiDescriptorParser::DeviceInfo::BcdVersionToString(
73 return base::StringPrintf("%d.%02d", DecodeBcd(version
>> 8),
74 DecodeBcd(version
& 0xff));
77 UsbMidiDescriptorParser::UsbMidiDescriptorParser()
78 : is_parsing_usb_midi_interface_(false),
79 current_endpoint_address_(0),
80 current_cable_number_(0) {}
82 UsbMidiDescriptorParser::~UsbMidiDescriptorParser() {}
84 bool UsbMidiDescriptorParser::Parse(UsbMidiDevice
* device
,
87 std::vector
<UsbMidiJack
>* jacks
) {
89 bool result
= ParseInternal(device
, data
, size
, jacks
);
96 bool UsbMidiDescriptorParser::ParseDeviceInfo(
97 const uint8
* data
, size_t size
, DeviceInfo
* info
) {
99 for (const uint8
* current
= data
;
100 current
< data
+ size
;
101 current
+= current
[0]) {
102 uint8 length
= current
[0];
104 DVLOG(1) << "Descriptor Type is not accessible.";
107 if (current
+ length
> data
+ size
) {
108 DVLOG(1) << "The header size is incorrect.";
111 DescriptorType descriptor_type
= static_cast<DescriptorType
>(current
[1]);
112 if (descriptor_type
!= TYPE_DEVICE
)
114 // We assume that ParseDevice doesn't modify |*info| if it returns false.
115 return ParseDevice(current
, length
, info
);
117 // No DEVICE descriptor is found.
121 bool UsbMidiDescriptorParser::ParseInternal(UsbMidiDevice
* device
,
124 std::vector
<UsbMidiJack
>* jacks
) {
125 for (const uint8
* current
= data
;
126 current
< data
+ size
;
127 current
+= current
[0]) {
128 uint8 length
= current
[0];
130 DVLOG(1) << "Descriptor Type is not accessible.";
133 if (current
+ length
> data
+ size
) {
134 DVLOG(1) << "The header size is incorrect.";
137 DescriptorType descriptor_type
= static_cast<DescriptorType
>(current
[1]);
138 if (descriptor_type
!= TYPE_INTERFACE
&& !is_parsing_usb_midi_interface_
)
141 switch (descriptor_type
) {
143 if (!ParseInterface(current
, length
))
146 case TYPE_CS_INTERFACE
:
147 // We are assuming that the corresponding INTERFACE precedes
148 // the CS_INTERFACE descriptor, as specified.
149 if (!ParseCSInterface(device
, current
, length
))
153 // We are assuming that endpoints are contained in an interface.
154 if (!ParseEndpoint(current
, length
))
157 case TYPE_CS_ENDPOINT
:
158 // We are assuming that the corresponding ENDPOINT precedes
159 // the CS_ENDPOINT descriptor, as specified.
160 if (!ParseCSEndpoint(current
, length
, jacks
))
164 // Ignore uninteresting types.
171 bool UsbMidiDescriptorParser::ParseDevice(
172 const uint8
* data
, size_t size
, DeviceInfo
* info
) {
174 DVLOG(1) << "DEVICE header size is incorrect.";
178 info
->vendor_id
= data
[8] | (data
[9] << 8);
179 info
->product_id
= data
[0xa] | (data
[0xb] << 8);
180 info
->bcd_device_version
= data
[0xc] | (data
[0xd] << 8);
181 info
->manufacturer_index
= data
[0xe];
182 info
->product_index
= data
[0xf];
186 bool UsbMidiDescriptorParser::ParseInterface(const uint8
* data
, size_t size
) {
188 DVLOG(1) << "INTERFACE header size is incorrect.";
191 incomplete_jacks_
.clear();
193 uint8 interface_class
= data
[5];
194 uint8 interface_subclass
= data
[6];
196 // All descriptors of endpoints contained in this interface
197 // precede the next INTERFACE descriptor.
198 is_parsing_usb_midi_interface_
=
199 interface_class
== kAudioInterfaceClass
&&
200 interface_subclass
== kAudioMidiInterfaceSubclass
;
204 bool UsbMidiDescriptorParser::ParseCSInterface(UsbMidiDevice
* device
,
207 // Descriptor Type and Descriptor Subtype should be accessible.
209 DVLOG(1) << "CS_INTERFACE header size is incorrect.";
213 DescriptorSubType subtype
= static_cast<DescriptorSubType
>(data
[2]);
215 if (subtype
!= SUBTYPE_MIDI_OUT_JACK
&&
216 subtype
!= SUBTYPE_MIDI_IN_JACK
)
220 DVLOG(1) << "CS_INTERFACE (MIDI JACK) header size is incorrect.";
223 uint8 jack_type
= data
[3];
225 if (jack_type
== JACK_TYPE_EMBEDDED
) {
226 // We can't determine the associated endpoint now.
227 incomplete_jacks_
.push_back(UsbMidiJack(device
, id
, 0, 0));
232 bool UsbMidiDescriptorParser::ParseEndpoint(const uint8
* data
, size_t size
) {
234 DVLOG(1) << "ENDPOINT header size is incorrect.";
237 current_endpoint_address_
= data
[2];
238 current_cable_number_
= 0;
242 bool UsbMidiDescriptorParser::ParseCSEndpoint(const uint8
* data
,
244 std::vector
<UsbMidiJack
>* jacks
) {
245 const size_t kSizeForEmptyJacks
= 4;
246 // CS_ENDPOINT must be of size 4 + n where n is the number of associated
248 if (size
< kSizeForEmptyJacks
) {
249 DVLOG(1) << "CS_ENDPOINT header size is incorrect.";
252 uint8 num_jacks
= data
[3];
253 if (size
!= kSizeForEmptyJacks
+ num_jacks
) {
254 DVLOG(1) << "CS_ENDPOINT header size is incorrect.";
258 for (size_t i
= 0; i
< num_jacks
; ++i
) {
259 uint8 jack
= data
[kSizeForEmptyJacks
+ i
];
260 std::vector
<UsbMidiJack
>::iterator it
=
261 std::find_if(incomplete_jacks_
.begin(),
262 incomplete_jacks_
.end(),
264 if (it
== incomplete_jacks_
.end()) {
265 DVLOG(1) << "A non-existing MIDI jack is associated.";
268 if (current_cable_number_
> 0xf) {
269 DVLOG(1) << "Cable number should range from 0x0 to 0xf.";
272 // CS_ENDPOINT follows ENDPOINT and hence we can use the following
274 it
->cable_number
= current_cable_number_
++;
275 it
->endpoint_address
= current_endpoint_address_
;
276 jacks
->push_back(*it
);
277 incomplete_jacks_
.erase(it
);
282 void UsbMidiDescriptorParser::Clear() {
283 is_parsing_usb_midi_interface_
= false;
284 current_endpoint_address_
= 0;
285 current_cable_number_
= 0;
286 incomplete_jacks_
.clear();