Files
HC_APTBS/Infrastructure/Kwp/FtdiInterface.cs
2026-04-11 12:45:18 +02:00

869 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
namespace HC_APTBS.Infrastructure.Kwp
{
public class FT_DEVICE_INFO_NODE
{
/// <summary>
/// Indicates device state. Can be any combination of the following: FT_FLAGS_OPENED, FT_FLAGS_HISPEED
/// </summary>
public UInt32 Flags;
/// <summary>
/// Indicates the device type. Can be one of the following: FT_DEVICE_232R, FT_DEVICE_2232C, FT_DEVICE_BM, FT_DEVICE_AM, FT_DEVICE_100AX or FT_DEVICE_UNKNOWN
/// </summary>
public byte Type;
/// <summary>
/// The Vendor ID and Product ID of the device
/// </summary>
public UInt32 ID;
/// <summary>
/// The physical location identifier of the device
/// </summary>
public UInt32 LocId;
/// <summary>
/// The device serial number
/// </summary>
public string SerialNumber;
/// <summary>
/// The device description
/// </summary>
public string Description;
/// <summary>
/// The device handle. This value is not used externally and is provided for information only.
/// If the device is not open, this value is 0.
/// </summary>
public IntPtr ftHandle;
}
public class FtdiInterface : IInterface
{
private FT _ft = null;
private IntPtr _handle = IntPtr.Zero;
private readonly byte[] _buf = new byte[1];
public FtdiInterface(string serialNumber, int baudRate)
{
_ft = new FT();
var status = _ft.Open(
serialNumber, FT.OpenExFlags.BySerialNumber, out _handle);
FT.AssertOk(status);
status = _ft.SetBaudRate(_handle, (uint)baudRate);
FT.AssertOk(status);
status = _ft.SetDataCharacteristics(
_handle,
FT.Bits.Eight,
FT.StopBits.One,
FT.Parity.None);
FT.AssertOk(status);
status = _ft.SetFlowControl(
_handle,
FT.FlowControl.None, 0, 0);
FT.AssertOk(status);
status = _ft.ClrRts(_handle);
FT.AssertOk(status);
status = _ft.SetDtr(_handle);
FT.AssertOk(status);
status = _ft.SetTimeouts(
_handle,
500,
500);
FT.AssertOk(status);
// Should allow faster response times for small packets
status = _ft.SetLatencyTimer(_handle, 1);
FT.AssertOk(status);
}
public void Dispose()
{
if (_handle != IntPtr.Zero)
{
var status = _ft.Close(_handle);
_handle = IntPtr.Zero;
FT.AssertOk(status);
}
if (_ft != null)
{
_ft.Dispose();
_ft = null;
}
}
public byte ReadByte()
{
var status = _ft.Read(_handle, _buf, 1, out uint countOfBytesRead);
FT.AssertOk(status);
if (countOfBytesRead != 1)
{
throw new TimeoutException("Read timed out");
}
var b = _buf[0];
return b;
}
/// <summary>
/// Write a byte to the interface but do not read/discard its echo.
/// </summary>
public void WriteByteRaw(byte b)
{
_buf[0] = b;
var status = _ft.Write(_handle, _buf, 1, out uint countOfBytesWritten);
FT.AssertOk(status);
if (countOfBytesWritten != 1)
{
throw new InvalidOperationException(
$"Expected to write 1 byte but wrote {countOfBytesWritten} bytes");
}
}
public void SetBreakOn()
{
var status = _ft.SetBreakOn(_handle);
FT.AssertOk(status);
}
public void SetBreakOff()
{
var status = _ft.SetBreakOff(_handle);
FT.AssertOk(status);
}
public void ClearReceiveBuffer()
{
var status = _ft.Purge(_handle, FT.PurgeMask.RX);
FT.AssertOk(status);
}
public static uint GetDevicesCount()
{
uint numdevs = 0;
FT tmp_ft = new FT();
try
{
var status = tmp_ft.CreateDeviceInfoList(out numdevs);
FT.AssertOk(status);
}
finally
{
tmp_ft.Dispose();
}
return numdevs;
}
public static void GetDeviceList(FT_DEVICE_INFO_NODE[] devicelist)
{
FT tmp_ft = new FT();
try
{
uint numdevs;
var status = tmp_ft.CreateDeviceInfoList(out numdevs);
FT.AssertOk(status);
byte[] sernum = new byte[16];
byte[] desc = new byte[64];
for (UInt32 i = 0; i < numdevs; i++)
{
devicelist[i] = new FT_DEVICE_INFO_NODE();
var ftStatus = tmp_ft.GetDeviceInfoDetail(i, out devicelist[i].Flags, out devicelist[i].Type, out devicelist[i].ID, out devicelist[i].LocId, sernum, desc, out devicelist[i].ftHandle);
devicelist[i].SerialNumber = Encoding.ASCII.GetString(sernum);
devicelist[i].Description = Encoding.ASCII.GetString(desc);
// Trim strings to first occurrence of a null terminator character
int nullIndex = devicelist[i].SerialNumber.IndexOf("\0", StringComparison.Ordinal);
if (nullIndex != -1)
devicelist[i].SerialNumber = devicelist[i].SerialNumber.Substring(0, nullIndex);
nullIndex = devicelist[i].Description.IndexOf("\0");
if (nullIndex != -1)
devicelist[i].Description = devicelist[i].Description.Substring(0, nullIndex);
}
}
finally
{
tmp_ft.Dispose();
}
}
public byte ReadEEPROM(uint Address, ref ushort EEValue)
{
var status = _ft.ReadEEPROM(_handle, Address, ref EEValue);
FT.AssertOk(status);
return (byte) status;
}
private static byte DecodeByte(byte b)
{
byte tmp = (byte)(((((b & 2) << 1) | (b & 0xF8)) << 3) | (b & 1));
byte tmp2 = (byte)((((((b >> 2) & 0x10) | (b & 0x87)) >> 1) | (b & 0x30)) >> 1);
return (byte)((tmp << 1) | tmp2); ;
}
private static UInt32 DecodeFTChipID(UInt32 id)
{
return (UInt32)((DecodeByte((byte)(id & 0xFF)) << 24) | (DecodeByte((byte)(id >> 8)) << 16) | (DecodeByte((byte)(id >> 16)) << 8) | DecodeByte((byte)((id >> 24) & 0xFF))) ^ 0xA5F0F7D1;
}
public static UInt32 CalculateKey(UInt32 id)
{
UInt32 result = 0;
result = id;
for (UInt32 i = 0; i < 10; i++)
{
if ((result & 0x00000001) == 0x00000001)
{
result = (result >> 1) | 0x80000000;
result ^= 0x63AC294D;
}
else
{
result >>= 1;
result ^= 0xF19A5712;
}
if ((result + 0x4A2FDC49) > 0xFFFFFFFF)
{
result += 0x4A2FDC49;
if ((result & 0x00000001) == 0x00000001)
{
result = (result >> 1) | 0x80000000;
}
else
{
result = (result >> 1);
}
}
else
{
result += 0x4A2FDC49;
}
}
return result;
}
public bool ReadChipID(ref UInt32 ChipID)
{
ushort a = 0, b = 0;
var status = _ft.ReadEEPROM(_handle, 0x44, ref a);
FT.AssertOk(status);
status = _ft.ReadEEPROM(_handle, 0x43, ref b);
FT.AssertOk(status);
ChipID = DecodeFTChipID( (UInt32) (a << 16 | b) );
return (status == FT.Status.Ok);
}
public bool CheckIsChipIDVaild(UInt32 Key)
{
UInt32 ChipID = 0;
if (!ReadChipID(ref ChipID))
{
return false;
}
UInt32 CorrectKey = CalculateKey(ChipID);
return (Key == CorrectKey);
}
public bool EEUserAreaSize(ref uint UASize)
{
var status = _ft.EE_UASize(_handle, ref UASize);
FT.AssertOk(status);
return (status == FT.Status.Ok);
}
public bool EEReadUserArea(byte[] UserAreaDataBuffer, ref uint numBytesRead)
{
var status = _ft.EE_UARead(_handle, UserAreaDataBuffer, UserAreaDataBuffer.Length, ref numBytesRead);
FT.AssertOk(status);
return (status == FT.Status.Ok);
}
public bool EEWriteUserArea(byte[] UserAreaDataBuffer)
{
var status = _ft.EE_UAWrite(_handle, UserAreaDataBuffer, UserAreaDataBuffer.Length);
FT.AssertOk(status);
return (status == FT.Status.Ok);
}
public bool CheckCableValidity()
{
UInt32 user_area_size = 0;
if (!this.EEUserAreaSize(ref user_area_size) || (user_area_size == 0))
{
throw new InvalidOperationException($"Cable validation error");
}
byte[] user_area_data = new byte[user_area_size];
UInt32 bytes_read = 0;
if (!this.EEReadUserArea(user_area_data, ref bytes_read) || (bytes_read == 0))
{
throw new InvalidOperationException($"Cable validation error");
}
UInt32 Key = (UInt32)((user_area_data[0] << 24) | (user_area_data[1] << 16) | (user_area_data[2] << 8) | user_area_data[3]);
if (!this.CheckIsChipIDVaild(Key))
{
throw new InvalidOperationException($"Cable validation error");
}
return true;
}
}
[System.Reflection.ObfuscationAttribute(Feature = "renaming")] //cuidao
class FT : IDisposable
{
private IntPtr _d2xx = IntPtr.Zero;
// Delegates used to call into the FTID D2xx DLL
#pragma warning disable CS0649
private readonly FTDll.SetVidPid _setVidPid;
private readonly FTDll.OpenBySerialNumber _openBySerialNumber;
private readonly FTDll.Close _close;
private readonly FTDll.SetBaudRate _setBaudRate;
private readonly FTDll.SetDataCharacteristics _setDataCharacteristics;
private readonly FTDll.SetFlowControl _setFlowControl;
private readonly FTDll.SetDtr _setDtr;
private readonly FTDll.ClrDtr _clrDtr;
private readonly FTDll.SetRts _setRts;
private readonly FTDll.ClrRts _clrRts;
private readonly FTDll.SetTimeouts _setTimeouts;
private readonly FTDll.SetLatencyTimer _setLatencyTimer;
private readonly FTDll.Purge _purge;
private readonly FTDll.SetBreakOn _setBreakOn;
private readonly FTDll.SetBreakOff _setBreakOff;
private readonly FTDll.Read _read;
private readonly FTDll.Write _write;
private readonly FTDll.CreateDeviceInfoList _createDeviceInfoList;
private readonly FTDll.GetDeviceInfoDetail _getDeviceInfoDetail;
private readonly FTDll.ReadEE _readEE;
private readonly FTDll.EE_UASize _ee_UASize;
private readonly FTDll.EE_UARead _ee_UARead;
private readonly FTDll.EE_UAWrite _ee_UAWrite;
#pragma warning restore CS0649
//[System.Reflection.ObfuscationAttribute(Feature = "renaming")] //no funciona imposible
public FT()
{
string libName;
bool isMacOs = false;
bool isLinux = false;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
libName = "libftd2xx.dylib";
isMacOs = true;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
libName = "ftd2xx.so";
isLinux = true;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
//libName = Environment.Is64BitProcess ? "ftd2xx64.dll" : "ftd2xx.dll";
libName = "ftd2xx.dll";
}
else
{
throw new InvalidOperationException($"Unknown OS: {RuntimeInformation.OSDescription}");
}
//_d2xx = NativeLibrary.Load(
// libName, typeof(FT).Assembly, DllImportSearchPath.SafeDirectories);
_d2xx = LoadLibrary(libName);
if (_d2xx == IntPtr.Zero)
{
_d2xx = LoadLibrary(@Path.GetDirectoryName(GetType().Assembly.Location) + "\\"+ libName);
}
if (_d2xx == IntPtr.Zero)
{
throw new InvalidOperationException("Failed to load FTD2XX.DLL");
}
var fieldNames = new List<string>
{
nameof(_openBySerialNumber),
nameof(_close),
nameof(_setBaudRate),
nameof(_setDataCharacteristics),
nameof(_setFlowControl),
nameof(_setDtr),
nameof(_clrDtr),
nameof(_setRts),
nameof(_clrRts),
nameof(_setTimeouts),
nameof(_setLatencyTimer),
nameof(_purge),
nameof(_setBreakOn),
nameof(_setBreakOff),
nameof(_read),
nameof(_write),
nameof(_createDeviceInfoList),
nameof(_getDeviceInfoDetail),
nameof(_readEE),
nameof(_ee_UASize),
nameof(_ee_UARead),
nameof(_ee_UAWrite)
};
if (isMacOs || isLinux)
{
fieldNames.Add(nameof(_setVidPid));
}
foreach (var fieldName in fieldNames)
{
var fieldInfo = typeof(FT).GetField(
fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
var nativeMethodName = fieldInfo.FieldType.GetCustomAttribute<SymbolNameAttribute>().Name;
//var export = NativeLibrary.GetExport(_d2xx, nativeMethodName);
var export = GetProcAddress(_d2xx, nativeMethodName);
var delegateVal = Marshal.GetDelegateForFunctionPointer(export, fieldInfo.FieldType);
fieldInfo.SetValue(this, delegateVal);
}
if (isMacOs || isLinux)
{
var vidStr = Environment.GetEnvironmentVariable("FTDI_VID");
var pidStr = Environment.GetEnvironmentVariable("FTDI_PID");
if (!string.IsNullOrEmpty(vidStr) && !string.IsNullOrEmpty(pidStr))
{
//var vid = Utils.ParseUint(vidStr);
var vid = UInt32.Parse(vidStr);
var pid = UInt32.Parse(pidStr);
//var pid = Utils.ParseUint(pidStr);
System.Diagnostics.Debug.WriteLine($"Setting FTDI VID=0x{vid:X4}, PID=0x{pid:X4}");
var status = SetVidPid(vid, pid);
AssertOk(status);
}
}
}
public void Dispose()
{
if (_d2xx != IntPtr.Zero)
{
//NativeLibrary.Free(_d2xx);
FreeLibrary(_d2xx);
_d2xx = IntPtr.Zero;
}
}
public static void AssertOk(FT.Status status)
{
if (status != FT.Status.Ok)
{
throw new InvalidOperationException(
$"D2xx library returned {status} instead of Ok");
}
}
public Status SetVidPid(
uint vid,
uint pid)
{
return _setVidPid(vid, pid);
}
public Status Open(
string serialNumber,
OpenExFlags flags,
out IntPtr handle)
{
return _openBySerialNumber(serialNumber, flags, out handle);
}
public Status Close(
IntPtr handle)
{
return _close(handle);
}
public Status SetBaudRate(
IntPtr handle,
uint baudRate)
{
return _setBaudRate(handle, baudRate);
}
public Status SetDataCharacteristics(
IntPtr handle,
Bits wordLength,
StopBits stopBits,
Parity parity)
{
return _setDataCharacteristics(handle, wordLength, stopBits, parity);
}
public Status SetFlowControl(
IntPtr handle,
FlowControl flowControl,
byte xonChar,
byte xoffChar)
{
return _setFlowControl(handle, flowControl, xonChar, xoffChar);
}
public Status SetDtr(
IntPtr handle)
{
return _setDtr(handle);
}
public Status ClrDtr(
IntPtr handle)
{
return _clrDtr(handle);
}
public Status SetRts(
IntPtr handle)
{
return _setRts(handle);
}
public Status ClrRts(
IntPtr handle)
{
return _clrRts(handle);
}
public Status SetTimeouts(
IntPtr handle,
uint readTimeoutMS,
uint writeTimeoutMS)
{
return _setTimeouts(handle, readTimeoutMS, writeTimeoutMS);
}
public Status SetLatencyTimer(
IntPtr handle,
byte timerMS)
{
return _setLatencyTimer(handle, timerMS);
}
public Status Purge(
IntPtr handle,
PurgeMask mask)
{
return _purge(handle, mask);
}
public Status SetBreakOn(
IntPtr handle)
{
return _setBreakOn(handle);
}
public Status SetBreakOff(
IntPtr handle)
{
return _setBreakOff(handle);
}
public Status Read(
IntPtr handle,
byte[] buffer,
uint countOfBytesToRead,
out uint countOfBytesRead)
{
return _read(handle, buffer, countOfBytesToRead, out countOfBytesRead);
}
public Status Write(
IntPtr handle,
byte[] buffer,
uint countOfBytesToWrite,
out uint countOfBytesWritten)
{
return _write(handle, buffer, countOfBytesToWrite, out countOfBytesWritten);
}
public Status CreateDeviceInfoList(out uint devcount)
{
return _createDeviceInfoList(out devcount);
}
public Status GetDeviceInfoDetail(
UInt32 index,
out UInt32 flags,
out byte chiptype,
out UInt32 id,
out UInt32 locid,
byte[] serialnumber,
byte[] description,
out IntPtr ftHandle)
{
return _getDeviceInfoDetail(index, out flags, out chiptype, out id, out locid, serialnumber, description, out ftHandle);
}
public Status ReadEEPROM(
IntPtr ftHandle,
uint dwWordOffset,
ref ushort lpwValue)
{
return _readEE(ftHandle, dwWordOffset, ref lpwValue);
}
public Status EE_UASize(
IntPtr ftHandle,
ref uint dwSize)
{
return _ee_UASize(ftHandle, ref dwSize);
}
public Status EE_UARead(
IntPtr ftHandle,
byte[] pucData,
int dwDataLen,
ref uint lpdwDataRead)
{
return _ee_UARead(ftHandle, pucData, dwDataLen, ref lpdwDataRead);
}
public Status EE_UAWrite(
IntPtr ftHandle,
byte[] pucData,
int dwDataLen)
{
return _ee_UAWrite(ftHandle, pucData, dwDataLen);
}
public enum Status : uint
{
Ok = 0,
InvalidHandle,
DeviceNotFound,
DeviceNotOpened,
IOError,
insufficient_resources,
InvalidParameter,
InvalidBaudRate,
DeviceNotOpenedForErase,
DeviceNotOpenedForWrite,
FailedToWriteDevice,
EepromReadFailed,
EepromWriteFailed,
EepromEraseFailed,
EepromNotPresent,
EepromNotProgrammed,
InvalidArgs,
NotSupported,
OtherError,
DeviceListNotReady,
};
[Flags]
public enum OpenExFlags : uint
{
BySerialNumber = 1,
ByDescription = 2,
ByLocation = 4
};
public enum Bits : byte
{
Eight = 8,
Seven = 7
};
public enum StopBits : byte
{
One = 0,
Two = 2
};
public enum Parity : byte
{
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4
};
public enum FlowControl : ushort
{
None = 0x0000,
RtsCts = 0x0100,
DtrDsr = 0x0200,
XonXoff = 0x0400
};
[Flags]
public enum PurgeMask : uint
{
RX = 1,
TX = 2
};
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
}
[AttributeUsage(AttributeTargets.Delegate)]
public class SymbolNameAttribute : Attribute
{
public SymbolNameAttribute(string name)
{
Name = name;
}
public string Name { get; private set; }
}
static class FTDll
{
[SymbolName("FT_SetVIDPID")]
public delegate FT.Status SetVidPid(
uint vid, uint pid);
[SymbolName("FT_OpenEx")]
public delegate FT.Status OpenBySerialNumber(
[MarshalAs(UnmanagedType.LPStr)] string serialNumber,
FT.OpenExFlags flags,
out IntPtr handle);
[SymbolName("FT_Close")]
public delegate FT.Status Close(
IntPtr handle);
[SymbolName("FT_SetBaudRate")]
public delegate FT.Status SetBaudRate(
IntPtr handle,
uint baudRate);
[SymbolName("FT_SetDataCharacteristics")]
public delegate FT.Status SetDataCharacteristics(
IntPtr handle,
FT.Bits wordLength,
FT.StopBits stopBits,
FT.Parity parity);
[SymbolName("FT_SetFlowControl")]
public delegate FT.Status SetFlowControl(
IntPtr handle,
FT.FlowControl flowControl,
byte xonChar,
byte xoffChar);
[SymbolName("FT_SetDtr")]
public delegate FT.Status SetDtr(
IntPtr handle);
[SymbolName("FT_ClrDtr")]
public delegate FT.Status ClrDtr(
IntPtr handle);
[SymbolName("FT_SetRts")]
public delegate FT.Status SetRts(
IntPtr handle);
[SymbolName("FT_ClrRts")]
public delegate FT.Status ClrRts(
IntPtr handle);
[SymbolName("FT_SetTimeouts")]
public delegate FT.Status SetTimeouts(
IntPtr handle,
uint readTimeoutMS,
uint writeTimeoutMS);
[SymbolName("FT_SetLatencyTimer")]
public delegate FT.Status SetLatencyTimer(
IntPtr handle,
byte timerMS);
[SymbolName("FT_Purge")]
public delegate FT.Status Purge(
IntPtr handle,
FT.PurgeMask mask);
[SymbolName("FT_SetBreakOn")]
public delegate FT.Status SetBreakOn(
IntPtr handle);
[SymbolName("FT_SetBreakOff")]
public delegate FT.Status SetBreakOff(
IntPtr handle);
[SymbolName("FT_Read")]
public delegate FT.Status Read(
IntPtr handle,
byte[] buffer,
uint countOfBytesToRead,
out uint countOfBytesRead);
[SymbolName("FT_Write")]
public delegate FT.Status Write(
IntPtr handle,
byte[] buffer,
uint countOfBytesToWrite,
out uint countOfBytesWritten);
[SymbolName("FT_CreateDeviceInfoList")]
public delegate FT.Status CreateDeviceInfoList(
out UInt32 numdevs);
[SymbolName("FT_GetDeviceInfoDetail")]
public delegate FT.Status GetDeviceInfoDetail(
UInt32 index,
out UInt32 flags,
out byte chiptype,
out UInt32 id,
out UInt32 locid,
byte[] serialnumber,
byte[] description,
out IntPtr ftHandle);
[SymbolName("FT_ReadEE")]
public delegate FT.Status ReadEE(
IntPtr ftHandle,
uint dwWordOffset,
ref ushort lpwValue);
[SymbolName("FT_EE_UASize")]
public delegate FT.Status EE_UASize(
IntPtr ftHandle,
ref uint dwSize);
[SymbolName("FT_EE_UARead")]
public delegate FT.Status EE_UARead(
IntPtr ftHandle,
byte[] pucData,
int dwDataLen,
ref uint lpdwDataRead);
[SymbolName("FT_EE_UAWrite")]
public delegate FT.Status EE_UAWrite(
IntPtr ftHandle,
byte[] pucData,
int dwDataLen);
}
}