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