Update V8 to version 4.7.42.
[chromium-blink-merge.git] / media / midi / usb_midi_descriptor_parser.cc
blob9d0af75be9ce6791d47fd7112ff48f5248a9c2e4
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"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "base/strings/stringprintf.h"
12 namespace media {
13 namespace midi {
15 namespace {
17 // The constants below are specified in USB spec, USB audio spec
18 // and USB midi spec.
20 enum DescriptorType {
21 TYPE_DEVICE = 1,
22 TYPE_CONFIGURATION = 2,
23 TYPE_STRING = 3,
24 TYPE_INTERFACE = 4,
25 TYPE_ENDPOINT = 5,
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,
39 SUBTYPE_ELEMENT = 4,
42 enum JackType {
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;
51 class JackMatcher {
52 public:
53 explicit JackMatcher(uint8 id) : id_(id) {}
55 bool operator() (const UsbMidiJack& jack) const {
56 return jack.jack_id == id_;
59 private:
60 uint8 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);
69 } // namespace
71 std::string UsbMidiDescriptorParser::DeviceInfo::BcdVersionToString(
72 uint16 version) {
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,
85 const uint8* data,
86 size_t size,
87 std::vector<UsbMidiJack>* jacks) {
88 jacks->clear();
89 bool result = ParseInternal(device, data, size, jacks);
90 if (!result)
91 jacks->clear();
92 Clear();
93 return result;
96 bool UsbMidiDescriptorParser::ParseDeviceInfo(
97 const uint8* data, size_t size, DeviceInfo* info) {
98 *info = DeviceInfo();
99 for (const uint8* current = data;
100 current < data + size;
101 current += current[0]) {
102 uint8 length = current[0];
103 if (length < 2) {
104 DVLOG(1) << "Descriptor Type is not accessible.";
105 return false;
107 if (current + length > data + size) {
108 DVLOG(1) << "The header size is incorrect.";
109 return false;
111 DescriptorType descriptor_type = static_cast<DescriptorType>(current[1]);
112 if (descriptor_type != TYPE_DEVICE)
113 continue;
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.
118 return false;
121 bool UsbMidiDescriptorParser::ParseInternal(UsbMidiDevice* device,
122 const uint8* data,
123 size_t size,
124 std::vector<UsbMidiJack>* jacks) {
125 for (const uint8* current = data;
126 current < data + size;
127 current += current[0]) {
128 uint8 length = current[0];
129 if (length < 2) {
130 DVLOG(1) << "Descriptor Type is not accessible.";
131 return false;
133 if (current + length > data + size) {
134 DVLOG(1) << "The header size is incorrect.";
135 return false;
137 DescriptorType descriptor_type = static_cast<DescriptorType>(current[1]);
138 if (descriptor_type != TYPE_INTERFACE && !is_parsing_usb_midi_interface_)
139 continue;
141 switch (descriptor_type) {
142 case TYPE_INTERFACE:
143 if (!ParseInterface(current, length))
144 return false;
145 break;
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))
150 return false;
151 break;
152 case TYPE_ENDPOINT:
153 // We are assuming that endpoints are contained in an interface.
154 if (!ParseEndpoint(current, length))
155 return false;
156 break;
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))
161 return false;
162 break;
163 default:
164 // Ignore uninteresting types.
165 break;
168 return true;
171 bool UsbMidiDescriptorParser::ParseDevice(
172 const uint8* data, size_t size, DeviceInfo* info) {
173 if (size < 0x12) {
174 DVLOG(1) << "DEVICE header size is incorrect.";
175 return false;
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];
183 return true;
186 bool UsbMidiDescriptorParser::ParseInterface(const uint8* data, size_t size) {
187 if (size != 9) {
188 DVLOG(1) << "INTERFACE header size is incorrect.";
189 return false;
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;
201 return true;
204 bool UsbMidiDescriptorParser::ParseCSInterface(UsbMidiDevice* device,
205 const uint8* data,
206 size_t size) {
207 // Descriptor Type and Descriptor Subtype should be accessible.
208 if (size < 3) {
209 DVLOG(1) << "CS_INTERFACE header size is incorrect.";
210 return false;
213 DescriptorSubType subtype = static_cast<DescriptorSubType>(data[2]);
215 if (subtype != SUBTYPE_MIDI_OUT_JACK &&
216 subtype != SUBTYPE_MIDI_IN_JACK)
217 return true;
219 if (size < 6) {
220 DVLOG(1) << "CS_INTERFACE (MIDI JACK) header size is incorrect.";
221 return false;
223 uint8 jack_type = data[3];
224 uint8 id = data[4];
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));
229 return true;
232 bool UsbMidiDescriptorParser::ParseEndpoint(const uint8* data, size_t size) {
233 if (size < 4) {
234 DVLOG(1) << "ENDPOINT header size is incorrect.";
235 return false;
237 current_endpoint_address_ = data[2];
238 current_cable_number_ = 0;
239 return true;
242 bool UsbMidiDescriptorParser::ParseCSEndpoint(const uint8* data,
243 size_t size,
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
247 // jacks.
248 if (size < kSizeForEmptyJacks) {
249 DVLOG(1) << "CS_ENDPOINT header size is incorrect.";
250 return false;
252 uint8 num_jacks = data[3];
253 if (size != kSizeForEmptyJacks + num_jacks) {
254 DVLOG(1) << "CS_ENDPOINT header size is incorrect.";
255 return false;
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(),
263 JackMatcher(jack));
264 if (it == incomplete_jacks_.end()) {
265 DVLOG(1) << "A non-existing MIDI jack is associated.";
266 return false;
268 if (current_cable_number_ > 0xf) {
269 DVLOG(1) << "Cable number should range from 0x0 to 0xf.";
270 return false;
272 // CS_ENDPOINT follows ENDPOINT and hence we can use the following
273 // member variables.
274 it->cable_number = current_cable_number_++;
275 it->endpoint_address = current_endpoint_address_;
276 jacks->push_back(*it);
277 incomplete_jacks_.erase(it);
279 return true;
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();
289 } // namespace midi
290 } // namespace media