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