summaryrefslogtreecommitdiffstats
path: root/libusb/darwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'libusb/darwin.c')
-rw-r--r--libusb/darwin.c1209
1 files changed, 1209 insertions, 0 deletions
diff --git a/libusb/darwin.c b/libusb/darwin.c
new file mode 100644
index 0000000..6f90ecc
--- /dev/null
+++ b/libusb/darwin.c
@@ -0,0 +1,1209 @@
+/*
+ * Darwin/MacOS X Support
+ *
+ * (c) 2002-2005 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ *
+ * (04/17/2005):
+ * - Lots of minor fixes.
+ * - Endpoint table now made by claim_interface to fix a bug.
+ * - Merged Read/Write to make modifications easier.
+ * (03/25/2005):
+ * - Fixed a bug when using asynchronous callbacks within a multi-threaded application.
+ * (03/14/2005):
+ * - Added an endpoint table to speed up bulk transfers.
+ * 0.1.11 (02/22/2005):
+ * - Updated error checking in read/write routines to check completion codes.
+ * - Updated set_configuration so that the open interface is reclaimed before completion.
+ * - Fixed several typos.
+ * 0.1.8 (01/12/2004):
+ * - Fixed several memory leaks.
+ * - Readded 10.0 support
+ * - Added support for USB fuctions defined in 10.3 and above
+ * (01/02/2003):
+ * - Applied a patch by Philip Edelbrock <phil@edgedesign.us> that fixes a bug in usb_control_msg.
+ * (12/16/2003):
+ * - Even better error printing.
+ * - Devices that cannot be opened can have their interfaces opened.
+ * 0.1.6 (05/12/2002):
+ * - Fixed problem where libusb holds resources after program completion.
+ * - Mouse should no longer freeze up now.
+ * 0.1.2 (02/13/2002):
+ * - Bulk functions should work properly now.
+ * 0.1.1 (02/11/2002):
+ * - Fixed major bug (device and interface need to be released after use)
+ * 0.1.0 (01/06/2002):
+ * - Tested driver with gphoto (works great as long as Image Capture isn't running)
+ * 0.1d (01/04/2002):
+ * - Implimented clear_halt and resetep
+ * - Uploaded to CVS.
+ * 0.1b (01/04/2002):
+ * - Added usb_debug line to bulk read and write function.
+ * 0.1a (01/03/2002):
+ * - Driver mostly completed using the macosx driver I wrote for my rioutil software.
+ *
+ * Derived from Linux version by Richard Tobin.
+ * Also partly derived from BSD version.
+ *
+ * This library is covered by the LGPL, read LICENSE for details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/* standard includes for darwin/os10 (IOKit) */
+#include <mach/mach_port.h>
+#include <IOKit/IOCFBundle.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOCFPlugIn.h>
+
+#include "usbi.h"
+
+/* some defines */
+/* IOUSBInterfaceInferface */
+#if defined (kIOUSBInterfaceInterfaceID220)
+
+// #warning "libusb being compiled for 10.4 or later"
+#define usb_interface_t IOUSBInterfaceInterface220
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
+#define InterfaceVersion 220
+
+#elif defined (kIOUSBInterfaceInterfaceID197)
+
+// #warning "libusb being compiled for 10.3 or later"
+#define usb_interface_t IOUSBInterfaceInterface197
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197
+#define InterfaceVersion 197
+
+#elif defined (kIOUSBInterfaceInterfaceID190)
+
+// #warning "libusb being compiled for 10.2 or later"
+#define usb_interface_t IOUSBInterfaceInterface190
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190
+#define InterfaceVersion 190
+
+#elif defined (kIOUSBInterfaceInterfaceID182)
+
+// #warning "libusb being compiled for 10.1 or later"
+#define usb_interface_t IOUSBInterfaceInterface182
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182
+#define InterfaceVersion 182
+
+#else
+
+/* No timeout functions available! Time to upgrade your os. */
+#warning "libusb being compiled without support for timeout bulk functions! 10.0 and up"
+#define usb_interface_t IOUSBInterfaceInterface
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID
+#define LIBUSB_NO_TIMEOUT_INTERFACE
+#define InterfaceVersion 180
+
+#endif
+
+/* IOUSBDeviceInterface */
+#if defined (kIOUSBDeviceInterfaceID197)
+
+#define usb_device_t IOUSBDeviceInterface197
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
+#define DeviceVersion 197
+
+#elif defined (kIOUSBDeviceInterfaceID187)
+
+#define usb_device_t IOUSBDeviceInterface187
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID187
+#define DeviceVersion 187
+
+#elif defined (kIOUSBDeviceInterfaceID182)
+
+#define usb_device_t IOUSBDeviceInterface182
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID182
+#define DeviceVersion 182
+
+#else
+
+#define usb_device_t IOUSBDeviceInterface
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID
+#define LIBUSB_NO_TIMEOUT_DEVICE
+#define LIBUSB_NO_SEIZE_DEVICE
+#define DeviceVersion 180
+
+#endif
+
+typedef IOReturn io_return_t;
+typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
+typedef SInt32 s_int32_t;
+typedef IOReturn (*rw_async_func_t)(void *self, UInt8 pipeRef, void *buf, UInt32 size,
+ IOAsyncCallback1 callback, void *refcon);
+typedef IOReturn (*rw_async_to_func_t)(void *self, UInt8 pipeRef, void *buf, UInt32 size,
+ UInt32 noDataTimeout, UInt32 completionTimeout,
+ IOAsyncCallback1 callback, void *refcon);
+
+#if !defined(IO_OBJECT_NULL)
+#define IO_OBJECT_NULL ((io_object_t)0)
+#endif
+
+/* Darwin/OS X impl does not use fd field, instead it uses this */
+struct darwin_dev_handle {
+ usb_device_t **device;
+ usb_interface_t **interface;
+ int open;
+
+ /* stored translation table for pipes to endpoints */
+ int num_endpoints;
+ unsigned char *endpoint_addrs;
+};
+
+static CFMutableDictionaryRef matchingDict;
+static IONotificationPortRef gNotifyPort;
+static mach_port_t masterPort = MACH_PORT_NULL;
+
+static void darwin_cleanup (void)
+{
+ IONotificationPortDestroy(gNotifyPort);
+ mach_port_deallocate(mach_task_self(), masterPort);
+}
+
+static char *darwin_error_str (int result) {
+ switch (result) {
+ case kIOReturnSuccess:
+ return "no error";
+ case kIOReturnNotOpen:
+ return "device not opened for exclusive access";
+ case kIOReturnNoDevice:
+ return "no connection to an IOService";
+ case kIOUSBNoAsyncPortErr:
+ return "no async port has been opened for interface";
+ case kIOReturnExclusiveAccess:
+ return "another process has device opened for exclusive access";
+ case kIOUSBPipeStalled:
+ return "pipe is stalled";
+ case kIOReturnError:
+ return "could not establish a connection to the Darwin kernel";
+ case kIOUSBTransactionTimeout:
+ return "transaction timed out";
+ case kIOReturnBadArgument:
+ return "invalid argument";
+ case kIOReturnAborted:
+ return "transaction aborted";
+ default:
+ return "unknown error";
+ }
+}
+
+/* not a valid errorno outside darwin.c */
+#define LUSBDARWINSTALL (ELAST+1)
+
+static int darwin_to_errno (int result) {
+ switch (result) {
+ case kIOReturnSuccess:
+ return 0;
+ case kIOReturnNotOpen:
+ return EBADF;
+ case kIOReturnNoDevice:
+ case kIOUSBNoAsyncPortErr:
+ return ENXIO;
+ case kIOReturnExclusiveAccess:
+ return EBUSY;
+ case kIOUSBPipeStalled:
+ return LUSBDARWINSTALL;
+ case kIOReturnBadArgument:
+ return EINVAL;
+ case kIOUSBTransactionTimeout:
+ return ETIMEDOUT;
+ case kIOReturnError:
+ default:
+ return 1;
+ }
+}
+
+static int usb_setup_iterator (io_iterator_t *deviceIterator)
+{
+ int result;
+
+ /* set up the matching dictionary for class IOUSBDevice and its subclasses.
+ It will be consumed by the IOServiceGetMatchingServices call */
+ if ((matchingDict = IOServiceMatching(kIOUSBDeviceClassName)) == NULL) {
+ darwin_cleanup ();
+
+ USB_ERROR_STR(-1, "libusb/darwin.c usb_setup_iterator: Could not create a matching dictionary.\n");
+ }
+
+ result = IOServiceGetMatchingServices(masterPort, matchingDict, deviceIterator);
+ matchingDict = NULL;
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR (-darwin_to_errno (result), "libusb/darwin.c usb_setup_iterator: IOServiceGetMatchingServices: %s\n",
+ darwin_error_str(result));
+
+ return 0;
+}
+
+static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp)
+{
+ io_cf_plugin_ref_t *plugInInterface = NULL;
+ usb_device_t **device;
+ io_service_t usbDevice;
+ long result, score;
+
+ if (!IOIteratorIsValid (deviceIterator) || !(usbDevice = IOIteratorNext(deviceIterator)))
+ return NULL;
+
+ result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface,
+ &score);
+
+ result = IOObjectRelease(usbDevice);
+ if (result || !plugInInterface)
+ return NULL;
+
+ (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
+ (LPVOID)&device);
+
+ (*plugInInterface)->Stop(plugInInterface);
+ IODestroyPlugInInterface (plugInInterface);
+ plugInInterface = NULL;
+
+ (*(device))->GetLocationID(device, locationp);
+
+ return device;
+}
+
+int usb_os_open(usb_dev_handle *dev)
+{
+ struct darwin_dev_handle *device;
+
+ io_return_t result;
+ io_iterator_t deviceIterator;
+
+ usb_device_t **darwin_device;
+
+ UInt32 location = *((UInt32 *)dev->device->dev);
+ UInt32 dlocation;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if (masterPort == MACH_PORT_NULL)
+ USB_ERROR(-EINVAL);
+
+ device = calloc(1, sizeof(struct darwin_dev_handle));
+ if (!device)
+ USB_ERROR(-ENOMEM);
+
+ if (usb_debug > 3)
+ fprintf(stderr, "usb_os_open: %04x:%04x\n",
+ dev->device->descriptor.idVendor,
+ dev->device->descriptor.idProduct);
+
+ if ((result = usb_setup_iterator (&deviceIterator)) < 0)
+ return result;
+
+ /* This port of libusb uses locations to keep track of devices. */
+ while ((darwin_device = usb_get_next_device (deviceIterator, &dlocation)) != NULL) {
+ if (dlocation == location)
+ break;
+
+ (*darwin_device)->Release(darwin_device);
+ }
+
+ IOObjectRelease(deviceIterator);
+ device->device = darwin_device;
+
+ if (device->device == NULL)
+ USB_ERROR_STR (-ENOENT, "usb_os_open: %s\n", "Device not found!");
+
+#if !defined (LIBUSB_NO_SEIZE_DEVICE)
+ result = (*(device->device))->USBDeviceOpenSeize (device->device);
+#else
+ /* No Seize in OS X 10.0 (Darwin 1.4) */
+ result = (*(device->device))->USBDeviceOpen (device->device);
+#endif
+
+ if (result != kIOReturnSuccess) {
+ switch (result) {
+ case kIOReturnExclusiveAccess:
+ if (usb_debug > 0)
+ fprintf (stderr, "usb_os_open(USBDeviceOpenSeize): %s\n", darwin_error_str(result));
+ break;
+ default:
+ (*(device->device))->Release (device->device);
+ USB_ERROR_STR(-darwin_to_errno (result), "usb_os_open(USBDeviceOpenSeize): %s",
+ darwin_error_str(result));
+ }
+
+ device->open = 0;
+ } else
+ device->open = 1;
+
+ dev->impl_info = device;
+ dev->interface = -1;
+ dev->altsetting = -1;
+
+ device->num_endpoints = 0;
+ device->endpoint_addrs = NULL;
+
+ return 0;
+}
+
+int usb_os_close(usb_dev_handle *dev)
+{
+ struct darwin_dev_handle *device;
+ io_return_t result;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR(-ENOENT);
+
+ usb_release_interface(dev, dev->interface);
+
+ if (usb_debug > 3)
+ fprintf(stderr, "usb_os_close: %04x:%04x\n",
+ dev->device->descriptor.idVendor,
+ dev->device->descriptor.idProduct);
+
+ if (device->open == 1)
+ result = (*(device->device))->USBDeviceClose(device->device);
+ else
+ result = kIOReturnSuccess;
+
+ /* device may not need to be released, but if it has to... */
+ (*(device->device))->Release(device->device);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_os_close(USBDeviceClose): %s", darwin_error_str(result));
+
+ free (device);
+
+ return 0;
+}
+
+static int get_endpoints (struct darwin_dev_handle *device)
+{
+ io_return_t ret;
+
+ u_int8_t numep, direction, number;
+ u_int8_t dont_care1, dont_care3;
+ u_int16_t dont_care2;
+
+ int i;
+
+ if (device == NULL || device->interface == NULL)
+ return -EINVAL;
+
+ if (usb_debug > 1)
+ fprintf(stderr, "libusb/darwin.c get_endpoints: building table of endpoints.\n");
+
+ /* retrieve the total number of endpoints on this interface */
+ ret = (*(device->interface))->GetNumEndpoints(device->interface, &numep);
+ if ( ret ) {
+ if ( usb_debug > 1 )
+ fprintf ( stderr, "get_endpoints: interface is %p\n", device->interface );
+
+ USB_ERROR_STR ( -ret, "get_endpoints: can't get number of endpoints for interface" );
+ }
+
+ free (device->endpoint_addrs);
+ device->endpoint_addrs = calloc (sizeof (unsigned char), numep);
+
+ /* iterate through pipe references */
+ for (i = 1 ; i <= numep ; i++) {
+ ret = (*(device->interface))->GetPipeProperties(device->interface, i, &direction, &number,
+ &dont_care1, &dont_care2, &dont_care3);
+
+ if (ret != kIOReturnSuccess) {
+ fprintf (stderr, "get_endpoints: an error occurred getting pipe information on pipe %d\n",
+ i );
+ USB_ERROR_STR(-darwin_to_errno(ret), "get_endpoints(GetPipeProperties): %s", darwin_error_str(ret));
+ }
+
+ if (usb_debug > 1)
+ fprintf (stderr, "get_endpoints: Pipe %i: DIR: %i number: %i\n", i, direction, number);
+
+ device->endpoint_addrs[i - 1] = ((direction << 7 & USB_ENDPOINT_DIR_MASK) |
+ (number & USB_ENDPOINT_ADDRESS_MASK));
+ }
+
+ device->num_endpoints = numep;
+
+ if (usb_debug > 1)
+ fprintf(stderr, "libusb/darwin.c get_endpoints: complete.\n");
+
+ return 0;
+}
+
+static int claim_interface (usb_dev_handle *dev, int interface)
+{
+ io_iterator_t interface_iterator;
+ io_service_t usbInterface = IO_OBJECT_NULL;
+ io_return_t result;
+ io_cf_plugin_ref_t *plugInInterface = NULL;
+
+ IOUSBFindInterfaceRequest request;
+
+ struct darwin_dev_handle *device;
+ long score;
+ int current_interface;
+
+ device = dev->impl_info;
+
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ result = (*(device->device))->CreateInterfaceIterator(device->device, &request, &interface_iterator);
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR (-darwin_to_errno(result), "claim_interface(CreateInterfaceIterator): %s",
+ darwin_error_str(result));
+
+ for ( current_interface=0 ; current_interface <= interface ; current_interface++ ) {
+ usbInterface = IOIteratorNext(interface_iterator);
+ if ( usb_debug > 3 )
+ fprintf ( stderr, "Interface %d of device is 0x%08x\n",
+ current_interface, usbInterface );
+ }
+
+ current_interface--;
+
+ /* the interface iterator is no longer needed, release it */
+ IOObjectRelease(interface_iterator);
+
+ if (!usbInterface) {
+ u_int8_t nConfig; /* Index of configuration to use */
+ IOUSBConfigurationDescriptorPtr configDesc; /* to describe which configuration to select */
+ /* Only a composite class device with no vendor-specific driver will
+ be configured. Otherwise, we need to do it ourselves, or there
+ will be no interfaces for the device. */
+
+ if ( usb_debug > 3 )
+ fprintf ( stderr,"claim_interface: No interface found; selecting configuration\n" );
+
+ result = (*(device->device))->GetNumberOfConfigurations ( device->device, &nConfig );
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(GetNumberOfConfigurations): %s",
+ darwin_error_str(result));
+
+ if (nConfig < 1)
+ USB_ERROR_STR(-ENXIO ,"claim_interface(GetNumberOfConfigurations): no configurations");
+ else if ( nConfig > 1 && usb_debug > 0 )
+ fprintf ( stderr, "claim_interface: device has more than one"
+ " configuration, using the first (warning)\n" );
+
+ if ( usb_debug > 3 )
+ fprintf ( stderr, "claim_interface: device has %d configuration%s\n",
+ (int)nConfig, (nConfig>1?"s":"") );
+
+ /* Always use the first configuration */
+ result = (*(device->device))->GetConfigurationDescriptorPtr ( (device->device), 0, &configDesc );
+ if (result != kIOReturnSuccess) {
+ if (device->open == 1) {
+ (*(device->device))->USBDeviceClose ( (device->device) );
+ (*(device->device))->Release ( (device->device) );
+ }
+
+ USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(GetConfigurationDescriptorPtr): %s",
+ darwin_error_str(result));
+ } else if ( usb_debug > 3 )
+ fprintf ( stderr, "claim_interface: configuration value is %d\n",
+ configDesc->bConfigurationValue );
+
+ if (device->open == 1) {
+ result = (*(device->device))->SetConfiguration ( (device->device), configDesc->bConfigurationValue );
+
+ if (result != kIOReturnSuccess) {
+ (*(device->device))->USBDeviceClose ( (device->device) );
+ (*(device->device))->Release ( (device->device) );
+
+ USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(SetConfiguration): %s",
+ darwin_error_str(result));
+ }
+
+ dev->config = configDesc->bConfigurationValue;
+ }
+
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ /* Now go back and get the chosen interface */
+ result = (*(device->device))->CreateInterfaceIterator(device->device, &request, &interface_iterator);
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR (-darwin_to_errno(result), "claim_interface(CreateInterfaceIterator): %s",
+ darwin_error_str(result));
+
+ for (current_interface = 0 ; current_interface <= interface ; current_interface++) {
+ usbInterface = IOIteratorNext(interface_iterator);
+
+ if ( usb_debug > 3 )
+ fprintf ( stderr, "claim_interface: Interface %d of device is 0x%08x\n",
+ current_interface, usbInterface );
+ }
+ current_interface--;
+
+ /* the interface iterator is no longer needed, release it */
+ IOObjectRelease(interface_iterator);
+
+ if (!usbInterface)
+ USB_ERROR_STR (-ENOENT, "claim_interface: interface iterator returned NULL");
+ }
+
+ result = IOCreatePlugInInterfaceForService(usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ /* No longer need the usbInterface object after getting the plug-in */
+ result = IOObjectRelease(usbInterface);
+ if (result || !plugInInterface)
+ USB_ERROR(-ENOENT);
+
+ /* Now create the device interface for the interface */
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(InterfaceInterfaceID),
+ (LPVOID) &device->interface);
+
+ /* No longer need the intermediate plug-in */
+ (*plugInInterface)->Stop(plugInInterface);
+ IODestroyPlugInInterface (plugInInterface);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(QueryInterface): %s",
+ darwin_error_str(result));
+
+ if (!device->interface)
+ USB_ERROR(-EACCES);
+
+ if ( usb_debug > 3 )
+ fprintf ( stderr, "claim_interface: Interface %d of device from QueryInterface is %p\n",
+ current_interface, device->interface);
+
+ /* claim the interface */
+ result = (*(device->interface))->USBInterfaceOpen(device->interface);
+ if (result)
+ USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(USBInterfaceOpen): %s",
+ darwin_error_str(result));
+
+ result = get_endpoints (device);
+
+ if (result) {
+ /* this should not happen */
+ usb_release_interface (dev, interface);
+ USB_ERROR_STR ( result, "claim_interface: could not build endpoint table");
+ }
+
+ return 0;
+}
+
+int usb_set_configuration (usb_dev_handle *dev, int configuration)
+{
+ struct darwin_dev_handle *device;
+ io_return_t result;
+ int interface;
+
+ if ( usb_debug > 3 )
+ fprintf ( stderr, "usb_set_configuration: called for config %x\n", configuration );
+
+ if (!dev)
+ USB_ERROR_STR ( -ENXIO, "usb_set_configuration: called with null device\n" );
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR_STR ( -ENOENT, "usb_set_configuration: device not properly initialized" );
+
+ /* Setting configuration will invalidate the interface, so we need
+ to reclaim it. First, dispose of existing interface, if any. */
+ interface = dev->interface;
+
+ if ( device->interface )
+ usb_release_interface(dev, dev->interface);
+
+ result = (*(device->device))->SetConfiguration(device->device, configuration);
+
+ if (result)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_set_configuration(SetConfiguration): %s",
+ darwin_error_str(result));
+
+ /* Reclaim interface */
+ if (interface != -1)
+ result = usb_claim_interface (dev, interface);
+
+ dev->config = configuration;
+
+ return result;
+}
+
+int usb_claim_interface(usb_dev_handle *dev, int interface)
+{
+ struct darwin_dev_handle *device = dev->impl_info;
+
+ io_return_t result;
+
+ if ( usb_debug > 3 )
+ fprintf ( stderr, "usb_claim_interface: called for interface %d\n", interface );
+
+ if (!device)
+ USB_ERROR_STR ( -ENOENT, "usb_claim_interface: device is NULL" );
+
+ if (!(device->device))
+ USB_ERROR_STR ( -EINVAL, "usb_claim_interface: device->device is NULL" );
+
+ /* If we have already claimed an interface, release it */
+ if ( device->interface )
+ usb_release_interface(dev, dev->interface);
+
+ result = claim_interface ( dev, interface );
+ if ( result )
+ USB_ERROR_STR ( result, "usb_claim_interface: couldn't claim interface" );
+
+ dev->interface = interface;
+
+ /* interface is claimed and async IO is set up: return 0 */
+ return 0;
+}
+
+int usb_release_interface(usb_dev_handle *dev, int interface)
+{
+ struct darwin_dev_handle *device;
+ io_return_t result;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR(-ENOENT);
+
+ /* interface is not open */
+ if (!device->interface)
+ return 0;
+
+ result = (*(device->interface))->USBInterfaceClose(device->interface);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_release_interface(USBInterfaceClose): %s",
+ darwin_error_str(result));
+
+ result = (*(device->interface))->Release(device->interface);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_release_interface(Release): %s",
+ darwin_error_str(result));
+
+ device->interface = NULL;
+
+ free (device->endpoint_addrs);
+
+ device->num_endpoints = 0;
+ device->endpoint_addrs = NULL;
+
+ dev->interface = -1;
+ dev->altsetting = -1;
+
+ return 0;
+}
+
+int usb_set_altinterface(usb_dev_handle *dev, int alternate)
+{
+ struct darwin_dev_handle *device;
+ io_return_t result;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR(-ENOENT);
+
+ /* interface is not open */
+ if (!device->interface)
+ USB_ERROR_STR(-EACCES, "usb_set_altinterface: interface used without being claimed");
+
+ result = (*(device->interface))->SetAlternateInterface(device->interface, alternate);
+
+ if (result)
+ USB_ERROR_STR(result, "usb_set_altinterface: could not set alternate interface");
+
+ dev->altsetting = alternate;
+
+ result = get_endpoints (device);
+ if (result) {
+ /* this should not happen */
+ USB_ERROR_STR ( result, "usb_set_altinterface: could not build endpoint table");
+ }
+
+ return 0;
+}
+
+/* simple function that figures out what pipeRef is associated with an endpoint */
+static int ep_to_pipeRef (struct darwin_dev_handle *device, int ep)
+{
+ int i;
+
+ if (usb_debug > 1)
+ fprintf(stderr, "libusb/darwin.c ep_to_pipeRef: Converting ep address to pipeRef.\n");
+
+ for (i = 0 ; i < device->num_endpoints ; i++)
+ if (device->endpoint_addrs[i] == ep)
+ return i + 1;
+
+ /* No pipe found with the correct endpoint address */
+ if (usb_debug > 1)
+ fprintf(stderr, "libusb/darwin.c ep_to_pipeRef: No pipeRef found with endpoint address 0x%02x.\n", ep);
+
+ return -1;
+}
+
+/* argument to handle multiple parameters to rw_completed */
+struct rw_complete_arg {
+ UInt32 io_size;
+ IOReturn result;
+ CFRunLoopRef cf_loop;
+};
+
+static void rw_completed(void *refcon, io_return_t result, void *io_size)
+{
+ struct rw_complete_arg *rw_arg = (struct rw_complete_arg *)refcon;
+
+ if (usb_debug > 2)
+ fprintf(stderr, "io async operation completed: %s, size=%lu, result=0x%08x\n", darwin_error_str(result),
+ (UInt32)io_size, result);
+
+ rw_arg->io_size = (UInt32)io_size;
+ rw_arg->result = result;
+
+ CFRunLoopStop(rw_arg->cf_loop);
+}
+
+static int usb_bulk_transfer (usb_dev_handle *dev, int ep, char *bytes, int size, int timeout,
+ rw_async_func_t rw_async, rw_async_to_func_t rw_async_to)
+{
+ struct darwin_dev_handle *device;
+
+ io_return_t result = -1;
+
+ CFRunLoopSourceRef cfSource;
+ int pipeRef;
+
+ struct rw_complete_arg rw_arg;
+
+ u_int8_t transferType;
+
+ /* None of the values below are used in libusb for bulk transfers */
+ u_int8_t direction, number, interval;
+ u_int16_t maxPacketSize;
+
+ if (!dev)
+ USB_ERROR_STR ( -ENXIO, "usb_bulk_transfer: Called with NULL device" );
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR_STR ( -ENOENT, "usb_bulk_transfer: Device not open" );
+
+ /* interface is not open */
+ if (!device->interface)
+ USB_ERROR_STR(-EACCES, "usb_bulk_transfer: Interface used before it was opened");
+
+
+ /* Set up transfer */
+ if ((pipeRef = ep_to_pipeRef(device, ep)) < 0)
+ USB_ERROR_STR ( -EINVAL, "usb_bulk_transfer: Invalid pipe reference" );
+
+ (*(device->interface))->GetPipeProperties (device->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ (*(device->interface))->CreateInterfaceAsyncEventSource(device->interface, &cfSource);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
+
+ bzero((void *)&rw_arg, sizeof(struct rw_complete_arg));
+ rw_arg.cf_loop = CFRunLoopGetCurrent();
+ /* Transfer set up complete */
+
+ if (usb_debug > 0)
+ fprintf (stderr, "libusb/darwin.c usb_bulk_transfer: Transfering %i bytes of data on endpoint 0x%02x\n",
+ size, ep);
+
+ /* Bulk transfer */
+ if (transferType == kUSBInterrupt && usb_debug > 3)
+ fprintf (stderr, "libusb/darwin.c usb_bulk_transfer: USB pipe is an interrupt pipe. Timeouts will not be used.\n");
+
+ if ( transferType != kUSBInterrupt && rw_async_to != NULL)
+
+ result = rw_async_to (device->interface, pipeRef, bytes, size, timeout, timeout,
+ (IOAsyncCallback1)rw_completed, (void *)&rw_arg);
+ else
+ result = rw_async (device->interface, pipeRef, bytes, size, (IOAsyncCallback1)rw_completed,
+ (void *)&rw_arg);
+
+ if (result == kIOReturnSuccess) {
+ /* wait for write to complete */
+ if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, (timeout+999)/1000, true) == kCFRunLoopRunTimedOut) {
+ (*(device->interface))->AbortPipe(device->interface, pipeRef);
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); /* Pick up aborted callback */
+ if (usb_debug)
+ fprintf(stderr, "usb_bulk_read: input timed out\n");
+ }
+ }
+
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
+
+ /* Check the return code of both the write and completion functions. */
+ if (result != kIOReturnSuccess || (rw_arg.result != kIOReturnSuccess &&
+ rw_arg.result != kIOReturnAborted) ) {
+ int error_code;
+ char *error_str;
+
+ if (result == kIOReturnSuccess) {
+ error_code = darwin_to_errno (rw_arg.result);
+ error_str = darwin_error_str (rw_arg.result);
+ } else {
+ error_code = darwin_to_errno(result);
+ error_str = darwin_error_str (result);
+ }
+
+ if (transferType != kUSBInterrupt && rw_async_to != NULL)
+ USB_ERROR_STR(-error_code, "usb_bulk_transfer (w/ Timeout): %s", error_str);
+ else
+ USB_ERROR_STR(-error_code, "usb_bulk_transfer (No Timeout): %s", error_str);
+ }
+
+ return rw_arg.io_size;
+}
+
+int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout)
+{
+ int result;
+ rw_async_to_func_t to_func = NULL;
+ struct darwin_dev_handle *device;
+
+ if (dev == NULL || dev->impl_info == NULL)
+ return -EINVAL;
+
+ device = dev->impl_info;
+
+#if !defined (LIBUSB_NO_TIMEOUT_INTERFACE)
+ to_func = (*(device->interface))->WritePipeAsyncTO;
+#endif
+
+ if ((result = usb_bulk_transfer (dev, ep, bytes, size, timeout,
+ (*(device->interface))->WritePipeAsync, to_func)) < 0)
+ USB_ERROR_STR (result, "usb_bulk_write: An error occured during write (see messages above)");
+
+ return result;
+}
+
+int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout)
+{
+ int result;
+ rw_async_to_func_t to_func = NULL;
+ struct darwin_dev_handle *device;
+
+ if (dev == NULL || dev->impl_info == NULL)
+ return -EINVAL;
+
+ ep |= 0x80;
+
+ device = dev->impl_info;
+
+#if !defined (LIBUSB_NO_TIMEOUT_INTERFACE)
+ to_func = (*(device->interface))->ReadPipeAsyncTO;
+#endif
+
+ if ((result = usb_bulk_transfer (dev, ep, bytes, size, timeout,
+ (*(device->interface))->ReadPipeAsync, to_func)) < 0)
+ USB_ERROR_STR (result, "usb_bulk_read: An error occured during read (see messages above)");
+
+ return result;
+}
+
+/* interrupt endpoints seem to be treated just like any other endpoint under OSX/Darwin */
+int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size,
+ int timeout)
+{
+ return usb_bulk_write (dev, ep, bytes, size, timeout);
+}
+
+int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+ int timeout)
+{
+ return usb_bulk_read (dev, ep, bytes, size, timeout);
+}
+
+int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
+ int value, int index, char *bytes, int size, int timeout)
+{
+ struct darwin_dev_handle *device = dev->impl_info;
+
+ io_return_t result;
+
+#if !defined (LIBUSB_NO_TIMEOUT_DEVICE)
+ IOUSBDevRequestTO urequest;
+#else
+ IOUSBDevRequest urequest;
+#endif
+
+ if (usb_debug >= 3)
+ fprintf(stderr, "usb_control_msg: %d %d %d %d %p %d %d\n",
+ requesttype, request, value, index, bytes, size, timeout);
+
+ bzero(&urequest, sizeof(urequest));
+
+ urequest.bmRequestType = requesttype;
+ urequest.bRequest = request;
+ urequest.wValue = value;
+ urequest.wIndex = index;
+ urequest.wLength = size;
+ urequest.pData = bytes;
+#if !defined (LIBUSB_NO_TIMEOUT_DEVICE)
+ urequest.completionTimeout = timeout;
+ urequest.noDataTimeout = timeout;
+
+ result = (*(device->device))->DeviceRequestTO(device->device, &urequest);
+#else
+ result = (*(device->device))->DeviceRequest(device->device, &urequest);
+#endif
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_control_msg(DeviceRequestTO): %s", darwin_error_str(result));
+
+ /* Bytes transfered is stored in the wLenDone field*/
+ return urequest.wLenDone;
+}
+
+int usb_os_find_busses(struct usb_bus **busses)
+{
+ struct usb_bus *fbus = NULL;
+
+ io_iterator_t deviceIterator;
+ io_return_t result;
+
+ usb_device_t **device;
+
+ UInt32 location;
+
+ char buf[20];
+ int i = 1;
+
+ /* Create a master port for communication with IOKit (this should
+ have been created if the user called usb_init() )*/
+ if (masterPort == MACH_PORT_NULL) {
+ usb_init ();
+
+ if (masterPort == MACH_PORT_NULL)
+ USB_ERROR(-ENOENT);
+ }
+
+ if ((result = usb_setup_iterator (&deviceIterator)) < 0)
+ return result;
+
+ while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) {
+ struct usb_bus *bus;
+
+ if (location & 0x00ffffff)
+ continue;
+
+ bus = calloc(1, sizeof(struct usb_bus));
+ if (bus == NULL)
+ USB_ERROR(-ENOMEM);
+
+ sprintf(buf, "%03i", i++);
+ bus->location = location;
+
+ strncpy(bus->dirname, buf, sizeof(bus->dirname) - 1);
+ bus->dirname[sizeof(bus->dirname) - 1] = 0;
+
+ LIST_ADD(fbus, bus);
+
+ if (usb_debug >= 2)
+ fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname);
+
+ (*(device))->Release(device);
+ }
+
+ IOObjectRelease(deviceIterator);
+
+ *busses = fbus;
+
+ return 0;
+}
+
+int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices)
+{
+ struct usb_device *fdev = NULL;
+
+ io_iterator_t deviceIterator;
+ io_return_t result;
+
+ usb_device_t **device;
+
+ u_int16_t address;
+ UInt32 location;
+ UInt32 bus_loc = bus->location;
+
+ /* for use in retrieving device description */
+ IOUSBDevRequest req;
+
+ /* a master port should have been created by usb_os_init */
+ if (masterPort == MACH_PORT_NULL)
+ USB_ERROR(-ENOENT);
+
+ if ((result = usb_setup_iterator (&deviceIterator)) < 0)
+ return result;
+
+ /* Set up request for device descriptor */
+ req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = kUSBDeviceDesc << 8;
+ req.wIndex = 0;
+ req.wLength = sizeof(IOUSBDeviceDescriptor);
+
+
+ while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) {
+ unsigned char device_desc[DEVICE_DESC_LENGTH];
+
+ result = (*(device))->GetDeviceAddress(device, (USBDeviceAddress *)&address);
+
+ if (usb_debug >= 2)
+ fprintf(stderr, "usb_os_find_devices: Found USB device at location 0x%08lx\n", location);
+
+ /* first byte of location appears to be associated with the device's bus */
+ if (location >> 24 == bus_loc >> 24) {
+ struct usb_device *dev;
+
+ dev = calloc(1, sizeof(struct usb_device));
+ if (dev == NULL)
+ USB_ERROR(-ENOMEM);
+
+ dev->bus = bus;
+
+ req.pData = device_desc;
+ result = (*(device))->DeviceRequest(device, &req);
+
+ usb_parse_descriptor(device_desc, "bbwbbbbwwwbbbb", &dev->descriptor);
+
+ sprintf(dev->filename, "%03i-%04x-%04x-%02x-%02x", address,
+ dev->descriptor.idVendor, dev->descriptor.idProduct,
+ dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass);
+
+ dev->dev = (USBDeviceAddress *)malloc(4);
+ memcpy(dev->dev, &location, 4);
+
+ LIST_ADD(fdev, dev);
+
+ if (usb_debug >= 2)
+ fprintf(stderr, "usb_os_find_devices: Found %s on %s at location 0x%08lx\n",
+ dev->filename, bus->dirname, location);
+ }
+
+ /* release the device now */
+ (*(device))->Release(device);
+ }
+
+ IOObjectRelease(deviceIterator);
+
+ *devices = fdev;
+
+ return 0;
+}
+
+int usb_os_determine_children(struct usb_bus *bus)
+{
+ /* Nothing yet */
+ return 0;
+}
+
+void usb_os_init(void)
+{
+ if (masterPort == MACH_PORT_NULL) {
+ IOMasterPort(masterPort, &masterPort);
+
+ gNotifyPort = IONotificationPortCreate(masterPort);
+ }
+}
+
+void usb_os_cleanup (void)
+{
+ if (masterPort != MACH_PORT_NULL)
+ darwin_cleanup ();
+}
+
+int usb_resetep(usb_dev_handle *dev, unsigned int ep)
+{
+ struct darwin_dev_handle *device;
+
+ io_return_t result = -1;
+
+ int pipeRef;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR(-ENOENT);
+
+ /* interface is not open */
+ if (!device->interface)
+ USB_ERROR_STR(-EACCES, "usb_resetep: interface used without being claimed");
+
+ if ((pipeRef = ep_to_pipeRef(device, ep)) == -1)
+ USB_ERROR(-EINVAL);
+
+ result = (*(device->interface))->ResetPipe(device->interface, pipeRef);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_resetep(ResetPipe): %s", darwin_error_str(result));
+
+ return 0;
+}
+
+int usb_clear_halt(usb_dev_handle *dev, unsigned int ep)
+{
+ struct darwin_dev_handle *device;
+
+ io_return_t result = -1;
+
+ int pipeRef;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR(-ENOENT);
+
+ /* interface is not open */
+ if (!device->interface)
+ USB_ERROR_STR(-EACCES, "usb_clear_halt: interface used without being claimed");
+
+ if ((pipeRef = ep_to_pipeRef(device, ep)) == -1)
+ USB_ERROR(-EINVAL);
+
+ result = (*(device->interface))->ClearPipeStall(device->interface, pipeRef);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_clear_halt(ClearPipeStall): %s", darwin_error_str(result));
+
+ return 0;
+}
+
+int usb_reset(usb_dev_handle *dev)
+{
+ struct darwin_dev_handle *device;
+
+ io_return_t result;
+
+ if (!dev)
+ USB_ERROR(-ENXIO);
+
+ if ((device = dev->impl_info) == NULL)
+ USB_ERROR(-ENOENT);
+
+ if (!device->device)
+ USB_ERROR_STR(-ENOENT, "usb_reset: no such device");
+
+ result = (*(device->device))->ResetDevice(device->device);
+
+ if (result != kIOReturnSuccess)
+ USB_ERROR_STR(-darwin_to_errno(result), "usb_reset(ResetDevice): %s", darwin_error_str(result));
+
+ return 0;
+}