JTAG on Intel Galileo Gen 2

Project:

  • 2014
  • Hardware debugger for Intel Quark Microcontroller target running under C# on host side

Parts:

  • Galileo Gen 2
  • GHI breakout
  • GHI flat cable
  • DLP USB 1232H
  • Breadboard

Software:

  • .. to be opened on GitHub

My favorite processor with my highest hopes of being completely transparent is Intel Quark. It is more or less opened and has a documented JTAG. I have built the JTAG adapter using my another favorite USB chip FTDI FT2232.

 

So far I got to the point when JTAP TAP points respond to my C# application with expected data. There is little sense for me in this data, since I did not study well the JTAG terminology, debugging protocols and whole decades of culture of hardware level debugging.

 

My current intent is to learn how to emit few machine code instructions into Quark, set registers content directly over wire, step through instructions and fetch changed data from registers back to host.

 

... to be continued

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FTD2XX_NET;
using System.Threading;

namespace UnitedAtoms // by VK UnitedAtoms.com
{
    class Program
    {
        static int Main(string[] args)
        {
            uint x1 = JtagClient.GetLibraryVersion();
            uint numberOfConnectedDevices = JtagClient.GetNumberOfConnectedDevices(); // x1=00030209, i.e. version 3.2.9.0
            string ser0 = JtagClient.GetSerialNumberOfDevice(0);
            string ser2 = JtagClient.GetSerialNumberOfDevice(1);
            string[] xx = JtagClient.GetDevicesDescriptions();
            uint[] xxx = JtagClient.GetDevicesLocations();

            if (numberOfConnectedDevices <= 0)
            {
                throw new Exception("No connected FTDI devices found JtagClient");
            }

            for (uint deviceNumber = 0; deviceNumber < numberOfConnectedDevices; deviceNumber++)
            {
                using (JtagClient jtagClient = new JtagClient(deviceNumber))
                {
                    jtagClient.ResetDevice();
                    FTDeviceInfo ftDeviceInfo = jtagClient.GetDeviceInfo();
                    uint bytesToRead = jtagClient.GetQueueStatus();
                    if (bytesToRead > 0)
                    {
                        byte[] bytes = jtagClient.Read(bytesToRead);
                    }

                    jtagClient.SetUSBParameters();
                    jtagClient.SetChars();
                    jtagClient.SetTimeouts(0, 5000);
                    jtagClient.SetLatencyTimer(16);
                    jtagClient.SetBitMode(0x0, 0x00); //Reset controller
                    jtagClient.SetBitMode(0x0, FT_BIT_MODES.FT_BIT_MODE_MPSSE);//Enable MPSSE mode
                    Thread.Sleep(50); // Wait for all the USB stuff to complete and work		
                    // Synchronize MPSSE by sending bad command 0xAA and receiving it back
                    jtagClient.Write(new byte[] { 0xAA });
                    do
                    {
                        bytesToRead = jtagClient.GetQueueStatus();
                        if (bytesToRead > 0)
                        {
                            byte[] bytes = jtagClient.Read(bytesToRead);
                            bool foundEcho = false;
                            for (int i = 0; i <= bytes.Length - 2; i++)
                            {
                                // 0xFA is a response about bad command
                                if (bytes[i] == 0xFA && bytes[i + 1] == 0xAA)
                                {
                                    foundEcho = true;
                                    break;
                                }
                            }
                            if (!foundEcho)
                                throw new Exception("Error in synchronizing MPSSE");
                            break;
                        }
                    } while (bytesToRead == 0);

                    // Set up the Hi-Speed specific commands for the FTx232H
                    jtagClient.Write(new byte[]{
                        0x8A, // Use 60MHz master clock (disable divide by 5)
                        0x97, // Turn off adaptive clocking (may be needed for ARM)
                        0x8D  // Disable three-phase clocking
                    });

                    // Set initial states of the MPSSE interface - low byte, both pin directions and output values
                    //		Pin name	Signal	Direction	Config	Initial State	Config
                    //		ADBUS0		TCK		output		1		low				0
                    //		ADBUS1		TDI		output		1		low				0
                    //		ADBUS2		TDO		input		0						0
                    //		ADBUS3		TMS		output		1		high			1
                    //		ADBUS4		GPIOL0	input		0						0
                    //		ADBUS5		GPIOL1	input		0						0
                    //		ADBUS6		GPIOL2	input		0						0
                    //		ADBUS7		GPIOL3	input		0						0
                    jtagClient.Write(new byte[]{
                        0x80, // Set data bits low-byte of MPSSE port
                        0x08, // Initial state config above
                        0x0B  // Direction config above
                    });

                    // Set initial states of the MPSSE interface - high byte, both pin directions and output values
                    //		Pin name	Signal	Direction	Config	Initial State	Config
                    //		ACBUS0		GPIOH0	input		0						0
                    //		ACBUS1		GPIOH1	input		0						0
                    //		ACBUS2		GPIOH2	input		0						0
                    //		ACBUS3		GPIOH3	input		0						0
                    //		ACBUS4		GPIOH4	input		0						0
                    //		ACBUS5		GPIOH5	input		0						0
                    //		ACBUS6		GPIOH6	input		0						0
                    //		ACBUS7		GPIOH7	input		0						0

                    jtagClient.Write(new byte[]{
                    0x82, // Set data bits low-byte of MPSSE port
                    0x00, // Initial state config above
                    0x00});
                    // Direction config above

                    uint clockDivisor = 0x05DB;	// Value of clock divisor, SCL Frequency = 60/((1+0x05DB)*2) (MHz) = 20khz
                    // Set TCK frequency 
                    // TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2)
                    jtagClient.Write(new byte[]{
                    0x86, // Command to set clock divisor
                    (byte)(clockDivisor & 0xFF), //Set 0xValueL of clock divisor
                    (byte)((clockDivisor >> 8) & 0xFF) //Set 0xValueH of clock divisor
                    });


                    // Disable internal loop-back
                    jtagClient.Write(new byte[]{
                        0x85 // Disable loopback
                    });// Send off the loopback command

                    // Navigage TMS through Test-Logic-Reset -> Run-Test-Idle -> Select-DR-Scan -> Select-IR-Scan
                    //                      TMS=1               TMS=0            TMS=1             TMS=1
                    jtagClient.Write(new byte[]{
                        0x4B, // Don't read data in Test-Logic-Reset, Run-Test-Idle, Select-DR-Scan, Select-IR-Scan
                        0x05, // Number of clock pulses = Length + 1 (6 clocks here)
                        0x0D
                    });
                    // Data is shifted LSB first, so the TMS pattern is 101100
                    // Send off the TMS command

                    // TMS is currently low.  State machine is in Shift-IR, so now use the TDI/TDO command to shift 1's out TDI/DO while reading TDO/DI
                    // Although 8 bits need shifted in, only 7 are clocked here.  The 8th will be in conjunciton with a TMS command, coming next
                    jtagClient.Write(new byte[]{
                        0x3B, // Clock data out throuth states Capture-IR, Shift-IR and Exit-IR, read back result
                        0x06, // Number of clock pulses = Length + 1 (7 clocks here)
                        0xFF
                    }); // Shift out 1111111 (ignore last bit)
                    // Send off the TMS command

                    // Here is the TMS command for one clock.  Data is also shifted in.
                    jtagClient.Write(new byte[]{
                        0x6B, // Clock out TMS, Read one bit.
                        0x00, // Number of clock pulses = Length + 0 (1 clock here)
                        0x83  // Data is shifted LSB first, so TMS becomes 1.  Also, bit 7 is shifted into TDI/DO, also a 1
                    });
                    // The 1 in bit 1 will leave TMS high for the next commands.
                    // Send off the TMS command


                    // Navigage TMS from Exit-IR through Update-IR -> Select-DR-Scan -> Capture-DR
                    //					                 TMS=1        TMS=1             TMS=0
                    jtagClient.Write(new byte[]{
                        0x4B, // Don't read data in Update-IR -> Select-DR-Scan -> Capture-DR
                        0x03, // Number of clock pulses = Length + 1 (4 clocks here)
                        0x83
                    });
                    // Data is shifted LSB first, so the TMS pattern is 110
                    // Send off the TMS command

                    // TMS is currently low.  State machine is in Shift-DR, so now use the TDI/TDO command to shift 101 out TDI/DO while reading TDO/DI
                    // Although 3 bits need shifted in, only 2 are clocked here.  The 3rd will be in conjunciton with a TMS command, coming next
                    jtagClient.Write(new byte[]{        
                        0x3B, // Clock data out throuth states Shift-DR and Exit-DR.
                        0x01, // Number of clock pulses = Length + 1 (2 clocks here)
                        0x01 // Shift out 101 (ignore last bit)
                    });
                    // Send off the TMS command

                    // Here is the TMS command for one clock.  Data is also shifted in.
                    jtagClient.Write(new byte[]{
                        0x6B, // Clock out TMS, Read one bit.
                        0x00, // Number of clock pulses = Length + 0 (1 clock here)
                        0x83
                    });
                    // Data is shifted LSB first, so TMS becomes 1.  Also, bit 7 is shifted into TDI/DO, also a 1
                    // The 1 in bit 1 will leave TMS high for the next commands.
                    // Send off the TMS command


                    // Navigage TMS through Update-DR -> Select-DR-Scan -> Select-IR-Scan -> Test Logic Reset
                    //                      TMS=1        TMS=1             TMS=1             TMS=1
                    jtagClient.Write(new byte[]{
                        0x4B, // Don't read data in Update-DR -> Select-DR-Scan -> Select-IR-Scan -> Test Logic Reset
                        0x03, // Number of clock pulses = Length + 1 (4 clocks here)
                        0xFF // Data is shifted LSB first, so the TMS pattern is 101100
                    });
                    // Send off the TMS command

                    bytesToRead = 0;
                    do
                    {
                        bytesToRead = jtagClient.GetQueueStatus();
                        // Get the number of bytes in the device input buffer
                    } while (bytesToRead == 0);
                    //or Timeout

                    byte[] response = jtagClient.Read(bytesToRead); 
                    //Read out the data from input buffer

                    Console.WriteLine("The value scanned by the FT2232H = 0x{0:X}", (response[response.Length - 1] >> 5));

                    // Generate a clock while in Test-Logic-Reset
                    // This will not do anything with the TAP in the Test-Logic-Reset state, 
                    //		but will demonstrate generation of clocks without any data transfer
                    jtagClient.Write(new byte[]{
                        0x8F, // Generate clock pulses
                        0x02, // (0x0002 + 1) * 8 = 24 clocks
                        0x00  // 
                    });
                    // Send off the clock commands
                }
            }
            Console.ReadLine();
            return 0;
        }
    }
}
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace UnitedAtoms
{
    /// 
    /// Represents single opened FTD2XX device
    /// 
    public interface IFtd2xx
    {

    }

    public enum FT_STATUS : uint
    {
        FT_OK = 0,
        FT_INVALID_HANDLE = 1,
        FT_DEVICE_NOT_FOUND = 2,
        FT_DEVICE_NOT_OPENED = 3,
        FT_IO_ERROR = 4,
        FT_INSUFFICIENT_RESOURCES = 5,
        FT_INVALID_PARAMETER = 6,
        FT_INVALID_BAUD_RATE = 7,
        FT_DEVICE_NOT_OPENED_FOR_ERASE = 8,
        FT_DEVICE_NOT_OPENED_FOR_WRITE = 9,
        FT_FAILED_TO_WRITE_DEVICE = 10,
        FT_EEPROM_READ_FAILED = 11,
        FT_EEPROM_WRITE_FAILED = 12,
        FT_EEPROM_ERASE_FAILED = 13,
        FT_EEPROM_NOT_PRESENT = 14,
        FT_EEPROM_NOT_PROGRAMMED = 15,
        FT_INVALID_ARGS = 16,
        FT_NOT_SUPPORTED = 17,
        FT_OTHER_ERROR = 18,
        FT_DEVICE_LIST_NOT_READY = 19
    }

    [Flags]
    internal enum FT_OPEN_LIST_FLAGS : uint
    {
        FT_OPEN_BY_SERIAL_NUMBER = 0x00000001,
        FT_OPEN_BY_DESCRIPTION = 0x00000002,
        FT_OPEN_BY_LOCATION = 0x00000004,
        FT_LIST_NUMBER_ONLY = 0x80000000,
        FT_LIST_BY_INDEX = 0x40000000,
        FT_LIST_ALL = 0x20000000,
    }

    internal enum FT_DEVICE : uint
    {
        /// 
        /// FT232B or FT245B device
        /// 
        FT_DEVICE_BM = 0,
        /// 
        /// FT8U232AM or FT8U245AM device
        /// 
        FT_DEVICE_AM = 1,
        /// 
        /// FT8U100AX device
        /// 
        FT_DEVICE_100AX = 2,
        /// 
        /// Unknown device
        /// 
        FT_DEVICE_UNKNOWN = 3,
        /// 
        /// FT2232 device
        /// 
        FT_DEVICE_2232 = 4,
        /// 
        /// FT232R or FT245R device
        /// 
        FT_DEVICE_232R = 5,
        /// 
        /// FT2232H device
        /// 
        FT_DEVICE_2232H = 6,
        /// 
        /// FT4232H device
        /// 
        FT_DEVICE_4232H = 7,
        /// 
        /// FT232H device
        /// 
        FT_DEVICE_232H = 8,
        /// 
        /// FT232X device
        /// 
        FT_DEVICE_X_SERIES = 9
    }

    /// 
    /// Permitted bit mode values for FTDI devices.  For use with SetBitMode
    /// 
    [Flags]
    public enum FT_BIT_MODES : byte
    {
        /// 
        /// Reset bit mode
        /// 
        FT_BIT_MODE_RESET = 0x00,
        /// 
        /// Asynchronous bit-bang mode
        /// 
        FT_BIT_MODE_ASYNC_BITBANG = 0x01,
        /// 
        /// MPSSE bit mode - only available on FT2232, FT2232H, FT4232H and FT232H
        /// 
        FT_BIT_MODE_MPSSE = 0x02,
        /// 
        /// Synchronous bit-bang mode
        /// 
        FT_BIT_MODE_SYNC_BITBANG = 0x04,
        /// 
        /// MCU host bus emulation mode - only available on FT2232, FT2232H, FT4232H and FT232H
        /// 
        FT_BIT_MODE_MCU_HOST = 0x08,
        /// 
        /// Fast opto-isolated serial mode - only available on FT2232, FT2232H, FT4232H and FT232H
        /// 
        FT_BIT_MODE_FAST_SERIAL = 0x10,
        /// 
        /// CBUS bit-bang mode - only available on FT232R and FT232H
        /// 
        FT_BIT_MODE_CBUS_BITBANG = 0x20,
        /// 
        /// Single channel synchronous 245 FIFO mode - only available on FT2232H channel A and FT232H
        /// 
        FT_BIT_MODE_SYNC_FIFO = 0x40
    }

    public struct FTDeviceInfo
    {
        internal FT_DEVICE DeviceType;
        internal uint Id;
        internal string SerialNumber;
        internal string Description;
    }

    [SuppressUnmanagedCodeSecurity()]
    internal static class Ftd2xxNative
    {
        [DllImport("ftd2xx.dll", EntryPoint = "FT_GetLibraryVersion")]
        internal static extern FT_STATUS FT_GetLibraryVersion(ref uint lpdwLibraryVersion);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_ListDevices")]
        internal static extern FT_STATUS FT_ListDevices_FT_LIST_NUMBER_ONLY(ref uint arg1, uint arg2 = 0, FT_OPEN_LIST_FLAGS flags = FT_OPEN_LIST_FLAGS.FT_LIST_NUMBER_ONLY);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_ListDevices")]
        internal static extern FT_STATUS FT_ListDevices_GET_SERIAL_NUMBER(UIntPtr deviceNumber, byte[] buffer, FT_OPEN_LIST_FLAGS flags = FT_OPEN_LIST_FLAGS.FT_LIST_BY_INDEX | FT_OPEN_LIST_FLAGS.FT_OPEN_BY_SERIAL_NUMBER);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_ListDevices")]
        internal static extern FT_STATUS FT_ListDevices_GET_ALL(IntPtr buffer, out uint devicesCount, FT_OPEN_LIST_FLAGS flags = FT_OPEN_LIST_FLAGS.FT_LIST_ALL | FT_OPEN_LIST_FLAGS.FT_OPEN_BY_DESCRIPTION);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_ListDevices")]
        internal static extern FT_STATUS FT_ListDevices_GET_LOCATIONS(uint[] result, out uint devicesCount, FT_OPEN_LIST_FLAGS flags = FT_OPEN_LIST_FLAGS.FT_LIST_ALL | FT_OPEN_LIST_FLAGS.FT_OPEN_BY_LOCATION);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_Open")]
        internal static extern FT_STATUS FT_Open(uint deviceNumber, out Ftd2xxHandle pHandle);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_Close")]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        internal static extern FT_STATUS FT_Close(IntPtr ftHandle);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_GetDeviceInfo", CharSet = CharSet.Ansi)]
        internal static extern FT_STATUS FT_GetDeviceInfo(Ftd2xxHandle ftHandle, out FT_DEVICE deviceType, out uint id, StringBuilder serialNumber, StringBuilder description, ref byte dummyByte);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_ResetDevice")]
        internal static extern FT_STATUS FT_ResetDevice(Ftd2xxHandle ftHandle);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_GetQueueStatus")]
        internal static extern FT_STATUS FT_GetQueueStatus(Ftd2xxHandle ftHandle, out uint bytesToRead);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_Read")]
        internal static extern FT_STATUS FT_Read(Ftd2xxHandle ftHandle, byte[] buffer, uint bytesToRead, out uint bytesReturned);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_SetUSBParameters")]
        internal static extern FT_STATUS FT_SetUSBParameters(Ftd2xxHandle ftHandle, uint inTransferSize, uint outTransferSize);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_SetChars", CharSet = CharSet.Ansi)]
        internal static extern FT_STATUS FT_SetChars(Ftd2xxHandle ftHandle, char eventChar, byte eventCharEnable, char errorChar, byte errorCharEnable);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_SetTimeouts")]
        internal static extern FT_STATUS FT_SetTimeouts(Ftd2xxHandle ftHandle, uint readTimeout, uint writeTimeout);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_SetLatencyTimer")]
        internal static extern FT_STATUS FT_SetLatencyTimer(Ftd2xxHandle ftHandle, byte latency);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_SetBitMode")]
        internal static extern FT_STATUS FT_SetBitMode(Ftd2xxHandle ftHandle, byte mask, FT_BIT_MODES bitMode);
        [DllImport("ftd2xx.dll", EntryPoint = "FT_Write")]
        internal static extern FT_STATUS FT_Write(Ftd2xxHandle ftHandle, byte[] buffer, uint bytesToWrite, out uint bytesWritten);
    }

    /// 
    /// http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle(v=vs.110).aspx
    /// 
    [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
    [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
    internal class Ftd2xxHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        // Create a SafeHandle, informing the base class that this SafeHandle instance "owns" the handle,
        // and therefore SafeHandle should call our ReleaseHandle method when the SafeHandle is no longer in use. 
        private Ftd2xxHandle()
            : base(true)
        {
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        override protected bool ReleaseHandle()
        {
            // Here, we must obey all rules for constrained execution regions. 
            return Ftd2xxNative.FT_Close(handle) == FT_STATUS.FT_OK;
            // If ReleaseHandle failed, it can be reported via the "releaseHandleFailed" managed debugging assistant (MDA).  This
            // MDA is disabled by default, but can be enabled in a debugger or during testing to diagnose handle corruption problems. 
            // We do not throw an exception because most code could not recover from the problem.
        }
    }

    // The JtagClient class is a sample class that accesses an operating system resource and implements IDisposable. 
    // This is useful to show the types of transformation required to make your resource wrapping classes more resilient. 
    // Note the Dispose and Finalize implementations. Consider the similarity to System.IO.FileStream.
    public class JtagClient : IDisposable
    {
        #region safe handle
        // _handle is set to null to indicate disposal of this instance. 
        private Ftd2xxHandle handle;

        public JtagClient(uint deviceNumber)
        {
            // Open a device, and save its handle in this.handle. 
            // Note that the most optimized code turns into two processor instructions: 
            // 1) a call, and 
            // 2) moving the return value into the this.handle field.  
            // With SafeHandle, the CLR's platform invoke marshaling layer will store the handle into the SafeHandle object in an atomic fashion. 
            // There is still the problem that the SafeHandle object may not be stored in _handle, but the real operating system handle 
            // value has been safely stored in a critical finalizable object, ensuring against leaking the handle even if there is an asynchronous exception.
            Ftd2xxHandle tmpHandle;
            FT_STATUS ftStatus = Ftd2xxNative.FT_Open(deviceNumber, out tmpHandle);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);

            // An async exception here will cause us to run our finalizer with a null _handle, 
            // but Ftd2xxHandle's ReleaseHandle code will be invoked to free the handle. 

            // This call to Sleep, run from the fault injection code in Main, will help trigger a race. 
            // But it will not cause a handle leak because the handle is already stored in a SafeHandle instance. 
            // Critical finalization then guarantees that freeing the handle, even during an unexpected AppDomain unload.
            // Thread.Sleep(500);
            handle = tmpHandle;  // Makes this.handle point to a critical finalizable object. 

            // Determine if handle has positive/non-zero successfull value 
            if (handle.IsInvalid)  // Is the handle useable ? 
                throw new Ftd2xxException(ftStatus);

            // Jtag requires MPSSE support
            FTDeviceInfo deviceInfo = GetDeviceInfo();
            if (!(deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_232H ||
                  deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232 ||
                  deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232H ||
                  deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_4232H))
                throw new Ftd2xxException(deviceInfo.DeviceType);
        }

        // Follow the Dispose pattern - public nonvirtual.
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // No finalizer is needed. 
        // The finalizer on SafeHandle will clean up the Ftd2xxHandle instance, if it hasn't already been disposed. 
        // However, there may be a need for a subclass to introduce a finalizer, so Dispose is properly implemented here.
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        protected virtual void Dispose(bool disposing)
        {
            // Note there are three interesting states here: 
            // 1) CreateFile failed, _handle contains an invalid handle 
            // 2) We called Dispose already, _handle is closed. 
            // 3) _handle is null, due to an async exception before calling FT_Open. 
            // Note that the finalizer runs if the constructor fails. 
            if (handle != null && !handle.IsInvalid)
            {
                // Free the handle
                handle.Dispose();
            }
            // SafeHandle records the fact that we've called Dispose.
        }
        #endregion safe handle

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public static uint GetLibraryVersion()
        {
            uint result = 0;
            FT_STATUS ftStatus = Ftd2xxNative.FT_GetLibraryVersion(ref result);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            return result;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public static uint GetNumberOfConnectedDevices()
        {
            uint result = 0;
            FT_STATUS ftStatus = Ftd2xxNative.FT_ListDevices_FT_LIST_NUMBER_ONLY(ref result);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            return result;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public static string GetSerialNumberOfDevice(uint deviceNumber)
        {
            byte[] result = new byte[64];
            FT_STATUS ftStatus = Ftd2xxNative.FT_ListDevices_GET_SERIAL_NUMBER((UIntPtr)deviceNumber, result);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            return Encoding.ASCII.GetString(result, 0, Array.IndexOf(result, 0));
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public static string[] GetDevicesDescriptions()
        {
            const int TOTAL_DESCRIPTIONS = 16 + 1; // One last entry in array of arrays must be null
            const int DESCRIPTION_SIZE64 = 64;
            uint devicesCount;
            // IntPtr for array of native pointers
            IntPtr ipa = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(void*)) * TOTAL_DESCRIPTIONS);
            try
            {
                bool pointersAre8bytes = Marshal.SizeOf(typeof(void*)) == Marshal.SizeOf(typeof(long));
                FT_STATUS ftStatus;
                long[] pointersAs64 = new long[TOTAL_DESCRIPTIONS];
                int[] pointersAs32 = new int[TOTAL_DESCRIPTIONS];
                if (pointersAre8bytes)
                {
                    for (int i = 0; i < pointersAs64.Length; i++)
                    {
                        pointersAs64[i] = i == pointersAs64.Length - 1 ?
                            IntPtr.Zero.ToInt64() :
                            Marshal.AllocHGlobal(DESCRIPTION_SIZE64).ToInt64();
                    }
                    // Copy the array to unmanaged memory.
                    Marshal.Copy(pointersAs64, 0, ipa, pointersAs64.Length);
                    ftStatus = Ftd2xxNative.FT_ListDevices_GET_ALL(ipa, out devicesCount);
                }
                else
                {
                    for (int i = 0; i < pointersAs32.Length; i++)
                    {
                        pointersAs32[i] = i == pointersAs32.Length - 1 ?
                            IntPtr.Zero.ToInt32() :
                            Marshal.AllocHGlobal(DESCRIPTION_SIZE64).ToInt32();
                    }
                    // Copy the array to unmanaged memory.
                    Marshal.Copy(pointersAs32, 0, ipa, pointersAs32.Length);
                    ftStatus = Ftd2xxNative.FT_ListDevices_GET_ALL(ipa, out devicesCount);
                }
                if (ftStatus != FT_STATUS.FT_OK)
                {
                    throw new Ftd2xxException(ftStatus);
                }
                string[] result = new string[devicesCount];
                for (int i = 0; i < TOTAL_DESCRIPTIONS; i++)
                {
                    IntPtr ip = new IntPtr(pointersAre8bytes ? pointersAs64[i] : pointersAs32[i]);
                    if (i < devicesCount)
                    {
                        byte[] buffer = new byte[DESCRIPTION_SIZE64];
                        // Copy the unmanaged array back to managed array.
                        Marshal.Copy(ip, buffer, 0, DESCRIPTION_SIZE64);
                        result[i] = Encoding.ASCII.GetString(buffer, 0, Array.IndexOf(buffer, 0));
                    }
                    if (i != TOTAL_DESCRIPTIONS - 1)
                    {
                        Marshal.FreeHGlobal(ip);
                    }
                }
                return result;
            }
            finally
            {
                // Free the unmanaged memory.
                Marshal.FreeHGlobal(ipa);
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public static uint[] GetDevicesLocations()
        {
            const int TOTAL_LOCATIONS = 256;
            uint devicesCount;
            uint[] result = new uint[TOTAL_LOCATIONS];
            FT_STATUS ftStatus = Ftd2xxNative.FT_ListDevices_GET_LOCATIONS(result, out devicesCount);
            if (ftStatus != FT_STATUS.FT_OK)
            {
                throw new Ftd2xxException(ftStatus);
            }
            Array.Resize(ref result, (int)devicesCount);
            return result;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public FTDeviceInfo GetDeviceInfo()
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            //internal static extern FT_STATUS FT_GetDeviceInfo(Ftd2xxHandle ftHandle, ref int itype, int id, string serialNumber, string description, ref byte pvDummy);
            FT_DEVICE deviceType;
            uint id;
            byte dummyByte = 0;
            StringBuilder serialNumber = new StringBuilder(16);
            StringBuilder description = new StringBuilder(64);
            FT_STATUS ftStatus = Ftd2xxNative.FT_GetDeviceInfo(handle, out deviceType, out id, serialNumber, description, ref dummyByte);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            return new FTDeviceInfo()
            {
                DeviceType = deviceType,
                Id = id,
                SerialNumber = serialNumber.ToString(),
                Description = description.ToString()
            };
        }

        /// 
        /// Puts the device in a mode other than the default UART or FIFO mode.
        /// 
        /// Sets up which bits are inputs and which are outputs.  A bit value of 0 sets the corresponding pin to an input, a bit value of 1 sets the corresponding pin to an output.
        /// In the case of CBUS Bit Bang, the upper nibble of this value controls which pins are inputs and outputs, while the lower nibble controls which of the outputs are high and low.
        ///  
        /// For FT232H devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG, FT_BIT_MODE_MPSSE, FT_BIT_MODE_SYNC_BITBANG, FT_BIT_MODE_CBUS_BITBANG, FT_BIT_MODE_MCU_HOST, FT_BIT_MODE_FAST_SERIAL, FT_BIT_MODE_SYNC_FIFO.
        /// For FT2232H devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG, FT_BIT_MODE_MPSSE, FT_BIT_MODE_SYNC_BITBANG, FT_BIT_MODE_MCU_HOST, FT_BIT_MODE_FAST_SERIAL, FT_BIT_MODE_SYNC_FIFO.
        /// For FT4232H devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG, FT_BIT_MODE_MPSSE, FT_BIT_MODE_SYNC_BITBANG.
        /// For FT232R devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG, FT_BIT_MODE_SYNC_BITBANG, FT_BIT_MODE_CBUS_BITBANG.
        /// For FT245R devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG, FT_BIT_MODE_SYNC_BITBANG.
        /// For FT2232 devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG, FT_BIT_MODE_MPSSE, FT_BIT_MODE_SYNC_BITBANG, FT_BIT_MODE_MCU_HOST, FT_BIT_MODE_FAST_SERIAL.
        /// For FT232B and FT245B devices, valid values are FT_BIT_MODE_RESET, FT_BIT_MODE_ASYNC_BITBANG.
        /// Thrown when the current device does not support the requested bit mode.
        public void SetBitMode(byte mask, FT_BIT_MODES bitModes)
        {
            // If the DLL hasn't been loaded, just return here
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");

            FTDeviceInfo deviceInfo = GetDeviceInfo();
            // Get the interface identifier, like "A", "B", etc.
            string interfaceIdentifier = String.Empty;
            if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232) | (deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232H) | (deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_4232H))
            {
                interfaceIdentifier = deviceInfo.Description.Substring(deviceInfo.Description.Length - 1);
            }

            if (deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_AM)
            {
                // Set Bit Mode does not apply to FT8U232AM, FT8U245AM or FT8U100AX devices
                throw new Ftd2xxException(bitModes);
            }
            else if (deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_100AX)
            {
                // Set Bit Mode does not apply to FT8U232AM, FT8U245AM or FT8U100AX devices
                throw new Ftd2xxException(bitModes);
            }
            else if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_BM) && (bitModes != FT_BIT_MODES.FT_BIT_MODE_RESET))
            {
                if ((bitModes & (FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG)) == 0)
                {
                    throw new Ftd2xxException(bitModes);
                }
            }
            else if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232) && (bitModes != FT_BIT_MODES.FT_BIT_MODE_RESET))
            {
                if ((bitModes & (FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_MPSSE | FT_BIT_MODES.FT_BIT_MODE_SYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_MCU_HOST | FT_BIT_MODES.FT_BIT_MODE_FAST_SERIAL)) == 0)
                {
                    throw new Ftd2xxException(bitModes);
                }
                if ((bitModes == FT_BIT_MODES.FT_BIT_MODE_MPSSE) & (interfaceIdentifier != "A"))
                {
                    // MPSSE mode is only available on channel A
                    throw new Ftd2xxException(bitModes);
                }
            }
            else if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_232R) && (bitModes != FT_BIT_MODES.FT_BIT_MODE_RESET))
            {
                if ((bitModes & (FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_SYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_CBUS_BITBANG)) == 0)
                {
                    throw new Ftd2xxException(bitModes);
                }
            }
            else if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232H) && (bitModes != FT_BIT_MODES.FT_BIT_MODE_RESET))
            {
                if ((bitModes & (FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_MPSSE | FT_BIT_MODES.FT_BIT_MODE_SYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_MCU_HOST | FT_BIT_MODES.FT_BIT_MODE_FAST_SERIAL | FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO)) == 0)
                {
                    throw new Ftd2xxException(bitModes);
                }
                if (((bitModes == FT_BIT_MODES.FT_BIT_MODE_MCU_HOST) | (bitModes == FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO)) & (interfaceIdentifier != "A"))
                {
                    // MCU Host Emulation and Single channel synchronous 245 FIFO mode is only available on channel A
                    throw new Ftd2xxException(bitModes);
                }
            }
            else if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_4232H) && (bitModes != FT_BIT_MODES.FT_BIT_MODE_RESET))
            {
                if ((bitModes & (FT_BIT_MODES.FT_BIT_MODE_ASYNC_BITBANG | FT_BIT_MODES.FT_BIT_MODE_MPSSE | FT_BIT_MODES.FT_BIT_MODE_SYNC_BITBANG)) == 0)
                {
                    throw new Ftd2xxException(bitModes);
                }
                if ((bitModes == FT_BIT_MODES.FT_BIT_MODE_MPSSE) & ((interfaceIdentifier != "A") & (interfaceIdentifier != "B")))
                {
                    // MPSSE mode is only available on channel A and B
                    throw new Ftd2xxException(bitModes);
                }
            }
            else if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_232H) && (bitModes != FT_BIT_MODES.FT_BIT_MODE_RESET))
            {
                // FT232H supports all current bit modes!
                if (bitModes > FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO)
                {
                    throw new Ftd2xxException(bitModes);
                }
            }

            // Requested bit mode is supported
            // Note FT_BIT_MODES.FT_BIT_MODE_RESET falls through to here - no bits set so cannot check for AND
            FT_STATUS ftStatus = Ftd2xxNative.FT_SetBitMode(this.handle, mask, bitModes);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void ResetDevice()
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            FT_STATUS ftStatus = Ftd2xxNative.FT_ResetDevice(handle);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public uint GetQueueStatus()
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            uint result = 0;
            FT_STATUS ftStatus = Ftd2xxNative.FT_GetQueueStatus(handle, out result);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            return result;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public byte[] Read(uint bytesToRead)
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            byte[] result = new byte[bytesToRead];
            uint bytesRead = 0;
            FT_STATUS ftStatus = Ftd2xxNative.FT_Read(handle, result, bytesToRead, out bytesRead);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            if (bytesRead != bytesToRead)
            {
                throw new Ftd2xxException(bytesToRead, bytesRead);
                //Array.Resize(ref result, (int)numRead);
            }
            return result;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void Write(byte[] data)
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            uint numWritten = 0;
            FT_STATUS ftStatus = Ftd2xxNative.FT_Write(handle, data, (uint)data.Length, out numWritten);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
            if (data.Length != numWritten)
            {
                throw new Ftd2xxException((uint)data.Length, numWritten);
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void SetUSBParameters(uint inTransferSize = 0xFFFF, uint outTransferSize = 0xFFFF)
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            FT_STATUS ftStatus = Ftd2xxNative.FT_SetUSBParameters(handle, inTransferSize, outTransferSize);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void SetChars(char eventChar = (char) 0, bool enableEventChar = false, char errorChar = (char) 0, bool enableErrorChar = false)
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            FT_STATUS ftStatus = Ftd2xxNative.FT_SetChars(handle, eventChar, Convert.ToByte(enableEventChar), errorChar, Convert.ToByte(enableErrorChar));
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void SetTimeouts(uint readTimeout = 0, uint writeTimeout = 5000)
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            FT_STATUS ftStatus = Ftd2xxNative.FT_SetTimeouts(handle, readTimeout, writeTimeout);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void SetLatencyTimer(byte latency = 16)
        {
            if (handle.IsInvalid || handle.IsClosed)  // Is the handle useable ? 
                throw new ObjectDisposedException("JtagClient is closed");
            FTDeviceInfo deviceInfo = GetDeviceInfo();
            if ((deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_BM) || (deviceInfo.DeviceType == FT_DEVICE.FT_DEVICE_2232))
            {
                // Do not allow latency of 1ms or 0ms for older devices
                // since this can cause problems/lock up due to buffering mechanism
                if (latency < 2) latency = 2;
            }
            FT_STATUS ftStatus = Ftd2xxNative.FT_SetLatencyTimer(handle, latency);
            if (ftStatus != FT_STATUS.FT_OK)
                throw new Ftd2xxException(ftStatus);
        }

    }

    public class Ftd2xxException : Exception
    {
        internal Ftd2xxException(FT_BIT_MODES bitMode)
            : base(bitMode.ToString() + ". Invalid bit mode for given deviceType and interface identifier.")
        {
        }

        internal Ftd2xxException(FT_DEVICE ftDeviceType)
            : base(ftDeviceType.ToString() + ". Invalid device type.")
        {
        }

        internal Ftd2xxException(FT_STATUS ftStatus)
            : base(ftStatus.ToString())
        {
        }

        internal Ftd2xxException(uint bytesToWrite, uint bytesWritten)
            : base("Incomplete read or write. Expected " + bytesToWrite + " bytes, read or wrote only " + bytesWritten + " bytes.")
        {
        }
    }