Files
sics/pmacprot.c
Koennecke Mark e577d9c313 - Removed defunct capture command
- Moved some protocolls from sicspsi to kernel as discussed at ANSTO
2015-04-20 09:08:34 +02:00

225 lines
6.3 KiB
C

/**
* 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:
* data<CR>data<CR>data>CR><ACK>
* <BELL>ERRxxx<CR>
* <STX>data<CR>
* 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 <errno.h>
#include <ascon.h>
#include <ascon.i>
#include <dynstring.h>
#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);
}