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     UInt32 locationId;
    268 
    269     // Create an intermediate plugin.
    270     kr = IOCreatePlugInInterfaceForService(device,
    271             kIOUSBDeviceUserClientTypeID,
    272             kIOCFPlugInInterfaceID,
    273             &plugin, &score);
    274 
    275     if ((kr != 0) || (plugin == NULL)) {
    276         ERR("Unable to create a plug-in (%08x)\n", kr);
    277         goto error;
    278     }
    279 
    280     // Now create the device interface.
    281     result = (*plugin)->QueryInterface(plugin,
    282             CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
    283     if ((result != 0) || (dev == NULL)) {
    284         ERR("Couldn't create a device interface (%08x)\n", (int) result);
    285         goto error;
    286     }
    287 
    288     /*
    289      * We don't need the intermediate interface after the device interface
    290      * is created.
    291      */
    292     IODestroyPlugInInterface(plugin);
    293 
    294     // So, we have a device, finally. Grab its vitals.
    295 
    296     kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
    297     if (kr != 0) {
    298         ERR("GetDeviceVendor");
    299         goto error;
    300     }
    301 
    302     kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
    303     if (kr != 0) {
    304         ERR("GetDeviceProduct");
    305         goto error;
    306     }
    307 
    308     kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
    309     if (kr != 0) {
    310         ERR("GetDeviceClass");
    311         goto error;
    312     }
    313 
    314     kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
    315     if (kr != 0) {
    316         ERR("GetDeviceSubClass");
    317         goto error;
    318     }
    319 
    320     kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
    321     if (kr != 0) {
    322         ERR("GetDeviceProtocol");
    323         goto error;
    324     }
    325 
    326     kr = (*dev)->GetLocationID(dev, &locationId);
    327     if (kr != 0) {
    328         ERR("GetLocationId");
    329         goto error;
    330     }
    331     snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId);
    332 
    333     kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
    334 
    335     if (serialIndex > 0) {
    336         IOUSBDevRequest req;
    337         UInt16  buffer[256];
    338 
    339         req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
    340         req.bRequest = kUSBRqGetDescriptor;
    341         req.wValue = (kUSBStringDesc << 8) | serialIndex;
    342         //language ID (en-us) for serial number string
    343         req.wIndex = 0x0409;
    344         req.pData = buffer;
    345         req.wLength = sizeof(buffer);
    346         kr = (*dev)->DeviceRequest(dev, &req);
    347 
    348         if (kr == kIOReturnSuccess && req.wLenDone > 0) {
    349             int i, count;
    350 
    351             // skip first word, and copy the rest to the serial string, changing shorts to bytes.
    352             count = (req.wLenDone - 1) / 2;
    353             for (i = 0; i < count; i++)
    354               handle->info.serial_number[i] = buffer[i + 1];
    355             handle->info.serial_number[i] = 0;
    356         }
    357     } else {
    358         // device has no serial number
    359         handle->info.serial_number[0] = 0;
    360     }
    361     handle->info.writable = 1;
    362 
    363     if (try_interfaces(dev, handle)) {
    364         goto error;
    365     }
    366 
    367     (*dev)->Release(dev);
    368     return 0;
    369 
    370     error:
    371 
    372     if (dev != NULL) {
    373         (*dev)->Release(dev);
    374     }
    375 
    376     return -1;
    377 }
    378 
    379 
    380 /** Initializes the USB system. Returns 0 on success, -1 on error. */
    381 static int init_usb(ifc_match_func callback, usb_handle **handle) {
    382     int ret = -1;
    383     CFMutableDictionaryRef matchingDict;
    384     kern_return_t result;
    385     io_iterator_t iterator;
    386     usb_handle h;
    387 
    388     h.success = 0;
    389     h.callback = callback;
    390 
    391     /*
    392      * Create our matching dictionary to find appropriate devices.
    393      * IOServiceAddMatchingNotification consumes the reference, so we
    394      * do not need to release it.
    395      */
    396     matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
    397 
    398     if (matchingDict == NULL) {
    399         ERR("Couldn't create USB matching dictionary.\n");
    400         return -1;
    401     }
    402 
    403     result = IOServiceGetMatchingServices(
    404             kIOMasterPortDefault, matchingDict, &iterator);
    405 
    406     if (result != 0) {
    407         ERR("Could not create iterator.");
    408         return -1;
    409     }
    410 
    411     for (;;) {
    412         if (! IOIteratorIsValid(iterator)) {
    413             /*
    414              * Apple documentation advises resetting the iterator if
    415              * it should become invalid during iteration.
    416              */
    417             IOIteratorReset(iterator);
    418             continue;
    419         }
    420 
    421         io_service_t device = IOIteratorNext(iterator);
    422 
    423         if (device == 0) {
    424             break;
    425         }
    426 
    427         if (try_device(device, &h) != 0) {
    428             IOObjectRelease(device);
    429             ret = -1;
    430             break;
    431         }
    432 
    433         if (h.success) {
    434             *handle = calloc(1, sizeof(usb_handle));
    435             memcpy(*handle, &h, sizeof(usb_handle));
    436             ret = 0;
    437             break;
    438         }
    439 
    440         IOObjectRelease(device);
    441     }
    442 
    443     IOObjectRelease(iterator);
    444 
    445     return ret;
    446 }
    447 
    448 
    449 
    450 /*
    451  * Definitions of this file's public functions.
    452  */
    453 
    454 usb_handle *usb_open(ifc_match_func callback) {
    455     usb_handle *handle = NULL;
    456 
    457     if (init_usb(callback, &handle) < 0) {
    458         /* Something went wrong initializing USB. */
    459         return NULL;
    460     }
    461 
    462     return handle;
    463 }
    464 
    465 int usb_close(usb_handle *h) {
    466     /* TODO: Something better here? */
    467     return 0;
    468 }
    469 
    470 int usb_read(usb_handle *h, void *data, int len) {
    471     IOReturn result;
    472     UInt32 numBytes = len;
    473 
    474     if (len == 0) {
    475         return 0;
    476     }
    477 
    478     if (h == NULL) {
    479         return -1;
    480     }
    481 
    482     if (h->interface == NULL) {
    483         ERR("usb_read interface was null\n");
    484         return -1;
    485     }
    486 
    487     if (h->bulkIn == 0) {
    488         ERR("bulkIn endpoint not assigned\n");
    489         return -1;
    490     }
    491 
    492     result = (*h->interface)->ReadPipe(
    493             h->interface, h->bulkIn, data, &numBytes);
    494 
    495     if (result == 0) {
    496         return (int) numBytes;
    497     } else {
    498         ERR("usb_read failed with status %x\n", result);
    499     }
    500 
    501     return -1;
    502 }
    503 
    504 int usb_write(usb_handle *h, const void *data, int len) {
    505     IOReturn result;
    506 
    507     if (len == 0) {
    508         return 0;
    509     }
    510 
    511     if (h == NULL) {
    512         return -1;
    513     }
    514 
    515     if (h->interface == NULL) {
    516         ERR("usb_write interface was null\n");
    517         return -1;
    518     }
    519 
    520     if (h->bulkOut == 0) {
    521         ERR("bulkOut endpoint not assigned\n");
    522         return -1;
    523     }
    524 
    525 #if 0
    526     result = (*h->interface)->WritePipe(
    527             h->interface, h->bulkOut, (void *)data, len);
    528 #else
    529     /* Attempt to work around crashes in the USB driver that may be caused
    530      * by trying to write too much data at once.  The kernel IOCopyMapper
    531      * panics if a single iovmAlloc needs more than half of its mapper pages.
    532      */
    533     const int maxLenToSend = 1048576; // 1 MiB
    534     int lenRemaining = len;
    535     result = 0;
    536     while (lenRemaining > 0) {
    537         int lenToSend = lenRemaining > maxLenToSend
    538             ? maxLenToSend : lenRemaining;
    539 
    540         result = (*h->interface)->WritePipe(
    541                 h->interface, h->bulkOut, (void *)data, lenToSend);
    542         if (result != 0) break;
    543 
    544         lenRemaining -= lenToSend;
    545         data = (const char*)data + lenToSend;
    546     }
    547 #endif
    548 
    549     #if 0
    550     if ((result == 0) && (h->zero_mask)) {
    551         /* we need 0-markers and our transfer */
    552         if(!(len & h->zero_mask)) {
    553             result = (*h->interface)->WritePipe(
    554                     h->interface, h->bulkOut, (void *)data, 0);
    555         }
    556     }
    557     #endif
    558 
    559     if (result != 0) {
    560         ERR("usb_write failed with status %x\n", result);
    561         return -1;
    562     }
    563 
    564     return len;
    565 }
    566