1 // Copyright 2018 The Fuchsia 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 <ddk/debug.h>
6 #include <ddk/metadata.h>
7 #include <usb/usb.h>
8 #include <zircon/hw/usb/audio.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include "usb-composite.h"
14 #include "usb-interface.h"
15
16 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
17
18 #define NEXT_DESCRIPTOR(header) ((usb_descriptor_header_t*)((void*)header + header->bLength))
19
20 // returns whether the interface with the given id was removed.
usb_composite_remove_interface_by_id_locked(usb_composite_t * comp,uint8_t interface_id)21 static bool usb_composite_remove_interface_by_id_locked(usb_composite_t* comp,
22 uint8_t interface_id) {
23 usb_interface_t* intf;
24 usb_interface_t* tmp;
25
26 list_for_every_entry_safe(&comp->children, intf, tmp, usb_interface_t, node) {
27 if (usb_interface_contains_interface(intf, interface_id)) {
28 list_delete(&intf->node);
29 device_remove(intf->zxdev);
30 return true;
31 }
32 }
33 return false;
34 }
35
usb_composite_add_interface(usb_composite_t * comp,usb_interface_descriptor_t * interface_desc,size_t interface_desc_length)36 static zx_status_t usb_composite_add_interface(usb_composite_t* comp,
37 usb_interface_descriptor_t* interface_desc,
38 size_t interface_desc_length) {
39 usb_device_descriptor_t* device_desc = &comp->device_desc;
40 usb_interface_t* intf = calloc(1, sizeof(usb_interface_t));
41 if (!intf) {
42 free(interface_desc);
43 return ZX_ERR_NO_MEMORY;
44 }
45
46 intf->comp = comp;
47 intf->last_interface_id = interface_desc->bInterfaceNumber;
48 intf->descriptor = (usb_descriptor_header_t *)interface_desc;
49 intf->descriptor_length = interface_desc_length;
50
51 uint8_t usb_class, usb_subclass, usb_protocol;
52 if (interface_desc->bInterfaceClass == 0) {
53 usb_class = device_desc->bDeviceClass;
54 usb_subclass = device_desc->bDeviceSubClass;
55 usb_protocol = device_desc->bDeviceProtocol;
56 } else {
57 // class/subclass/protocol defined per-interface
58 usb_class = interface_desc->bInterfaceClass;
59 usb_subclass = interface_desc->bInterfaceSubClass;
60 usb_protocol = interface_desc->bInterfaceProtocol;
61 }
62
63 zx_status_t status = usb_interface_configure_endpoints(intf, interface_desc->bInterfaceNumber,
64 0);
65 if (status != ZX_OK) {
66 free(interface_desc);
67 free(intf);
68 return status;
69 }
70
71 mtx_lock(&comp->interface_mutex);
72 // need to do this first so usb_composite_set_interface() can be called from driver bind
73 list_add_head(&comp->children, &intf->node);
74 mtx_unlock(&comp->interface_mutex);
75
76 char name[20];
77 snprintf(name, sizeof(name), "ifc-%03d", interface_desc->bInterfaceNumber);
78
79 zx_device_prop_t props[] = {
80 { BIND_PROTOCOL, 0, ZX_PROTOCOL_USB },
81 { BIND_USB_VID, 0, device_desc->idVendor },
82 { BIND_USB_PID, 0, device_desc->idProduct },
83 { BIND_USB_CLASS, 0, usb_class },
84 { BIND_USB_SUBCLASS, 0, usb_subclass },
85 { BIND_USB_PROTOCOL, 0, usb_protocol },
86 };
87
88 device_add_args_t args = {
89 .version = DEVICE_ADD_ARGS_VERSION,
90 .name = name,
91 .ctx = intf,
92 .ops = &usb_interface_proto,
93 .proto_id = ZX_PROTOCOL_USB,
94 .proto_ops = &usb_device_protocol,
95 .props = props,
96 .prop_count = countof(props),
97 };
98
99 status = device_add(comp->zxdev, &args, &intf->zxdev);
100 if (status != ZX_OK) {
101 list_delete(&intf->node);
102 free(interface_desc);
103 free(intf);
104 }
105 return status;
106 }
107
usb_composite_add_interface_assoc(usb_composite_t * comp,usb_interface_assoc_descriptor_t * assoc_desc,size_t assoc_desc_length)108 static zx_status_t usb_composite_add_interface_assoc(usb_composite_t* comp,
109 usb_interface_assoc_descriptor_t* assoc_desc,
110 size_t assoc_desc_length) {
111 usb_device_descriptor_t* device_desc = &comp->device_desc;
112 usb_interface_t* intf = calloc(1, sizeof(usb_interface_t));
113 if (!intf) {
114 return ZX_ERR_NO_MEMORY;
115 }
116
117 intf->comp = comp;
118 // Interfaces in an IAD interface collection must be contiguous.
119 intf->last_interface_id = assoc_desc->bFirstInterface + assoc_desc->bInterfaceCount - 1;
120 intf->descriptor = (usb_descriptor_header_t *)assoc_desc;
121 intf->descriptor_length = assoc_desc_length;
122
123 uint8_t usb_class, usb_subclass, usb_protocol;
124 if (assoc_desc->bFunctionClass == 0) {
125 usb_class = device_desc->bDeviceClass;
126 usb_subclass = device_desc->bDeviceSubClass;
127 usb_protocol = device_desc->bDeviceProtocol;
128 } else {
129 // class/subclass/protocol defined per-interface
130 usb_class = assoc_desc->bFunctionClass;
131 usb_subclass = assoc_desc->bFunctionSubClass;
132 usb_protocol = assoc_desc->bFunctionProtocol;
133 }
134
135 usb_descriptor_header_t* header = intf->descriptor;
136 usb_descriptor_header_t* end = (usb_descriptor_header_t*)((void*)header +
137 intf->descriptor_length);
138 while (header < end) {
139 if (header->bDescriptorType == USB_DT_INTERFACE) {
140 usb_interface_descriptor_t* intf_desc = (usb_interface_descriptor_t*)header;
141 if (intf_desc->bAlternateSetting == 0) {
142 zx_status_t status = usb_interface_configure_endpoints(intf,
143 intf_desc->bInterfaceNumber,
144 0);
145 if (status != ZX_OK) {
146 return status;
147 }
148 }
149 }
150 header = NEXT_DESCRIPTOR(header);
151 }
152
153 mtx_lock(&comp->interface_mutex);
154 // need to do this first so usb_composite_set_interface() can be called from driver bind
155 list_add_head(&comp->children, &intf->node);
156 mtx_unlock(&comp->interface_mutex);
157
158 char name[20];
159 snprintf(name, sizeof(name), "asc-%03d", assoc_desc->iFunction);
160
161 zx_device_prop_t props[] = {
162 { BIND_PROTOCOL, 0, ZX_PROTOCOL_USB },
163 { BIND_USB_VID, 0, device_desc->idVendor },
164 { BIND_USB_PID, 0, device_desc->idProduct },
165 { BIND_USB_CLASS, 0, usb_class },
166 { BIND_USB_SUBCLASS, 0, usb_subclass },
167 { BIND_USB_PROTOCOL, 0, usb_protocol },
168 };
169
170 device_add_args_t args = {
171 .version = DEVICE_ADD_ARGS_VERSION,
172 .name = name,
173 .ctx = intf,
174 .ops = &usb_interface_proto,
175 .proto_id = ZX_PROTOCOL_USB,
176 .proto_ops = &usb_device_protocol,
177 .props = props,
178 .prop_count = countof(props),
179 };
180
181 zx_status_t status = device_add(comp->zxdev, &args, &intf->zxdev);
182 if (status != ZX_OK) {
183 list_delete(&intf->node);
184 free(assoc_desc);
185 free(intf);
186 }
187 return status;
188 }
189
usb_composite_add_interfaces(usb_composite_t * comp)190 static zx_status_t usb_composite_add_interfaces(usb_composite_t* comp) {
191 usb_configuration_descriptor_t* config = comp->config_desc;
192
193 comp->interface_statuses = calloc(config->bNumInterfaces, sizeof(interface_status_t));
194 if (!comp->interface_statuses) {
195 return ZX_ERR_NO_MEMORY;
196 }
197
198 // Iterate through interfaces in first configuration and create devices for them
199 usb_descriptor_header_t* header = NEXT_DESCRIPTOR(config);
200 usb_descriptor_header_t* end = (usb_descriptor_header_t*)((void*)config +
201 le16toh(config->wTotalLength));
202
203 zx_status_t result = ZX_OK;
204
205 while (header < end) {
206 if (header->bDescriptorType == USB_DT_INTERFACE_ASSOCIATION) {
207 usb_interface_assoc_descriptor_t* assoc_desc =
208 (usb_interface_assoc_descriptor_t*)header;
209 int interface_count = assoc_desc->bInterfaceCount;
210
211 // find end of this interface association
212 usb_descriptor_header_t* next = NEXT_DESCRIPTOR(assoc_desc);
213 while (next < end) {
214 if (next->bDescriptorType == USB_DT_INTERFACE_ASSOCIATION) {
215 break;
216 } else if (next->bDescriptorType == USB_DT_INTERFACE) {
217 usb_interface_descriptor_t* test_intf = (usb_interface_descriptor_t*)next;
218
219 if (test_intf->bAlternateSetting == 0) {
220 if (interface_count == 0) {
221 break;
222 }
223 interface_count--;
224 }
225 }
226 next = NEXT_DESCRIPTOR(next);
227 }
228
229 size_t length = (void *)next - (void *)assoc_desc;
230 usb_interface_assoc_descriptor_t* assoc_copy = malloc(length);
231 if (!assoc_copy) return ZX_ERR_NO_MEMORY;
232 memcpy(assoc_copy, assoc_desc, length);
233
234 zx_status_t status = usb_composite_add_interface_assoc(comp, assoc_copy, length);
235 if (status != ZX_OK) {
236 result = status;
237 }
238
239 header = next;
240 } else if (header->bDescriptorType == USB_DT_INTERFACE) {
241 usb_interface_descriptor_t* intf_desc = (usb_interface_descriptor_t*)header;
242 // find end of current interface descriptor
243 usb_descriptor_header_t* next = NEXT_DESCRIPTOR(intf_desc);
244 while (next < end) {
245 if (next->bDescriptorType == USB_DT_INTERFACE) {
246 usb_interface_descriptor_t* test_intf = (usb_interface_descriptor_t*)next;
247 // Iterate until we find the next top-level interface
248 // Include alternate interfaces in the current interface
249 if (test_intf->bAlternateSetting == 0) {
250 break;
251 }
252 }
253 next = NEXT_DESCRIPTOR(next);
254 }
255
256 // Only create a child device if no child interface has claimed this interface.
257 mtx_lock(&comp->interface_mutex);
258 interface_status_t intf_status = comp->interface_statuses[intf_desc->bInterfaceNumber];
259 mtx_unlock(&comp->interface_mutex);
260
261 size_t length = (void *)next - (void *)intf_desc;
262 if (intf_status == AVAILABLE) {
263 usb_interface_descriptor_t* intf_copy = malloc(length);
264 if (!intf_copy) return ZX_ERR_NO_MEMORY;
265 memcpy(intf_copy, intf_desc, length);
266 zx_status_t status = usb_composite_add_interface(comp, intf_copy, length);
267 if (status != ZX_OK) {
268 result = status;
269 }
270 // The interface may have been claimed in the meanwhile, so we need to
271 // check the interface status again.
272 mtx_lock(&comp->interface_mutex);
273 if (comp->interface_statuses[intf_desc->bInterfaceNumber] == CLAIMED) {
274 bool removed = usb_composite_remove_interface_by_id_locked(comp,
275 intf_desc->bInterfaceNumber);
276 if (!removed) {
277 mtx_unlock(&comp->interface_mutex);
278 return ZX_ERR_BAD_STATE;
279 }
280 } else {
281 comp->interface_statuses[intf_desc->bInterfaceNumber] = CHILD_DEVICE;
282 }
283 mtx_unlock(&comp->interface_mutex);
284 }
285 header = next;
286 } else {
287 header = NEXT_DESCRIPTOR(header);
288 }
289 }
290
291 return result;
292 }
293
usb_composite_remove_interfaces(usb_composite_t * comp)294 static void usb_composite_remove_interfaces(usb_composite_t* comp) {
295 mtx_lock(&comp->interface_mutex);
296
297 usb_interface_t* intf;
298 while ((intf = list_remove_head_type(&comp->children, usb_interface_t, node)) != NULL) {
299 device_remove(intf->zxdev);
300 }
301 free(comp->interface_statuses);
302 comp->interface_statuses = NULL;
303
304 mtx_unlock(&comp->interface_mutex);
305 }
306
usb_composite_do_claim_interface(usb_composite_t * comp,uint8_t interface_id)307 zx_status_t usb_composite_do_claim_interface(usb_composite_t* comp, uint8_t interface_id) {
308 mtx_lock(&comp->interface_mutex);
309
310 interface_status_t status = comp->interface_statuses[interface_id];
311 if (status == CLAIMED) {
312 // The interface has already been claimed by a different interface.
313 mtx_unlock(&comp->interface_mutex);
314 return ZX_ERR_ALREADY_BOUND;
315 } else if (status == CHILD_DEVICE) {
316 bool removed = usb_composite_remove_interface_by_id_locked(comp, interface_id);
317 if (!removed) {
318 mtx_unlock(&comp->interface_mutex);
319 return ZX_ERR_BAD_STATE;
320 }
321 }
322 comp->interface_statuses[interface_id] = CLAIMED;
323
324 mtx_unlock(&comp->interface_mutex);
325
326 return ZX_OK;
327 }
328
usb_composite_set_interface(usb_composite_t * comp,uint8_t interface_id,uint8_t alt_setting)329 zx_status_t usb_composite_set_interface(usb_composite_t* comp, uint8_t interface_id,
330 uint8_t alt_setting) {
331 mtx_lock(&comp->interface_mutex);
332 usb_interface_t* intf;
333 list_for_every_entry(&comp->children, intf, usb_interface_t, node) {
334 if (usb_interface_contains_interface(intf, interface_id)) {
335 mtx_unlock(&comp->interface_mutex);
336 return usb_interface_set_alt_setting(intf, interface_id, alt_setting);
337 }
338 }
339 mtx_unlock(&comp->interface_mutex);
340 return ZX_ERR_INVALID_ARGS;
341 }
342
usb_composite_unbind(void * ctx)343 static void usb_composite_unbind(void* ctx) {
344 usb_composite_t* comp = ctx;
345 usb_composite_remove_interfaces(comp);
346 device_remove(comp->zxdev);
347 }
348
usb_composite_release(void * ctx)349 static void usb_composite_release(void* ctx) {
350 usb_composite_t* comp = ctx;
351
352 free(comp->config_desc);
353 free(comp->interface_statuses);
354 free(comp);
355 }
356
357 static zx_protocol_device_t usb_composite_device_proto = {
358 .version = DEVICE_OPS_VERSION,
359 .unbind = usb_composite_unbind,
360 .release = usb_composite_release,
361 };
362
usb_composite_bind(void * ctx,zx_device_t * parent)363 static zx_status_t usb_composite_bind(void* ctx, zx_device_t* parent) {
364 usb_protocol_t usb;
365 zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_USB, &usb);
366 if (status != ZX_OK) {
367 return status;
368 }
369
370 usb_composite_t* comp = calloc(1, sizeof(usb_composite_t));
371 if (!comp) {
372 return ZX_ERR_NO_MEMORY;
373 }
374 memcpy(&comp->usb, &usb, sizeof(comp->usb));
375
376 list_initialize(&comp->children);
377
378 mtx_init(&comp->interface_mutex, mtx_plain);
379
380 usb_get_device_descriptor(&usb, &comp->device_desc);
381
382 uint8_t configuration = usb_get_configuration(&comp->usb);
383 size_t config_length;
384 status = usb_get_configuration_descriptor_length(&comp->usb, configuration, &config_length);
385 if (status != ZX_OK) {
386 return status;
387 }
388 comp->config_desc = malloc(config_length);
389 if (!comp->config_desc) {
390 return ZX_ERR_NO_MEMORY;
391 }
392 size_t actual;
393 status = usb_get_configuration_descriptor(&comp->usb, configuration,
394 comp->config_desc, config_length, &actual);
395 if (status != ZX_OK) {
396 goto error_exit;
397 }
398
399 char name[16];
400 snprintf(name, sizeof(name), "%03d", usb_get_device_id(&usb));
401
402 device_add_args_t args = {
403 .version = DEVICE_ADD_ARGS_VERSION,
404 .name = name,
405 .ctx = comp,
406 .ops = &usb_composite_device_proto,
407 .flags = DEVICE_ADD_NON_BINDABLE,
408 };
409
410 status = device_add(parent, &args, &comp->zxdev);
411 if (status == ZX_OK) {
412 return usb_composite_add_interfaces(comp);
413 }
414
415 error_exit:
416 free(comp->config_desc);
417 free(comp);
418 return status;
419 }
420
421 static zx_driver_ops_t usb_composite_driver_ops = {
422 .version = DRIVER_OPS_VERSION,
423 .bind = usb_composite_bind,
424 };
425
426 // The '*' in the version string is important. This marks this driver as a fallback,
427 // to allow other drivers to bind against ZX_PROTOCOL_USB_DEVICE to handle more specific cases.
428 ZIRCON_DRIVER_BEGIN(usb_composite, usb_composite_driver_ops, "zircon", "*0.1", 1)
429 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_USB_DEVICE),
430 ZIRCON_DRIVER_END(usb_composite)
431