/** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
 * Frame consists of two datagrams, one LRW and one FPRMW.
 *
 * @param[in] LogAdr		= Logical memory address
 * @param[in] length		= length of databuffer
 * @param[in,out] data		= databuffer to write to and read from slave.
 * @param[in] DCrs			= Distributed Clock reference slave address.
 * @param[out] DCtime		= DC time read from reference slave.
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{
    uint16 DCtO;
    uint8 idx;
	int wkc;
	uint64 DCtE;

    idx = ec_getindex();
	/* LRW in first datagram */
    ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
	/* FPRMW in second datagram */
	DCtE = htoell(*DCtime);
    DCtO = ec_adddatagram(&ec_txbuf[idx], EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
    wkc = ec_srconfirm(idx, timeout);
    if ((wkc > 0) && (ec_rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
    {
        memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
		memcpy(&wkc, &ec_rxbuf[idx][EC_HEADERSIZE + length], EC_WKCSIZE);
		memcpy(&DCtE, &ec_rxbuf[idx][DCtO], sizeof(*DCtime));
		*DCtime = etohll(DCtE);
    }
    ec_setbufstat(idx, EC_BUF_EMPTY);

    return wkc;
}
/** LWR "logical memory write" primitive. Blocking.
 *
 * @param[in] LogAdr		= Logical memory address
 * @param[in] length		= length of databuffer
 * @param[in] data			= databuffer to write to slave.
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout)
{
    uint8 idx;
	int wkc;

    idx = ec_getindex();
    ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
    wkc = ec_srconfirm(idx, timeout);
    ec_setbufstat(idx, EC_BUF_EMPTY);

    return wkc;
}
/** FPWR "configured address write" primitive. Blocking.
 *
 * @param[in] ADP			= Address Position, slave that has address writes.
 * @param[in] ADO			= Address Offset, slave memory address
 * @param[in] length		= length of databuffer
 * @param[in] data			= databuffer to write to slave.
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
    int wkc;
    uint8 idx;

    idx = ec_getindex();
    ec_setupdatagram(&ec_txbuf[idx], EC_CMD_FPWR, idx, ADP, ADO, length, data);
    wkc = ec_srconfirm(idx, timeout);
    ec_setbufstat(idx, EC_BUF_EMPTY);

    return wkc;
}
/** LRD "logical memory read" primitive. Blocking.
 *
 * @param[in] LogAdr		= Logical memory address
 * @param[in] length		= length of bytes to read from slave.
 * @param[out] data			= databuffer to read from slave.
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout)
{
    uint8 idx;
	int wkc;

    idx = ec_getindex();
    ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
    wkc = ec_srconfirm(idx, timeout);
    if ((wkc > 0) && (ec_rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD))
    {
        memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
    }
    ec_setbufstat(idx, EC_BUF_EMPTY);

    return wkc;
}
/** FPRD "configured address read" primitive. Blocking.
 *
 * @param[in] ADP			= Address Position, slave that has address reads.
 * @param[in] ADO			= Address Offset, slave memory address
 * @param[in] length		= length of databuffer
 * @param[out] data			= databuffer to put slave data in
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
    int wkc;
    uint8 idx;

    idx = ec_getindex();
    ec_setupdatagram(&ec_txbuf[idx], EC_CMD_FPRD, idx, ADP, ADO, length, data);
    wkc = ec_srconfirm(idx, timeout);
    if (wkc > 0)
    {
        memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
    }
    ec_setbufstat(idx, EC_BUF_EMPTY);

    return wkc;
}
/** BRW "broadcast write" primitive. Blocking.
 *
 * @param[in] ADP			= Address Position, normally 0
 * @param[in] ADO			= Address Offset, slave memory address
 * @param[in] length		= length of databuffer
 * @param[in] data			= databuffer to be written to slaves
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
	uint8 idx;
	int wkc;

	/* get fresh index */
	idx = ec_getindex();
	/* setup datagram */
	ec_setupdatagram(&ec_txbuf[idx], EC_CMD_BWR, idx, ADP, ADO, length, data);
	/* send data and wait for answer */
	wkc = ec_srconfirm (idx, timeout);
	/* clear buffer status */
    ec_setbufstat(idx, EC_BUF_EMPTY);

	return wkc;
}
int ec_receive_cyclic_packet( int timeout)
{
   int pos, idx;
   int wkc = 0, wkc2, i, WKCoffset, FrameOffset;
    uint8 NumCmd;  
   /* get first index */
   pos = ec_MY_pullindex();
   /* read the same number of frames as send */
   while (pos >= 0)
   {   
      idx = ec_MY_idxstack.idx[pos];
	  NumCmd=ec_MY_idxstack.NumCmd[pos];
	  //receive packet and check last wkc (BRD command)
      wkc2 = ec_waitinframe(idx, timeout);
	  if (wkc2 != ec_MY_idxstack.ExpectedWKC[pos][NumCmd-1])
	        return -1;
			
	  //check other wkc (if any)
	  if (ec_MY_idxstack.NumCmd[pos]>1)
	      for (i=0; i<(NumCmd-1);i++)
		   {
		     WKCoffset=ec_MY_idxstack.dataoffset[pos][i]+ec_MY_idxstack.length[pos][i];
		     wkc=ec_rxbuf[idx][WKCoffset];
			 if (wkc != ec_MY_idxstack.ExpectedWKC[pos][i])
			 return -1;
		   }
      /* check if there is input data in frame */
     
	    FrameOffset=EC_CMDOFFSET;
		for (i=0; i<ec_MY_idxstack.NumCmd[pos];i++)
		{
         if((ec_rxbuf[idx][FrameOffset]==EC_CMD_LRD) || (ec_rxbuf[idx][FrameOffset]==EC_CMD_LRW))
             /* copy input data back to process data buffer */
               memcpy(ec_MY_idxstack.databuffer[pos][i], &ec_rxbuf[idx][ ec_MY_idxstack.dataoffset[pos][i] ], ec_MY_idxstack.length[pos][i]);
		/*find the position of the next command: end of this datagram (dataoffset+datalength+wkc)*/
		FrameOffset=ec_MY_idxstack.dataoffset[pos][i]+ec_MY_idxstack.length[pos][i]+EC_WKCSIZE;
       } 
      
      /* release buffer */
      ec_setbufstat(idx, EC_BUF_EMPTY);
      /* get next index */
      pos = ec_MY_pullindex();
   }   

   return 1;
}   
/** BRD "broadcast read" primitive. Blocking.
 *
 * @param[in] ADP			= Address Position, normally 0
 * @param[in] ADO			= Address Offset, slave memory address
 * @param[in] length		= length of databuffer
 * @param[out] data			= databuffer to put slave data in
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{
	uint8 idx;
	int wkc;

	/* get fresh index */
	idx=ec_getindex();
	/* setup datagram */
	ec_setupdatagram(&ec_txbuf[idx], EC_CMD_BRD, idx, ADP, ADO, length, data);
	/* send data and wait for answer */
	wkc = ec_srconfirm (idx, timeout);
	if (wkc > 0)
	{
		/* copy datagram to data buffer */
		memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
	}
	/* clear buffer status */
    ec_setbufstat(idx, EC_BUF_EMPTY);

	return wkc;
}