//-----------------------------------------------------------------------
//
// 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
}
}