usb_osx.cpp revision 81c24f6f869a19466052b29c501fd9cf8372b0b2
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 "usb.h" 39 40 41/* 42 * Internal helper functions and associated definitions. 43 */ 44 45#if TRACE_USB 46#define WARN(x...) fprintf(stderr, x) 47#else 48#define WARN(x...) 49#endif 50 51#define ERR(x...) fprintf(stderr, "ERROR: " x) 52 53/** An open usb device */ 54struct usb_handle 55{ 56 int success; 57 ifc_match_func callback; 58 usb_ifc_info info; 59 60 UInt8 bulkIn; 61 UInt8 bulkOut; 62 IOUSBInterfaceInterface190 **interface; 63 unsigned int zero_mask; 64}; 65 66/** Try out all the interfaces and see if there's a match. Returns 0 on 67 * success, -1 on failure. */ 68static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { 69 IOReturn kr; 70 IOUSBFindInterfaceRequest request; 71 io_iterator_t iterator; 72 io_service_t usbInterface; 73 IOCFPlugInInterface **plugInInterface; 74 IOUSBInterfaceInterface190 **interface = NULL; 75 HRESULT result; 76 SInt32 score; 77 UInt8 interfaceNumEndpoints; 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 (UInt8 endpoint = 1; 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 for endpoint %u (%08x)\n", endpoint, kr); 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 297 kr = (*dev)->USBDeviceOpen(dev); 298 if (kr != 0) { 299 WARN("USBDeviceOpen"); 300 goto out; 301 } 302 303 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor); 304 if (kr != 0) { 305 ERR("GetDeviceVendor"); 306 goto error; 307 } 308 309 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product); 310 if (kr != 0) { 311 ERR("GetDeviceProduct"); 312 goto error; 313 } 314 315 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class); 316 if (kr != 0) { 317 ERR("GetDeviceClass"); 318 goto error; 319 } 320 321 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass); 322 if (kr != 0) { 323 ERR("GetDeviceSubClass"); 324 goto error; 325 } 326 327 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol); 328 if (kr != 0) { 329 ERR("GetDeviceProtocol"); 330 goto error; 331 } 332 333 kr = (*dev)->GetLocationID(dev, &locationId); 334 if (kr != 0) { 335 ERR("GetLocationId"); 336 goto error; 337 } 338 snprintf(handle->info.device_path, sizeof(handle->info.device_path), 339 "usb:%" PRIu32 "X", (unsigned int)locationId); 340 341 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); 342 343 if (serialIndex > 0) { 344 IOUSBDevRequest req; 345 UInt16 buffer[256]; 346 347 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); 348 req.bRequest = kUSBRqGetDescriptor; 349 req.wValue = (kUSBStringDesc << 8) | serialIndex; 350 //language ID (en-us) for serial number string 351 req.wIndex = 0x0409; 352 req.pData = buffer; 353 req.wLength = sizeof(buffer); 354 kr = (*dev)->DeviceRequest(dev, &req); 355 356 if (kr == kIOReturnSuccess && req.wLenDone > 0) { 357 int i, count; 358 359 // skip first word, and copy the rest to the serial string, changing shorts to bytes. 360 count = (req.wLenDone - 1) / 2; 361 for (i = 0; i < count; i++) 362 handle->info.serial_number[i] = buffer[i + 1]; 363 handle->info.serial_number[i] = 0; 364 } 365 } else { 366 // device has no serial number 367 handle->info.serial_number[0] = 0; 368 } 369 handle->info.writable = 1; 370 371 if (try_interfaces(dev, handle)) { 372 goto error; 373 } 374 375 out: 376 377 (*dev)->USBDeviceClose(dev); 378 (*dev)->Release(dev); 379 return 0; 380 381 error: 382 383 if (dev != NULL) { 384 (*dev)->USBDeviceClose(dev); 385 (*dev)->Release(dev); 386 } 387 388 return -1; 389} 390 391 392/** Initializes the USB system. Returns 0 on success, -1 on error. */ 393static int init_usb(ifc_match_func callback, usb_handle **handle) { 394 int ret = -1; 395 CFMutableDictionaryRef matchingDict; 396 kern_return_t result; 397 io_iterator_t iterator; 398 usb_handle h; 399 400 h.success = 0; 401 h.callback = callback; 402 403 /* 404 * Create our matching dictionary to find appropriate devices. 405 * IOServiceAddMatchingNotification consumes the reference, so we 406 * do not need to release it. 407 */ 408 matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 409 410 if (matchingDict == NULL) { 411 ERR("Couldn't create USB matching dictionary.\n"); 412 return -1; 413 } 414 415 result = IOServiceGetMatchingServices( 416 kIOMasterPortDefault, matchingDict, &iterator); 417 418 if (result != 0) { 419 ERR("Could not create iterator."); 420 return -1; 421 } 422 423 for (;;) { 424 if (! IOIteratorIsValid(iterator)) { 425 /* 426 * Apple documentation advises resetting the iterator if 427 * it should become invalid during iteration. 428 */ 429 IOIteratorReset(iterator); 430 continue; 431 } 432 433 io_service_t device = IOIteratorNext(iterator); 434 435 if (device == 0) { 436 break; 437 } 438 439 if (try_device(device, &h) != 0) { 440 IOObjectRelease(device); 441 ret = -1; 442 break; 443 } 444 445 if (h.success) { 446 *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle))); 447 memcpy(*handle, &h, sizeof(usb_handle)); 448 ret = 0; 449 break; 450 } 451 452 IOObjectRelease(device); 453 } 454 455 IOObjectRelease(iterator); 456 457 return ret; 458} 459 460 461 462/* 463 * Definitions of this file's public functions. 464 */ 465 466usb_handle *usb_open(ifc_match_func callback) { 467 usb_handle *handle = NULL; 468 469 if (init_usb(callback, &handle) < 0) { 470 /* Something went wrong initializing USB. */ 471 return NULL; 472 } 473 474 return handle; 475} 476 477int usb_close(usb_handle *h) { 478 /* TODO: Something better here? */ 479 return 0; 480} 481 482int usb_wait_for_disconnect(usb_handle *usb) { 483 /* TODO: Punt for now */ 484 return 0; 485} 486 487int usb_read(usb_handle *h, void *data, int len) { 488 IOReturn result; 489 UInt32 numBytes = len; 490 491 if (len == 0) { 492 return 0; 493 } 494 495 if (h == NULL) { 496 return -1; 497 } 498 499 if (h->interface == NULL) { 500 ERR("usb_read interface was null\n"); 501 return -1; 502 } 503 504 if (h->bulkIn == 0) { 505 ERR("bulkIn endpoint not assigned\n"); 506 return -1; 507 } 508 509 result = (*h->interface)->ReadPipe( 510 h->interface, h->bulkIn, data, &numBytes); 511 512 if (result == 0) { 513 return (int) numBytes; 514 } else { 515 ERR("usb_read failed with status %x\n", result); 516 } 517 518 return -1; 519} 520 521int usb_write(usb_handle *h, const void *data, int len) { 522 IOReturn result; 523 524 if (len == 0) { 525 return 0; 526 } 527 528 if (h == NULL) { 529 return -1; 530 } 531 532 if (h->interface == NULL) { 533 ERR("usb_write interface was null\n"); 534 return -1; 535 } 536 537 if (h->bulkOut == 0) { 538 ERR("bulkOut endpoint not assigned\n"); 539 return -1; 540 } 541 542#if 0 543 result = (*h->interface)->WritePipe( 544 h->interface, h->bulkOut, (void *)data, len); 545#else 546 /* Attempt to work around crashes in the USB driver that may be caused 547 * by trying to write too much data at once. The kernel IOCopyMapper 548 * panics if a single iovmAlloc needs more than half of its mapper pages. 549 */ 550 const int maxLenToSend = 1048576; // 1 MiB 551 int lenRemaining = len; 552 result = 0; 553 while (lenRemaining > 0) { 554 int lenToSend = lenRemaining > maxLenToSend 555 ? maxLenToSend : lenRemaining; 556 557 result = (*h->interface)->WritePipe( 558 h->interface, h->bulkOut, (void *)data, lenToSend); 559 if (result != 0) break; 560 561 lenRemaining -= lenToSend; 562 data = (const char*)data + lenToSend; 563 } 564#endif 565 566 #if 0 567 if ((result == 0) && (h->zero_mask)) { 568 /* we need 0-markers and our transfer */ 569 if(!(len & h->zero_mask)) { 570 result = (*h->interface)->WritePipe( 571 h->interface, h->bulkOut, (void *)data, 0); 572 } 573 } 574 #endif 575 576 if (result != 0) { 577 ERR("usb_write failed with status %x\n", result); 578 return -1; 579 } 580 581 return len; 582} 583