usb_osx.c revision 13081c6915220db03886b177f1a8e0b2c63467c9
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 */ 53struct 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. */ 67static 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 249next_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 */ 260static 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 req.wIndex = 0; 343 req.pData = buffer; 344 req.wLength = sizeof(buffer); 345 kr = (*dev)->DeviceRequest(dev, &req); 346 347 if (kr == kIOReturnSuccess && req.wLenDone > 0) { 348 int i, count; 349 350 // skip first word, and copy the rest to the serial string, changing shorts to bytes. 351 count = (req.wLenDone - 1) / 2; 352 for (i = 0; i < count; i++) 353 handle->info.serial_number[i] = buffer[i + 1]; 354 handle->info.serial_number[i] = 0; 355 } 356 } else { 357 // device has no serial number 358 handle->info.serial_number[0] = 0; 359 } 360 handle->info.writable = 1; 361 362 if (try_interfaces(dev, handle)) { 363 goto error; 364 } 365 366 (*dev)->Release(dev); 367 return 0; 368 369 error: 370 371 if (dev != NULL) { 372 (*dev)->Release(dev); 373 } 374 375 return -1; 376} 377 378 379/** Initializes the USB system. Returns 0 on success, -1 on error. */ 380static int init_usb(ifc_match_func callback, usb_handle **handle) { 381 int ret = -1; 382 CFMutableDictionaryRef matchingDict; 383 kern_return_t result; 384 io_iterator_t iterator; 385 usb_handle h; 386 387 h.success = 0; 388 h.callback = callback; 389 390 /* 391 * Create our matching dictionary to find appropriate devices. 392 * IOServiceAddMatchingNotification consumes the reference, so we 393 * do not need to release it. 394 */ 395 matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 396 397 if (matchingDict == NULL) { 398 ERR("Couldn't create USB matching dictionary.\n"); 399 return -1; 400 } 401 402 result = IOServiceGetMatchingServices( 403 kIOMasterPortDefault, matchingDict, &iterator); 404 405 if (result != 0) { 406 ERR("Could not create iterator."); 407 return -1; 408 } 409 410 for (;;) { 411 if (! IOIteratorIsValid(iterator)) { 412 /* 413 * Apple documentation advises resetting the iterator if 414 * it should become invalid during iteration. 415 */ 416 IOIteratorReset(iterator); 417 continue; 418 } 419 420 io_service_t device = IOIteratorNext(iterator); 421 422 if (device == 0) { 423 break; 424 } 425 426 if (try_device(device, &h) != 0) { 427 IOObjectRelease(device); 428 ret = -1; 429 break; 430 } 431 432 if (h.success) { 433 *handle = calloc(1, sizeof(usb_handle)); 434 memcpy(*handle, &h, sizeof(usb_handle)); 435 ret = 0; 436 break; 437 } 438 439 IOObjectRelease(device); 440 } 441 442 IOObjectRelease(iterator); 443 444 return ret; 445} 446 447 448 449/* 450 * Definitions of this file's public functions. 451 */ 452 453usb_handle *usb_open(ifc_match_func callback) { 454 usb_handle *handle = NULL; 455 456 if (init_usb(callback, &handle) < 0) { 457 /* Something went wrong initializing USB. */ 458 return NULL; 459 } 460 461 return handle; 462} 463 464int usb_close(usb_handle *h) { 465 /* TODO: Something better here? */ 466 return 0; 467} 468 469int usb_read(usb_handle *h, void *data, int len) { 470 IOReturn result; 471 UInt32 numBytes = len; 472 473 if (len == 0) { 474 return 0; 475 } 476 477 if (h == NULL) { 478 return -1; 479 } 480 481 if (h->interface == NULL) { 482 ERR("usb_read interface was null\n"); 483 return -1; 484 } 485 486 if (h->bulkIn == 0) { 487 ERR("bulkIn endpoint not assigned\n"); 488 return -1; 489 } 490 491 result = (*h->interface)->ReadPipe( 492 h->interface, h->bulkIn, data, &numBytes); 493 494 if (result == 0) { 495 return (int) numBytes; 496 } else { 497 ERR("usb_read failed with status %x\n", result); 498 } 499 500 return -1; 501} 502 503int usb_write(usb_handle *h, const void *data, int len) { 504 IOReturn result; 505 506 if (len == 0) { 507 return 0; 508 } 509 510 if (h == NULL) { 511 return -1; 512 } 513 514 if (h->interface == NULL) { 515 ERR("usb_write interface was null\n"); 516 return -1; 517 } 518 519 if (h->bulkOut == 0) { 520 ERR("bulkOut endpoint not assigned\n"); 521 return -1; 522 } 523 524#if 0 525 result = (*h->interface)->WritePipe( 526 h->interface, h->bulkOut, (void *)data, len); 527#else 528 /* Attempt to work around crashes in the USB driver that may be caused 529 * by trying to write too much data at once. The kernel IOCopyMapper 530 * panics if a single iovmAlloc needs more than half of its mapper pages. 531 */ 532 const int maxLenToSend = 1048576; // 1 MiB 533 int lenRemaining = len; 534 result = 0; 535 while (lenRemaining > 0) { 536 int lenToSend = lenRemaining > maxLenToSend 537 ? maxLenToSend : lenRemaining; 538 539 result = (*h->interface)->WritePipe( 540 h->interface, h->bulkOut, (void *)data, lenToSend); 541 if (result != 0) break; 542 543 lenRemaining -= lenToSend; 544 data = (const char*)data + lenToSend; 545 } 546#endif 547 548 #if 0 549 if ((result == 0) && (h->zero_mask)) { 550 /* we need 0-markers and our transfer */ 551 if(!(len & h->zero_mask)) { 552 result = (*h->interface)->WritePipe( 553 h->interface, h->bulkOut, (void *)data, 0); 554 } 555 } 556 #endif 557 558 if (result != 0) { 559 ERR("usb_write failed with status %x\n", result); 560 return -1; 561 } 562 563 return len; 564} 565