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