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. 4 5#include "media/midi/usb_midi_descriptor_parser.h" 6 7#include <algorithm> 8 9#include "base/logging.h" 10 11namespace media { 12 13namespace { 14 15// The constants below are specified in USB spec, USB audio spec 16// and USB midi spec. 17 18enum DescriptorType { 19 TYPE_DEVICE = 1, 20 TYPE_CONFIGURATION = 2, 21 TYPE_STRING = 3, 22 TYPE_INTERFACE = 4, 23 TYPE_ENDPOINT = 5, 24 TYPE_DEVICE_QUALIFIER = 6, 25 TYPE_OTHER_SPEED_CONFIGURATION = 7, 26 TYPE_INTERFACE_POWER = 8, 27 28 TYPE_CS_INTERFACE = 36, 29 TYPE_CS_ENDPOINT = 37, 30}; 31 32enum DescriptorSubType { 33 SUBTYPE_MS_DESCRIPTOR_UNDEFINED = 0, 34 SUBTYPE_MS_HEADER = 1, 35 SUBTYPE_MIDI_IN_JACK = 2, 36 SUBTYPE_MIDI_OUT_JACK = 3, 37 SUBTYPE_ELEMENT = 4, 38}; 39 40enum JackType { 41 JACK_TYPE_UNDEFINED = 0, 42 JACK_TYPE_EMBEDDED = 1, 43 JACK_TYPE_EXTERNAL = 2, 44}; 45 46const uint8 kAudioInterfaceClass = 1; 47const uint8 kAudioMidiInterfaceSubclass = 3; 48 49class JackMatcher { 50 public: 51 explicit JackMatcher(uint8 id) : id_(id) {} 52 53 bool operator() (const UsbMidiJack& jack) const { 54 return jack.jack_id == id_; 55 } 56 57 private: 58 uint8 id_; 59}; 60 61} // namespace 62 63UsbMidiDescriptorParser::UsbMidiDescriptorParser() 64 : is_parsing_usb_midi_interface_(false), 65 current_endpoint_address_(0), 66 current_cable_number_(0) {} 67 68UsbMidiDescriptorParser::~UsbMidiDescriptorParser() {} 69 70bool UsbMidiDescriptorParser::Parse(UsbMidiDevice* device, 71 const uint8* data, 72 size_t size, 73 std::vector<UsbMidiJack>* jacks) { 74 jacks->clear(); 75 bool result = ParseInternal(device, data, size, jacks); 76 if (!result) 77 jacks->clear(); 78 Clear(); 79 return result; 80} 81 82bool UsbMidiDescriptorParser::ParseInternal(UsbMidiDevice* device, 83 const uint8* data, 84 size_t size, 85 std::vector<UsbMidiJack>* jacks) { 86 for (const uint8* current = data; 87 current < data + size; 88 current += current[0]) { 89 uint8 length = current[0]; 90 if (length < 2) { 91 DVLOG(1) << "Descriptor Type is not accessible."; 92 return false; 93 } 94 if (current + length > data + size) { 95 DVLOG(1) << "The header size is incorrect."; 96 return false; 97 } 98 DescriptorType descriptor_type = static_cast<DescriptorType>(current[1]); 99 if (descriptor_type != TYPE_INTERFACE && !is_parsing_usb_midi_interface_) 100 continue; 101 102 switch (descriptor_type) { 103 case TYPE_INTERFACE: 104 if (!ParseInterface(current, length)) 105 return false; 106 break; 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)) 111 return false; 112 break; 113 case TYPE_ENDPOINT: 114 // We are assuming that endpoints are contained in an interface. 115 if (!ParseEndpoint(current, length)) 116 return false; 117 break; 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)) 122 return false; 123 break; 124 default: 125 // Ignore uninteresting types. 126 break; 127 } 128 } 129 return true; 130} 131 132bool UsbMidiDescriptorParser::ParseInterface(const uint8* data, size_t size) { 133 if (size != 9) { 134 DVLOG(1) << "INTERFACE header size is incorrect."; 135 return false; 136 } 137 incomplete_jacks_.clear(); 138 139 uint8 interface_class = data[5]; 140 uint8 interface_subclass = data[6]; 141 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; 147 return true; 148} 149 150bool UsbMidiDescriptorParser::ParseCSInterface(UsbMidiDevice* device, 151 const uint8* data, 152 size_t size) { 153 // Descriptor Type and Descriptor Subtype should be accessible. 154 if (size < 3) { 155 DVLOG(1) << "CS_INTERFACE header size is incorrect."; 156 return false; 157 } 158 159 DescriptorSubType subtype = static_cast<DescriptorSubType>(data[2]); 160 161 if (subtype != SUBTYPE_MIDI_OUT_JACK && 162 subtype != SUBTYPE_MIDI_IN_JACK) 163 return true; 164 165 if (size < 6) { 166 DVLOG(1) << "CS_INTERFACE (MIDI JACK) header size is incorrect."; 167 return false; 168 } 169 uint8 jack_type = data[3]; 170 uint8 id = data[4]; 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)); 174 } 175 return true; 176} 177 178bool UsbMidiDescriptorParser::ParseEndpoint(const uint8* data, size_t size) { 179 if (size < 4) { 180 DVLOG(1) << "ENDPOINT header size is incorrect."; 181 return false; 182 } 183 current_endpoint_address_ = data[2]; 184 current_cable_number_ = 0; 185 return true; 186} 187 188bool UsbMidiDescriptorParser::ParseCSEndpoint(const uint8* data, 189 size_t size, 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 193 // jacks. 194 if (size < kSizeForEmptyJacks) { 195 DVLOG(1) << "CS_ENDPOINT header size is incorrect."; 196 return false; 197 } 198 uint8 num_jacks = data[3]; 199 if (size != kSizeForEmptyJacks + num_jacks) { 200 DVLOG(1) << "CS_ENDPOINT header size is incorrect."; 201 return false; 202 } 203 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(), 209 JackMatcher(jack)); 210 if (it == incomplete_jacks_.end()) { 211 DVLOG(1) << "A non-existing MIDI jack is associated."; 212 return false; 213 } 214 if (current_cable_number_ > 0xf) { 215 DVLOG(1) << "Cable number should range from 0x0 to 0xf."; 216 return false; 217 } 218 // CS_ENDPOINT follows ENDPOINT and hence we can use the following 219 // member variables. 220 it->cable_number = current_cable_number_++; 221 it->endpoint_address = current_endpoint_address_; 222 jacks->push_back(*it); 223 incomplete_jacks_.erase(it); 224 } 225 return true; 226} 227 228void UsbMidiDescriptorParser::Clear() { 229 is_parsing_usb_midi_interface_ = false; 230 current_endpoint_address_ = 0; 231 current_cable_number_ = 0; 232 incomplete_jacks_.clear(); 233} 234 235} // namespace media 236