- Removed defunct capture command
- Moved some protocolls from sicspsi to kernel as discussed at ANSTO
This commit is contained in:
224
pmacprot.c
Normal file
224
pmacprot.c
Normal file
@ -0,0 +1,224 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
Reference in New Issue
Block a user