1 /*
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
12 * the documentation and/or other materials provided with the
13 * 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
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * 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
29 #include <inttypes.h>
30 #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 <memory>
39
40 #include "usb.h"
41
42
43 /*
44 * Internal helper functions and associated definitions.
45 */
46
47 #if TRACE_USB
48 #define WARN(x...) fprintf(stderr, x)
49 #else
50 #define WARN(x...)
51 #endif
52
53 #define ERR(x...) fprintf(stderr, "ERROR: " x)
54
55 /** An open usb device */
56 struct usb_handle
57 {
58 int success;
59 ifc_match_func callback;
60 usb_ifc_info info;
61
62 UInt8 bulkIn;
63 UInt8 bulkOut;
64 IOUSBInterfaceInterface500** interface;
65 unsigned int zero_mask;
66 };
67
68 class OsxUsbTransport : public UsbTransport {
69 public:
70 // A timeout of 0 is blocking
OsxUsbTransport(std::unique_ptr<usb_handle> handle,uint32_t ms_timeout=0)71 OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
72 : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
73 ~OsxUsbTransport() override;
74
75 ssize_t Read(void* data, size_t len) override;
76 ssize_t Write(const void* data, size_t len) override;
77 int Close() override;
78 int Reset() override;
79
80 private:
81 std::unique_ptr<usb_handle> handle_;
82 const uint32_t ms_timeout_;
83
84 DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
85 };
86
87 /** Try out all the interfaces and see if there's a match. Returns 0 on
88 * success, -1 on failure. */
try_interfaces(IOUSBDeviceInterface500 ** dev,usb_handle * handle)89 static int try_interfaces(IOUSBDeviceInterface500** dev, usb_handle* handle) {
90 IOReturn kr;
91 IOUSBFindInterfaceRequest request;
92 io_iterator_t iterator;
93 io_service_t usbInterface;
94 IOCFPlugInInterface **plugInInterface;
95 IOUSBInterfaceInterface500** interface = NULL;
96 HRESULT result;
97 SInt32 score;
98 UInt8 interfaceNumEndpoints;
99
100 request.bInterfaceClass = 0xff;
101 request.bInterfaceSubClass = 0x42;
102 request.bInterfaceProtocol = 0x03;
103 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
104
105 // Get an iterator for the interfaces on the device
106 kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
107
108 if (kr != 0) {
109 WARN("Couldn't create a device interface iterator: (%08x)\n", kr);
110 return -1;
111 }
112
113 while ((usbInterface = IOIteratorNext(iterator))) {
114 // Create an intermediate plugin
115 kr = IOCreatePlugInInterfaceForService(
116 usbInterface,
117 kIOUSBInterfaceUserClientTypeID,
118 kIOCFPlugInInterfaceID,
119 &plugInInterface,
120 &score);
121
122 // No longer need the usbInterface object now that we have the plugin
123 (void) IOObjectRelease(usbInterface);
124
125 if ((kr != 0) || (!plugInInterface)) {
126 WARN("Unable to create plugin (%08x)\n", kr);
127 continue;
128 }
129
130 // Now create the interface interface for the interface
131 result = (*plugInInterface)
132 ->QueryInterface(plugInInterface,
133 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500),
134 (LPVOID*)&interface);
135
136 // No longer need the intermediate plugin
137 (*plugInInterface)->Release(plugInInterface);
138
139 if (result || !interface) {
140 ERR("Couldn't create interface interface: (%08x)\n",
141 (unsigned int) result);
142 // continue so we can try the next interface
143 continue;
144 }
145
146 /*
147 * Now open the interface. This will cause the pipes
148 * associated with the endpoints in the interface descriptor
149 * to be instantiated.
150 */
151
152 /*
153 * TODO: Earlier comments here indicated that it was a bad
154 * idea to just open any interface, because opening "mass
155 * storage endpoints" is bad. However, the only way to find
156 * out if an interface does bulk in or out is to open it, and
157 * the framework in this application wants to be told about
158 * bulk in / out before deciding whether it actually wants to
159 * use the interface. Maybe something needs to be done about
160 * this situation.
161 */
162
163 kr = (*interface)->USBInterfaceOpen(interface);
164
165 if (kr != 0) {
166 WARN("Could not open interface: (%08x)\n", kr);
167 (void) (*interface)->Release(interface);
168 // continue so we can try the next interface
169 continue;
170 }
171
172 // Get the number of endpoints associated with this interface.
173 kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
174
175 if (kr != 0) {
176 ERR("Unable to get number of endpoints: (%08x)\n", kr);
177 goto next_interface;
178 }
179
180 // Get interface class, subclass and protocol
181 if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
182 (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
183 (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
184 {
185 ERR("Unable to get interface class, subclass and protocol\n");
186 goto next_interface;
187 }
188
189 handle->info.has_bulk_in = 0;
190 handle->info.has_bulk_out = 0;
191
192 // Iterate over the endpoints for this interface and see if there
193 // are any that do bulk in/out.
194 for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; ++endpoint) {
195 UInt8 transferType;
196 UInt16 endPointMaxPacketSize = 0;
197 UInt8 interval;
198
199 // Attempt to retrieve the 'true' packet-size from supported interface.
200 kr = (*interface)
201 ->GetEndpointProperties(interface, 0, endpoint,
202 kUSBOut,
203 &transferType,
204 &endPointMaxPacketSize, &interval);
205 if (kr == kIOReturnSuccess && !endPointMaxPacketSize) {
206 ERR("GetEndpointProperties() returned zero len packet-size");
207 }
208
209 UInt16 pipePropMaxPacketSize;
210 UInt8 number;
211 UInt8 direction;
212
213 // Proceed with extracting the transfer direction, so we can fill in the
214 // appropriate fields (bulkIn or bulkOut).
215 kr = (*interface)->GetPipeProperties(interface, endpoint,
216 &direction,
217 &number, &transferType, &pipePropMaxPacketSize, &interval);
218
219 if (kr == 0) {
220 if (transferType != kUSBBulk) {
221 continue;
222 }
223
224 if (direction == kUSBIn) {
225 handle->info.has_bulk_in = 1;
226 handle->bulkIn = endpoint;
227 } else if (direction == kUSBOut) {
228 handle->info.has_bulk_out = 1;
229 handle->bulkOut = endpoint;
230 }
231
232 if (handle->info.ifc_protocol == 0x01) {
233 handle->zero_mask = (endPointMaxPacketSize == 0) ?
234 pipePropMaxPacketSize - 1 : endPointMaxPacketSize - 1;
235 }
236 } else {
237 ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
238 }
239
240 if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
241 break;
242 }
243 }
244
245 if (handle->callback(&handle->info) == 0) {
246 handle->interface = interface;
247 handle->success = 1;
248
249 /*
250 * Clear both the endpoints, because it has been observed
251 * that the Mac may otherwise (incorrectly) start out with
252 * them in bad state.
253 */
254
255 if (handle->info.has_bulk_in) {
256 kr = (*interface)->ClearPipeStallBothEnds(interface,
257 handle->bulkIn);
258 if (kr != 0) {
259 ERR("could not clear input pipe; result %x, ignoring...\n", kr);
260 }
261 }
262
263 if (handle->info.has_bulk_out) {
264 kr = (*interface)->ClearPipeStallBothEnds(interface,
265 handle->bulkOut);
266 if (kr != 0) {
267 ERR("could not clear output pipe; result %x, ignoring....\n", kr);
268 }
269 }
270
271 return 0;
272 }
273
274 next_interface:
275 (*interface)->USBInterfaceClose(interface);
276 (*interface)->Release(interface);
277 }
278
279 return 0;
280 }
281
282 /** Try out the given device and see if there's a match. Returns 0 on
283 * success, -1 on failure.
284 */
try_device(io_service_t device,usb_handle * handle)285 static int try_device(io_service_t device, usb_handle *handle) {
286 kern_return_t kr;
287 IOCFPlugInInterface **plugin = NULL;
288 IOUSBDeviceInterface500** dev = NULL;
289 SInt32 score;
290 HRESULT result;
291 UInt8 serialIndex;
292 UInt32 locationId;
293
294 // Create an intermediate plugin.
295 kr = IOCreatePlugInInterfaceForService(device,
296 kIOUSBDeviceUserClientTypeID,
297 kIOCFPlugInInterfaceID,
298 &plugin, &score);
299
300 if ((kr != 0) || (plugin == NULL)) {
301 goto error;
302 }
303
304 // Now create the device interface.
305 result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),
306 (LPVOID*)&dev);
307 if ((result != 0) || (dev == NULL)) {
308 ERR("Couldn't create a device interface (%08x)\n", (int) result);
309 goto error;
310 }
311
312 /*
313 * We don't need the intermediate interface after the device interface
314 * is created.
315 */
316 IODestroyPlugInInterface(plugin);
317
318 // So, we have a device, finally. Grab its vitals.
319
320 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
321 if (kr != 0) {
322 ERR("GetDeviceVendor");
323 goto error;
324 }
325
326 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
327 if (kr != 0) {
328 ERR("GetDeviceProduct");
329 goto error;
330 }
331
332 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
333 if (kr != 0) {
334 ERR("GetDeviceClass");
335 goto error;
336 }
337
338 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
339 if (kr != 0) {
340 ERR("GetDeviceSubClass");
341 goto error;
342 }
343
344 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
345 if (kr != 0) {
346 ERR("GetDeviceProtocol");
347 goto error;
348 }
349
350 kr = (*dev)->GetLocationID(dev, &locationId);
351 if (kr != 0) {
352 ERR("GetLocationId");
353 goto error;
354 }
355 snprintf(handle->info.device_path, sizeof(handle->info.device_path),
356 "usb:%" PRIu32 "X", (unsigned int)locationId);
357
358 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
359
360 if (serialIndex > 0) {
361 IOUSBDevRequest req;
362 UInt16 buffer[256];
363
364 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
365 req.bRequest = kUSBRqGetDescriptor;
366 req.wValue = (kUSBStringDesc << 8) | serialIndex;
367 //language ID (en-us) for serial number string
368 req.wIndex = 0x0409;
369 req.pData = buffer;
370 req.wLength = sizeof(buffer);
371 kr = (*dev)->DeviceRequest(dev, &req);
372
373 if (kr == kIOReturnSuccess && req.wLenDone > 0) {
374 int i, count;
375
376 // skip first word, and copy the rest to the serial string, changing shorts to bytes.
377 count = (req.wLenDone - 1) / 2;
378 for (i = 0; i < count; i++)
379 handle->info.serial_number[i] = buffer[i + 1];
380 handle->info.serial_number[i] = 0;
381 }
382 } else {
383 // device has no serial number
384 handle->info.serial_number[0] = 0;
385 }
386 handle->info.interface[0] = 0;
387 handle->info.writable = 1;
388
389 if (try_interfaces(dev, handle)) {
390 goto error;
391 }
392
393 (*dev)->Release(dev);
394 return 0;
395
396 error:
397
398 if (dev != NULL) {
399 (*dev)->Release(dev);
400 }
401
402 return -1;
403 }
404
405
406 /** Initializes the USB system. Returns 0 on success, -1 on error. */
init_usb(ifc_match_func callback,std::unique_ptr<usb_handle> * handle)407 static int init_usb(ifc_match_func callback, std::unique_ptr<usb_handle>* handle) {
408 int ret = -1;
409 CFMutableDictionaryRef matchingDict;
410 kern_return_t result;
411 io_iterator_t iterator;
412 usb_handle h;
413
414 h.success = 0;
415 h.callback = callback;
416
417 /*
418 * Create our matching dictionary to find appropriate devices.
419 * IOServiceAddMatchingNotification consumes the reference, so we
420 * do not need to release it.
421 */
422 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
423
424 if (matchingDict == NULL) {
425 ERR("Couldn't create USB matching dictionary.\n");
426 return -1;
427 }
428
429 result = IOServiceGetMatchingServices(
430 kIOMasterPortDefault, matchingDict, &iterator);
431
432 if (result != 0) {
433 ERR("Could not create iterator.");
434 return -1;
435 }
436
437 for (;;) {
438 if (! IOIteratorIsValid(iterator)) {
439 /*
440 * Apple documentation advises resetting the iterator if
441 * it should become invalid during iteration.
442 */
443 IOIteratorReset(iterator);
444 continue;
445 }
446
447 io_service_t device = IOIteratorNext(iterator);
448
449 if (device == 0) {
450 break;
451 }
452
453 if (try_device(device, &h) != 0) {
454 IOObjectRelease(device);
455 continue;
456 }
457
458 if (h.success) {
459 handle->reset(new usb_handle(h));
460 ret = 0;
461 break;
462 }
463
464 IOObjectRelease(device);
465 }
466
467 IOObjectRelease(iterator);
468
469 return ret;
470 }
471
472
473
474 /*
475 * Definitions of this file's public functions.
476 */
477
usb_open(ifc_match_func callback,uint32_t timeout_ms)478 UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
479 std::unique_ptr<usb_handle> handle;
480
481 if (init_usb(callback, &handle) < 0) {
482 /* Something went wrong initializing USB. */
483 return nullptr;
484 }
485
486 return new OsxUsbTransport(std::move(handle), timeout_ms);
487 }
488
~OsxUsbTransport()489 OsxUsbTransport::~OsxUsbTransport() {
490 Close();
491 }
492
Close()493 int OsxUsbTransport::Close() {
494 /* TODO: Something better here? */
495 return 0;
496 }
497
498 /*
499 TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface.
500 However to perform operations that manipulate the state of the device, you must
501 claim ownership of the device with USBDeviceOpenSeize(). However, this operation
502 always fails with kIOReturnExclusiveAccess.
503 It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice
504 always loads and claims ownership of the device and refuses to give it up.
505 */
Reset()506 int OsxUsbTransport::Reset() {
507 ERR("USB reset is currently unsupported on osx\n");
508 return -1;
509 }
510
Read(void * data,size_t len)511 ssize_t OsxUsbTransport::Read(void* data, size_t len) {
512 IOReturn result;
513 UInt32 numBytes = len;
514
515 if (len == 0) {
516 return 0;
517 }
518
519 if (handle_ == nullptr) {
520 return -1;
521 }
522
523 if (handle_->interface == nullptr) {
524 ERR("usb_read interface was null\n");
525 return -1;
526 }
527
528 if (handle_->bulkIn == 0) {
529 ERR("bulkIn endpoint not assigned\n");
530 return -1;
531 }
532
533 if (!ms_timeout_) {
534 result = (*handle_->interface)
535 ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
536 } else {
537 result = (*handle_->interface)
538 ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,
539 ms_timeout_, ms_timeout_);
540 }
541
542 if (result == 0) {
543 return (int) numBytes;
544 } else {
545 ERR("usb_read failed with status %x\n", result);
546 }
547
548 return -1;
549 }
550
Write(const void * data,size_t len)551 ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
552 IOReturn result;
553
554 if (len == 0) {
555 return 0;
556 }
557
558 if (handle_ == NULL) {
559 return -1;
560 }
561
562 if (handle_->interface == NULL) {
563 ERR("usb_write interface was null\n");
564 return -1;
565 }
566
567 if (handle_->bulkOut == 0) {
568 ERR("bulkOut endpoint not assigned\n");
569 return -1;
570 }
571
572 #if 0
573 result = (*handle_->interface)->WritePipe(
574 handle_->interface, handle_->bulkOut, (void *)data, len);
575 #else
576 /* Attempt to work around crashes in the USB driver that may be caused
577 * by trying to write too much data at once. The kernel IOCopyMapper
578 * panics if a single iovmAlloc needs more than half of its mapper pages.
579 */
580 const int maxLenToSend = 1048576; // 1 MiB
581 int lenRemaining = len;
582 result = 0;
583 while (lenRemaining > 0) {
584 int lenToSend = lenRemaining > maxLenToSend
585 ? maxLenToSend : lenRemaining;
586
587 if (!ms_timeout_) { // blocking
588 result = (*handle_->interface)
589 ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data,
590 lenToSend);
591 } else {
592 result = (*handle_->interface)
593 ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data,
594 lenToSend, ms_timeout_, ms_timeout_);
595 }
596
597 if (result != 0) break;
598
599 lenRemaining -= lenToSend;
600 data = (const char*)data + lenToSend;
601 }
602 #endif
603
604 #if 0
605 if ((result == 0) && (handle_->zero_mask)) {
606 /* we need 0-markers and our transfer */
607 if(!(len & handle_->zero_mask)) {
608 result = (*handle_->interface)->WritePipe(
609 handle_->interface, handle_->bulkOut, (void *)data, 0);
610 }
611 }
612 #endif
613
614 if (result != 0) {
615 ERR("usb_write failed with status %x\n", result);
616 return -1;
617 }
618
619 return len;
620 }
621