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 268 // Create an intermediate plugin. 269 kr = IOCreatePlugInInterfaceForService(device, 270 kIOUSBDeviceUserClientTypeID, 271 kIOCFPlugInInterfaceID, 272 &plugin, &score); 273 274 if ((kr != 0) || (plugin == NULL)) { 275 ERR("Unable to create a plug-in (%08x)\n", kr); 276 goto error; 277 } 278 279 // Now create the device interface. 280 result = (*plugin)->QueryInterface(plugin, 281 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev); 282 if ((result != 0) || (dev == NULL)) { 283 ERR("Couldn't create a device interface (%08x)\n", (int) result); 284 goto error; 285 } 286 287 /* 288 * We don't need the intermediate interface after the device interface 289 * is created. 290 */ 291 IODestroyPlugInInterface(plugin); 292 293 // So, we have a device, finally. Grab its vitals. 294 295 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor); 296 if (kr != 0) { 297 ERR("GetDeviceVendor"); 298 goto error; 299 } 300 301 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product); 302 if (kr != 0) { 303 ERR("GetDeviceProduct"); 304 goto error; 305 } 306 307 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class); 308 if (kr != 0) { 309 ERR("GetDeviceClass"); 310 goto error; 311 } 312 313 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass); 314 if (kr != 0) { 315 ERR("GetDeviceSubClass"); 316 goto error; 317 } 318 319 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol); 320 if (kr != 0) { 321 ERR("GetDeviceProtocol"); 322 goto error; 323 } 324 325 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); 326 327 if (serialIndex > 0) { 328 IOUSBDevRequest req; 329 UInt16 buffer[256]; 330 331 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); 332 req.bRequest = kUSBRqGetDescriptor; 333 req.wValue = (kUSBStringDesc << 8) | serialIndex; 334 //language ID (en-us) for serial number string 335 req.wIndex = 0x0409; 336 req.pData = buffer; 337 req.wLength = sizeof(buffer); 338 kr = (*dev)->DeviceRequest(dev, &req); 339 340 if (kr == kIOReturnSuccess && req.wLenDone > 0) { 341 int i, count; 342 343 // skip first word, and copy the rest to the serial string, changing shorts to bytes. 344 count = (req.wLenDone - 1) / 2; 345 for (i = 0; i < count; i++) 346 handle->info.serial_number[i] = buffer[i + 1]; 347 handle->info.serial_number[i] = 0; 348 } 349 } else { 350 // device has no serial number 351 handle->info.serial_number[0] = 0; 352 } 353 handle->info.writable = 1; 354 355 if (try_interfaces(dev, handle)) { 356 goto error; 357 } 358 359 (*dev)->Release(dev); 360 return 0; 361 362 error: 363 364 if (dev != NULL) { 365 (*dev)->Release(dev); 366 } 367 368 return -1; 369 } 370 371 372 /** Initializes the USB system. Returns 0 on success, -1 on error. */ 373 static int init_usb(ifc_match_func callback, usb_handle **handle) { 374 int ret = -1; 375 CFMutableDictionaryRef matchingDict; 376 kern_return_t result; 377 io_iterator_t iterator; 378 usb_handle h; 379 380 h.success = 0; 381 h.callback = callback; 382 383 /* 384 * Create our matching dictionary to find appropriate devices. 385 * IOServiceAddMatchingNotification consumes the reference, so we 386 * do not need to release it. 387 */ 388 matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 389 390 if (matchingDict == NULL) { 391 ERR("Couldn't create USB matching dictionary.\n"); 392 return -1; 393 } 394 395 result = IOServiceGetMatchingServices( 396 kIOMasterPortDefault, matchingDict, &iterator); 397 398 if (result != 0) { 399 ERR("Could not create iterator."); 400 return -1; 401 } 402 403 for (;;) { 404 if (! IOIteratorIsValid(iterator)) { 405 /* 406 * Apple documentation advises resetting the iterator if 407 * it should become invalid during iteration. 408 */ 409 IOIteratorReset(iterator); 410 continue; 411 } 412 413 io_service_t device = IOIteratorNext(iterator); 414 415 if (device == 0) { 416 break; 417 } 418 419 if (try_device(device, &h) != 0) { 420 IOObjectRelease(device); 421 ret = -1; 422 break; 423 } 424 425 if (h.success) { 426 *handle = calloc(1, sizeof(usb_handle)); 427 memcpy(*handle, &h, sizeof(usb_handle)); 428 ret = 0; 429 break; 430 } 431 432 IOObjectRelease(device); 433 } 434 435 IOObjectRelease(iterator); 436 437 return ret; 438 } 439 440 441 442 /* 443 * Definitions of this file's public functions. 444 */ 445 446 usb_handle *usb_open(ifc_match_func callback) { 447 usb_handle *handle = NULL; 448 449 if (init_usb(callback, &handle) < 0) { 450 /* Something went wrong initializing USB. */ 451 return NULL; 452 } 453 454 return handle; 455 } 456 457 int usb_close(usb_handle *h) { 458 /* TODO: Something better here? */ 459 return 0; 460 } 461 462 int usb_read(usb_handle *h, void *data, int len) { 463 IOReturn result; 464 UInt32 numBytes = len; 465 466 if (len == 0) { 467 return 0; 468 } 469 470 if (h == NULL) { 471 return -1; 472 } 473 474 if (h->interface == NULL) { 475 ERR("usb_read interface was null\n"); 476 return -1; 477 } 478 479 if (h->bulkIn == 0) { 480 ERR("bulkIn endpoint not assigned\n"); 481 return -1; 482 } 483 484 result = (*h->interface)->ReadPipe( 485 h->interface, h->bulkIn, data, &numBytes); 486 487 if (result == 0) { 488 return (int) numBytes; 489 } else { 490 ERR("usb_read failed with status %x\n", result); 491 } 492 493 return -1; 494 } 495 496 int usb_write(usb_handle *h, const void *data, int len) { 497 IOReturn result; 498 499 if (len == 0) { 500 return 0; 501 } 502 503 if (h == NULL) { 504 return -1; 505 } 506 507 if (h->interface == NULL) { 508 ERR("usb_write interface was null\n"); 509 return -1; 510 } 511 512 if (h->bulkOut == 0) { 513 ERR("bulkOut endpoint not assigned\n"); 514 return -1; 515 } 516 517 #if 0 518 result = (*h->interface)->WritePipe( 519 h->interface, h->bulkOut, (void *)data, len); 520 #else 521 /* Attempt to work around crashes in the USB driver that may be caused 522 * by trying to write too much data at once. The kernel IOCopyMapper 523 * panics if a single iovmAlloc needs more than half of its mapper pages. 524 */ 525 const int maxLenToSend = 1048576; // 1 MiB 526 int lenRemaining = len; 527 result = 0; 528 while (lenRemaining > 0) { 529 int lenToSend = lenRemaining > maxLenToSend 530 ? maxLenToSend : lenRemaining; 531 532 result = (*h->interface)->WritePipe( 533 h->interface, h->bulkOut, (void *)data, lenToSend); 534 if (result != 0) break; 535 536 lenRemaining -= lenToSend; 537 data = (const char*)data + lenToSend; 538 } 539 #endif 540 541 #if 0 542 if ((result == 0) && (h->zero_mask)) { 543 /* we need 0-markers and our transfer */ 544 if(!(len & h->zero_mask)) { 545 result = (*h->interface)->WritePipe( 546 h->interface, h->bulkOut, (void *)data, 0); 547 } 548 } 549 #endif 550 551 if (result != 0) { 552 ERR("usb_write failed with status %x\n", result); 553 return -1; 554 } 555 556 return len; 557 } 558