//
// Copyright 2013-2014 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <uhd/transport/nirio/niriok_proxy_impl_v1.h>
#include <cstring>

// "push" and "pop" introduced in GCC 4.6; works with all clang
#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 5)
    #pragma GCC diagnostic push
#endif
#if defined(__clang__) || defined(__GNUC__)
    #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif

// CTL_CODE macro for non-win OSes
#ifndef UHD_PLATFORM_WIN32
    #define CTL_CODE(a,controlCode,b,c) (controlCode)
#endif

const uint32_t NIRIO_IOCTL_BASE = 0x800;

const uint32_t NIRIO_IOCTL_SYNCOP =
   CTL_CODE(FILE_DEVICE_UNKNOWN,
            NIRIO_IOCTL_BASE + 4,
            METHOD_OUT_DIRECT,
            FILE_READ_DATA | FILE_WRITE_DATA);
                                ///< The synchronous operation code. Note: We
                                /// must use METHOD_OUT_DIRECT on the syncOp()
                                /// IOCTL to ensure the contents of the output
                                /// block are available in the kernel.

const uint32_t NIRIO_IOCTL_GET_IFACE_NUM =
   CTL_CODE(FILE_DEVICE_UNKNOWN,
            NIRIO_IOCTL_BASE + 6,
            METHOD_BUFFERED,
            FILE_READ_DATA);    ///< Get the interface number for a device

const uint32_t NIRIO_IOCTL_GET_SESSION =
   CTL_CODE(FILE_DEVICE_UNKNOWN,
            NIRIO_IOCTL_BASE + 8,
            METHOD_BUFFERED,
            FILE_READ_ACCESS);  ///< Gets a previously opened session to a device

const uint32_t NIRIO_IOCTL_POST_OPEN =
   CTL_CODE(FILE_DEVICE_UNKNOWN,
            NIRIO_IOCTL_BASE + 9,
            METHOD_BUFFERED,
            FILE_READ_ACCESS);  ///< Called after opening a session

const uint32_t NIRIO_IOCTL_PRE_CLOSE =
   CTL_CODE(FILE_DEVICE_UNKNOWN,
            NIRIO_IOCTL_BASE + 10,
            METHOD_BUFFERED,
            FILE_READ_ACCESS);  ///< Called before closing a session

namespace uhd { namespace niusrprio
{
    // -------------------------------
    // Function Codes: defined as integers rather than enums because they
    // are going to be carried accross boundaries so size matters

    struct NIRIO_FUNC
    {
       static const uint32_t GET32             = 0x00000001;
       static const uint32_t SET32             = 0x00000002;
       static const uint32_t SET_DRIVER_CONFIG = 0x00000007;
       static const uint32_t FIFO              = 0x00000008;
       static const uint32_t IO                = 0x0000000A;
       static const uint32_t FIFO_STOP_ALL     = 0x0000000C;
       static const uint32_t ADD_RESOURCE      = 0x0000000D;
       static const uint32_t GET_STRING        = 0x0000000E;
       static const uint32_t SET_STRING        = 0x0000000F;
       static const uint32_t DOWNLOAD          = 0x00000013;
       static const uint32_t RESET             = 0x00000014;
    };

    struct NIRIO_RESOURCE
    {
       static const uint32_t INPUT_FIFO    = 0xD0000001;
       static const uint32_t OUTPUT_FIFO   = 0xD0000002;
    };

    struct NIRIO_FIFO
    {
       static const uint32_t CONFIGURE = 0x80000001;
       static const uint32_t START     = 0x80000002;
       static const uint32_t STOP      = 0x80000003;
       static const uint32_t READ      = 0x80000004;
       static const uint32_t WRITE     = 0x80000005;
       static const uint32_t WAIT      = 0x80000006;
       static const uint32_t GRANT     = 0x80000007;
    };

    struct NIRIO_IO
    {
       static const uint32_t POKE64             = 0xA0000005;
       static const uint32_t POKE32             = 0xA0000006;
       static const uint32_t POKE16             = 0xA0000007;
       static const uint32_t POKE8              = 0xA0000008;
       static const uint32_t PEEK64             = 0xA0000009;
       static const uint32_t PEEK32             = 0xA000000A;
       static const uint32_t PEEK16             = 0xA000000B;
       static const uint32_t PEEK8              = 0xA000000C;
       static const uint32_t READ_BLOCK         = 0xA000000D;
       static const uint32_t WRITE_BLOCK        = 0xA000000E;
       static const uint32_t GET_IO_WINDOW      = 0xA000000F;
       static const uint32_t GET_IO_WINDOW_SIZE = 0xA0000010;
    };

    struct nirio_ioctl_packet_t {
        nirio_ioctl_packet_t(void* const _outBuf, const uint32_t _outSize, const int32_t _statusCode)
        {
            outBuf._64BitField = 0;
            outBuf.pointer = _outBuf;
            outSize    = _outSize;
            statusCode = _statusCode;
        };

        union {
            void* pointer;
            uint64_t _64BitField;
        } outBuf;

        uint32_t outSize;
        int32_t statusCode;
    };

    struct nirio_syncop_in_params_t
    {
       uint32_t function;
       uint32_t subfunction;

       union
       {
          struct
          {
             uint32_t attribute;
             uint32_t value;
          } attribute32;

          struct
          {
             uint32_t attribute;
             uint64_t value;
          } attribute64;

          struct
          {
             uint32_t attribute;
          } attributeStr;

          struct
          {
             uint32_t attribute;
          } download;

          union
          {
             struct
             {
                uint32_t reserved_field_0_0_0;
             } reserved_field_0_0;
             struct
             {
                uint32_t reserved_field_0_1_0;
                uint32_t reserved_field_0_1_1;
             } reserved_field_0_1;
             struct
             {
                uint32_t reserved_field_0_2_0;
             } reserved_field_0_2;
          } reserved_field_0;

          union
          {
             struct
             {
                uint32_t channel;
                uint32_t baseAddress;
                uint32_t depthInSamples;
                uint32_t version;
             } fifo;
             struct
             {
                uint32_t channel;
                uint32_t baseAddress;
                uint32_t depthInSamples;
                uint32_t version;
                uint32_t scalarType;
                uint32_t bitWidth;
             } fifoWithDataType;
             struct
             {
                uint64_t rangeBaseAddress;
                uint32_t rangeSizeInBytes;
                uint32_t rangeAttribute;
             } atomic; // obsolete
          } add;

          struct
          {
             uint32_t channel;

             union
             {
                struct
                {
                   uint32_t requestedDepth;
                   uint8_t  requiresActuals;
                } config;
                struct
                {
                   uint32_t timeout;
                } read;
                struct
                {
                   uint32_t timeout;
                   uint32_t scalarType;
                   uint32_t bitWidth;
                } readWithDataType;
                struct
                {
                   uint32_t timeout;
                } write;
                struct
                {
                   uint32_t timeout;
                   uint32_t scalarType;
                   uint32_t bitWidth;
                } writeWithDataType;
                struct
                {
                   uint32_t elementsRequested;
                   uint32_t scalarType;
                   uint32_t bitWidth;
                   uint32_t timeout;
                   uint8_t  output;
                } wait;
                struct
                {
                   uint32_t elements;
                } grant;
             } op;
          } fifo;

          struct
          {
             uint64_t reserved_field_1_0;
             uint32_t reserved_field_1_1;
             uint32_t reserved_field_1_2;
          } reserved_field_1; // Obsolete

          struct
          {
             uint32_t offset;
             union
             {
                uint64_t value64;
                uint32_t value32;
                uint16_t value16;
                uint8_t  value8;
             } value;
             union
             {
                uint32_t   sizeToMap;
             } memoryMappedIoWindow;
          } io;

          struct
          {
             uint32_t reserved_field_2_0;
             uint32_t reserved_field_2_1;
          } reserved_field_2;

          struct
          {
             uint32_t reserved_field_3_0;
          } reserved_field_3;

          union
          {
             struct
             {
                uint32_t reserved_field_4_0;
                int32_t  reserved_field_4_1;
             } wait;
          } reserved_field_4;

       } params;

       uint32_t inbufByteLen;

       union
       {
          const void* pointer;
          uint64_t    _64BitField;
       } inbuf;
    };

    static inline void init_syncop_in_params(nirio_syncop_in_params_t& param, const void* const buf, const uint32_t len)
    {
       param.inbuf._64BitField = 0;
       param.inbuf.pointer = buf;
       param.inbufByteLen = len;
    }

    struct nirio_syncop_out_params_t
    {
       union
       {
          struct
          {
             uint32_t value;
          } attribute32;

          struct
          {
             uint64_t value;
          } attribute64;

          union
          {
             struct
             {
                uint32_t reserved_field_0_0;
             } enable;
          } reserved_field_0;

          struct
          {
             union
             {
                struct
                {
                   uint32_t actualDepth;
                   uint32_t actualSize;
                } config;
                struct
                {
                   uint32_t numberRead;
                   uint32_t numberRemaining;
                } read;
                struct
                {
                   uint32_t numberRemaining;
                } write;
                struct
                {
                   union
                   {
                      void*    pointer;
                      uint64_t _64BitField;
                   } elements;
                } wait;
             } op;
          } fifo;

          struct
          {
            union
             {
                union
                {
                   uint64_t value64;
                   uint32_t value32;
                   uint16_t value16;
                   uint8_t  value8;
                } value;
                union
                {
                   void*    memoryMappedAddress;
                   uint64_t _64BitField;
                } memoryMappedIoWindow;
                union
                {
                   uint32_t   size;
                } memoryMappedIoWindowSize;
             };
          } io;

          uint32_t stringLength;

          struct
          {
             uint32_t reserved_field_1_0;
          } reserved_field_1;

       } params;

       uint32_t    outbufByteLen;

       union
       {
          void*    pointer;
          uint64_t _64BitField;
       } outbuf;
    };

    static inline void init_syncop_out_params(nirio_syncop_out_params_t& param, void* buf, uint32_t len)
    {
       param.outbuf._64BitField = 0;
       param.outbuf.pointer = buf;
       param.outbufByteLen = len;
    }

    //-------------------------------------------------------
    // niriok_proxy_impl_v1
    //-------------------------------------------------------
    niriok_proxy_impl_v1::niriok_proxy_impl_v1()
    {
    }

    niriok_proxy_impl_v1::~niriok_proxy_impl_v1()
    {
        close();
    }

    nirio_status niriok_proxy_impl_v1::open(const std::string& interface_path)
    {
        WRITER_LOCK

        if (interface_path.empty()) return NiRio_Status_ResourceNotFound;

        //close if already open.
        // use non-locking _close since we already have the lock
        _close();

        nirio_status status = NiRio_Status_Success;
        nirio_status_chain(nirio_driver_iface::rio_open(
            interface_path, _device_handle), status);
        if (nirio_status_not_fatal(status)) {
            nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle,
                                        NIRIO_IOCTL_POST_OPEN,
                                        NULL, 0, NULL, 0), status);
            nirio_ioctl_packet_t out(&_interface_num, sizeof(_interface_num), 0);
            nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle,
                                        NIRIO_IOCTL_GET_IFACE_NUM,
                                        NULL, 0,
                                        &out, sizeof(out)), status);

            if (nirio_status_fatal(status)) _close();
        }
        return status;
    }

    void niriok_proxy_impl_v1::close(void)
    {
        WRITER_LOCK

        _close();
    }

    // this protected _close doesn't acquire the lock, so it can be used in methods 
    // that already have the lock
    void niriok_proxy_impl_v1::_close()
    {

       if(nirio_driver_iface::rio_isopen(_device_handle))
       {
            nirio_driver_iface::rio_ioctl(
                _device_handle, NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0);
            nirio_driver_iface::rio_close(_device_handle);
       }
    }

    nirio_status niriok_proxy_impl_v1::reset()
    {
        READER_LOCK

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function = NIRIO_FUNC::RESET;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::get_version(
        nirio_version_t type,
        uint32_t& major,
        uint32_t& upgrade,
        uint32_t& maintenance,
        char& phase,
        uint32_t& build)
    {   
        nirio_device_attribute32_t version_attr = (type==CURRENT)?RIO_CURRENT_VERSION:RIO_OLDEST_COMPATIBLE_VERSION;
        uint32_t raw_version = 0;
        nirio_status status = get_attribute(version_attr, raw_version);

        major       = (raw_version & VERSION_MAJOR_MASK) >> VERSION_MAJOR_SHIFT;
        upgrade     = (raw_version & VERSION_UPGRD_MASK) >> VERSION_UPGRD_SHIFT;
        maintenance = (raw_version & VERSION_MAINT_MASK) >> VERSION_MAINT_SHIFT;
        build       = (raw_version & VERSION_BUILD_MASK) >> VERSION_BUILD_SHIFT;

        uint32_t phase_num = (raw_version & VERSION_PHASE_MASK) >> VERSION_PHASE_SHIFT;
        switch (phase_num) {
            case 0: phase = 'd'; break;
            case 1: phase = 'a'; break;
            case 2: phase = 'b'; break;
            case 3: phase = 'f'; break;
        }

        return status;
    }

    nirio_status niriok_proxy_impl_v1::sync_operation(
        const void *writeBuffer,
        size_t writeBufferLength,
        void *readBuffer,
        size_t readBufferLength)
    {
        READER_LOCK

        nirio_ioctl_packet_t out(readBuffer, readBufferLength, 0);
        nirio_status ioctl_status = nirio_driver_iface::rio_ioctl(_device_handle,
                                    NIRIO_IOCTL_SYNCOP,
                                    writeBuffer, writeBufferLength,
                                    &out, sizeof(out));
        if (nirio_status_fatal(ioctl_status)) return ioctl_status;

        return out.statusCode;
    }

    nirio_status niriok_proxy_impl_v1::get_attribute(
        const nirio_device_attribute32_t attribute,
        uint32_t& attrValue)
    {
        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function = NIRIO_FUNC::GET32;
        in.params.attribute32.attribute = static_cast <uint32_t> (attribute);

        nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));

        attrValue = out.params.attribute32.value;
        return status;
    }

    nirio_status niriok_proxy_impl_v1::set_attribute(
        const nirio_device_attribute32_t attribute,
        const uint32_t value)
    {
        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

       in.function = NIRIO_FUNC::SET32;
       in.params.attribute32.attribute = static_cast <uint32_t> (attribute);
       in.params.attribute32.value  = value;

       return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::peek(uint32_t offset, uint32_t& value)
    {
        if (offset % 4 != 0) return NiRio_Status_MisalignedAccess;

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function         = NIRIO_FUNC::IO;
        in.subfunction      = NIRIO_IO::PEEK32;
        in.params.io.offset = offset;

        nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));
        value = out.params.io.value.value32;
        return status;
    }

    nirio_status niriok_proxy_impl_v1::peek(uint32_t offset, uint64_t& value)
    {
        if (offset % 8 != 0) return NiRio_Status_MisalignedAccess;

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function         = NIRIO_FUNC::IO;
        in.subfunction      = NIRIO_IO::PEEK64;
        in.params.io.offset = offset;

        nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));
        value = out.params.io.value.value64;
        return status;
    }

    nirio_status niriok_proxy_impl_v1::poke(uint32_t offset, const uint32_t& value)
    {
        if (offset % 4 != 0) return NiRio_Status_MisalignedAccess;

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function                 = NIRIO_FUNC::IO;
        in.subfunction              = NIRIO_IO::POKE32;
        in.params.io.offset         = offset;
        in.params.io.value.value32  = value;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::poke(uint32_t offset, const uint64_t& value)
    {
        if (offset % 8 != 0) return NiRio_Status_MisalignedAccess;

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function                 = NIRIO_FUNC::IO;
        in.subfunction              = NIRIO_IO::POKE64;
        in.params.io.offset         = offset;
        in.params.io.value.value64  = value;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::map_fifo_memory(
        uint32_t fifo_instance,
        size_t size,
        nirio_driver_iface::rio_mmap_t& map)
    {
        READER_LOCK

        return nirio_driver_iface::rio_mmap(_device_handle,
                GET_FIFO_MEMORY_TYPE(fifo_instance),
                size, true, map);
    }

    nirio_status niriok_proxy_impl_v1::unmap_fifo_memory(
        nirio_driver_iface::rio_mmap_t& map)
    {
        READER_LOCK

        return nirio_driver_iface::rio_munmap(map);
    }

    nirio_status niriok_proxy_impl_v1::stop_all_fifos()
    {
        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function = NIRIO_FUNC::FIFO_STOP_ALL;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::add_fifo_resource(const nirio_fifo_info_t& fifo_info)
    {
       nirio_syncop_in_params_t in = {};
       nirio_syncop_out_params_t out = {};

       in.function    = NIRIO_FUNC::ADD_RESOURCE;
       if (fifo_info.direction == OUTPUT_FIFO)
           in.subfunction = NIRIO_RESOURCE::OUTPUT_FIFO;
       else
           in.subfunction = NIRIO_RESOURCE::INPUT_FIFO;

       in.params.add.fifoWithDataType.channel        = fifo_info.channel;
       in.params.add.fifoWithDataType.baseAddress    = fifo_info.base_addr;
       in.params.add.fifoWithDataType.depthInSamples = fifo_info.depth;
       in.params.add.fifoWithDataType.scalarType     = static_cast <uint32_t> (fifo_info.scalar_type);
       in.params.add.fifoWithDataType.bitWidth       = fifo_info.bitWidth;
       in.params.add.fifoWithDataType.version        = fifo_info.version;
                                                     //fifo_info.integerWordLength is not needed by the v1 kernel interface

       return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

   nirio_status niriok_proxy_impl_v1::set_device_config()
   {
      nirio_syncop_in_params_t in = {};
      nirio_syncop_out_params_t out = {};

      in.function    = NIRIO_FUNC::SET_DRIVER_CONFIG;
      in.subfunction = 0;

      return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::start_fifo(
       uint32_t channel)
    {
        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function    = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::START;

        in.params.fifo.channel = channel;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::stop_fifo(
        uint32_t channel)
    {
        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function    = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::STOP;

        in.params.fifo.channel = channel;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::configure_fifo(
       uint32_t channel,
       uint32_t requested_depth,
       uint8_t requires_actuals,
       uint32_t& actual_depth,
       uint32_t& actual_size)
    {
        nirio_status status = NiRio_Status_Success;

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::CONFIGURE;

        in.params.fifo.channel = channel;
        in.params.fifo.op.config.requestedDepth = requested_depth;
        in.params.fifo.op.config.requiresActuals = requires_actuals;

        status = sync_operation(&in, sizeof(in), &out, sizeof(out));
        if (nirio_status_fatal(status)) return status;

        actual_depth = out.params.fifo.op.config.actualDepth;
        actual_size = out.params.fifo.op.config.actualSize;

        return status;
    }

    nirio_status niriok_proxy_impl_v1::wait_on_fifo(
        uint32_t channel,
        uint32_t elements_requested,
        uint32_t scalar_type,
        uint32_t bit_width,
        uint32_t timeout,
        uint8_t output,
        void*& data_pointer,
        uint32_t& elements_acquired,
        uint32_t& elements_remaining)
    {
        nirio_status status = NiRio_Status_Success;

        nirio_syncop_in_params_t in = {};
        uint32_t stuffed[2];
        nirio_syncop_out_params_t out = {};
        init_syncop_out_params(out, stuffed, sizeof(stuffed));

        in.function    = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::WAIT;

        in.params.fifo.channel                   = channel;
        in.params.fifo.op.wait.elementsRequested = elements_requested;
        in.params.fifo.op.wait.scalarType        = scalar_type;
        in.params.fifo.op.wait.bitWidth          = bit_width;
        in.params.fifo.op.wait.output            = output;
        in.params.fifo.op.wait.timeout           = timeout;

        status = sync_operation(&in, sizeof(in), &out, sizeof(out));
        if (nirio_status_fatal(status)) return status;

        data_pointer = out.params.fifo.op.wait.elements.pointer;
        elements_acquired = stuffed[0];
        elements_remaining = stuffed[1];

        return status;
    }

    nirio_status niriok_proxy_impl_v1::grant_fifo(
       uint32_t channel,
       uint32_t elements_to_grant)
    {
        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};

        in.function = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::GRANT;

        in.params.fifo.channel = channel;
        in.params.fifo.op.grant.elements = elements_to_grant;

        return sync_operation(&in, sizeof(in), &out, sizeof(out));
    }

    nirio_status niriok_proxy_impl_v1::read_fifo(
        uint32_t channel,
        uint32_t elements_to_read,
        void* buffer,
        uint32_t buffer_datatype_width,
        uint32_t scalar_type,
        uint32_t bit_width,
        uint32_t timeout,
        uint32_t& number_read,
        uint32_t& number_remaining)
    {
        nirio_status status = NiRio_Status_Success;

        nirio_syncop_in_params_t in = {};
        nirio_syncop_out_params_t out = {};
        init_syncop_out_params(out, buffer, elements_to_read * buffer_datatype_width);

        in.function = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::READ;

        in.params.fifo.channel = channel;
        in.params.fifo.op.readWithDataType.timeout = timeout;
        in.params.fifo.op.readWithDataType.scalarType = scalar_type;
        in.params.fifo.op.readWithDataType.bitWidth = bit_width;

        status = sync_operation(&in, sizeof(in), &out, sizeof(out));
        if (nirio_status_fatal(status) && (status != NiRio_Status_FifoTimeout)) return status;

        number_read = out.params.fifo.op.read.numberRead;
        number_remaining = out.params.fifo.op.read.numberRemaining;

        return status;
    }

    nirio_status niriok_proxy_impl_v1::write_fifo(
        uint32_t channel,
        uint32_t elements_to_write,
        void* buffer,
        uint32_t buffer_datatype_width,
        uint32_t scalar_type,
        uint32_t bit_width,
        uint32_t timeout,
        uint32_t& number_remaining)
    {
        nirio_status status = NiRio_Status_Success;

        nirio_syncop_in_params_t in = {};
        init_syncop_in_params(in, buffer, elements_to_write * buffer_datatype_width);
        nirio_syncop_out_params_t out = {};

        in.function = NIRIO_FUNC::FIFO;
        in.subfunction = NIRIO_FIFO::WRITE;

        in.params.fifo.channel = channel;
        in.params.fifo.op.writeWithDataType.timeout = timeout;
        in.params.fifo.op.writeWithDataType.scalarType = scalar_type;
        in.params.fifo.op.writeWithDataType.bitWidth = bit_width;

        status = sync_operation(&in, sizeof(in), &out, sizeof(out));
        if (nirio_status_fatal(status) && (status != NiRio_Status_FifoTimeout)) return status;

        number_remaining = out.params.fifo.op.write.numberRemaining;

        return status;
    }

}}

#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 5)
    #pragma GCC diagnostic pop
#endif
