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