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