/** * This is an asynchronous protocol driver for the Delta-Tau PMAC * series of controllers, connected via TCP/IP. The PMAC needs its * commands in a special purpose data structure, describe below. * As responses, it can send any of the following formats: * datadatadata>CR> * ERRxxx * data * There can be multiple data and errors in a string. However, I wish to * restrict this to processing one command at any time. This driver owes * some insight and little code to the EPICS driver by * Pete Leicester, Diamond. * * ** Before this can be used, I3=2 and I6=1 must be set on the PMAC ** * * copyright: see file COPYRIGHT * * Mark Koennecke, December 2008 * * Introduced a delay on read in order to throttle requests * * Mark Koennecke, June 2011 */ #include #include #include #include #define ETHERNET_DATA_SIZE 1492 #define INPUT_SIZE (ETHERNET_DATA_SIZE+1) /* +1 to allow space to add terminating ACK */ #define STX '\2' #define CTRLB '\2' #define CTRLC '\3' #define ACK '\6' #define CTRLF '\6' #define BELL '\7' #define CTRLG '\7' #define CTRLP '\16' #define CTRLV '\22' #define CTRLX '\24' /* PMAC ethernet command structure */ #pragma pack(1) typedef struct tagEthernetCmd { unsigned char RequestType; unsigned char Request; unsigned short wValue; unsigned short wIndex; unsigned short wLength; /* length of bData */ unsigned char bData[ETHERNET_DATA_SIZE]; } ethernetCmd; #pragma pack() #define ETHERNET_CMD_HEADER ( sizeof(ethernetCmd) - ETHERNET_DATA_SIZE ) /* PMAC ethernet commands - RequestType field */ #define VR_UPLOAD 0xC0 #define VR_DOWNLOAD 0x40 /* PMAC ethernet commands - Request field */ #define VR_PMAC_SENDLINE 0xB0 #define VR_PMAC_GETLINE 0xB1 #define VR_PMAC_FLUSH 0xB3 #define VR_PMAC_GETMEM 0xB4 #define VR_PMAC_SETMEN 0xB5 #define VR_PMAC_SETBIT 0xBA #define VR_PMAC_SETBITS 0xBB #define VR_PMAC_PORT 0xBE #define VR_PMAC_GETRESPONSE 0xBF #define VR_PMAC_READREADY 0xC2 #define VR_CTRL_RESPONSE 0xC4 #define VR_PMAC_GETBUFFER 0xC5 #define VR_PMAC_WRITEBUFFER 0xC6 #define VR_PMAC_WRITEERROR 0xC7 #define VR_FWDOWNLOAD 0xCB #define VR_IPADDRESS 0xE0 /*--------------------------------------------------------------------------- * a private data structurli to keep track of the PMAC *---------------------------------------------------------------------------*/ typedef struct { ethernetCmd cmd; char *ptr; int bytesToWrite; int expectACK; double startTime; } PMACPrivate, *pPMACPrivate; /*---------------------------------------------------------------------------* * wait period before read ----------------------------------------------------------------------------*/ #define READDELAY .05 /*---------------------------------------------------------------------------*/ static int PMACHandler(Ascon * a) { char *data = NULL; int ret, l; char chr; pPMACPrivate priv = NULL; priv = a->private; switch (a->state) { case AsconWriteStart: data = GetCharArray(a->wrBuffer); memset(priv, 0, sizeof(PMACPrivate)); priv->cmd.RequestType = VR_DOWNLOAD; priv->cmd.Request = VR_PMAC_GETRESPONSE; priv->cmd.wValue = 0; priv->cmd.wIndex = 0; priv->cmd.wLength = htons(strlen(data)); /* may be one more */ priv->bytesToWrite = strlen(data) + 1 + ETHERNET_CMD_HEADER; strcpy((char *) priv->cmd.bData, data); priv->expectACK = 1; priv->ptr = (char *) &priv->cmd; a->state = AsconWriting; a->wrPos = 0; break; case AsconWriting: AsconReadGarbage(a->fd); l = priv->bytesToWrite - a->wrPos; ret = AsconWriteChars(a->fd, priv->ptr, l); if (ret < 0) { if (errno != EINTR && errno != EAGAIN) { AsconError(a, "send failed:", errno); } /* * Ooops: which state shall we go to after a write fail? * This seems to retry. */ } else { a->wrPos += ret; if (a->wrPos >= priv->bytesToWrite) { a->state = AsconWriteDone; priv->startTime = DoubleTime(); } else { priv->ptr += ret; } } break; case AsconReadStart: DynStringClear(a->rdBuffer); if(DoubleTime() > priv->startTime + READDELAY){ a->start = DoubleTime(); a->state = AsconReading; } break; case AsconReading: ret = AsconReadChar(a->fd, &chr); if (ret < 0) { /* EINTR means we must retry */ if (errno != EINTR && errno != EAGAIN) { AsconError(a, "AsconReadChar failed:", errno); } return 1; } else if (ret > 0) { a->start = DoubleTime(); if (chr == STX || chr == BELL) { priv->expectACK = 0; return 1; } if (priv->expectACK && chr == ACK) { if (GetDynStringLength(a->rdBuffer) == 0) { DynStringConcat(a->rdBuffer, "ACK"); } DynStringConcatChar(a->rdBuffer, '\0'); a->state = AsconReadDone; break; } if (priv->expectACK == 0 && chr == '\r') { DynStringConcatChar(a->rdBuffer, '\0'); a->state = AsconReadDone; break; } if (DynStringConcatChar(a->rdBuffer, chr) == 0) { AsconError(a, "DynStringConcatChar failed:", ENOMEM); break; } } else if (ret == 0) { if (a->timeout > 0) { if (DoubleTime() - a->start > a->timeout) { AsconError(a, "read timeout", 0); a->state = AsconTimeout; } } return 0; } break; default: return AsconStdHandler(a); } return 1; } /*------------------------------------------------------------------------*/ static int PMACInit(Ascon * a, SConnection * con, int argc, char *argv[]) { pPMACPrivate priv = NULL; priv = calloc(sizeof(PMACPrivate), 1); a->fd = -1; a->state = AsconConnectStart; a->reconnectInterval = 10; a->hostport = strdup(argv[1]); if (argc > 2) { a->timeout = atof(argv[2]); } else { a->timeout = 2.0; /* sec */ } a->private = priv; a->killPrivate = free; return 1; } /*------------------------------------------------------------------------*/ void AddPMACProtocoll() { AsconProtocol *prot = NULL; prot = calloc(sizeof(AsconProtocol), 1); prot->name = strdup("pmac"); prot->init = PMACInit; prot->handler = PMACHandler; AsconInsertProtocol(prot); }