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