blob: a959566e0b06f1e43784de2b23e58dfdb6550391 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
Anatol Pomazau5ae3f932012-02-28 07:21:08 -080012 * the documentation and/or other materials provided with the
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080013 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
Anatol Pomazau5ae3f932012-02-28 07:21:08 -080022 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080023 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Elliott Hughesfbcb93a2015-06-24 13:28:24 -070029#include <inttypes.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080030#include <stdio.h>
31#include <CoreFoundation/CoreFoundation.h>
32#include <IOKit/IOKitLib.h>
33#include <IOKit/IOCFPlugIn.h>
34#include <IOKit/usb/IOUSBLib.h>
35#include <IOKit/IOMessage.h>
36#include <mach/mach_port.h>
37
38#include "usb.h"
39
40
41/*
42 * Internal helper functions and associated definitions.
43 */
44
45#if TRACE_USB
46#define WARN(x...) fprintf(stderr, x)
47#else
48#define WARN(x...)
49#endif
50
51#define ERR(x...) fprintf(stderr, "ERROR: " x)
52
53/** An open usb device */
54struct usb_handle
55{
56 int success;
57 ifc_match_func callback;
58 usb_ifc_info info;
Anatol Pomazau5ae3f932012-02-28 07:21:08 -080059
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080060 UInt8 bulkIn;
61 UInt8 bulkOut;
62 IOUSBInterfaceInterface190 **interface;
63 unsigned int zero_mask;
64};
65
66/** Try out all the interfaces and see if there's a match. Returns 0 on
67 * success, -1 on failure. */
Jeff Brownb6406372010-05-21 13:20:47 -070068static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080069 IOReturn kr;
70 IOUSBFindInterfaceRequest request;
71 io_iterator_t iterator;
72 io_service_t usbInterface;
73 IOCFPlugInInterface **plugInInterface;
74 IOUSBInterfaceInterface190 **interface = NULL;
75 HRESULT result;
76 SInt32 score;
77 UInt8 interfaceNumEndpoints;
78 UInt8 endpoint;
79 UInt8 configuration;
80
81 // Placing the constant KIOUSBFindInterfaceDontCare into the following
82 // fields of the IOUSBFindInterfaceRequest structure will allow us to
83 // find all of the interfaces
84 request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
85 request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
86 request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
87 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
88
89 // SetConfiguration will kill an existing UMS connection, so let's
90 // not do this if not necessary.
91 configuration = 0;
92 (*dev)->GetConfiguration(dev, &configuration);
93 if (configuration != 1)
94 (*dev)->SetConfiguration(dev, 1);
95
96 // Get an iterator for the interfaces on the device
97 kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
98
99 if (kr != 0) {
100 ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
101 return -1;
102 }
103
104 while ((usbInterface = IOIteratorNext(iterator))) {
105 // Create an intermediate plugin
106 kr = IOCreatePlugInInterfaceForService(
107 usbInterface,
108 kIOUSBInterfaceUserClientTypeID,
109 kIOCFPlugInInterfaceID,
110 &plugInInterface,
111 &score);
112
113 // No longer need the usbInterface object now that we have the plugin
114 (void) IOObjectRelease(usbInterface);
115
116 if ((kr != 0) || (!plugInInterface)) {
117 WARN("Unable to create plugin (%08x)\n", kr);
118 continue;
119 }
120
121 // Now create the interface interface for the interface
122 result = (*plugInInterface)->QueryInterface(
123 plugInInterface,
124 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
Elliott Hughesfbcb93a2015-06-24 13:28:24 -0700125 (LPVOID*) &interface);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800126
127 // No longer need the intermediate plugin
128 (*plugInInterface)->Release(plugInInterface);
129
130 if (result || !interface) {
131 ERR("Couldn't create interface interface: (%08x)\n",
132 (unsigned int) result);
133 // continue so we can try the next interface
134 continue;
135 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800136
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800137 /*
138 * Now open the interface. This will cause the pipes
139 * associated with the endpoints in the interface descriptor
140 * to be instantiated.
141 */
142
143 /*
144 * TODO: Earlier comments here indicated that it was a bad
145 * idea to just open any interface, because opening "mass
146 * storage endpoints" is bad. However, the only way to find
147 * out if an interface does bulk in or out is to open it, and
148 * the framework in this application wants to be told about
149 * bulk in / out before deciding whether it actually wants to
150 * use the interface. Maybe something needs to be done about
151 * this situation.
152 */
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800153
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800154 kr = (*interface)->USBInterfaceOpen(interface);
155
156 if (kr != 0) {
157 WARN("Could not open interface: (%08x)\n", kr);
158 (void) (*interface)->Release(interface);
159 // continue so we can try the next interface
160 continue;
161 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800162
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800163 // Get the number of endpoints associated with this interface.
164 kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
165
166 if (kr != 0) {
167 ERR("Unable to get number of endpoints: (%08x)\n", kr);
168 goto next_interface;
169 }
170
171 // Get interface class, subclass and protocol
172 if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
173 (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
174 (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
175 {
176 ERR("Unable to get interface class, subclass and protocol\n");
177 goto next_interface;
178 }
179
180 handle->info.has_bulk_in = 0;
181 handle->info.has_bulk_out = 0;
182
183 // Iterate over the endpoints for this interface and see if there
184 // are any that do bulk in/out.
185 for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
186 UInt8 transferType;
187 UInt16 maxPacketSize;
188 UInt8 interval;
189 UInt8 number;
190 UInt8 direction;
191
192 kr = (*interface)->GetPipeProperties(interface, endpoint,
193 &direction,
194 &number, &transferType, &maxPacketSize, &interval);
195
196 if (kr == 0) {
197 if (transferType != kUSBBulk) {
198 continue;
199 }
200
201 if (direction == kUSBIn) {
202 handle->info.has_bulk_in = 1;
203 handle->bulkIn = endpoint;
204 } else if (direction == kUSBOut) {
205 handle->info.has_bulk_out = 1;
206 handle->bulkOut = endpoint;
207 }
208
209 if (handle->info.ifc_protocol == 0x01) {
210 handle->zero_mask = maxPacketSize - 1;
211 }
212 } else {
213 ERR("could not get pipe properties\n");
214 }
215
216 if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
217 break;
218 }
219 }
220
221 if (handle->callback(&handle->info) == 0) {
222 handle->interface = interface;
223 handle->success = 1;
224
225 /*
226 * Clear both the endpoints, because it has been observed
227 * that the Mac may otherwise (incorrectly) start out with
228 * them in bad state.
229 */
230
231 if (handle->info.has_bulk_in) {
232 kr = (*interface)->ClearPipeStallBothEnds(interface,
233 handle->bulkIn);
234 if (kr != 0) {
Nick Pelly286d50f2010-07-22 10:59:55 -0700235 ERR("could not clear input pipe; result %x, ignoring...\n", kr);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800236 }
237 }
238
239 if (handle->info.has_bulk_out) {
240 kr = (*interface)->ClearPipeStallBothEnds(interface,
241 handle->bulkOut);
242 if (kr != 0) {
Nick Pelly286d50f2010-07-22 10:59:55 -0700243 ERR("could not clear output pipe; result %x, ignoring....\n", kr);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800244 }
245 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800246
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800247 return 0;
248 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800249
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800250next_interface:
251 (*interface)->USBInterfaceClose(interface);
252 (*interface)->Release(interface);
253 }
254
255 return 0;
256}
257
258/** Try out the given device and see if there's a match. Returns 0 on
259 * success, -1 on failure.
260 */
261static int try_device(io_service_t device, usb_handle *handle) {
262 kern_return_t kr;
263 IOCFPlugInInterface **plugin = NULL;
264 IOUSBDeviceInterface182 **dev = NULL;
265 SInt32 score;
266 HRESULT result;
267 UInt8 serialIndex;
Scott Anderson13081c62012-04-06 12:39:30 -0700268 UInt32 locationId;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800269
270 // Create an intermediate plugin.
271 kr = IOCreatePlugInInterfaceForService(device,
272 kIOUSBDeviceUserClientTypeID,
273 kIOCFPlugInInterfaceID,
274 &plugin, &score);
275
276 if ((kr != 0) || (plugin == NULL)) {
277 ERR("Unable to create a plug-in (%08x)\n", kr);
278 goto error;
279 }
280
281 // Now create the device interface.
282 result = (*plugin)->QueryInterface(plugin,
Elliott Hughesfbcb93a2015-06-24 13:28:24 -0700283 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800284 if ((result != 0) || (dev == NULL)) {
285 ERR("Couldn't create a device interface (%08x)\n", (int) result);
286 goto error;
287 }
288
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800289 /*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800290 * We don't need the intermediate interface after the device interface
291 * is created.
292 */
293 IODestroyPlugInInterface(plugin);
294
295 // So, we have a device, finally. Grab its vitals.
296
297 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
298 if (kr != 0) {
299 ERR("GetDeviceVendor");
300 goto error;
301 }
302
303 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
304 if (kr != 0) {
305 ERR("GetDeviceProduct");
306 goto error;
307 }
308
309 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
310 if (kr != 0) {
311 ERR("GetDeviceClass");
312 goto error;
313 }
314
315 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
316 if (kr != 0) {
317 ERR("GetDeviceSubClass");
318 goto error;
319 }
320
321 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
322 if (kr != 0) {
323 ERR("GetDeviceProtocol");
324 goto error;
325 }
326
Scott Anderson13081c62012-04-06 12:39:30 -0700327 kr = (*dev)->GetLocationID(dev, &locationId);
328 if (kr != 0) {
329 ERR("GetLocationId");
330 goto error;
331 }
Ying Wang42a809b2014-08-14 15:50:13 -0700332 snprintf(handle->info.device_path, sizeof(handle->info.device_path),
333 "usb:%" PRIu32 "X", (unsigned int)locationId);
Scott Anderson13081c62012-04-06 12:39:30 -0700334
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800335 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
336
337 if (serialIndex > 0) {
338 IOUSBDevRequest req;
339 UInt16 buffer[256];
340
341 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
342 req.bRequest = kUSBRqGetDescriptor;
343 req.wValue = (kUSBStringDesc << 8) | serialIndex;
mgrossc8406532011-07-07 17:08:10 -0700344 //language ID (en-us) for serial number string
345 req.wIndex = 0x0409;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800346 req.pData = buffer;
347 req.wLength = sizeof(buffer);
348 kr = (*dev)->DeviceRequest(dev, &req);
349
350 if (kr == kIOReturnSuccess && req.wLenDone > 0) {
351 int i, count;
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800352
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800353 // skip first word, and copy the rest to the serial string, changing shorts to bytes.
354 count = (req.wLenDone - 1) / 2;
355 for (i = 0; i < count; i++)
356 handle->info.serial_number[i] = buffer[i + 1];
357 handle->info.serial_number[i] = 0;
358 }
359 } else {
360 // device has no serial number
361 handle->info.serial_number[0] = 0;
362 }
Elliott Hughesb4add9b2009-10-06 18:07:49 -0700363 handle->info.writable = 1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800364
365 if (try_interfaces(dev, handle)) {
366 goto error;
367 }
368
369 (*dev)->Release(dev);
370 return 0;
371
372 error:
373
374 if (dev != NULL) {
375 (*dev)->Release(dev);
376 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800377
378 return -1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800379}
380
381
382/** Initializes the USB system. Returns 0 on success, -1 on error. */
383static int init_usb(ifc_match_func callback, usb_handle **handle) {
384 int ret = -1;
385 CFMutableDictionaryRef matchingDict;
386 kern_return_t result;
387 io_iterator_t iterator;
388 usb_handle h;
389
390 h.success = 0;
391 h.callback = callback;
392
393 /*
394 * Create our matching dictionary to find appropriate devices.
395 * IOServiceAddMatchingNotification consumes the reference, so we
396 * do not need to release it.
397 */
398 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
399
400 if (matchingDict == NULL) {
401 ERR("Couldn't create USB matching dictionary.\n");
402 return -1;
403 }
404
405 result = IOServiceGetMatchingServices(
406 kIOMasterPortDefault, matchingDict, &iterator);
407
408 if (result != 0) {
409 ERR("Could not create iterator.");
410 return -1;
411 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800412
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800413 for (;;) {
414 if (! IOIteratorIsValid(iterator)) {
415 /*
416 * Apple documentation advises resetting the iterator if
417 * it should become invalid during iteration.
418 */
419 IOIteratorReset(iterator);
420 continue;
421 }
Anatol Pomazau5ae3f932012-02-28 07:21:08 -0800422
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800423 io_service_t device = IOIteratorNext(iterator);
424
425 if (device == 0) {
426 break;
427 }
428
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800429 if (try_device(device, &h) != 0) {
430 IOObjectRelease(device);
431 ret = -1;
432 break;
433 }
434
435 if (h.success) {
Elliott Hughesfbcb93a2015-06-24 13:28:24 -0700436 *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800437 memcpy(*handle, &h, sizeof(usb_handle));
438 ret = 0;
439 break;
440 }
441
442 IOObjectRelease(device);
443 }
444
445 IOObjectRelease(iterator);
446
447 return ret;
448}
449
450
451
452/*
453 * Definitions of this file's public functions.
454 */
455
456usb_handle *usb_open(ifc_match_func callback) {
457 usb_handle *handle = NULL;
458
459 if (init_usb(callback, &handle) < 0) {
460 /* Something went wrong initializing USB. */
461 return NULL;
462 }
463
464 return handle;
465}
466
467int usb_close(usb_handle *h) {
468 /* TODO: Something better here? */
469 return 0;
470}
471
Mark Wachsler157b0012013-10-02 09:35:38 -0400472int usb_wait_for_disconnect(usb_handle *usb) {
473 /* TODO: Punt for now */
474 return 0;
475}
476
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800477int usb_read(usb_handle *h, void *data, int len) {
478 IOReturn result;
479 UInt32 numBytes = len;
480
481 if (len == 0) {
482 return 0;
483 }
484
485 if (h == NULL) {
486 return -1;
487 }
488
489 if (h->interface == NULL) {
490 ERR("usb_read interface was null\n");
491 return -1;
492 }
493
494 if (h->bulkIn == 0) {
495 ERR("bulkIn endpoint not assigned\n");
496 return -1;
497 }
498
499 result = (*h->interface)->ReadPipe(
500 h->interface, h->bulkIn, data, &numBytes);
501
502 if (result == 0) {
503 return (int) numBytes;
504 } else {
505 ERR("usb_read failed with status %x\n", result);
506 }
507
508 return -1;
509}
510
511int usb_write(usb_handle *h, const void *data, int len) {
512 IOReturn result;
513
514 if (len == 0) {
515 return 0;
516 }
517
518 if (h == NULL) {
519 return -1;
520 }
521
522 if (h->interface == NULL) {
523 ERR("usb_write interface was null\n");
524 return -1;
525 }
526
527 if (h->bulkOut == 0) {
528 ERR("bulkOut endpoint not assigned\n");
529 return -1;
530 }
531
Jeff Brownb6406372010-05-21 13:20:47 -0700532#if 0
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800533 result = (*h->interface)->WritePipe(
534 h->interface, h->bulkOut, (void *)data, len);
Jeff Brownb6406372010-05-21 13:20:47 -0700535#else
536 /* Attempt to work around crashes in the USB driver that may be caused
537 * by trying to write too much data at once. The kernel IOCopyMapper
538 * panics if a single iovmAlloc needs more than half of its mapper pages.
539 */
540 const int maxLenToSend = 1048576; // 1 MiB
541 int lenRemaining = len;
542 result = 0;
543 while (lenRemaining > 0) {
544 int lenToSend = lenRemaining > maxLenToSend
545 ? maxLenToSend : lenRemaining;
546
547 result = (*h->interface)->WritePipe(
548 h->interface, h->bulkOut, (void *)data, lenToSend);
549 if (result != 0) break;
550
551 lenRemaining -= lenToSend;
552 data = (const char*)data + lenToSend;
553 }
554#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800555
556 #if 0
557 if ((result == 0) && (h->zero_mask)) {
558 /* we need 0-markers and our transfer */
559 if(!(len & h->zero_mask)) {
560 result = (*h->interface)->WritePipe(
561 h->interface, h->bulkOut, (void *)data, 0);
562 }
563 }
564 #endif
565
566 if (result != 0) {
567 ERR("usb_write failed with status %x\n", result);
568 return -1;
569 }
570
571 return len;
572}