/*
 * Licensed under the GNU General Public License version 2 with exceptions. See
 * LICENSE file in the project root for full license information
 */

/** \file
 * \brief
 * CAN over EtherCAT (CoE) module.
 *
 * SDO read / write and SDO service functions
 */

#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatcoe.h"

/** SDO structure, not to be confused with EcSDOserviceT */
PACKED_BEGIN
typedef struct PACKED
{
   ec_mbxheadert   MbxHeader;
   uint16          CANOpen;
   uint8           Command;
   uint16          Index;
   uint8           SubIndex;
   union
   {
      uint8   bdata[0x200]; /* variants for easy data access */
      uint16  wdata[0x100];
      uint32  ldata[0x80];
   };
} ec_SDOt;
PACKED_END

/** SDO service structure */
PACKED_BEGIN
typedef struct PACKED
{
   ec_mbxheadert   MbxHeader;
   uint16          CANOpen;
   uint8           Opcode;
   uint8           Reserved;
   uint16          Fragments;
   union
   {
      uint8   bdata[0x200]; /* variants for easy data access */
      uint16  wdata[0x100];
      uint32  ldata[0x80];
   };
} ec_SDOservicet;
PACKED_END

/** Report SDO error.
 *
 * @param[in]  context    = context struct
 * @param[in]  Slave      = Slave number
 * @param[in]  Index      = Index that generated error
 * @param[in]  SubIdx     = Subindex that generated error
 * @param[in]  AbortCode  = Abortcode, see EtherCAT documentation for list
 */
void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
{
   ec_errort Ec;

   memset(&Ec, 0, sizeof(Ec));
   Ec.Time = osal_current_time();
   Ec.Slave = Slave;
   Ec.Index = Index;
   Ec.SubIdx = SubIdx;
   *(context->ecaterror) = TRUE;
   Ec.Etype = EC_ERR_TYPE_SDO_ERROR;
   Ec.AbortCode = AbortCode;
   ecx_pusherror(context, &Ec);
}

/** Report SDO info error
 *
 * @param[in]  context    = context struct
 * @param[in]  Slave      = Slave number
 * @param[in]  Index      = Index that generated error
 * @param[in]  SubIdx     = Subindex that generated error
 * @param[in]  AbortCode  = Abortcode, see EtherCAT documentation for list
 */
static void ecx_SDOinfoerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
{
   ec_errort Ec;

   memset(&Ec, 0, sizeof(Ec));
   Ec.Slave = Slave;
   Ec.Index = Index;
   Ec.SubIdx = SubIdx;
   *(context->ecaterror) = TRUE;
   Ec.Etype = EC_ERR_TYPE_SDOINFO_ERROR;
   Ec.AbortCode = AbortCode;
   ecx_pusherror(context, &Ec);
}

/** CoE SDO read, blocking. Single subindex or Complete Access.
 *
 * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes
 * then a "expedited" response is returned, otherwise a "normal" response. If a "normal"
 * response is larger than the mailbox size then the response is segmented. The function
 * will combine all segments and copy them to the parameter buffer.
 *
 * @param[in]  context    = context struct
 * @param[in]  slave      = Slave number
 * @param[in]  index      = Index to read
 * @param[in]  subindex   = Subindex to read, must be 0 or 1 if CA is used.
 * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes read.
 * @param[in,out] psize   = Size in bytes of parameter buffer, returns bytes read from SDO.
 * @param[out] p          = Pointer to parameter buffer
 * @param[in]  timeout    = Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 */
int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex,
               boolean CA, int *psize, void *p, int timeout)
{
   ec_SDOt *SDOp, *aSDOp;
   uint16 bytesize, Framedatasize;
   int wkc;
   int32 SDOlen;
   uint8 *bp;
   uint8 *hp;
   ec_mbxbuft MbxIn, MbxOut;
   uint8 cnt, toggle;
   boolean NotLast;

   ec_clearmbx(&MbxIn);
   /* Empty slave out mailbox if something is in. Timeout set to 0 */
   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
   ec_clearmbx(&MbxOut);
   aSDOp = (ec_SDOt *)&MbxIn;
   SDOp = (ec_SDOt *)&MbxOut;
   SDOp->MbxHeader.length = htoes(0x000a);
   SDOp->MbxHeader.address = htoes(0x0000);
   SDOp->MbxHeader.priority = 0x00;
   /* get new mailbox count value, used as session handle */
   cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
   context->slavelist[slave].mbx_cnt = cnt;
   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
   if (CA)
   {
      SDOp->Command = ECT_SDO_UP_REQ_CA; /* upload request complete access */
   }
   else
   {
      SDOp->Command = ECT_SDO_UP_REQ; /* upload request normal */
   }
   SDOp->Index = htoes(index);
   if (CA && (subindex > 1))
   {
      subindex = 1;
   }
   SDOp->SubIndex = subindex;
   SDOp->ldata[0] = 0;
   /* send CoE request to slave */
   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
   if (wkc > 0) /* succeeded to place mailbox in slave ? */
   {
      /* clean mailboxbuffer */
      ec_clearmbx(&MbxIn);
      /* read slave response */
      wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
      if (wkc > 0) /* succeeded to read slave response ? */
      {
         /* slave response should be CoE, SDO response and the correct index */
         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
             ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
              (aSDOp->Index == SDOp->Index))
         {
            if ((aSDOp->Command & 0x02) > 0)
            {
               /* expedited frame response */
               bytesize = 4 - ((aSDOp->Command >> 2) & 0x03);
               if (*psize >= bytesize) /* parameter buffer big enough ? */
               {
                  /* copy parameter in parameter buffer */
                  memcpy(p, &aSDOp->ldata[0], bytesize);
                  /* return the real parameter size */
                  *psize = bytesize;
               }
               else
               {
                  wkc = 0;
                  ecx_packeterror(context, slave, index, subindex, 3); /*  data container too small for type */
               }
            }
            else
            { /* normal frame response */
               SDOlen = etohl(aSDOp->ldata[0]);
               /* Does parameter fit in parameter buffer ? */
               if (SDOlen <= *psize)
               {
                  bp = p;
                  hp = p;
                  /* calculate mailbox transfer size */
                  Framedatasize = (etohs(aSDOp->MbxHeader.length) - 10);
                  if (Framedatasize < SDOlen) /* transfer in segments? */
                  {
                     /* copy parameter data in parameter buffer */
                     memcpy(hp, &aSDOp->ldata[1], Framedatasize);
                     /* increment buffer pointer */
                     hp += Framedatasize;
                     *psize = Framedatasize;
                     NotLast = TRUE;
                     toggle= 0x00;
                     while (NotLast) /* segmented transfer */
                     {
                        SDOp = (ec_SDOt *)&MbxOut;
                        SDOp->MbxHeader.length = htoes(0x000a);
                        SDOp->MbxHeader.address = htoes(0x0000);
                        SDOp->MbxHeader.priority = 0x00;
                        cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
                        context->slavelist[slave].mbx_cnt = cnt;
                        SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
                        SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
                        SDOp->Command = ECT_SDO_SEG_UP_REQ + toggle; /* segment upload request */
                        SDOp->Index = htoes(index);
                        SDOp->SubIndex = subindex;
                        SDOp->ldata[0] = 0;
                        /* send segmented upload request to slave */
                        wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
                        /* is mailbox transferred to slave ? */
                        if (wkc > 0)
                        {
                           ec_clearmbx(&MbxIn);
                           /* read slave response */
                           wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
                           /* has slave responded ? */
                           if (wkc > 0)
                           {
                              /* slave response should be CoE, SDO response */
                              if ((((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
                                   ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
                                   ((aSDOp->Command & 0xe0) == 0x00)))
                                        {
                                 /* calculate mailbox transfer size */
                                 Framedatasize = etohs(aSDOp->MbxHeader.length) - 3;
                                 if ((aSDOp->Command & 0x01) > 0)
                                 { /* last segment */
                                    NotLast = FALSE;
                                    if (Framedatasize == 7)
                                       /* subtract unused bytes from frame */
                                       Framedatasize = Framedatasize - ((aSDOp->Command & 0x0e) >> 1);
                                    /* copy to parameter buffer */
                                    memcpy(hp, &(aSDOp->Index), Framedatasize);
                                 }
                                 else /* segments follow */
                                 {
                                    /* copy to parameter buffer */
                                    memcpy(hp, &(aSDOp->Index), Framedatasize);
                                    /* increment buffer pointer */
                                    hp += Framedatasize;
                                 }
                                 /* update parameter size */
                                 *psize += Framedatasize;
                              }
                              /* unexpected frame returned from slave */
                              else
                              {
                                 NotLast = FALSE;
                                 if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
                                    ecx_SDOerror(context, slave, index, subindex, etohl(aSDOp->ldata[0]));
                                 else
                                    ecx_packeterror(context, slave, index, subindex, 1); /* Unexpected frame returned */
                                 wkc = 0;
                              }
                           }
                        }
                        toggle = toggle ^ 0x10; /* toggle bit for segment request */
                     }
                  }
                  /* non segmented transfer */
                  else
                  {
                     /* copy to parameter buffer */
                     memcpy(bp, &aSDOp->ldata[1], SDOlen);
                     *psize = SDOlen;
                  }
               }
               /* parameter buffer too small */
               else
               {
                  wkc = 0;
                  ecx_packeterror(context, slave, index, subindex, 3); /*  data container too small for type */
               }
            }
         }
         /* other slave response */
         else
         {
            if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
            {
               ecx_SDOerror(context, slave, index, subindex, etohl(aSDOp->ldata[0]));
            }
            else
            {
               ecx_packeterror(context, slave, index, subindex, 1); /* Unexpected frame returned */
            }
            wkc = 0;
         }
      }
   }
   return wkc;
}

/** CoE SDO write, blocking. Single subindex or Complete Access.
 *
 * A "normal" download request is issued, unless we have
 * small data, then a "expedited" transfer is used. If the parameter is larger than
 * the mailbox size then the download is segmented. The function will split the
 * parameter data in segments and send them to the slave one by one.
 *
 * @param[in]  context    = context struct
 * @param[in]  Slave      = Slave number
 * @param[in]  Index      = Index to write
 * @param[in]  SubIndex   = Subindex to write, must be 0 or 1 if CA is used.
 * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes written.
 * @param[in]  psize      = Size in bytes of parameter buffer.
 * @param[out] p          = Pointer to parameter buffer
 * @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 */
int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex,
                 boolean CA, int psize, const void *p, int Timeout)
{
   ec_SDOt *SDOp, *aSDOp;
   int wkc, maxdata, framedatasize;
   ec_mbxbuft MbxIn, MbxOut;
   uint8 cnt, toggle;
   boolean  NotLast;
   const uint8 *hp;

   ec_clearmbx(&MbxIn);
   /* Empty slave out mailbox if something is in. Timeout set to 0 */
   wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, 0);
   ec_clearmbx(&MbxOut);
   aSDOp = (ec_SDOt *)&MbxIn;
   SDOp = (ec_SDOt *)&MbxOut;
   maxdata = context->slavelist[Slave].mbx_l - 0x10; /* data section=mailbox size - 6 mbx - 2 CoE - 8 sdo req */
   /* if small data use expedited transfer */
   if ((psize <= 4) && !CA)
   {
      SDOp->MbxHeader.length = htoes(0x000a);
      SDOp->MbxHeader.address = htoes(0x0000);
      SDOp->MbxHeader.priority = 0x00;
      /* get new mailbox counter, used for session handle */
      cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
      context->slavelist[Slave].mbx_cnt = cnt;
      SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
      SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits */
      SDOp->Command = ECT_SDO_DOWN_EXP | (((4 - psize) << 2) & 0x0c); /* expedited SDO download transfer */
      SDOp->Index = htoes(Index);
      SDOp->SubIndex = SubIndex;
      hp = p;
      /* copy parameter data to mailbox */
      memcpy(&SDOp->ldata[0], hp, psize);
      /* send mailbox SDO download request to slave */
      wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
      if (wkc > 0)
      {
         ec_clearmbx(&MbxIn);
         /* read slave response */
         wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
         if (wkc > 0)
         {
            /* response should be CoE, SDO response, correct index and subindex */
            if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
                ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
                 (aSDOp->Index == SDOp->Index) &&
                 (aSDOp->SubIndex == SDOp->SubIndex))
            {
                 /* all OK */
            }
            /* unexpected response from slave */
            else
            {
               if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
               {
                  ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
               }
               else
               {
                  ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
               }
               wkc = 0;
            }
         }
      }
   }
   else
   {
      framedatasize = psize;
      NotLast = FALSE;
      if (framedatasize > maxdata)
      {
         framedatasize = maxdata;  /*  segmented transfer needed  */
         NotLast = TRUE;
      }
      SDOp->MbxHeader.length = htoes((uint16)(0x0a + framedatasize));
      SDOp->MbxHeader.address = htoes(0x0000);
      SDOp->MbxHeader.priority = 0x00;
      /* get new mailbox counter, used for session handle */
      cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
      context->slavelist[Slave].mbx_cnt = cnt;
      SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
      SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits */
      if (CA)
      {
         SDOp->Command = ECT_SDO_DOWN_INIT_CA; /* Complete Access, normal SDO init download transfer */
      }
      else
      {
         SDOp->Command = ECT_SDO_DOWN_INIT; /* normal SDO init download transfer */
      }
      SDOp->Index = htoes(Index);
      SDOp->SubIndex = SubIndex;
      if (CA && (SubIndex > 1))
      {
         SDOp->SubIndex = 1;
      }
      SDOp->ldata[0] = htoel(psize);
      hp = p;
      /* copy parameter data to mailbox */
      memcpy(&SDOp->ldata[1], hp, framedatasize);
      hp += framedatasize;
      psize -= framedatasize;
      /* send mailbox SDO download request to slave */
      wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
      if (wkc > 0)
      {
         ec_clearmbx(&MbxIn);
         /* read slave response */
         wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
         if (wkc > 0)
         {
            /* response should be CoE, SDO response, correct index and subindex */
            if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
                ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
                 (aSDOp->Index == SDOp->Index) &&
                 (aSDOp->SubIndex == SDOp->SubIndex))
            {
               /* all ok */
               maxdata += 7;
               toggle = 0;
               /* repeat while segments left */
               while (NotLast)
               {
                  SDOp = (ec_SDOt *)&MbxOut;
                  framedatasize = psize;
                  NotLast = FALSE;
                  SDOp->Command = 0x01; /* last segment */
                  if (framedatasize > maxdata)
                  {
                     framedatasize = maxdata;  /*  more segments needed  */
                     NotLast = TRUE;
                     SDOp->Command = 0x00; /* segments follow */
                  }
                  if (!NotLast && (framedatasize < 7))
                  {
                     SDOp->MbxHeader.length = htoes(0x0a); /* minimum size */
                     SDOp->Command = (uint8)(0x01 + ((7 - framedatasize) << 1)); /* last segment reduced octets */
                  }
                  else
                  {
                     SDOp->MbxHeader.length = htoes((uint16)(framedatasize + 3)); /* data + 2 CoE + 1 SDO */
                  }
                  SDOp->MbxHeader.address = htoes(0x0000);
                  SDOp->MbxHeader.priority = 0x00;
                  /* get new mailbox counter value */
                  cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
                  context->slavelist[Slave].mbx_cnt = cnt;
                  SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
                  SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOREQ << 12)); /* number 9bits service upper 4 bits (SDO request) */
                  SDOp->Command = SDOp->Command + toggle; /* add toggle bit to command byte */
                  /* copy parameter data to mailbox */
                  memcpy(&SDOp->Index, hp, framedatasize);
                  /* update parameter buffer pointer */
                  hp += framedatasize;
                  psize -= framedatasize;
                  /* send SDO download request */
                  wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
                  if (wkc > 0)
                  {
                     ec_clearmbx(&MbxIn);
                     /* read slave response */
                     wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, Timeout);
                     if (wkc > 0)
                     {
                        if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
                            ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_SDORES) &&
                            ((aSDOp->Command & 0xe0) == 0x20))
                        {
                                   /* all OK, nothing to do */
                        }
                        else
                        {
                           if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
                           {
                              ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
                           }
                           else
                           {
                              ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
                           }
                           wkc = 0;
                           NotLast = FALSE;
                        }
                     }
                  }
                  toggle = toggle ^ 0x10; /* toggle bit for segment request */
               }
            }
            /* unexpected response from slave */
            else
            {
               if (aSDOp->Command == ECT_SDO_ABORT) /* SDO abort frame received */
               {
                  ecx_SDOerror(context, Slave, Index, SubIndex, etohl(aSDOp->ldata[0]));
               }
               else
               {
                  ecx_packeterror(context, Slave, Index, SubIndex, 1); /* Unexpected frame returned */
               }
               wkc = 0;
            }
         }
      }
   }

   return wkc;
}

/** CoE RxPDO write, blocking.
 *
 * A RxPDO download request is issued.
 *
 * @param[in]  context       = context struct
 * @param[in]  Slave         = Slave number
 * @param[in]  RxPDOnumber   = Related RxPDO number
 * @param[in]  psize         = Size in bytes of PDO buffer.
 * @param[out] p             = Pointer to PDO buffer
 * @return Workcounter from last slave response
 */
int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber, int psize, const void *p)
{
   ec_SDOt *SDOp;
   int wkc, maxdata, framedatasize;
   ec_mbxbuft MbxIn, MbxOut;
   uint8 cnt;

   ec_clearmbx(&MbxIn);
   /* Empty slave out mailbox if something is in. Timeout set to 0 */
   wkc = ecx_mbxreceive(context, Slave, (ec_mbxbuft *)&MbxIn, 0);
   ec_clearmbx(&MbxOut);
   SDOp = (ec_SDOt *)&MbxOut;
   maxdata = context->slavelist[Slave].mbx_l - 0x08; /* data section=mailbox size - 6 mbx - 2 CoE */
   framedatasize = psize;
   if (framedatasize > maxdata)
   {
      framedatasize = maxdata;  /*  limit transfer */
   }
   SDOp->MbxHeader.length = htoes((uint16)(0x02 + framedatasize));
   SDOp->MbxHeader.address = htoes(0x0000);
   SDOp->MbxHeader.priority = 0x00;
   /* get new mailbox counter, used for session handle */
   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
   context->slavelist[Slave].mbx_cnt = cnt;
   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
   SDOp->CANOpen = htoes((RxPDOnumber & 0x01ff) + (ECT_COES_RXPDO << 12)); /* number 9bits service upper 4 bits */
   /* copy PDO data to mailbox */
   memcpy(&SDOp->Command, p, framedatasize);
   /* send mailbox RxPDO request to slave */
   wkc = ecx_mbxsend(context, Slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);

   return wkc;
}

/** CoE TxPDO read remote request, blocking.
 *
 * A TxPDO download request is issued.
 *
 * @param[in]  context       = context struct
 * @param[in]  slave         = Slave number
 * @param[in]  TxPDOnumber   = Related TxPDO number
 * @param[in,out] psize      = Size in bytes of PDO buffer, returns bytes read from PDO.
 * @param[out] p             = Pointer to PDO buffer
 * @param[in]  timeout       = Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 */
int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout)
{
   ec_SDOt *SDOp, *aSDOp;
   int wkc;
   ec_mbxbuft MbxIn, MbxOut;
   uint8 cnt;
   uint16 framedatasize;

   ec_clearmbx(&MbxIn);
   /* Empty slave out mailbox if something is in. Timeout set to 0 */
   wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
   ec_clearmbx(&MbxOut);
   aSDOp = (ec_SDOt *)&MbxIn;
   SDOp = (ec_SDOt *)&MbxOut;
   SDOp->MbxHeader.length = htoes(0x02);
   SDOp->MbxHeader.address = htoes(0x0000);
   SDOp->MbxHeader.priority = 0x00;
   /* get new mailbox counter, used for session handle */
   cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
   context->slavelist[slave].mbx_cnt = cnt;
   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
   SDOp->CANOpen = htoes((TxPDOnumber & 0x01ff) + (ECT_COES_TXPDO_RR << 12)); /* number 9bits service upper 4 bits */
   wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
   if (wkc > 0)
   {
      /* clean mailboxbuffer */
      ec_clearmbx(&MbxIn);
      /* read slave response */
      wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
      if (wkc > 0) /* succeeded to read slave response ? */
      {
         /* slave response should be CoE, TxPDO */
         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
             ((etohs(aSDOp->CANOpen) >> 12) == ECT_COES_TXPDO))
         {
            /* TxPDO response */
            framedatasize = (aSDOp->MbxHeader.length - 2);
            if (*psize >= framedatasize) /* parameter buffer big enough ? */
            {
               /* copy parameter in parameter buffer */
               memcpy(p, &aSDOp->Command, framedatasize);
               /* return the real parameter size */
               *psize = framedatasize;
            }
            /* parameter buffer too small */
            else
            {
               wkc = 0;
               ecx_packeterror(context, slave, 0, 0, 3); /*  data container too small for type */
            }
         }
         /* other slave response */
         else
         {
            if ((aSDOp->Command) == ECT_SDO_ABORT) /* SDO abort frame received */
            {
               ecx_SDOerror(context, slave, 0, 0, etohl(aSDOp->ldata[0]));
            }
            else
            {
               ecx_packeterror(context, slave, 0, 0, 1); /* Unexpected frame returned */
            }
            wkc = 0;
         }
      }
   }

   return wkc;
}

/** Read PDO assign structure
 * @param[in]  context       = context struct
 * @param[in]  Slave         = Slave number
 * @param[in]  PDOassign     = PDO assign object
 * @return total bitlength of PDO assign
 */
uint32 ecx_readPDOassign(ecx_contextt *context, uint16 Slave, uint16 PDOassign)
{
   uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
   uint8 subcnt;
   int wkc, rdl;
   int32 rdat2;
   uint32 bsize = 0;

   rdl = sizeof(rdat); rdat = 0;
   /* read PDO assign subindex 0 ( = number of PDO's) */
   wkc = ecx_SDOread(context, Slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
   rdat = etohs(rdat);
   /* positive result from slave ? */
   if ((wkc > 0) && (rdat > 0))
   {
      /* number of available sub indexes */
      nidx = rdat;
      bsize = 0;
      /* read all PDO's */
      for (idxloop = 1; idxloop <= nidx; idxloop++)
      {
         rdl = sizeof(rdat); rdat = 0;
         /* read PDO assign */
         wkc = ecx_SDOread(context, Slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
         /* result is index of PDO */
         idx = etohs(rdat);
         if (idx > 0)
         {
            rdl = sizeof(subcnt); subcnt = 0;
            /* read number of subindexes of PDO */
            wkc = ecx_SDOread(context, Slave,idx, 0x00, FALSE, &rdl, &subcnt, EC_TIMEOUTRXM);
            subidx = subcnt;
            /* for each subindex */
            for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
            {
               rdl = sizeof(rdat2); rdat2 = 0;
               /* read SDO that is mapped in PDO */
               wkc = ecx_SDOread(context, Slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
               rdat2 = etohl(rdat2);
               /* extract bitlength of SDO */
               if (LO_BYTE(rdat2) < 0xff)
               {
                  bsize += LO_BYTE(rdat2);
               }
               else
               {
                  rdl = sizeof(rdat); rdat = htoes(0xff);
                  /* read Object Entry in Object database */
//                  wkc = ec_readOEsingle(idx, (uint8)SubCount, pODlist, pOElist);
                  bsize += etohs(rdat);
               }
            }
         }
      }
   }
   /* return total found bitlength (PDO) */
   return bsize;
}

/** Read PDO assign structure in Complete Access mode
 * @param[in]  context       = context struct
 * @param[in]  Slave         = Slave number
 * @param[in]  Thread_n      = Calling thread index
 * @param[in]  PDOassign     = PDO assign object
 * @return total bitlength of PDO assign
 */
uint32 ecx_readPDOassignCA(ecx_contextt *context, uint16 Slave, int Thread_n,
      uint16 PDOassign)
{
   uint16 idxloop, nidx, subidxloop, idx, subidx;
   int wkc, rdl;
   uint32 bsize = 0;

   /* find maximum size of PDOassign buffer */
   rdl = sizeof(ec_PDOassignt);
   context->PDOassign[Thread_n].n=0;
   /* read rxPDOassign in CA mode, all subindexes are read in one struct */
   wkc = ecx_SDOread(context, Slave, PDOassign, 0x00, TRUE, &rdl,
         &(context->PDOassign[Thread_n]), EC_TIMEOUTRXM);
   /* positive result from slave ? */
   if ((wkc > 0) && (context->PDOassign[Thread_n].n > 0))
   {
      nidx = context->PDOassign[Thread_n].n;
      bsize = 0;
      /* for each PDO do */
      for (idxloop = 1; idxloop <= nidx; idxloop++)
      {
         /* get index from PDOassign struct */
         idx = etohs(context->PDOassign[Thread_n].index[idxloop - 1]);
         if (idx > 0)
         {
            rdl = sizeof(ec_PDOdesct); context->PDOdesc[Thread_n].n = 0;
            /* read SDO's that are mapped in PDO, CA mode */
            wkc = ecx_SDOread(context, Slave,idx, 0x00, TRUE, &rdl,
                  &(context->PDOdesc[Thread_n]), EC_TIMEOUTRXM);
            subidx = context->PDOdesc[Thread_n].n;
            /* extract all bitlengths of SDO's */
            for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
            {
               bsize += LO_BYTE(etohl(context->PDOdesc[Thread_n].PDO[subidxloop -1]));
            }
         }
      }
   }

   /* return total found bitlength (PDO) */
   return bsize;
}

/** CoE read PDO mapping.
 *
 * CANopen has standard indexes defined for PDO mapping. This function
 * tries to read them and collect a full input and output mapping size
 * of designated slave.
 *
 * Principal structure in slave:\n
 * 1C00:00 is number of SM defined\n
 * 1C00:01 SM0 type -> 1C10\n
 * 1C00:02 SM1 type -> 1C11\n
 * 1C00:03 SM2 type -> 1C12\n
 * 1C00:04 SM3 type -> 1C13\n
 * Type 0 = unused, 1 = mailbox in, 2 = mailbox out,
 * 3 = outputs (RxPDO), 4 = inputs (TxPDO).
 *
 * 1C12:00 is number of PDO's defined for SM2\n
 * 1C12:01 PDO assign SDO #1 -> f.e. 1A00\n
 * 1C12:02 PDO assign SDO #2 -> f.e. 1A04\
 *
 * 1A00:00 is number of object defined for this PDO\n
 * 1A00:01 object mapping #1, f.e. 60100710 (SDO 6010 SI 07 bitlength 0x10)
 *
 * @param[in]  context = context struct
 * @param[in]  Slave   = Slave number
 * @param[out] Osize   = Size in bits of output mapping (rxPDO) found
 * @param[out] Isize   = Size in bits of input mapping (txPDO) found
 * @return >0 if mapping successful.
 */
int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, uint32 *Osize, uint32 *Isize)
{
   int wkc, rdl;
   int retVal = 0;
   uint8 nSM, iSM, tSM;
   uint32 Tsize;
   uint8 SMt_bug_add;

   *Isize = 0;
   *Osize = 0;
   SMt_bug_add = 0;
   rdl = sizeof(nSM); nSM = 0;
   /* read SyncManager Communication Type object count */
   wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
   /* positive result from slave ? */
   if ((wkc > 0) && (nSM > 2))
   {
      /* limit to maximum number of SM defined, if true the slave can't be configured */
      if (nSM > EC_MAXSM)
         nSM = EC_MAXSM;
      /* iterate for every SM type defined */
      for (iSM = 2 ; iSM < nSM ; iSM++)
      {
         rdl = sizeof(tSM); tSM = 0;
         /* read SyncManager Communication Type */
         wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
         if (wkc > 0)
         {
// start slave bug prevention code, remove if possible
            if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
            {
               SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
            }
            if(tSM)
            {
               tSM += SMt_bug_add; // only add if SMt > 0
            }
            if((iSM == 2) && (tSM == 0)) // SM2 has type 0, this is a bug in the slave!
            {
               tSM = 3;
            }
            if((iSM == 3) && (tSM == 0)) // SM3 has type 0, this is a bug in the slave!
            {
               tSM = 4;
            }
// end slave bug prevention code

            context->slavelist[Slave].SMtype[iSM] = tSM;
            /* check if SM is unused -> clear enable flag */
            if (tSM == 0)
            {
               context->slavelist[Slave].SM[iSM].SMflags =
                  htoel( etohl(context->slavelist[Slave].SM[iSM].SMflags) & EC_SMENABLEMASK);
            }
            if ((tSM == 3) || (tSM == 4))
            {
               /* read the assign PDO */
               Tsize = ecx_readPDOassign(context, Slave, ECT_SDO_PDOASSIGN + iSM );
               /* if a mapping is found */
               if (Tsize)
               {
                  context->slavelist[Slave].SM[iSM].SMlength = htoes((uint16)((Tsize + 7) / 8));
                  if (tSM == 3)
                  {
                     /* we are doing outputs */
                     *Osize += Tsize;
                  }
                  else
                  {
                     /* we are doing inputs */
                     *Isize += Tsize;
                  }
               }
            }
         }
      }
   }

   /* found some I/O bits ? */
   if ((*Isize > 0) || (*Osize > 0))
   {
      retVal = 1;
   }

   return retVal;
}

/** CoE read PDO mapping in Complete Access mode (CA).
 *
 * CANopen has standard indexes defined for PDO mapping. This function
 * tries to read them and collect a full input and output mapping size
 * of designated slave. Slave has to support CA, otherwise use ec_readPDOmap().
 *
 * @param[in]  context  = context struct
 * @param[in]  Slave    = Slave number
 * @param[in]  Thread_n = Calling thread index
 * @param[out] Osize    = Size in bits of output mapping (rxPDO) found
 * @param[out] Isize    = Size in bits of input mapping (txPDO) found
 * @return >0 if mapping successful.
 */
int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, uint32 *Osize, uint32 *Isize)
{
   int wkc, rdl;
   int retVal = 0;
   uint8 nSM, iSM, tSM;
   uint32 Tsize;
   uint8 SMt_bug_add;

   *Isize = 0;
   *Osize = 0;
   SMt_bug_add = 0;
   rdl = sizeof(ec_SMcommtypet);
   context->SMcommtype[Thread_n].n = 0;
   /* read SyncManager Communication Type object count Complete Access*/
   wkc = ecx_SDOread(context, Slave, ECT_SDO_SMCOMMTYPE, 0x00, TRUE, &rdl,
         &(context->SMcommtype[Thread_n]), EC_TIMEOUTRXM);
   /* positive result from slave ? */
   if ((wkc > 0) && (context->SMcommtype[Thread_n].n > 2))
   {
      nSM = context->SMcommtype[Thread_n].n;
      /* limit to maximum number of SM defined, if true the slave can't be configured */
      if (nSM > EC_MAXSM)
      {
         nSM = EC_MAXSM;
         ecx_packeterror(context, Slave, 0, 0, 10); /* #SM larger than EC_MAXSM */
      }
      /* iterate for every SM type defined */
      for (iSM = 2 ; iSM < nSM ; iSM++)
      {
         tSM = context->SMcommtype[Thread_n].SMtype[iSM];

// start slave bug prevention code, remove if possible
         if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
         {
            SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
         }
         if(tSM)
         {
            tSM += SMt_bug_add; // only add if SMt > 0
         }
// end slave bug prevention code

         context->slavelist[Slave].SMtype[iSM] = tSM;
         /* check if SM is unused -> clear enable flag */
         if (tSM == 0)
         {
            context->slavelist[Slave].SM[iSM].SMflags =
               htoel( etohl(context->slavelist[Slave].SM[iSM].SMflags) & EC_SMENABLEMASK);
         }
         if ((tSM == 3) || (tSM == 4))
         {
            /* read the assign PDO */
            Tsize = ecx_readPDOassignCA(context, Slave, Thread_n,
                  ECT_SDO_PDOASSIGN + iSM );
            /* if a mapping is found */
            if (Tsize)
            {
               context->slavelist[Slave].SM[iSM].SMlength = htoes((uint16)((Tsize + 7) / 8));
               if (tSM == 3)
               {
                  /* we are doing outputs */
                  *Osize += Tsize;
               }
               else
               {
                  /* we are doing inputs */
                  *Isize += Tsize;
               }
            }
         }
      }
   }

   /* found some I/O bits ? */
   if ((*Isize > 0) || (*Osize > 0))
   {
      retVal = 1;
   }
   return retVal;
}

/** CoE read Object Description List.
 *
 * @param[in]  context  = context struct
 * @param[in]  Slave    = Slave number.
 * @param[out] pODlist  = resulting Object Description list.
 * @return Workcounter of slave response.
 */
int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist)
{
   ec_SDOservicet *SDOp, *aSDOp;
   ec_mbxbuft MbxIn, MbxOut;
   int wkc;
   uint16 x, n, i, sp, offset;
   boolean stop;
   uint8 cnt;
   boolean First;

   pODlist->Slave = Slave;
   pODlist->Entries = 0;
   ec_clearmbx(&MbxIn);
   /* clear pending out mailbox in slave if available. Timeout is set to 0 */
   wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
   ec_clearmbx(&MbxOut);
   aSDOp = (ec_SDOservicet*)&MbxIn;
   SDOp = (ec_SDOservicet*)&MbxOut;
   SDOp->MbxHeader.length = htoes(0x0008);
   SDOp->MbxHeader.address = htoes(0x0000);
   SDOp->MbxHeader.priority = 0x00;
   /* Get new mailbox counter value */
   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
   context->slavelist[Slave].mbx_cnt = cnt;
   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
   SDOp->Opcode = ECT_GET_ODLIST_REQ; /* get object description list request */
   SDOp->Reserved = 0;
   SDOp->Fragments = 0; /* fragments left */
   SDOp->wdata[0] = htoes(0x01); /* all objects */
   /* send get object description list request to slave */
   wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
   /* mailbox placed in slave ? */
   if (wkc > 0)
   {
      x = 0;
      sp = 0;
      First = TRUE;
      offset = 1; /* offset to skip info header in first frame, otherwise set to 0 */
      do
      {
         stop = TRUE; /* assume this is last iteration */
         ec_clearmbx(&MbxIn);
         /* read slave response */
         wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
         /* got response ? */
         if (wkc > 0)
         {
            /* response should be CoE and "get object description list response" */
            if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
                ((aSDOp->Opcode & 0x7f) == ECT_GET_ODLIST_RES))
            {
               if (First)
               {
                  /* extract number of indexes from mailbox data size */
                  n = (etohs(aSDOp->MbxHeader.length) - (6 + 2)) / 2;
               }
               else
               {
                  /* extract number of indexes from mailbox data size */
                  n = (etohs(aSDOp->MbxHeader.length) - 6) / 2;
               }
               /* check if indexes fit in buffer structure */
               if ((sp + n) > EC_MAXODLIST)
               {
                  n = EC_MAXODLIST + 1 - sp;
                  ecx_SDOinfoerror(context, Slave, 0, 0, 0xf000000); /* Too many entries for master buffer */
                  stop = TRUE;
               }
               /* trim to maximum number of ODlist entries defined */
               if ((pODlist->Entries + n) > EC_MAXODLIST)
               {
                  n = EC_MAXODLIST - pODlist->Entries;
               }
               pODlist->Entries += n;
               /* extract indexes one by one */
               for (i = 0; i < n; i++)
               {
                  pODlist->Index[sp + i] = etohs(aSDOp->wdata[i + offset]);
               }
               sp += n;
               /* check if more fragments will follow */
               if (aSDOp->Fragments > 0)
               {
                  stop = FALSE;
               }
               First = FALSE;
               offset = 0;
            }
            /* got unexpected response from slave */
            else
            {
               if ((aSDOp->Opcode &  0x7f) == ECT_SDOINFO_ERROR) /* SDO info error received */
               {
                  ecx_SDOinfoerror(context, Slave, 0, 0, etohl(aSDOp->ldata[0]));
                  stop = TRUE;
               }
               else
               {
                  ecx_packeterror(context, Slave, 0, 0, 1); /* Unexpected frame returned */
               }
               wkc = 0;
               x += 20;
            }
         }
         x++;
      }
      while ((x <= 128) && !stop);
   }
   return wkc;
}

/** CoE read Object Description. Adds textual description to object indexes.
 *
 * @param[in]  context       = context struct
 * @param[in] Item           = Item number in ODlist.
 * @param[in,out] pODlist    = referencing Object Description list.
 * @return Workcounter of slave response.
 */
int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist)
{
   ec_SDOservicet *SDOp, *aSDOp;
   int wkc;
   uint16  n, Slave;
   ec_mbxbuft MbxIn, MbxOut;
   uint8 cnt;

   Slave = pODlist->Slave;
   pODlist->DataType[Item] = 0;
   pODlist->ObjectCode[Item] = 0;
   pODlist->MaxSub[Item] = 0;
   pODlist->Name[Item][0] = 0;
   ec_clearmbx(&MbxIn);
   /* clear pending out mailbox in slave if available. Timeout is set to 0 */
   wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
   ec_clearmbx(&MbxOut);
   aSDOp = (ec_SDOservicet*)&MbxIn;
   SDOp = (ec_SDOservicet*)&MbxOut;
   SDOp->MbxHeader.length = htoes(0x0008);
   SDOp->MbxHeader.address = htoes(0x0000);
   SDOp->MbxHeader.priority = 0x00;
   /* Get new mailbox counter value */
   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
   context->slavelist[Slave].mbx_cnt = cnt;
   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
   SDOp->Opcode = ECT_GET_OD_REQ; /* get object description request */
   SDOp->Reserved = 0;
   SDOp->Fragments = 0; /* fragments left */
   SDOp->wdata[0] = htoes(pODlist->Index[Item]); /* Data of Index */
   /* send get object description request to slave */
   wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
   /* mailbox placed in slave ? */
   if (wkc > 0)
   {
      ec_clearmbx(&MbxIn);
      /* read slave response */
      wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
      /* got response ? */
      if (wkc > 0)
      {
         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
             ((aSDOp->Opcode & 0x7f) == ECT_GET_OD_RES))
         {
            n = (etohs(aSDOp->MbxHeader.length) - 12); /* length of string(name of object) */
            if (n > EC_MAXNAME)
            {
               n = EC_MAXNAME; /* max chars */
            }
            pODlist->DataType[Item] = etohs(aSDOp->wdata[1]);
            pODlist->ObjectCode[Item] = aSDOp->bdata[5];
            pODlist->MaxSub[Item] = aSDOp->bdata[4];

            strncpy(pODlist->Name[Item] , (char *)&aSDOp->bdata[6], n);
            pODlist->Name[Item][n] = 0x00; /* String terminator */
         }
         /* got unexpected response from slave */
         else
         {
            if (((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR)) /* SDO info error received */
            {
               ecx_SDOinfoerror(context, Slave,pODlist->Index[Item], 0, etohl(aSDOp->ldata[0]));
            }
            else
            {
               ecx_packeterror(context, Slave,pODlist->Index[Item], 0, 1); /* Unexpected frame returned */
            }
            wkc = 0;
         }
      }
   }

   return wkc;
}

/** CoE read SDO service object entry, single subindex.
 * Used in ec_readOE().
 *
 * @param[in]  context       = context struct
 * @param[in] Item           = Item in ODlist.
 * @param[in] SubI           = Subindex of item in ODlist.
 * @param[in] pODlist        = Object description list for reference.
 * @param[out] pOElist       = resulting object entry structure.
 * @return Workcounter of slave response.
 */
int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist)
{
   ec_SDOservicet *SDOp, *aSDOp;
   int wkc;
   uint16 Index, Slave;
   int16 n;
   ec_mbxbuft MbxIn, MbxOut;
   uint8 cnt;

   wkc = 0;
   Slave = pODlist->Slave;
   Index = pODlist->Index[Item];
   ec_clearmbx(&MbxIn);
   /* clear pending out mailbox in slave if available. Timeout is set to 0 */
   wkc = ecx_mbxreceive(context, Slave, &MbxIn, 0);
   ec_clearmbx(&MbxOut);
   aSDOp = (ec_SDOservicet*)&MbxIn;
   SDOp = (ec_SDOservicet*)&MbxOut;
   SDOp->MbxHeader.length = htoes(0x000a);
   SDOp->MbxHeader.address = htoes(0x0000);
   SDOp->MbxHeader.priority = 0x00;
   /* Get new mailbox counter value */
   cnt = ec_nextmbxcnt(context->slavelist[Slave].mbx_cnt);
   context->slavelist[Slave].mbx_cnt = cnt;
   SDOp->MbxHeader.mbxtype = ECT_MBXT_COE + MBX_HDR_SET_CNT(cnt); /* CoE */
   SDOp->CANOpen = htoes(0x000 + (ECT_COES_SDOINFO << 12)); /* number 9bits service upper 4 bits */
   SDOp->Opcode = ECT_GET_OE_REQ; /* get object entry description request */
   SDOp->Reserved = 0;
   SDOp->Fragments = 0;      /* fragments left */
   SDOp->wdata[0] = htoes(Index);      /* Index */
   SDOp->bdata[2] = SubI;       /* SubIndex */
   SDOp->bdata[3] = 1 + 2 + 4; /* get access rights, object category, PDO */
   /* send get object entry description request to slave */
   wkc = ecx_mbxsend(context, Slave, &MbxOut, EC_TIMEOUTTXM);
   /* mailbox placed in slave ? */
   if (wkc > 0)
   {
      ec_clearmbx(&MbxIn);
      /* read slave response */
      wkc = ecx_mbxreceive(context, Slave, &MbxIn, EC_TIMEOUTRXM);
      /* got response ? */
      if (wkc > 0)
      {
         if (((aSDOp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_COE) &&
             ((aSDOp->Opcode &  0x7f) == ECT_GET_OE_RES))
         {
            pOElist->Entries++;
            n = (etohs(aSDOp->MbxHeader.length) - 16); /* length of string(name of object) */
            if (n > EC_MAXNAME)
            {
               n = EC_MAXNAME; /* max string length */
            }
            if (n < 0 )
            {
               n = 0;
            }
            pOElist->ValueInfo[SubI] = aSDOp->bdata[3];
            pOElist->DataType[SubI] = etohs(aSDOp->wdata[2]);
            pOElist->BitLength[SubI] = etohs(aSDOp->wdata[3]);
            pOElist->ObjAccess[SubI] = etohs(aSDOp->wdata[4]);

            strncpy(pOElist->Name[SubI] , (char *)&aSDOp->wdata[5], n);
            pOElist->Name[SubI][n] = 0x00; /* string terminator */
         }
         /* got unexpected response from slave */
         else
         {
            if (((aSDOp->Opcode & 0x7f) == ECT_SDOINFO_ERROR)) /* SDO info error received */
            {
               ecx_SDOinfoerror(context, Slave, Index, SubI, etohl(aSDOp->ldata[0]));
            }
            else
            {
               ecx_packeterror(context, Slave, Index, SubI, 1); /* Unexpected frame returned */
            }
            wkc = 0;
         }
      }
   }

   return wkc;
}

/** CoE read SDO service object entry.
 *
 * @param[in] context        = context struct
 * @param[in] Item           = Item in ODlist.
 * @param[in] pODlist        = Object description list for reference.
 * @param[out] pOElist       = resulting object entry structure.
 * @return Workcounter of slave response.
 */
int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist)
{
   uint16 SubCount;
   int wkc;
   uint8 SubI;

   wkc = 0;
   pOElist->Entries = 0;
   SubI = pODlist->MaxSub[Item];
   /* for each entry found in ODlist */
   for (SubCount = 0; SubCount <= SubI; SubCount++)
   {
      /* read subindex of entry */
      wkc = ecx_readOEsingle(context, Item, (uint8)SubCount, pODlist, pOElist);
   }

   return wkc;
}

#ifdef EC_VER1
/** Report SDO error.
 *
 * @param[in]  Slave      = Slave number
 * @param[in]  Index      = Index that generated error
 * @param[in]  SubIdx     = Subindex that generated error
 * @param[in]  AbortCode  = Abortcode, see EtherCAT documentation for list
 * @see ecx_SDOerror
 */
void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode)
{
   ecx_SDOerror(&ecx_context, Slave, Index, SubIdx, AbortCode);
}

/** CoE SDO read, blocking. Single subindex or Complete Access.
 *
 * Only a "normal" upload request is issued. If the requested parameter is <= 4bytes
 * then a "expedited" response is returned, otherwise a "normal" response. If a "normal"
 * response is larger than the mailbox size then the response is segmented. The function
 * will combine all segments and copy them to the parameter buffer.
 *
 * @param[in]  slave      = Slave number
 * @param[in]  index      = Index to read
 * @param[in]  subindex   = Subindex to read, must be 0 or 1 if CA is used.
 * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes read.
 * @param[in,out] psize   = Size in bytes of parameter buffer, returns bytes read from SDO.
 * @param[out] p          = Pointer to parameter buffer
 * @param[in]  timeout    = Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 * @see ecx_SDOread
 */
int ec_SDOread(uint16 slave, uint16 index, uint8 subindex,
               boolean CA, int *psize, void *p, int timeout)
{
   return ecx_SDOread(&ecx_context, slave, index, subindex, CA, psize, p, timeout);
}

/** CoE SDO write, blocking. Single subindex or Complete Access.
 *
 * A "normal" download request is issued, unless we have
 * small data, then a "expedited" transfer is used. If the parameter is larger than
 * the mailbox size then the download is segmented. The function will split the
 * parameter data in segments and send them to the slave one by one.
 *
 * @param[in]  Slave      = Slave number
 * @param[in]  Index      = Index to write
 * @param[in]  SubIndex   = Subindex to write, must be 0 or 1 if CA is used.
 * @param[in]  CA         = FALSE = single subindex. TRUE = Complete Access, all subindexes written.
 * @param[in]  psize      = Size in bytes of parameter buffer.
 * @param[out] p          = Pointer to parameter buffer
 * @param[in]  Timeout    = Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 * @see ecx_SDOwrite
 */
int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex,
                boolean CA, int psize, const void *p, int Timeout)
{
   return ecx_SDOwrite(&ecx_context, Slave, Index, SubIndex, CA, psize, p, Timeout);
}

/** CoE RxPDO write, blocking.
 *
 * A RxPDO download request is issued.
 *
 * @param[in]  Slave         = Slave number
 * @param[in]  RxPDOnumber   = Related RxPDO number
 * @param[in]  psize         = Size in bytes of PDO buffer.
 * @param[out] p             = Pointer to PDO buffer
 * @return Workcounter from last slave response
 * @see ecx_RxPDO
 */
int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber, int psize, const void *p)
{
   return ecx_RxPDO(&ecx_context, Slave, RxPDOnumber, psize, p);
}

/** CoE TxPDO read remote request, blocking.
 *
 * A TxPDO download request is issued.
 *
 * @param[in]  slave         = Slave number
 * @param[in]  TxPDOnumber   = Related TxPDO number
 * @param[in,out] psize      = Size in bytes of PDO buffer, returns bytes read from PDO.
 * @param[out] p             = Pointer to PDO buffer
 * @param[in]  timeout       = Timeout in us, standard is EC_TIMEOUTRXM
 * @return Workcounter from last slave response
 * @see ecx_TxPDO
 */
int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout)
{
   return ecx_TxPDO(&ecx_context, slave, TxPDOnumber, psize, p, timeout);
}

/** Read PDO assign structure
 * @param[in]  Slave         = Slave number
 * @param[in]  PDOassign     = PDO assign object
 * @return total bitlength of PDO assign
 */
uint32 ec_readPDOassign(uint16 Slave, uint16 PDOassign)
{
   return ecx_readPDOassign(&ecx_context, Slave, PDOassign);
}

/** Read PDO assign structure in Complete Access mode
 * @param[in]  Slave         = Slave number
 * @param[in]  PDOassign     = PDO assign object
 * @param[in]  Thread_n      = Calling thread index
 * @return total bitlength of PDO assign
 * @see ecx_readPDOmap
 */
uint32 ec_readPDOassignCA(uint16 Slave, uint16 PDOassign, int Thread_n)
{
   return ecx_readPDOassignCA(&ecx_context, Slave, Thread_n, PDOassign);
}

/** CoE read PDO mapping.
 *
 * CANopen has standard indexes defined for PDO mapping. This function
 * tries to read them and collect a full input and output mapping size
 * of designated slave.
 *
 * For details, see #ecx_readPDOmap
 *
 * @param[in] Slave    = Slave number
 * @param[out] Osize   = Size in bits of output mapping (rxPDO) found
 * @param[out] Isize   = Size in bits of input mapping (txPDO) found
 * @return >0 if mapping succesful.
 */
int ec_readPDOmap(uint16 Slave, uint32 *Osize, uint32 *Isize)
{
   return ecx_readPDOmap(&ecx_context, Slave, Osize, Isize);
}

/** CoE read PDO mapping in Complete Access mode (CA).
 *
 * CANopen has standard indexes defined for PDO mapping. This function
 * tries to read them and collect a full input and output mapping size
 * of designated slave. Slave has to support CA, otherwise use ec_readPDOmap().
 *
 * @param[in] Slave    = Slave number
 * @param[in] Thread_n = Calling thread index
 * @param[out] Osize   = Size in bits of output mapping (rxPDO) found
 * @param[out] Isize   = Size in bits of input mapping (txPDO) found
 * @return >0 if mapping succesful.
 * @see ecx_readPDOmap ec_readPDOmapCA
 */
int ec_readPDOmapCA(uint16 Slave, int Thread_n, uint32 *Osize, uint32 *Isize)
{
   return ecx_readPDOmapCA(&ecx_context, Slave, Thread_n, Osize, Isize);
}

/** CoE read Object Description List.
 *
 * @param[in] Slave      = Slave number.
 * @param[out] pODlist  = resulting Object Description list.
 * @return Workcounter of slave response.
 * @see ecx_readODlist
 */
int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist)
{
   return ecx_readODlist(&ecx_context, Slave, pODlist);
}

/** CoE read Object Description. Adds textual description to object indexes.
 *
 * @param[in] Item           = Item number in ODlist.
 * @param[in,out] pODlist    = referencing Object Description list.
 * @return Workcounter of slave response.
 * @see ecx_readODdescription
 */
int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist)
{
   return ecx_readODdescription(&ecx_context, Item, pODlist);
}

int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist)
{
   return ecx_readOEsingle(&ecx_context, Item, SubI, pODlist, pOElist);
}

/** CoE read SDO service object entry.
 *
 * @param[in] Item           = Item in ODlist.
 * @param[in] pODlist        = Object description list for reference.
 * @param[out] pOElist       = resulting object entry structure.
 * @return Workcounter of slave response.
 * @see ecx_readOE
 */
int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist)
{
   return ecx_readOE(&ecx_context, Item, pODlist, pOElist);
}
#endif
