• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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