public MacUsbConnection(IOObject usbService, string name, string serialNumber)
        {
            if (usbService.Handle == IntPtr.Zero)
            {
                throw new NullReferenceException("USB Service is null");
            }

            int    plugInSize            = Marshal.SizeOf(typeof(IOCFPlugInInterface));
            IntPtr pluginInterfacePtrPtr = IntPtr.Zero;
            var    device = IntPtr.Zero;

            int score = 0;

            var kr = NativeMethods.IOCreatePlugInInterfaceForService(usbService.Handle, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, out pluginInterfacePtrPtr, out score);

            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn't create plug-in interface. Error: " + kr);
                return;
            }

            IOCFPlugInInterface pluginInterface = IOCFPlugInInterface.GetPlugInInterfaceFromPtrPtr(pluginInterfacePtrPtr);

            IntPtr deviceInterfacePtrPtr = IntPtr.Zero;

            kr = pluginInterface.QueryInterface(pluginInterfacePtrPtr, kIOUSBDeviceInterfaceID320, out deviceInterfacePtrPtr);
            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn't query interface. Error: " + kr);
                return;
            }

            IntPtr deviceInterfacePtr = new IntPtr(Marshal.ReadInt64(deviceInterfacePtrPtr));

            if (deviceInterfacePtr != IntPtr.Zero)
            {
                deviceInterface        = (IOUSBDeviceInterface320)Marshal.PtrToStructure(deviceInterfacePtr, typeof(IOUSBDeviceInterface320));
                deviceInterface.Handle = deviceInterfacePtrPtr;
            }

            Name   = name;
            Serial = serialNumber;
        }
        public async Task <bool> OpenAsync()
        {
            deviceInterface.GetDeviceAsyncEventSource(deviceInterface.Handle);
            var kr = deviceInterface.USBDeviceOpen(deviceInterface.Handle);

            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn’t open device: " + kr);
                return(false);
            }

            byte numConfigs = 0;

            deviceInterface.GetNumberOfConfigurations(deviceInterface.Handle, out numConfigs);
            if (numConfigs == 0)
            {
                return(false);
            }

            IOUSBConfigurationDescriptor configDesc = new IOUSBConfigurationDescriptor();

            kr = deviceInterface.GetConfigurationDescriptorPtr(deviceInterface.Handle, 0, out configDesc);
            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn’t get configuration descriptor for index: " + kr);
                return(false);
            }

            kr = deviceInterface.SetConfiguration(deviceInterface.Handle, configDesc.bConfigurationValue);
            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn’t set configuration to value: " + kr);
                return(false);
            }

            // HACK: the LED blinks three times since we reconfigured it. Ugh, we should wait here so we don't miss commands
            await Task.Delay(500).ConfigureAwait(false);

            IOUSBFindInterfaceRequest interfaceRequest = new IOUSBFindInterfaceRequest();
            IntPtr interfaceIteratorPtr = IntPtr.Zero;

            kr = deviceInterface.CreateInterfaceIterator(deviceInterface.Handle, interfaceRequest, out interfaceIteratorPtr);

            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Failed to create interface iterator: " + kr);
                return(false);
            }

            IOIterator interfaceIterator = new IOIterator(interfaceIteratorPtr);

            var usbInterface = interfaceIterator.Next();

            if (usbInterface == null)
            {
                Debug.WriteLine("Failed to find an interface.");
                return(false);
            }

            IntPtr pluginInterfacePtrPtr = IntPtr.Zero;
            int    score = 0;

            if (NativeMethods.IOCreatePlugInInterfaceForService(usbInterface.Handle, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, out pluginInterfacePtrPtr, out score) != IOKitError.Success)
            {
                Debug.WriteLine("Failed to create CF Plug-In interface: " + kr);
                return(false);
            }

            IOCFPlugInInterface pluginInterface = IOCFPlugInInterface.GetPlugInInterfaceFromPtrPtr(pluginInterfacePtrPtr);

            usbInterface.Dispose();
            interfaceIterator.Dispose();


            IntPtr interfaceInterfacePtrPtr = IntPtr.Zero;

            if (pluginInterface.QueryInterface(pluginInterfacePtrPtr, kIOUSBInterfaceInterfaceID, out interfaceInterfacePtrPtr) != IOKitError.Success)
            {
                Debug.WriteLine("Could not query plugin interface to retrieve interface interface: " + kr);
                return(false);
            }

            IntPtr interfaceInterfacePtr = new IntPtr(Marshal.ReadInt64(interfaceInterfacePtrPtr));

            if (interfaceInterfacePtr == IntPtr.Zero)
            {
                Debug.WriteLine("Bad InterfaceInterface pointer");
                return(false);
            }

            interfaceInterface        = (IOUSBInterfaceInterface197)Marshal.PtrToStructure(interfaceInterfacePtr, typeof(IOUSBInterfaceInterface197));
            interfaceInterface.Handle = interfaceInterfacePtrPtr;

            byte intNumber = 0;

            interfaceInterface.GetNumEndpoints(interfaceInterface.Handle, out intNumber);
            interfaceInterface.GetInterfaceClass(interfaceInterface.Handle, out intNumber);

            kr = interfaceInterface.GetInterfaceNumber(interfaceInterface.Handle, out intNumber);
            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn't get interface number: " + kr);
                return(false);
            }

            kr = interfaceInterface.USBInterfaceOpen(interfaceInterface.Handle);

            if (kr != IOKitError.Success)
            {
                Debug.WriteLine("Couldn't open interface: " + kr);
                return(false);
            }

            isConnected = true;

            pinReportThread = new Thread(() =>
            {
                byte[] pinReport    = new byte[41];
                uint numBytesToRead = 41;
                while (isConnected)
                {
                    var status = interfaceInterface.ReadPipeTO(interfaceInterface.Handle, 1, pinReport, out numBytesToRead, 1000, 1000);
                    switch (status)
                    {
                    case IOKitError.Success:
                        PinEventDataReceived?.Invoke(pinReport);
                        break;

                    case IOKitError.NoDevice:
                    case IOKitError.Aborted:
                        return;     // board was unplugged, so kill this thread

                        break;

                    case IOKitError.TransactionTimedOut:
                        // we probably don't have any inputs activated. No need to report to the user.
                        status = interfaceInterface.ClearPipeStallBothEnds(interfaceInterface.Handle, 1);
                        break;

                    default:
                        Debug.WriteLine("Read from pin report failed: " + status);
                        status = interfaceInterface.ClearPipeStallBothEnds(interfaceInterface.Handle, 1);
                        if (status != IOKitError.Success)
                        {
                            Debug.WriteLine("Can't clear pipe stall: " + status);
                        }
                        break;
                    }
                    Thread.Sleep(UpdateRate);
                }
            });

            pinReportThread.Name = "PinReportThread";
            pinReportThread.Start();

            return(true);
        }