/// <summary> /// Perform a USB control transfer for multi-threaded applications using the <see cref="LibUsbEventHandler"/> class. /// </summary> /// <remarks> /// <para>The direction of the transfer is inferred from the bmRequestType field of the setup packet.</para> /// <para>The wValue, wIndex and wLength fields values should be given in host-endian byte order.</para> /// <note type="tip" title="Libusb-1.0 API:"><seelibusb10 group="syncio"/></note> /// </remarks> /// <param name="deviceHandle">A handle for the device to communicate with.</param> /// <param name="requestType">The request type field for the setup packet.</param> /// <param name="request">The request field for the setup packet.</param> /// <param name="value">The value field for the setup packet</param> /// <param name="index">The index field for the setup packet.</param> /// <param name="pData">A suitably-sized data buffer for either input or output (depending on direction bits within bmRequestType).</param> /// <param name="dataLength">The length field for the setup packet. The data buffer should be at least this size.</param> /// <param name="timeout">timeout (in milliseconds) that this function should wait before giving up due to no response being received. For an unlimited timeout, use value 0.</param> /// <returns> /// <list type="bullet"> /// <item>on success, the number of bytes actually transferred</item> /// <item><see cref="LibUsbError.ErrorTimeout"/> if the transfer timed out</item> /// <item><see cref="LibUsbError.ErrorPipe"/> if the control request was not supported by the device.</item> /// <item><see cref="LibUsbError.ErrorNoDevice"/> if the device has been disconnected</item> /// <item>another <see cref="LibUsbError"/> code on other failures</item> /// </list> /// </returns> public static int ControlTransferAsync([In] LibUsbDeviceHandle deviceHandle, byte requestType, byte request, short value, short index, IntPtr pData, short dataLength, int timeout) { LibUsbControlSetupHandle setupHandle = new LibUsbControlSetupHandle(requestType, request, value, index, pData, dataLength); LibUsbTransfer transfer = new LibUsbTransfer(0); ManualResetEvent completeEvent = new ManualResetEvent(false); GCHandle gcCompleteEvent = GCHandle.Alloc(completeEvent); transfer.FillControl(deviceHandle, setupHandle, DefaultAsyncDelegate, GCHandle.ToIntPtr(gcCompleteEvent), timeout); int r = (int)transfer.Submit(); if (r < 0) { transfer.Free(); gcCompleteEvent.Free(); return r; } IntPtr pSessionHandle; LibUsbSessionHandle sessionHandle = LibUsbEventHandler.SessionHandle; if (sessionHandle == null) pSessionHandle = IntPtr.Zero; else pSessionHandle = sessionHandle.DangerousGetHandle(); if (LibUsbEventHandler.IsStopped) { while (!completeEvent.WaitOne(0, false)) { r = HandleEvents(pSessionHandle); if (r < 0) { if (r == (int)LibUsbError.ErrorInterrupted) continue; transfer.Cancel(); while (!completeEvent.WaitOne(0, false)) if (HandleEvents(pSessionHandle) < 0) break; transfer.Free(); gcCompleteEvent.Free(); return r; } } } else { completeEvent.WaitOne(Timeout.Infinite, UsbConstants.EXIT_CONTEXT); } if (transfer.Status == LibUsbTansferStatus.TransferCompleted) { r = transfer.ActualLength; if (r > 0) { byte[] ctrlDataBytes = setupHandle.ControlSetup.GetData(r); Marshal.Copy(ctrlDataBytes, 0, pData, Math.Min(ctrlDataBytes.Length, dataLength)); } } else r = (int)MonoLibUsbErrorFromTransferStatus(transfer.Status); transfer.Free(); gcCompleteEvent.Free(); return r; }
/// <summary> /// Helper function to populate the required <see cref="LibUsbTransfer"/> properties for a control transfer. /// </summary> /// <remarks> /// <note type="tip"> /// <para>Isochronous transfers are not supported on windows.</para> /// </note> /// <note title="Libusb-1.0 API Note:" type="cpp"> /// <see cref="FillControl"/> is similar to /// <a href="http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga3a8513ed87229fe2c9771ef0bf17206e">libusb_fill_control_transfer()</a>. /// </note> /// </remarks> /// <param name="devHandle">handle of the device that will handle the transfer</param> /// <param name="controlSetupHandle">the setup packet/control data to transfer.</param> /// <param name="callback">callback function to be invoked on transfer completion</param> /// <param name="userData">user data to pass to callback function</param> /// <param name="timeout">timeout for the transfer in milliseconds</param> public void FillControl(LibUsbDeviceHandle devHandle, LibUsbControlSetupHandle controlSetupHandle, Delegate callback, IntPtr userData, int timeout) { PtrDeviceHandle = devHandle.DangerousGetHandle(); Endpoint = 0; PtrCallbackFn = Marshal.GetFunctionPointerForDelegate(callback); PtrUserData = userData; Timeout = timeout; Type = EndpointType.Control; Flags = LibUsbTransferFlags.None; IntPtr pSetupPacket = controlSetupHandle.DangerousGetHandle(); PtrBuffer = pSetupPacket; LibUsbControlSetup w = new LibUsbControlSetup(pSetupPacket); Length = LibUsbControlSetup.SETUP_PACKET_SIZE + w.Length; }