usb_osx.c revision dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0
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 */ 53struct 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. */ 67static int try_interfaces(IOUSBDeviceInterface **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 %d", kr); 235 return -1; 236 } 237 } 238 239 if (handle->info.has_bulk_out) { 240 kr = (*interface)->ClearPipeStallBothEnds(interface, 241 handle->bulkOut); 242 if (kr != 0) { 243 ERR("could not clear output pipe; result %d", kr); 244 return -1; 245 } 246 } 247 248 return 0; 249 } 250 251next_interface: 252 (*interface)->USBInterfaceClose(interface); 253 (*interface)->Release(interface); 254 } 255 256 return 0; 257} 258 259/** Try out the given device and see if there's a match. Returns 0 on 260 * success, -1 on failure. 261 */ 262static int try_device(io_service_t device, usb_handle *handle) { 263 kern_return_t kr; 264 IOCFPlugInInterface **plugin = NULL; 265 IOUSBDeviceInterface182 **dev = NULL; 266 SInt32 score; 267 HRESULT result; 268 UInt8 serialIndex; 269 270 // Create an intermediate plugin. 271 kr = IOCreatePlugInInterfaceForService(device, 272 kIOUSBDeviceUserClientTypeID, 273 kIOCFPlugInInterfaceID, 274 &plugin, &score); 275 276 if ((kr != 0) || (plugin == NULL)) { 277 ERR("Unable to create a plug-in (%08x)\n", kr); 278 goto error; 279 } 280 281 // Now create the device interface. 282 result = (*plugin)->QueryInterface(plugin, 283 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev); 284 if ((result != 0) || (dev == NULL)) { 285 ERR("Couldn't create a device interface (%08x)\n", (int) result); 286 goto error; 287 } 288 289 /* 290 * We don't need the intermediate interface after the device interface 291 * is created. 292 */ 293 IODestroyPlugInInterface(plugin); 294 295 // So, we have a device, finally. Grab its vitals. 296 297 kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor); 298 if (kr != 0) { 299 ERR("GetDeviceVendor"); 300 goto error; 301 } 302 303 kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product); 304 if (kr != 0) { 305 ERR("GetDeviceProduct"); 306 goto error; 307 } 308 309 kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class); 310 if (kr != 0) { 311 ERR("GetDeviceClass"); 312 goto error; 313 } 314 315 kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass); 316 if (kr != 0) { 317 ERR("GetDeviceSubClass"); 318 goto error; 319 } 320 321 kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol); 322 if (kr != 0) { 323 ERR("GetDeviceProtocol"); 324 goto error; 325 } 326 327 kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); 328 329 if (serialIndex > 0) { 330 IOUSBDevRequest req; 331 UInt16 buffer[256]; 332 333 req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); 334 req.bRequest = kUSBRqGetDescriptor; 335 req.wValue = (kUSBStringDesc << 8) | serialIndex; 336 req.wIndex = 0; 337 req.pData = buffer; 338 req.wLength = sizeof(buffer); 339 kr = (*dev)->DeviceRequest(dev, &req); 340 341 if (kr == kIOReturnSuccess && req.wLenDone > 0) { 342 int i, count; 343 344 // skip first word, and copy the rest to the serial string, changing shorts to bytes. 345 count = (req.wLenDone - 1) / 2; 346 for (i = 0; i < count; i++) 347 handle->info.serial_number[i] = buffer[i + 1]; 348 handle->info.serial_number[i] = 0; 349 } 350 } else { 351 // device has no serial number 352 handle->info.serial_number[0] = 0; 353 } 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. */ 373static 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 usb_ifc_info info; 420 421 if (try_device(device, &h) != 0) { 422 IOObjectRelease(device); 423 ret = -1; 424 break; 425 } 426 427 if (h.success) { 428 *handle = calloc(1, sizeof(usb_handle)); 429 memcpy(*handle, &h, sizeof(usb_handle)); 430 ret = 0; 431 break; 432 } 433 434 IOObjectRelease(device); 435 } 436 437 IOObjectRelease(iterator); 438 439 return ret; 440} 441 442 443 444/* 445 * Definitions of this file's public functions. 446 */ 447 448usb_handle *usb_open(ifc_match_func callback) { 449 usb_handle *handle = NULL; 450 451 if (init_usb(callback, &handle) < 0) { 452 /* Something went wrong initializing USB. */ 453 return NULL; 454 } 455 456 return handle; 457} 458 459int usb_close(usb_handle *h) { 460 /* TODO: Something better here? */ 461 return 0; 462} 463 464int usb_read(usb_handle *h, void *data, int len) { 465 IOReturn result; 466 UInt32 numBytes = len; 467 468 if (len == 0) { 469 return 0; 470 } 471 472 if (h == NULL) { 473 return -1; 474 } 475 476 if (h->interface == NULL) { 477 ERR("usb_read interface was null\n"); 478 return -1; 479 } 480 481 if (h->bulkIn == 0) { 482 ERR("bulkIn endpoint not assigned\n"); 483 return -1; 484 } 485 486 result = (*h->interface)->ReadPipe( 487 h->interface, h->bulkIn, data, &numBytes); 488 489 if (result == 0) { 490 return (int) numBytes; 491 } else { 492 ERR("usb_read failed with status %x\n", result); 493 } 494 495 return -1; 496} 497 498int usb_write(usb_handle *h, const void *data, int len) { 499 IOReturn result; 500 501 if (len == 0) { 502 return 0; 503 } 504 505 if (h == NULL) { 506 return -1; 507 } 508 509 if (h->interface == NULL) { 510 ERR("usb_write interface was null\n"); 511 return -1; 512 } 513 514 if (h->bulkOut == 0) { 515 ERR("bulkOut endpoint not assigned\n"); 516 return -1; 517 } 518 519 result = (*h->interface)->WritePipe( 520 h->interface, h->bulkOut, (void *)data, len); 521 522 #if 0 523 if ((result == 0) && (h->zero_mask)) { 524 /* we need 0-markers and our transfer */ 525 if(!(len & h->zero_mask)) { 526 result = (*h->interface)->WritePipe( 527 h->interface, h->bulkOut, (void *)data, 0); 528 } 529 } 530 #endif 531 532 if (result != 0) { 533 ERR("usb_write failed with status %x\n", result); 534 return -1; 535 } 536 537 return len; 538} 539