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 { /// /// Indicates device state. Can be any combination of the following: FT_FLAGS_OPENED, FT_FLAGS_HISPEED /// public UInt32 Flags; /// /// 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 /// public byte Type; /// /// The Vendor ID and Product ID of the device /// public UInt32 ID; /// /// The physical location identifier of the device /// public UInt32 LocId; /// /// The device serial number /// public string SerialNumber; /// /// The device description /// public string Description; /// /// 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. /// 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; } /// /// Write a byte to the interface but do not read/discard its echo. /// 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 { 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().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); } }