//----------------------------------------------------------------------- // // Copyright © 2012 Nils Hammar and Future Technology Devices International Limited. All rights reserved. // //----------------------------------------------------------------------- /* ** FTD2XX_NET.cs ** ** Copyright © 2009-2012 Future Technology Devices International Limited ** ** C# Source file for .NET wrapper of the Windows FTD2XX.dll API calls. ** Main module ** ** Author: FTDI ** Project: CDM Windows Driver Package ** Module: FTD2XX_NET Managed Wrapper ** Requires: ** Comments: ** ** History: ** 1.0.0 - Initial version ** 1.0.12 - Included support for the FT232H device. ** 1.0.14 - Included Support for the X-Series of devices. ** * Major rewrite 2012 by Nils Hammar to resolve some stability issues, * make it more flexible and improve performance. */ namespace FtdiApi { using System; using System.Text; using global::FtdiApi.Constants; using global::FtdiApi.Exceptions; /// /// Class wrapper for FTD2XX.DLL /// [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1516:ElementsMustBeSeparatedByBlankLine", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:UseBuiltInTypeAlias", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:CurlyBracketsMustNotBeOmitted", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Reviewed.")] public class FTDI { #region FIELDS /// /// Gets Native methods instance. /// internal FtdiApi.Native.NativeMethods NativeMethodsInstance { get; private set; } #endregion #region CONSTRUCTOR_DESTRUCTOR /// /// Initializes a new instance of the class. /// public FTDI() { this.NativeMethodsInstance = FtdiApi.Native.NativeMethods.Instance; // If the DLL hasn't been loaded, just return here if (!this.NativeMethodsInstance.DllLoaded) { ErrorHandler("FTDI:[init]", FT_STATUS.FT_OTHER_ERROR, FT_ERROR.FT_DLL_NOT_FOUND); } } #endregion #region DEFAULT_VALUES /// /// Default baud rate. /// private const uint FT_DEFAULT_BAUD_RATE = 9600; /// /// Default deadman timeout. /// private const uint FT_DEFAULT_DEADMAN_TIMEOUT = 5000; /// /// Indicator for no assigned COM port. /// private const Int32 FT_COM_PORT_NOT_ASSIGNED = -1; /// /// Default IN transfer size. /// private const uint FT_DEFAULT_IN_TRANSFER_SIZE = 0x1000; /// /// Default OUT transfer size. /// private const uint FT_DEFAULT_OUT_TRANSFER_SIZE = 0x1000; /// /// Default latency. /// private const byte FT_DEFAULT_LATENCY = 16; /// /// Default device ID. /// private const uint FT_DEFAULT_DEVICE_ID = 0x04036001; #endregion #region METHOD_DEFINITIONS /// /// Gets the number of FTDI devices available. /// /// The number of FTDI devices available. public void GetNumberOfDevices(ref uint devcount) { FunctionValidator("FT_CreateDeviceInfoList", this.NativeMethodsInstance.FT_CreateDeviceInfoList); // Call FT_CreateDeviceInfoList FT_STATUS ftStatus = this.NativeMethodsInstance.FT_CreateDeviceInfoList(ref devcount); ErrorHandler("GetNumberOfDevices", ftStatus, FT_ERROR.FT_NO_ERROR); } /// /// Gets information on all of the FTDI devices available. /// /// An array of type FT_DEVICE_INFO_NODE to contain the device information for all available devices. /// Thrown when the supplied buffer is not large enough to contain the device info list. public void GetDeviceList(FT_DEVICE_INFO_NODE[] devicelist) { FunctionValidator("FT_CreateDeviceInfoList", this.NativeMethodsInstance.FT_CreateDeviceInfoList); FunctionValidator("FT_GetDeviceInfoDetail", this.NativeMethodsInstance.FT_GetDeviceInfoDetail); // Initialise ftStatus to something other than FT_OK uint devcount = 0; // Call FT_CreateDeviceInfoList FT_STATUS ftStatus = this.NativeMethodsInstance.FT_CreateDeviceInfoList(ref devcount); ErrorHandler("GetDeviceList:CreateDeviceInfoList", ftStatus, FT_ERROR.FT_NO_ERROR); // Allocate the required storage for our list byte[] sernum = new byte[16]; byte[] desc = new byte[64]; if (devcount > 0) { // Check the size of the buffer passed in is big enough if (devicelist.Length < devcount) { // Buffer not big enough FT_ERROR ftErrorCondition = FT_ERROR.FT_BUFFER_SIZE; // Throw exception ErrorHandler("GetDeviceList", ftStatus, ftErrorCondition); } // Instantiate the array elements as FT_DEVICE_INFO_NODE for (uint i = 0; i < devcount; i++) { devicelist[i] = new FT_DEVICE_INFO_NODE(); // Call FT_GetDeviceInfoDetail ftStatus = this.NativeMethodsInstance.FT_GetDeviceInfoDetail(i, ref devicelist[i].Flags, ref devicelist[i].Type, ref devicelist[i].ID, ref devicelist[i].LocId, sernum, desc, ref devicelist[i].ftHandle); ErrorHandler("GetDeviceList:GetDeviceInfoDetail", ftStatus, FT_ERROR.FT_NO_ERROR); // Convert byte arrays to strings devicelist[i].SerialNumber = Encoding.ASCII.GetString(sernum); devicelist[i].Description = Encoding.ASCII.GetString(desc); // Trim strings to first occurrence of a null terminator character devicelist[i].SerialNumber = devicelist[i].SerialNumber.Substring(0, devicelist[i].SerialNumber.IndexOf("\0")); devicelist[i].Description = devicelist[i].Description.Substring(0, devicelist[i].Description.IndexOf("\0")); } } } /// /// Causes the system to check for USB hardware changes. This is equivalent to clicking on the "Scan for hardware changes" button in the Device Manager. /// public void Rescan() { FunctionValidator("FT_Rescan", this.NativeMethodsInstance.FT_Rescan); // Call FT_Rescan FT_STATUS ftStatus = this.NativeMethodsInstance.FT_Rescan(); ErrorHandler("Rescan", ftStatus, FT_ERROR.FT_NO_ERROR); } /// /// Forces a reload of the driver for devices with a specific VID and PID combination. /// /// If the VID and PID parameters are 0, the drivers for USB root hubs will be reloaded, causing all USB devices connected to reload their drivers /// Vendor ID of the devices to have the driver reloaded /// Product ID of the devices to have the driver reloaded public void Reload(ushort VendorID, ushort ProductID) { FunctionValidator("FT_Reload", this.NativeMethodsInstance.FT_Reload); // Call FT_Reload FT_STATUS ftStatus = this.NativeMethodsInstance.FT_Reload(VendorID, ProductID); ErrorHandler("Reload", ftStatus, FT_ERROR.FT_NO_ERROR); } /// /// Gets the current FTD2XX.DLL driver version number. /// /// The current library version. public void GetLibraryVersion(ref uint LibraryVersion) { FunctionValidator("FT_GetLibraryVersion", this.NativeMethodsInstance.FT_GetLibraryVersion); // Call FT_GetLibraryVersion FT_STATUS ftStatus = this.NativeMethodsInstance.FT_GetLibraryVersion(ref LibraryVersion); ErrorHandler("GetLibraryVersion", ftStatus, FT_ERROR.FT_NO_ERROR); } #endregion #region HELPER_METHODS /// /// Validates function pointer and throws exception if function object is 'null'. /// /// String describing source method/call where the check failed. /// Object to validate. /// Thrown when object is 'null' internal static void FunctionValidator(string source, object obj) { if (obj == null) { ErrorHandler(source, FT_STATUS.FT_OTHER_ERROR, FT_ERROR.FT_FUNCTION_NOT_FOUND); } } /// /// Validates function pointer and throws exception if function pointer is 'null'. /// /// String describing source method/call where the check failed. /// Pointer to function /// Thrown when pointer is 'null' internal static void FunctionPointerValidator(string source, IntPtr ptr) { if (ptr == IntPtr.Zero) { ErrorHandler(source, FT_STATUS.FT_OTHER_ERROR, FT_ERROR.FT_FUNCTION_NOT_FOUND); } } /// /// Method to check ftStatus and ftErrorCondition values for error conditions and throw exceptions accordingly. /// /// Notice: If FT_STATUS has the value 'FT_OTHER_ERROR' then FT_ERROR is checked for details and an exception is thrown /// based on the value of FT_ERROR if the value of FT_ERROR is not 'FT_NO_ERROR'. Otherwise a more generic exception is thrown. /// /// /// String describing source method/call where the check failed. /// Status from call to external DLL function. /// Error condition. /// Thrown when status or error is in an state indicating a problem. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Reviewed.")] internal static void ErrorHandler(string source, FT_STATUS ftStatus, FT_ERROR ftErrorCondition) { if (ftStatus != FT_STATUS.FT_OK) { // Check FT_STATUS values returned from FTD2XX DLL calls switch (ftStatus) { case FT_STATUS.FT_DEVICE_NOT_FOUND: { throw new FT_EXCEPTION(source + ": FTDI device not found."); } case FT_STATUS.FT_DEVICE_NOT_OPENED: { throw new FT_EXCEPTION(source + ": FTDI device not opened."); } case FT_STATUS.FT_DEVICE_NOT_OPENED_FOR_ERASE: { throw new FT_EXCEPTION(source + ": FTDI device not opened for erase."); } case FT_STATUS.FT_DEVICE_NOT_OPENED_FOR_WRITE: { throw new FT_EXCEPTION(source + ": FTDI device not opened for write."); } case FT_STATUS.FT_EEPROM_ERASE_FAILED: { throw new FT_EXCEPTION(source + ": Failed to erase FTDI device EEPROM."); } case FT_STATUS.FT_EEPROM_NOT_PRESENT: { throw new FT_EXCEPTION(source + ": No EEPROM fitted to FTDI device."); } case FT_STATUS.FT_EEPROM_NOT_PROGRAMMED: { throw new FT_EXCEPTION(source + ": FTDI device EEPROM not programmed."); } case FT_STATUS.FT_EEPROM_READ_FAILED: { throw new FT_EXCEPTION(source + ": Failed to read FTDI device EEPROM."); } case FT_STATUS.FT_EEPROM_WRITE_FAILED: { throw new FT_EXCEPTION(source + ": Failed to write FTDI device EEPROM."); } case FT_STATUS.FT_FAILED_TO_WRITE_DEVICE: { throw new FT_EXCEPTION(source + ": Failed to write to FTDI device."); } case FT_STATUS.FT_INSUFFICIENT_RESOURCES: { throw new FT_EXCEPTION(source + ": Insufficient resources."); } case FT_STATUS.FT_INVALID_ARGS: { throw new FT_EXCEPTION(source + ": Invalid arguments for FTD2XX function call."); } case FT_STATUS.FT_INVALID_BAUD_RATE: { throw new FT_EXCEPTION(source + ": Invalid Baud rate for FTDI device."); } case FT_STATUS.FT_INVALID_HANDLE: { throw new FT_EXCEPTION(source + ": Invalid handle for FTDI device."); } case FT_STATUS.FT_INVALID_PARAMETER: { throw new FT_EXCEPTION(source + ": Invalid parameter for FTD2XX function call."); } case FT_STATUS.FT_IO_ERROR: { throw new FT_EXCEPTION(source + ": FTDI device IO error."); } case FT_STATUS.FT_OTHER_ERROR: // If we have an error condition we shall use that instead. if (ftErrorCondition == FT_ERROR.FT_NO_ERROR) { throw new FT_EXCEPTION(source + ": An unexpected error has occurred when trying to communicate with the FTDI device."); } break; default: break; } } if (ftErrorCondition != FT_ERROR.FT_NO_ERROR) { // Check for other error conditions not handled by FTD2XX DLL switch (ftErrorCondition) { case FT_ERROR.FT_INCORRECT_DEVICE: { throw new FT_EXCEPTION(source + ": The current device type does not match the EEPROM structure."); } case FT_ERROR.FT_INVALID_BITMODE: { throw new FT_EXCEPTION(source + ": The requested bit mode is not valid for the current device."); } case FT_ERROR.FT_BUFFER_SIZE: { throw new FT_EXCEPTION(source + ": The supplied buffer is not big enough."); } case FT_ERROR.FT_DLL_NOT_FOUND: { throw new FT_EXCEPTION(source + ": The DLL was not properly loaded."); } case FT_ERROR.FT_FUNCTION_NOT_FOUND: { throw new FT_EXCEPTION(source + ": The function was not found in the DLL."); } default: break; } } return; } #endregion } }