/** 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;
}
/** Initialise lib in redundant NIC mode
 * @param[in] ifname	= Primary Dev name, f.e. "eth0"
 * @param[in] if2name	= Secondary Dev name, f.e. "eth1"
 * @return >0 if OK
 */
int ec_init_redundant(const char *ifname, const char *if2name)
{
	int rval, zbuf;
	ec_etherheadert *ehp;
	
	ec_setupnic(ifname, FALSE);
	rval = ec_setupnic(if2name, TRUE);
	/* prepare "dummy" BRD tx frame for redundant operation */
	ehp = (ec_etherheadert *)&ec_txbuf2;
	ehp->sa1 = htons(secMAC[0]);
	zbuf = 0;
	ec_setupdatagram(&ec_txbuf2, EC_CMD_BRD, 0, 0x0000, 0x0000, 2, &zbuf);
	ec_txbuflength2 = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + 2;
	
	return rval;
}
/** 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;
}
/** 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;
}
 uint8 PrepareCycPacket(uint8 FrameIndex)
 {
    boolean more=TRUE;
    uint8 idx,cmd,i;
   
   uint8 *data;
   uint16 ADP, ADO;
   uint16 Datalength;
   uint16 IOoffset;
   
   CycFrame[FrameIndex].nInfo=ec_Frame[FrameIndex].NumCmd;
   CycFrame[FrameIndex].timeout=EC_TIMEOUTRET;
   //index
	  idx=ec_getindex();
	  CycFrame[FrameIndex].nIdx=idx;
	  
   //buffer
      CycFrame[FrameIndex].FrameBuffer=&ec_txbuf[idx];
	  CycFrame[FrameIndex].FrameStatus=&ec_rxbufstat[idx];
	  
    for (i=0;i<CycFrame[FrameIndex].nInfo;i++)
	    {
		  if (i==(CycFrame[FrameIndex].nInfo-1))//is the last command?
		      more=FALSE;
			  
			//cmd
		 if (ec_Frame[FrameIndex].FrameCmds[i].state& ec_slave[0].state)
		     cmd=ec_Frame[FrameIndex].FrameCmds[i].cmd;
			 else cmd=EC_CMD_NOP;
			 
		 //length	 
		 Datalength=ec_Frame[FrameIndex].FrameCmds[i].DataLength;
		 CycFrame[FrameIndex].length[i]=Datalength;
		 
		 //data default
		 data=(uint8 *)malloc(Datalength);
		 memset(data, 0, Datalength);
		 
		 //IO map offset
		 IOoffset=ec_Frame[FrameIndex].FrameCmds[i].Offs+DATAOFFS+FrameIndex*PROCOFFS; //(offset of the command in the frame) + (offset of data in the command) + (Number of Frame * 1536)
		 
		 //address & data
		 switch (cmd)
		 { case EC_CMD_LRD:
		      ADP=LO_WORD(ec_Frame[FrameIndex].FrameCmds[i].laddr);
		      ADO=HI_WORD(ec_Frame[FrameIndex].FrameCmds[i].laddr);
			 
			  CycFrame[FrameIndex].RXdatabuffer[i]=&InProc[IOoffset];
		      break;
			  
			case EC_CMD_NOP:
			   ADP=0x0000;
			   ADO=0x0100;
			  break;
			  
			case EC_CMD_LRW:
            case EC_CMD_LWR:
			    ADP=LO_WORD(ec_Frame[FrameIndex].FrameCmds[i].laddr);
		        ADO=HI_WORD(ec_Frame[FrameIndex].FrameCmds[i].laddr);
			    if (ec_slave[0].state==EC_STATE_OPERATIONAL)
			        memcpy(data, &OutProc[IOoffset], Datalength);
				CycFrame[FrameIndex].TXdatabuffer[i]=&OutProc[IOoffset];
                CycFrame[FrameIndex].RXdatabuffer[i]=&InProc[IOoffset];				
				break;
			default:
		    ADP=ec_Frame[FrameIndex].FrameCmds[i].ADP;
			ADO=ec_Frame[FrameIndex].FrameCmds[i].ADO;
		     break;
		 } 
		 //expected wkc
		 CycFrame[FrameIndex].ExpectedWKC[i]=ec_Frame[FrameIndex].FrameCmds[i].cnt;
		 
		 //build frame
		 if (i==0) //the frame is empty?
		      {ec_setupdatagram(&ec_txbuf[idx], cmd, idx, ADP, ADO, Datalength, data);
			  CycFrame[FrameIndex].DataOffset[i]=DATAOFFS+2;} //in ec_rxbuf is stored the frame without Ethernet header (12 byte),
			                                                  //so data of first datagram are located after EtherCAT header(2 byte)
															  //and datagram header (10 byte)
          else
            CycFrame[FrameIndex].DataOffset[i]=ec_adddatagram(&ec_txbuf[idx], cmd, idx, more, ADP, ADO, Datalength, data);
             
    	free(data);	 
		}
	   
	return idx;
}