Darwin: Fix incorrect mutex protection of libusb_darwin_acfl global
[platform/upstream/libusb.git] / libusb / os / darwin_usb.c
index dfad470..f092562 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <libkern/OSAtomic.h>
 
 #include <mach/clock.h>
 #include <mach/clock_types.h>
 
 #include "darwin_usb.h"
 
-static mach_port_t  libusb_darwin_mp = 0; /* master port */
+/* async event thread */
+static pthread_mutex_t libusb_darwin_at_mutex;
+static pthread_cond_t  libusb_darwin_at_cond;
+
+static clock_serv_t clock_realtime;
+static clock_serv_t clock_monotonic;
+
 static CFRunLoopRef libusb_darwin_acfl = NULL; /* async cf loop */
-static int initCount = 0;
+static volatile int32_t initCount = 0;
 
 /* async event thread */
 static pthread_t libusb_darwin_at;
@@ -123,7 +130,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
 
   /* current interface */
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
 
   int8_t i, iface;
 
@@ -175,7 +182,7 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long locati
     /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */
   }
 
-  return IOServiceGetMatchingServices(libusb_darwin_mp, matchingDict, deviceIterator);
+  return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
 }
 
 static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) {
@@ -193,6 +200,9 @@ static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32
     result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID,
                                               kIOCFPlugInInterfaceID, &plugInInterface,
                                               &score);
+
+    /* we are done with the usb_device_t */
+    (void)IOObjectRelease(usbDevice);
     if (kIOReturnSuccess == result && plugInInterface)
       break;
 
@@ -202,7 +212,6 @@ static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32
   if (!usbDevice)
     return NULL;
 
-  (void)IOObjectRelease(usbDevice);
   (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
                                           (LPVOID)&device);
 
@@ -303,6 +312,12 @@ static void *event_thread_main (void *arg0) {
   struct libusb_context *ctx = (struct libusb_context *)arg0;
   CFRunLoopRef runloop;
 
+  /* Set this thread's name, so it can be seen in the debugger
+     and crash reports. */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+  pthread_setname_np ("org.libusb.device-detach");
+#endif
+
   /* Tell the Objective-C garbage collector about this thread.
      This is required because, unlike NSThreads, pthreads are
      not automatically registered. Although we don't use
@@ -322,9 +337,9 @@ static void *event_thread_main (void *arg0) {
   CFRetain (runloop);
 
   /* add the notification port to the run loop */
-  libusb_notification_port     = IONotificationPortCreate (libusb_darwin_mp);
+  libusb_notification_port     = IONotificationPortCreate (kIOMasterPortDefault);
   libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
-  CFRunLoopAddSource(CFRunLoopGetCurrent (), libusb_notification_cfsource, kCFRunLoopDefaultMode);
+  CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
 
   /* create notifications for removed devices */
   kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
@@ -341,19 +356,22 @@ static void *event_thread_main (void *arg0) {
   /* arm notifiers */
   darwin_clear_iterator (libusb_rem_device_iterator);
 
-  /* let the main thread know about the async runloop */
-  libusb_darwin_acfl = CFRunLoopGetCurrent ();
-
   usbi_info (ctx, "thread ready to receive events");
 
+  /* signal the main thread */
+  pthread_mutex_lock (&libusb_darwin_at_mutex);
+  libusb_darwin_acfl = runloop;
+  pthread_cond_signal (&libusb_darwin_at_cond);
+  pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
   /* run the runloop */
   CFRunLoopRun();
 
   usbi_info (ctx, "thread exiting");
 
   /* delete notification port */
-  CFRunLoopSourceInvalidate (libusb_notification_cfsource);
   IONotificationPortDestroy (libusb_notification_port);
+  IOObjectRelease (libusb_rem_device_iterator);
 
   CFRelease (runloop);
 
@@ -363,37 +381,38 @@ static void *event_thread_main (void *arg0) {
 }
 
 static int darwin_init(struct libusb_context *ctx) {
-  IOReturn kresult;
+  host_name_port_t host_self;
 
-  if (!(initCount++)) {
-    /* Create the master port for talking to IOKit */
-    if (!libusb_darwin_mp) {
-      kresult = IOMasterPort (MACH_PORT_NULL, &libusb_darwin_mp);
+  if (OSAtomicIncrement32Barrier(&initCount) == 1) {
+    /* create the clocks that will be used */
 
-      if (kresult != kIOReturnSuccess || !libusb_darwin_mp)
-       return darwin_to_libusb (kresult);
-    }
+    host_self = mach_host_self();
+    host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
+    host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
+    mach_port_deallocate(mach_task_self(), host_self);
+
+    pthread_mutex_init (&libusb_darwin_at_mutex, NULL);
+    pthread_cond_init (&libusb_darwin_at_cond, NULL);
 
     pthread_create (&libusb_darwin_at, NULL, event_thread_main, (void *)ctx);
 
+    pthread_mutex_lock (&libusb_darwin_at_mutex);
     while (!libusb_darwin_acfl)
-      usleep (10);
+      pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+    pthread_mutex_unlock (&libusb_darwin_at_mutex);
   }
 
   return 0;
 }
 
 static void darwin_exit (void) {
-  if (!(--initCount)) {
+  if (OSAtomicDecrement32Barrier(&initCount) == 0) {
+    mach_port_deallocate(mach_task_self(), clock_realtime);
+    mach_port_deallocate(mach_task_self(), clock_monotonic);
 
     /* stop the async runloop */
     CFRunLoopStop (libusb_darwin_acfl);
     pthread_join (libusb_darwin_at, NULL);
-
-    if (libusb_darwin_mp)
-      mach_port_deallocate(mach_task_self(), libusb_darwin_mp);
-
-    libusb_darwin_mp = 0;
   }
 }
 
@@ -678,6 +697,7 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device
   struct libusb_device *dev;
   struct discovered_devs *discdevs;
   UInt16                address;
+  UInt8                 devSpeed;
   int ret = 0, need_unref = 0;
 
   do {
@@ -710,6 +730,16 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device
     dev->bus_number     = locationID >> 24;
     dev->device_address = address;
 
+    (*device)->GetDeviceSpeed (device, &devSpeed);
+
+    switch (devSpeed) {
+    case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
+    case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
+    case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break;
+    default:
+      usbi_warn (ctx, "Got unknown device speed %d", devSpeed);
+    }
+
     /* save our location, we'll need this later */
     priv->location = locationID;
     snprintf(priv->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", address, priv->dev_descriptor.idVendor, priv->dev_descriptor.idProduct,
@@ -743,9 +773,6 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_
   kern_return_t        kresult;
   UInt32               location;
 
-  if (!libusb_darwin_mp)
-    return LIBUSB_ERROR_INVALID_PARAM;
-
   kresult = usb_setup_device_iterator (&deviceIterator, 0);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
@@ -952,7 +979,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   kern_return_t kresult;
 
@@ -1000,7 +1027,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
   SInt32                score;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
   if (kresult != kIOReturnSuccess)
@@ -1032,6 +1059,10 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
   /* get an interface to the device's interface */
   kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID,
                                               kIOCFPlugInInterfaceID, &plugInInterface, &score);
+
+  /* ignore release error */
+  (void)IOObjectRelease (usbInterface);
+
   if (kresult) {
     usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
@@ -1042,21 +1073,17 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
-  /* ignore release error */
-  (void)IOObjectRelease (usbInterface);
-
   /* Do the actual claim */
   kresult = (*plugInInterface)->QueryInterface(plugInInterface,
                                               CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
                                               (LPVOID)&cInterface->interface);
+  /* We no longer need the intermediate plug-in */
+  IODestroyPlugInInterface (plugInInterface);
   if (kresult || !cInterface->interface) {
     usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
-  /* We no longer need the intermediate plug-in */
-  (*plugInInterface)->Release(plugInInterface);
-
   /* claim the interface */
   kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
   if (kresult) {
@@ -1099,7 +1126,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int
   IOReturn kresult;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   /* Check to see if an interface is open */
   if (!cInterface->interface)
@@ -1132,7 +1159,7 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand
   IOReturn kresult;
 
   /* current interface */
-  struct __darwin_interface *cInterface = &priv->interfaces[iface];
+  struct darwin_interface *cInterface = &priv->interfaces[iface];
 
   if (!cInterface->interface)
     return LIBUSB_ERROR_NO_DEVICE;
@@ -1157,7 +1184,7 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
 
   /* current interface */
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
   uint8_t pipeRef, iface;
   IOReturn kresult;
 
@@ -1221,18 +1248,23 @@ static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle,
 
 /* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */
 static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+  (void)dev_handle;
+  (void)interface;
   return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
 static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+  (void)dev_handle;
+  (void)interface;
   return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
 static void darwin_destroy_device(struct libusb_device *dev) {
+  (void)dev;
 }
 
 static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
 
   IOReturn               ret;
@@ -1242,7 +1274,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
   uint8_t                direction, number, interval, pipeRef, iface;
   uint16_t               maxPacketSize;
 
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
 
   /* are we reading or writing? */
   is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN;
@@ -1288,7 +1320,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
 }
 
 static int submit_iso_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
 
@@ -1299,7 +1331,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   AbsoluteTime            atTime;
   int                     i;
 
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
 
   /* are we reading or writing? */
   is_read = transfer->endpoint & LIBUSB_ENDPOINT_IN;
@@ -1369,7 +1401,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
 }
 
 static int submit_control_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer;
   struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
@@ -1396,7 +1428,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) {
   /* all transfers in libusb-1.0 are async */
 
   if (transfer->endpoint) {
-    struct __darwin_interface *cInterface;
+    struct darwin_interface *cInterface;
     uint8_t                 pipeRef, iface;
 
     if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) {
@@ -1419,7 +1451,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) {
 }
 
 static int darwin_submit_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
   switch (transfer->type) {
   case LIBUSB_TRANSFER_TYPE_CONTROL:
@@ -1436,21 +1468,25 @@ static int darwin_submit_transfer(struct usbi_transfer *itransfer) {
 }
 
 static int cancel_control_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
   IOReturn kresult;
 
   usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions control pipe");
 
+  if (!dpriv->device)
+    return LIBUSB_ERROR_NO_DEVICE;
+
   kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device);
 
   return darwin_to_libusb (kresult);
 }
 
 static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv;
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
-  struct __darwin_interface *cInterface;
+  struct darwin_interface *cInterface;
   uint8_t pipeRef, iface;
   IOReturn kresult;
 
@@ -1462,6 +1498,9 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
 
   cInterface = &priv->interfaces[iface];
 
+  if (!dpriv->device)
+    return LIBUSB_ERROR_NO_DEVICE;
+
   usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions on interface %d pipe %d", iface, pipeRef);
 
   /* abort transactions */
@@ -1481,7 +1520,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
 }
 
 static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
   switch (transfer->type) {
   case LIBUSB_TRANSFER_TYPE_CONTROL:
@@ -1497,7 +1536,7 @@ static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
 }
 
 static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
 
   if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) {
@@ -1508,18 +1547,20 @@ static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
 
 static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) {
   struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon;
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
-  UInt32 message;
+  UInt32 message, size;
 
   usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed");
 
+  size = (UInt32) arg0;
+
   /* send a completion message to the device's file descriptor */
   message = MESSAGE_ASYNC_IO_COMPLETE;
   write (priv->fds[1], &message, sizeof (message));
   write (priv->fds[1], &itransfer, sizeof (itransfer));
   write (priv->fds[1], &result, sizeof (IOReturn));
-  write (priv->fds[1], &arg0, sizeof (UInt32));
+  write (priv->fds[1], &size, sizeof (size));
 }
 
 static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
@@ -1549,7 +1590,7 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_
 }
 
 static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) {
-  struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
   int isIsoc      = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
   int isBulk      = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
@@ -1586,7 +1627,8 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL
   struct usbi_transfer *itransfer;
   UInt32 io_size;
   IOReturn kresult;
-  int i = 0, ret;
+  POLL_NFDS_TYPE i = 0;
+  ssize_t ret;
   UInt32 message;
 
   usbi_mutex_lock(&ctx->open_devs_lock);
@@ -1609,7 +1651,7 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL
 
     if (!(pollfd->revents & POLLERR)) {
       ret = read (hpriv->fds[0], &message, sizeof (message));
-      if (ret < sizeof (message))
+      if (ret < (ssize_t)sizeof (message))
        continue;
     } else
       /* could not poll the device-- response is to delete the device (this seems a little heavy-handed) */
@@ -1654,11 +1696,11 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
   switch (clk_id) {
   case USBI_CLOCK_REALTIME:
     /* CLOCK_REALTIME represents time since the epoch */
-    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clock_ref);
+    clock_ref = clock_realtime;
     break;
   case USBI_CLOCK_MONOTONIC:
     /* use system boot time as reference for the monotonic clock */
-    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_ref);
+    clock_ref = clock_monotonic;
     break;
   default:
     return LIBUSB_ERROR_INVALID_PARAM;