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