// 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(); } } }