/***************************************************************************
 * Copyright (c) 2009 Digi International Inc., All Rights Reserved
 * 
 * This software contains proprietary and confidential information of Digi
 * International Inc.  By accepting transfer of this copy, Recipient agrees
 * to retain this software in confidence, to prevent disclosure to others,
 * and to make no use of this software other than that for which it was
 * delivered.  This is an unpublished copyrighted work of Digi International
 * Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 * prohibited.
 * 
 * Restricted Rights Legend
 *
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 * Technical Data and Computer Software clause at DFARS 252.227-7031 or
 * subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 * Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 ***************************************************************************
 *
 * @Descr: I2C Class
 *
 * @History:
 *	10/15/09:	aramos      initial version.
 *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Digi.Common;
using Digi.Imported;

/*! \package Digi.Drivers
 */
/// <summary>
/// The Digi.Drivers includes the classes that implement the different Digi Drivers (GPIO, I2C, SPI...)
/// </summary>
namespace Digi.Drivers
{
    /*! \class I2C
     */
    /// <summary>
    /// Inter-Integrated Circuit class
    /// This class manage the I2C driver of ConnectCore 9C/Wi-9C/9P 9360 and ConnectCore 9M/Wi-9M 2443 platforms
    /// </summary>
    public abstract class I2C
    {
        
        /*! \fn public I2C()
         */
        /// <summary>
        /// Constructor
        /// </summary>
        public I2C()
        {
        }

        /*! \fn public bool Read(ref byte[] Buffer, int count)
         */
        /// <summary>
        /// Method that reads from I2C
        /// </summary>
        /// <param name="Buffer">Buffer to store the data read</param>
        /// <param name="count">Number of bytes to read</param>
        /// <returns>True indicates success, false indicates failure</returns>
        public abstract bool Read(ref byte[] Buffer, int count);

        /*! \fn public bool Write(ref byte[] Buffer, int count)
         */
        /// <summary>
        /// Method that writes into I2C
        /// </summary>
        /// <param name="Buffer">Buffer to write</param>
        /// <param name="count">Number of bytes to write</param>
        /// <returns>True indicates success, false indicates failure</returns>
        public abstract bool Write(ref byte[] Buffer, int count);

    }

    /*! \class I2CiMX51
     */
    /// <summary>
    /// Inter-Integrated Circuit class
    /// This class manage the I2C driver of ConnectCore imX51 platform
    /// </summary>
    public class I2CiMX51 : I2C
    {
        //Handle
        private static DriverIO hI2C = new DriverIO();

        //Internal I2C Messages
        private I2CPacket I2CP;
        private I2C_PACKET I2C_P  = new I2C_PACKET();

        /*! \enum RWOperation
             */
        /// <summary>
        /// Operation set in the I2C
        /// </summary>        
        /// <example>
        /// <code>
        /// // Use of the constants
        /// RWOperation.Write
        /// RWOperation.Read</code>
        /// </example>  
        public enum RWOperation : byte
        {
            Write = 1,
            Read = 2
        }

        /*! \enum I2CPort
             */
        /// <summary>
        /// I2C Ports
        /// </summary>        
        /// <example>
        /// <code>
        /// // Use of the constants
        /// I2CPort.I2C1
        /// I2CPort.I2C2
        /// I2CPort.I2C3</code>
        /// </example>  
        public enum I2CPort : uint
        {
            I2C1 = 1,
            I2C2 = 2,
            I2C3 = 3
        }

        /*! \enum I2CResult
             */
        /// <summary>
        /// I2C Results
        /// </summary>        
        /// <example>
        /// <code>
        /// // Use of the constants
        /// I2CResult.NoError
        /// I2CResult.ErrorMutexCreationFailed
        /// I2CResult.ErrorPhysicalVirtualMappingFailed
        /// I2CResult.ErrorEventCreationFailed
        /// I2CResult.ErrorIRQSystemInterruptIDMappingFailed
        /// I2CResult.ErrorInterruptInitializationFailed
        /// I2CResult.ErrorWorkerThreadFailed
        /// I2CResult.ErrorNoAcknowledgeIssued
        /// I2CResult.ErrorNullBuffer
        /// I2CResult.ErrorInvalidBufferSize
        /// I2CResult.ErrorNulllpiResultField
        /// I2CResult.ErrorCRMOperationFailure
        /// I2CResult.ErrorI2CTimeout
        /// I2CResult.ErrorI2CArbitrationLost
        /// I2CResult.ErrorI2CGotoAnUndefined</code>
        /// </example>  
        public enum I2CResult : int
        {
            NoError = 0,
            ErrorMutexCreationFailed = -1,
            ErrorPhysicalVirtualMappingFailed = -2,
            ErrorEventCreationFailed = -3,
            ErrorIRQSystemInterruptIDMappingFailed = -4,
            ErrorInterruptInitializationFailed = -5,
            ErrorWorkerThreadFailed = -6,
            ErrorNoAcknowledgeIssued = -7,
            ErrorNullBuffer = -8,
            ErrorInvalidBufferSize = -9,
            ErrorNulllpiResultField = -10,
            ErrorCRMOperationFailure = -11,
            ErrorI2CTimeout = -12,
            ErrorI2CArbitrationLost = -13,
            ErrorI2CGotoAnUndefined = -14
        }

        /// <summary>
        /// Internal structure, not accesible to the user
        /// </summary>
        private struct I2CPacket
        {
            public byte byAddr;
            public byte byRW;
            public byte[] pbyBuf;
            public uint wLen;
            public int lpiResult;
        }

        //I2C IOCTLs
        private static uint I2C_IOCTL_SET_SLAVE_MODE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3000, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_SET_MASTER_MODE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3001, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_IS_MASTER
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3002, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_IS_SLAVE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3003, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_GET_CLOCK_RATE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3004, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_SET_CLOCK_RATE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3005, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_SET_FREQUENCY
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3006, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_SET_SELF_ADDR
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3007, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_GET_SELF_ADDR
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3008, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_TRANSFER
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3009, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_RESET
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3010, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_ENABLE_SLAVE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3011, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_DISABLE_SLAVE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3012, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_SET_SLAVE_TXT
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3013, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_GET_SLAVE_TXT
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3014, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_SET_SLAVESIZE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3015, MethodType.Buffered, AccessType.Any);
            }
        }
        private static uint I2C_IOCTL_GET_SLAVESIZE
        {
            get
            {
                return WINIOCTL_H.CTL_CODE(DeviceType.BusExtender, 3016, MethodType.Buffered, AccessType.Any);
            }
        }

        /*! \fn public I2C()
         */
        /// <summary>
        /// Constructor
        /// </summary>
        public I2CiMX51(I2CPort I2Cnumber)
        {
            I2CPacket I2CM_intern = I2CP;
            string I2Cdevice;

            switch ((int)I2Cnumber)
            {
                case 1: I2Cdevice = "I2C1:";
                    break;
                case 2: I2Cdevice = "I2C2:";
                    break;
                case 3: I2Cdevice = "I2C3:";
                    break;
                default:
                    throw new Exception("[I2CiMX51]Invalid I2C device");
            }
            if (hI2C.FileHandle == (IntPtr)(-1))
            {
                if (!(hI2C.CreateFile(I2Cdevice, GenericAccessRights.ReadWrite, 0, CreationDisposition.OpenExisting, 0)))
                    throw new Exception("[I2C]CreateFile failed");
            }
        }

        //Private function that converts a I2CMessage structure in a byte array
        private byte[] ConvertI2CPtoBuffer(I2CPacket i2cP)
        {
            I2C_P.byAddr = i2cP.byAddr;
            I2C_P.byRW = i2cP.byRW;
            I2C_P.pbyBuf = i2cP.pbyBuf;
            I2C_P.wLen = i2cP.wLen;
            I2C_P.lpiResult = i2cP.lpiResult;

            return I2C_P.GetBytes();
        }

        //Private function that converts a byte array in a I2CMessage structure
        private I2CPacket ConvertBuffertoI2CP(byte[] buffer)
        {
            I2CPacket I2C_intern = new I2CPacket();
            
            I2C_P.SetBytes(buffer);
            I2C_intern.byAddr = I2C_P.byAddr;
            I2C_intern.byRW = I2C_P.byRW;
            I2C_intern.pbyBuf = I2C_P.pbyBuf;
            I2C_intern.wLen = I2C_P.wLen;
            I2C_intern.lpiResult = I2C_P.lpiResult;
            return I2C_intern;
        }

        /*! \property byte Addr
             */
        /// <summary>
        /// Address property
        /// </summary>    
        /// <value>Get/Set Address property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.byAddr = 0x12; 
        /// byte Value = myi2c.byAddr;</code>
        /// </example> 
        public byte Addr
        {
            get
            {
                return (I2CP.byAddr);
            }
            set
            {
                I2CP.byAddr = value;
            }
        }


        /*! \property RWOperation RW
             */
        /// <summary>
        /// Read/Write operation property
        /// </summary>    
        /// <value>Get/Set Read/Write operation property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.RW = 0x0; 
        /// RWOperation Value = myi2c.RW;</code>
        /// </example> 
        public RWOperation RW
        {
            get
            {
                return ((RWOperation)I2CP.byRW);
            }
            set
            {
                I2CP.byRW = (byte)value;
            }
        }

        /*! \property uint Length
             */
        /// <summary>
        /// Length property
        /// </summary>    
        /// <value>Get/Set Length property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.Length = 1; 
        /// uint Value = myi2c.Length;</code>
        /// </example> 
        public uint Length
        {
            get
            {
                return (I2CP.wLen);
            }
            set
            {
                I2CP.wLen = value;
            }
        }

        /*! \property int Result
             */
        /// <summary>
        /// Result property
        /// </summary>    
        /// <value>Get the result after an operation (int)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// int Value = myi2c.Result;</code>
        /// </example> 
        public int Result
        {
            get
            {
                return (I2CP.lpiResult);
            }
        }

        /*! \fn public bool SetSlaveMode()
         */
        /// <summary>
        /// Set the I2C device in slave mode
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool SetSlaveMode()
        {
            byte[] buffer = null;
            if ((hI2C.DeviceIOControl(I2C_IOCTL_SET_SLAVE_MODE, ref buffer, 0, ref buffer, 0)) == -1)
                return false;
            return true;
        }

        /*! \fn public bool SetMasterMode()
        */
        /// <summary>
        /// set the I2C device in master mode
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool SetMasterMode()
        {
            byte[] buffer = null;
            if ((hI2C.DeviceIOControl(I2C_IOCTL_SET_MASTER_MODE, ref buffer, 0, ref buffer, 0)) == -1)
                return false;
            return true;
        }

        /*! \fn public bool IsMaster()
        */
        /// <summary>
        /// determines whether the I2C is currently in Master mode
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool IsMaster(ref bool ismaster)
        {
            byte[] buffer = new byte[1];
            if ((hI2C.DeviceIOControl(I2C_IOCTL_IS_MASTER, ref buffer, 0, ref buffer, sizeof(bool))) == -1)
                return false;
            if (buffer[0] == 1)
                ismaster = true;
            else
                ismaster = false;
            return true;
        }

        /*! \fn public bool IsSlave()
        */
        /// <summary>
        /// determines whether the I2C is currently in Slave mode
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool IsSlave(ref bool isslave)
        {
            byte[] buffer = new byte[1];
            if ((hI2C.DeviceIOControl(I2C_IOCTL_IS_SLAVE, ref buffer, 0, ref buffer, sizeof(bool))) == -1)
                return false;
            if (buffer[0] == 1)
                isslave = true;
            else
                isslave = false;
            return true;
        }

        /*! \property UInt16 ClockRate
             */
        /// <summary>
        ///  Get/Set the clock rate divisor. 
        ///  Note that the value is not the absolute peripheral clock frequency. The value get/set should be
        ///  compared against the I2C specifications to obtain the true frequency
        /// </summary>    
        /// <value>Get/Set Clock Rate property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.ClockRate = 0xFF; 
        /// UInt16 Value = myi2c.ClockRate;</code>
        /// </example> 
        public UInt16 ClockRate
        {
            get
            {
                short clockrate;
                byte[] buffer = new byte[2];
                if (hI2C.DeviceIOControl(I2C_IOCTL_GET_CLOCK_RATE, ref buffer, 0, ref buffer, buffer.Length) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_GET_CLOCK_RATE failed");
                clockrate = DigiMath.BuffToShort(buffer, 0, true);
                return ((UInt16)clockrate);
            }
            set
            {
                byte[] buffer = new byte[2];
                DigiMath.ShortToBuff(buffer, 0, (short)value, true);
                if (hI2C.DeviceIOControl(I2C_IOCTL_SET_CLOCK_RATE, ref buffer, buffer.Length, ref buffer, 0) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_SET_CLOCK_RATE failed");
            }

        }

        /*! \property uint Frequency
            */
        /// <summary>
        ///  Estimate and set the nearest clock rate acceptable for I2C device and initialize the I2C device to use the estimated clock rate divisor. 
        ///  If the estimated clock rate divisor index is required, please refer ClockRate property to determine the estimated index.
        /// </summary>    
        /// <value>Set Frequency property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.Frequency = 9600; 
        /// uint Value = myi2c.Frequency;</code>
        /// </example> 
        public uint Frequency
        {
            set
            {
                byte[] buffer = new byte[4];
                DigiMath.UintToBuff(buffer, 0, (uint)value, true);
                if (hI2C.DeviceIOControl(I2C_IOCTL_SET_FREQUENCY, ref buffer, buffer.Length, ref buffer, 0) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_SET_FREQUENCY failed");
            }

        }

        /*! \property byte SelfAddress
            */
        /// <summary>
        ///  Get/Set the address of the I2C device.
        ///  Note that this property will have no effect if the
        ///  I2C device is in Master mode</summary>    
        /// <value>Get/Set the Self Address  property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.SelfAddress = 0x10; 
        /// byte Value = myi2c.SelfAddress;</code>
        /// </example> 
        public byte SelfAddress
        {
            get
            {
                byte selfaddress;
                byte[] buffer = new byte[1];
                if (hI2C.DeviceIOControl(I2C_IOCTL_GET_SELF_ADDR, ref buffer, 0, ref buffer, buffer.Length) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_GET_SELF_ADDR failed");
                selfaddress = buffer[0];
                return (selfaddress);
            }
            set
            {
                byte[] buffer = new byte[1];
                buffer[0] = value;
                if (hI2C.DeviceIOControl(I2C_IOCTL_SET_SELF_ADDR, ref buffer, buffer.Length, ref buffer, 0) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_SET_SELF_ADDR failed");
            }

        }

        /*! \fn public bool Reset()
        */
        /// <summary>
        /// Perform a hardware reset. 
        /// Note that the I2C driver will still maintain all the current information of the device,
        /// which includes all the initialized addresses.
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool Reset()
        {
            byte[] buffer = null;
            if ((hI2C.DeviceIOControl(I2C_IOCTL_RESET, ref buffer, 0, ref buffer, 0)) == -1)
                return false;
            return true;
        }

        /*! \fn public bool EnableSlave()
        */
        /// <summary>
        /// Enable a I2C slave access from the bus. 
        /// Note that after the I2C slave interface enabled, I2C slave driver wait for master access all the time.
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool EnableSlave()
        {
            byte[] buffer = null;
            if ((hI2C.DeviceIOControl(I2C_IOCTL_ENABLE_SLAVE, ref buffer, 0, ref buffer, 0)) == -1)
                return false;
            return true;
        }

        /*! \fn public bool DisableSlave()
        */
        /// <summary>
        /// Disable I2C slave access from the bus. 
        /// Note that after the I2C slaveinterface disabled, I2C slave module can be turn off.
        /// </summary>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool DisableSlave()
        {
            byte[] buffer = null;
            if ((hI2C.DeviceIOControl(I2C_IOCTL_DISABLE_SLAVE, ref buffer, 0, ref buffer, 0)) == -1)
                return false;
            return true;
        }

        /*! \property uint SlaveSize
        */
        /// <summary>
        ///  Get/Set I2C slave interface buffer length. 
        ///  The max acceptable length is I2CSLAVEBUFSIZE (512), if input length is longer than I2CSLAVEBUFSIZE,
        ///  the operation fail, and origin buffer length not changed. Note that I2C slave driver
        /// directly return data to master from interface buffer. The interface buffer can
        /// be set at any time, even when I2C slave module has been turned off.</summary>    
        /// <value>Get/Set the slave size  property (uint)</value>
        /// <example>
        /// <code>
        /// //example of use
        /// myi2c.SlaveSize = 0x30; 
        /// uint Value = myi2c.SlaveSize;</code>
        /// </example> 
        public uint SlaveSize
        {
            get
            {
                uint slavesize;
                byte[] buffer = new byte[4];
                if (hI2C.DeviceIOControl(I2C_IOCTL_GET_SLAVESIZE, ref buffer, 0, ref buffer, buffer.Length) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_GET_SELF_ADDR failed");
                slavesize = DigiMath.BuffToUint(buffer, 0, true);
                return (slavesize);
            }
            set
            {
                byte[] buffer = new byte[4];
                DigiMath.UintToBuff(buffer, 0, (uint)value, true);
                if (hI2C.DeviceIOControl(I2C_IOCTL_SET_SLAVESIZE, ref buffer, buffer.Length, ref buffer, 0) == -1)
                    throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_SET_SELF_ADDR failed");
            }

        }

        /*! \fn public bool SetSlaveText(byte[] text)
         */
        /// <summary>
        /// Store I2C slave text to interface buffer. 
        /// Note that I2C slave driver directly return data to master from interface buffer. 
        /// The interface buffer can be accessed at any time, even when I2C slave module has been turned off.
        /// </summary>
        /// <param name="text">User buffer to set text in the interface buffer</param>
        /// <returns>Return TRUE or FALSE. If the result is TRUE, the operation is successful.</returns>
        public bool SetSlaveText(byte[] text)
        {
            if (hI2C.DeviceIOControl(I2C_IOCTL_SET_SLAVE_TXT, ref text, text.Length, ref text, 0) == -1)
                throw new Exception("[I2CiMX51]DeviceIOControl I2C_IOCTL_SET_SLAVETXT failed");
            return true;
        }

        /*! \fn public int GetSlaveText(ref byte[] text)
         */
        /// <summary>
        /// Return I2C slave interface buffer text. 
        /// Note that I2C slave driver directly return data to master from interface buffer. 
        /// The interface buffer can be accessed at any time, even when I2C slave module has been turned off.
        /// </summary>
        /// <param name="text">User buffer to store text returned from interface buffer</param>
        /// <returns>Actual data bytes returned</returns>
        public int GetSlaveText(ref byte[] text)
        {
            return (hI2C.DeviceIOControl(I2C_IOCTL_GET_SLAVE_TXT, ref text, 0, ref text, text.Length));
        }

        /*! \fn public bool Read(ref byte[] Buffer, int count)
         */
        /// <summary>
        /// Method that reads from I2C
        /// </summary>
        /// <param name="Buffer">Buffer to store the data read</param>
        /// <param name="count">Number of bytes to read</param>
        /// <returns>True indicates success, false indicates failure</returns>
        public override bool Read(ref byte[] Buffer, int count)
        {
            int iResult = -1234567;
            I2CPacket I2CP_intern = I2CP;
            if (count >= 512)
            {
                throw new Exception("[I2CiMX51]Buffer length bigger than 512bytes");
            }
            I2CP_intern.pbyBuf = Buffer;
            I2CP_intern.wLen = (uint)count;
            I2CP_intern.byRW = (byte)RWOperation.Read;
            I2CP_intern.lpiResult = iResult;

            byte[] buffer = ConvertI2CPtoBuffer(I2CP_intern);
            if ((hI2C.DeviceIOControl(I2C_IOCTL_TRANSFER, ref buffer, buffer.Length, ref buffer, 0)) == -1)
                return false;
            I2CP_intern = ConvertBuffertoI2CP(buffer);

            I2CP = I2CP_intern;
            Buffer = I2CP.pbyBuf;
            return (true);
        }

        /*! \fn public bool Write(ref byte[] Buffer, int count)
         */
        /// <summary>
        /// Method that writes into I2C
        /// </summary>
        /// <param name="Buffer">Buffer to write</param>
        /// <param name="count">Number of bytes to write</param>
        /// <returns>True indicates success, false indicates failure</returns>
        public override bool Write(ref byte[] Buffer, int count)
        {
            int iResult = -1234567;
            I2CPacket I2CP_intern = I2CP;
            if (count >= 512)
            {
                throw new Exception("[I2CiMX51]Buffer length bigger than 512bytes");
            }
            I2CP_intern.pbyBuf = Buffer;
            I2CP_intern.wLen = (uint)count;
            I2CP_intern.byRW = (byte)RWOperation.Write;
            I2CP_intern.lpiResult = iResult;

            byte[] buffer = ConvertI2CPtoBuffer(I2CP_intern);
            if ((hI2C.DeviceIOControl(I2C_IOCTL_TRANSFER, ref buffer, buffer.Length, ref buffer, 0)) == -1)
                return false;
            I2CP_intern = ConvertBuffertoI2CP(buffer);

            I2CP = I2CP_intern;
            Buffer = I2CP.pbyBuf;
            return (true);

        }

        /*! \class I2C_PACKET
        */
        /// <summary>
        /// private class I2C_MESSAGE not accesible for the user
        /// This class has been created to convert the I2CMessage into a byte array
        /// </summary>
        private class I2C_PACKET
        {
            private StructBuilder pBuffer;
            private StructBuilder pResult;
            private StructBuilder cStruct;
            private StructBuilder cStructTB;

            public I2C_PACKET()
            {
                cStruct = new StructBuilder(20);
                pBuffer = new StructBuilder(512);
                pResult = new StructBuilder(4);
                cStructTB = new StructBuilder(8);
            }

            // byte
            public byte byAddr
            {
                get
                {
                    byte[] data = new byte[1];
                    cStruct.Get(0, 1, ref data);
                    return (data[0]);
                }
                set
                {
                    byte[] data = new byte[1];
                    data[0] = value;
                    cStruct.Set(0, data);
                }

            }

            // byte
            public byte byRW
            {
                get
                {
                    byte[] data = new byte[1];
                    cStruct.Get(4, 1, ref data);
                    return (data[0]);
                }
                set
                {
                    byte[] data = new byte[1];
                    data[0] = value;
                    cStruct.Set(4, data);
                }

            }

            // pointer to the buffer
            public byte[] pbyBuf
            {
                get
                {
                    byte[] data = new byte[pBuffer.GetSize()];
                    pBuffer.Get(0, pBuffer.GetSize(), ref data);
                    return data;
                }
                set
                {
                    pBuffer.Set(0, value, value.Length);
                    cStruct.SetStruct(8, pBuffer);
                }
            }



            // uint
            public uint wLen
            {
                get
                {
                    byte[] data = new byte[4];
                    cStruct.Get(12, 4, ref data);
                    return DigiMath.BuffToUint(data, 0, true);
                }
                set
                {
                    byte[] data = new byte[4];
                    DigiMath.UintToBuff(data, 0, value, true);
                    cStruct.Set(12, data);
                }

            }

            // pointer to the Result
            public int lpiResult
            {
                get
                {
                    byte[] data = new byte[pResult.GetSize()];
                    pResult.Get(0, pResult.GetSize(), ref data);
                    return DigiMath.BuffToInt(data, 0, true);
                }
                set
                {
                    byte[] data = new byte[4];
                    DigiMath.IntToBuff(data, 0, value, true);
                    pResult.Set(0, data,sizeof(int));
                    cStruct.SetStruct(16, pResult);
                }
            }
            
            public byte[] GetBytes()
            {
                byte[] data = new byte[cStruct.GetSize()];
                cStruct.Get(0, ref data);
                byte[] dataP = new byte[16];
                //data trick
                dataP[0] = data[0];
                dataP[1] = data[4];
                dataP[2] = 0x00;
                dataP[3] = 0x00;
                dataP[4] = data[8];
                dataP[5] = data[9];
                dataP[6] = data[10];
                dataP[7] = data[11];
                dataP[8] = data[12];
                dataP[9] = data[13];
                dataP[10] = 0x00;
                dataP[11] = 0x00;
                dataP[12] = data[16];
                dataP[13] = data[17];
                dataP[14] = data[18];
                dataP[15] = data[19];

                cStruct.Set(0, dataP, 16);
                cStructTB.SetStruct(0, cStruct);
                byte[] dataNumPacket = new byte[4];
                DigiMath.UintToBuff(dataNumPacket, 0, 1, true);
                cStructTB.Set(4, dataNumPacket, dataNumPacket.Length);
                byte[] DataTB = new byte[cStructTB.GetSize()];
                cStructTB.Get(0, ref DataTB);
                return DataTB;
            }

            public void SetBytes(byte[] data)
            {
                cStructTB.Set(0, data);
                byte[] dataP = new byte[cStruct.GetSize()];
                cStruct.Get(0, ref dataP);
                //data trick
                byte[] data_np = new byte[20];
                data_np[0] = dataP[0];
                data_np[1] = 0x00;
                data_np[2] = 0x00;
                data_np[3] = 0x00;
                data_np[4] = dataP[1];
                data_np[5] = 0x00;
                data_np[6] = 0x00;
                data_np[7] = 0x00;
                data_np[8] = dataP[4];
                data_np[9] = dataP[5];
                data_np[10] = dataP[6];
                data_np[11] = dataP[7];
                data_np[12] = dataP[8];
                data_np[13] = dataP[9];
                data_np[14] = 0x00;
                data_np[15] = 0x00;
                data_np[16] = dataP[12];
                data_np[17] = dataP[13];
                data_np[18] = dataP[14];
                data_np[19] = dataP[15];
                cStruct.Set(0, data_np, data_np.Length);
            }            
        }
    }   
}
