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             break;
440         }
441 
442         io_service_t device = IOIteratorNext(iterator);
443 
444         if (device == 0) {
445             break;
446         }
447 
448         if (try_device(device, &h) != 0) {
449             IOObjectRelease(device);
450             continue;
451         }
452 
453         if (h.success) {
454             handle->reset(new usb_handle(h));
455             ret = 0;
456             break;
457         }
458 
459         IOObjectRelease(device);
460     }
461 
462     IOObjectRelease(iterator);
463 
464     return ret;
465 }
466 
467 
468 
469 /*
470  * Definitions of this file's public functions.
471  */
usb_open(ifc_match_func callback,uint32_t timeout_ms)472 std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
473     std::unique_ptr<UsbTransport> result;
474     std::unique_ptr<usb_handle> handle;
475 
476     if (init_usb(callback, &handle) < 0) {
477         /* Something went wrong initializing USB. */
478         return result;
479     }
480 
481     if (handle) {
482         result = std::make_unique<OsxUsbTransport>(std::move(handle), timeout_ms);
483     }
484 
485     return result;
486 }
487 
~OsxUsbTransport()488 OsxUsbTransport::~OsxUsbTransport() {
489     Close();
490 }
491 
Close()492 int OsxUsbTransport::Close() {
493     /* TODO: Something better here? */
494     return 0;
495 }
496 
497 /*
498   TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface.
499   However to perform operations that manipulate the state of the device, you must
500   claim ownership of the device with USBDeviceOpenSeize(). However, this operation
501   always fails with kIOReturnExclusiveAccess.
502   It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice
503   always loads and claims ownership of the device and refuses to give it up.
504 */
Reset()505 int OsxUsbTransport::Reset() {
506     ERR("USB reset is currently unsupported on osx\n");
507     return -1;
508 }
509 
Read(void * data,size_t len)510 ssize_t OsxUsbTransport::Read(void* data, size_t len) {
511     IOReturn result;
512     UInt32 numBytes = len;
513 
514     if (len == 0) {
515         return 0;
516     }
517 
518     if (handle_ == nullptr) {
519         return -1;
520     }
521 
522     if (handle_->interface == nullptr) {
523         ERR("usb_read interface was null\n");
524         return -1;
525     }
526 
527     if (handle_->bulkIn == 0) {
528         ERR("bulkIn endpoint not assigned\n");
529         return -1;
530     }
531 
532     if (!ms_timeout_) {
533         result = (*handle_->interface)
534                          ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
535     } else {
536         result = (*handle_->interface)
537                          ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,
538                                       ms_timeout_, ms_timeout_);
539     }
540 
541     if (result == 0) {
542         return (int) numBytes;
543     } else {
544         ERR("usb_read failed with status %x\n", result);
545     }
546 
547     return -1;
548 }
549 
Write(const void * data,size_t len)550 ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
551     IOReturn result;
552 
553     if (len == 0) {
554         return 0;
555     }
556 
557     if (handle_ == NULL) {
558         return -1;
559     }
560 
561     if (handle_->interface == NULL) {
562         ERR("usb_write interface was null\n");
563         return -1;
564     }
565 
566     if (handle_->bulkOut == 0) {
567         ERR("bulkOut endpoint not assigned\n");
568         return -1;
569     }
570 
571 #if 0
572     result = (*handle_->interface)->WritePipe(
573             handle_->interface, handle_->bulkOut, (void *)data, len);
574 #else
575     /* Attempt to work around crashes in the USB driver that may be caused
576      * by trying to write too much data at once.  The kernel IOCopyMapper
577      * panics if a single iovmAlloc needs more than half of its mapper pages.
578      */
579     const int maxLenToSend = 1048576; // 1 MiB
580     int lenRemaining = len;
581     result = 0;
582     while (lenRemaining > 0) {
583         int lenToSend = lenRemaining > maxLenToSend
584             ? maxLenToSend : lenRemaining;
585 
586         if (!ms_timeout_) {  // blocking
587             result = (*handle_->interface)
588                              ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data,
589                                          lenToSend);
590         } else {
591             result = (*handle_->interface)
592                              ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data,
593                                            lenToSend, ms_timeout_, ms_timeout_);
594         }
595 
596         if (result != 0) break;
597 
598         lenRemaining -= lenToSend;
599         data = (const char*)data + lenToSend;
600     }
601 #endif
602 
603     #if 0
604     if ((result == 0) && (handle_->zero_mask)) {
605         /* we need 0-markers and our transfer */
606         if(!(len & handle_->zero_mask)) {
607             result = (*handle_->interface)->WritePipe(
608                     handle_->interface, handle_->bulkOut, (void *)data, 0);
609         }
610     }
611     #endif
612 
613     if (result != 0) {
614         ERR("usb_write failed with status %x\n", result);
615         return -1;
616     }
617 
618     return len;
619 }
620