/**
 * \brief IQI's IO-Link Master control interface
 * \copyright 2013-2014, IQ² Development GmbH, Neckartenzlingen. All rights reserved.
 */

#include "iqi_define.h"
#include "iqi_rs232.h"
#include "iqi_ethernet_internal.h"
#include "iqi_comm.h"
#include "iqi_mst.h"

enum                                                                            // CH_IOL
{
  CMD_POWER		   = 0x13,
  CMD_SET_MODE     = 0x21,
  CMD_GET_STATUS   = 0x22,
  CMD_SET_PDOUT    = 0x23,
  CMD_OD_WRITE_REQ = 0x24,
  CMD_OD_WRITE_RSP = 0x25,
  CMD_OD_READ_REQ  = 0x26,
  CMD_OD_READ_RSP  = 0x27,
  CMD_GET_EVENT    = 0x28
};

uchar08 DEFAULT_ID = (byte) 0x00;
uchar08 IQDLL_ID = (byte) 0x05;

DLL_API
int16 mst_GetVersion(uint16* p_major, uint16* p_minor, char08 *cpErrMsg)
{
	*p_major = IQI_DLL_VERSION_MAJOR;
	*p_minor = IQI_DLL_VERSION_MINOR;
	return ERR_NONE;
}

DLL_API
int16 mst_Connect(uchar08 ucFromPortNo,
                  uchar08 ucUntilPortNo,
                  char08 *cpErrMsg)
{
  uint16 uiPortNo;
  uchar08 ucIQIOperMode = IQMODE_IOL_MASTER;
  ASSIGN_RETURN(uiPortNo, comm_SearchPortNo(ucFromPortNo, ucUntilPortNo, cpErrMsg));
  CHK_RETURN(rs232_OpenPort(uiPortNo, 115200, cpErrMsg));
  comm_WriteParameter(uiPortNo, IQParSWToolID, &IQDLL_ID, 1, cpErrMsg);
  CHK_RETURN(comm_WriteParameter(uiPortNo, IQParIQIOperMode, &ucIQIOperMode, 1, cpErrMsg));
  return uiPortNo;
}

DLL_API
int16 mst_EthernetConnect(enet_ConnectionT * connection, 
                          uint16 uiSendRecvTimoutMs, 
                          char08 *cpErrMsg)
{
  uint16 uiPortNo;
  uchar08 ucIQIOperMode = IQMODE_IOL_MASTER;
  ASSIGN_RETURN(uiPortNo, enet_OpenConnection(connection, uiSendRecvTimoutMs, cpErrMsg));
  comm_WriteParameter(uiPortNo, IQParSWToolID, &IQDLL_ID, 1, cpErrMsg);
  CHK_RETURN(comm_WriteParameter(uiPortNo, IQParIQIOperMode, &ucIQIOperMode, 1, cpErrMsg));
  return uiPortNo;
}

DLL_API
int16 mst_Disconnect(uint16 uiPortNo,
                     char08 *cpErrMsg)
{
  mst_SetOperatingMode(uiPortNo, MST_OPERATING_MODE_INACTIVE, 
                       MST_STATE_INACTIVE, NULL, cpErrMsg);
  comm_WriteParameter(uiPortNo, IQParSWToolID, &DEFAULT_ID, 1, cpErrMsg);
  if (uiPortNo < MAX_COMPORT_NUM)
    CHK_RETURN(rs232_ClosePort(uiPortNo, cpErrMsg));
  else
    CHK_RETURN(enet_CloseConnection(uiPortNo, cpErrMsg));
  return ERR_NONE;
}

DLL_API
int16 mst_GetGadgetID(uint16 uiPortNo,
                      uchar08 *ucpGadgetID,
					  char08 *cpErrMsg)
{
  CHK_RETURN(comm_ReadParameter(uiPortNo, IQParGadgetID, ucpGadgetID, 1, cpErrMsg));

  return ERR_NONE;
}

DLL_API
int16 mst_SetConfig(uint16 uiPortNo,
                    mst_ConfigT *pConfig,
                    char08 *cpErrMsg)
{
  uchar08 hd[17];
  
  if (pConfig->cycleTimeOperate > MAX_CYCLE_TIME)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Config cycle time operate is greater than its maximum %d", MAX_CYCLE_TIME);
    return ERR_DATA;
  }
  SET_BIGEND_UINT16(hd, pConfig->cycleTimeOperate);
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParConfigCycleTimeOperate, hd, 2, cpErrMsg),
    "Config cycle time operate", cpErrMsg);
  
  if (pConfig->restOfCycleTimeOperate > MAX_CYCLE_TIME)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Config rest of cycle time operate is greater than its maximum %d", MAX_CYCLE_TIME);
    return ERR_DATA;
  }
  SET_BIGEND_UINT16(hd, pConfig->restOfCycleTimeOperate);
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParConfigRestOfCycleTime, hd, 2, cpErrMsg),
    "Config rest of cycle time operate", cpErrMsg);

  hd[0] = (uchar08)pConfig->revisionID;
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParConfigRevisionID, hd, 1, cpErrMsg),
    "Config revision ID", cpErrMsg);

  hd[0] = (uchar08)pConfig->inspectionLevel;
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParInspectionLevel, hd, 1, cpErrMsg),
    "Inspection level", cpErrMsg);

  SET_BIGEND_UINT16(hd, pConfig->deviceVendorID);
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDeviceVendorID, hd, 2, cpErrMsg),
    "Config device vendor ID", cpErrMsg);

  if (pConfig->deviceID > 0x00FFFFFF)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Device id is greater than its maximum %d", 0x00FFFFFF);
    return ERR_DATA;
  }
  SET_BIGEND_ULONG32(hd, pConfig->deviceID);
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDeviceID, &(hd[1]), 3, cpErrMsg),
    "Config device ID", cpErrMsg);

  SET_BIGEND_UINT16(hd, pConfig->deviceFunctionID);
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDeviceFunctionID, hd, 2, cpErrMsg),
    "Config device function ID", cpErrMsg);

  if (pConfig->deviceSerialNumberLen > MST_DEV_SER_NUM_MAX_LEN)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Config device serial number length is greater than its maximum %d", MST_DEV_SER_NUM_MAX_LEN);
    return ERR_DATA;
  }
  hd[0] = pConfig->deviceSerialNumberLen;
  memcpy(&(hd[1]), pConfig->deviceSerialNumber, pConfig->deviceSerialNumberLen);
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDeviceSernum, hd, pConfig->deviceSerialNumberLen + 1, cpErrMsg),
    "Config device serial number", cpErrMsg);

  hd[0] = (uchar08)pConfig->dsActivState;
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDSActivState, hd, 1, cpErrMsg),
    "DS activation state", cpErrMsg);

  hd[0] = pConfig->dsUploadEnable == TRUE ? 0xFF : 0x00;
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDSUploadEnable, hd, 1, cpErrMsg),
    "DS upload enable", cpErrMsg);

  hd[0] = pConfig->dsDownloadEnable == TRUE ? 0xFF : 0x00;
  CHK_RETURN_MES(comm_WriteParameter(uiPortNo, IQParDSDownloadEnable, hd, 1, cpErrMsg),
    "DS download enable", cpErrMsg);

  return ERR_NONE;
}

DLL_API
int16 mst_GetConfig(uint16 uiPortNo,
                    mst_ConfigT *pConfig,
                    char08 *cpErrMsg)
{
  uchar08 hd[17];
  int16 iActSize;

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParMstStackVersion, hd, 2, cpErrMsg));
  IQPAR_CHECK_LEN("master iqStack version", iActSize, 2);
  pConfig->stackVersion = GET_BIGEND_UINT16(hd);

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParConfigCycleTimeOperate, hd, 2, cpErrMsg));
  IQPAR_CHECK_LEN("config cycle time operate", iActSize, 2);
  pConfig->cycleTimeOperate = GET_BIGEND_UINT16(hd);
  
  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParConfigRestOfCycleTime, hd, 2, cpErrMsg));
  IQPAR_CHECK_LEN("config rest of cycle time operate", iActSize, 2);
  pConfig->restOfCycleTimeOperate = GET_BIGEND_UINT16(hd);

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParConfigRevisionID, hd, 1, cpErrMsg));
  IQPAR_CHECK_LEN("config revision ID", iActSize, 1);
  pConfig->revisionID = (IOLRevisionIDT)hd[0];

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParInspectionLevel, hd, 1, cpErrMsg));
  IQPAR_CHECK_LEN("inspection level", iActSize, 1);
  pConfig->inspectionLevel = (mst_InspectionLevelT)hd[0];

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDeviceVendorID, hd, 2, cpErrMsg));
  IQPAR_CHECK_LEN("config device vendor ID", iActSize, 2);
  pConfig->deviceVendorID = GET_BIGEND_UINT16(hd);

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDeviceID, hd, 3, cpErrMsg));
  IQPAR_CHECK_LEN("config device ID", iActSize, 3); hd[3] = 0;
  pConfig->deviceID = GET_BIGEND_ULONG32(hd) >> 8;

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDeviceFunctionID, hd, 2, cpErrMsg));
  IQPAR_CHECK_LEN("config device function ID", iActSize, 2);
  pConfig->deviceFunctionID = GET_BIGEND_UINT16(hd);

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDeviceSernum, hd, 17, cpErrMsg));
  //IQPAR_CHECK_LEN("config device serial number", iActSize, 17);
  pConfig->deviceSerialNumberLen = hd[0];
  if (pConfig->deviceSerialNumberLen > MST_DEV_SER_NUM_MAX_LEN)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Config device serial number length is greater than its maximum %d", MST_DEV_SER_NUM_MAX_LEN);
    return ERR_DATA;
  }
  memcpy(pConfig->deviceSerialNumber, &(hd[1]), pConfig->deviceSerialNumberLen);
  pConfig->deviceSerialNumber[pConfig->deviceSerialNumberLen] = 0;

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParRealConnectionBaudrate, hd, 1, cpErrMsg));
  IQPAR_CHECK_LEN("real sdci communication baudrate", iActSize, 1);
  pConfig->realBaudrate = (IOLBaudrateT)hd[0];

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDSActivState, hd, 1, cpErrMsg));
  IQPAR_CHECK_LEN("DS activation state", iActSize, 1);
  pConfig->dsActivState = (mst_DSActivStateT)hd[0];

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDSUploadEnable, hd, 1, cpErrMsg));
  IQPAR_CHECK_LEN("DS upload enable flag", iActSize, 1);
  pConfig->dsUploadEnable = hd[0] > 0 ? TRUE : FALSE;

  ASSIGN_RETURN(iActSize, comm_ReadParameter(uiPortNo, IQParDSDownloadEnable, hd, 1, cpErrMsg));
  IQPAR_CHECK_LEN("DS download enable flag", iActSize, 1);
  pConfig->dsDownloadEnable = hd[0] > 0 ? TRUE : FALSE;

  return ERR_NONE;
}

int16 mst_PowerControl(uint16 uiPortNo,
                       uchar08 ucSwitch,
					   char08 *cpErrMsg)
{
  return comm_SendRecv(uiPortNo, CH_IOL, CMD_POWER, &ucSwitch, 1, NULL, 0, cpErrMsg);
}

DLL_API
int16 mst_GetStatus(uint16 uiPortNo,
                    uchar08 *pStatus,
                    uchar08 *ucpPDInBuf,
                    uint16 uiPDInBufSize,
                    char08 *cpErrMsg)
{ 
  uchar08 buf[33];
  int16 actSize;
  
  ASSIGN_RETURN(actSize,
    comm_SendRecv(uiPortNo, CH_IOL, CMD_GET_STATUS, NULL, 0, buf, 33, cpErrMsg));
  
  if (!actSize)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN, "Zero bytes has been received.");
    return ERR_DATA;
  }
  
  *pStatus = buf[0];
  
  if ((actSize - 1) > uiPDInBufSize)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Too small PD input buffer depth ('%d', but '%d' is needed).", uiPDInBufSize, actSize - 1);
    return ERR_BUFFER_OVERFLOW;
  }

  memcpy(ucpPDInBuf, buf + 1, actSize - 1);
  
  return actSize - 1;
}

DLL_API
int16 mst_SetOperatingMode(uint16 uiPortNo,
                           uchar08 ucOperMode,
                           uchar08 ucStateToWait,
                           uchar08 *ucpState,
                           char08 *cpErrMsg)
{
  clock_t timeout;
  uchar08 pdIn[MAX_PD_BUF_LEN];
  mst_StatusT oldStatus, newStatus;

  CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&oldStatus, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
  
  CHK_RETURN(comm_SendRecv(uiPortNo, CH_IOL, CMD_SET_MODE, 
                           &ucOperMode, 1, NULL, 0, cpErrMsg));
  
  if (ucpState == NULL)
    return ERR_NONE;

  for (timeout = clock() + MST_SET_OPERATING_MODE_TIMEOUT * CLOCKS_PER_SEC;
       timeout >= clock();)
  {
    CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&newStatus, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
    *ucpState = (uchar08)newStatus.State;

    if (ucStateToWait == newStatus.State)
    {      
      // wait fallback
      if (ucOperMode == MST_OPERATING_MODE_INACTIVE)
        Sleep(500);
      return ERR_NONE;  
    }
    
    Sleep(50);
  } 
  
  snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
    "The state hasn't changed during timeout (%d sec).", MST_SET_OPERATING_MODE_TIMEOUT);
  return ERR_OPERATION;  
}

DLL_API
int16 mst_SetPDValue(uint16 uiPortNo,
                     uchar08 *ucpPDOutBuf,
                     uint16 uiBytesCount,
                     char08 *cpErrMsg)
{
  clock_t timeout;
  uchar08 pdIn[MAX_PD_BUF_LEN];
  mst_StatusT status;
  
  CHK_RETURN(comm_SendRecv(uiPortNo,
    CH_IOL, CMD_SET_PDOUT, ucpPDOutBuf, uiBytesCount, NULL, 0, cpErrMsg));
  
  for (timeout = clock() + MST_SET_PDOUT_VALUE_TIMEOUT * CLOCKS_PER_SEC;
       timeout >= clock();)
  {
    CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&status, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
    
    if (!(status.PDOutSet)) return ERR_NONE;  
    
    Sleep(50);
  } 
  
  snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
    "Process data isn't sent during timeout (%d sec).", MST_SET_PDOUT_VALUE_TIMEOUT);
  return ERR_OPERATION;  
}

DLL_API
int16 mst_SetPDValidity(uint16 uiPortNo,
                        boolean bValidity,
                        char08 *cpErrMsg)
{
  clock_t timeout;
  uchar08 pdIn[MAX_PD_BUF_LEN];
  mst_StatusT status;
  
  uchar08 hd[4] = {0, 0, 1, bValidity == TRUE ? 0x98 : 0x99};
  
  CHK_RETURN(comm_SendRecv(uiPortNo,
    CH_IOL, CMD_OD_WRITE_REQ, hd, 4, NULL, 0, cpErrMsg));
  
  for (timeout = clock() + MST_SET_PDOUT_VALIDITY_TIMEOUT * CLOCKS_PER_SEC;
       timeout >= clock();)
  {
    CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&status, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
    
    if ((bValidity == TRUE && !(status.PDOutInvalid)) ||
        (bValidity == FALSE && status.PDOutInvalid))
      return ERR_NONE;  
    
    Sleep(50);
  } 
  
  snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
    "Process data isn't change it's validity during timeout (%d sec).", MST_SET_PDOUT_VALIDITY_TIMEOUT);
  return ERR_OPERATION;  
}

DLL_API
int16 mst_WaitODRsp(uint16 uiPortNo, char08 *cpErrMsg)
{
  clock_t timeout;
  uchar08 pdIn[MAX_PD_BUF_LEN];
  mst_StatusT status;

  for (timeout = clock() + MST_OD_TIMEOUT * CLOCKS_PER_SEC;
       timeout >= clock();)
  {
    CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&status, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
    if (!(status.OD)) return ERR_NONE;
    Sleep(50);
  } 
  
  snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
    "ISDU request timeout (%d sec).", MST_OD_TIMEOUT);
  return ERR_INTERNAL;
}

DLL_API
int16 mst_StartReadOD(uint16 uiPortNo,
                      uint16 uiIndex,
                      uchar08 ucSubindex,
                      char08 *cpErrMsg)
{
  uchar08 hd[3];
  uchar08 pdIn[MAX_PD_BUF_LEN];
  mst_StatusT status;
  
  *((uint16*)hd) = uiIndex; hd[2] = ucSubindex;
  CHK_RETURN(comm_SendRecv(uiPortNo,
    CH_IOL, CMD_OD_READ_REQ, hd, 3, NULL, 0, cpErrMsg));

  CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&status, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
  
  return ERR_NONE;
}

DLL_API
int16 mst_GetReadODRsp(uint16 uiPortNo, 
                       uchar08 *ucpBuf, 
                       uint16 uiBufSize, 
                       uint16* uipISDUErr, 
                       char08 *cpErrMsg)
{
  uchar08 buf[256];
  int16 actSize;
  
  ASSIGN_RETURN(actSize, comm_SendRecv(uiPortNo,
    CH_IOL, CMD_OD_READ_RSP, NULL, 0, buf, 256, cpErrMsg));

  if (actSize < 1)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN, "On-request read data response size is zero.");
    return ERR_INTERNAL;
  }

  if (*buf)                                                                 // an ISDU error
  {
    *uipISDUErr = GET_BIGEND_UINT16(buf);
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "On-request read data response fail (ISDU error '0x%04X').", *uipISDUErr);
    return ERR_OPERATION;
  }

  if ((actSize - 1) > uiBufSize)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "Too small on-request receive buffer depth ('%d', but '%d' is needed).", uiBufSize, actSize - 1);
    return ERR_BUFFER_OVERFLOW;
  }

  memcpy(ucpBuf, buf + 1, actSize - 1);
  
  return actSize - 1;
}

DLL_API
int16 mst_ReadOD(uint16 uiPortNo,
                 uint16 uiIndex,
                 uchar08 ucSubindex,
                 uchar08 *ucpBuf,
                 uint16 uiBufSize,
                 uint16* uipISDUErr,
                 char08 *cpErrMsg)
{
  CHK_RETURN(mst_StartReadOD(uiPortNo, uiIndex, ucSubindex, cpErrMsg));
  CHK_RETURN(mst_WaitODRsp(uiPortNo, cpErrMsg));
  return mst_GetReadODRsp(uiPortNo, ucpBuf, uiBufSize, uipISDUErr, cpErrMsg);
}

DLL_API
int16 mst_StartWriteOD(uint16 uiPortNo,
					             uint16 uiIndex,
					             uchar08 ucSubindex,
					             uchar08 *ucpBuf,
					             uint16 uiBytesCount,
					             char08 *cpErrMsg)
{
  uchar08 hd[256];
  uchar08 pdIn[MAX_PD_BUF_LEN];
  mst_StatusT status;
  
  if (uiBytesCount > MAX_OD_BUF_LEN)
  {
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "ISDU length is too large (%d, but must be not greater than %d).", uiBytesCount, MAX_OD_BUF_LEN);
    return ERR_PARAMETER;
  }

  *((uint16*)hd) = uiIndex; hd[2] = ucSubindex;
  memcpy(hd + 3, ucpBuf, uiBytesCount);
  CHK_RETURN(comm_SendRecv(uiPortNo,
    CH_IOL, CMD_OD_WRITE_REQ, hd, uiBytesCount + 3, NULL, 0, cpErrMsg));
  
  CHK_RETURN(mst_GetStatus(uiPortNo, (uchar08*)&status, pdIn, MAX_PD_BUF_LEN, cpErrMsg));

  return ERR_NONE;
}

DLL_API
int16 mst_GetWriteODRsp(uint16 uiPortNo, uint16* uipISDUErr, char08 *cpErrMsg)
{
  uchar08 buf[2];
  int16 actSize;
      
  ASSIGN_RETURN(actSize, comm_SendRecv(uiPortNo,
   CH_IOL, CMD_OD_WRITE_RSP, NULL, 0, buf, 2, cpErrMsg));
  
  if (actSize < 1)
  {
   snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN, "On-request write data respose size is zero.");
   return ERR_INTERNAL;
  }
  
  if (*buf)                                                                             // an ISDU error
  {   
    *uipISDUErr = GET_BIGEND_UINT16(buf);
    snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
      "On-request write data response fail (ISDU error '0x%04X').", *uipISDUErr);
    return ERR_OPERATION;
  }
      
  return ERR_NONE;
}

DLL_API
int16 mst_WriteOD(uint16 uiPortNo,
                  uint16 uiIndex,
                  uchar08 ucSubindex,
                  uchar08 *ucpBuf,
                  uint16 uiBytesCount,
                  uint16* uipISDUErr,
                  char08 *cpErrMsg)
{
  CHK_RETURN(mst_StartWriteOD(uiPortNo, uiIndex, ucSubindex, ucpBuf, uiBytesCount, cpErrMsg));
  CHK_RETURN(mst_WaitODRsp(uiPortNo, cpErrMsg));
  CHK_RETURN(mst_GetWriteODRsp(uiPortNo, uipISDUErr, cpErrMsg));
  return ERR_NONE;
}

DLL_API
int16 mst_ReadEvent(uint16 uiPortNo,
                    uchar08 *ucQualifier,
                    uint16 *uiCode,
                    char08 *cpErrMsg)
{
  clock_t timeout;
  uchar08 pdIn[MAX_PD_BUF_LEN], ucpBuf[3];
  mst_StatusT status;

  for (timeout = clock() + MST_EVENT_TIMEOUT * CLOCKS_PER_SEC;
       timeout >= clock();)
  {
    CHK_RETURN(mst_GetStatus(uiPortNo, ucpBuf, pdIn, MAX_PD_BUF_LEN, cpErrMsg));
    *((uchar08*)&status) = ucpBuf[0];
    if (status.Event)
    {
      CHK_RETURN(comm_SendRecv(uiPortNo, CH_IOL, CMD_GET_EVENT, NULL, 0, ucpBuf, 3, cpErrMsg));
      *ucQualifier = ucpBuf[0];
      *uiCode = GET_BIGEND_UINT16(&(ucpBuf[1]));
      return ERR_NONE;
    }
    
    Sleep(50);
  }

  snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
    "No received event after timeout (%d sec).", MST_EVENT_TIMEOUT);
  return ERR_INTERNAL;
}

DLL_API
int16 mst_ResetParameters2Default(uint16 uiPortNo, char08 *cpErrMsg)
{
	uchar08 ucpSendBuf[1] = { 0 }; //not used
	return comm_WriteParameter(uiPortNo, IQParResetParameters, ucpSendBuf, 1, cpErrMsg);
}

DLL_API
int16 mst_Reboot(uint16 uiPortNo, char08 *cpErrMsg)
{
	uchar08 ucpSendBuf[1] = { 0 }; //not used
	return comm_WriteParameter(uiPortNo, IQParRebootDevice, ucpSendBuf, 1, cpErrMsg);
}