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 */
56struct usb_handle
57{
58    int success;
59    ifc_match_func callback;
60    usb_ifc_info info;
61
62    UInt8 bulkIn;
63    UInt8 bulkOut;
64    IOUSBInterfaceInterface190 **interface;
65    unsigned int zero_mask;
66};
67
68class OsxUsbTransport : public Transport {
69  public:
70    OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
71    ~OsxUsbTransport() override = default;
72
73    ssize_t Read(void* data, size_t len) override;
74    ssize_t Write(const void* data, size_t len) override;
75    int Close() override;
76
77  private:
78    std::unique_ptr<usb_handle> handle_;
79
80    DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
81};
82
83/** Try out all the interfaces and see if there's a match. Returns 0 on
84 * success, -1 on failure. */
85static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
86    IOReturn kr;
87    IOUSBFindInterfaceRequest request;
88    io_iterator_t iterator;
89    io_service_t usbInterface;
90    IOCFPlugInInterface **plugInInterface;
91    IOUSBInterfaceInterface190 **interface = NULL;
92    HRESULT result;
93    SInt32 score;
94    UInt8 interfaceNumEndpoints;
95
96    // Placing the constant KIOUSBFindInterfaceDontCare into the following
97    // fields of the IOUSBFindInterfaceRequest structure will allow us to
98    // find all of the interfaces
99    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
100    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
101    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
102    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
103
104    // Get an iterator for the interfaces on the device
105    kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
106
107    if (kr != 0) {
108        ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
109        return -1;
110    }
111
112    while ((usbInterface = IOIteratorNext(iterator))) {
113        // Create an intermediate plugin
114        kr = IOCreatePlugInInterfaceForService(
115                usbInterface,
116                kIOUSBInterfaceUserClientTypeID,
117                kIOCFPlugInInterfaceID,
118                &plugInInterface,
119                &score);
120
121        // No longer need the usbInterface object now that we have the plugin
122        (void) IOObjectRelease(usbInterface);
123
124        if ((kr != 0) || (!plugInInterface)) {
125            WARN("Unable to create plugin (%08x)\n", kr);
126            continue;
127        }
128
129        // Now create the interface interface for the interface
130        result = (*plugInInterface)->QueryInterface(
131                plugInInterface,
132                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
133                (LPVOID*) &interface);
134
135        // No longer need the intermediate plugin
136        (*plugInInterface)->Release(plugInInterface);
137
138        if (result || !interface) {
139            ERR("Couldn't create interface interface: (%08x)\n",
140               (unsigned int) result);
141            // continue so we can try the next interface
142            continue;
143        }
144
145        /*
146         * Now open the interface. This will cause the pipes
147         * associated with the endpoints in the interface descriptor
148         * to be instantiated.
149         */
150
151        /*
152         * TODO: Earlier comments here indicated that it was a bad
153         * idea to just open any interface, because opening "mass
154         * storage endpoints" is bad. However, the only way to find
155         * out if an interface does bulk in or out is to open it, and
156         * the framework in this application wants to be told about
157         * bulk in / out before deciding whether it actually wants to
158         * use the interface. Maybe something needs to be done about
159         * this situation.
160         */
161
162        kr = (*interface)->USBInterfaceOpen(interface);
163
164        if (kr != 0) {
165            WARN("Could not open interface: (%08x)\n", kr);
166            (void) (*interface)->Release(interface);
167            // continue so we can try the next interface
168            continue;
169        }
170
171        // Get the number of endpoints associated with this interface.
172        kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
173
174        if (kr != 0) {
175            ERR("Unable to get number of endpoints: (%08x)\n", kr);
176            goto next_interface;
177        }
178
179        // Get interface class, subclass and protocol
180        if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
181            (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
182            (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
183        {
184            ERR("Unable to get interface class, subclass and protocol\n");
185            goto next_interface;
186        }
187
188        handle->info.has_bulk_in = 0;
189        handle->info.has_bulk_out = 0;
190
191        // Iterate over the endpoints for this interface and see if there
192        // are any that do bulk in/out.
193        for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
194            UInt8   transferType;
195            UInt16  maxPacketSize;
196            UInt8   interval;
197            UInt8   number;
198            UInt8   direction;
199
200            kr = (*interface)->GetPipeProperties(interface, endpoint,
201                    &direction,
202                    &number, &transferType, &maxPacketSize, &interval);
203
204            if (kr == 0) {
205                if (transferType != kUSBBulk) {
206                    continue;
207                }
208
209                if (direction == kUSBIn) {
210                    handle->info.has_bulk_in = 1;
211                    handle->bulkIn = endpoint;
212                } else if (direction == kUSBOut) {
213                    handle->info.has_bulk_out = 1;
214                    handle->bulkOut = endpoint;
215                }
216
217                if (handle->info.ifc_protocol == 0x01) {
218                    handle->zero_mask = maxPacketSize - 1;
219                }
220            } else {
221                ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
222            }
223
224            if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
225                break;
226            }
227        }
228
229        if (handle->callback(&handle->info) == 0) {
230            handle->interface = interface;
231            handle->success = 1;
232
233            /*
234             * Clear both the endpoints, because it has been observed
235             * that the Mac may otherwise (incorrectly) start out with
236             * them in bad state.
237             */
238
239            if (handle->info.has_bulk_in) {
240                kr = (*interface)->ClearPipeStallBothEnds(interface,
241                        handle->bulkIn);
242                if (kr != 0) {
243                    ERR("could not clear input pipe; result %x, ignoring...\n", kr);
244                }
245            }
246
247            if (handle->info.has_bulk_out) {
248                kr = (*interface)->ClearPipeStallBothEnds(interface,
249                        handle->bulkOut);
250                if (kr != 0) {
251                    ERR("could not clear output pipe; result %x, ignoring....\n", kr);
252                }
253            }
254
255            return 0;
256        }
257
258next_interface:
259        (*interface)->USBInterfaceClose(interface);
260        (*interface)->Release(interface);
261    }
262
263    return 0;
264}
265
266/** Try out the given device and see if there's a match. Returns 0 on
267 * success, -1 on failure.
268 */
269static int try_device(io_service_t device, usb_handle *handle) {
270    kern_return_t kr;
271    IOCFPlugInInterface **plugin = NULL;
272    IOUSBDeviceInterface182 **dev = NULL;
273    SInt32 score;
274    HRESULT result;
275    UInt8 serialIndex;
276    UInt32 locationId;
277
278    // Create an intermediate plugin.
279    kr = IOCreatePlugInInterfaceForService(device,
280            kIOUSBDeviceUserClientTypeID,
281            kIOCFPlugInInterfaceID,
282            &plugin, &score);
283
284    if ((kr != 0) || (plugin == NULL)) {
285        ERR("Unable to create a plug-in (%08x)\n", kr);
286        goto error;
287    }
288
289    // Now create the device interface.
290    result = (*plugin)->QueryInterface(plugin,
291            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (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. */
391static 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            ret = -1;
440            break;
441        }
442
443        if (h.success) {
444            handle->reset(new usb_handle);
445            memcpy(handle->get(), &h, sizeof(usb_handle));
446            ret = 0;
447            break;
448        }
449
450        IOObjectRelease(device);
451    }
452
453    IOObjectRelease(iterator);
454
455    return ret;
456}
457
458
459
460/*
461 * Definitions of this file's public functions.
462 */
463
464Transport* usb_open(ifc_match_func callback) {
465    std::unique_ptr<usb_handle> handle;
466
467    if (init_usb(callback, &handle) < 0) {
468        /* Something went wrong initializing USB. */
469        return nullptr;
470    }
471
472    return new OsxUsbTransport(std::move(handle));
473}
474
475int OsxUsbTransport::Close() {
476    /* TODO: Something better here? */
477    return 0;
478}
479
480ssize_t OsxUsbTransport::Read(void* data, size_t len) {
481    IOReturn result;
482    UInt32 numBytes = len;
483
484    if (len == 0) {
485        return 0;
486    }
487
488    if (handle_ == nullptr) {
489        return -1;
490    }
491
492    if (handle_->interface == nullptr) {
493        ERR("usb_read interface was null\n");
494        return -1;
495    }
496
497    if (handle_->bulkIn == 0) {
498        ERR("bulkIn endpoint not assigned\n");
499        return -1;
500    }
501
502    result = (*handle_->interface)->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
503
504    if (result == 0) {
505        return (int) numBytes;
506    } else {
507        ERR("usb_read failed with status %x\n", result);
508    }
509
510    return -1;
511}
512
513ssize_t OsxUsbTransport::Write(const void* data, size_t len) {
514    IOReturn result;
515
516    if (len == 0) {
517        return 0;
518    }
519
520    if (handle_ == NULL) {
521        return -1;
522    }
523
524    if (handle_->interface == NULL) {
525        ERR("usb_write interface was null\n");
526        return -1;
527    }
528
529    if (handle_->bulkOut == 0) {
530        ERR("bulkOut endpoint not assigned\n");
531        return -1;
532    }
533
534#if 0
535    result = (*handle_->interface)->WritePipe(
536            handle_->interface, handle_->bulkOut, (void *)data, len);
537#else
538    /* Attempt to work around crashes in the USB driver that may be caused
539     * by trying to write too much data at once.  The kernel IOCopyMapper
540     * panics if a single iovmAlloc needs more than half of its mapper pages.
541     */
542    const int maxLenToSend = 1048576; // 1 MiB
543    int lenRemaining = len;
544    result = 0;
545    while (lenRemaining > 0) {
546        int lenToSend = lenRemaining > maxLenToSend
547            ? maxLenToSend : lenRemaining;
548
549        result = (*handle_->interface)->WritePipe(
550                handle_->interface, handle_->bulkOut, (void *)data, lenToSend);
551        if (result != 0) break;
552
553        lenRemaining -= lenToSend;
554        data = (const char*)data + lenToSend;
555    }
556#endif
557
558    #if 0
559    if ((result == 0) && (handle_->zero_mask)) {
560        /* we need 0-markers and our transfer */
561        if(!(len & handle_->zero_mask)) {
562            result = (*handle_->interface)->WritePipe(
563                    handle_->interface, handle_->bulkOut, (void *)data, 0);
564        }
565    }
566    #endif
567
568    if (result != 0) {
569        ERR("usb_write failed with status %x\n", result);
570        return -1;
571    }
572
573    return len;
574}
575