/* WinUSBNet library * (C) 2010 Thomas Bleeker (www.madwizard.org) * * Licensed under the MIT license, see license.txt or: * http://www.opensource.org/licenses/mit-license.php */ using System; using System.Collections.Generic; namespace MadWizard.WinUSBNet { /// /// The UsbDevice class represents a single WinUSB device. /// public class USBDevice : IDisposable { private API.WinUSBDevice _wuDevice = null; private bool _disposed = false; /// /// Collection of all pipes available on the USB device /// public USBPipeCollection Pipes { get; private set; } /// /// The input pipe. Use this pipe to read from the device. /// public USBPipe InputPipe { get; private set; } /// /// The output pipe. Use this pipe to write to the device. /// public USBPipe OutputPipe { get; private set; } /// /// Collection of all interfaces available on the USB device /// public USBInterfaceCollection Interfaces { get; private set; } /// /// Device descriptor with information about the device /// public USBDeviceDescriptor Descriptor { get; private set; } /// /// Constructs a new USB device /// /// USB device info of the device to create public USBDevice(USBDeviceInfo deviceInfo) : this(deviceInfo.DevicePath) { // Handled in other constructor } /// /// Disposes the UsbDevice including all unmanaged WinUSB handles. This function /// should be called when the UsbDevice object is no longer in use, otherwise /// unmanaged handles will remain open until the garbage collector finalizes the /// object. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Finalizer for the UsbDevice. Disposes all unmanaged handles. /// ~USBDevice() { Dispose(false); } /// /// Disposes the object /// /// Indicates wether Dispose was called manually (true) or by /// the garbage collector (false) via the destructor. protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { if (_wuDevice != null) _wuDevice.Dispose(); } // Clean unmanaged resources here. // (none currently) _disposed = true; } /// /// Constructs a new USB device /// /// Device path name of the USB device to create public USBDevice(string devicePathName) { try { Descriptor = GetDeviceDescriptor(devicePathName); } catch // (Exception Ex) { //WPinternals.LogFile.LogException(Ex); } _wuDevice = new API.WinUSBDevice(); try { _wuDevice.OpenDevice(devicePathName); InitializeInterfaces(); } catch (API.APIException e) { _wuDevice.Dispose(); throw new USBException("Failed to open device.", e); } } internal API.WinUSBDevice InternalDevice { get { return _wuDevice; } } private void InitializeInterfaces() { int numInterfaces = _wuDevice.InterfaceCount; List allPipes = new List(); InputPipe = null; OutputPipe = null; USBInterface[] interfaces = new USBInterface[numInterfaces]; // UsbEndpoint for (int i = 0; i < numInterfaces; i++) { API.USB_INTERFACE_DESCRIPTOR descriptor; API.WINUSB_PIPE_INFORMATION[] pipesInfo; _wuDevice.GetInterfaceInfo(i, out descriptor, out pipesInfo); USBPipe[] interfacePipes = new USBPipe[pipesInfo.Length]; for (int k = 0; k < pipesInfo.Length; k++) { USBPipe pipe = new USBPipe(this, pipesInfo[k]); interfacePipes[k] = pipe; allPipes.Add(pipe); if (Convert.ToBoolean((pipesInfo[k].PipeId & 0x80)) && (InputPipe == null)) InputPipe = pipe; if (!Convert.ToBoolean((pipesInfo[k].PipeId & 0x80)) && (OutputPipe == null)) OutputPipe = pipe; } // TODO: //if (descriptor.iInterface != 0) // _wuDevice.GetStringDescriptor(descriptor.iInterface); USBPipeCollection pipeCollection = new USBPipeCollection(interfacePipes); interfaces[i] = new USBInterface(this, i, descriptor, pipeCollection); } Pipes = new USBPipeCollection(allPipes.ToArray()); Interfaces = new USBInterfaceCollection(interfaces); } private void CheckControlParams(int value, int index, byte[] buffer, int length) { if (value < ushort.MinValue || value > ushort.MaxValue) throw new ArgumentOutOfRangeException("Value parameter out of range."); if (index < ushort.MinValue || index > ushort.MaxValue) throw new ArgumentOutOfRangeException("Index parameter out of range."); if (length > buffer.Length) throw new ArgumentOutOfRangeException("Length parameter is larger than the size of the buffer."); if (length > ushort.MaxValue) throw new ArgumentOutOfRangeException("Length too large"); } /// /// Specifies the timeout in milliseconds for control pipe operations. If a control transfer does not finish within the specified time it will fail. /// When set to zero, no timeout is used. Default value is 5000 milliseconds. /// /// WinUSB_GetPipePolicy for a more detailed description public int ControlPipeTimeout { get { byte PipeID = 0; if (InputPipe != null) PipeID = InputPipe.Address; return (int)_wuDevice.GetPipePolicyUInt(0, 0x00, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT); } set { if (value < 0) throw new ArgumentOutOfRangeException("Control pipe timeout cannot be negative."); //_wuDevice.SetPipePolicy(0, 0x00, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); if (InputPipe != null) _wuDevice.SetPipePolicy(0, InputPipe.Address, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); if (OutputPipe != null) _wuDevice.SetPipePolicy(0, OutputPipe.Address, API.POLICY_TYPE.PIPE_TRANSFER_TIMEOUT, (uint)value); } } /// /// Initiates a control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending /// on the highest bit of the parameter. Alternatively, and /// can be used for control transfers in a specific direction, which is the recommended way because /// it prevents using the wrong direction accidentally. Use the ControlTransfer method when the direction is not known at compile time. /// /// The setup packet request type. /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. /// Length of the data to transfer. Must be equal to or less than the length of . /// The setup packet's length member will be set to this length. public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, int length) { // Parameters are int and not ushort because ushort is not CLS compliant. CheckNotDisposed(); CheckControlParams(value, index, buffer, length); try { _wuDevice.ControlTransfer(requestType, request, (ushort)value, (ushort)index, (ushort)length, buffer); } catch (API.APIException e) { throw new USBException("Control transfer failed", e); } } /// /// Initiates an asynchronous control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending /// on the highest bit of the parameter. Alternatively, and /// can be used for asynchronous control transfers in a specific direction, which is /// the recommended way because it prevents using the wrong direction accidentally. Use the BeginControlTransfer method when the direction is not /// known at compile time. /// The setup packet request type. /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. Note: This buffer is not allowed /// to change for the duration of the asynchronous operation. /// Length of the data to transfer. Must be equal to or less than the length of . The setup packet's length member will be set to this length. /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, int length, AsyncCallback userCallback, object stateObject) { // Parameters are int and not ushort because ushort is not CLS compliant. CheckNotDisposed(); CheckControlParams(value, index, buffer, length); USBAsyncResult result = new USBAsyncResult(userCallback, stateObject); try { _wuDevice.ControlTransferOverlapped(requestType, request, (ushort)value, (ushort)index, (ushort)length, buffer, result); } catch (API.APIException e) { if (result != null) result.Dispose(); throw new USBException("Asynchronous control transfer failed", e); } catch (Exception) { if (result != null) result.Dispose(); throw; } return result; } /// /// Initiates an asynchronous control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending /// on the highest bit of the parameter. Alternatively, and /// can be used for asynchronous control transfers in a specific direction, which is /// the recommended way because it prevents using the wrong direction accidentally. Use the BeginControlTransfer method when the direction is not /// known at compile time. /// The setup packet request type. /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. The setup packet's length member will /// be set to the length of this buffer. Note: This buffer is not allowed to change for the duration of the asynchronous operation. /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer, AsyncCallback userCallback, object stateObject) { return BeginControlTransfer(requestType, request, value, index, buffer, buffer.Length, userCallback, stateObject); } /// /// Waits for a pending asynchronous control transfer to complete. /// /// The object representing the asynchonous operation, /// as returned by one of the ControlIn, ControlOut or ControlTransfer methods. /// The number of bytes transfered during the operation. /// Every asynchronous control transfer must have a matching call to to dispose /// of any resources used and to retrieve the result of the operation. When the operation was successful the method returns the number /// of bytes that were transfered. If an error occurred during the operation this method will throw the exceptions that would /// otherwise have ocurred during the operation. If the operation is not yet finished EndControlTransfer will wait for the /// operation to finish before returning. public int EndControlTransfer(IAsyncResult asyncResult) { if (asyncResult == null) throw new NullReferenceException("asyncResult cannot be null"); if (!(asyncResult is USBAsyncResult)) throw new ArgumentException("AsyncResult object was not created by calling one of the BeginControl* methods on this class."); // todo: check duplicate end control USBAsyncResult result = (USBAsyncResult)asyncResult; try { if (!result.IsCompleted) result.AsyncWaitHandle.WaitOne(); if (result.Error != null) throw new USBException("Asynchronous control transfer from pipe has failed.", result.Error); return result.BytesTransfered; } finally { result.Dispose(); } } /// /// Initiates a control transfer over the default control endpoint. This method allows both IN and OUT direction transfers, depending /// on the highest bit of the parameter). Alternatively, and /// can be used for control transfers in a specific direction, which is the recommended way because /// it prevents using the wrong direction accidentally. Use the ControlTransfer method when the direction is not known at compile time. /// /// The setup packet request type. /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The data to transfer in the data stage of the control. When the transfer is in the IN direction the data received will be /// written to this buffer. For an OUT direction transfer the contents of the buffer are written sent through the pipe. The length of this /// buffer is used as the number of bytes in the control transfer. The setup packet's length member will be set to this length as well. public void ControlTransfer(byte requestType, byte request, int value, int index, byte[] buffer) { ControlTransfer(requestType, request, value, index, buffer, buffer.Length); } /// /// Initiates a control transfer without a data stage over the default control endpoint. This method allows both IN and OUT direction transfers, depending /// on the highest bit of the parameter). Alternatively, and /// can be used for control transfers in a specific direction, which is the recommended way because /// it prevents using the wrong direction accidentally. Use the ControlTransfer method when the direction is not known at compile time. /// /// The setup packet request type. /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). public void ControlTransfer(byte requestType, byte request, int value, int index) { // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) ControlTransfer(requestType, request, value, index, new byte[0], 0); } private void CheckIn(byte requestType) { if ((requestType & 0x80) == 0) // Host to device? throw new ArgumentException("Request type is not IN."); } private void CheckOut(byte requestType) { if ((requestType & 0x80) == 0x80) // Device to host? throw new ArgumentException("Request type is not OUT."); } /// /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit /// of the parameter). A buffer to receive the data is automatically created by this method. /// /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// Length of the data to transfer. A buffer will be created with this length and the length member of the setup packet /// will be set to this length. /// A buffer containing the data transfered. public byte[] ControlIn(byte requestType, byte request, int value, int index, int length) { CheckIn(requestType); byte[] buffer = new byte[length]; ControlTransfer(requestType, request, value, index, buffer, buffer.Length); return buffer; } /// /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit /// of the parameter). /// /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The buffer that will receive the data transfered. /// Length of the data to transfer. The length member of the setup packet will be set to this length. The buffer specified /// by the parameter should have at least this length. public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length) { CheckIn(requestType); ControlTransfer(requestType, request, value, index, buffer, length); } /// /// Initiates a control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit /// of the parameter). The length of buffer given by the parameter will dictate /// the number of bytes that are transfered and the value of the setup packet's length member. /// /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The buffer that will receive the data transfered. The length of this buffer will be the number of bytes transfered. public void ControlIn(byte requestType, byte request, int value, int index, byte[] buffer) { CheckIn(requestType); ControlTransfer(requestType, request, value, index, buffer); } /// /// Initiates a control transfer without a data stage over the default control endpoint. The request should have an IN direction (specified by the highest bit /// of the parameter). The setup packets' length member will be set to zero. /// /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). public void ControlIn(byte requestType, byte request, int value, int index) { CheckIn(requestType); // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) ControlTransfer(requestType, request, value, index, new byte[0]); } /// /// Initiates a control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit /// of the parameter). /// /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// A buffer containing the data to transfer in the data stage. /// Length of the data to transfer. Only the first bytes of will be transfered. /// The setup packet's length parameter is set to this length. public void ControlOut(byte requestType, byte request, int value, int index, byte[] buffer, int length) { CheckOut(requestType); ControlTransfer(requestType, request, value, index, buffer, length); } /// /// Initiates a control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit /// of the parameter). /// /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// A buffer containing the data to transfer in the data stage. The complete buffer is transfered. The setup packet's length /// parameter is set to the length of this buffer. public void ControlOut(byte requestType, byte request, int value, int index, byte[] buffer) { CheckOut(requestType); ControlTransfer(requestType, request, value, index, buffer); } /// /// Initiates a control transfer without a data stage over the default control endpoint. The request should have an OUT direction (specified by the highest bit /// of the parameter. The setup packets' length member will be set to zero. /// /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). public void ControlOut(byte requestType, byte request, int value, int index) { CheckOut(requestType); // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) ControlTransfer(requestType, request, value, index, new byte[0]); } /// /// Initiates an asynchronous control transfer without a data stage over the default control endpoint. This method allows both IN and OUT direction transfers, depending /// on the highest bit of the parameter. Alternatively, and /// can be used for asynchronous control transfers in a specific direction, which is /// the recommended way because it prevents using the wrong direction accidentally. Use the BeginControlTransfer method when the direction is not /// known at compile time. /// The setup packet request type. /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlTransfer(byte requestType, byte request, int value, int index, AsyncCallback userCallback, object stateObject) { // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) return BeginControlTransfer(requestType, request, value, index, new byte[0], 0, userCallback, stateObject); } /// /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit /// of the parameter). /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The buffer that will receive the data transfered. /// Length of the data to transfer. Must be equal to or less than the length of . The setup packet's length member will be set to this length. /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlIn(byte requestType, byte request, int value, int index, byte[] buffer, int length, AsyncCallback userCallback, object stateObject) { CheckIn(requestType); return BeginControlTransfer(requestType, request, value, index, buffer, length, userCallback, stateObject); } /// /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an IN direction (specified by the highest bit /// of the parameter). /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The buffer that will receive the data transfered. The setup packet's length member will be set to the length of this buffer. /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlIn(byte requestType, byte request, int value, int index, byte[] buffer, AsyncCallback userCallback, object stateObject) { CheckIn(requestType); return BeginControlTransfer(requestType, request, value, index, buffer, userCallback, stateObject); } /// /// Initiates an asynchronous control transfer without a data stage over the default control endpoint. /// The request should have an IN direction (specified by the highest bit of the parameter). /// The setup packets' length member will be set to zero. /// The setup packet request type. The request type must specify the IN direction (highest bit set). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlIn(byte requestType, byte request, int value, int index, AsyncCallback userCallback, object stateObject) { CheckIn(requestType); return BeginControlTransfer(requestType, request, value, index, userCallback, stateObject); } /// /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit /// of the parameter). /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The buffer that contains the data to be transfered. /// Length of the data to transfer. Must be equal to or less than the length of . The setup packet's length member will be set to this length. /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlOut(byte requestType, byte request, int value, int index, byte[] buffer, int length, AsyncCallback userCallback, object stateObject) { CheckOut(requestType); return BeginControlTransfer(requestType, request, value, index, buffer, length, userCallback, stateObject); } /// /// Initiates an asynchronous control transfer over the default control endpoint. The request should have an OUT direction (specified by the highest bit /// of the parameter). /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// The buffer that contains the data to be transfered. The setup packet's length member will be set to the length of this buffer. /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlOut(byte requestType, byte request, int value, int index, byte[] buffer, AsyncCallback userCallback, object stateObject) { CheckOut(requestType); return BeginControlTransfer(requestType, request, value, index, buffer, userCallback, stateObject); } /// /// Initiates an asynchronous control transfer without a data stage over the default control endpoint. /// The request should have an OUT direction (specified by the highest bit of the parameter). /// The setup packets' length member will be set to zero. /// The setup packet request type. The request type must specify the OUT direction (highest bit cleared). /// The setup packet device request. /// The value member in the setup packet. Its meaning depends on the request. Value should be between zero and 65535 (0xFFFF). /// The index member in the setup packet. Its meaning depends on the request. Index should be between zero and 65535 (0xFFFF). /// An optional asynchronous callback, to be called when the control transfer is complete. Can be null if no callback is required. /// A user-provided object that distinguishes this particular asynchronous operation. Can be null if not required. /// An object repesenting the asynchronous control transfer, which could still be pending. /// This method always completes immediately even if the operation is still pending. The object returned represents the operation /// and must be passed to to retrieve the result of the operation. For every call to this method a matching call to /// must be made. When specifies a callback function, this function will be called when the operation is completed. The optional /// parameter can be used to pass user-defined information to this callback or the . The /// also provides an event handle () that will be triggered when the operation is complete as well. /// public IAsyncResult BeginControlOut(byte requestType, byte request, int value, int index, AsyncCallback userCallback, object stateObject) { CheckOut(requestType); // TODO: null instead of empty buffer. But overlapped code would have to be fixed for this (no buffer to pin) return BeginControlTransfer(requestType, request, value, index, new byte[0], userCallback, stateObject); } private void CheckNotDisposed() { if (_disposed) throw new ObjectDisposedException("USB device object has been disposed."); } /// /// Finds WinUSB devices with a GUID matching the parameter guidString /// /// The GUID string that the device should match. /// The format of this string may be any format accepted by the constructor /// of the System.Guid class /// An array of USBDeviceInfo objects representing the /// devices found. When no devices are found an empty array is /// returned. public static USBDeviceInfo[] GetDevices(string guidString) { return GetDevices(new Guid(guidString)); } /// /// Finds WinUSB devices with a GUID matching the parameter guid /// /// The GUID that the device should match. /// An array of USBDeviceInfo objects representing the /// devices found. When no devices are found an empty array is /// returned. public static USBDeviceInfo[] GetDevices(Guid guid) { API.DeviceDetails[] detailList = API.DeviceManagement.FindDevicesFromGuid(guid); USBDeviceInfo[] devices = new USBDeviceInfo[detailList.Length]; for (int i = 0; i < detailList.Length; i++) { devices[i] = new USBDeviceInfo(detailList[i]); } return devices; } /// /// Finds the first WinUSB device with a GUID matching the parameter guid. /// If multiple WinUSB devices match the GUID only the first one is returned. /// /// The GUID that the device should match. /// An UsbDevice object representing the device if found. If /// no device with the given GUID could be found null is returned. public static USBDevice GetSingleDevice(Guid guid) { API.DeviceDetails[] detailList = API.DeviceManagement.FindDevicesFromGuid(guid); if (detailList.Length == 0) return null; return new USBDevice(detailList[0].DevicePath); } /// /// Finds the first WinUSB device with a GUID matching the parameter guidString. /// If multiple WinUSB devices match the GUID only the first one is returned. /// /// The GUID string that the device should match. /// An UsbDevice object representing the device if found. If /// no device with the given GUID could be found null is returned. public static USBDevice GetSingleDevice(string guidString) { return USBDevice.GetSingleDevice(new Guid(guidString)); } private static USBDeviceDescriptor GetDeviceDescriptor(string devicePath) { try { USBDeviceDescriptor descriptor; using (API.WinUSBDevice wuDevice = new API.WinUSBDevice()) { wuDevice.OpenDevice(devicePath); API.USB_DEVICE_DESCRIPTOR deviceDesc = wuDevice.GetDeviceDescriptor(); // string q = wuDevice.GetStringDescriptor(0); // TODO: use language id properly string manufacturer = null, product = null, serialNumber = null; byte idx = 0; try { idx = deviceDesc.iManufacturer; if (idx > 0) manufacturer = wuDevice.GetStringDescriptor(idx); } catch { } try { idx = deviceDesc.iProduct; if (idx > 0) product = wuDevice.GetStringDescriptor(idx); } catch { } try { idx = deviceDesc.iSerialNumber; if (idx > 0) serialNumber = wuDevice.GetStringDescriptor(idx); } catch { } descriptor = new USBDeviceDescriptor(devicePath, deviceDesc, manufacturer, product, serialNumber); } return descriptor; } catch (API.APIException e) { throw new USBException("Failed to retrieve device descriptor.", e); } } } }