• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
258 next_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  */
try_device(io_service_t device,usb_handle * handle)269 static 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. */
init_usb(ifc_match_func callback,std::unique_ptr<usb_handle> * handle)391 static 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 
usb_open(ifc_match_func callback)464 Transport* 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 
Close()475 int OsxUsbTransport::Close() {
476     /* TODO: Something better here? */
477     return 0;
478 }
479 
Read(void * data,size_t len)480 ssize_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 
Write(const void * data,size_t len)513 ssize_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