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 maxPacketSize;
197 UInt8 interval;
198 UInt8 number;
199 UInt8 direction;
200
201 kr = (*interface)->GetPipeProperties(interface, endpoint,
202 &direction,
203 &number, &transferType, &maxPacketSize, &interval);
204
205 if (kr == 0) {
206 if (transferType != kUSBBulk) {
207 continue;
208 }
209
210 if (direction == kUSBIn) {
211 handle->info.has_bulk_in = 1;
212 handle->bulkIn = endpoint;
213 } else if (direction == kUSBOut) {
214 handle->info.has_bulk_out = 1;
215 handle->bulkOut = endpoint;
216 }
217
218 if (handle->info.ifc_protocol == 0x01) {
219 handle->zero_mask = maxPacketSize - 1;
220 }
221 } else {
222 ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
223 }
224
225 if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
226 break;
227 }
228 }
229
230 if (handle->callback(&handle->info) == 0) {
231 handle->interface = interface;
232 handle->success = 1;
233
234 /*
235 * Clear both the endpoints, because it has been observed
236 * that the Mac may otherwise (incorrectly) start out with
237 * them in bad state.
238 */
239
240 if (handle->info.has_bulk_in) {
241 kr = (*interface)->ClearPipeStallBothEnds(interface,
242 handle->bulkIn);
243 if (kr != 0) {
244 ERR("could not clear input pipe; result %x, ignoring...\n", kr);
245 }
246 }
247
248 if (handle->info.has_bulk_out) {
249 kr = (*interface)->ClearPipeStallBothEnds(interface,
250 handle->bulkOut);
251 if (kr != 0) {
252 ERR("could not clear output pipe; result %x, ignoring....\n", kr);
253 }
254 }
255
256 return 0;
257 }
258
259 next_interface:
260 (*interface)->USBInterfaceClose(interface);
261 (*interface)->Release(interface);
262 }
263
264 return 0;
265 }
266
267 /** Try out the given device and see if there's a match. Returns 0 on
268 * success, -1 on failure.
269 */
try_device(io_service_t device,usb_handle * handle)270 static int try_device(io_service_t device, usb_handle *handle) {
271 kern_return_t kr;
272 IOCFPlugInInterface **plugin = NULL;
273 IOUSBDeviceInterface500** dev = NULL;
274 SInt32 score;
275 HRESULT result;
276 UInt8 serialIndex;
277 UInt32 locationId;
278
279 // Create an intermediate plugin.
280 kr = IOCreatePlugInInterfaceForService(device,
281 kIOUSBDeviceUserClientTypeID,
282 kIOCFPlugInInterfaceID,
283 &plugin, &score);
284
285 if ((kr != 0) || (plugin == NULL)) {
286 goto error;
287 }
288
289 // Now create the device interface.
290 result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),
291 (LPVOID*)&dev);
292 if ((result != 0) || (dev == NULL)) {
293 ERR("Couldn't create a device interface (%08x)\n", (int) result);
294 goto error;
295 }
296
297 /*
298 * We don't need the intermediate interface after the device interface
299 * is created.
300 */
301 IODestroyPlugInInterface(plugin);
302
303 // So, we have a device, finally. Grab its vitals.
304
305 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
306 if (kr != 0) {
307 ERR("GetDeviceVendor");
308 goto error;
309 }
310
311 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
312 if (kr != 0) {
313 ERR("GetDeviceProduct");
314 goto error;
315 }
316
317 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
318 if (kr != 0) {
319 ERR("GetDeviceClass");
320 goto error;
321 }
322
323 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
324 if (kr != 0) {
325 ERR("GetDeviceSubClass");
326 goto error;
327 }
328
329 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
330 if (kr != 0) {
331 ERR("GetDeviceProtocol");
332 goto error;
333 }
334
335 kr = (*dev)->GetLocationID(dev, &locationId);
336 if (kr != 0) {
337 ERR("GetLocationId");
338 goto error;
339 }
340 snprintf(handle->info.device_path, sizeof(handle->info.device_path),
341 "usb:%" PRIu32 "X", (unsigned int)locationId);
342
343 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
344
345 if (serialIndex > 0) {
346 IOUSBDevRequest req;
347 UInt16 buffer[256];
348
349 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
350 req.bRequest = kUSBRqGetDescriptor;
351 req.wValue = (kUSBStringDesc << 8) | serialIndex;
352 //language ID (en-us) for serial number string
353 req.wIndex = 0x0409;
354 req.pData = buffer;
355 req.wLength = sizeof(buffer);
356 kr = (*dev)->DeviceRequest(dev, &req);
357
358 if (kr == kIOReturnSuccess && req.wLenDone > 0) {
359 int i, count;
360
361 // skip first word, and copy the rest to the serial string, changing shorts to bytes.
362 count = (req.wLenDone - 1) / 2;
363 for (i = 0; i < count; i++)
364 handle->info.serial_number[i] = buffer[i + 1];
365 handle->info.serial_number[i] = 0;
366 }
367 } else {
368 // device has no serial number
369 handle->info.serial_number[0] = 0;
370 }
371 handle->info.writable = 1;
372
373 if (try_interfaces(dev, handle)) {
374 goto error;
375 }
376
377 (*dev)->Release(dev);
378 return 0;
379
380 error:
381
382 if (dev != NULL) {
383 (*dev)->Release(dev);
384 }
385
386 return -1;
387 }
388
389
390 /** Initializes the USB system. Returns 0 on success, -1 on error. */
init_usb(ifc_match_func callback,std::unique_ptr<usb_handle> * handle)391 static int init_usb(ifc_match_func callback, std::unique_ptr<usb_handle>* handle) {
392 int ret = -1;
393 CFMutableDictionaryRef matchingDict;
394 kern_return_t result;
395 io_iterator_t iterator;
396 usb_handle h;
397
398 h.success = 0;
399 h.callback = callback;
400
401 /*
402 * Create our matching dictionary to find appropriate devices.
403 * IOServiceAddMatchingNotification consumes the reference, so we
404 * do not need to release it.
405 */
406 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
407
408 if (matchingDict == NULL) {
409 ERR("Couldn't create USB matching dictionary.\n");
410 return -1;
411 }
412
413 result = IOServiceGetMatchingServices(
414 kIOMasterPortDefault, matchingDict, &iterator);
415
416 if (result != 0) {
417 ERR("Could not create iterator.");
418 return -1;
419 }
420
421 for (;;) {
422 if (! IOIteratorIsValid(iterator)) {
423 /*
424 * Apple documentation advises resetting the iterator if
425 * it should become invalid during iteration.
426 */
427 IOIteratorReset(iterator);
428 continue;
429 }
430
431 io_service_t device = IOIteratorNext(iterator);
432
433 if (device == 0) {
434 break;
435 }
436
437 if (try_device(device, &h) != 0) {
438 IOObjectRelease(device);
439 continue;
440 }
441
442 if (h.success) {
443 handle->reset(new usb_handle);
444 memcpy(handle->get(), &h, sizeof(usb_handle));
445 ret = 0;
446 break;
447 }
448
449 IOObjectRelease(device);
450 }
451
452 IOObjectRelease(iterator);
453
454 return ret;
455 }
456
457
458
459 /*
460 * Definitions of this file's public functions.
461 */
462
usb_open(ifc_match_func callback,uint32_t timeout_ms)463 UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
464 std::unique_ptr<usb_handle> handle;
465
466 if (init_usb(callback, &handle) < 0) {
467 /* Something went wrong initializing USB. */
468 return nullptr;
469 }
470
471 return new OsxUsbTransport(std::move(handle), timeout_ms);
472 }
473
~OsxUsbTransport()474 OsxUsbTransport::~OsxUsbTransport() {
475 Close();
476 }
477
Close()478 int OsxUsbTransport::Close() {
479 /* TODO: Something better here? */
480 return 0;
481 }
482
483 /*
484 TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface.
485 However to perform operations that manipulate the state of the device, you must
486 claim ownership of the device with USBDeviceOpenSeize(). However, this operation
487 always fails with kIOReturnExclusiveAccess.
488 It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice
489 always loads and claims ownership of the device and refuses to give it up.
490 */
Reset()491 int OsxUsbTransport::Reset() {
492 ERR("USB reset is currently unsupported on osx\n");
493 return -1;
494 }
495
Read(void * data,size_t len)496 ssize_t OsxUsbTransport::Read(void* data, size_t len) {
497 IOReturn result;
498 UInt32 numBytes = len;
499
500 if (len == 0) {
501 return 0;
502 }
503
504 if (handle_ == nullptr) {
505 return -1;
506 }
507
508 if (handle_->interface == nullptr) {
509 ERR("usb_read interface was null\n");
510 return -1;
511 }
512
513 if (handle_->bulkIn == 0) {
514 ERR("bulkIn endpoint not assigned\n");
515 return -1;
516 }
517
518 if (!ms_timeout_) {
519 result = (*handle_->interface)
520 ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
521 } else {
522 result = (*handle_->interface)
523 ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,
524 ms_timeout_, ms_timeout_);
525 }
526
527 if (result == 0) {
528 return (int) numBytes;
529 } else {
530 ERR("usb_read failed with status %x\n", result);
531 }
532
533 return -1;
534 }
535
Write(const void * data,size_t len)536 ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
537 IOReturn result;
538
539 if (len == 0) {
540 return 0;
541 }
542
543 if (handle_ == NULL) {
544 return -1;
545 }
546
547 if (handle_->interface == NULL) {
548 ERR("usb_write interface was null\n");
549 return -1;
550 }
551
552 if (handle_->bulkOut == 0) {
553 ERR("bulkOut endpoint not assigned\n");
554 return -1;
555 }
556
557 #if 0
558 result = (*handle_->interface)->WritePipe(
559 handle_->interface, handle_->bulkOut, (void *)data, len);
560 #else
561 /* Attempt to work around crashes in the USB driver that may be caused
562 * by trying to write too much data at once. The kernel IOCopyMapper
563 * panics if a single iovmAlloc needs more than half of its mapper pages.
564 */
565 const int maxLenToSend = 1048576; // 1 MiB
566 int lenRemaining = len;
567 result = 0;
568 while (lenRemaining > 0) {
569 int lenToSend = lenRemaining > maxLenToSend
570 ? maxLenToSend : lenRemaining;
571
572 if (!ms_timeout_) { // blocking
573 result = (*handle_->interface)
574 ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data,
575 lenToSend);
576 } else {
577 result = (*handle_->interface)
578 ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data,
579 lenToSend, ms_timeout_, ms_timeout_);
580 }
581
582 if (result != 0) break;
583
584 lenRemaining -= lenToSend;
585 data = (const char*)data + lenToSend;
586 }
587 #endif
588
589 #if 0
590 if ((result == 0) && (handle_->zero_mask)) {
591 /* we need 0-markers and our transfer */
592 if(!(len & handle_->zero_mask)) {
593 result = (*handle_->interface)->WritePipe(
594 handle_->interface, handle_->bulkOut, (void *)data, 0);
595 }
596 }
597 #endif
598
599 if (result != 0) {
600 ERR("usb_write failed with status %x\n", result);
601 return -1;
602 }
603
604 return len;
605 }
606