• 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(IOUSBDeviceInterface ** dev,usb_handle * handle)67 static 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 
251 next_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  */
try_device(io_service_t device,usb_handle * handle)262 static 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. */
init_usb(ifc_match_func callback,usb_handle ** handle)373 static 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 
usb_open(ifc_match_func callback)448 usb_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 
usb_close(usb_handle * h)459 int usb_close(usb_handle *h) {
460     /* TODO: Something better here? */
461     return 0;
462 }
463 
usb_read(usb_handle * h,void * data,int len)464 int 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 
usb_write(usb_handle * h,const void * data,int len)498 int 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