• 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     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