// Copyright © 2006-2010 Travis Robinson. All rights reserved.
//
// website: http://sourceforge.net/projects/libusbdotnet
// e-mail: libusbdotnet@gmail.com
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. or
// visit www.gnu.org.
//
//
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using LibUsbDotNet.Descriptors;
using LibUsbDotNet.Info;
using LibUsbDotNet.Internal;
using LibUsbDotNet.LudnMonoLibUsb;
using LibUsbDotNet.Main;
using MonoLibUsb;
namespace LibUsbDotNet
{
/// Contains non-driver specific USB device communication members.
///
/// This class is compatible with WinUSB, LibUsb-Win32, and linux libusb v1.x.
/// Platform independent applications should only use usb device members from this class.
/// If more functionality is required, it is up to the application to handle multi-driver
/// and/or multi-platfrom requirements.
///
public abstract partial class UsbDevice
{
#region Enumerations
///
/// Driver modes enumeration. See the UsbDevice. property.
///
public enum DriverModeType
{
///
/// Not yet determined.
///
Unknown,
///
/// Using LibUsb kernel driver (Legacy or Native) on windows.
///
LibUsb,
///
/// Using WinUsb user-mode driver on windows.
///
WinUsb,
///
/// Using Libusb 1.0 driver on linux.
///
MonoLibUsb,
///
/// Using Libusb 1.0 windows backend driver on windows.
///
LibUsbWinBack
}
#endregion
internal readonly UsbEndpointList mActiveEndpoints;
internal readonly UsbApiBase mUsbApi;
internal UsbDeviceDescriptor mCachedDeviceDescriptor;
internal List mConfigs;
internal int mCurrentConfigValue = -1;
internal UsbDeviceInfo mDeviceInfo;
internal SafeHandle mUsbHandle;
internal UsbRegistry mUsbRegistry;
internal UsbDevice(UsbApiBase usbApi, SafeHandle usbHandle)
{
mUsbApi = usbApi;
mUsbHandle = usbHandle;
mActiveEndpoints = new UsbEndpointList();
}
///
/// Gets all available configurations for this
///
///
/// The first time this property is accessed it will query the for all configurations.
/// Subsequent request will return a cached copy of all configurations.
///
public virtual ReadOnlyCollection Configs
{
get
{
if ((ReferenceEquals(mConfigs, null)))
{
mConfigs = GetDeviceConfigs(this);
}
return mConfigs.AsReadOnly();
}
}
///
/// Gets the actual device descriptor the the current .
///
public virtual UsbDeviceInfo Info
{
get
{
if (ReferenceEquals(mDeviceInfo, null))
{
mDeviceInfo = new UsbDeviceInfo(this);
}
return mDeviceInfo;
}
}
///
/// Gets the class that opened the device, or null if the device was not opened by the class.
///
public virtual UsbRegistry UsbRegistryInfo
{
get { return mUsbRegistry; }
}
///
/// Gets a value indication if the device handle is valid.
///
public bool IsOpen
{
get { return ((mUsbHandle != null) && !mUsbHandle.IsClosed) && !mUsbHandle.IsInvalid; }
}
///
/// A list of endpoints that have beened opened by this class.
///
public UsbEndpointList ActiveEndpoints
{
get { return mActiveEndpoints; }
}
internal SafeHandle Handle
{
get { return mUsbHandle; }
}
///
/// Returns the DriverMode this USB device is using.
///
public abstract DriverModeType DriverMode { get; }
///
/// Closes the and disposes any .
///
/// True on success.
public abstract bool Close();
///
/// Opens the USB device handle.
///
///
///True if the device is already opened or was opened successfully.
///False if the device does not exists or is no longer valid.
///
public abstract bool Open();
///
/// Transmits control data over a default control endpoint.
///
/// An 8-byte setup packet which contains parameters for the control request.
/// See section 9.3 USB Device Requests of the Universal Serial Bus Specification Revision 2.0 for more information.
/// Data to be sent/received from the device.
/// Length of the buffer param.
/// Number of bytes sent or received (depends on the direction of the control transfer).
/// True on success.
public virtual bool ControlTransfer(ref UsbSetupPacket setupPacket, IntPtr buffer, int bufferLength, out int lengthTransferred)
{
bool bSuccess = mUsbApi.ControlTransfer(mUsbHandle, setupPacket, buffer, bufferLength, out lengthTransferred);
if (!bSuccess)
UsbError.Error(ErrorCode.Win32Error, Marshal.GetLastWin32Error(), "ControlTransfer", this);
return bSuccess;
}
///
/// Transmits control data over a default control endpoint.
///
/// An 8-byte setup packet which contains parameters for the control request.
/// See section 9.3 USB Device Requests of the Universal Serial Bus Specification Revision 2.0 for more information.
/// Data to be sent/received from the device.
/// Length of the buffer param.
/// Number of bytes sent or received (depends on the direction of the control transfer).
/// True on success.
public virtual bool ControlTransfer(ref UsbSetupPacket setupPacket, object buffer, int bufferLength, out int lengthTransferred)
{
PinnedHandle pinned = new PinnedHandle(buffer);
bool bSuccess = ControlTransfer(ref setupPacket, pinned.Handle, bufferLength, out lengthTransferred);
pinned.Dispose();
return bSuccess;
}
///
/// Gets the USB devices active configuration value.
///
/// The active configuration value. A zero value means the device is not configured and a non-zero value indicates the device is configured.
/// True on success.
public virtual bool GetConfiguration(out byte config)
{
config = 0;
byte[] buf = new byte[1];
int uTransferLength;
UsbSetupPacket setupPkt = new UsbSetupPacket();
setupPkt.RequestType = (byte) UsbEndpointDirection.EndpointIn | (byte) UsbRequestType.TypeStandard |
(byte) UsbRequestRecipient.RecipDevice;
setupPkt.Request = (byte) UsbStandardRequest.GetConfiguration;
setupPkt.Value = 0;
setupPkt.Index = 0;
setupPkt.Length = 1;
bool bSuccess = ControlTransfer(ref setupPkt, buf, buf.Length, out uTransferLength);
if (bSuccess && uTransferLength == 1)
{
config = buf[0];
mCurrentConfigValue = config;
return true;
}
UsbError.Error(ErrorCode.Win32Error, Marshal.GetLastWin32Error(), "GetConfiguration", this);
return false;
}
///
/// Gets a descriptor from the device. See for more information.
///
/// The descriptor type ID to retrieve; this is usually one of the enumerations.
/// Descriptor index.
/// Descriptor language id.
/// Memory to store the returned descriptor in.
/// Length of the buffer parameter in bytes.
/// The number of bytes transferred to buffer upon success.
/// True on success.
public virtual bool GetDescriptor(byte descriptorType, byte index, short langId, IntPtr buffer, int bufferLength, out int transferLength)
{
transferLength = 0;
bool wasOpen = IsOpen;
if (!wasOpen) Open();
if (!IsOpen) return false;
bool bSuccess = mUsbApi.GetDescriptor(mUsbHandle, descriptorType, index, (ushort) langId, buffer, bufferLength, out transferLength);
if (!bSuccess)
UsbError.Error(ErrorCode.Win32Error, Marshal.GetLastWin32Error(), "GetDescriptor", this);
if (!wasOpen && IsOpen) Close();
return bSuccess;
}
///
/// Opens a endpoint for writing
///
/// Endpoint number for read operations.
/// A class ready for writing. If the specified endpoint is already been opened, the original class is returned.
public virtual UsbEndpointWriter OpenEndpointWriter(WriteEndpointID writeEndpointID) { return OpenEndpointWriter(writeEndpointID, EndpointType.Bulk); }
///
/// Opens an endpoint for writing
///
/// Endpoint number for read operations.
/// The type of endpoint to open.
/// A class ready for writing. If the specified endpoint is already been opened, the original class is returned.
public virtual UsbEndpointWriter OpenEndpointWriter(WriteEndpointID writeEndpointID, EndpointType endpointType)
{
foreach (UsbEndpointBase activeEndpoint in ActiveEndpoints)
if (activeEndpoint.EpNum == (byte) writeEndpointID)
return (UsbEndpointWriter) activeEndpoint;
UsbEndpointWriter epNew = new UsbEndpointWriter(this, writeEndpointID, endpointType);
return (UsbEndpointWriter) mActiveEndpoints.Add(epNew);
}
internal static List GetDeviceConfigs(UsbDevice usbDevice)
{
List rtnConfigs = new List();
byte[] cfgBuffer = new byte[UsbConstants.MAX_CONFIG_SIZE];
int iConfigs = usbDevice.Info.Descriptor.ConfigurationCount;
for (int iConfig = 0; iConfig < iConfigs; iConfig++)
{
int iBytesTransmitted;
bool bSuccess = usbDevice.GetDescriptor((byte) DescriptorType.Configuration, 0, 0, cfgBuffer, cfgBuffer.Length, out iBytesTransmitted);
if (bSuccess)
{
if (iBytesTransmitted >= UsbConfigDescriptor.Size && cfgBuffer[1] == (byte) DescriptorType.Configuration)
{
UsbConfigDescriptor configDescriptor = new UsbConfigDescriptor();
Helper.BytesToObject(cfgBuffer, 0, Math.Min(UsbConfigDescriptor.Size, cfgBuffer[0]), configDescriptor);
if (configDescriptor.TotalLength == iBytesTransmitted)
{
List rawDescriptorList = new List();
int iRawLengthPosition = configDescriptor.Length;
while (iRawLengthPosition < configDescriptor.TotalLength)
{
byte[] rawDescriptor = new byte[cfgBuffer[iRawLengthPosition]];
if (iRawLengthPosition + rawDescriptor.Length > iBytesTransmitted)
throw new UsbException(usbDevice, "Descriptor length is out of range.");
Array.Copy(cfgBuffer, iRawLengthPosition, rawDescriptor, 0, rawDescriptor.Length);
rawDescriptorList.Add(rawDescriptor);
iRawLengthPosition += rawDescriptor.Length;
}
rtnConfigs.Add(new UsbConfigInfo(usbDevice, configDescriptor, ref rawDescriptorList));
}
else
UsbError.Error(ErrorCode.InvalidConfig,
0,
"GetDeviceConfigs: USB config descriptor length doesn't match the length received.",
usbDevice);
}
else
UsbError.Error(ErrorCode.InvalidConfig, 0, "GetDeviceConfigs: USB config descriptor is invalid.", usbDevice);
}
else
UsbError.Error(ErrorCode.InvalidConfig, 0, "GetDeviceConfigs", usbDevice);
}
return rtnConfigs;
}
///
/// Gets a descriptor from the device. See for more information.
///
/// The descriptor type ID to retrieve; this is usually one of the enumerations.
/// Descriptor index.
/// Descriptor language id.
/// Memory to store the returned descriptor in.
/// Length of the buffer parameter in bytes.
/// The number of bytes transferred to buffer upon success.
/// True on success.
public bool GetDescriptor(byte descriptorType, byte index, short langId, object buffer, int bufferLength, out int transferLength)
{
PinnedHandle pinned = new PinnedHandle(buffer);
bool bSuccess = GetDescriptor(descriptorType, index, langId, pinned.Handle, bufferLength, out transferLength);
pinned.Dispose();
return bSuccess;
}
///
/// Asking for the zero'th index is special - it returns a string
/// descriptor that contains all the language IDs supported by the
/// device. Typically there aren't many - often only one. The
/// language IDs are 16 bit numbers, and they start at the third byte
/// in the descriptor. See USB 2.0 specification, section 9.6.7, for
/// more information on this.
///
/// A collection of LCIDs that the current supports.
public bool GetLangIDs(out short[] langIDs)
{
LangStringDescriptor sd = new LangStringDescriptor(UsbDescriptor.Size + (16*sizeof (short)));
int ret;
bool bSuccess = GetDescriptor((byte) DescriptorType.String, 0, 0, sd.Ptr, sd.MaxSize, out ret);
if (bSuccess && ret == sd.Length)
{
bSuccess = sd.Get(out langIDs);
}
else
{
langIDs = new short[0];
UsbError.Error(ErrorCode.Win32Error, Marshal.GetLastWin32Error(), "GetLangIDs", this);
}
sd.Free();
return bSuccess;
}
///
/// Gets a descriptor from the device.
///
/// Buffer to store the returned string in upon success.
/// The language ID to retrieve the string in. (0x409 for english).
/// The string index to retrieve.
/// True on success.
public bool GetString(out string stringData, short langId, byte stringIndex)
{
stringData = null;
int iTransferLength;
LangStringDescriptor sd = new LangStringDescriptor(255);
bool bSuccess = GetDescriptor((byte) DescriptorType.String, stringIndex, langId, sd.Ptr, sd.MaxSize, out iTransferLength);
if (bSuccess && iTransferLength > UsbDescriptor.Size && sd.Length == iTransferLength)
bSuccess = sd.Get(out stringData);
else if (!bSuccess)
UsbError.Error(ErrorCode.Win32Error, Marshal.GetLastWin32Error(), "GetString:GetDescriptor", this);
else
stringData = String.Empty;
return bSuccess;
}
///
/// Opens a endpoint for reading
///
/// Endpoint number for read operations.
/// A class ready for reading. If the specified endpoint is already been opened, the original class is returned.
public UsbEndpointReader OpenEndpointReader(ReadEndpointID readEndpointID) { return OpenEndpointReader(readEndpointID, UsbEndpointReader.DefReadBufferSize); }
///
/// Opens a endpoint for reading
///
/// Endpoint number for read operations.
/// Size of the read buffer allocated for the event.
/// A class ready for reading. If the specified endpoint is already been opened, the original class is returned.
public UsbEndpointReader OpenEndpointReader(ReadEndpointID readEndpointID, int readBufferSize) { return OpenEndpointReader(readEndpointID, readBufferSize, EndpointType.Bulk); }
///
/// Opens an endpoint for reading
///
/// Endpoint number for read operations.
/// Size of the read buffer allocated for the event.
/// The type of endpoint to open.
/// A class ready for reading. If the specified endpoint is already been opened, the original class is returned.
public virtual UsbEndpointReader OpenEndpointReader(ReadEndpointID readEndpointID, int readBufferSize, EndpointType endpointType)
{
foreach (UsbEndpointBase activeEndpoint in mActiveEndpoints)
if (activeEndpoint.EpNum == (byte) readEndpointID)
return (UsbEndpointReader) activeEndpoint;
UsbEndpointReader epNew = new UsbEndpointReader(this, readBufferSize, readEndpointID, endpointType);
return (UsbEndpointReader) mActiveEndpoints.Add(epNew);
}
///
/// Gets the selected alternate interface of the specified interface.
///
/// The interface settings number (index) to retrieve the selected alternate interface setting for.
/// The alternate interface setting selected for use with the specified interface.
/// True on success.
public bool GetAltInterfaceSetting(byte interfaceID, out byte selectedAltInterfaceID)
{
byte[] buf = new byte[1];
int uTransferLength;
UsbSetupPacket setupPkt = new UsbSetupPacket();
setupPkt.RequestType = (byte) UsbEndpointDirection.EndpointIn | (byte) UsbRequestType.TypeStandard |
(byte) UsbRequestRecipient.RecipInterface;
setupPkt.Request = (byte) UsbStandardRequest.GetInterface;
setupPkt.Value = 0;
setupPkt.Index = interfaceID;
setupPkt.Length = 1;
bool bSuccess = ControlTransfer(ref setupPkt, buf, buf.Length, out uTransferLength);
if (bSuccess && uTransferLength == 1)
selectedAltInterfaceID = buf[0];
else
selectedAltInterfaceID = 0;
return bSuccess;
}
///
/// De-initializes the USB driver.
///
///
/// If this method is not called before the application exits, it can cause it to hang indefinitely.
/// Calling this method multiple times will have no effect.
///
public static void Exit()
{
lock (MonoUsbDevice.OLockDeviceList)
{
if (MonoUsbDevice.mMonoUSBProfileList != null)
MonoUsbDevice.mMonoUSBProfileList.Close();
MonoUsbDevice.mMonoUSBProfileList = null;
}
MonoUsbApi.StopAndExit();
}
}
}