• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 namespace media {
12 
13 namespace {
14 
15 // The constants below are specified in USB spec, USB audio spec
16 // and USB midi spec.
17 
18 enum 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 
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,
37   SUBTYPE_ELEMENT = 4,
38 };
39 
40 enum JackType {
41   JACK_TYPE_UNDEFINED = 0,
42   JACK_TYPE_EMBEDDED = 1,
43   JACK_TYPE_EXTERNAL = 2,
44 };
45 
46 const uint8 kAudioInterfaceClass = 1;
47 const uint8 kAudioMidiInterfaceSubclass = 3;
48 
49 class JackMatcher {
50  public:
JackMatcher(uint8 id)51   explicit JackMatcher(uint8 id) : id_(id) {}
52 
operator ()(const UsbMidiJack & jack) const53   bool operator() (const UsbMidiJack& jack) const {
54     return jack.jack_id == id_;
55   }
56 
57  private:
58   uint8 id_;
59 };
60 
61 }  // namespace
62 
UsbMidiDescriptorParser()63 UsbMidiDescriptorParser::UsbMidiDescriptorParser()
64     : is_parsing_usb_midi_interface_(false),
65       current_endpoint_address_(0),
66       current_cable_number_(0) {}
67 
~UsbMidiDescriptorParser()68 UsbMidiDescriptorParser::~UsbMidiDescriptorParser() {}
69 
Parse(UsbMidiDevice * device,const uint8 * data,size_t size,std::vector<UsbMidiJack> * jacks)70 bool 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 
ParseInternal(UsbMidiDevice * device,const uint8 * data,size_t size,std::vector<UsbMidiJack> * jacks)82 bool 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 
ParseInterface(const uint8 * data,size_t size)132 bool 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 
ParseCSInterface(UsbMidiDevice * device,const uint8 * data,size_t size)150 bool 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 
ParseEndpoint(const uint8 * data,size_t size)178 bool 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 
ParseCSEndpoint(const uint8 * data,size_t size,std::vector<UsbMidiJack> * jacks)188 bool 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 
Clear()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();
233 }
234 
235 }  // namespace media
236