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