/*
 Part of Libnodave, a free communication libray for Siemens S7 200/300/400 via
 the MPI adapter 6ES7 972-0CA22-0XAC
 or  MPI adapter 6ES7 972-0CA23-0XAC
 or  TS adapter 6ES7 972-0CA33-0XAC
 or  MPI adapter 6ES7 972-0CA11-0XAC,
 IBH/MHJ-NetLink or CPs 243, 343 and 443
 
 (C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2002..2005
 
 S5 basic communication parts (C) Andrew Rostovtsew 2004.

 Libnodave is free software; you can redistribute it and/or modify
 it under the terms of the GNU Library General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 Libnodave is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU Library General Public License
 along with Libnodave; see the file COPYING.  If not, write to
 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
*/
#include "nodave.h"
#include <stdio.h>
#include "log2.h"
#include <string.h>

#define DUMPLIMIT 11132

#define DAVE_HAVE_MEMCPY  // normally we have

//#define DEBUG_CALLS	// Define this and recompile to get parameters and results
			// of each function call printed. I could have made this an
			// option bit in daveDebug, but most applications will never,never
			// need it. Still they would have to do the jumps...
			// This option is just useful when developing bindings to new
			// programming languages.
/**
    Library specific:
**/
#ifdef LINUX
#define HAVE_UNISTD
#define HAVE_SELECT
#define DECL2
#include <time.h>
#include <sys/time.h>
#endif

#ifdef HAVE_UNISTD
#include <unistd.h>
#define daveWriteFile(a,b,c,d) d=write(a,b,c)
#endif

#ifdef AVR
#include <unistd.h>
#endif

int daveDebug=0;

#ifdef BCCWIN
#include <winsock2.h>
#include "openS7online.h"	// We can use the Siemens transport dlls only on Windows

void setTimeOut(daveInterface * di, int tmo) {
    COMMTIMEOUTS cto;
#ifdef DEBUG_CALLS
    LOG3("setTimeOut(di:%p, time:%d)\n",	di,tmo);
    FLUSH;
#endif	    
//    if(di->fd.connectionType==daveSerialConnection) {
	GetCommTimeouts(di->fd.rfd, &cto);
	cto.ReadIntervalTimeout=0;
	cto.ReadTotalTimeoutMultiplier=0;
	cto.ReadTotalTimeoutConstant=tmo/1000;
	SetCommTimeouts(di->fd.rfd,&cto);
//    } else if(di->fd.connectionType==daveTcpConnection) {  
//    }
}    
#endif


#ifdef HAVE_SELECT
int DECL2 stdwrite(daveInterface * di, char * buffer, int length) {
    if (daveDebug & daveDebugByte)
	_daveDump("I send", (uc*)buffer, length);
    return write(di->fd.wfd, buffer,length);
}

int DECL2 stdread(daveInterface * di, char * buffer, int length) {
    fd_set FDS;	
    struct timeval t;
    int i;
    t.tv_sec = di->timeout / 1000000;
    t.tv_usec = (di->timeout % 1000000);
    FD_ZERO(&FDS);
    FD_SET(di->fd.rfd, &FDS);
    i=0;
    if(select(di->fd.rfd + 1, &FDS, NULL, NULL, &t)>0) {
        i=read(di->fd.rfd, buffer, length);
    }
//    if (daveDebug & daveDebugByte)
//	_daveDump("got",buffer,i);
    return i;
}
#endif

#ifdef BCCWIN

int DECL2 stdread(daveInterface * di,
	 char * buffer, 
	 int length) {
    unsigned long i;
    ReadFile(di->fd.rfd, buffer, length, &i, NULL);
//    if (daveDebug & daveDebugByte)
//	_daveDump("got",buffer,i);
    return i;
}

int DECL2 stdwrite(daveInterface * di, char * buffer, int length) {
    unsigned long i;
    if (daveDebug & daveDebugByte)
	_daveDump("I send",buffer,length);
//    EscapeCommFunction(di->fd.rfd, CLRRTS);  // patch from Keith Harris. He says:
					     //******* this is what microwin does (needed for usb-serial)	
    WriteFile(di->fd.rfd, buffer, length, &i,NULL);
// patch from Andrea. He says:
// In this way the PC Adapter connected to CPU313C hangs waiting for RTS line before answering back.
// Added the following to regain answers:
//    EscapeCommFunction(di->fd.rfd, SETRTS);
    return i;
}
#endif 


/* 
    Setup a new interface structure from an initialized serial interface's handle and a name.
*/
daveInterface * DECL2 daveNewInterface(_daveOSserialType nfd, char * nname, int localMPI, int protocol, int speed){
    daveInterface * di=(daveInterface *) calloc(1, sizeof(daveInterface));
#ifdef DEBUG_CALLS
    LOG7("daveNewInterface(fd.rfd:%d fd.wfd:%d name:%s local MPI:%d protocol:%d PB speed:%d)\n",
	nfd.rfd,nfd.wfd,nname, localMPI, protocol, speed);
    FLUSH;	
#endif	
    if (di) {
//	di->name=nname;
	strncpy(di->realName,nname,20);
	di->name=di->realName;
	di->fd=nfd;
	di->localMPI=localMPI;
	di->protocol=protocol;
	di->timeout=1000000; /* 1 second */
	di->nextConnection=0x14;
	di->speed=speed;
	
	di->getResponse=_daveGetResponseISO_TCP;
	di->ifread=stdread;
	di->ifwrite=stdwrite;
	di->initAdapter=_daveReturnOkDummy;	
	di->connectPLC=_daveReturnOkDummy2;
	di->disconnectPLC=_daveReturnOkDummy2;
	di->disconnectAdapter=_daveReturnOkDummy;	
	di->listReachablePartners=_daveListReachablePartnersDummy;
	switch (protocol) {
	    case daveProtoMPI:
		di->initAdapter=_daveInitAdapterMPI1;
		di->connectPLC=_daveConnectPLCMPI1;
		di->disconnectPLC=_daveDisconnectPLCMPI;
		di->disconnectAdapter=_daveDisconnectAdapterMPI;
		di->exchange=_daveExchangeMPI;
		di->sendMessage=_daveSendMessageMPI;
		di->getResponse=_daveGetResponseMPI;
		di->listReachablePartners=_daveListReachablePartnersMPI;
		break;	
	    	
	    case daveProtoMPI2:
	    case daveProtoMPI4:
		di->initAdapter=_daveInitAdapterMPI2;
		di->connectPLC=_daveConnectPLCMPI2;
		di->disconnectPLC=_daveDisconnectPLCMPI;
		di->disconnectAdapter=_daveDisconnectAdapterMPI;
		di->exchange=_daveExchangeMPI;
		di->sendMessage=_daveSendMessageMPI;
		di->getResponse=_daveGetResponseMPI;
		di->listReachablePartners=_daveListReachablePartnersMPI;
		di->nextConnection=0x3;
		break;
	    
	    case daveProtoMPI3:
		di->initAdapter=_daveInitAdapterMPI3;
		di->connectPLC=_daveConnectPLCMPI3;
		di->disconnectPLC=_daveDisconnectPLCMPI3;
		di->disconnectAdapter=_daveDisconnectAdapterMPI3;
		di->exchange=_daveExchangeMPI3;
		di->sendMessage=_daveSendMessageMPI3;
		di->getResponse=_daveGetResponseMPI3;
		di->listReachablePartners=_daveListReachablePartnersMPI3;
		di->nextConnection=0x3;
		break;	

	    case daveProtoISOTCP:
	    case daveProtoISOTCP243:
		di->getResponse=_daveGetResponseISO_TCP;
		di->connectPLC=_daveConnectPLCTCP;
		di->exchange=_daveExchangeTCP;
		break;		
		
	    case daveProtoPPI:
		di->getResponse=_daveGetResponsePPI;
		di->exchange=_daveExchangePPI;
		di->connectPLC=_daveConnectPLCPPI;
		di->timeout=150000; /* 0.15 seconds */
		break;
	    case daveProtoMPI_IBH:
		di->exchange=_daveExchangeIBH;
		di->connectPLC=_daveConnectPLC_IBH;
		di->disconnectPLC=_daveDisconnectPLC_IBH;
		di->sendMessage=_daveSendMessageMPI_IBH;
		di->getResponse=_daveGetResponseMPI_IBH;
		di->listReachablePartners=_daveListReachablePartnersMPI_IBH;
		break;	
	    case daveProtoPPI_IBH:
		di->exchange=_daveExchangePPI_IBH;
		di->connectPLC=_daveConnectPLCPPI;
		di->sendMessage=_daveSendMessageMPI_IBH;
		di->getResponse=_daveGetResponsePPI_IBH;
		di->listReachablePartners=_daveListReachablePartnersMPI_IBH;
		break;	
	    case daveProtoS7online:
		di->exchange=_daveExchangeS7online;
		di->connectPLC=_daveConnectPLCS7online;
		di->sendMessage=_daveSendMessageS7online;
		di->getResponse=_daveGetResponseS7online;
		di->listReachablePartners=_daveListReachablePartnersS7online;
		di->disconnectPLC=_daveDisconnectPLCS7online;
//		di->disconnectAdapter=_daveDisconnectAdapterS7online;
		break;		
	    case daveProtoAS511:
		di->connectPLC=_daveConnectPLCAS511;
		di->disconnectPLC=_daveDisconnectPLCAS511;
		di->exchange=_daveFakeExchangeAS511;
		di->sendMessage=_daveFakeExchangeAS511;
		break;		
	    case daveProtoNLpro:
		di->initAdapter=_daveInitAdapterNLpro;
		di->connectPLC=_daveConnectPLCNLpro;
		di->disconnectPLC=_daveDisconnectPLCNLpro;
		di->disconnectAdapter=_daveDisconnectAdapterNLpro;
		di->exchange=_daveExchangeNLpro;
		di->sendMessage=_daveSendMessageNLpro;
		di->getResponse=_daveGetResponseNLpro;
		di->listReachablePartners=_daveListReachablePartnersNLpro;
		break;
	
	}
#ifdef BCCWIN
	setTimeOut(di, di->timeout);
#endif
    }
    return di;	
}

daveInterface * DECL2 davePascalNewInterface(_daveOSserialType* nfd, char * nname, int localMPI, int protocol, int speed){
#ifdef DEBUG_CALLS
    LOG7("davePascalNewInterface(fd.rfd:%d fd.wfd:%d name:%s local MPI:%d protocol:%d PB speed:%d)\n",
	nfd->rfd,nfd->wfd,nname, localMPI, protocol, speed);
    FLUSH;	
#endif	
    return daveNewInterface(*nfd,nname, localMPI, protocol, speed);
}

/*
    debugging:
    set debug level by setting variable daveDebug. Debugging is split into
    several topics. Output goes to stderr.
    The file descriptor is written after the module name, so you may
    distinguish messages from multiple connections.
    
    naming: all stuff begins with dave... to avoid conflicts with other
    namespaces. Things beginning with _dave.. are not intended for
    public use.
*/



void DECL2 daveSetDebug(int nDebug) {
#ifdef DEBUG_CALLS
    LOG2("daveSetDebug(%d)\n",nDebug);
    FLUSH;
#endif	
    daveDebug=nDebug;
}

int DECL2 daveGetDebug() {
#ifdef DEBUG_CALLS
    LOG1("daveGetDebug()\n");
    FLUSH;
#endif	
    return daveDebug;
}
/**
    C# interoperability:
**/
void DECL2 daveSetTimeout(daveInterface * di, int tmo) {
#ifdef DEBUG_CALLS
    LOG3("daveSetTimeOut(di:%p, time:%d)\n",	di,tmo);
#endif	    
    di->timeout=tmo;
#ifdef BCCWIN
    setTimeOut(di,tmo);
#endif    
}

int DECL2 daveGetTimeout(daveInterface * di) {
#ifdef DEBUG_CALLS
    LOG2("daveGetTimeOut(di:%p)\n",di);
    FLUSH;
#endif	    
    return di->timeout;
}

char * DECL2 daveGetName(daveInterface * di) {
#ifdef DEBUG_CALLS
    LOG2("daveGetName(di:%p)\n",di);
    FLUSH;
#endif	    
    return di->name;
}


int DECL2 daveGetMPIAdr(daveConnection * dc) {
#ifdef DEBUG_CALLS
    LOG2("daveGetMPIAdr(dc:%p)\n",dc);
    FLUSH;
#endif	    
    return dc->MPIAdr;
}

int DECL2 daveGetAnswLen(daveConnection * dc) {
#ifdef DEBUG_CALLS
    LOG2("daveGetAnswLen(dc:%p)\n",dc);
    FLUSH;
#endif	    
    return dc->AnswLen;
}

int DECL2 daveGetMaxPDULen(daveConnection * dc) {
#ifdef DEBUG_CALLS
    LOG2("daveGetMaxPDULen(dc:%p)\n",dc);
    FLUSH;
#endif	    
    return dc->maxPDUlength;
}

/**
    PDU handling:
**/

/*
    set up the header. Needs valid header pointer
*/
void DECL2 _daveInitPDUheader(PDU * p, int type) {
    memset(p->header, 0, sizeof(PDUHeader)); 
    if (type==2 || type==3)
	p->hlen=12;
    else
	p->hlen=10;
    p->param=p->header+p->hlen;
    ((PDUHeader*)p->header)->P=0x32;
    ((PDUHeader*)p->header)->type=type;
    p->dlen=0;
    p->plen=0;
    p->udlen=0;
    p->data=NULL;
    p->udata=NULL;
}

/*
    add parameters after header, adjust pointer to data.
    needs valid header
*/
void DECL2 _daveAddParam(PDU * p,uc * param,us len) {
#ifdef DEBUG_CALLS
    LOG4("_daveAddParam(PDU:%p, param %p, len:%d)\n", p, param, len);
    FLUSH;
#endif
    p->plen=len;
#ifdef DAVE_HAVE_MEMCPY
    memcpy(p->param, param, len);
#else
    int i;
    for (i=0;i<len;i++) p->param[i]=param[i];
#endif
    ((PDUHeader2*)p->header)->plenHi=len/256;
    ((PDUHeader2*)p->header)->plenLo=len%256;
//    ((PDUHeader*)p->header)->plen=daveSwapIed_16(len);
    p->data=p->param+len;
    p->dlen=0;
}

/*
    add data after parameters, set dlen
    needs valid header,parameters
*/
void DECL2 _daveAddData(PDU * p,void * data,int len) {
#ifdef DEBUG_CALLS
    LOG4("_daveAddData(PDU:%p, data %p, len:%d)\n", p, data, len);
//    _daveDumpPDU(p);
    FLUSH;
#endif
    uc * dn= p->data+p->dlen;
    p->dlen+=len;
#ifdef DAVE_HAVE_MEMCPY
    memcpy(dn, data, len);
#else
    int i; uc * d=(uc*)data;
    for (i=0;i<len;i++) p->data[p->dlen+i]=d[i];
#endif
    ((PDUHeader2*)p->header)->dlenHi=p->dlen/256;
    ((PDUHeader2*)p->header)->dlenLo=p->dlen%256;
//    ((PDUHeader*)p->header)->dlen=daveSwapIed_16(p->dlen);
}

/*
    add values after value header in data, adjust dlen and data count.
    needs valid header,parameters,data,dlen
*/
void DECL2 _daveAddValue(PDU * p,void * data,int len) {
    us dCount;
    uc * dtype;
#ifdef DEBUG_CALLS
    LOG4("_daveAddValue(PDU:%p, data %p, len:%d)\n", p, data, len);
    _daveDumpPDU(p);
    FLUSH;
#endif

    dtype=p->data+p->dlen-4+1;			/* position of first byte in the 4 byte sequence */

    dCount= p->data[p->dlen-4+2+1];
    dCount+= 256*p->data[p->dlen-4+2];
    
    if (daveDebug & daveDebugPDU)
	LOG2("dCount: %d\n", dCount);
    if (*dtype==4) {	/* bit data, length is in bits */
	dCount+=8*len;
    } else if (*dtype==9) {	/* byte data, length is in bytes */
	dCount+=len;
    } else if (* dtype==3) {	/* bit data, length is in bits */
	dCount+=len;	
    } else {
	if (daveDebug & daveDebugPDU)
	    LOG2("unknown data type/length: %d\n", *dtype);
    }	    
    if (p->udata==NULL) p->udata=p->data+4;
    p->udlen+=len;	
    if (daveDebug & daveDebugPDU)
	LOG2("dCount: %d\n", dCount);

    p->data[p->dlen-4+2]= dCount/256;
    p->data[p->dlen-4+2+1]=dCount%256;

    _daveAddData(p, data, len);
}

/*
    add data in user data. Add a user data header, if not yet present.
*/
void DECL2 _daveAddUserData(PDU * p, uc * da, int len) {
    uc udh[]={0xff,9,0,0};
    if (p->dlen==0) {
	if (daveDebug & daveDebugPDU)
	    LOG1("adding user data header.\n");	
	_daveAddData(p, udh, sizeof(udh));
    }
    _daveAddValue(p, da, len);
}

void DECL2 davePrepareReadRequest(daveConnection * dc, PDU *p) {
    uc pa[]=	{daveFuncRead,0};
#ifdef DEBUG_CALLS
    LOG3("davePrepareReadRequest(dc:%p PDU:%p)\n", dc, p);
    FLUSH;
#endif	    
    p->header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
}    

PDU * DECL2 daveNewPDU() {
    PDU * p;
    p=(PDU*)malloc(sizeof(PDU));
#ifdef DEBUG_CALLS
    LOG2("daveNewPDU() = %p\n",p);
    FLUSH;
#endif	        
    return p;
}    


void DECL2 davePrepareWriteRequest(daveConnection * dc, PDU *p) {
    uc pa[]=	{daveFuncWrite,	0};
#ifdef DEBUG_CALLS
    LOG3("davePrepareWriteRequest(dc:%p PDU:%p)\n", dc, p);
    FLUSH;
#endif	    
    p->header=dc->msgOut+dc->PDUstartO;		
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    p->dlen=0;
}    

void DECL2 daveAddToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount, int isBit) {
    uc pa[]=	{
	0x12, 0x0a, 0x10,
	0x02,		/* 1=single bit, 2=byte, 4=word */
	0,0,		/* length in bytes */
	0,0,		/* DB number */
	0,		/* area code */
	0,0,0		/* start address in bits */
    };

    if ((area==daveAnaIn) || (area==daveAnaOut) /*|| (area==daveP)*/) {
	pa[3]=4;
	start*=8;			/* bits */
    } else if ((area==daveTimer) || (area==daveCounter)||(area==daveTimer200) || (area==daveCounter200)) {
	pa[3]=area;
    } else {
	if(isBit) {
	    pa[3]=1;
	} else {
	    start*=8;			/* bit address of byte */
	}    
    }
    
    pa[4]=byteCount / 256;
    pa[5]=byteCount & 0xff;
    pa[6]=DBnum / 256;
    pa[7]=DBnum & 0xff;
    pa[8]=area;
    pa[11]=start & 0xff;
    pa[10]=(start / 0x100) & 0xff;
    pa[9]=start / 0x10000; 
    
    p->param[1]++;
    memcpy(p->param+p->plen, pa, sizeof(pa));
    p->plen+=sizeof(pa);

    ((PDUHeader2*)p->header)->plenHi=p->plen/256;
    ((PDUHeader2*)p->header)->plenLo=p->plen%256;

    p->data=p->param+p->plen;
    p->dlen=0;
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(p);
    }	
}    

void DECL2 daveAddVarToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount) {
#ifdef DEBUG_CALLS
    LOG6("daveAddVarToReadRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d)\n",
	p, daveAreaName(area), DBnum, start, byteCount);
    FLUSH;	
#endif	    	

    daveAddToReadRequest(p, area, DBnum, start, byteCount, 0);
}    

void DECL2 daveAddBitVarToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount) {
    daveAddToReadRequest(p, area, DBnum, start, byteCount, 1);
}    

void DECL2 daveAddToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount, 
	void * buffer,
	uc * da,
	int dasize,
	uc * pa,
	int pasize
) {
    uc saveData[1024];
#ifdef DEBUG_CALLS
    LOG7("daveAddToWriteRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
	p, daveAreaName(area), DBnum, start, byteCount, buffer);
//    _daveDumpPDU(p);
    FLUSH;
#endif

    if ((area==daveTimer) || (area==daveCounter)||(area==daveTimer200) || (area==daveCounter200)) {    
	pa[3]=area;
	pa[4]=((byteCount+1)/2) / 0x100;
	pa[5]=((byteCount+1)/2) & 0xff;
    } else if ((area==daveAnaIn) || (area==daveAnaOut)) {
	pa[3]=4;
	pa[4]=((byteCount+1)/2) / 0x100;
	pa[5]=((byteCount+1)/2) & 0xff;
    } else {	
	pa[4]=byteCount / 0x100;		
	pa[5]=byteCount & 0xff;	
    }
    pa[6]=DBnum / 256;		
    pa[7]=DBnum & 0xff;		
    pa[8]=area;		
    pa[11]=start & 0xff;
    pa[10]=(start / 0x100) & 0xff;
    pa[9]=start / 0x10000; 
    if(p->dlen%2) {
	_daveAddData(p, da, 1); 
    }    
    p->param[1]++;
#ifdef DAVE_HAVE_MEMCPY
    if(p->dlen){
	 memcpy(saveData, p->data, p->dlen);
	 memcpy(p->data+pasize, saveData, p->dlen);
    }	 
    memcpy(p->param+p->plen, pa, pasize);
#else
    int i;
    if(p->dlen){
	for (i=0; i<p->dlen; i++) saveData[i]=p->data[i];
	for (i=0; i<p->dlen; i++) p->data[i+pasize]=saveData[i];
    }
    for (i=0; i<pasize; i++) p->param[i+p->plen]=pa[i];
#endif 
    p->plen+=pasize;

    ((PDUHeader2*)p->header)->plenHi=p->plen/256;
    ((PDUHeader2*)p->header)->plenLo=p->plen%256;

    p->data=p->param+p->plen;
    _daveAddData(p, da, dasize);
    _daveAddValue(p, buffer, byteCount);
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(p);
    }
}

void DECL2 daveAddVarToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount, void * buffer) {
    uc da[]=	{0,4,0,0,};
    uc pa[]=	{
		0x12, 0x0a, 0x10,
		0x02,		/* unit (for count?, for consistency?) byte */
		0,0,		/* length in bytes */
		0,0,		/* DB number */
		0,		/* area code */
		0,0,0		/* start address in bits */
		};
#ifdef DEBUG_CALLS
    LOG7("daveAddVarToWriteRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
	p, daveAreaName(area), DBnum, start, byteCount, buffer);
    FLUSH;	
#endif	    	
		
    daveAddToWriteRequest(p, area, DBnum, 8*start, byteCount,buffer,da,sizeof(da),pa,sizeof(pa));
}


void DECL2 daveAddBitVarToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount, void * buffer) {
    uc da[]=	{0,3,0,0,};
    uc pa[]=	{
		0x12, 0x0a, 0x10,
		0x01,		/* single bit */
		0,0,		/* insert length in bytes here */
		0,0,		/* insert DB number here */
		0,		/* change this to real area code */
		0,0,0		/* insert start address in bits */
		};
#ifdef DEBUG_CALLS
    LOG7("daveAddBitVarToWriteRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
	p, daveAreaName(area), DBnum, start, byteCount, buffer);
    FLUSH;	
#endif	    	
		
    daveAddToWriteRequest(p, area, DBnum, start, byteCount,buffer,da,sizeof(da),pa,sizeof(pa));		
}    

/*
    Get the eror code from a PDU, if one.
*/
int DECL2 daveGetPDUerror(PDU * p) {
#ifdef DEBUG_CALLS
    LOG2("daveGetPDUerror(PDU:%p\n", p);
    FLUSH;
#endif	    	
    if (p->header[1]==2 || p->header[1]==3) {
	return daveGetU16from(p->header+10);
    } else
	return 0;
}

/*
    Sets up pointers to the fields of a received message. 
*/
int DECL2 _daveSetupReceivedPDU(daveConnection * dc, PDU * p) {
	int res; /* = daveResCannotEvaluatePDU; */
	p->header=dc->msgIn+dc->PDUstartI;
	res=0;
        if (p->header[1]==2 || p->header[1]==3) {
	    p->hlen=12;
	    res=256*p->header[10]+p->header[11];
	} else {
	    p->hlen=10;
	}    
	p->param=p->header+p->hlen;    

	p->plen=256*p->header[6] + p->header[7];
	p->data=p->param+p->plen;
        p->dlen=256*p->header[8] + p->header[9];
	p->udlen=0; 
	p->udata=NULL;
	if (daveDebug & daveDebugPDU)
	    _daveDumpPDU(p);	
	return res;
}	

int DECL2 _daveTestResultData(PDU * p) {	
	int res; /*=daveResCannotEvaluatePDU;*/
	if ((p->data[0]==255)&&(p->dlen>4)) {
	    res=daveResOK;
	    p->udata=p->data+4;
	    p->udlen=p->data[2]*0x100+p->data[3];
	    if (p->data[1]==4) {
    	        p->udlen>>=3;	/* len is in bits, adjust */
	    } else if (p->data[1]==9) {
	        /* len is already in bytes, ok */
	    } else if (p->data[1]==3) {
	        /* len is in bits, but there is a byte per result bit, ok */
	    } else {
	        if (daveDebug & daveDebugPDU)
			LOG2("fixme: what to do with data type %d?\n",p->data[1]);
		res = daveResUnknownDataUnitSize;
	    }	    
	} else {
	    res=p->data[0];
	}
	return res;    
}
	
int DECL2 _daveTestReadResult(PDU * p) {	
    if (p->param[0]!=daveFuncRead) return daveResUnexpectedFunc;
    return _daveTestResultData(p);
}

int DECL2 _daveTestPGReadResult(PDU * p) {	
    int pres=0;
    if (p->param[0]!=0) return daveResUnexpectedFunc;
    if (p->plen==12) pres=(256*p->param[10]+p->param[11]);
    if (pres==0)return _daveTestResultData(p); else return pres;
}

int DECL2 _daveTestWriteResult(PDU * p) {
	int res;/* =daveResCannotEvaluatePDU; */
	if (p->param[0]!=daveFuncWrite) return daveResUnexpectedFunc;
	if ((p->data[0]==255)) {
	    res=daveResOK;
	} else 
	    res=p->data[0];
	if (daveDebug & daveDebugPDU) {
	    _daveDumpPDU(p);
	}
	return res;
}

/*****
    Utilities:
****/
/*
    This is an extended memory compare routine. It can handle don't care and stop flags 
    in the sample data. A stop flag lets it return success.
*/
int DECL2 _daveMemcmp(us * a, uc *b, size_t len) {
    unsigned int i;
    us * a1=(us*)a;
    uc * b1=(uc*)b;
    for (i=0;i<len;i++){
	if (((*a1)&0xff)!=*b1) {
	    if (((*a1)&0x100)!=0x100) {
//		LOG3("want:%02X got:%02X\n",*a1,*b1);
		 return i+1;
	    }	 
	    if (((*a1)&0x200)==0x200) return 0;
	}
	a1++;
	b1++;
    }
    return 0;
}

/*
    Hex dump:
*/
void DECL2 _daveDump(char * name, void*b,int len) {//void DECL2 _daveDump(char * name,uc*b,int len) {
    int j;
    LOG2("%s:                             ",name);
    if (len>daveMaxRawLen) len=daveMaxRawLen; 	/* this will avoid to dump zillions of chars */
    if (len>DUMPLIMIT) len=DUMPLIMIT; 		/* this will avoid large dumps */
    for (j=0; j<len; j++){
	if((j & 0xf)==0) LOG_2("\n                            %x:",j);
	LOG_2("0x%02X,",((uc*)(b))[j]);
    }
    LOG_1("\n");
}

/*
    Hex dump PDU:
*/
void DECL2 _daveDumpPDU(PDU * p) {
    int i,dl;
    uc * pd;
    _daveDump("PDU header", p->header, p->hlen);
    LOG3("plen: %d dlen: %d\n",p->plen, p->dlen);
    if(p->plen>0) _daveDump("Parameter",p->param,p->plen);
    if(p->dlen>0) _daveDump("Data     ",p->data,p->dlen);
    if ((p->plen==2)&&(p->param[0]==daveFuncRead)) {
	pd=p->data;
	for (i=0;i<p->param[1];i++) {
	    _daveDump("Data hdr ",pd,4);
	
	    dl=0x100*pd[2]+pd[3];
	    if (pd[1]==4) dl/=8;
	    pd+=4;        
	    _daveDump("Data     ",pd,dl);
	    if(i<p->param[1]-1) dl=dl+(dl%2);  	// the PLC places extra bytes at the end of all 
						// but last result, if length is not a multiple 
						// of 2
	    pd+=dl;
	}
    } else if ((p->header[1]==1)&&/*(p->plen==2)&&*/(p->param[0]==daveFuncWrite)) {
	pd=p->data;
	for (i=0;i<p->param[1];i++) {
	    _daveDump("Write Data hdr ",pd,4);
	    
	    dl=0x100*pd[2]+pd[3];
	    if (pd[1]==4) dl/=8;
	    pd+=4;        
	    _daveDump("Data     ",pd,dl);
	    if(i<p->param[1]-1) dl=dl+(dl%2);  	// the PLC places extra bytes at the end of all 
						// but last result, if length is not a multiple 
						// of 2
	    pd+=dl;
	}
    } else {    
/*    
	if(p->dlen>0) {
	    if(p->udlen==0)
		_daveDump("Data     ",p->data,p->dlen);
	    else
		_daveDump("Data hdr ",p->data,4);
	}	
	if(p->udlen>0) _daveDump("result Data ",p->udata,p->udlen);
*/	
    }
    if ((p->header[1]==2)||(p->header[1]==3)) {
	LOG2("error: %s\n",daveStrerror(daveGetPDUerror(p)));
    }	
}

/*
    name Objects:
*/
char * DECL2 daveBlockName(uc bn) {
//#ifdef DEBUG_CALLS
    LOG2("daveBlockName(bn:%d)\n", bn);
    FLUSH;
//#endif	    	
    switch(bn) {
	case daveBlockType_OB: return "OB";
	case daveBlockType_DB: return "DB";
	case daveBlockType_SDB: return "SDB";
	case daveBlockType_FC: return "FC";
	case daveBlockType_SFC: return "SFC";
	case daveBlockType_FB: return "FB";
	case daveBlockType_SFB: return "SFB";
	default:return "unknown block type!";
    }    
}

char * DECL2 daveAreaName(uc n) {
#ifdef DEBUG_CALLS
    LOG2("daveAreaName(n:%d)\n", n);
    FLUSH;
#endif	    	
    switch (n) {
	case daveSysInfo:	return "System info mem.area of 200 family";
	case daveSysFlags:	return "System flags of 200 family";
	case daveAnaIn:		return "analog inputs of 200 family";
	case daveAnaOut:	return "analog outputs of 200 family";
	
	case daveP:		return "Peripheral I/O";
	case daveInputs:	return "Inputs";
	case daveOutputs:	return "Outputs";
	case daveDB:		return "DB";
	case daveDI:		return "DI (instance data)";
	case daveFlags:		return "Flags";
	case daveLocal:		return "local data";
	case daveV:		return "caller's local data";
	case daveCounter:	return "S7 counters";
	case daveTimer:		return "S7 timers";
	case daveCounter200:	return "IEC counters";
	case daveTimer200:	return "IEC timers";
	default:return "unknown area!";
    }	
}

/*
    Functions to load blocks from PLC:
*/
void DECL2 _daveConstructUpload(PDU *p,char blockType, int blockNr) {
    uc pa[]=	{0x1d,
    0,0,0,0,0,0,0,9,0x5f,0x30,0x41,48,48,48,48,49,65};
    pa[11]=blockType;
    sprintf((char*)(pa+12),"%05d",blockNr);
    pa[17]='A';
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(p);
    }	
}

void DECL2 _daveConstructDoUpload(PDU * p, int uploadID) {
    uc pa[]=	{0x1e,0,0,0,0,0,0,1};
    pa[7]=uploadID;
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(p);
    }	
}    

void DECL2 _daveConstructEndUpload(PDU * p, int uploadID) {
    uc pa[]=	{0x1f,0,0,0,0,0,0,1};
    pa[7]=uploadID;
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(p);
    }	
}    

uc paInsert[]= {		// sended after transmission of a complete block,
				// I guess this makes the CPU link the block into a program.
    0x28,0,0,0,0,0,0,0xFD,0,0x0A,1,0,0x30,0x42,0x30,0x30,0x30,0x30,0x34,0x50, // block type code and number	
    0x05,'_','I','N','S','E',
};

uc paMakeRun[]= {
    0x28,0,0,0,0,0,0,0xFD,0,0x00,9,'P','_','P','R','O','G','R','A','M'
};

uc paCompress[]= {
    0x28,0,0,0,0,0,0,0xFD,0,0x00,5,'_','G','A','R','B'
};
    
uc paMakeStop[]= {
    0x29,0,0,0,0,0,9,'P','_','P','R','O','G','R','A','M'
};

uc paCopyRAMtoROM[]= {
    0x28,0,0,0,0,0,0,0xfd,0,2,'E','P',5,'_','M','O','D','U'
};

int DECL2 daveStop(daveConnection * dc) {
    int res;
    PDU p,p2;
#ifdef DEBUG_CALLS
    LOG2("daveStop(dc:%p)\n", dc);
    FLUSH;
#endif	  
    if (dc->iface->protocol==daveProtoAS511) {	    
	return daveStopS5(dc);
    }  	
    p.header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(&p, 1);
    _daveAddParam(&p, paMakeStop, sizeof(paMakeStop));
    res=_daveExchange(dc, &p);
    if (res==daveResOK) {
	res=_daveSetupReceivedPDU(dc,&p2);
	if (daveDebug & daveDebugPDU) {
	    _daveDumpPDU(&p2);
	}
    }
    return res;
}
	
int DECL2 daveStart(daveConnection*dc) {
    int res;
    PDU p,p2;
#ifdef DEBUG_CALLS
    LOG2("daveStart(dc:%p)\n", dc);
    FLUSH;
#endif	    	
    if (dc->iface->protocol==daveProtoAS511) {	    
	return daveStartS5(dc);
    }  	
    p.header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(&p, 1);
    _daveAddParam(&p, paMakeRun, sizeof(paMakeRun));
    res=_daveExchange(dc, &p);
    if (res==daveResOK) {
	res=_daveSetupReceivedPDU(dc, &p2);
	if (daveDebug & daveDebugPDU) {
	    _daveDumpPDU(&p2);
	}
    }
    return res;
}

int DECL2 daveCopyRAMtoROM(daveConnection * dc) {
    int res;
    PDU p,p2;
#ifdef DEBUG_CALLS
    LOG2("davecopyRAMtoROM(dc:%p)\n", dc);
    FLUSH;
#endif	  
    p.header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(&p, 1);
    _daveAddParam(&p, paCopyRAMtoROM, sizeof(paCopyRAMtoROM));
    res=_daveExchange(dc, &p);
    if (res==daveResOK) {
	res=_daveSetupReceivedPDU(dc,&p2);  /* possible problem, Timeout */
	if (daveDebug & daveDebugPDU) {
	    _daveDumpPDU(&p2);
	}
    }
    return res;
}



/*
    Build a PDU with user data ud, send it and prepare received PDU.
*/
int DECL2 daveBuildAndSendPDU(daveConnection * dc, PDU*p2,uc *pa,int psize, uc *ud,int usize) {
    int res;
    PDU p;
    uc nullData[]={0x0a,0,0,0};
    p.header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(&p, 7);
    _daveAddParam(&p, pa, psize);
    if (ud!=NULL) _daveAddUserData(&p, ud, usize); else
    if (usize!=0) _daveAddData(&p, nullData, 4);
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(&p);
    }	
    res=_daveExchange(dc, &p);
    if (daveDebug & daveDebugErrorReporting)
	LOG2("*** res of _daveExchange(): %d\n",res);
    if (res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc,p2);
    if (daveDebug & daveDebugPDU) {
        _daveDumpPDU(p2);
    }
    if (daveDebug & daveDebugErrorReporting)
	LOG2("*** res of _daveSetupReceivedPDU(): %04X\n",res);
    if (res!=daveResOK) return res;
    res=_daveTestPGReadResult(p2);
    if (daveDebug & daveDebugErrorReporting)
	LOG2("*** res of _daveTestPGReadResult(): %04X\n",res);
    return res;
}    	

int DECL2 daveListBlocksOfType(daveConnection * dc,uc type,daveBlockEntry * buf) {
    int res,i, len;
    PDU p2;
    uc * buffer=(uc*)buf;
    uc pa[]={0,1,18,4,17,67,2,0};
    uc da[]={'0','0'};
    uc pam[]={0,1,18,8,0x12,0x43,2,1,0,0,0,0};
#ifdef DEBUG_CALLS
    LOG4("ListBlocksOfType(dc:%p type:%d buf:%p)\n", dc, type, buf);
    FLUSH;
#endif	    	
    da[1]=type;
    res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
    if (res!=daveResOK) return -res;
    len=0;
    while (p2.param[9]!=0) {
	if (buffer!=NULL) memcpy(buffer+len,p2.udata,p2.udlen);
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len+=p2.udlen;
	printf("more data\n");
	res=daveBuildAndSendPDU(dc, &p2,pam, sizeof(pam), NULL, 1);
	if (res!=daveResOK) return res; 	// bugfix from Natalie Kather
    }


    if (res==daveResOK) {
	if (buffer!=NULL) memcpy(buffer+len,p2.udata,p2.udlen);
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len+=p2.udlen;
    } else {
	if(daveDebug & daveDebugPrintErrors)
	    LOG3("daveListBlocksOfType: %d=%s\n",res, daveStrerror(res));
    }
    dc->AnswLen=len;
    res=len/sizeof(daveBlockEntry);
    for (i=0; i<res; i++) {
        buf[i].number=daveSwapIed_16(buf[i].number);
    }
    return res;
}    

/*
    doesn't work on S7-200
*/
int DECL2 daveGetOrderCode(daveConnection * dc,char * buf) {
    int res=0;
    PDU p2;
    uc pa[]={0,1,18,4,17,68,1,0};
    uc da[]={1,17,0,1};  /* SZL-ID 0x111 index 1 */
#ifdef DEBUG_CALLS
    LOG3("daveGetOrderCode(dc:%p buf:%p)\n", dc, buf);
    FLUSH;
#endif	    	
    res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
    if (res!=daveResOK) return res; // similar to bugfix from Natalie Kather
    if (buf) {
	memcpy(buf, p2.udata+10, daveOrderCodeSize);
	buf[daveOrderCodeSize]=0;
    }
    return res;
}

int DECL2 daveReadSZL(daveConnection * dc, int ID, int index, void * buffer, int buflen) {
    int res,len,cpylen;
    int pa7;
//    int pa6;
    PDU p2;
    uc pa[]={0,1,18,4,17,68,1,0};
    uc da[]={1,17,0,1};
    
    uc pam[]={0,1,18,8,18,68,1,1,0,0,0,0};
//    uc dam[]={10,0,0,0};

#ifdef DEBUG_CALLS
    LOG5("daveReadSZL(dc:%p, ID:%d, index:%d, buffer:%p)\n", dc, ID, index, buffer);
    FLUSH;
#endif	    	
    da[0]=ID / 0x100;
    da[1]=ID % 0x100;
    da[2]=index / 0x100;
    da[3]=index % 0x100;
    res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
    if (res!=daveResOK) return res; // similar to bugfix from Natalie Kather

    len=0;
    pa7=p2.param[7];
//    pa6=p2.param[6];
    while (p2.param[9]!=0) {
	if (buffer!=NULL) {
          cpylen = p2.udlen;
          if (len + cpylen > buflen) cpylen = buflen - len;
          if (cpylen > 0) memcpy((uc *)buffer+len,p2.udata,cpylen);
        }
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len+=p2.udlen;
		pam[7]=pa7;
//		res=daveBuildAndSendPDU(dc, &p2,pam, sizeof(pam), NULL, sizeof(dam));
		res=daveBuildAndSendPDU(dc, &p2,pam, sizeof(pam), NULL, 1);
		if (res!=daveResOK) return res; // similar to bugfix from Natalie Kather
    }


    if (res==daveResOK) {
	if (buffer!=NULL) {
          cpylen = p2.udlen;
          if (len + cpylen > buflen) cpylen = buflen - len;
          if (cpylen > 0) memcpy((uc *)buffer+len,p2.udata,cpylen);
        }
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len+=p2.udlen;
    } 
    dc->AnswLen=len;
    return res;
}

int DECL2 daveGetBlockInfo(daveConnection * dc, daveBlockInfo *dbi, uc type, int number) {
    int res;
    uc pa[]={0,1,18,4,17,67,3,0};	   /* param */
    uc da[]={'0',0,'0','0','0','1','0','A'};
    PDU p2;
    sprintf((char*)(da+2),"%05d",number);
    da[1]=type;
    da[7]='A';
    res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));    
    if (res!=daveResOK) return res; // similar to bugfix from Natalie Kather
    if ((dbi!=NULL) && (p2.udlen==sizeof(daveBlockInfo))) {
	memcpy(dbi, p2.udata, p2.udlen);
	dbi->number=daveSwapIed_16(dbi->number);
        dbi->length=daveSwapIed_16(dbi->length);
    }
    return res;	
}

int DECL2 daveListBlocks(daveConnection * dc,daveBlockTypeEntry * buf) {
    int res,i;
    PDU p2;
    uc pa[]={0,1,18,4,17,67,1,0};
    res=daveBuildAndSendPDU(dc, &p2, pa, sizeof(pa), NULL, 1/*da, sizeof(da)*/);
    if (res!=daveResOK) return res; // similar to bugfix from Natalie Kather
    res=p2.udlen/sizeof(daveBlockTypeEntry);
    if (buf) {
	memcpy(buf, p2.udata, p2.udlen);
	for (i=0; i<res; i++) {
	    buf[i].count=daveSwapIed_16(buf[i].count);
	}
    }
    return res;
}

int DECL2 daveReadManyBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer) {
    int res, pos, readLen;
    uc * pbuf;
    pos=0;
    if (buffer==NULL) return daveResNoBuffer;
    pbuf=(uc*) buffer; 
    res=daveResInvalidLength; //the only chance to return this is when len<=0
    while (len>0) {
	if (len>dc->maxPDUlength-18) readLen=dc->maxPDUlength-18; else readLen=len;
	res=daveReadBytes(dc,area, DBnum, start, readLen, pbuf);
	if (res!=0) return res;
	len-=readLen;
	start+=readLen;
	pbuf+=readLen;
    }	
    return res;
}

/*
    Read len bytes from PLC memory area "area", data block DBnum. 
    Return the Number of bytes read.
    If a buffer pointer is provided, data will be copied into this buffer.
    If it's NULL you can get your data from the resultPointer in daveConnection long
    as you do not send further requests.
*/
int DECL2 daveReadBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
    PDU p1,p2;
    int res;
#ifdef DEBUG_CALLS
    LOG7("daveReadBytes(dc:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
	dc, daveAreaName(area), DBnum, start,len, buffer);
    FLUSH;	
#endif
    if (dc->iface->protocol==daveProtoAS511) {	    
	return daveReadS5Bytes(dc, area, DBnum, start, len/*, buffer*/);
    }
    dc->AnswLen=0;	// 03/12/05
    dc->resultPointer=NULL;
    dc->_resultPointer=NULL;
    p1.header=dc->msgOut+dc->PDUstartO;
    davePrepareReadRequest(dc, &p1);
    daveAddVarToReadRequest(&p1, area, DBnum, start, len);
    res=_daveExchange(dc, &p1);
    if (res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if (daveDebug & daveDebugPDU)
	LOG3("_daveSetupReceivedPDU() returned: %d=%s\n", res,daveStrerror(res));
    if (res!=daveResOK) return res;
    
    res=_daveTestReadResult(&p2);
    if (daveDebug & daveDebugPDU)
	LOG3("_daveTestReadResult() returned: %d=%s\n", res,daveStrerror(res));
    if (res!=daveResOK) return res;	
    
    if (p2.udlen==0) {
	return daveResCPUNoData; 
    }	
/*
    copy to user buffer and setup internal buffer pointers:
*/    
    if (buffer!=NULL) memcpy(buffer,p2.udata,p2.udlen);
    dc->resultPointer=p2.udata;
    dc->_resultPointer=p2.udata;
    dc->AnswLen=p2.udlen;
    return res;
}

/*
    Read len BITS from PLC memory area "area", data block DBnum. 
    Return the Number of bytes read.
    If a buffer pointer is provided, data will be copied into this buffer.
    If it's NULL you can get your data from the resultPointer in daveConnection long
    as you do not send further requests.
*/
int DECL2 daveReadBits(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
    PDU p1,p2;
    int res;
#ifdef DEBUG_CALLS
    LOG7("daveReadBits(dc:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
	dc, daveAreaName(area), DBnum, start,len,buffer);
    FLUSH;	    
#endif	    	
    dc->resultPointer=NULL;
    dc->_resultPointer=NULL;
    dc->AnswLen=0;
    p1.header=dc->msgOut+dc->PDUstartO;
    davePrepareReadRequest(dc, &p1);
    daveAddBitVarToReadRequest(&p1, area, DBnum, start, len);
    
    res=_daveExchange(dc, &p1);
    if (res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if (daveDebug & daveDebugPDU)
	LOG3("_daveSetupReceivedPDU() returned: %d=%s\n", res,daveStrerror(res));
    if (res!=daveResOK) return res;
    
    res=_daveTestReadResult(&p2);
    if (daveDebug & daveDebugPDU)
	LOG3("_daveTestReadResult() returned: %d=%s\n", res,daveStrerror(res));
    if (res!=daveResOK) return res;	
    if (daveDebug & daveDebugPDU)
	    LOG2("got %d bytes of data\n", p2.udlen);
    if (p2.udlen==0) {
	return daveResCPUNoData; 
    }	
    if (buffer!=NULL) {
	if (daveDebug & daveDebugPDU)
	    LOG2("copy %d bytes to buffer\n", p2.udlen);
	memcpy(buffer,p2.udata,p2.udlen);
    }	
    dc->resultPointer=p2.udata;
    dc->_resultPointer=p2.udata;
    dc->AnswLen=p2.udlen;
    return res;
}

/*
    Execute a predefined read request. Store results into the resultSet structure.
*/
int DECL2 daveExecReadRequest(daveConnection * dc, PDU *p, daveResultSet* rl){
    PDU p2;
    uc * q;
    daveResult * cr, *c2;
    int res, i, len, rlen;
#ifdef DEBUG_CALLS
    LOG4("daveExecReadRequest(dc:%p, PDU:%p, rl:%p\n", dc, p, rl);
    FLUSH;	
#endif	    	
    dc->AnswLen=0;	// 03/12/05
    dc->resultPointer=NULL;
    dc->_resultPointer=NULL;
    res=_daveExchange(dc, p);
    if (res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if (res!=daveResOK) return res;
    res=_daveTestReadResult(&p2);
    if(res!=daveResOK) return res;
    i=0;
    if (rl!=NULL) {
	cr=(daveResult*)calloc(p2.param[1], sizeof(daveResult));
	rl->numResults=p2.param[1];
	rl->results=cr;
	c2=cr;
	q=p2.data;
	rlen=p2.dlen;
	while (i<p2.param[1]) {
/*	    printf("result %d: %d  %d %d %d\n",i, *q,q[1],q[2],q[3]); */
	    if ((*q==255)&&(rlen>4)) {
		len=q[2]*0x100+q[3];
		if (q[1]==4) {
		    len>>=3;	/* len is in bits, adjust */
		} else if (q[1]==9) {
		    /* len is already in bytes, ok */
		} else if (q[1]==3) {
		    /* len is in bits, but there is a byte per result bit, ok */
		} else {
		    if (daveDebug & daveDebugPDU)
			LOG2("fixme: what to do with data type %d?\n",q[1]);
		}
	    } else {
		len=0;
	    }	
/*	    printf("Store result %d length:%d\n", i, len); */
	    c2->length=len;
	    if(len>0){
		 c2->bytes=(uc*)malloc(len);
		 memcpy(c2->bytes, q+4, len);
	    }	 
	    c2->error=daveUnknownError;

	    if (q[0]==0xFF) {
		c2->error=daveResOK;    
	    } else
		c2->error=q[0];    
		
/*	    printf("Error %d\n", c2->error); */
	    q+=len+4;
	    rlen-=len;
	    if ((len % 2)==1) {
	        q++;
	        rlen--;
	    } 
	    c2++;
	    i++;
	}
    }	
    return res;
}

/*
    Execute a predefined write request.
*/
int DECL2 daveExecWriteRequest(daveConnection * dc, PDU *p, daveResultSet* rl){
    PDU p2;
    uc * q;
    daveResult * cr, *c2;
    int res, i;
#ifdef DEBUG_CALLS
    LOG4("daveExecWriteRequest(dc:%p, PDU:%p, rl:%p\n", dc, p, rl);
    FLUSH;
#endif	    	
    res=_daveExchange(dc, p);
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if(res!=daveResOK) return res;
    res=_daveTestWriteResult(&p2);
    if(res!=daveResOK) return res;
    if (rl!=NULL) {
        cr=(daveResult*)calloc(p2.param[1], sizeof(daveResult));
        rl->numResults=p2.param[1];
        rl->results=cr;
        c2=cr;
        q=p2.data;
        i=0;
        while (i<p2.param[1]) {
/*		printf("result %d: %d  %d %d %d\n",i, *q,q[1],q[2],q[3]); */
		c2->error=daveUnknownError;
		if (q[0]==0x0A) {	/* 300 and 400 families */
		    c2->error=daveResItemNotAvailable;    
		} else if (q[0]==0x03) {	/* 200 family */
		    c2->error=daveResItemNotAvailable;    
		} else if (q[0]==0x05) {
		    c2->error=daveAddressOutOfRange;    
		} else if (q[0]==0xFF) {
		    c2->error=daveResOK;   
		} else if (q[0]==0x07) {
	    	    c2->error=daveWriteDataSizeMismatch;
		}	
/*		    printf("Error %d\n", c2->error); */
		q++;
		c2++;
		i++;
	    } 
    }
    return res;
}


int DECL2 daveUseResult(daveConnection * dc, daveResultSet * rl, int n){
    daveResult * dr;
#ifdef DEBUG_CALLS
    LOG4("daveUseResult(dc:%p, result set:%p, number:%d)\n", dc, rl, n);
#endif	    	
    if (rl==NULL) {
#ifdef DEBUG_CALLS
	LOG1("invalid resultSet \n");
	FLUSH;
#endif
	 return daveEmptyResultSetError;
    } 
#ifdef DEBUG_CALLS
	LOG2("result set has %d results\n",rl->numResults);
	FLUSH;
#endif        
    if (rl->numResults==0) return daveEmptyResultSetError;
    if (n>=rl->numResults) return daveEmptyResultSetError;
    dr = &(rl->results[n]);
    if (dr->error!=0) return dr->error;
    if (dr->length<=0) return daveEmptyResultError;
    
    dc->resultPointer=dr->bytes;
    dc->_resultPointer=dr->bytes;
    return 0;
}

void DECL2 daveFreeResults(daveResultSet * rl){
    daveResult * r;
    int i;
#ifdef DEBUG_CALLS
    LOG2("daveFreeResults(%p)",rl);
#endif	        
    if (rl==NULL) {
#ifdef DEBUG_CALLS
        LOG1("no Results,ready\n");
#endif	            
        return;	// make it NULL safe
}	
/*    printf("result set: %p\n",rl); */
    for (i=0; i<rl->numResults; i++) {
        r=&(rl->results[i]);
/*	printf("result: %p bytes at:%p\n",r,r->bytes); */
	if (r->bytes!=NULL) free(r->bytes);
    }
#ifdef DEBUG_CALLS
        LOG2(" free'd %d results\n",rl->numResults);
#endif	       
    free(rl->results);	// fix from Renato Gartmann     
    rl->numResults=0;
/*    free(rl);	*/ /* This is NOT malloc'd by library but in the application's memory space! */
}

int DECL2 daveGetErrorOfResult(daveResultSet *rs, int number) {
    return rs->results[number].error;
}

/* 
    This will setup a new connection structure using an initialized
    daveInterface and PLC's MPI address.
*/
daveConnection * DECL2 daveNewConnection(daveInterface * di, int MPI, int rack, int slot) {
    daveConnection * dc=(daveConnection *) calloc(1,sizeof(daveConnection));
    if (dc) {
	dc->iface=di;
	dc->MPIAdr=MPI;

	dc->rack=rack;
	dc->slot=slot;
	
	dc->maxPDUlength=1920;				// assume an (unreal?) maximum
	dc->connectionNumber=di->nextConnection;	// 1/10/05 trying Andrew's patch
	
	dc->PDUnumber=0xFFFE;			// just a start value; // test!
	dc->messageNumber=0;
	dc->communicationType=davePGCommunication;

	switch (di->protocol) {
	    case daveProtoMPI:		/* my first Version of MPI */
		dc->PDUstartO=8;	/* position of PDU in outgoing messages */
		dc->PDUstartI=8;	/* position of PDU in incoming messages */
		di->ackPos=6;		/* position of 0xB0 in ack packet */
		dc->maxPDUlength=240;	/* limit because we still cannot assemble a PDU transported in multiple packets */
		break;
	    case daveProtoMPI3:		/* Step 7 Version of MPI */
		dc->PDUstartO=8;	/* position of PDU in outgoing messages */
		dc->PDUstartI=12;	/* position of PDU in incoming messages */
		di->ackPos=10;		/* position of 0xB0 in ack packet */
		dc->maxPDUlength=240;	/* limit because we still cannot assemble a PDU transported in multiple packets */
		break;	
	    case daveProtoMPI2:		/* Andrew's Version of MPI */
	    case daveProtoMPI4:		/* Andrew's Version of MPI with extra STX */
		dc->PDUstartO=6;	/* position of PDU in outgoing messages */
		dc->PDUstartI=6;	/* position of PDU in incoming messages */
		di->ackPos=4;		/* position of 0xB0 in ack packet */
		dc->maxPDUlength=240;	/* limit because we still cannot assemble a PDU transported in multiple packets */
		break;	

	    case daveProtoNLpro:	/* Deltalogic NetLink Pro */	
		dc->PDUstartO=6;	/* position of PDU in outgoing messages */
		dc->PDUstartI=8;	/* position of PDU in incoming messages */
		di->ackPos=4;		/* position of 0xB0 in ack packet */
		break;	

	    case daveProtoPPI:
		dc->PDUstartO=3;	/* position of PDU in outgoing messages */
		dc->PDUstartI=7;	/* position of PDU in incoming messages */
		break;	
		
	    case daveProtoISOTCP:
	    case daveProtoISOTCP243:
		dc->PDUstartO=7;	/* position of PDU in outgoing messages */
		dc->PDUstartI=7;	/* position of PDU in incoming messages */
		di->timeout=1500000;
		break;	
	    case daveProtoMPI_IBH:	
//		dc->maxPDUlength=240;	// limit for NetLink as reported by AFK. Not needed any more. Now, a PDU can be split into multiple IBH packets.
		dc->PDUstartI= sizeof(IBHpacket)+sizeof(MPIheader);	
		dc->PDUstartO= sizeof(IBHpacket)+sizeof(MPIheader); // 02/01/2005	
		break;
	    case daveProtoPPI_IBH:	
//	        dc->maxPDUlength=240;	// limit for NetLink as reported by AFK
		dc->PDUstartI=14; // sizeof(IBHpacket)+7;	
		dc->PDUstartO=13;// sizeof(IBHpacket)+7; // 02/01/2005	
		break;	
		
	    case daveProtoAS511:
		dc->PDUstartI=0; 
		dc->PDUstartO=0;
		break;
	    
	    case daveProtoUserTransport:
		dc->PDUstartI=0;
		dc->PDUstartO=0;
		break;
	    case daveProtoS7online:
		dc->PDUstartI=80;
		dc->PDUstartO=80;
		break;

	    default:
		dc->PDUstartO=8;	/* position of PDU in outgoing messages */
		dc->PDUstartI=8;	/* position of PDU in incoming messages */
		fprintf(stderr,"Unknown protocol on interface %s\n",di->name);	
	}    
#ifdef BCCWIN	
	setTimeOut(di, di->timeout);
#endif
    }
    return dc;	
}

void DECL2 daveSetRoutingDestination(daveConnection * dc, int subnet1,int subnet3,int adrsize, uc* plcadr) {
    memset(&(dc->routingData), 0, sizeof(daveRoutingData));
    dc->routing=1;
    dc->routingData.subnetID1=subnet1;
    dc->routingData.subnetID2=0;
    dc->routingData.subnetID3=subnet3;
    dc->routingData.PLCadrsize=adrsize;
    memcpy(&(dc->routingData.PLCadr), plcadr, adrsize);
}

void DECL2 daveSetCommunicationType(daveConnection * dc,  int communicationType) {
    dc->communicationType=communicationType;
}


int DECL2 daveWriteManyBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
    int res, pos, writeLen;
    uc * pbuf;
    pos=0;
    if (buffer==NULL) return daveResNoBuffer;
    pbuf=(uc*) buffer;
    res=daveResInvalidLength; //the only chance to return this is when len<=0
    while (len>0) {
	if (len>dc->maxPDUlength-28) writeLen=dc->maxPDUlength-28; else writeLen=len;
	res=daveWriteBytes(dc,area, DBnum, start, writeLen, pbuf);
	if (res!=0) return res;
	len-=writeLen;
	start+=writeLen;
	pbuf+=writeLen;
    }	
    return res;
}

int DECL2 daveWriteBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer) {
    PDU p1,p2;
    int res;
    if (dc->iface->protocol==daveProtoAS511) {	    
	return daveWriteS5Bytes(dc, area, DB, start, len, buffer);
    }
    p1.header=dc->msgOut+dc->PDUstartO;
    davePrepareWriteRequest(dc, &p1);
    daveAddVarToWriteRequest(&p1, area, DB, start, len, buffer);
    res=_daveExchange(dc, &p1);
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if(res!=daveResOK) return res;
    res=_daveTestWriteResult(&p2);
    return res;
}

int DECL2 daveWriteBits(daveConnection * dc,int area, int DB, int start, int len, void * buffer) {
    PDU p1,p2;
    int res;
    p1.header=dc->msgOut+dc->PDUstartO;
    davePrepareWriteRequest(dc,&p1);
    daveAddBitVarToWriteRequest(&p1, area, DB, start, len, buffer);
    res=_daveExchange(dc, &p1);
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if (res!=0) return res;
    res=_daveTestWriteResult(&p2);
    return res;
}

/*
    Simplified single bit set:
*/
int DECL2 daveSetBit(daveConnection * dc,int area, int DB, int byteAdr, int bitAdr) {
    int a=1;
    return daveWriteBits(dc, area, DB, 8*byteAdr+bitAdr, 1, &a);
}
/*
    Simplified single bit clear:
*/
int DECL2 daveClrBit(daveConnection * dc,int area, int DB, int byteAdr, int bitAdr) {
    int a=0;
    return daveWriteBits(dc, area, DB, 8*byteAdr+bitAdr, 1, &a);
}


int DECL2 initUpload(daveConnection * dc,char blockType, int blockNr, int * uploadID){
    PDU p1,p2;
    int res;
    if (daveDebug & daveDebugUpload) {
	LOG1("****initUpload\n");
    }	
    p1.header=dc->msgOut+dc->PDUstartO;
    _daveConstructUpload(&p1, blockType, blockNr);
    res=_daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
        LOG2("error:%d\n", res);
        FLUSH;
    }	
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if(res!=daveResOK) return res;
    * uploadID=p2.param[7];
    return 0;
}


int DECL2 doUpload(daveConnection*dc, int * more, uc**buffer, int*len, int uploadID){
    PDU p1,p2;
    int res, netLen;
    p1.header=dc->msgOut+dc->PDUstartO;
    _daveConstructDoUpload(&p1, uploadID);
    res=_daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
	LOG2("error:%d\n", res);
	FLUSH;
    }	
    *more=0;
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    *more=p2.param[1];
    if(res!=daveResOK) return res;
//    netLen=p2.data[1] /* +256*p2.data[0]; */ /* for long PDUs, I guess it is so */;
    netLen=p2.data[1]+256*p2.data[0]; /* some user confirmed my guess... */;
    if (*buffer) {
	memcpy(*buffer,p2.data+4,netLen);
	*buffer+=netLen;
	if (daveDebug & daveDebugUpload) {
	    LOG2("buffer:%p\n",*buffer);
	    FLUSH;
	}    
    }
    *len+=netLen;
    return res;
}

int DECL2 endUpload(daveConnection*dc, int uploadID){
    PDU p1,p2;
    int res;
    
    p1.header=dc->msgOut+dc->PDUstartO;
    _daveConstructEndUpload(&p1,uploadID);
    
    res=_daveExchange(dc, &p1);
    if (daveDebug & daveDebugUpload) {
        LOG2("error:%d\n", res);
        FLUSH;
    }
    if(res!=daveResOK) return res;	
    res=_daveSetupReceivedPDU(dc, &p2);
    return res;
}

/*
    error code to message string conversion:
*/
char * DECL2 daveStrerror(int code) {
    switch (code) {
	case daveResOK: return "ok";
	case daveResMultipleBitsNotSupported:return "the CPU does not support reading a bit block of length<>1";
	case daveResItemNotAvailable: return "the desired item is not available in the PLC";
	case daveResItemNotAvailable200: return "the desired item is not available in the PLC (200 family)";
	case daveAddressOutOfRange: return "the desired address is beyond limit for this PLC";
	case daveResCPUNoData : return "the PLC returned a packet with no result data";
	case daveUnknownError : return "the PLC returned an error code not understood by this library";
	case daveEmptyResultError : return "this result contains no data";
	case daveEmptyResultSetError: return "cannot work with an undefined result set";
	case daveResCannotEvaluatePDU: return "cannot evaluate the received PDU";
	case daveWriteDataSizeMismatch: return "Write data size error";
	case daveResNoPeripheralAtAddress: return "No data from I/O module";
	case daveResUnexpectedFunc: return "Unexpected function code in answer";
	case daveResUnknownDataUnitSize: return "PLC responds with an unknown data type";
	
	case daveResShortPacket: return "Short packet from PLC";
	case daveResTimeout: return "Timeout when waiting for PLC response";
	case daveResNoBuffer: return "No buffer provided";
	case daveNotAvailableInS5: return "Function not supported for S5";
	
	case 0x8000: return "function already occupied.";
	case 0x8001: return "not allowed in current operating status.";
	case 0x8101: return "hardware fault.";
	case 0x8103: return "object access not allowed.";
	case 0x8104: return "context is not supported. Step7 says:Function not implemented or error in telgram.";
	case 0x8105: return "invalid address.";
	case 0x8106: return "data type not supported.";
	case 0x8107: return "data type not consistent.";
	case 0x810A: return "object does not exist.";
	case 0x8301: return "insufficient CPU memory ?";
	case 0x8402: return "CPU already in RUN or already in STOP ?";
	case 0x8404: return "severe error ?";
	case 0x8500: return "incorrect PDU size.";
	case 0x8702: return "address invalid."; ;
	case 0xd002: return "Step7:variant of command is illegal.";
	case 0xd004: return "Step7:status for this command is illegal.";
	case 0xd0A1: return "Step7:function is not allowed in the current protection level.";
	case 0xd201: return "block name syntax error.";
	case 0xd202: return "syntax error function parameter.";
	case 0xd203: return "syntax error block type.";
	case 0xd204: return "no linked block in storage medium.";
	case 0xd205: return "object already exists.";
	case 0xd206: return "object already exists.";
	case 0xd207: return "block exists in EPROM.";
	case 0xd209: return "block does not exist/could not be found.";
	case 0xd20e: return "no block present.";
	case 0xd210: return "block number too big.";
//	case 0xd240: return "unfinished block transfer in progress?";  // my guess
	case 0xd240: return "Coordination rules were violated.";
/*  Multiple functions tried to manipulate the same object.
    Example: a block could not be copied,because it is already present in the target system
    and
*/    
	case 0xd241: return "Operation not permitted in current protection level.";
/**/	case 0xd242: return "protection violation while processing F-blocks. F-blocks can only be processed after password input.";
	case 0xd401: return "invalid SZL ID.";
	case 0xd402: return "invalid SZL index.";
	case 0xd406: return "diagnosis: info not available.";
	case 0xd409: return "diagnosis: DP error.";
	case 0xdc01: return "invalid BCD code or Invalid time format?";
	default: return "no message defined!";
    }
}

/*
    Copy an internal String into an external string buffer. This is needed to interface
    with Visual Basic. Maybe it is helpful elsewhere, too.
*/
void DECL2 daveStringCopy(char * intString, char * extString) {
    strncpy(extString, intString, 255);	// arbritray limit. I hope each external string has at least this 
				// capacity
}

/*
    I'm not quite sure whether this is all correct, but it seems to work for all numbers I tested
*/
float DECL2 daveGetKGAt(daveConnection * dc,int pos) {
    char kgExponent;
    int sign;
    union {
	uc b[4];
	int mantissa;
    } f;
    union {
	int a;
	float f;
    } v;
    uc* p=dc->_resultPointer+pos;
    kgExponent=*p;
    p++;
#ifdef DAVE_LITTLE_ENDIAN    
    f.b[3]=0;
    f.b[2]=*p;
    p++;
    f.b[1]=*p;
    p++;
    f.b[0]=*p;
    sign=(f.b[2]& 0x80);
    f.b[2]&=0x7f;
#else        
    f.b[0]=0;
    f.b[1]=*p;
    p++;
    f.b[2]=*p;
    p++;
    f.b[3]=*p;
    sign=(f.b[1]& 0x80);
    f.b[1]&=0x7f;
#endif    
    p++;
    LOG3("daveGetKG(dc:%p, mantissa:0x%08X)\n",dc, f.mantissa);
    if(sign) {
	f.mantissa=f.mantissa ^0xffffffff;
	f.mantissa=f.mantissa +0x00800000;
    }	
    v.f=f.mantissa;
    if(sign) {
	v.f=-v.f;
    }
    LOG5("daveGetKG(dc:%p, mantissa:0x%08X exponent:0x%02X %0.8f)\n",dc, f.mantissa, kgExponent,v.f);
    while (kgExponent>23) {
	v.f=v.f*2.0;
	kgExponent--;
    }
    while (kgExponent<23) {
	v.f=v.f/2.0;
	kgExponent++;
    }	
    LOG2("daveGetKG(%08X)\n",v.a);
    v.f=-v.f;
    LOG2("daveGetKG(%08X)\n",v.a);
    v.f=-v.f;
#ifdef DEBUG_CALLS
    LOG3("daveGetKG(dc:%p, result:%0.6f)\n",	dc, v.f);
    FLUSH;
#endif	    
    return (v.f);
}

float DECL2 daveGetKG(daveConnection * dc) {
    float f;
    f=daveGetKGAt(dc, ((int)dc->resultPointer-(int)dc->_resultPointer));
    dc->resultPointer+=4;
    return f;
}

float DECL2 daveGetFloat(daveConnection * dc) {
    union {
	float a;
	uc b[4];
    } f;
#ifdef DAVE_LITTLE_ENDIAN    
    f.b[3]=*(dc->resultPointer);
    dc->resultPointer++;
    f.b[2]=*(dc->resultPointer);
    dc->resultPointer++;
    f.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    f.b[0]=*(dc->resultPointer);
#else        
    f.b[0]=*(dc->resultPointer);
    dc->resultPointer++;
    f.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    f.b[2]=*(dc->resultPointer);
    dc->resultPointer++;
    f.b[3]=*(dc->resultPointer);
#endif    
    dc->resultPointer++;
#ifdef DEBUG_CALLS
    LOG3("daveGetFloat(dc:%p, result:%0.6f)\n",	dc, f.a);
    FLUSH;
#endif	    
    return (f.a);
}

float DECL2 daveGetFloatAt(daveConnection * dc, int pos) {
    union {
	float a;
	uc b[4];
    } f;
    uc* p=(uc*)dc->_resultPointer;
    p+=pos;
#ifdef DAVE_LITTLE_ENDIAN
    f.b[3]=*p;p++;
    f.b[2]=*p;p++;
    f.b[1]=*p;p++;
    f.b[0]=*p;
#else    
    f.b[0]=*p;p++;
    f.b[1]=*p;p++;
    f.b[2]=*p;p++;
    f.b[3]=*p;
#endif    
    return (f.a);
}

float DECL2 toPLCfloat(float ff) {
#ifdef DAVE_LITTLE_ENDIAN    
    union {
	float a;
	uc b[4];
    } f;
    uc c;

    f.a=ff;
    c=f.b[0];
    f.b[0]=f.b[3];
    f.b[3]=c;
    c=f.b[1];
    f.b[1]=f.b[2];
    f.b[2]=c;

//    f.a=ff;  //fixed bug found by luca at ventisei
#ifdef DEBUG_CALLS
    LOG3("toPLCfloat(%0.6f) = %0.6f\n",ff,f.a);
    FLUSH;
#endif	    
    return (f.a);
#else    
#ifdef DEBUG_CALLS
    LOG3("toPLCfloat(%0.6f) = %0.6f\n",ff,ff);
    FLUSH;
#endif	    
    return ff;
#endif        
}

int DECL2 daveToPLCfloat(float ff) {
    union {
	float a;
	uc b[4];
	int c;
    } f;
#ifdef DAVE_LITTLE_ENDIAN    
    uc c;
    f.a=ff;
    c=f.b[0];
    f.b[0]=f.b[3];
    f.b[3]=c;
    c=f.b[1];
    f.b[1]=f.b[2];
    f.b[2]=c;
#else    
    f.a=ff;
#endif    
#ifdef DEBUG_CALLS
    LOG3("toPLCfloat(%0.6f) = %08x\n",ff,f.c);
    FLUSH;
#endif	    
    return (f.c);
}

int DECL2 daveToKG(float ff) {
    union {
	uc b[4];
	int c;
    } f,f2;
    char kgExponent=23;
    LOG2("daveToKG(%0.8f)\n",ff);
    if (ff==0.0) {
	f.c=0;
	return 0;
    }	
    f2.c=(int)ff;	// attention! what does this cast? I do want to take the integer part of that float, NOT reinterpret the bit pattern as int!
    LOG4("daveToKG(mantissa:0x%08X exponent:0x%02X %0.8f)\n", f2.c, kgExponent,ff);
    while (f2.c > 0x00400000){
	ff/=2;
	f2.c=(int)ff;	// attention! what does this cast? I do want to take the integer part of that float, NOT reinterpret the bit pattern as int!
	kgExponent++;
    }
    while (f2.c < 0x00400000){
	ff*=2;
	f2.c=(int)ff;  	// attention! what does this cast? I do want to take the integer part of that float, NOT reinterpret the bit pattern as int!
	kgExponent--;
    }
    LOG4("daveToKG(mantissa:0x%08X exponent:0x%02X %0.8f)\n", f2.c, kgExponent,ff);
    f.b[0]=kgExponent;
#ifdef DAVE_LITTLE_ENDIAN    
    f.b[1]=f2.b[2];
    f.b[2]=f2.b[1];
    f.b[3]=f2.b[0];
#else    
    f.b[3]=f2.b[3];
    f.b[2]=f2.b[2];
    f.b[1]=f2.b[1];
#endif    
#ifdef DEBUG_CALLS
    LOG3("daveToKG(%0.6f) = %08x\n",ff,f.c);
    FLUSH;
#endif	    
    return (f.c);
}


short DECL2 daveSwapIed_16(short ff) {
#ifdef DAVE_LITTLE_ENDIAN
    union {
	short a;
	uc b[2];
    } f;
    uc c;
    f.a=ff;
    c=f.b[0];
    f.b[0]=f.b[1];
    f.b[1]=c;
    return (f.a);
#else
//    printf("Here we are in BIG ENDIAN!!!\n");
    return (ff);
#endif    
}

int DECL2 daveSwapIed_32(int ff) {
#ifdef DAVE_LITTLE_ENDIAN
    union {
	int a;
	uc b[4];
    } f;
    uc c;
    f.a=ff;
    c=f.b[0];
    f.b[0]=f.b[3];
    f.b[3]=c;
    c=f.b[1];
    f.b[1]=f.b[2];
    f.b[2]=c;
    return f.a;
#else
//    printf("Here we are in BIG ENDIAN!!!\n");
    return ff;
#endif       
}

/**
    Timer and Counter conversion functions:
**/
/*	
    get time in seconds from current read position:
*/
float DECL2 daveGetSeconds(daveConnection * dc) {
    uc b[2],a;
    float f;
    b[1]=*(dc->resultPointer)++;
    b[0]=*(dc->resultPointer)++;
    f=b[0] & 0xf;
    f+=10*((b[0] & 0xf0)>>4);
    f+=100*(b[1] & 0xf);
    a=((b[1] & 0xf0)>>4);
    switch (a) {
	case 0: f*=0.01;break;
	case 1: f*=0.1;break;
	case 3: f*=10.0;break;
    }
    return (f);    
}
/*	
    get time in seconds from random position:
*/
float DECL2 daveGetSecondsAt(daveConnection * dc, int pos) {
    float f;
    uc b[2],a;
    uc* p=(uc*)dc->_resultPointer;
    p+=pos;
    b[1]=*p;
    p++;
    b[0]=*p;
    f=b[0] & 0xf;
    f+=10*((b[0] & 0xf0)>>4);
    f+=100*(b[1] & 0xf);
    a=((b[1] & 0xf0)>>4);
    switch (a) {
	case 0: f*=0.01;break;
	case 1: f*=0.1;break;
	case 3: f*=10.0;break;
    }
    return (f);
}
/*	
    get counter value from current read position:
*/
int DECL2 daveGetCounterValue(daveConnection * dc) {
    uc b[2];
    int f;
    b[1]=*(dc->resultPointer)++;
    b[0]=*(dc->resultPointer)++;
    f=b[0] & 0xf;
    f+=10*((b[0] & 0xf0)>>4);
    f+=100*(b[1] & 0xf);
    return (f);    
}
/*
    get counter value from random read position:
*/
int DECL2 daveGetCounterValueAt(daveConnection * dc,int pos){
    int f;
    uc b[2];
    uc* p=(uc*)dc->_resultPointer;
    p+=pos;
    b[1]=*p;
    p++;
    b[0]=*p;
    f=b[0] & 0xf;
    f+=10*((b[0] & 0xf0)>>4);
    f+=100*(b[1] & 0xf);
    return (f);
}

/*
    dummy functions for protocols not providing a specific function:
*/

int DECL2 _daveReturnOkDummy(daveInterface * di){
    return 0;
}

int DECL2 _daveReturnOkDummy2(daveConnection * dc){
    return 0;
}

int DECL2 _daveListReachablePartnersDummy (daveInterface * di, char * buf) {
    return 0;
}

/*
    MPI specific functions:
*/

/*
    This writes a single chracter to the serial interface:
*/

void DECL2 _daveSendSingle(daveInterface * di,	/* serial interface */
		     uc c  			/* chracter to be send */
		     ) 
{
    di->ifwrite(di, (char*)&c, 1);
}

int DECL2 _daveReadSingle(daveInterface * di) {
	char res;
	int i;
	i=di->ifread(di, &res,1);
	if ((daveDebug & daveDebugSpecialChars)!=0)
	    LOG3("readSingle %d chars. 1st %02X\n",i,res);
	if (i==1) return res;    
	return 0;
}

int DECL2 _daveReadMPI(daveInterface * di, uc *b) {
	int res=0,state=0,nr_read;
	uc bcc=0;
rep:
	{	
	    nr_read= di->ifread(di, (char*)(b+res), 1);
	    if (nr_read==0) return 0;
	    res+=nr_read;
	    if ((res==1) && (*(b+res-1)==DLE)) {
		if ((daveDebug & daveDebugSpecialChars)!=0)
		    LOG1("readMPI single DLE!\n");
		return 1;
	    }		
	    if ((res==1) && (*(b+res-1)==STX)) {
		if ((daveDebug & daveDebugSpecialChars)!=0)
		    LOG1("readMPI single STX!\n");
	        return 1;
	    }
	    if (*(b+res-1)==DLE) {
		if (state==0) {
		    state=1;
/*		    if ((daveDebug & daveDebugSpecialChars)!=0)
			LOG1("readMPI 1st DLE in data.\n") 
			;
*/			
		} else if (state==1) {
		    state=0;
		    res--;		/* forget this DLE, it is the second of a pair */
/*		    if ((daveDebug & daveDebugSpecialChars)!=0)
		        LOG1("readMPI 2nd DLE in data.\n") 
		    ;
*/		    
		}
	    } 	
	    if (state==3) {
	        if ((daveDebug & daveDebugSpecialChars)!=0)
		    LOG4("readMPI: packet size %d, got BCC: %x. I calc: %x\n",res,*(b+res-1),bcc);
		if ((daveDebug & daveDebugRawRead)!=0)
		    _daveDump("answer",b,res);
		return res;
	    } else {
		bcc=bcc^(*(b+res-1));
	    }
	    	
	    if (*(b+res-1)==ETX) if (state==1) {
	        state=3;
	        if ((daveDebug & daveDebugSpecialChars)!=0)
		    LOG1("readMPI: DLE ETX,packet end.\n");
	    }	
	    goto rep;
	} 
    }

int DECL2 _daveReadMPI2(daveInterface * di, uc *b) {
    uc b6;
    uc b2[daveMaxRawLen];
    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14,5,1,0};
    int res2, re;
    int res=_daveReadMPI(di, b);
    re=res;
    b6=b[6];
again:
    if ((re>=7)&&(b6==0xF0)) {
	if ((daveDebug & daveDebugRawRead)!=0)
	    LOG1("follow up expected\n");
//	uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14,0xB0,0,0};
//	uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14,5,1,0};
/*
	uc m[3];
	m[0]=0xB1;
        m[1]=0x01;
	m[2]=nr;
*/	
	fix[8]=b[7];
	fix[1]=b[1];
	_daveSendSingle(di, DLE);
	_daveSendSingle(di, STX);
	_daveReadSingle(di);
	_daveSendWithCRC(di, fix, sizeof(fix));
	_daveReadSingle(di);
	_daveReadSingle(di);
	_daveSendSingle(di, STX);
	_daveSendSingle(di, DLE);
//	_daveReadSingle(di);
//	_daveReadSingle(di);
	res2=_daveReadMPI(di, b2);
	b6=b2[6];
	re=res2;
	memcpy(b+res-3, b2+6, res2-9);
	res+=res2-9;
	b[7]++;		// increase packet number for ack
	goto again;
    }
    if (res>1) {
        _daveSendSingle(di, DLE);
        _daveSendSingle(di, STX);
    }

    return res;
}

int DECL2 _daveGetAck(daveConnection * dc) {
    int res;    
    daveInterface * di=dc->iface;
    int nr=dc->needAckNumber;
    uc b1[daveMaxRawLen];
    if (daveDebug & daveDebugPacket)
	LOG2("%s enter getAck ack\n", di->name);
    res = _daveReadMPI(di, b1);
    if (res<0) return res-10;
    if (res!=di->ackPos+6) {
	if (daveDebug & daveDebugPrintErrors) {
    	    LOG4("%s *** getAck wrong length %d for ack. Waiting for %d\n dump:", di->name, res, nr);
	    _daveDump("wrong ack:",b1,res);
	}
	return -1;
    }	
    if (b1[di->ackPos]!=0xB0) {
	if (daveDebug & daveDebugPrintErrors) {
    	    LOG3("%s *** getAck char[6] %x no ack\n", di->name, b1[di->ackPos+2]);
	}    
	return -2;
    }		
    if (b1[di->ackPos+2]!=nr) {
	if (daveDebug & daveDebugPrintErrors) {
    	    LOG4("%s *** getAck got: %d need: %d\n", di->name, b1[di->ackPos+2],nr);
	}    
	return -3;
    }	
    return 0;
}


#define tmo_normal 95000
/*
    This reads up to max chracters when it can get them and returns the number:
*/
int DECL2 _daveReadChars2(daveInterface * di,	/* serial interface */
		    uc *b, 		/* a buffer */
		    int max		/* limit */
		)
{
    return di->ifread(di,(char*)b,max);
}

/* 
    This sends a string after doubling DLEs in the String
    and adding DLE,ETX and bcc.
*/
int DECL2 _daveSendWithCRC(daveInterface * di, /* serial interface */
		    uc *b, 		 /* a buffer containing the message */
		    int size		 /* the size of the string */
		)
{		
    uc target[daveMaxRawLen];	
    int i,targetSize=0;
    int bcc=DLE^ETX; /* preload */
    for (i=0; i<size; i++) {
	target[targetSize]=b[i];targetSize++;
	if (DLE==b[i]) {
	    target[targetSize]=DLE;
	    targetSize++;
	}  else 
	    bcc=bcc^b[i];	/* The doubled DLE effectively contributes nothing */
    };
    target[targetSize]=DLE;
    target[targetSize+1]=ETX;
    target[targetSize+2]=bcc;
    targetSize+=3;
//    daveWriteFile(di->fd.wfd, target, targetSize, wr);
    di->ifwrite(di, (char*)target, targetSize);
    if (daveDebug & daveDebugPacket)
	_daveDump("_daveSendWithCRC",target, targetSize);
    return 0;
}

/* 
    This adds a prefix to a string and theen sends it
    after doubling DLEs in the String
    and adding DLE,ETX and bcc.
*/
int DECL2 _daveSendWithPrefix(daveConnection * dc, uc *b, int size)
{
    uc target[daveMaxRawLen];
    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
    uc fix2[]= {0x00,0x0c,0x03,0x03};
    if (dc->iface->protocol==daveProtoMPI2) {	
	fix2[2]=dc->connectionNumber2; 		// 1/10/05 trying Andrew's patch
	fix2[3]=dc->connectionNumber; 		// 1/10/05 trying Andrew's patch
	memcpy(target,fix2,sizeof(fix2));
	memcpy(target+sizeof(fix2),b,size);
	return _daveSendWithCRC(dc->iface,target,size+sizeof(fix2));
    }  else {
	fix[4]=dc->connectionNumber2; 		// 1/10/05 trying Andrew's patch
	fix[5]=dc->connectionNumber; 		// 1/10/05 trying Andrew's patch
    	memcpy(target,fix,sizeof(fix));
	memcpy(target+sizeof(fix),b,size);
	target[1]|=dc->MPIAdr;
//	target[2]|=dc->iface->localMPI;
	memcpy(target+sizeof(fix),b,size);
	return _daveSendWithCRC(dc->iface,target,size+sizeof(fix));
    }	
}

int DECL2 _daveSendWithPrefix2(daveConnection * dc, int size)
{
    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
    uc fix2[]= {0x00, 0x0C, 0x03, 0x03};
    
    if (dc->iface->protocol==daveProtoMPI2) {
	fix2[2]=dc->connectionNumber2; 		// 1/10/05 trying Andrew's patch
	fix2[3]=dc->connectionNumber; 		// 1/10/05 trying Andrew's patch
	memcpy(dc->msgOut, fix2, sizeof(fix2));
	dc->msgOut[sizeof(fix2)]=0xF1;
	return _daveSendWithCRC(dc->iface, dc->msgOut, size+sizeof(fix2));
    }
    else if (dc->iface->protocol==daveProtoMPI) {
	fix[4]=dc->connectionNumber2;		// 1/10/05 trying Andrew's patch
	fix[5]=dc->connectionNumber;		// 1/10/05 trying Andrew's patch
	memcpy(dc->msgOut, fix, sizeof(fix));
	dc->msgOut[1]|=dc->MPIAdr;
//	dc->msgOut[2]|=dc->iface->localMPI; //???
	dc->msgOut[sizeof(fix)]=0xF1;
/*	if (daveDebug & daveDebugPacket)
	    _daveDump("_daveSendWithPrefix2",dc->msgOut,size+sizeof(fix)); */
	return _daveSendWithCRC(dc->iface, dc->msgOut, size+sizeof(fix));
    }
    return -1; /* shouldn't happen. */
}

/* 
    Sends an ackknowledge message for the message number nr:
*/
int DECL2 _daveSendAck(daveConnection * dc, int nr)
{
    uc m[3];
    if (daveDebug & daveDebugPacket)
	LOG3("%s sendAck for message %d \n", dc->iface->name,nr);
    m[0]=0xB0;
    m[1]=0x01;
    m[2]=nr;
    return _daveSendWithPrefix(dc, m, 3);
}

/* 
    Handle MPI message numbers in a central place:
*/
int DECL2 _daveIncMessageNumber(daveConnection * dc) {
	int res=dc->messageNumber++;	
	if (daveDebug & daveDebugPacket)
	    LOG2("_daveIncMessageNumber new number %d \n", dc->messageNumber);
	if ((dc->messageNumber)==0) dc->messageNumber=1;
	return res;
}	
/*
    Executes part of the dialog necessary to send a message:
*/
int DECL2 _daveSendDialog2(daveConnection * dc, int size)
{
    int a;
    _daveSendSingle(dc->iface, STX);
    if (_daveReadSingle(dc->iface)!=DLE) {
	if (daveDebug & daveDebugPrintErrors)
	    LOG2("%s *** no DLE before send.\n", dc->iface->name);
        _daveSendSingle(dc->iface, DLE);
	if (_daveReadSingle(dc->iface)!=DLE) {
	    if (daveDebug & daveDebugPrintErrors)
		LOG2("%s retry*** no DLE before send.\n", dc->iface->name);
	    return -1;
	}
    } 
    if (size>5){
	dc->needAckNumber=dc->messageNumber;
	dc->msgOut[dc->iface->ackPos+1]=_daveIncMessageNumber(dc);
    }	
    _daveSendWithPrefix2(dc, size);
    a=_daveReadSingle(dc->iface);
    if (a!=DLE) {
	LOG3("%s *** no DLE after send(1) %02x.\n", dc->iface->name,a);
	a=_daveReadSingle(dc->iface);
	if (a!=DLE) {
	    LOG3("%s *** no DLE after send(2) %02x.\n", dc->iface->name,a);
	    _daveSendWithPrefix2(dc, size);
	    a=_daveReadSingle(dc->iface);
	    if (a!=DLE) {
		LOG3("%s *** no DLE after resend(3) %02x.\n", dc->iface->name,a);
		_daveSendSingle(dc->iface, STX);
		a=_daveReadSingle(dc->iface);
		if (a!=DLE) {
    		    LOG2("%s *** no DLE before resend.\n", dc->iface->name);	    
		    return -1;
		} else {
		    _daveSendWithPrefix2(dc, size);
		    a=_daveReadSingle(dc->iface);
		    if (a!=DLE) {
    			LOG2("%s *** no DLE before resend.\n", dc->iface->name);
			return -1;
		    } else {
			LOG2("%s *** got DLE after repeating whole transmisson.\n", dc->iface->name);
			return 0;
		    }	
		}
	    } else	
		LOG3("%s *** got DLE after resend(3) %02x.\n", dc->iface->name,a);
	}    
	
    }
    return 0;
}

int DECL2 _daveGetResponseMPI(daveConnection *dc) {
    int res;
    res= _daveReadSingle(dc->iface);
    if (res!=STX) {
	if (daveDebug & daveDebugPrintErrors) {
	    LOG2("%s *** _daveGetResponseMPI no STX before answer data.\n", dc->iface->name);	    
	}        
	res= _daveReadSingle(dc->iface);
    }
    _daveSendSingle(dc->iface,DLE);
    if (daveDebug & daveDebugExchange) {
        LOG2("%s _daveGetResponseMPI receive message.\n", dc->iface->name);	    
    }	
    res = _daveReadMPI2(dc->iface,dc->msgIn);
/*	LOG3("%s *** _daveExchange read result %d.\n", dc->iface->name, res); */
    if (res<=0) {
	if (daveDebug & daveDebugPrintErrors) {
	    LOG2("%s *** _daveGetResponseMPI no answer data.\n", dc->iface->name);	    
	}        
	return -3;
    }	
/*	This is NONSENSE!    
    if (daveDebug & daveDebugExchange) {
        LOG3("%s _daveGetResponseMPI got %d bytes\n", dc->iface->name, dc->AnswLen);	    
    }    
*/    
    if (_daveReadSingle(dc->iface)!=DLE) {
        if (daveDebug & daveDebugPrintErrors) {
            LOG2("%s *** _daveGetResponseMPI: no DLE.\n", dc->iface->name);	    
        }	
	return -5;
    }    
    _daveSendAck(dc, dc->msgIn[dc->iface->ackPos+1]);
    if (_daveReadSingle(dc->iface)!=DLE) {
        if (daveDebug & daveDebugPrintErrors) {
            LOG2("%s *** _daveGetResponseMPI: no DLE after Ack.\n", dc->iface->name);	    
        }
	return -6;
    }    
    return 0;
}

/*
    Sends a message and gets ackknowledge:
*/
int DECL2 _daveSendMessageMPI(daveConnection * dc, PDU * p) {
    if (daveDebug & daveDebugExchange) {
        LOG2("%s enter _daveSendMessageMPI\n", dc->iface->name);	    
    }    
    if (_daveSendDialog2(dc, 2+p->hlen+p->plen+p->dlen)) {
	LOG2("%s *** _daveSendMessageMPI error in _daveSendDialog.\n",dc->iface->name);	    		
//	return -1;	
    }	
    if (daveDebug & daveDebugExchange) {
        LOG3("%s _daveSendMessageMPI send done. needAck %x\n", dc->iface->name,dc->needAckNumber);	    
    }	
    
    if (_daveReadSingle(dc->iface)!=STX) {
	if (daveDebug & daveDebugPrintErrors) {
	    LOG2("%s *** _daveSendMessageMPI no STX after _daveSendDialog.\n",dc->iface->name);
	}    
	if ( _daveReadSingle(dc->iface)!=STX) {
	    if (daveDebug & daveDebugPrintErrors) {
		LOG2("%s *** _daveSendMessageMPI no STX after _daveSendDialog.\n",dc->iface->name);
	    }	
	    return -2;
	} else {
	    if (daveDebug & daveDebugPrintErrors) {
		LOG2("%s *** _daveSendMessageMPI got STX after retry.\n",dc->iface->name);
	    }
	}    
    }
    _daveSendSingle(dc->iface,DLE);
    _daveGetAck(dc);
    _daveSendSingle(dc->iface,DLE);
    return 0;
}

int DECL2 _daveExchangeMPI(daveConnection * dc, PDU * p) {
    _daveSendMessageMPI(dc, p);
    dc->AnswLen=0;
    return _daveGetResponseMPI(dc);
}

/* 
    Send a string of init data to the MPI adapter.
*/
int DECL2 _daveInitStep(daveInterface * di, int nr, uc *fix, int len, char * caller) {
    _daveSendSingle(di, STX);
    if (_daveReadSingle(di)!=DLE){
	if (daveDebug & daveDebugInitAdapter)
	    LOG3("%s %s no answer (DLE) from adapter.\n", di->name, caller);
        if (_daveReadSingle(di)!=DLE){
	    if (daveDebug & daveDebugInitAdapter)
	        LOG3("%s %s no answer (DLE) from adapter.\n", di->name, caller);
	    return nr;
	}    
    }	 
    if (daveDebug & daveDebugInitAdapter)
	LOG4("%s %s step %d.\n", di->name, caller, nr);
    _daveSendWithCRC(di, fix, len);
    if (_daveReadSingle(di)!=DLE) return nr+1;
    if (daveDebug & daveDebugInitAdapter)
	LOG4("%s %s step %d.\n", di->name, caller,nr+1);
    if (_daveReadSingle(di)!=STX) return nr+2;
    if (daveDebug & daveDebugInitAdapter)
	LOG4("%s %s step %d.\n", di->name, caller,nr+2);
    _daveSendSingle(di, DLE);
    return 0;
}    

/* 
    This initializes the MPI adapter. Andrew's version.
*/
int DECL2 _daveInitAdapterMPI2(daveInterface * di)  /* serial interface */
{
    uc b3[]={
	0x01,0x03,0x02,0x17, 0x00,0x9F,0x01,0x3C,
 	0x00,0x90,0x01,0x14, 0x00,	/* ^^^ MaxTsdr */
	0x00,0x5,
 	0x02,/* Bus speed */

	0x00,0x0F,0x05,0x01,0x01,0x03,0x80,/* from topserverdemo */
	/*^^ - Local mpi */
    };		
    
    int res;
    uc b1[daveMaxRawLen];
    b3[16]=di->localMPI;
    if (di->speed==daveSpeed500k)
	b3[7]=0x64;
    if (di->speed==daveSpeed1500k)
	b3[7]=0x96;
    b3[15]=di->speed;

    res=_daveInitStep(di, 1, b3, sizeof(b3),"initAdapter()");

    res= _daveReadMPI(di, b1);
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s initAdapter() success.\n", di->name);
   _daveSendSingle(di,DLE);
    di->users=0;	/* there cannot be any connections now */
    return 0;
}

/* 
    Initializes the MPI adapter.
*/
int DECL2 _daveInitAdapterMPI1(daveInterface * di) {
    uc b2[]={
	0x01,0x0D,0x02,
    };
//  us answ1[]={0x01,0x0D,0x20,'V','0','0','.','8','3'};
//  us adapter0330[]={0x01,0x03,0x20,'E','=','0','3','3','0'};
//  us answ2[]={0x01,0x03,0x20,'V','0','0','.','8','3'};
    us answ1[]={0x01,0x10D,0x20,'V','0','0','.',0x138,0x133};
    us adapter0330[]={0x01,0x03,0x20,'E','=','0','3',0x133,0x130};

    
    uc b3[]={
	0x01,0x03,0x02,0x27, 0x00,0x9F,0x01,0x3C,
	0x00,0x90,0x01,0x14, 0x00,
	0x00,0x05,
	0x02,
	0x00,0x1F,0x02,0x01,0x01,0x03,0x80,
//	^localMPI
    };		
    uc v1[]={
	0x01,0x0C,0x02,
    };
    int res;
    uc b1[daveMaxRawLen];
    if (daveDebug & daveDebugInitAdapter)
	LOG2("%s enter initAdapter(1).\n", di->name);
	
    res=_daveInitStep(di, 1, b2, sizeof(b2),"initAdapter()");
    if (res) {
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() fails.\n", di->name);
	return -44;    
    }	    
    
    res= _daveReadMPI(di, b1);
    _daveSendSingle(di,DLE);
    
    if (_daveMemcmp(answ1, b1, sizeof(answ1)/2)) return 4;
    
    b3[16]=di->localMPI;

    if (di->speed==daveSpeed500k)
	b3[7]=0x64;
    if (di->speed==daveSpeed1500k)
	b3[7]=0x96;
    b3[15]=di->speed;
    res=_daveInitStep(di, 4, b3, sizeof(b3),"initAdapter()");	
    if (res) {
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() fails.\n", di->name);
	return -54;    
    }
/*
    The following extra lines seem to be necessary for 
    TS adapter 6ES7 972-0CA33-0XAC:
*/        
    res= _daveReadMPI(di, b1);
    _daveSendSingle(di,DLE);    
    if (!_daveMemcmp(adapter0330, b1, sizeof(adapter0330)/2)) {
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() found Adapter E=0330.\n", di->name);
	_daveSendSingle(di,STX);
	res= _daveReadMPI2(di, b1);
	_daveSendWithCRC(di, v1, sizeof(v1));	
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() Adapter E=0330 step 7.\n", di->name);
	if (_daveReadSingle(di)!=DLE) return 8;
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() Adapter E=0330 step 8.\n", di->name);
	res= _daveReadMPI(di, b1);
	if (res!=1 || b1[0]!=STX) return 9;
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() Adapter E=0330 step 9.\n", di->name);
	_daveSendSingle(di,DLE);
/* This needed the exact Adapter version:    */
/* instead, just read and waste it */ 
	res= _daveReadMPI(di, b1);
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() Adapter E=0330 step 10.\n", di->name);
	_daveSendSingle(di,DLE);    
	return 0;    
    
    } else if (!_daveMemcmp(answ1, b1, sizeof(answ1)/2)) {
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() success.\n", di->name);
	di->users=0;	/* there cannot be any connections now */
	return 0;
    } else {
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() failed.\n", di->name);
	return -56;    
    }
}

us ccrc(uc *b,int size) {
	us sum;
	int i,j,m,lll;
//initialize for crc
	lll=0xcf87;
	sum=0x7e;
	for(j=2;j<=size;j++) {
	    for(m=0;m<=7;m++) {
		if((lll&0x8000)!=0) {
		    lll=lll^0x8408;
		    lll=lll<<1;
		    lll=lll+1;
		} else {
		    lll=lll<<1;
		}
	    }
	    sum=sum^lll;
	}
	for(j=0;j<size;j++) {
	    sum=sum ^ b[j];
	    for(i=0;i<=7;i++) {
		if(sum&0x01) {
		    sum=sum>>1;
		    sum=sum^0x8408;
		} else {
		    sum=sum>>1;
		}
	    }
	}
	return sum;
}


int daveSendWithCRC3(daveInterface * di, uc* buffer,int length) {
    uc target[daveMaxRawLen];
    us crc;
    memcpy(target+4,buffer,length);
    target[0]=0x7e;
    if (target[10]==0xB0) {
	target[1]=di->seqNumber+1;
    } else {	
	di->seqNumber+=0x11;
	if (di->seqNumber>=0x88) di->seqNumber=0;
	target[1]=di->seqNumber;
    }	
    target[2]=(length);
    target[3]=0xff-(length);
//    crc=ccrc(target,length+4,startTab[length]);
    crc=ccrc(target,length+4);
    target[4+length]=crc % 256;
    target[5+length]=crc / 256;
    target[6+length]=0x7e;
    di->ifwrite(di, (char*)target, length+7);
    return 0;
}

int read1(daveInterface * di, uc* b) {
    int len,res;    
    if (daveDebug & daveDebugByte)
	LOG1("enter read1\n");
    len=0;
again:    
    res=di->ifread(di, (char*)b, 5);
    if (res==5) {
	if(b[4]==0x7e) goto again;    
	if(b[2]==255-b[3])  {
	    len=b[2]+7;
//	    LOG2("need length %d\n",len);
	    while (res<len) {
		res+=di->ifread(di, (char*)(b+res), len-res);
	    }
	}
    }
//    LOG3("need length %d got %d\n",len,res);
    if (daveDebug & daveDebugByte)
	_daveDump("got",b,res);
    return res;
}
/* 
    This initializes the MPI adapter. Step 7 version.
*/
int DECL2 _daveInitAdapterMPI3(daveInterface * di) 
{
    uc b2[]={0x7E,0xFC,0x9B,0xCD,0x7E};
    us adapter0330[]={0x01,0x03,0x20,'E','=','0','3','3','0'};
    uc v1[]={0x01,0x0C,0x02};
    
    uc b3[]={
	0x01,0x03,0x02,0x17, 0x00,0x9F,0x01,0x3C,
 	0x00,0x90,0x01,0x14, 0x00,	/* ^^^ MaxTsdr */
	0x00,0x5,
 	0x02,/* Bus speed */

	0x00,0x1F,0x05,0x01,0x01,0x03,0x80,/* from topserverdemo */
	/*^^ - Local mpi */
    };		
    uc m4[]={0x7e,0xca,0x2e,0x99,0x7e};
    uc b55[]={0x01,0x08,0x02};
    uc b1[daveMaxRawLen];
    
    int res,count;
    
    b3[16]=di->localMPI;
    if (di->speed==daveSpeed500k)
	b3[7]=0x64;
    if (di->speed==daveSpeed1500k)
	b3[7]=0x96;
    b3[15]=di->speed;
    count=0;
again:    
    count++;
    if (count>20) return -2;
    di->seqNumber=0x77;
    di->ifwrite(di, (char*)b2, sizeof(b2));
    res=di->ifread(di, (char*)b1, 5);
    if (res==0) {
	di->ifwrite(di, (char*)b2, sizeof(b2));
	res=di->ifread(di, (char*)b1, 5);
    }
    if (res==0) {
	di->ifwrite(di, (char*)b2,sizeof(b2));
	res=di->ifread(di, (char*)b1, 5);
    }	
    if (daveDebug & daveDebugByte)
	_daveDump("got", b1, res);
    if (res==5) {
	if (b1[1]==0xCE) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("ok, I begin sequence\n");
	    di->seqNumber=0x77;
	} else if (b1[1]==0xCA) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("refused.\n");
	    goto again;
//	    res=di->ifread(di, b1, 100);	//certainly nonsense after a jump
	} else if (b1[1]==0xF8) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("refused.\n");
	    di->ifwrite(di, (char*)m4, sizeof(m4));
	    res=di->ifread(di, (char*)b1, 100);
	    goto again;
	} else if (b1[1]==0x8a) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("in sequence. set to 0x11\n");
	    di->seqNumber=0x0;
	} else if (b1[1]==0x8b) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("in sequence. set to 0x22\n");
	    di->seqNumber=0x22;
	} else if (b1[1]==0x8c) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("in sequence. set to 0x33\n");
	    di->seqNumber=0x33;
	} else if (b1[1]==0x8d) {
	    if (daveDebug & daveDebugInitAdapter)
		LOG1("in sequence. set to 0x44\n");
	    di->seqNumber=0x44;
	}
    } else return -1;
    daveSendWithCRC3(di,b3,sizeof(b3));
    read1(di, b1);
    if (!_daveMemcmp(adapter0330, b1+4, sizeof(adapter0330)/2)) {
	if (daveDebug & daveDebugInitAdapter)
	    LOG2("%s initAdapter() found Adapter E=0330.\n", di->name);
	daveSendWithCRC3(di,v1,sizeof(v1));    
	read1(di, b1);
	return 0;
    }    
    daveSendWithCRC3(di,b55,sizeof(b55));
    read1(di, b1);
//    daveSendWithCRC3(di,b66,sizeof(b66));
//    read1(di, b1);
    return 0;
}

int DECL2 _daveSendWithPrefix32(daveConnection * dc, int size) {
    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
    fix[4]=dc->connectionNumber2;		// 1/10/05 trying Andrew's patch
    fix[5]=dc->connectionNumber;		// 1/10/05 trying Andrew's patch
    memcpy(dc->msgOut, fix, sizeof(fix));
    dc->msgOut[1]|=dc->MPIAdr;
    dc->msgOut[sizeof(fix)]=0xF1;
    return daveSendWithCRC3(dc->iface, dc->msgOut, size+sizeof(fix));
}

int DECL2 _daveListReachablePartnersMPI3(daveInterface * di,char * buf) {
    uc b1[daveMaxRawLen];
    uc m1[]={1,7,2};
    int res, len;
    daveSendWithCRC3(di,m1,sizeof(m1));
    res=read1(di, b1);
    if (daveDebug & daveDebugInitAdapter)
	LOG2("res:%d\n",res);
    if(140==res){
        memcpy(buf,b1+10,126);
	return 126;
    } else
	return 0;	
}   


/* 
    Open connection to a PLC. This assumes that dc is initialized by
    daveNewConnection and is not yet used.
    (or reused for the same PLC ?)
*/
int DECL2 _daveConnectPLCMPI3(daveConnection * dc) {
    int res, mpi, len;
    PDU p1;
    uc b1[daveMaxRawLen];
    uc * pcha;
    
    uc e18[]={0x04,0x82,0x00,0x0d,0x00,0x14,
	0xe0,0x04,0x00,0x80,0x00,0x02,
	    0x00,0x02,
	    0x01,
	    0x00,
	    0x01,0x00,
	    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	    };

//Step7
//7E 00 1F E0 
//04 86 00 0D 00 14   
//E0 04 00 80 00 02   
//01 
//0F  
// 01 00  
// 06 04 02  sizes
// AA BB 00 00 CC DD  
// C0 A8 02 BC  
// 01 03 8C 60 7E 	

    uc b5[]={	
	0x05,0x01,
    };

    mpi=dc->MPIAdr;
    e18[1]|=dc->MPIAdr;
    
    pcha=e18+16;
    len=18;
    if(dc->routing) {
	e18[12]=1;
	e18[13]=11+dc->routingData.PLCadrsize;
	*pcha=6; pcha++;
	*pcha=dc->routingData.PLCadrsize; pcha++;
	*pcha=2; pcha++;
	*pcha=(dc->routingData.subnetID1) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID1) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) % 0x100; pcha++;
	memcpy(pcha, dc->routingData.PLCadr, dc->routingData.PLCadrsize);
	pcha+=dc->routingData.PLCadrsize;
	len=27+dc->routingData.PLCadrsize;
    }
    *pcha=dc->communicationType; pcha++; 
    *pcha=0; pcha++; // rack+slot ?
    
    daveSendWithCRC3(dc->iface,e18, len);
    read1(dc->iface, b1);
    
    dc->connectionNumber2=b1[9];
    dc->connectionNumber=0x14;

    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 3.\n", dc->iface->name);	

    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 4.\n", dc->iface->name);	
    	
    _daveSendWithPrefix31(dc, b5, sizeof(b5));		
    read1(dc->iface, b1);
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);	
    res= _daveNegPDUlengthRequest(dc, &p1);
    return 0;
}

/* 
    This adds a prefix to a string and theen sends it
    after doubling DLEs in the String
    and adding DLE,ETX and bcc.
*/
int DECL2 _daveSendWithPrefix31(daveConnection * dc, uc *b, int size)
{
    uc target[daveMaxRawLen];
    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
    fix[4]=dc->connectionNumber2; 		// 1/10/05 trying Andrew's patch
    fix[5]=dc->connectionNumber; 		// 1/10/05 trying Andrew's patch
    memcpy(target,fix,sizeof(fix));
    memcpy(target+sizeof(fix),b,size);
    target[1]|=dc->MPIAdr;
    memcpy(target+sizeof(fix),b,size);
    return daveSendWithCRC3(dc->iface,target,size+sizeof(fix));
}

/*
    Executes part of the dialog necessary to send a message:
*/
int DECL2 _daveSendDialog3(daveConnection * dc, int size)
{
    if (size>5){
	dc->needAckNumber=dc->messageNumber;
	dc->msgOut[dc->iface->ackPos-dc->PDUstartI+dc->PDUstartO+1]=_daveIncMessageNumber(dc);
    }	
    _daveSendWithPrefix32(dc, size);
    return 0;
}

int DECL2 _daveSendMessageMPI3(daveConnection * dc, PDU * p) {
    if (daveDebug & daveDebugExchange) {
        LOG2("%s enter _daveSendMessageMPI3\n", dc->iface->name);	    
    }    
    if (_daveSendDialog3(dc, 2+p->hlen+p->plen+p->dlen)) {
	LOG2("%s *** _daveSendMessageMPI3 error in _daveSendDialog.\n",dc->iface->name);	    		
//	return -1;	
    }	
    if (daveDebug & daveDebugExchange) {
        LOG3("%s _daveSendMessageMPI send done. needAck %x\n", dc->iface->name,dc->needAckNumber);	    
    }	
    return 0;
}

/* 
    Sends an ackknowledge message for the message number nr:
*/
int DECL2 _daveSendAckMPI3(daveConnection * dc, int nr)
{
    uc m[3];
    if (daveDebug & daveDebugPacket)
	LOG3("%s sendAck for message %d \n", dc->iface->name,nr);
    m[0]=0xB0;
    m[1]=0x01;
    m[2]=nr;
    return _daveSendWithPrefix31(dc, m, 3);
}

//#define CRC
#ifdef CRC
int testcrc(unsigned char *b, int size, int start)
{
    unsigned short sum, s2;
    int i, j;
    unsigned char *b1 = b;
    sum = start;
    
    for (j = 0; j < size-2; j++) {
//	LOG2("I calc: %x.\n", sum);
	sum = sum ^ (b1[j]);
//	LOG2("after xor data: %x.\n", sum);
	s2 = sum;
	for (i = 0; i <= 7; i++) {
	    if (sum & 0x1) {
		sum = sum >> 1;
		sum = sum ^ 0x8408;
	    } else
		sum = sum >> 1;
//	    LOG2("loop: %x.\n", sum);
	}
    }
/*    
    if (
	((sum /256)==b[size-1]) &&
	((sum %256)==b[size-2])
    ) {
	printf ("found 1 %04x \n",start);
	return 1;
    }	
*/    
    if (
	((sum %256)==b[size-2]) &&
	((sum /256)==b[size-1])
    ){
	printf ("found 2 %04x %d\n",start, size-6);
	startTab[size-6]=start;
	return 1;
    }
    return 0;
}
#endif

int DECL2 _daveGetResponseMPI3(daveConnection *dc) {
    int res,count;
    if (daveDebug & daveDebugExchange)
	LOG1("enter _daveGetResponseMPI3\n");
    count=0;
    dc->msgIn[10]=0;
    do {
//	res=dc->iface->ifread(dc->iface, dc->msgIn, 400);
	res=read1(dc->iface, dc->msgIn);
	count++;
    }while((count<5) && (dc->msgIn[10]!=0xF1));
    if (dc->msgIn[10]==0xF1) {
	dc->iface->seqNumber=dc->msgIn[1];
	_daveSendAckMPI3(dc, dc->msgIn[dc->iface->ackPos+1]);        
#ifdef CRC	
	if (startTab[res-7]==0) {
	for(count=0;count<0xffff;count++)
	    testcrc(dc->msgIn,res-1, count);
	}    
#endif
	return 0;
    } 
    return -10;
}


int DECL2 _daveExchangeMPI3(daveConnection * dc, PDU * p) {
    _daveSendMessageMPI3(dc, p);
    dc->AnswLen=0;
    return _daveGetResponseMPI3(dc);
}

int DECL2 _daveDisconnectPLCMPI3(daveConnection * dc)
{
//    uc m[]={
//        0x80
//    };
    uc fix[]= {04,0x82,0x0,0x0C,0x03,0x14,0x80};
    fix[4]=dc->connectionNumber2; 		// 1/10/05 trying Andrew's patch
    fix[5]=dc->connectionNumber; 		// 1/10/05 trying Andrew's patch
//    _daveSendWithPrefix31(dc, m, 1);	
    fix[1]|=dc->MPIAdr;
    daveSendWithCRC3(dc->iface,fix,sizeof(fix));
    read1(dc->iface,dc->msgIn);
    return 0;
}    
/*
    It seems to be better to complete this subroutine even if answers
    from adapter are not as expected.
*/
int DECL2 _daveDisconnectAdapterMPI3(daveInterface * di) {
//    uc m3[]={
//        0x80
//    };
    uc m2[]={
        1,4,2
    };
    uc b[daveMaxRawLen];
//    uc m4[]={0x7e,0xca,0x2e,0x99,0x7e};
//    _daveSendWithPrefix31(di, m3, sizeof(m3));
//    read1(di,b);
    daveSendWithCRC3(di, m2, sizeof(m2));
    read1(di,b);
#ifdef CRC
    printf ("\n\n\n\nus startTab[]={");
    for (res=0;res<255;res++) {
	printf ("0x%04x , // %d\n",startTab[res],res);
    }
    printf ("}\n\n\n\n");
#endif    
//    di->ifwrite(di, m4, 5);
    return 0;	
}

/*
    It seems to be better to complete this subroutine even if answers
    from adapter are not as expected.
*/
int DECL2 _daveDisconnectAdapterMPI(daveInterface * di) {
    int res;
    uc m2[]={
        1,4,2
    };
    
    uc b1[daveMaxRawLen];
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s enter DisconnectAdapter()\n", di->name);	
    _daveSendSingle(di, STX);
    res=_daveReadMPI(di,b1);
/*    if ((res!=1)||(b1[0]!=DLE)) return -1; */
    _daveSendWithCRC(di, m2, sizeof(m2));		
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s daveDisconnectAdapter() step 1.\n", di->name);	
    res=_daveReadMPI(di, b1);
/*    if ((res!=1)||(b1[0]!=DLE)) return -2; */
    res=_daveReadMPI(di, b1);
/*    if ((res!=1)||(b1[0]!=STX)) return -3; */
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s daveDisconnectAdapter() step 2.\n", di->name);	
    _daveSendSingle(di, DLE);
    di->ifread(di, b1, daveMaxRawLen);
//    _daveReadChars(di, b1, tmo_normal, daveMaxRawLen);
    _daveSendSingle(di, DLE);
    if (daveDebug & daveDebugInitAdapter) 
	_daveDump("got",b1,10);
    return 0;	
}

/*
    This doesn't work yet. I'm not sure whether it is possible to get that
    list after having connected to a PLC.
*/
int DECL2 _daveListReachablePartnersMPI(daveInterface * di,char * buf) {
    uc b1[daveMaxRawLen];
    uc m1[]={1,7,2};
    int res;
    res=_daveInitStep(di, 1, m1, sizeof(m1),"listReachablePartners()");	
    if (res) return 0;
    res=_daveReadMPI(di,b1);
//    LOG2("res %d\n", res);	
    if(136==res){
	_daveSendSingle(di,DLE);
        memcpy(buf,b1+6,126);
	return 126;
    } else
	return 0;	
}   

int DECL2 _daveDisconnectPLCMPI(daveConnection * dc)
{
    int res;
    uc m[]={
        0x80
    };
    uc b1[daveMaxRawLen];
    
    _daveSendSingle(dc->iface, STX);
    
    res=_daveReadMPI(dc->iface,b1);
    if ((res!=1)||(b1[0]!=DLE)) {
	if (daveDebug & daveDebugPrintErrors)
    	    LOG2("%s *** no DLE before send.\n", dc->iface->name);	    
	return -1;
    }
    _daveSendWithPrefix(dc, m, 1);	
    
    res=_daveReadMPI(dc->iface,b1);
    if ((res!=1)||(b1[0]!=DLE)) {
	if (daveDebug & daveDebugPrintErrors)
	    LOG2("%s *** no DLE after send.\n", dc->iface->name);	    
	return -2;
    }    
    
    _daveSendSingle(dc->iface, DLE);
    
    res=_daveReadMPI(dc->iface,b1);
    if ((res!=1)||(b1[0]!=STX)) return 6;
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveDisConnectPLC() step 6.\n", dc->iface->name);	
    res=_daveReadMPI(dc->iface,b1);
    if (daveDebug & daveDebugConnect) 
	_daveDump("got",b1,10);
    _daveSendSingle(dc->iface, DLE);
    return 0;
}    

/*
    build the PDU for a PDU length negotiation    
*/
int DECL2 _daveNegPDUlengthRequest(daveConnection * dc, PDU *p) {
    uc pa[]=	{0xF0, 0 ,0, 1, 0, 1, 
    dc->maxPDUlength / 0x100, //3, 		
    dc->maxPDUlength % 0x100, //0xC0,
    };
    int res;
    int CpuPduLimit;
    PDU p2;
    p->header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(p);
    }	
    res=_daveExchange(dc, p);
    if(res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc, &p2);
    if(res!=daveResOK) return res;
    CpuPduLimit=daveGetU16from(p2.param+6);
    if (dc->maxPDUlength > CpuPduLimit) dc->maxPDUlength = CpuPduLimit; // use lower number as limit
    if (daveDebug & daveDebugConnect) {
	LOG3("\n*** Partner offered PDU length: %d used limit %d\n\n",CpuPduLimit,dc->maxPDUlength);
    }	
    return res;
}    

/* 
    Open connection to a PLC. This assumes that dc is initialized by
    daveNewConnection and is not yet used.
    (or reused for the same PLC ?)
*/
int DECL2 _daveConnectPLCMPI2(daveConnection * dc) {
    int res;
    PDU p1;
    uc b1[daveMaxRawLen];
    
    uc b4[]={
	0x00,0x0d,0x00,0x03,0xe0,0x04,0x00,0x80,
	0x00,0x02,0x01,0x06,
	0x01,
	0x00,
	0x00,0x01,
	0x02,0x03,0x01,0x00
           /*^^ MPI ADDR */
	};

    us t4[]={
	0x00,0x0c,0x103,0x103,0xd0,0x04,0x00,0x80,
	0x01,0x06,
	0x00,0x02,0x00,0x01,0x02,
	0x03,0x01,0x00,
	0x01,0x00,0x10,0x03,0x4d
    };
    uc b5[]={	
	0x05,0x01,
    };
    
    us t5[]={    
	0x00,
	0x0c,
	0x103,0x103,0x05,0x01,0x10,0x03,0x1b
    };

    b4[3]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
    b4[sizeof(b4)-3]=dc->MPIAdr;	
    t4[15]=dc->MPIAdr;
    t4[sizeof(t4)/2-1]^=dc->MPIAdr; /* 'patch' the checksum	*/

    _daveInitStep(dc->iface, 1, b4, sizeof(b4),"connectPLC(2)");    
    res=_daveReadMPI2(dc->iface,b1);
    if (_daveMemcmp(t4, b1, res)) {
	LOG2("%s daveConnectPLC() step 3 ends with 3.\n", dc->iface->name);	
	return 3;
    }	
    dc->connectionNumber2=b1[3]; // 1/10/05 trying Andrew's patch

    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 4.\n", dc->iface->name);	
    res=_daveReadMPI(dc->iface,b1);
    if ((res!=1)||(b1[0]!=DLE)) {
	LOG2("%s daveConnectPLC() step 4 ends with 4.\n", dc->iface->name);	
	return 4;
    }	

    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 5.\n", dc->iface->name);	
    _daveSendWithPrefix(dc, b5, sizeof(b5));		
    res=_daveReadMPI(dc->iface,b1);
    if ((res!=1)||(b1[0]!=DLE)) return 5;
    res=_daveReadMPI(dc->iface,b1);
    if ((res!=1)||(b1[0]!=STX)) return 5;
    
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);	
    _daveSendSingle(dc->iface, DLE);
    
    res=_daveReadMPI(dc->iface,b1);
    _daveSendSingle(dc->iface, DLE);
    if (dc->iface->protocol==daveProtoMPI4) _daveSendSingle(dc->iface, STX);
    if (_daveMemcmp(t5, b1, res)) return 6;
    
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);	
    res= _daveNegPDUlengthRequest(dc, &p1);
    return 0;
}

/* 
    Open connection to a PLC. This assumes that dc is initialized by
    daveNewConnection and is not yet used.
    (or reused for the same PLC ?)
*/
int DECL2 _daveConnectPLCMPI1(daveConnection * dc) {
    int res, len;
    PDU p1;
    uc * pcha;
    
    uc b4[]={
	0x04,0x80,0x80,0x0D,0x00,0x14,
	0xE0,0x04,0x00,0x80,0x00,0x02,
	0x00,
	0x02,
	0x01,0x00,
	0x01,0x00,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    };

	//7E 11 1F E0 04 82 00 0D 00 14   
	//E0 04 00 80 00 02   
	//01 0F  
	//01 00  
	//06 04 02  AA BB 00 00 CC DD  C0 A8 02 BC  01 03 18 87 7E 	
	//Step7//7E 00 1F E0 04 86 00 0D 00 14   E0 04 00 80 00 02   01 0F  01 00  06 04 02  AA BB 00 00 CC DD  C0 A8 02 BC  01 03 8C 60 7E 	

    us t4[]={
	0x04,0x80,0x180,0x0C,0x114,0x103,0xD0,0x04,	// 1/10/05 trying Andrew's patch
	0x00,0x80,
	0x00,0x02,0x00,0x02,0x01,
	0x00,0x01,0x00,
    };

    uc b5[]={	
	0x05,0x01,
    };
    us t5[]={    
	0x04,
	0x80,
	0x180,0x0C,0x114,0x103,0x05,0x01,
    };
    b4[1]|=dc->MPIAdr;	
    b4[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
    
//    b4R[1]|=dc->MPIAdr;	
//    b4R[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
    
    t4[1]|=dc->MPIAdr;	
    t5[1]|=dc->MPIAdr;	

    pcha=b4+16;
    len=18;
    if(dc->routing) {
	b4[12]=1;
	b4[13]=11+dc->routingData.PLCadrsize;
	*pcha=6; pcha++;
	*pcha=dc->routingData.PLCadrsize; pcha++;
	*pcha=2; pcha++;
	*pcha=(dc->routingData.subnetID1) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID1) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) % 0x100; pcha++;
	memcpy(pcha, dc->routingData.PLCadr, dc->routingData.PLCadrsize);
	pcha+=dc->routingData.PLCadrsize;
	*pcha=1; pcha++;
	*pcha=0; pcha++;
	len=27+dc->routingData.PLCadrsize;
    }
    _daveInitStep(dc->iface, 1, b4, len,"connectPLC(1)");
    
    
    res= _daveReadMPI2(dc->iface,dc->msgIn);

    if (_daveMemcmp(t4, dc->msgIn, 10)) return 3;

    dc->connectionNumber2=dc->msgIn[5]; // 1/10/05 trying Andrew's patch
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC(1) step 4.\n", dc->iface->name);
    
    if (_daveReadSingle(dc->iface)!=DLE) return 4;
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 5.\n", dc->iface->name);	
    _daveSendWithPrefix(dc, b5, sizeof(b5));		
    if (_daveReadSingle(dc->iface)!=DLE) return 5;
    if (_daveReadSingle(dc->iface)!=STX) return 5;
        
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);	
    _daveSendSingle(dc->iface, DLE);
    res= _daveReadMPI2(dc->iface,dc->msgIn);
    if (_daveMemcmp(t5, dc->msgIn, sizeof(t5)/2)) return 6;
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);	
    res= _daveNegPDUlengthRequest(dc, &p1);
    return 0;
}

/*
    Protocol specific functions for ISO over TCP:
*/
#ifdef HAVE_SELECT
int DECL2 _daveReadOne(daveInterface * di, uc *b) {
	fd_set FDS;
	struct timeval t;
	FD_ZERO(&FDS);
	FD_SET(di->fd.rfd, &FDS);
	
	t.tv_sec = di->timeout / 1000000;
	t.tv_usec = di->timeout % 1000000;
/*	if (daveDebug & daveDebugByte) 
	    LOG2("timeout %d\n",di->timeout); */
	if (select(di->fd.rfd + 1, &FDS, NULL, NULL, &t) <= 0) 
	{
	    if (daveDebug & daveDebugByte) LOG1("timeout in readOne.\n");
	    return (0);
	} else {
	    return read(di->fd.rfd, b, 1);
	} 
};
#endif

#ifdef BCCWIN
int DECL2 _daveReadOne(daveInterface * di, uc *b) {
    unsigned long i;
    char res;
    ReadFile(di->fd.rfd, b, 1, &i,NULL);
    return i;
}
#endif 

/*
    Universal receive with timeout:
*/
int DECL2 _daveTimedRecv(daveInterface * di, uc *b, int len){
    fd_set FDS;
    struct timeval t;
    FD_ZERO(&FDS);
#ifdef BCCWIN
    FD_SET((SOCKET)(di->fd.rfd), &FDS);
#endif
#ifdef LINUX
    FD_SET(di->fd.rfd, &FDS);
#endif
    t.tv_sec = di->timeout / 1000000;
    t.tv_usec = di->timeout % 1000000;
#ifdef BCCWIN
    if (select(1, &FDS, NULL, NULL, &t) <= 0) {
        if (daveDebug & daveDebugByte) LOG1("timeout in TCP read.\n");
	    return 0;
    } else {
	return recv((SOCKET)(di->fd.rfd), b, len, 0);
    }
#endif

#ifdef LINUX
    if (select(di->fd.rfd+1, &FDS, NULL, NULL, &t) <= 0) {
        if (daveDebug & daveDebugByte) LOG1("timeout in TCP read.\n");
	    return 0;
    } else {
	return read(di->fd.rfd, b, len);
    }
#endif
}

int DECL2 _daveReadIBHPacket2(daveInterface * di,uc *b) {
    int res, len;
    res=_daveTimedRecv(di, b, 3);
    if (res<3) {
        if (daveDebug & daveDebugByte) {
	    LOG2("res %d ",res);
	    _daveDump("readIBHpacket2: short packet", b, res);
        }
        return (0); /* short packet */
    }
    len=b[2]+8;
    res+=_daveTimedRecv(di, b+3, len-3);
    if (daveDebug & daveDebugByte) {
        LOG3("readIBHpacket2: %d bytes read, %d needed\n",res, len);
        _daveDump("readIBHpacket2: packet", b, res);
    }
//    _daveDump("readIBHpacket2: packet", b, 8);
    return res;
}



uc IBHfollow[]={
	0,0,7,0xb,
	0,0,0x82,0,
	0,0,0,0,
	2,5,1
    };


int DECL2 _daveReadIBHPacket(daveInterface * di,uc *b) {
    int res,res2,len2;
    uc b2[300];
    res= _daveReadIBHPacket2(di, b);

    if ((res>15) && (b[15]==0xf0)) {
again:	    
//	    LOG1("FOLLOW UP\n");
	    
	    IBHfollow[0]=b[1];
	    IBHfollow[1]=b[0];
	    IBHfollow[8]=b[8];
	    IBHfollow[9]=b[9];
	    IBHfollow[10]=b[10];
	    IBHfollow[11]=b[11];
	
//	    _daveDump("IBHfollow", IBHfollow, 15);

	    res2=send((unsigned int)(di->fd.wfd), IBHfollow, 15, 0);

//	    LOG2("send: res2:%d\n",res2);

	    res2= _daveReadIBHPacket2(di, b2);
//	    LOG2("read: res2:%d\n",res2);
	    res2= _daveReadIBHPacket2(di, b2);
//	    LOG2("read: res2:%d\n",res2);

//	    if ((res>15) && (b[15]==0xf0)) 
	    memcpy(b+res,b2+17,res2-17);
	    b[16]=b2[16];
	    res+=res2-17;
	    b[15]=0xf1;
//	    LOG2("new b2[15]: %d\n",b2[15]);
	    if ((res>15) && (b2[15]==0xf0)) goto again;
	}

	if (daveDebug & daveDebugByte) {
	    LOG2("readIBHpacket: %d bytes read\n",res);
//	    _daveDump("readIBHpacket: packet", b, res);
	}

	return (res);
}

/*
    Read one complete packet. 
*/
int DECL2 _daveReadISOPacket(daveInterface * di,uc *b) {
	int res,i,length, follow;
	uc lhdr[7];
	i=_daveTimedRecv(di, b, 4);
	if (i<0) return 0;
	res=i;
        if (res<4) {
	    if (daveDebug & daveDebugByte) {
		LOG2("res %d ",res);
		_daveDump("readISOpacket: short packet", b, res);
	    }
	    return (0); /* short packet */
	}
	length=b[3]+0x100*b[2];
	i=_daveTimedRecv(di, b+4, length-4);
	res+=i;
	if (daveDebug & daveDebugByte) {
	    LOG3("readISOpacket: %d bytes read, %d needed\n",res, length);
	    _daveDump("readISOpacket: packet", b, res);
	}
	follow=((b[5]==0xf0)&& ((b[6] & 0x80)==0) );
	while (follow) {
	    if (daveDebug & daveDebugByte) {
		LOG2("readISOpacket: more data follows %d\n",b[6]);
	    }
	    i=_daveTimedRecv(di, lhdr, 7);
	    length=lhdr[3]+0x100*lhdr[2];
	    if (daveDebug & daveDebugByte) {
		_daveDump("readISOpacket: follow %d %d", lhdr, i);
	    }
	    i=_daveTimedRecv(di, b+res, length-7);
	    if (daveDebug & daveDebugByte) {
		_daveDump("readISOpacket: follow %d %d", b+res, i);
	    }
	    res+=i;
	    follow=((lhdr[5]==0xf0)&& ((lhdr[6] & 0x80)==0) );
	}
	return (res);
}


int DECL2 _daveSendISOPacket(daveConnection * dc, int size) {
    unsigned long i;
    int res;
    size+=4;
    *(dc->msgOut+dc->partPos+3)=size % 0x100;	//was %0xFF, certainly a bug
    *(dc->msgOut+dc->partPos+2)=size / 0x100;
    *(dc->msgOut+dc->partPos+1)=0;
    *(dc->msgOut+dc->partPos+0)=3;
    if (daveDebug & daveDebugByte)
	_daveDump("send packet: ",dc->msgOut+dc->partPos,size);
#ifdef HAVE_SELECT
    daveWriteFile(dc->iface->fd.wfd, dc->msgOut+dc->partPos, size, i);
#endif
#ifdef BCCWIN
    res = send((SOCKET)(dc->iface->fd.wfd), dc->msgOut+dc->partPos, size, 0);
    if (res==SOCKET_ERROR )
	if (daveDebug & daveDebugPrintErrors) LOG2("_daveSendISOPacket WSAGetLastError: %d \n",WSAGetLastError());
    
#endif
    return 0;
}


#define ISOTCPminPacketLength 16
int DECL2 _daveGetResponseISO_TCP(daveConnection * dc) {
    int res;
    res=_daveReadISOPacket(dc->iface,dc->msgIn);
    if(res==7) {
	if (daveDebug & daveDebugByte) 
	    LOG1("CPU sends funny 7 byte packets.\n");
	res=_daveReadISOPacket(dc->iface,dc->msgIn);
    }
    if (res==0) return daveResTimeout; 
    if (res<ISOTCPminPacketLength) return  daveResShortPacket; 
    return 0;
}
/*
    Executes the dialog around one message:
*/
int DECL2 _daveExchangeTCP(daveConnection * dc, PDU * p) {
    int res, totLen, sLen;

    if (daveDebug & daveDebugExchange) {
        LOG2("%s enter _daveExchangeTCP\n", dc->iface->name);
    }

//    _daveSendISOPacket(dc,3+p->hlen+p->plen+p->dlen);

    dc->partPos=0;
    totLen=p->hlen+p->plen+p->dlen;
    while(totLen) {
        if (totLen>dc->TPDUsize) {
	    sLen=dc->TPDUsize;
	    *(dc->msgOut+dc->partPos+6)=0x00;
	} else {
	    sLen=totLen;
	    *(dc->msgOut+dc->partPos+6)=0x80;
	}
	*(dc->msgOut+dc->partPos+5)=0xf0;
	*(dc->msgOut+dc->partPos+4)=0x02;
	_daveSendISOPacket(dc,3+sLen);
	totLen-=sLen;
	dc->partPos+=sLen;
    }

    res=_daveReadISOPacket(dc->iface,dc->msgIn);
    if(res==7) {
	if (daveDebug & daveDebugByte)
	    LOG1("CPU sends funny 7 byte packets.\n");
	res=_daveReadISOPacket(dc->iface,dc->msgIn);
    }
    if (daveDebug & daveDebugExchange) {
        LOG3("%s _daveExchangeTCP res from read %d\n", dc->iface->name,res);
    }
    if (res==0) return daveResTimeout;
    if (res<=ISOTCPminPacketLength) return  daveResShortPacket; 
    return 0;
}

int DECL2 _daveConnectPLCTCP(daveConnection * dc) {
    int res, success, retries, i, px;
    uc b4[]={
	0x11,0xE0,0x00,
	0x00,0x00,0x01,0x00,
	0xC1,2,1,0,
	0xC2,2,
	dc->communicationType,
	(dc->slot | dc->rack<<5), // hope I got it right this time...
	0xC0,1,0x9,
    };

    uc b4R2[]={			// for routing
	6+30+30+3,		// Length over all without this byte (6 byte fixed data, 30 bytes source TSAP (C1), 30 bytes dest TSAP (C2), 3 bytes TPDU size (C0))

	0xE0,		// TDPU Type CR = Connection Request (see RFC1006/ISO8073)
	0x00,0x00,	// TPDU Destination Reference (unknown)
	0x00,0x01,	// TPDU Source-Reference (my own reference, should not be zero)

	0x00,		// TPDU Class 0 and no Option

	0xC1,		// Parameter Source-TSAP
	28,		// Length of this parameter
	1,		// one block of data (???)
	0,		// Length for S7-Subnet-ID
	0,		// Length of PLC-Number
	2,		// Length of Function/Rack/Slot
	0,0,0,0, 0,0,0,0,	// empty Data 
	0,0,0,0, 0,0,0,0,
	0,0,0,0, 0,0,
	1,		// Function (1=PG,2=OP,3=Step7Basic)
	0,		// Rack (Bit 7-5) and Slot (Bit 4-0)

	0xC2,		// Parameter Destination-TSAP
	28,		// Length of this parameter 
	1,		// one block of data (???)

	6,		// Length for S7-Subnet-ID
	1,		// Length of PLC address
	2,		// Length of Function/Rack/Slot

	0x01,0x52,		// first part of S7-Subnet-ID  (look into the S7Project/Network configuration)
	0x00,0x00,		// fix always 0000 (reserved for later use ?)
	0x00,0x13,		// second part of S7-Subnet-ID 
				// (see S7Project/Network configuration)

	0x01,			// PLC address

	0,0,0,0,0,0,0,0,	// empty 
	0,0,0,0,0,0,0,

	dc->communicationType,		// Function (1=PG,2=OP,3=Step7Basic)
	(dc->slot | dc->rack<<5),	// Rack (Bit 7-5) and Slot (Bit 4-0) hope I got it right this time...

	0xC0,		// Parameter requested TPDU-Size
	1,		// Length of this parameter 
	0x9,		// requested TPDU-Size 8=256 Bytes, 9=512, a=1024 Bytes 
    };

    uc b243[]={
	0x11,0xE0,0x00,
	0x00,0x00,0x01,0x00,
	0xC1,2,'M','W',
	0xC2,2,'M','W',
	0xC0,1,9,
    };
    
    PDU p1;	
    success=0;
    retries=0;
    dc->partPos=0;
    
    if (dc->iface->protocol==daveProtoISOTCP243) {
        memcpy(dc->msgOut+4, b243, sizeof(b243));	
    } else if (dc->iface->protocol==daveProtoISOTCP) {
        if(!dc->routing) {
	    memcpy(dc->msgOut+4, b4, sizeof(b4));
	    dc->msgOut[17]=dc->communicationType;  // (1=PG Communication,2=OP Communication,3=Step7Basic Communication)
	    dc->msgOut[18]=dc->slot | dc->rack<<5; // hope I got it right this time...
	} else {
	    if (daveDebug & daveDebugConnect) {
		_daveDump("routing data 1: ", (char*)&(dc->routingData), 30);
	    }
	    b4R2[41]=dc->routingData.PLCadrsize;

	    px=43;
	    b4R2[px]=(dc->routingData.subnetID1) / 0x100;
	    b4R2[px+1]=(dc->routingData.subnetID1) % 0x100;
	    b4R2[px+2]=(dc->routingData.subnetID2) / 0x100;
	    b4R2[px+3]=(dc->routingData.subnetID2) % 0x100;
	    b4R2[px+4]=(dc->routingData.subnetID3) / 0x100;
	    b4R2[px+5]=(dc->routingData.subnetID3) % 0x100;
	    
	    memcpy(b4R2+49, dc->routingData.PLCadr, dc->routingData.PLCadrsize);
	    
	    memcpy(dc->msgOut+4, b4R2, sizeof(b4R2));	// with routing over MPI
	    
//	    dc->msgOut[17]=dc->rack+1;			// this is probably wrong
//	    dc->msgOut[18]=dc->slot;
	}
    }

    _daveSendISOPacket(dc, dc->msgOut[4]+1);
    do {
	res=_daveReadISOPacket(dc->iface,dc->msgIn);
        if (daveDebug & daveDebugConnect) {
	    LOG2("%s daveConnectPLC() step 1. ", dc->iface->name);	
	    _daveDump("got packet: ", dc->msgIn, res);
	}
	if ((res==22 && !dc->routing) || (res==48 && dc->routing)|| (res==74 && dc->routing)) {
	    success=1;
	    for (i=6;i<res;i++) {
		if (dc->msgIn[i]==0xc0) {
		    dc->TPDUsize=128 << (dc->msgIn[i+2]-7);
		    if (daveDebug & daveDebugConnect) {
			LOG3("TPDU len %d = %d\n",dc->msgIn[i+2],dc->TPDUsize);
		    }
		}
	    }
	} else {
	    if (daveDebug & daveDebugPrintErrors){
		LOG2("%s error in daveConnectPLC() step 1. retrying...", dc->iface->name);	
	    }	
	}
	retries++;
    } while ((success==0)&&(retries<3));
    if (success==0) return -1;
    
    retries=0;
    do {
	res= _daveNegPDUlengthRequest(dc, &p1);
	if (res==0) {
	    return res;
	} else {
	    if (daveDebug & daveDebugPrintErrors){
		LOG2("%s error in daveConnectPLC() step 1. retrying...\n", dc->iface->name);	
	    }	
	}
	retries++;
    } while (retries<3);	
    return -1;
}

/*
    Changes: 
    07/19/04 removed unused vars.
*/

/*
    Changes: 
    07/19/04 added return values in daveInitStep and daveSendWithPrefix2.
    09/09/04 applied patch for variable Profibus speed from Andrew Rostovtsew.
*/

/* PPI specific functions: */
#define tmo_normalPPI 140000

void DECL2 _daveSendLength(daveInterface * di, int len) {
    uc c[]={104,0,0,104};
    c[1]=len;
    c[2]=len;
    di->ifwrite(di, (char *)c, 4);
    if ((daveDebug & daveDebugByte)!=0) {
	_daveDump("I send", c, 4);
    }	
}

void DECL2 _daveSendIt(daveInterface * di, uc * b, int size) {
    int i;
    us sum = 0;
    for (i=0;i<size;i++) {
	sum+=b[i];
    }
    sum=sum & 0xff;
    b[size]=sum;
    size++;
    b[size]=SYN;
    size++;
    di->ifwrite(di, (char*)b, size);
    
    if ((daveDebug & daveDebugByte)!=0) {
	LOG2("send %d\n",i);
	_daveDump("I send", b, size);
    }	
}

void DECL2 _daveSendRequestData(daveConnection * dc,int alt) {
    uc b[]={DLE,0,0,0x5C,0,0};
    b[1]=dc->MPIAdr;
    b[2]=dc->iface->localMPI;
    if(alt) b[3]=0x7c; else b[3]=0x5c;
    dc->iface->ifwrite(dc->iface, (char*)b, 1);  //cs: 
    _daveSendIt(dc->iface, b+1, sizeof(b)-3);
}


int seconds, thirds;

int DECL2 _daveGetResponsePPI(daveConnection *dc) {
    int res, expectedLen, expectingLength, i, sum, alt;
    uc * b;
    res = 0;
    expectedLen=6;
    expectingLength=1;
    b=dc->msgIn;
    alt=1;
    while ((expectingLength)||(res<expectedLen)) {
	i = dc->iface->ifread(dc->iface, dc->msgIn+res, 1);
	res += i;
	if ((daveDebug & daveDebugByte)!=0) {
	    LOG3("i:%d res:%d\n",i,res);
	    FLUSH;
	}        
	if (i == 0) {
		    return daveResTimeout;
	} else {
	    if ( (expectingLength) && (res==1) && (b[0] == 0xE5)) {
		if(alt) {
		    _daveSendRequestData(dc,alt);
		    res=0;
		    alt=0;
		} else {
		    _daveSendRequestData(dc,alt);
		    res=0;
		    alt=1;
		}
	    }
	    if ( (expectingLength) && (res>=4) && (b[0] == b[3]) && (b[1] == b[2]) ) {
	        expectedLen=b[1]+6;
	        expectingLength=0;
	    }
	}	
    }
    if ((daveDebug & daveDebugByte)!=0) {
	LOG2("res %d testing lastChar\n",res);
    }	
    if (b[res-1]!=SYN) {
    	LOG1("block format error\n");
    	return 1024;
    }
    if ((daveDebug & daveDebugByte)!=0) {
	LOG1("testing check sum\n");
    }	
    sum=0;
    for (i=4; i<res-2; i++){
        sum+=b[i];
    }
    sum=sum&0xff;
    if ((daveDebug & daveDebugByte)!=0) {
	LOG3("I calc: %x sent: %x\n", sum, b[res-2]);
    }	
    if (b[res-2]!=sum) {
	if ((daveDebug & daveDebugByte)!=0) {
    	    LOG1("checksum error\n");
	}	
        return 2048;
    }
    return 0;
} 

int DECL2 _daveExchangePPI(daveConnection * dc,PDU * p1) {
    int i,res=0,len;
    dc->msgOut[0]=dc->MPIAdr;	/* address ? */
    dc->msgOut[1]=dc->iface->localMPI;	
    dc->msgOut[2]=108;	
    len=3+p1->hlen+p1->plen+p1->dlen;	/* The 3 fix bytes + all parts of PDU */
    _daveSendLength(dc->iface, len);			
    _daveSendIt(dc->iface, dc->msgOut, len);
    i = dc->iface->ifread(dc->iface, dc->msgIn+res, 1);
    if ((daveDebug & daveDebugByte)!=0) {
	LOG3("i:%d res:%d\n",i,res);
	_daveDump("got",dc->msgIn,i); // 5.1.2004
    }	
    if (i == 0) {
	seconds++;
	_daveSendLength(dc->iface, len);			
	_daveSendIt(dc->iface, dc->msgOut, len);
	i = dc->iface->ifread(dc->iface, dc->msgIn+res, 1);
	if (i == 0) {
	    thirds++;
	    _daveSendLength(dc->iface, len);			
	    _daveSendIt(dc->iface, dc->msgOut, len);
	    i = dc->iface->ifread(dc->iface, dc->msgIn+res, 1);
	    if (i == 0) {
		LOG1("timeout in _daveExchangePPI!\n");
		FLUSH;
    		return daveResTimeout;
	    }	
	}    
    }
    _daveSendRequestData(dc,0); 
    return _daveGetResponsePPI(dc);
}    

int DECL2 _daveConnectPLCPPI(daveConnection * dc) {
    PDU p;
    return _daveNegPDUlengthRequest(dc,&p);
} 

/* 
    "generic" functions calling the protocol specific ones (or the dummies)
*/
int DECL2 daveInitAdapter(daveInterface * di) {
    return di->initAdapter(di);
}

int DECL2 daveConnectPLC(daveConnection * dc) {
    return dc->iface->connectPLC(dc);
}

int DECL2 daveDisconnectPLC(daveConnection * dc) {
    return dc->iface->disconnectPLC(dc);
}

int DECL2 daveDisconnectAdapter(daveInterface * di) {
    return di->disconnectAdapter(di);
}

int DECL2 _daveExchange(daveConnection * dc, PDU *p) {
    int res;
    if ((p->header[4]==0)&&(p->header[5]==0)) { /* do not number already numbered PDUs 12/10/04 */
        dc->PDUnumber++;
        if (daveDebug & daveDebugExchange) {
		LOG2("_daveExchange PDU number: %d\n", dc->PDUnumber);
        }
        p->header[5]=dc->PDUnumber % 256;	// test!
        p->header[4]=dc->PDUnumber / 256;	// test!
    }
    res=dc->iface->exchange(dc, p);
    if (((daveDebug & daveDebugExchange)!=0) ||((daveDebug & daveDebugErrorReporting)!=0)) {
	LOG2("result of exchange: %d\n",res);
    }	
    return res;
}

int DECL2 daveSendMessage(daveConnection * dc, PDU *p) {
    return dc->iface->sendMessage(dc, p);
}

int DECL2 daveListReachablePartners(daveInterface * di, char * buf) {
    return di->listReachablePartners(di, buf);
}

int DECL2 daveGetResponse(daveConnection * dc) {
    return dc->iface->getResponse(dc);
}

/**
    Newer conversion routines. As the terms WORD, INT, INTEGER etc have different meanings
    for users of different programming languages and compilers, I choose to provide a new 
    set of conversion routines named according to the bit length of the value used. The 'U'
    or 'S' stands for unsigned or signed.
**/
/*
    Get a value from the position b points to. B is typically a pointer to a buffer that has
    been filled with daveReadBytes:
*/
int DECL2 daveGetS8from(uc *b) {
    char* p=(char*)b;
    return *p;
}

int DECL2 daveGetU8from(uc *b) {
    return *b;
}

int DECL2 daveGetS16from(uc *b) {
    union {
	short a;
	uc b[2];
    } u;
#ifdef DAVE_LITTLE_ENDIAN    
    u.b[1]=*b;
    b++;
    u.b[0]=*b;
#else
    u.b[0]=*b;
    b++;
    u.b[1]=*b;
#endif
    return u.a;
}

int DECL2 daveGetU16from(uc *b) {
    union {
	unsigned short a;
	uc b[2];
    } u;
#ifdef DAVE_LITTLE_ENDIAN    
    u.b[1]=*b;
    b++;
    u.b[0]=*b;
#else
    u.b[0]=*b;
    b++;
    u.b[1]=*b;
#endif    
    return u.a;
}

int DECL2 daveGetS32from(uc *b) {
    union {
	int a;
	uc b[4];
    } u;
#ifdef DAVE_LITTLE_ENDIAN
    u.b[3]=*b;
    b++;
    u.b[2]=*b;
    b++;
    u.b[1]=*b;
    b++;
    u.b[0]=*b;
#else
    u.b[0]=*b;
    b++;
    u.b[1]=*b;
    b++;
    u.b[2]=*b;
    b++;
    u.b[3]=*b;
#endif
    return u.a;
}

unsigned int DECL2 daveGetU32from(uc *b) {
    union {
	unsigned int a;
	uc b[4];
    } u;
#ifdef DAVE_LITTLE_ENDIAN
    u.b[3]=*b;
    b++;
    u.b[2]=*b;
    b++;
    u.b[1]=*b;
    b++;
    u.b[0]=*b;
#else
    u.b[0]=*b;
    b++;
    u.b[1]=*b;
    b++;
    u.b[2]=*b;
    b++;
    u.b[3]=*b;
#endif    
    return u.a;
}

float DECL2 daveGetFloatfrom(uc *b) {
    union {
	float a;
	uc b[4];
    } u;
#ifdef DAVE_LITTLE_ENDIAN
    u.b[3]=*b;
    b++;
    u.b[2]=*b;
    b++;
    u.b[1]=*b;
    b++;
    u.b[0]=*b;
#else
    u.b[0]=*b;
    b++;
    u.b[1]=*b;
    b++;
    u.b[2]=*b;
    b++;
    u.b[3]=*b;
#endif    
    return u.a;
}

/*
    Get a value from the current position in the last result read on the connection dc.
    This will increment an internal pointer, so the next value is read from the position
    following this value.
*/
int DECL2 daveGetS8(daveConnection * dc) {
    char * p;
#ifdef DEBUG_CALLS
    LOG2("daveGetS8(dc:%p)\n",dc);
    FLUSH;
#endif	
    p=(char *) dc->resultPointer;
    dc->resultPointer++;
    return *p;
}

int DECL2 daveGetU8(daveConnection * dc) {
    uc * p;
#ifdef DEBUG_CALLS
    LOG2("daveGetU8(dc:%p)\n",dc);
    FLUSH;
#endif	
    p=dc->resultPointer;
    dc->resultPointer++;
    return *p;
}    

int DECL2 daveGetS16(daveConnection * dc) {
    union {
	short a;
	uc b[2];
    } u;
#ifdef DEBUG_CALLS
    LOG2("daveGetS16(dc:%p)\n",dc);
    FLUSH;
#endif	
#ifdef DAVE_LITTLE_ENDIAN
    u.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[0]=*(dc->resultPointer);
#else
    u.b[0]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[1]=*(dc->resultPointer);
#endif
    dc->resultPointer++;
    return u.a;
}

int DECL2 daveGetU16(daveConnection * dc) {
    union {
	unsigned short a;
	uc b[2];
    } u;
#ifdef DEBUG_CALLS
    LOG2("daveGetU16(dc:%p)\n",dc);
    FLUSH;
#endif	    
#ifdef DAVE_LITTLE_ENDIAN
    u.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[0]=*(dc->resultPointer);
#else
    u.b[0]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[1]=*(dc->resultPointer);
#endif    
    dc->resultPointer++;
    return u.a;
}

int DECL2 daveGetS32(daveConnection * dc) {
    union {
	int a;
	uc b[4];
    } u;
#ifdef DEBUG_CALLS
    LOG2("daveGetS32(dc:%p)\n",dc);
    FLUSH;
#endif	    
#ifdef DAVE_LITTLE_ENDIAN
    u.b[3]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[2]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[0]=*(dc->resultPointer);
#else
    u.b[0]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[2]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[3]=*(dc->resultPointer);
#endif
    dc->resultPointer++;
    return u.a;
}

unsigned int DECL2 daveGetU32(daveConnection * dc) {
    union {
	unsigned int a;
	uc b[4];
    } u;
#ifdef DEBUG_CALLS
    LOG2("daveGetU32(dc:%p)\n",dc);
    FLUSH;
#endif	    
    
#ifdef DAVE_LITTLE_ENDIAN
    u.b[3]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[2]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[0]=*(dc->resultPointer);
#else
    u.b[0]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[1]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[2]=*(dc->resultPointer);
    dc->resultPointer++;
    u.b[3]=*(dc->resultPointer);
#endif    
    dc->resultPointer++;
    return u.a;
}
/*
    Get a value from a given position in the last result read on the connection dc.
*/
int DECL2 daveGetS8At(daveConnection * dc, int pos) {
    char * p=(char *)(dc->_resultPointer);
    p+=pos;
    return *p;
}

int DECL2 daveGetU8At(daveConnection * dc, int pos)  {
    uc * p=(uc *)(dc->_resultPointer);
    p+=pos;
    return *p;
}

int DECL2 daveGetS16At(daveConnection * dc, int pos) {
    union {
	short a;
	uc b[2];
    } u;
    uc * p=(uc *)(dc->_resultPointer);
    p+=pos;
#ifdef DAVE_LITTLE_ENDIAN
    u.b[1]=*p;
    p++;
    u.b[0]=*p;
#else
    u.b[0]=*p;
    p++;
    u.b[1]=*p;
#endif
    return u.a;
}

int DECL2 daveGetU16At(daveConnection * dc, int pos) {
    union {
	unsigned short a;
	uc b[2];
    } u;
    uc * p=(uc *)(dc->_resultPointer);
    p+=pos;
#ifdef DAVE_LITTLE_ENDIAN
    u.b[1]=*p;
    p++;
    u.b[0]=*p;
#else
    u.b[0]=*p;
    p++;
    u.b[1]=*p;
#endif    
    return u.a;
}

int DECL2 daveGetS32At(daveConnection * dc, int pos) {
    union {
	int a;
	uc b[4];
    } u;
    uc * p=dc->_resultPointer;
    p+=pos;
#ifdef DAVE_LITTLE_ENDIAN    
    u.b[3]=*p;
    p++;
    u.b[2]=*p;
    p++;
    u.b[1]=*p;
    p++;
    u.b[0]=*p;
#else
    u.b[0]=*p;
    p++;
    u.b[1]=*p;
    p++;
    u.b[2]=*p;
    p++;
    u.b[3]=*p;
#endif
    return u.a;
}

unsigned int DECL2 daveGetU32At(daveConnection * dc, int pos) {
    union {
	unsigned int a;
	uc b[4];
    } u;
    uc * p=(uc *)(dc->_resultPointer);
    p+=pos;
#ifdef DAVE_LITTLE_ENDIAN    
    u.b[3]=*p;
    p++;
    u.b[2]=*p;
    p++;
    u.b[1]=*p;
    p++;
    u.b[0]=*p;
#else
    u.b[0]=*p;
    p++;
    u.b[1]=*p;
    p++;
    u.b[2]=*p;
    p++;
    u.b[3]=*p;
#endif    
    return u.a;
}
/*
    put one byte into buffer b:
*/
uc * DECL2 davePut8(uc *b,int v) {
    *b = v & 0xff;
    b++;
    return b;
}

uc * DECL2 davePut16(uc *b,int v) {
    union {
	short a;
	uc b[2];
    } u;
    u.a=v;
#ifdef DAVE_LITTLE_ENDIAN    
    *b=u.b[1];
    b++;
    *b=u.b[0];
#else    
    *b=u.b[0];
    b++;
    *b=u.b[1];
#endif
    b++;
    return b;
}

uc * DECL2 davePut32(uc *b, int v) {
    union {
	int a;
	uc b[2];
    } u;
    u.a=v;
#ifdef DAVE_LITTLE_ENDIAN        
    *b=u.b[3];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[0];
#else    
    *b=u.b[0];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[3];
#endif    
    b++;
    return b;
}

uc * DECL2 davePutFloat(uc *b,float v) {
    union {
	float a;
	uc b[2];
    } u;
    u.a=v;
#ifdef DAVE_LITTLE_ENDIAN        
    *b=u.b[3];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[0];
#else    
    *b=u.b[0];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[3];
#endif
    b++;
    return b;
}

void DECL2 davePut8At(uc *b, int pos, int v) {
    union {
	short a;
	uc b[2];
    } u;
    u.a=v;
    b+=pos;
    *b=v & 0xff;
}

void DECL2 davePut16At(uc *b, int pos, int v) {
    union {
	short a;
	uc b[2];
    } u;
    u.a=v;
    b+=pos;
#ifdef DAVE_LITTLE_ENDIAN        
    *b=u.b[1];
    b++;
    *b=u.b[0];
#else    
    *b=u.b[0];
    b++;
    *b=u.b[1];
#endif    
}

void DECL2 davePut32At(uc *b, int pos, int v) {
    union {
	int a;
	uc b[2];
    } u;
    u.a=v;
    b+=pos;
#ifdef DAVE_LITTLE_ENDIAN        
    *b=u.b[3];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[0];
#else    
    *b=u.b[0];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[3];
#endif    
}

void DECL2 davePutFloatAt(uc *b, int pos,float v) {
    union {
	float a;
	uc b[2];
    } u;
    u.a=v;
    b+=pos;
#ifdef DAVE_LITTLE_ENDIAN        
    *b=u.b[3];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[0];
#else    
    *b=u.b[0];
    b++;
    *b=u.b[1];
    b++;
    *b=u.b[2];
    b++;
    *b=u.b[3];
#endif    
}
/*
    "passive mode" functions. Needed to "simulate" an S7 PLC.
*/
userReadFunc readCallBack=NULL;
userWriteFunc writeCallBack=NULL;

void DECL2 _daveConstructReadResponse(PDU * p) {
    uc pa[]={4,1}; 
    uc da[]={0xFF,4,0,0}; 
    _daveInitPDUheader(p,3);
    _daveAddParam(p, pa, sizeof(pa));
    _daveAddData(p, da, sizeof(da));    
}

void DECL2 _daveConstructBadReadResponse(PDU * p) {
    uc pa[]={4,1}; 
    uc da[]={0x0A,0,0,0}; 
    _daveInitPDUheader(p,3);
    _daveAddParam(p, pa, sizeof(pa));
    _daveAddData(p, da, sizeof(da));    
}

void DECL2 _daveConstructWriteResponse(PDU * p) {
    uc pa[]={5,1}; 
    uc da[]={0xFF}; 
    _daveInitPDUheader(p,3);
    _daveAddParam(p, pa, sizeof(pa));
    _daveAddData(p, da, sizeof(da));    
}

void DECL2 _daveHandleRead(PDU * p1,PDU * p2) {
    int result;
    uc * userBytes=NULL; //is this really better than reading from a dangling pointer?
    int bytes=0x100*p1->param[6]+p1->param[7];
    int DBnumber=0x100*p1->param[8]+p1->param[9];
    int area=p1->param[10];
    int start=0x10000*p1->param[11]+0x100*p1->param[12]+p1->param[13];
    LOG5("read %d bytes from %s %d beginning at %d.\n",
	bytes, daveAreaName(area),DBnumber,start);
    if (readCallBack)	
	userBytes=readCallBack(area, DBnumber,start, bytes, &result);	
    _daveConstructReadResponse(p2);	
    _daveAddValue(p2, userBytes, bytes);
    _daveDumpPDU(p2);
};

void DECL2 _daveHandleWrite(PDU * p1,PDU * p2) {
    int result,bytes=0x100*p1->param[6]+p1->param[7];
    int DBnumber=0x100*p1->param[8]+p1->param[9];
    int area=p1->param[10];
    int start=0x10000*p1->param[11]+0x100*p1->param[12]+p1->param[13];
    LOG5("write %d bytes to %s %d beginning at %d.\n",
	bytes, daveAreaName(area),DBnumber,start);
    if (writeCallBack)	
	writeCallBack(area, DBnumber,start, bytes, &result, p1->data+4);	
    LOG1("after callback\n");
    FLUSH;
    _daveConstructWriteResponse(p2);	
    LOG1("after ConstructWriteResponse()\n");
    FLUSH;
    _daveDumpPDU(p2);
    LOG1("after DumpPDU()\n");
    FLUSH;
};


/*
    10/04/2003 	PPI has an address. Implemented now.
    06/03/2004 	Fixed a bug in _davePPIexchange, which caused timeouts
		when the first call to readChars returned less then 4 characters.
*/

int DECL2 _daveWriteIBH(daveInterface * di, uc * buffer, int len) { //cs: let it be uc 
    int res;
    if (daveDebug & daveDebugByte) {
	_daveDump("writeIBH: ", buffer, len);
    }	
#ifdef HAVE_SELECT    
    res=write(di->fd.wfd, buffer, len);
#endif    
#ifdef BCCWIN
//    res=send((SOCKET)(di->fd.wfd), buffer, ((len+1)/2)*2, 0);
    res=send((SOCKET)(di->fd.wfd), buffer, len, 0);
#endif
    return res;
}    

int DECL2 _davePackPDU(daveConnection * dc,PDU *p) {
    IBHpacket * ibhp;
    MPIheader * hm= (MPIheader*) (dc->msgOut+sizeof(IBHpacket)); // MPI headerPDU begins packet header
    hm->MPI=dc->MPIAdr;
    hm->localMPI=dc->iface->localMPI;
    hm->src_conn=dc->ibhSrcConn;
    hm->dst_conn=dc->ibhDstConn;
    hm->len=2+p->hlen+p->plen+p->dlen;		// set MPI length
    hm->func=0xf1;				// set MPI "function code"
    hm->packetNumber=_daveIncMessageNumber(dc);
    ibhp = (IBHpacket*) dc->msgOut;
    ibhp->ch1=7;
    ibhp->ch2=0xff;
    ibhp->len=hm->len+5;
    ibhp->packetNumber=dc->packetNumber;
    dc->packetNumber++;
    ibhp->rFlags=0x82;
    ibhp->sFlags=0;
    
    return 0;
}    

uc _MPIack[]={
	0x07,0xff,0x08,0x05,0x00,0x00,0x82,0x00, 0x15,0x14,0x02,0x00,0x03,0xb0,0x01,0x00,
};


void DECL2 _daveSendMPIAck_IBH(daveConnection*dc) {
    _MPIack[15]=dc->msgIn[16];
    _MPIack[8]=dc->ibhSrcConn;
    _MPIack[9]=dc->ibhDstConn;
    _MPIack[10]=dc->MPIAdr;
    _daveWriteIBH(dc->iface,_MPIack,sizeof(_MPIack));
}

/*
    send a network level ackknowledge
*/
void DECL2 _daveSendIBHNetAck(daveConnection * dc) {
    IBHpacket * p;
    uc ack[13];
    memcpy(ack, dc->msgIn, sizeof(ack));
    p= (IBHpacket*) ack;
    p->len=sizeof(ack)-sizeof(IBHpacket);
    ack[11]=1;
    ack[12]=9;
//    LOG2("Sending net level ack for number: %d\n",p->packetNumber);
    _daveWriteIBH(dc->iface, ack,sizeof(ack));
}

uc _MPIconnectResponse[]={ 
	0xff,0x07,0x13,0x00,0x00,0x00,0xc2,0x02, 0x14,0x14,0x03,0x00,0x00,0x22,0x0c,0xd0,
	0x04,0x00,0x80,0x00,0x02,0x00,0x02,0x01, 0x00,0x01,0x00,
};

/*
    packet analysis. mixes all levels.
*/
int DECL2 __daveAnalyze(daveConnection * dc) {
    int haveResp;
    IBHpacket * p, *p2;
    MPIheader * pm;
    MPIheader2 * m2;
    PDU p1;
#ifdef passiveMode    
    PDU pr;
#endif    
    uc resp[2000];
    
//    if (dc->AnswLen==0) return _davePtEmpty;
    haveResp=0;
    
    p= (IBHpacket*) dc->msgIn;
    dc->needAckNumber=-1;		// Assume no ack
    if (daveDebug & daveDebugPacket){
	LOG2("Channel: %d\n",p->ch1);
	LOG2("Channel: %d\n",p->ch2);
        LOG2("Length:  %d\n",p->len);
	LOG2("Number:  %d\n",p->packetNumber);
        LOG3("sFlags:  %04x rFlags:%04x\n",p->sFlags,p->rFlags);
    }    
    if (p->rFlags==0x82) {
	pm= (MPIheader*) (dc->msgIn+sizeof(IBHpacket));
	if (daveDebug & daveDebugMPI){    
	    LOG2("srcconn: %d\n",pm->src_conn);
	    LOG2("dstconn: %d\n",pm->dst_conn);
	    LOG2("MPI:     %d\n",pm->MPI);
	    LOG2("MPI len: %d\n",pm->len);
	    LOG2("MPI func:%d\n",pm->func);
	}    
	if (pm->func==0xf1) {
	    if (daveDebug & daveDebugMPI)    
		LOG2("MPI packet number: %d needs ackknowledge\n",pm->packetNumber);
	    dc->needAckNumber=pm->packetNumber;
	    _daveSetupReceivedPDU(dc, &p1);
#ifdef passiveMode
// construct response:	    
	    pr.header=resp+sizeof(IBHpacket)+sizeof(MPIheader2);
#endif	    
	    p2= (IBHpacket*) resp;
	    p2->ch1=p->ch2;
	    p2->ch2=p->ch1;
	    p2->packetNumber=0;
	    p2->sFlags=0;
	    p2->rFlags=0x2c2;
	    
	    m2= (MPIheader2*) (resp+sizeof(IBHpacket));
	    m2->src_conn=pm->src_conn;
	    m2->dst_conn=pm->dst_conn;
	    m2->MPI=pm->MPI;
	    m2->xxx1=0;
	    m2->xxx2=0;
	    m2->xx22=0x22;
	    if (p1.param[0]==daveFuncRead) {
#ifdef passiveMode	    
		_daveHandleRead(&p1,&pr);
		haveResp=1;
		m2->len=pr.hlen+pr.plen+pr.dlen+2; 
#endif
		p2->len=m2->len+7;
	    } else if (p1.param[0]==daveFuncWrite) {
#ifdef passiveMode
		_daveHandleWrite(&p1,&pr);
		haveResp=1;
		m2->len=pr.hlen+pr.plen+pr.dlen+2;
#endif		
		p2->len=m2->len+7;
	    } else {
		if (daveDebug & daveDebugPDU)    
		    LOG2("Unsupported PDU function code: %d\n",p1.param[0]);
		return _davePtUnknownPDUFunc;
	    }
	    
	}
	if (pm->func==0xb0) {
    	    LOG2("Ackknowledge for packet number: %d\n",*(dc->msgIn+15));
	    return _davePtMPIAck;
	}
	if (pm->func==0xe0) {
	    LOG2("Connect to MPI: %d\n",pm->MPI);
	    memcpy(resp, _MPIconnectResponse, sizeof(_MPIconnectResponse));
	    resp[8]=pm->src_conn;
	    resp[9]=pm->src_conn;
	    resp[10]=pm->MPI;
	    haveResp=1;
	}    
    }	
    
    if (p->rFlags==0x2c2) {
	MPIheader2 * pm= (MPIheader2*) (dc->msgIn+sizeof(IBHpacket));
	if (daveDebug & daveDebugMPI) {
	    LOG2("srcconn: %d\n",pm->src_conn);
	    LOG2("dstconn: %d\n",pm->dst_conn);
	    LOG2("MPI:     %d\n",pm->MPI);
	    LOG2("MPI len: %d\n",pm->len);
	    LOG2("MPI func:%d\n",pm->func);
	}
	if (pm->func==0xf1) {
	    if (daveDebug & daveDebugMPI)    
		LOG1("analyze 1\n");
	    dc->needAckNumber=pm->packetNumber;
	    dc->PDUstartI= sizeof(IBHpacket)+sizeof(MPIheader2);
	    _daveSendMPIAck_IBH(dc);
	    
	    return 55;
	    
/*	
	    if (daveDebug & daveDebugMPI)    
		LOG2("MPI packet number: %d\n",pm->packetNumber);
	    dc->needAckNumber=pm->packetNumber;
//	    p1.header=((uc*)pm)+sizeof(MPIheader2);
	    dc->PDUstartI= sizeof(IBHpacket)+sizeof(MPIheader2);
	    _daveSetupReceivedPDU(dc, &p1);
	    
	    if (p1.param[0]==daveFuncRead) {
		LOG1("read Response\n");
		 _daveSendMPIAck_IBH(dc);
		 dc->resultPointer=p1.data+4;
		 dc->_resultPointer=p1.data+4;
		 dc->AnswLen=p1.dlen-4;
		return _davePtReadResponse;
	    } else if (p1.param[0]==daveFuncWrite) {
		 _daveSendMPIAck_IBH(dc);
		 LOG1("write Response\n");
		 return _davePtWriteResponse;
	    } else {
		LOG2("Unsupported PDU function code: %d\n",p1.param[0]);
	    }
*/	    
	}
	
	if (pm->func==0xb0) {
	    if (daveDebug & daveDebugMPI)
    		LOG2("Ackknowledge for packet number: %d\n",pm->packetNumber);
	} else {
	    if (daveDebug & daveDebugMPI)
		LOG2("Unsupported MPI function code !!: %d\n",pm->func);
	    _daveSendMPIAck_IBH(dc);
	}
    }
/*
    Sending IBHNetAck also for packets with sFlags=082 nearly doubles the speed for LINUX and
    speeds up windows version to the level of LINUX.
    Thanks to Axel Kinting for this proposal and for his patience finding it out!
*/    
    if (((p->sFlags==0x82)||(p->sFlags==0x82))&&(p->packetNumber)&&(p->len)) _daveSendIBHNetAck(dc);
    if (haveResp) {
	_daveWriteIBH(dc->iface, resp, resp[2]+8);
	_daveDump("I send response:", resp, resp[2]+8);
    }	
    return 0;
};

int DECL2 _daveExchangeIBH(daveConnection * dc, PDU * p) {
    
    _daveSendMessageMPI_IBH(dc, p);
    dc->AnswLen=0;
    return _daveGetResponseMPI_IBH(dc);
}

int DECL2 _daveSendMessageMPI_IBH(daveConnection * dc, PDU * p) {
    int res;
    _davePackPDU(dc, p);
    res=_daveWriteIBH(dc->iface, dc->msgOut, dc->msgOut[2]+8);
    if (daveDebug & daveDebugPDU)    
	_daveDump("I send request: ",dc->msgOut, dc->msgOut[2]+8);
    return res;	
}	

int DECL2 _daveGetResponseMPI_IBH(daveConnection * dc) {
    int res,count,pt;
    count=0;
    pt=0;
    do {
	res=_daveReadIBHPacket(dc->iface, dc->msgIn);
	count++;
	if(res>4)
	    pt=__daveAnalyze(dc);
	if (daveDebug & daveDebugExchange)    
	    LOG2("ExchangeIBH packet type:%d\n",pt);
    } while ((pt!=55)&&(count<7));	// 05/21/2013
    if(pt!=55) return daveResTimeout;
    return 0;
}


/*
    This performs initialization steps with sampled byte sequences. If chal is <>NULL
    it will send this byte sequence.
    It will then wait for a packet and compare it to the sample.
*/
int DECL2 _daveInitStepIBH(daveInterface * iface, uc * chal, int cl, us* resp, int rl, uc*b) {
    int res, res2, a=0;
    if (daveDebug & daveDebugConnect) 
	LOG1("_daveInitStepIBH before write.\n");
    if (chal) res=_daveWriteIBH(iface, chal, cl);else res=daveResInvalidParam;
    if (daveDebug & daveDebugConnect) 
	LOG2("_daveInitStepIBH write returned %d.\n",res);
    if (res<0) return 100;
    res=_daveReadIBHPacket(iface, b);
/*
    We may get a network layer ackknowledge and an MPI layer ackknowledge, which we discard.
    So, normally at least the 3rd packet should have the desired response. 
    Waiting for more does:
	-discard extra packets resulting from last step.
	-extend effective timeout.
*/    
    while (a<5) {
	if (a) {
	    res=_daveReadIBHPacket(iface, b);
//	    _daveDump("got:",b,res);
	}
	if (res>0) {
	    res2=_daveMemcmp(resp,b,rl/2);
	    if (0==res2) {
		if (daveDebug & daveDebugInitAdapter) 
		    LOG3("*** Got response %d %d\n",res,rl);
		return a;
	    }  else {
		if (daveDebug & daveDebugInitAdapter)  
		    LOG2("wrong! %d\n",res2);
	    }
	}
	a++;
    }
    return a;
}

/*
uc chal0[]={
	0x00,0xff,0x03,0x00,0x00,0x00,0x04,0x00, 0x03,0x07,0x02,
};
*/
/*
us resp0[]={
	0xff,0x00,0xfe,0x00, 0x04,0x00,0x00,0x00, 
	0x00,0x07,0x02,0x03,0x1f,
	0x100,		// MPI address of NetLink
	0x02,0x00,
	0x103,		// 187,5, MPI = 3
			// 19,5, PB = 1
	0x00,0x00,0x00,0x102,
	0x00,0x02,0x3e, 
	0x19f,		// 187,5, MPI = 9F
			// 19,5, PB = 64
	0x101,		// 187,5, MPI = 1
			// 19,5, PB = 0
	
	0x101,		// 187,5, MPI = 1
			// 19,5, PB = 0
	0x00,
	0x102,		// 187,5, MPI = 1	
			// 19,5, PB = 2
	0x00,
	0x116,		// 187,5, MPI = 3c	
			// 19,5, PB = 16
	0x00,
	0x13c,		// 187,5, MPI = 90
			// 19,5, PB = 3c
			
	0x101,			
	0x110,
	0x127,0x100,0x100,0x114,0x101, 0x100,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x0e,
	'H', 'i','l','s','c','h','e','r',' ','G','m','b','H',' ',
	
	0x200, / * do not compare the rest * /
	};
*/

uc chal1[]={ 
	0x07,0xff,0x08,0x01,0x00,0x00,0x96,0x00, 
	    0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x01,
};

us resp1[]={ 
	0xff,0x07,0x87,0x01,0x96,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x7f,0x0a,0x02,
};
/*
uc chal2[]={
	0x00,0x10,0x01,0x00,0x00,0x00,0x01,0x00, 0x0f,
};
*/
/*
us resp2[]={
	0x10,0x00,
	    0x20,
	    0x00,
	    0x01,0x00,0x00,0x00, 
	    0x01,0x106,0x120,0x103,0x17,0x00,0x43,0x00,
	    0x00,0x00,
	    0x122,0x121,
	    0x00,0x00,0x00,0x00, 0x49,0x42,0x48,0x53,0x00,0x00,0x00,0x00,
	    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
};
*/
uc chal3[]={
	0x07,0xff,0x06,0x01,0x00,0x00,0x97,0x00, 0x15,0xff,0xf0,0xf0,0xf0,0xf0,
};

us resp3[]={
	0xff,0x107,0x02,0x01,0x97,0x00,0x00,0x00, 0x114,0x100,
};

uc chal8[]={
	0x07,
	0xff,
	0x11,	// 17 bytes
	0x02,
	0x00,
	0x00,
	0x82,
	0x00,

	0x14,	// source SAP
	0x00,
	0x02,	// MPI address
	0x01,
	0x0c,	// 12 bytes follow
	0xe0,0x04,0x00,0x80,
	0x00,0x02,
	0x00,0x02,
	0x01,	// communication type?
	0x00,
	0x01,	// 6 when routed
	0x00,
};

	uc chal8R[]={   //Routing
	0x07,
	0xff,
	0x1b,	// 27 bytes, 10 more
	0x02,
	0x00,
	0x00,
	0x82,
	0x00,
	0x14,	// source SAP
	0x00,	
	0x02,	// MPI address
	0x01,
	0x16,	// 22 bytes follow (when routing to MPI, 25 when routing to IP)
	0xe0,0x04,0x00,0x80,
	0x00,0x02,
	0x01,0x0c,
	0x01,	// dc->CommunicationType, // communication type? I do NOT think so.
	0x00,
	0x06,	// 6 byte subnet ID
	0x01,	// 1 byte PLC address	my guess
	0x02,	// 2 byte something else
	0x01,0x52,0x00,0x00,0x00,0x13,	// subnet ID
	0x01,				// PLC address, MPI or Profibus or IP
	0x01,	// comunication type?
	0x02,	// rack,slot ?
	0,0,0
	};

/* This is the correct response. I just inserted a "don't care" to make it work with latest 
   IBH simulator. Better fix the simulator!
us resp7[]={
	0xff,0x07,0x13,0x00,0x00,0x00,0xc2,0x02, 0x115,0x114,0x102,0x100,0x00,0x22,0x0c,0xd0,
	0x04,0x00,0x80,0x00,0x02,0x00,0x02,0x01, 0x00,0x01,0x00,
};
*/

// from routing, target MPI
us resp7[]={
	0xFF,0x07,0x11D,0x00,0x00,0x00,0xC2,0x02, 0x114,0x12E,0x102,0x100,0x00,0x22,0x116,0xD0,
	0x04,0x00,0x80,
//	0x01,0x0C,0x00,0x02,0x06, 0x01,0x02,0x01,0x52,0x00,0x00,0x00,0x13,
//	0x102,0x101,0x102,0x101,0x100,
};

uc chal011[]={
	0x07,0xff,0x07,0x03,0x00,0x00,0x82,0x00, 0x15,0x14,0x02,0x00,0x02,0x05,0x01,
};

us resp09[]={
	0xff,0x07,0x09,0x00,0x00,0x00,0xc2,0x02, 0x115,0x114,0x102,0x100,0x00,0x22,0x02,0x05,
	0x101,	// A.K.
};


/*
    Connect to a PLC via IBH-NetLink. Returns 0 for success and a negative number on errors.
*/
int DECL2 _daveConnectPLC_IBH(daveConnection*dc) {
    int a, retries;
    PDU p1;
    uc b[daveMaxRawLen];
    uc * pcha;
    dc->iface->timeout=500000;
    dc->iface->localMPI=0;
    dc->ibhSrcConn=20-1;
    dc->ibhDstConn=20-1;
    retries=0;
    do {
	if (daveDebug & daveDebugConnect)  // show only if in debug mode
	    LOG1("trying next ID:\n");
	dc->ibhSrcConn++;
	chal3[8]=dc->ibhSrcConn;
	a=_daveInitStepIBH(dc->iface, chal3,sizeof(chal3),resp3,sizeof(resp3),b);
	retries++;
    } while ((b[9]!=0) && (retries<10));
    if (daveDebug & daveDebugConnect) 
	LOG2("_daveInitStepIBH 4:%d\n",a); if (a>3) /* !!! */ return -4;;
//    if (!dc->routing) {
	chal8[8]=dc->ibhSrcConn;
	chal8[10]=dc->MPIAdr;
//    } else {
	chal8R[8]=dc->ibhSrcConn;
	chal8R[10]=dc->MPIAdr;
//    }

//    LOG2("setting MPI %d\n",dc->MPIAdr);
    if (!dc->routing) {
	a=_daveInitStepIBH(dc->iface, chal8,sizeof(chal8),resp7,sizeof(resp7),b);
    } else {
	pcha=chal8R+2;
	*pcha=26+dc->routingData.PLCadrsize;

	pcha=chal8R+12;
	*pcha=21+dc->routingData.PLCadrsize;
	
	pcha=chal8R+20;
	*pcha=11+dc->routingData.PLCadrsize;

	pcha=chal8R+24;
	*pcha=dc->routingData.PLCadrsize;

	pcha=chal8R+26;
//	_daveAddSubnet(dc, &pcha);
	
	*pcha=(dc->routingData.subnetID1) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID1) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) % 0x100; pcha++;

	memcpy(pcha, dc->routingData.PLCadr, dc->routingData.PLCadrsize);
	pcha+=dc->routingData.PLCadrsize;
	*pcha=dc->communicationType;
        *pcha=(dc->slot | dc->rack<<5);
	
//	a=_daveInitStepIBH(dc->iface, chal8R, sizeof(chal8R), resp7R, sizeof(resp7R), b);
//	a=_daveInitStepIBH(dc->iface, chal8R, sizeof(chal8R)-4+dc->routingData.PLCadrsize, resp7R, sizeof(resp7R), b);
//	a=_daveInitStepIBH(dc->iface, chal8R, sizeof(chal8R)-4+dc->routingData.PLCadrsize, resp7R, 19, b);
	a=_daveInitStepIBH(dc->iface, chal8R, sizeof(chal8R)-4+dc->routingData.PLCadrsize, resp7, sizeof(resp7), b);
    }
    dc->ibhDstConn=b[9];
    if (daveDebug & daveDebugConnect) 
	LOG3("_daveInitStepIBH 5:%d connID: %d\n",a, dc->ibhDstConn); if (a>3) return -5;

    chal011[8]=dc->ibhSrcConn;
    chal011[9]=dc->ibhDstConn;
    chal011[10]=dc->MPIAdr;	//??????
    a=_daveInitStepIBH(dc->iface, chal011,sizeof(chal011),resp09,sizeof(resp09),b);

    dc->ibhDstConn=b[9];
    if (daveDebug & daveDebugConnect) 
	LOG3("_daveInitStepIBH 5a:%d connID: %d\n",a, dc->ibhDstConn); if (a>3) return -5;
    
    dc->packetNumber=4;
    return _daveNegPDUlengthRequest(dc, &p1);
}    

uc chal31[]={
	0x07,0xff,0x06,0x08,0x00,0x00,0x82,0x00, 0x14,0x14,0x02,0x00,0x01,0x80,
};

/*
    Disconnect from a PLC via IBH-NetLink. 
    Returns 0 for success and a negative number on errors.
*/
int DECL2 _daveDisconnectPLC_IBH(daveConnection*dc) {
    uc b[daveMaxRawLen];
    chal31[8]=dc->ibhSrcConn;
    chal31[9]=dc->ibhDstConn;
    chal31[10]=dc->MPIAdr;
    _daveWriteIBH(dc->iface, chal31, sizeof(chal31));
    _daveReadIBHPacket(dc->iface, b);
#ifdef BCCWIN    
#else    
    _daveReadIBHPacket(dc->iface, b);
#endif    
    return 0;
}

/*
    Disconnect from a PLC via IBH-NetLink. This can be used to free other than your own 
    connections. Be careful, this may disturb third party programs/devices.
*/
int DECL2 daveForceDisconnectIBH(daveInterface * di, int src, int dest, int mpi) {
    uc b[daveMaxRawLen];
    chal31[8]=src;
    chal31[9]=dest;
    chal31[10]=mpi;
    _daveWriteIBH(di, chal31, sizeof(chal31));
    _daveReadIBHPacket(di, b);
#ifdef BCCWIN    
#else    
    _daveReadIBHPacket(di, b);
#endif    
    return 0;
}   

/*
    Resets the IBH-NetLink.
    Returns 0 for success and a negative number on errors.
*/
int DECL2 daveResetIBH(daveInterface * di) {
    uc chalReset[]={
	0x00,0xff,0x01,0x00,0x00,0x00,0x01,0x00,0x01
    };
    uc b[daveMaxRawLen];
    _daveWriteIBH(di, chalReset, sizeof(chalReset));
    _daveReadIBHPacket(di, b);
#ifdef BCCWIN
#else
    _daveReadIBHPacket(di, b);
#endif   
    return 0;
}

void DECL2 _daveSendMPIAck2(daveConnection *dc) {
    IBHpacket * p;
    uc c;
    uc ack[18];
    memcpy(ack,dc->msgIn,sizeof(ack));
    p= (IBHpacket*) ack;
    p->rFlags|=0x240;	//Why that?
    c=p->ch1; p->ch1=p->ch2; p->ch2=c;
    p->len=sizeof(ack)-sizeof(IBHpacket);
    p->packetNumber=0;	// this may mean: no net work level acknowledge
    ack[13]=0x22;
    ack[14]=3;
    ack[15]=176;
    ack[16]=1;
    ack[17]=dc->needAckNumber;
    _daveDump("send MPI-Ack2",ack,sizeof(ack));
    _daveWriteIBH(dc->iface,ack,sizeof(ack));
};

int DECL2 _davePackPDU_PPI(daveConnection * dc,PDU *p) {
    IBHpacket * ibhp;
    uc IBHPPIHeader[]={0xff,0xff,2,0,8};
    IBHPPIHeader[2]=dc->MPIAdr;
    memcpy(dc->msgOut+sizeof(IBHpacket), &IBHPPIHeader, sizeof(IBHPPIHeader));
    
    ibhp = (IBHpacket*) dc->msgOut;
    ibhp->ch1=7;
    ibhp->ch2=0xff;
    ibhp->len=p->hlen+p->plen+p->dlen+5;		// set MPI length
//    dc->msgOut[3]=2;
    *(dc->msgOut+sizeof(IBHpacket)+4)=p->hlen+p->plen+p->dlen+0;
    ibhp->packetNumber=dc->packetNumber;
    ibhp->rFlags=0x80;
    ibhp->sFlags=0;
//    dc->msgOut[3]=dc->packetNumber;
    dc->packetNumber++;
    dc->msgOut[6]=0x82;
    dc->msgOut[3]=2;
    return 0;
}    

/*
    send a network level ackknowledge
*/
void DECL2 _daveSendIBHNetAckPPI(daveConnection * dc) {
    uc ack[]={7,0xff,5,3,0,0,0x82,0,0xff,0xff,2,0,0};
    ack[10]=dc->MPIAdr;
    ack[3]=_daveIncMessageNumber(dc);
    _daveWriteIBH(dc->iface, ack, sizeof(ack));
}

int DECL2 _daveListReachablePartnersMPI_IBH(daveInterface * di, char * buf) {
    int a, i;
    uc b[2*daveMaxRawLen];
    a=_daveInitStepIBH(di, chal1,sizeof(chal1),resp1,16,b);
    if (daveDebug & daveDebugListReachables) 
	LOG2("_daveListReachablePartnersMPI_IBH:%d\n",a); 
    for (i=0;i<126;i++) {
	if (b[i+16]==0xFF) buf[i]=0x10; else buf[i]=0x30;
//	LOG3(" %d %d\n",i, b[i+16]); 
    }	
    return 126;
}    

/*
    packet analysis. mixes all levels.
*/
int DECL2 __daveAnalyzePPI(daveConnection * dc, uc sa) {
    IBHpacket* p;
    
    p= (IBHpacket*) dc->msgIn;
    if (daveDebug & daveDebugPacket){
	LOG2("Channel: %d\n",p->ch1);
	LOG2("Channel: %d\n",p->ch2);
        LOG2("Length:  %d\n",p->len);
	LOG2("Number:  %d\n",p->packetNumber);
        LOG3("sFlags:  %04x rFlags:%04x\n",p->sFlags,p->rFlags);
    }    
    if (p->sFlags==0x82) {
	if(p->len<=5) {
	    if(sa) _daveSendIBHNetAckPPI(dc);
	} else    
	    if ((p->len>=7) && (dc->msgIn[14]==0x32))
	    return 55;
    }	
    return 0;
};

int DECL2 _daveExchangePPI_IBH(daveConnection * dc, PDU * p) {
    int res, count, pt;
    _davePackPDU_PPI(dc, p);
    
    res=_daveWriteIBH(dc->iface, dc->msgOut, dc->msgOut[2]+8);
    if (daveDebug & daveDebugExchange)    
	_daveDump("I send request: ",dc->msgOut, dc->msgOut[2]+8);
//    _daveSendIBHNetAckPPI(dc);	
    count=0;
    do {
	res=_daveReadIBHPacket(dc->iface, dc->msgIn);
	count++;
	if (res>0)
	    pt=__daveAnalyzePPI(dc,1);
	else 
	    pt=0;    
	if (daveDebug & daveDebugExchange)    
	    LOG2("ExchangeIBH packet type:%d\n",pt);
    } while ((pt!=55)&&(count<7));
    if(pt!=55) return daveResTimeout;
    return 0;
}

int DECL2 _daveGetResponsePPI_IBH(daveConnection * dc) {
    int res, count, pt;
    count=0;
    do {
	_daveSendIBHNetAckPPI(dc);
	res=_daveReadIBHPacket(dc->iface, dc->msgIn);
	LOG2("_daveReadIBHPacket():%d\n",res);
	count++;
	if (res>0)
	    pt=__daveAnalyzePPI(dc,0);
	else 
	    pt=0;    
	if (daveDebug & daveDebugExchange)    
	    LOG2("ExchangeIBH packet type:%d\n",pt);
    } while ((pt!=55)&&(count<7));
    if(pt!=55) return daveResTimeout;
    return 0;
}

/*
    Build a PDU with data from 2 data blocks.
*/
int DECL2 BuildAndSendPDU(daveConnection * dc, PDU*p2,uc *pa,int psize, uc *ud,int usize,
    uc *ud2,int usize2) {
    int res;
    PDU p,*p3;
    uc * dn;
    p.header=dc->msgOut+dc->PDUstartO;
    _daveInitPDUheader(&p, 7);
    _daveAddParam(&p, pa, psize);
    _daveAddUserData(&p, ud, usize);
//    LOG2("*** here we are: %d\n",p.dlen);
    p3=&p;
    dn= p3->data+p3->dlen;
    p3->dlen+=usize2;
    memcpy(dn, ud2, usize2);
    
    ((PDUHeader*)p3->header)->dlen=daveSwapIed_16(p3->dlen);
    
    LOG2("*** here we are: %d\n",p.dlen);
    if (daveDebug & daveDebugPDU) {
	_daveDumpPDU(&p);
    }
    res=_daveExchange(dc, &p);
    if (daveDebug & daveDebugErrorReporting)
	LOG2("*** res of _daveExchange(): %d\n",res);
    if (res!=daveResOK) return res;
    res=_daveSetupReceivedPDU(dc,p2);
    if (daveDebug & daveDebugPDU) {
        _daveDumpPDU(p2);
    }
    if (daveDebug & daveDebugErrorReporting)
	LOG2("*** res of _daveSetupReceivedPDU(): %d\n",res);
    if (res!=daveResOK) return res;
    res=_daveTestPGReadResult(p2);
    if (daveDebug & daveDebugErrorReporting)
	LOG2("*** res of _daveTestPGReadResult(): %d\n",res);
    return res;
}    	

int DECL2 daveForce200(daveConnection * dc,int area, int start, int val) {
    int res;
    PDU p2;
//    uc pa[]={0,1,18,4,17,67,2,0};
//    uc da[]={'0','0'};

//32,7,0,0,0,0,0,c,0,16,

    uc pa[]={0,1,18,8,18,72,14,0,0,0,0,0};
    uc da[]={0,1,0x10,2,
	0,1,
	0,0,
	0,		// area
	0,0,0,		// start
    };
    uc da2[]={0,4,0,8,0,0,};
//    uc da2[]={0,4,0,8,7,0,};
    
    if ((area==daveAnaIn) || (area==daveAnaOut) /*|| (area==daveP)*/) {
	da[3]=4;
	start*=8;			/* bits */
    } else if ((area==daveTimer) || (area==daveCounter)||(area==daveTimer200) || (area==daveCounter200)) {
	da[3]=area;
    } else {
	start*=8;
    }
/*    else {
	if(isBit) {
	    pa[3]=1;
	} else {
	    start*=8;			
	}    
    }
*/    
    da[8]=area; 
    da[9]=start / 0x10000; 
    da[10]=(start / 0x100) & 0xff;
    da[11]=start & 0xff;
    
    
    da2[4]=val % 0x100;
    da2[5]=val / 0x100;
    res=BuildAndSendPDU(dc, &p2, pa, sizeof(pa), da, sizeof(da), da2, sizeof(da2));
    return res;
}

daveResultSet * DECL2 daveNewResultSet() {
    daveResultSet * p=(daveResultSet*) calloc(1, sizeof(daveResultSet));
#ifdef DEBUG_CALLS
    LOG2("daveNewResultSet() = %p\n",p);
    FLUSH;
#endif	        
    return p;
}    

void DECL2 daveFree(void * dc) {
//    if (dc!=NULL) {	// I'm not sure whether freeing a NULL pointer will do no harm on each and 
			// every system. So for safety, we check and set it to NULL afterwards.
	free(dc);
//    }	
}

int DECL2 daveGetProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length) {
    int res, uploadID, len, more, totlen;
    uc *bb=(uc*)buffer;	//cs: is this right?
    len=0;
    totlen=0;
    if (dc->iface->protocol==daveProtoAS511) {	    
	return daveGetS5ProgramBlock(dc, blockType, number, buffer, length);
    }
    
    res=initUpload(dc, blockType, number, &uploadID); 
    if (res!=0) return res;
    do {
	res=doUpload(dc,&more,&bb,&len,uploadID);
	totlen+=len;
	if (res!=0) return res;
    } while (more);
    res=endUpload(dc,uploadID);
    *length=totlen;
    return res;
}

int DECL2 daveDeleteProgramBlock(daveConnection*dc, int blockType, int number) {
	int res;
	PDU p,p2;
	uc paDelete[]= {
	0x28,0,0,0,0,0,0,0xFD,0,
	0x0a,0x01,0x00,
	'0','C', //Block type in ASCII (0C = FC)
	'0','0','0','0','1', //Block Number in ASCII
	'B', //Direction?
	0x05, //Length of Command
	'_','D','E','L','E' //Command Delete	
	};

	paDelete[13] = blockType;
	sprintf((char*)(paDelete+14),"%05d",number);
	paDelete[19] = 'B'; //This is overriden by sprintf via 0x00 as String seperator!

	p.header=dc->msgOut+dc->PDUstartO;
	_daveInitPDUheader(&p, 1);
	_daveAddParam(&p, paDelete, sizeof(paDelete));
	res=_daveExchange(dc, &p);
	if (res==daveResOK) {
		res=_daveSetupReceivedPDU(dc, &p2);
		if (daveDebug & daveDebugPDU) {
			_daveDumpPDU(&p2);
		}
	}

	//Retval of 0x28 in Recieved PDU Parameter Part means delete was sucessfull.
	//This needs to be implemneted and also error Codes Like Block does not exist or block is locked and so on...
	return res;
}



int DECL2 daveReadPLCTime(daveConnection * dc) {
    int res, len;
    PDU p2;
    uc pa[]={0,1,18,4,17,'G',1,0};
#ifdef DEBUG_CALLS
    LOG2("daveGetTime(dc:%p)\n", dc);
    FLUSH;
#endif	    	
    len=0;res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), NULL, 1);
    if (res==daveResOK) {
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len=p2.udlen;
    } else {
	if(daveDebug & daveDebugPrintErrors)
	    LOG3("daveGetTime: %04X=%s\n",res, daveStrerror(res));
    }
    dc->AnswLen=len;
    return res;
}

int DECL2 daveSetPLCTime(daveConnection * dc,uc * ts) {
    int res, len;
    PDU p2;
    uc pa[]={0,1,18,4,17,'G',2,0};
#ifdef DEBUG_CALLS
    LOG2("daveSetTime(dc:%p)\n", dc);
    FLUSH;
#endif	    	
    len=0;
    res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), ts, 10);
    if (res==daveResOK) {
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len=p2.udlen;
    } else {
	if(daveDebug & daveDebugPrintErrors)
	    LOG3("daveGetTime: %04X=%s\n",res, daveStrerror(res));
    }
    dc->AnswLen=len;
    return res;
}    

uc DECL2 daveToBCD(uc i) {
    return 16*(i /10)+(i%10);
}

uc DECL2 daveFromBCD(uc i) {
    return 10*(i /16)+(i%16);
}

int DECL2 daveSetPLCTimeToSystime(daveConnection * dc) {
    int res, len;
    PDU p2;
    uc pa[]={0,1,18,4,17,'G',2,0};
    uc ts[]={
	0x00,0x19,0x05,0x08,0x23,0x04,0x10,0x23,0x67,0x83,
    };	
#ifdef LINUX    
    struct tm systime;
    struct timeval t1;
    gettimeofday(&t1, NULL);
    localtime_r(&(t1.tv_sec),&systime);
    t1.tv_usec/=100;		//tenth of miliseconds from microseconds
//    ts[1]=daveToBCD(systime.tm_year/100+19);
    ts[1]=daveToBCD(systime.tm_year / 100); // fix 2010 bug is this line necessary? ok?
    ts[2]=daveToBCD(systime.tm_year % 100); // fix 2010 bug
    ts[3]=daveToBCD(systime.tm_mon+1);
    ts[4]=daveToBCD(systime.tm_mday);
    ts[5]=daveToBCD(systime.tm_hour);
    ts[6]=daveToBCD(systime.tm_min);
    ts[7]=daveToBCD(systime.tm_sec);
    ts[8]=daveToBCD(t1.tv_usec/100);
    ts[9]=daveToBCD(t1.tv_usec%100);
//    _daveDump("timestamp: ",ts,10);
//    LOG2("tm.sec:  %d\n", systime.tm_sec);
//    LOG2("tm.min:  %d\n", systime.tm_min);
//    LOG2("tm.hour: %d\n", systime.tm_hour);
#endif    

#ifdef BCCWIN
    SYSTEMTIME t1;
//    gettimeofday(&t1, NULL);
    GetLocalTime(&t1);
//    tm=localtime(&t1);
//    t1.tv_usec/=100;		//tenth of miliseconds from microseconds
//    WORD wYear;
/*
    WORD wMonth;
    WORD wDayOfWeek;
    WORD wDay;
    WORD wHour;
    WORD wMinute;
    WORD wSecond;
    WORD wMilliseconds;
*/
    ts[1]=daveToBCD(t1.wYear / 100); // fix 2010 bug
    ts[2]=daveToBCD(t1.wYear % 100); // fix 2010 bug
    ts[3]=daveToBCD(t1.wMonth);
    ts[4]=daveToBCD(t1.wDay);
    ts[5]=daveToBCD(t1.wHour);
    ts[6]=daveToBCD(t1.wMinute);
    ts[7]=daveToBCD(t1.wSecond);
    ts[8]=daveToBCD(t1.wMilliseconds/10);
    ts[9]=daveToBCD((t1.wMilliseconds%10)*10);
//    _daveDump("timestamp: ",ts,10);
//    LOG2("tm.sec:  %d\n", t1.wSecond);
//    LOG2("tm.min:  %d\n", t1.wMinute);
//    LOG2("tm.hour: %d\n", t1.wHour);
#endif    

#ifdef DEBUG_CALLS
    LOG2("SetPLCTimeToSystime(dc:%p)\n", dc);
    FLUSH;
#endif	    	
    len=0;
    res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), ts, sizeof(ts));
    if (res==daveResOK) {
        dc->resultPointer=p2.udata;
        dc->_resultPointer=p2.udata;
        len=p2.udlen;
    } else {
	if(daveDebug & daveDebugPrintErrors)
	    LOG3("daveGetTime: %04X=%s\n",res, daveStrerror(res));
    }
    dc->AnswLen=len;
    return res;
}    


/***************
  Simatic S5:
****************/

uc __davet2[]={STX};
uc __davet10[]={DLE};
char __davet1006[]={DLE,ACK};  // cs: this is only sent by system functions, so let it be char
us __daveT1006[]={DLE,ACK};
uc __davet121003[]={0x12,DLE,ETX};
us __daveT121003[]={0x12,DLE,ETX};
uc __davet161003[]={0x16,DLE,ETX};
us __daveT161003[]={0x16,DLE,ETX};
/*
    Reads <count> bytes from area <BlockN> with offset <offset>, 
    that can be readed with daveGetInteger etc. You can read bytes from 
    PBs & FBs too, but use daveReadBlock for this:
*/
int DECL2 daveReadS5Bytes(daveConnection * dc, uc area, uc BlockN, int offset, int count)
{
    int res,datastart,dataend;
    daveS5AreaInfo ai;
    uc b1[daveMaxRawLen];
//    if (_daveIsS5BlockArea(area)==0) {
    if (area==daveDB) {
        res=_daveReadS5BlockAddress(dc,area,BlockN,&ai);//TODO make address cache
        if (res<0) {
            LOG2("%s *** Error in ReadS5Bytes.BlockAddr request.\n", dc->iface->name);
            return res-50;
        }
	datastart=ai.address;
    } else {
	switch (area) {
	    case daveRawMemoryS5: datastart=0; break;
	    case daveInputs: datastart=dc->cache->PAE; break;
	    case daveOutputs: datastart=dc->cache->PAA; break;
	    case daveFlags: datastart=dc->cache->flags; break;
	    case daveTimer: datastart=dc->cache->timers; break;
	    case daveCounter: datastart=dc->cache->counters; break;
	    case daveSysDataS5: datastart=dc->cache->systemData; break;
	    default:
		LOG2("%s *** Unknown area in ReadS5Bytes request.\n", dc->iface->name);
		return -1;
	}	
    }
    //It's difficult to convert Intel-Motorola so I will use arithmetic:
    if ((count>daveMaxRawLen)
//    ||(offset+count>ai.len)
    ) {
        LOG2("%s *** readS5Bytes: Requested data is out-of-range.\n", dc->iface->name);
        return -1;
    }
    datastart+=offset;
    dataend=datastart+count-1;
    b1[0]=datastart/256;
    b1[1]=datastart%256;
    b1[2]=dataend/256;
    b1[3]=dataend%256;
    res=_daveExchangeAS511(dc,b1,4,2*count+7,0x04);
    if (res<0) {
        LOG2("%s *** Error in ReadS5Bytes.Exchange sequence.\n", dc->iface->name);
	return res-10;
    }
    if (dc->AnswLen<count+7) {
        LOG3("%s *** Too few chars (%d) in ReadS5Bytes data.\n", dc->iface->name,dc->AnswLen);
	return -5;
    }
    if ((dc->msgIn[0]!=0)||(dc->msgIn[1]!=0)||(dc->msgIn[2]!=0)||(dc->msgIn[3]!=0)||(dc->msgIn[4]!=0)) {
        LOG2("%s *** Wrong ReadS5Bytes data signature.\n", dc->iface->name);
	return -6;
    }
    dc->resultPointer=dc->msgIn+5;
    dc->_resultPointer=dc->resultPointer;
    
//    dc->dlen=dc->AnswLen-7;
    dc->AnswLen-=7;
    return 0;
}

/* 
    Write DLE,ACK to the serial interface:
*/
void DECL2 _daveSendDLEACK(daveInterface * di)	// serial interface
{
    di->ifwrite(di, __davet1006, 2);
}

/* 
    Sends a sequence of characters after doubling DLEs and adding DLE,EOT.
*/
int DECL2 _daveSendWithDLEDup(daveInterface * di, // serial interface
		       uc *b, 		    // a buffer containing the message
		       int size		    // the size of the string
		   )
{		
    uc target[daveMaxRawLen];	
    int res;
    int targetSize=0,i; //preload 
    if(daveDebug & daveDebugExchange)
	LOG1("SendWithDLEDup: \n");
    if(daveDebug & daveDebugExchange)
	_daveDump("I send",b,size);	
    for (i=0; i<size; i++) {
	target[targetSize]=b[i];targetSize++;
	if (b[i]==DLE) {
	    target[targetSize]=DLE;
	    targetSize++;
	}
    };
    target[targetSize]=DLE;
    target[targetSize+1]=EOT;
    targetSize+=2;
    if(daveDebug & daveDebugExchange)
	_daveDump("I send", target, targetSize);
    res=di->ifwrite(di, (char*)target, targetSize);
    if(daveDebug & daveDebugExchange)
	LOG2("send: res:%d\n",res);
    return 0;
}

/*
    Executes part of the dialog that requests transaction with PLC:
*/
int DECL2 _daveReqTrans(daveConnection * dc, uc trN)
{
    uc b1[3];
    int res;
    if (daveDebug & daveDebugExchange)
	    LOG3("%s daveReqTrans %d\n", dc->iface->name, trN);
    _daveSendSingle(dc->iface, STX);
    res=dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
    if (daveDebug & daveDebugByte)
	_daveDump("2got",b1, res);
    if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
	if (daveDebug & daveDebugPrintErrors)
    	    LOG3("%s daveReqTrans %d *** no DLE,ACK before send.\n", dc->iface->name, trN);
	return -1;
    }
    _daveSendSingle(dc->iface, trN);
    if (_daveReadSingle(dc->iface)!=STX) {
	if (daveDebug & daveDebugPrintErrors)
    	    LOG3("%s daveReqTrans %d *** no STX before send.\n", dc->iface->name, trN);
	return -2;
    }
    _daveSendDLEACK(dc->iface);
    dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT161003)/2);
    if (daveDebug & daveDebugByte)
	_daveDump("1got",b1, res);
    if (_daveMemcmp(__daveT161003, b1, sizeof(__daveT161003)/2)) {    
	if (daveDebug & daveDebugPrintErrors)
	    LOG3("%s daveReqTrans %d *** no accept0 from plc.\n", dc->iface->name, trN);
	return -3;
    }
    _daveSendDLEACK(dc->iface);
    return 0;
}

/*
    Executes part of the dialog required to terminate transaction:
*/
int DECL2 _daveEndTrans(daveConnection * dc)
{
    int res;
    uc b1[3];
    if (daveDebug & daveDebugExchange)
	LOG2("%s daveEndTrans\n", dc->iface->name);
    if (_daveReadSingle(dc->iface)!=STX) {
        LOG2("%s daveEndTrans *** no STX at eot sequense.\n", dc->iface->name);
//	return -1;
    }
    _daveSendDLEACK(dc->iface);
    res=dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT121003)/2);
    if (daveDebug & daveDebugByte)
	_daveDump("3got",b1, res);
    if (_daveMemcmp(__daveT121003, b1, sizeof(__daveT121003)/2)) {
	LOG2("%s daveEndTrans *** no accept of eot/ETX from plc.\n", dc->iface->name);
	return -2;
    }	
    _daveSendDLEACK(dc->iface);
    return 0;
}

/*
    Remove the DLE doubling:
*/
int DECL2 _daveDLEDeDup(daveConnection * dc, uc* rawBuf, int rawLen) {
    int j=0,k;
    for (k=0;k<rawLen-2;k++){
	dc->msgIn[j]=rawBuf[k]; j++;
	if (DLE==rawBuf[k]){
	    if (DLE!=rawBuf[k+1]) return -1;//Bad doubling found
	    k++;
	}
    }
    dc->msgIn[j]=rawBuf[k];//Copy 2 last chars (DLE,ETX)
    j++;k++;
    dc->msgIn[j]=rawBuf[k];
    dc->AnswLen=j+1;
    return 0;
}

int DECL2 _daveExchangeAS511(daveConnection * dc, uc * b, int len, int maxlen, int trN) {
    int res, i;
    uc b1[3];
    res=_daveReqTrans(dc, trN);
    if (res<0) {
        LOG2("%s *** Error in Exchange.ReqTrans request.\n", dc->iface->name);
	return res-10;
    }
    if (trN==8) {		//Block write functions have advanced syntax
	LOG1("trN 8\n");
        _daveSendWithDLEDup(dc->iface,b,4);
	LOG1("trN 8 done\n");
    } else {
	if (daveDebug & daveDebugExchange)
	    LOG3("trN %d len %d\n",trN,len);
        _daveSendWithDLEDup(dc->iface,b,len);
	if (daveDebug & daveDebugExchange)
	    LOG2("trN %d done\n",trN);
    }
//    _daveSendDLEACK(dc->iface);
//    res=dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ 2000 /*sizeof(__daveT1006)/2*/);
    res=dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
    if (daveDebug & daveDebugByte)
	_daveDump("4 got:",b1, res);
    if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
        LOG2("%s *** no DLE,ACK in Exchange request.\n", dc->iface->name);
	return -1;
    }
    if ((trN!=3)&&(trN!=7)&&(trN!=9)) {//write bytes, compress & delblk
     if (!_daveReadSingle(dc->iface)==STX) {
      LOG2("%s *** no STX in Exchange request.\n", dc->iface->name);
           return -2;
    }
//	usleep(500000);
        _daveSendDLEACK(dc->iface);
	res=0;
	do {
//	    i=dc->iface->ifread(dc->iface, dc->msgIn+res, /*100*dc->iface->timeout,*/ daveMaxRawLen-res);
	    i=dc->iface->ifread(dc->iface, dc->msgIn+res, /*100*dc->iface->timeout,*/ 1);
	    res+=i;
	    if (daveDebug & daveDebugByte)
		_daveDump("5 got:",dc->msgIn, res);
	} while((i>0)&& ( (dc->msgIn[res-2]!=DLE) || (dc->msgIn[res-1]!=ETX)));

	if (daveDebug & daveDebugByte)
	    LOG3("%s *** got %d bytes.\n", dc->iface->name,res);
        if (res<0) {
            LOG2("%s *** Error in Exchange.ReadChars request.\n", dc->iface->name);
            return res-20;
        }
	if ((dc->msgIn[res-2]!=DLE)||(dc->msgIn[res-1]!=ETX)) {
            LOG2("%s *** No DLE,ETX in Exchange data.\n", dc->iface->name);
            return -4;
        }
        if (_daveDLEDeDup(dc,dc->msgIn,res)<0) {
            LOG2("%s *** Error in Exchange rawdata.\n", dc->iface->name);
            return -3;
        }
        
//	usleep(500000);
        _daveSendDLEACK(dc->iface);
    }
    if (trN==8) { //Write requests have more differences from others ;(
	if (dc->msgIn[0]!=9) {
            LOG2("%s 8 *** No 0x09 in special Exchange request.\n", dc->iface->name);
	    return -5;
	}
	_daveSendSingle(dc->iface,STX);
	res=dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
	_daveDump("got:",b1, res);
	if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {	
	    LOG2("%s 8 *** no DLE,ACK in special Exchange request.\n", dc->iface->name);
	    return -6;
        }
        _daveSendWithDLEDup(dc->iface,b+4,len);
	
	res=dc->iface->ifread(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
	_daveDump("got:",b1, res);
	if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
//        if (!_daveTestChars(dc->iface, __davet1006, 2)) {
	    LOG2("%s 8 *** no DLE,ACK after transfer in Exchange.\n", dc->iface->name);
	    return -7;
        }
    }
    if (trN==7) {
//    	usleep(450000);
    }//TODO: check compression time
    res=_daveEndTrans(dc);
    if (res<0) {
        LOG2("%s *** Error in Exchange.EndTrans request.\n", dc->iface->name);
	return res-30;
    }
    return 0;
}

/*
    In S7, we need to tell the PLC what memory area we want to read from or write to. The PLC
    behaves as if each area were a different physical memory starting with an offset of 0.
    In S5, everything is in common 64k of memory. For different areas, we have to add start 
    offsets of areas or objects. 
    The following is needed to make memory access as S7-compatible as possible.
*/
int areaFromBlockType(int area){
    switch(area) {
	case daveS5BlockType_DB:		// S5 block type
	case daveBlockType_DB:			// S7 block type
	case daveDB: 				// S7 area type
	    return daveS5BlockType_DB;
	case daveS5BlockType_OB:
	case daveBlockType_OB: 
	    return daveS5BlockType_OB;
	case daveS5BlockType_FB:
	case daveBlockType_FB: 
	    return daveS5BlockType_FB;    
// s5 only:	    
	case daveS5BlockType_PB:
	    return daveS5BlockType_PB;
	case daveS5BlockType_SB:
	    return daveS5BlockType_SB;
	default: return area;    
    }
}
/*
    Requests physical addresses and lengths of blocks in PLC memory and writes
    them to ai structure:
*/
int DECL2 _daveReadS5BlockAddress(daveConnection * dc, uc area, uc BlockN, daveS5AreaInfo * ai)
{
    int res,dbaddr,dblen, s5Area;
    uc b1[24];			//15 + some Dups
//    if (_daveIsS5BlockArea(area)<0) {
//            printf("%s *** Not block area .\n", dc->iface->name);
//	    return -1;
//    }

//    b1[0]=area;
    
    s5Area=areaFromBlockType(area);
    b1[0]=s5Area;
    b1[1]=BlockN;
    res=_daveExchangeAS511(dc,b1,2,24,0x1A);
    if (res<0) {
        printf("%s *** Error in BlockAddr.Exchange sequense.\n", dc->iface->name);
	return res-10;
    }
    if (dc->AnswLen<15) {
        printf("%s *** Too few chars (%d) in BlockAddr data.\n", dc->iface->name,dc->AnswLen);
	return -2;
    }
    if ((dc->msgIn[0]!=0)
      ||(dc->msgIn[3]!=0x70)
      ||(dc->msgIn[4]!=0x70)
      ||(dc->msgIn[5]!=0x40+s5Area)||(dc->msgIn[6]!=BlockN)) {
        printf("%s *** Wrong BlockAddr data signature.\n", dc->iface->name);
	return -3;
    }
    dbaddr=dc->msgIn[1];
    dbaddr=dbaddr*256+dc->msgIn[2];//Let make shift operations to compiler's optimizer
    dblen=dc->msgIn[11];
    dblen=(dblen*256+dc->msgIn[12]-5)*2; //PLC returns dblen in words including
					  //5 word header (but returnes the
					  //start address after the header) so 
					  //dblen is length of block body
    ai->address=dbaddr;
    ai->len=dblen;
    return 0;
}

int DECL2 _daveIsS5BlockArea(uc area)
{
    if (
//	(area!=daveBlockType_S5DB)&&
        (area!=daveS5BlockType_SB)&&
        (area!=daveS5BlockType_PB)&&
        (area!=daveS5BlockType_FX)&&
        (area!=daveS5BlockType_FB)&&
        (area!=daveS5BlockType_DX)&&
        (area!=daveS5BlockType_OB)) {
	    return -1;
    }
    return 0;
}

int DECL2 _daveIsS5DBlockArea(uc area)
{
    if (area!=daveDB) {
//        (area!=daveBlockType_S5DX))    
//        (area!=daveBlockType_S5DX)) {
	    return -1;
    }
    return 0;
}

#define maxSysinfoLen 87
/*
    This is a trick which will intercept all functions not available for S5 AS511. It works 
    this way:
    
    A function for S7 forms a packet for S7 communication and then calls daveExchange which 
    will send the packet and return the answer.
    If a function is also vailable for S5, it must check whether the protocol is AS511. If so,
    the function calls it's S5 counterpart and returns the result of it.
    Hence, it will never reach daveExchange.
    
    Now, functions for which there is no S5 counterpart simply continue, (superfluously) form
    the S7 packet and call daveExchange, which will point hereto.
    This fake function allways returns a specific error code so the user knows the function
    is not available in the S5 protocol.
    
    The advantage of this mechanism is that additional functions for S7 can be added at any 
    time without caring about S5: If no special handling is provided, they end up here.
*/
int DECL2 _daveFakeExchangeAS511(daveConnection * dc, PDU *p){
    return daveNotAvailableInS5;
}

/*
    This is a deviation from normal use of connect functions: There are no connections in AS511.
    The reason why we provide a daveConnect() is this:
    From an S5 CPU, you don't read inputs, outputs,flags or any other memory area but simply
    bytes from global memory.
    There are addresses of input image area, output image area, flags, timers etc. These depend
    on CPU model. Next, there are start addresses of the data blocks. These addresses change 
    whenever a data block is created or changed in size or modified by programming device.
    
    In both cases, we could read the adresses from the PLC before reading the data. To save
    time and gain efficiency, we read them once in connectPLC. We rely on users following the 
    S7 scheme: connect to a PLC before reading from it !!
    
    If we would read addresses each time, you could do something you cannot with S7: pull the
    plug from one PLC, connect to another PLC and the program still works.
    
    Here, you CANNOT do that. You have to call connectPLC again after changing to the new PLC.

    Another thing are data block addresses. We could fetch all 256 possible addresses in connectPLC,
    too. But that would use 256 entries that must exist while the program might not use data 
    blocks at all. So we don't. We add data block addresses to the PLC address cache when they 
    are used for the first time.
    There are S5 programs that create data blocks dynamically. Hence cached addresses get invalid.
    If you have a PLC with such a program use 
	daveSetNonCacheable(dc, DBnumber);
    If you suspect somebody could pull the plug, connect a programming device, modify data blocks 
    and reconnect your application program, use    
	daveSetNonCacheable(dc, allDBs);
    In this case, the actual address will be fetched before each read or write from/to the related
    data blocks (which will slow down your application).
*/

int DECL2 _daveConnectPLCAS511(daveConnection * dc){
    int res;
    uc b1[maxSysinfoLen]; //20 words + some Dups
//    dc->maxPDUlength=1000;
    dc->maxPDUlength=240;
    dc->cache=(daveS5cache*)calloc(1,sizeof(daveS5cache));
    
    res=_daveExchangeAS511(dc,b1,0,maxSysinfoLen,0x18);
    if (res<0) {
        LOG2("%s *** Error in ImageAddr.Exchange sequence.\n", dc->iface->name);
	return res-10;
    }
    if (dc->AnswLen<47) {
        LOG3("%s *** Too few chars (%d) in ImageAddr data.\n", dc->iface->name,dc->AnswLen);
	return -2;
    }
    _daveDump("connect:",dc->msgIn, 47);
    dc->cache->PAE=daveGetU16from(dc->msgIn+5);	// start of inputs;
    dc->cache->PAA=daveGetU16from(dc->msgIn+7);	// start of outputs;
    dc->cache->flags=daveGetU16from(dc->msgIn+9);	// start of flag (marker) memory;
    dc->cache->timers=daveGetU16from(dc->msgIn+11);	// start of timer memory;
    dc->cache->counters=daveGetU16from(dc->msgIn+13);	// start of counter memory
    dc->cache->systemData=daveGetU16from(dc->msgIn+15);	// start of system data
    dc->cache->first=NULL;
    LOG2("start of inputs in memory %04x\n",dc->cache->PAE);
    LOG2("start of outputs in memory %04x\n",dc->cache->PAA);
    LOG2("start of flags in memory %04x\n",dc->cache->flags);
    LOG2("start of timers in memory %04x\n",dc->cache->timers);
    LOG2("start of counters in memory %04x\n",dc->cache->counters);
    LOG2("start of system data in memory %04x\n",dc->cache->systemData);
    return 0;
}

int DECL2 _daveDisconnectPLCAS511(daveConnection * dc){
    free(dc->cache);
    dc->cache=0;
    return 0;
}

/*
    Writes <count> bytes from area <BlockN> with offset <offset> from buf.
    You can't write data to the program blocks because you can't syncronize
    with PLC cycle. For this purposes use daveWriteBlock:
*/
int DECL2 daveWriteS5Bytes(daveConnection * dc, uc area, uc BlockN, int offset, int count, void * buf)
{
    int res,datastart;
    daveS5AreaInfo ai;
    uc b1[daveMaxRawLen];
//    if (_daveIsS5DBlockArea(area)==0) {
    if (area==daveDB) {
//	LOG1("_daveIsS5DBlockArea\n");
        res=_daveReadS5BlockAddress(dc,area,BlockN,&ai);
        if (res<0) {
            LOG2("%s *** Error in WriteS5Bytes.BlockAddr request.\n", dc->iface->name);
            return res-50;
        }
	datastart=ai.address;
    } else {
        switch (area) {
	    case daveRawMemoryS5: datastart=0; break;
	    case daveInputs: datastart=dc->cache->PAE; break;
	    case daveOutputs: datastart=dc->cache->PAA; break;
	    case daveFlags: datastart=dc->cache->flags; break;
	    case daveTimer: datastart=dc->cache->timers; break;
	    case daveCounter: datastart=dc->cache->counters; break;
	    case daveSysDataS5: datastart=dc->cache->systemData; break;
	    default:
    		LOG2("%s *** Unknown area in WriteS5Bytes request.\n", dc->iface->name);
		return -1;
	}
    }
    if ((count>daveMaxRawLen)||(offset+count>ai.len)) {
        LOG2("%s writeS5Bytes *** Requested data is out-of-range.\n", dc->iface->name);
        return -1;
    }
//    datastart=ai.address+offset;
    LOG2("area start is %04x, ",datastart);
    datastart+=offset;
    LOG2("data start is %04x\n",datastart);
    b1[0]=datastart/256;
    b1[1]=datastart%256;
    memcpy(&b1[2],buf,count);
    res=_daveExchangeAS511(dc,b1,2+count,0,0x03);
    if (res<0) {
        LOG2("%s *** Error in WriteS5Bytes.Exchange sequense.\n", dc->iface->name);
	return res-10;
    }
    return 0;
}

int DECL2 daveStopS5(daveConnection * dc) {
    uc b1[]={0x88,0x04};	// I don't know what this mean
    return daveWriteBytes(dc,daveSysDataS5,0,0x0c,2,b1);
}

int DECL2 daveStartS5(daveConnection * dc) {
    uc b1[]={0x68,0x00};	// I don't know what this mean
    return daveWriteBytes(dc,daveSysDataS5,0,0x0c,2,b1);
}

int DECL2 daveGetS5ProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length) {
//    int totlen,res;
//    *length=totlen;
    return daveResNotYetImplemented;
}


/********************************************
  Use Siemens DLLs and drivers for transport:
*********************************************/

/*
    While the following code is useless under operating systems others than win32,
    I leave it here, independent of conditionals. This ensures it is and will continue
    to be at least compileable now and over version changes. Who knows what it might
    be good for in the future...
*/
/*
    fill some standard fields and pass it to SCP-send:
*/
int DECL2 _daveSCP_send(int fd, uc * reqBlock) {
    S7OexchangeBlock* fdr;
    fdr=(S7OexchangeBlock*)reqBlock;
    fdr->headerlength=80;
    fdr->allways2 = 2;
    fdr->payloadStart= 80;
    return SCP_send(fd, fdr->payloadLength+80, reqBlock);
}

int daveSCP_receive(int h, uc * buffer) {
    int res, datalen;
    S7OexchangeBlock * fdr;
    fdr=(S7OexchangeBlock*) buffer;
    res=SCP_receive(h, 0xFFFF, &datalen, sizeof(S7OexchangeBlock), buffer);
    if (daveDebug & daveDebugByte) {
	_daveDump("header:",buffer, 80);
	_daveDump("data:",buffer+80, fdr->payloadLength);
	_daveDump("data:",buffer+80, fdr->payloadLength);
    }	
    return res;	
}    

int DECL2 _daveConnectPLCS7online (daveConnection * dc) {

    int res=0;
    uc p2[]={
	0x00,0x02,0x01,0x00,
	0x0C,0x01,0x00,0x00,
	0x00,0x06,
	0x00,0x00,0x00,0x00,0x00,0x01,
	0x00,0x02,0x01,
    };

    uc pa[]=	{0xF0, 0 ,0, 1, 0, 1, 3, 0xc0,};

    PDU pu2,pu1, *p;

    int a;
    
    S7OexchangeBlock * fdr;
    fdr=(S7OexchangeBlock*)(dc->msgOut);
    p2[9]=dc->MPIAdr;	///
    dc->PDUstartI=80;

    memset(fdr,0,80);
    fdr->user= 110;
    fdr->field6= 64;
    fdr->field8= 16642;

    a= _daveSCP_send(((int)dc->iface->fd.wfd), dc->msgOut);
    daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);

    memset(fdr,0,206);
    fdr->user= 111;
    fdr->field6= 64;
    fdr->field7= 1;
    fdr->field8= 16642;
    fdr->validDataLength= 126;
    fdr->payloadLength= 126;
    fdr->field10= 1;
    fdr->field13= 2;
    fdr->field12= 114;
    memcpy(&(fdr->payload),p2,sizeof(p2));

    a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
    daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
    
    memset(fdr,0,98);
//    fdr->user= 112;
    fdr->field6= 64;
    fdr->field7= 6;
    fdr->field8= 16642;
    fdr->validDataLength= 18;
    fdr->payloadLength= 18;
    fdr->field10= 1;
    
    p=&pu1;
//    p->header=dc->msgOut+dc->PDUstartO;
    p->header=fdr->payload;
    _daveInitPDUheader(p,1);
    _daveAddParam(p, pa, sizeof(pa));
    if (daveGetDebug() & daveDebugPDU)
	_daveDumpPDU(p);
    
    a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
    daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
    pu2.header=dc->msgIn+80;
    _daveSetupReceivedPDU(dc, &pu2);
    if (daveGetDebug() & daveDebugPDU)
	_daveDumpPDU(&pu2);

    memset(fdr,0,560);
    fdr->user= 0;
    fdr->field6= 64;
    fdr->field7= 7;
    fdr->field8= 16642;
    fdr->payloadLength= 480;
    fdr->field10= 1;

    a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
    daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
    _daveSetupReceivedPDU(dc, &pu2);
    if (daveGetDebug() & daveDebugPDU)
	_daveDumpPDU(&pu2);
    dc->maxPDUlength=daveGetU16from(pu2.param+6);
//    if (daveDebug & daveDebugConnect) 
	LOG2("\n*** Partner offered PDU length: %d\n\n",dc->maxPDUlength);
    return res;

/*
    memset((uc*)(&giveBack)+80,0,480);
    giveBack.payloadLength= 480;
    return _daveNegPDUlengthRequest(dc, &pu1);
*/
}

int DECL2 _daveSendMessageS7online(daveConnection *dc, PDU *p) {
    int a;
    int datalen;
    int len=p->hlen+p->plen+p->dlen;
    uc buffer[sizeof(S7OexchangeBlock)];
    S7OexchangeBlock* fdr;
    fdr=(S7OexchangeBlock*)dc->msgOut;
    memset(dc->msgOut,0,80);
//    fdr->user= 114;
    fdr->field6= 64;
    fdr->field7= 6;
    fdr->field8= 16642;
    fdr->validDataLength= len;
    fdr->payloadLength= len;
    fdr->field10= 1;
    
//    memcpy(&(fdr->payload),buffer,len);
    a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
    SCP_receive((int)(dc->iface->fd.rfd), 0xFFFF, &datalen, sizeof(S7OexchangeBlock), buffer);
//    daveSCP_receive(dc->iface->fd.rfd, dc->msgIn);
    return 0;
}

int DECL2 _daveDisconnectPLCS7online (daveConnection * dc) {
    int co,er,a;
    S7OexchangeBlock reqBlock;
    S7OexchangeBlock* fdr;
    uc b1[sizeof(S7OexchangeBlock)];


    fdr=&reqBlock;

    memset(fdr,0,140);
    fdr->user= 102;
    fdr->priority= 0;
    
    fdr->field6= 64;
    fdr->field7= 12;
    fdr->field8= 255;
    fdr->payloadLength= 0;
    fdr->field10= 0x1;
//    fdr->functionCode= 0x1;
//    fdr->id3= 0x1;
    co=0;
    do{
	a= _daveSCP_send((int)(dc->iface->fd.wfd), (char *) &reqBlock);
	er=SCP_get_errno();
	printf("res 7:%d %d\n",a,er);
#ifdef BCCWIN
	Sleep(100);
#endif
#ifdef LINUX
	usleep(100000);
#endif
	co++;
    }	while ((a!=0)&&(co<10));
//    daveSCP_receive((int)(dc->iface->fd.rfd), b1);
    
    co=0;
    do{
	a=daveSCP_receive((int)(dc->iface->fd.rfd), b1);
	er=SCP_get_errno();
	printf("result 7:%d %d\n",a,er);
	co++;
    } while ((a!=0)&&(co<10));

}


int DECL2 _daveGetResponseS7online(daveConnection *dc) {
    int a;
    a= _daveSCP_send((int)(dc->iface->fd.rfd), dc->msgIn);
    daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
    return 0;
}

int DECL2 _daveExchangeS7online(daveConnection * dc, PDU * p) {
    int res;
    res=_daveSendMessageS7online(dc, p);
    dc->AnswLen=0;
    res=_daveGetResponseS7online(dc);
    return res;
}

int DECL2 _daveListReachablePartnersS7online (daveInterface * di, char * buf) {
    int a;
    S7OexchangeBlock reqBlock;
    uc b1[sizeof(S7OexchangeBlock)];
    S7OexchangeBlock* fdr;
    fdr=&reqBlock;

    memset(fdr,0,140);
    fdr->user= 102;
    fdr->priority= 1;
    fdr->field6= 34; // 0x22 is FDL
    fdr->field8= 16642;
    fdr->payloadLength= 60;
    fdr->application_block_service= 0x28;

    a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
    daveSCP_receive((int)(di->fd.rfd), b1);

    fdr->user= 103;
    fdr->priority= 1;
    fdr->field6= 34;
    fdr->field8= 16642;
    fdr->application_block_service= 0x17;

    a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
    daveSCP_receive((int)(di->fd.rfd), b1);
    memset(fdr,0,140);

    fdr->user= 104;
    fdr->priority= 1;
    fdr->field6= 34;
    fdr->field8= 16642;
    fdr->payloadLength= 60;
    fdr->application_block_service= 0x28;
    a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
    daveSCP_receive((int)(di->fd.rfd), b1);

    memset(fdr,0,208);
    fdr->user= 105;
    fdr->priority= 1;
    fdr->field6= 34;
    fdr->field8 =16642;
    fdr->payloadLength= 128;
    fdr->application_block_service= 0x1a;
    a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
    daveSCP_receive((int)(di->fd.rfd), b1);
    
    memcpy(buf,b1+80,126);
    return 126;
}

/*
    This is not quite the same as in other protocols: Normally, we have a file descriptor or
    file handle in di->fd.rfd, di->fd.wfd. disconnectAdapter does something like making the
    MPI adapter leaving the Profibus token ring. File descriptor remains valid until it is 
    closed with closePort().
    In principle, instead of closing it, we could redo the sequence 
	daveNewInterface, initAdapter and then continue to use it.
    We cannot use closePort() on a "handle" retrieved from SCP_open(). It isn't a file handle.
    We cannot make closePort() treat it differently as there is no information in di->fd.rfd
    WHAT it is.
    - We could make di->fd.rfd a structure with extra information.
    - We could pass struct di->fd (daveOSserialtype) instead of di->fd.rfd / di->fd.wfd to all
      all functions dealing with di->fd.rfd. Then we could add extra information to 
      daveOSserialtype
    - We could better pas a pointer to an extended daveOSserialtype as it makes less problems
      when passing it to different programming languages.
    These would be major changes. They would give up the (theroetical?) possibility to use file 
    handles obtained in quite a different way and to put them into daveOSserialtype.
    I chose to change as little as possible for s7online and just SCP_close the handle here,
    expecting no one will try to reuse after this.
    
    Up to here is what version 0.8 does. The probleme is now that an application cannot do
    daveDisconnectAdapter(), closePort() as it does for other protocols.
    
    Now comes a second kludge for 0.8.1: We replace the "file handles" value by -1. Now we can 
    tell closePort() to do nothing for a value of -1.
    
    Befor releasing that, I think it is better to use different close functions, closeS7oline 
    for s7online and closePort() for everything else.
*/

/*
int DECL2 _daveDisconnectAdapterS7online(daveInterface * di) {
    int res;
    res=SCP_close((int)(di->fd.rfd));
    di->fd.rfd=-1;
    di->fd.wfd=-1;
    return res;
}
*/
/***
    NetLink Pro
***/
#define NET

#ifndef NET
int DECL2 _daveReadMPINLpro(daveInterface * di, uc *b) {
	int res=0,state=0,nr_read;
	uc bcc=0;
        nr_read= di->ifread(di, b, 2);
	if (nr_read>=2) {
	    res=256*b[0]+b[1]+2;
	    if (res>nr_read)
		nr_read+=di->ifread(di, b+nr_read, res-nr_read);
	    if (daveDebug & daveDebugInitAdapter) 
		LOG4("%s nr_read:%d res:%d.\n", di->name, nr_read, res);
	    return res-2;
	}
}
#endif

#ifdef NET
/*
    Read one complete packet. The bytes 0 and 1 contain length information.
    This version needs a socket filedescriptor that is set to O_NONBLOCK or
    it will hang, if there are not enough bytes to read.
    The advantage may be that the timeout is not used repeatedly.
*/

int DECL2 _daveReadMPINLpro(daveInterface * di,uc *b) {
	int res,i,length;
	i=_daveTimedRecv(di, b, 2);
	res=i;
	if (res <= 0) return daveResTimeout;
	if (res<2) {
	    if (daveDebug & daveDebugByte) {
		LOG2("res %d ",res);
		_daveDump("readISOpacket: short packet", b, res);
	    }
	    return daveResShortPacket; /* short packet */
	}
	length=b[1]+0x100*b[0];
	i=_daveTimedRecv(di, b+2, length);
	res+=i;
	if (daveDebug & daveDebugByte) {
	    LOG3("readMPINLpro: %d bytes read, %d needed\n",res, length);
	    _daveDump("readMPIpro: packet", b, res);
	}
	return (res);
}
#endif


/*
    This initializes the MPI adapter. Andrew's version.
*/

int DECL2 _daveInitAdapterNLpro(daveInterface * di)  /* serial interface */
{
    uc b3[]={
	0x01,0x03,0x02,0x27, 0x00,0x9F,0x01,0x14,
 	0x00,0x90,0x01,0xc, 0x00,	/* ^^^ MaxTsdr */
	0x00,0x5,
 	0x02,/* Bus speed */

	0x00,0x0F,0x05,0x01,0x01,0x03,0x81,/* from topserverdemo */
	/*^^ - Local mpi */
    };		
    
    int res;
    b3[16]=di->localMPI;
    if (di->speed==daveSpeed500k)
	b3[7]=0x64;
    if (di->speed==daveSpeed1500k)
	b3[7]=0x96;
    b3[15]=di->speed;

//    res=_daveInitStep(di, 1, b3, sizeof(b3),"initAdapter()");
    res=_daveInitStepNLpro(di, 1, b3, sizeof(b3),"initAdapter()", NULL);

//    res= _daveReadMPINLpro(di, b1);
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s initAdapter() success.\n", di->name);
//   _daveSendSingle(di,DLE);
    di->users=0;	/* there cannot be any connections now */
    return 0;
}


void DECL2 _daveSendSingleNLpro(daveInterface * di,	/* serial interface */
		     uc c  			/* chracter to be send */
		     ) 
{
    unsigned long i;
    uc c3[3];
    c3[0]=0;
    c3[1]=1;
    c3[2]=c;
//    di->ifwrite(di, c3, 3);
#ifdef HAVE_SELECT
    daveWriteFile(di->fd.wfd, c3, 3, i);
#endif    
#ifdef BCCWIN
    send((unsigned int)(di->fd.wfd), c3, 3, 0);
#endif
    
}


/*
    This sends a string after doubling DLEs in the String
    and adding DLE,ETX and bcc.
*/
int DECL2 _daveSendWithCRCNLpro(daveInterface * di, /* serial interface */
		    uc *b, 		 /* a buffer containing the message */
		    int size		 /* the size of the string */
		)
{		
    uc target[daveMaxRawLen];
    int i,targetSize=2;
    target[0]=size / 256;
    target[1]=size % 256;
    
//    int bcc=DLE^ETX; /* preload */
    for (i=0; i<size; i++) {
	target[targetSize]=b[i];targetSize++;
    };
//    targetSize+=0;
//    di->ifwrite(di, target, targetSize);
#ifdef HAVE_SELECT
    daveWriteFile(di->fd.wfd, target, targetSize, i);
#endif    
#ifdef BCCWIN
    send((unsigned int)(di->fd.wfd), target, targetSize, 0);
#endif
    
    if (daveDebug & daveDebugPacket)
	    _daveDump("_daveSendWithCRCNLpro",target, targetSize);
    return 0;
}


/* 
    Send a string of init data to the MPI adapter.
*/
int DECL2 _daveInitStepNLpro(daveInterface * di, int nr, uc *fix, int len, char * caller, uc * buffer ) {
    uc res[500];
    int i;
    if (daveDebug & daveDebugInitAdapter)
	LOG4("%s %s step %d.\n", di->name, caller, nr);

    _daveSendWithCRCNLpro(di, fix, len);
    i=_daveReadMPINLpro(di, (buffer != NULL) ? buffer : res );
    return 0;
}    

/* 
    Open connection to a PLC. This assumes that dc is initialized by
    daveNewConnection and is not yet used.
    (or reused for the same PLC ?)
*/
int DECL2 _daveConnectPLCNLpro(daveConnection * dc) {
    int res, len;
    PDU p1;
    uc * pcha;

    uc b4[]={
		0x04, //00
		0x80, //01 (0x80 | MPI)
		0x80, //02
		0x0D, //03
		0x00, //04
		0x14, //05
		
		0xE0, //06
		0x04, //07
		0x00, //08
		0x80, //09
		0x00, //10
		0x02, //11
		0x00, //12 //01 ??? Routing???
		0x02, //13 //02 = no routing / 0c = Routing to MPI / 0f Routing to IP (Bytecount to End-2)
		1, 	//
		0, 	//
		1,	//
		0, 	//17 //End of Telegram when no Routing (00) / 01 Routing to MPI / 04 Routing to IP
		0x00, //18
		0x00, //19 subnet1
		0x00, //20 subnet1
		0x00, //21
		0x00, //22
		0x00, //23 subnet2
		0x00, //24 subnet2
		
		0x00, //25 PLC address
		0x00, //26 
		0x00, //27 
		0x00, //28 
		0x02, //29 Communication type
		0x04  //30 Rack, Slot
    };

    us t4[]={
	0x04,0x80,0x180,0x0C,0x114,0x103,0xD0,0x04,	// 1/10/05 trying Andrew's patch
	0x00,0x80,
	0x00,0x02,0x00,0x02,0x01,
	0x00,0x01,0x00,
    };
    uc b5[]={	
	0x05,0x07,
    };
    us t5[]={    
	0x04,
	0x80,
	0x180,0x0C,0x114,0x103,0x05,0x01,
    };
    b4[1]|=dc->MPIAdr;	
    b4[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
    
    len=18;
    t4[1]|=dc->MPIAdr;	
    t5[1]|=dc->MPIAdr;	

    pcha=b4+16;
    if(dc->routing) {
    
	pcha=b4+12;
	*pcha=1; pcha++;
	*pcha=11+dc->routingData.PLCadrsize; 
	pcha=b4+16;
	*pcha=6; pcha++;
	*pcha=dc->routingData.PLCadrsize; pcha++;
	*pcha=2; pcha++;

	*pcha=(dc->routingData.subnetID1) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID1) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID2) % 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) / 0x100; pcha++;
	*pcha=(dc->routingData.subnetID3) % 0x100; pcha++;
	memcpy(pcha, dc->routingData.PLCadr, dc->routingData.PLCadrsize);
	pcha+=dc->routingData.PLCadrsize;
	len=27+dc->routingData.PLCadrsize; 
	
    }
    *pcha=dc->communicationType; pcha++;
	*pcha=dc->rack; pcha++;
    _daveInitStepNLpro(dc->iface, 1, b4, len, "connectPLC(1)", dc->msgIn);
    
    // first 2 bytes of msgIn[] contain packet length
    dc->connectionNumber2=dc->msgIn[2+5]; // 1/10/05 trying Andrew's patch
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC(1) step 4.\n", dc->iface->name);	
    
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 5.\n", dc->iface->name);	

    _daveSendWithPrefixNLpro(dc, b5, sizeof(b5));		
        
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);	
    res= _daveReadMPINLpro(dc->iface,dc->msgIn);
    if (daveDebug & daveDebugConnect) 
	LOG2("%s daveConnectPLC() step 7.\n", dc->iface->name);	
    res= _daveNegPDUlengthRequest(dc, &p1);
    return 0;
}


/*
    Executes part of the dialog necessary to send a message:
*/
int DECL2 _daveSendDialogNLpro(daveConnection * dc, int size)
{
    if (size>5){
	dc->needAckNumber=dc->messageNumber;
	dc->msgOut[dc->iface->ackPos+1]=_daveIncMessageNumber(dc);
    }	
    _daveSendWithPrefix2NLpro(dc, size);
    return 0;
}


/*
    Sends a message and gets ackknowledge:
*/
int DECL2 _daveSendMessageNLpro(daveConnection * dc, PDU * p) {
    if (daveDebug & daveDebugExchange) {
        LOG2("%s enter _daveSendMessageNLpro\n", dc->iface->name);
    }    
    if (_daveSendDialogNLpro(dc, /*2+*/p->hlen+p->plen+p->dlen)) {
	LOG2("%s *** _daveSendMessageMPI error in _daveSendDialog.\n",dc->iface->name);	    		
//	return -1;	
    }	
    if (daveDebug & daveDebugExchange) {
        LOG3("%s _daveSendMessageMPI send done. needAck %x\n", dc->iface->name,dc->needAckNumber);	    
    }	
    return 0;
}


int DECL2 _daveExchangeNLpro(daveConnection * dc, PDU * p) {
    _daveSendMessageNLpro(dc, p);
    dc->AnswLen=0;
    return _daveGetResponseNLpro(dc);
}

int DECL2 _daveGetResponseNLpro(daveConnection *dc) {
    int res;
    if (daveDebug & daveDebugExchange) {
        LOG2("%s _daveGetResponseNLpro receive message.\n", dc->iface->name);	    
    }	
    res = _daveReadMPINLpro(dc->iface,dc->msgIn);
    if (res<0) {
	return res;
    }	
    if (res==0) {
	if (daveDebug & daveDebugPrintErrors) {
	    LOG2("%s *** _daveGetResponseNLpro no answer data.\n", dc->iface->name);
	}        
	return -3;
    }	
    return 0;
}


int DECL2 _daveSendWithPrefixNLpro(daveConnection * dc, uc *b, int size)
{
    uc target[daveMaxRawLen];
//    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
    uc fix[]= {0x4,0x80,0x80,0x0C,0x14,0x14};    
	fix[4]=dc->connectionNumber2; 		// 1/10/05 trying Andrew's patch
	fix[5]=dc->connectionNumber; 		// 1/10/05 trying Andrew's patch
    	memcpy(target,fix,sizeof(fix));
	memcpy(target+sizeof(fix),b,size);
	target[1]|=dc->MPIAdr;
//	target[2]|=dc->iface->localMPI;
	memcpy(target+sizeof(fix),b,size);
	return _daveSendWithCRCNLpro(dc->iface,target,size+sizeof(fix));
}


int DECL2 _daveSendWithPrefix2NLpro(daveConnection * dc, int size)
{
//    uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
    uc fix[]= {0x14,0x80,0x80,0x0C,0x14,0x14};
	fix[4]=dc->connectionNumber2;		// 1/10/05 trying Andrew's patch
	fix[5]=dc->connectionNumber;		// 1/10/05 trying Andrew's patch
	memcpy(dc->msgOut, fix, sizeof(fix));
	dc->msgOut[1]|=dc->MPIAdr;
//	dc->msgOut[2]|=dc->iface->localMPI; //???
///	dc->msgOut[sizeof(fix)]=0xF1;
/*	if (daveDebug & daveDebugPacket)
	    _daveDump("_daveSendWithPrefix2",dc->msgOut,size+sizeof(fix)); */
	return _daveSendWithCRCNLpro(dc->iface, dc->msgOut, size+sizeof(fix));
//    }
    return -1; /* shouldn't happen. */
}

int DECL2 _daveDisconnectPLCNLpro(daveConnection * dc)
{
    int res;
    uc m[]={
        0x80
    };
    uc b1[daveMaxRawLen];
    
    _daveSendSingleNLpro(dc->iface, STX);
    
    res=_daveReadMPINLpro(dc->iface,b1);
    _daveSendWithPrefixNLpro(dc, m, 1);	
    
    res=_daveReadMPINLpro(dc->iface,b1);
/*
    res=_daveReadMPI(dc->iface,b1);
    if (daveDebug & daveDebugConnect) 
	_daveDump("got",b1,10);
    _daveSendSingle(dc->iface, DLE);
*/
    return 0;
}    

/*
    It seems to be better to complete this subroutine even if answers
    from adapter are not as expected.
*/
int DECL2 _daveDisconnectAdapterNLpro(daveInterface * di) {
    int res;
    uc m2[]={
        1,4,2
    };
    
    uc b1[daveMaxRawLen];
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s enter DisconnectAdapter()\n", di->name);	
//    _daveSendSingleNLpro(di, STX);
//    res=_daveReadMPINLpro(di,b1);
/*    if ((res!=1)||(b1[0]!=DLE)) return -1; */
    _daveSendWithCRCNLpro(di, m2, sizeof(m2));		
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s daveDisconnectAdapter() step 1.\n", di->name);	
    res=_daveReadMPINLpro(di, b1);
/*    if ((res!=1)||(b1[0]!=DLE)) return -2; */
/*
    res=_daveReadMPI(di, b1);
*/    
/*    if ((res!=1)||(b1[0]!=STX)) return -3; */
/*
    if (daveDebug & daveDebugInitAdapter) 
	LOG2("%s daveDisconnectAdapter() step 2.\n", di->name);	
    _daveSendSingle(di, DLE);
    dc->iface->ifread(di, b1, daveMaxRawLen);
//    _daveReadChars(di, b1, tmo_normal, daveMaxRawLen);
    _daveSendSingle(di, DLE);
    if (daveDebug & daveDebugInitAdapter) 
	_daveDump("got",b1,10);
*/	
    return 0;	
}

/*
*/
int DECL2 _daveListReachablePartnersNLpro(daveInterface * di,char * buf) {
    uc b1[daveMaxRawLen];
    uc m1[]={1,7,2};
    int res;
    _daveSendWithCRCNLpro(di, m1, sizeof(m1));
    res=_daveReadMPINLpro(di,b1);
//    LOG2("res: %d\n", res);	
    if(135==res){
        memcpy(buf,b1+8,126);
	return 126;
    } else
	return 0;	
}   

_openFunc SCP_open;
_closeFunc SCP_close;
_sendFunc SCP_send;
_receiveFunc SCP_receive;
//_SetHWndMsgFunc SetSinecHWndMsg;
//_SetHWndFunc SetSinecHWnd;
_get_errnoFunc SCP_get_errno;


/*
    Changes: 
    09/09/04  applied patch for variable Profibus speed from Andrew Rostovtsew.
    12/09/04  removed debug printf from daveConnectPLC.
    12/09/04  found and fixed a bug in daveFreeResults(): The result set is provided by the 
	      application and not necessarily dynamic. So we shall not free() it.
    12/10/04  added single bit read/write functions.
    12/12/04  added Timer/Counter read functions.
    12/13/04  changed dumpPDU to dump multiple results from daveFuncRead
    12/15/04  changed comments to pure C style 
    12/15/04  replaced calls to write() with makro daveWriteFile.
    12/15/04  removed daveSendDialog. Was only used in 1 place.
    12/16/04  removed daveReadCharsPPI. It is replaced by daveReadChars.
    12/30/04  Read Timers and Counters from 200 family. These are different internal types!
    01/02/05  Hopefully fixed local MPI<>0.
    01/10/05  Fixed some debug levels in connectPLCMPI
    01/10/05  Splitted daveExchangeMPI into the send and receive parts. They are separately
	      useable when communication is initiated by PLC.
    01/10/05  Code cleanup. Some more things in connectPLC can be done using genaral
	      MPI communication subroutines.
    01/10/05  Partially applied changes from Andrew Rostovtsew for multiple MPI connections
	      over the same adapter.
    01/11/05  Lasts steps in connect PLC can be done with exchangeMPI.
    01/26/05  replaced _daveConstructReadRequest by the sequence prepareReadRequest, addVarToReadRequest
    01/26/05  added multiple write
    02/02/05  added readIBHpacket
    02/05/05  merged in fixes for (some?) ARM processors.
    02/06/05  Code cleanup.
    03/06/05  Fixed disconnectPLC_IBH for MPI adresses other than 2.
    03/12/05  clear answLen before read
    03/12/05  reset templ.packetNumber in connectPLC_IBH. This is necessary to reconnect if the
	      connection has been interrupted.
    03/23/05  fixes for target PPI addresses other than 2.

    04/05/05  reworked error reporting.
    04/06/05  renamed swap functions. When I began libnodave on little endian i386 and Linux, I used
	      used Linux bswap functions. Then users (and later me) tried other systems without
	      a bswap. I also cannot use inline functions in Pascal. So I made my own bswaps. Then 
	      I, made the core of my own swaps dependent of DAVE_LITTLE_ENDIAN conditional to support also
	      bigendien systems. Now I want to rename them from bswap to something else to avoid 
	      confusion for LINUX/UNIX users. The new names are daveSwapIed_16 and daveSwapIed_32. This
	      shall say swap "if endianness differs". While I could have used similar functions 
	      from the network API (htons etc.) on Unix and Win32, they may not be present on
	      e.g. microcontrollers.
	      I highly recommend to use these functions even when writing software for big endian 
	      systems, where they effectively do nothing, as it will make your software portable.
    04/09/05  removed template IBH_MPI header from daveConnection. Much of the information is
	      also available from other fields and the structure is simpler to define in other 
	      languages.
    04/09/05  removed CYGWIN defines. As there were no more differences against LINUX, it should 
	      work with LINUX defines.	      
    04/21/05  renamed LITTLEENDIAN to DAVE_LITTLE_ENDIAN because it seems to conflict with
	      another #define in winsock2.h.	      
    05/09/05  renamed more functions to daveXXX.	      
    05/11/05  added some functions for the convenience of usage with .net or mono. The goal is
	      that the application doesn't have to use members of data structures defined herein
	      directly. This avoids "unsafe" pointer expressions in .net/MONO. It should also ease
	      porting to VB or other languages for which it could be difficult to define byte by
	      byte equivalents of these structures.
    05/12/05  applied some bug fixes from Axel Kinting.
    05/12/05  applied bug fix from Lutz Nitzsche in daveSendISOpacket.
    07/31/05  added message string copying for Visual Basic.
    09/09/05  added code to ignore 7 byte packets from soft PLC 6ES7-4PY00-0YB7 in ISO_TCP.
    09/10/05  added explicit type casts for pointers optained from malloc and calloc.
    09/11/05  added read/write functions for long blocks of data.
    09/24/05  Code clean up:
		- Pointers to basic read/functions allow to redirect these functions, e.g. to libusb.
		- More common code. Only the very fundamental read/functions differ between Linux and Win32.
    09/24/05  added MPI protocol version 3. This is what Step7 talks to MPI adapters and seems
	      to be the only thing the Siemens USB-MPI adapter understands. This adapter is
	      currently only useable under Linux via libusb. 
    09/27/05  added bug fix from Renato Gartmann: freeResults didn't free() the memory used for
	      the result pointer array.
    09/29/05  hopefully fixed superfluos STX in daveConnectPLCMPI2. 
    10/04/05  No there are adapters which want it...
    10/05/05  Added first helper functions to use s7onlinx.dll for transport.
    10/06/05  Added standard protocol specific functions to use s7onlinx.dll for transport.
    10/06/05  renamed LITTLE_ENDIAN to DAVE_LITTLE_ENDIAN because it conflicts with
	      another #define in some headers on some ARM systems.
    10/10/05  change some pointer increments for gcc-4.0.2 compatibility.
    10/18/05  Indroduced a (temporary?) work around to allow applications to use normal sequence 
	      disconnectAdapter/closePort also with s7online.		
    02/20/06  Added code to support NetLink Pro.
    05/15/06  Applied changes from Ken Wenzel for NetLink Pro.
    07/28/06  Added CRC calculation code from Peter Etheridge.
    
    11/21/06  Hope to have fixed PDU length problem with IBHLink reported by Axel Kinting.

    01/04/07  Set last byte of resp09 to don't care as reported by Axel Kinting.
    02/07/08  Removed patch from Keith Harris for RTS line.
Version 0.8.4.5    
    07/10/09  Changed readISOpacket for Win32 to select() before recv().
    07/10/09  Added daveCopyRAMtoROM
    07/11/09  Changed calculation of netLen in doUpload()
Version 0.8.5
    05/17/13  return bad results from daveBuildAndSendPDU() in several places
    05/17/13  removed old code for table based CRC calculation
    05/17/13  added a disconnect function for S7online
    05/17/13  added support for transport of long PDUs split into multiple IBH packets
    05/18/13  added support for transport of long PDUs split into multiple ISO packets
    05/18/13  added routing support
    05/19/13  added communication type (PG, OP, S7-Basic)
    10/19/13  changed general code to be ARM compatible without ARM_FIX
*/
