522 lines
12 KiB
C
522 lines
12 KiB
C
/*-----------------------------------------------------------------------
|
|
The ECB is a rack controller from Risoe based on a Z80 processor.
|
|
This module provides some functions for communicating with such a
|
|
device. This is the implementation file.
|
|
|
|
WARNING: This contains code which may be endian dependent!
|
|
|
|
copyright: see file COPYRIGHT
|
|
|
|
Mark Koennecke, January 2002, with some bits taken from the original
|
|
tascom code.
|
|
-------------------------------------------------------------------------*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <tcl.h>
|
|
#include <unistd.h>
|
|
#include "fortify.h"
|
|
#include "sics.h"
|
|
#include "ecb.h"
|
|
#include "ecb.i"
|
|
/*------------- private defines and error codes ------------------------*/
|
|
#define ACKN ('\6') /* Acknowledge character */
|
|
#define READ_BYTES 3
|
|
#define WRITE_BYTES 4
|
|
#define DMAREAD 5
|
|
#define ECB_BYTES 65535
|
|
|
|
typedef union { /* Used to swap bytes in 'address' and 'byte_count' */
|
|
unsigned short word;
|
|
struct {
|
|
unsigned char msb; /* Most significant byte */
|
|
unsigned char lsb; /* Least significant byte */
|
|
} b;
|
|
} Swap;
|
|
/* ------- error codes */
|
|
#define ECBILLEGALFUNC -100
|
|
#define ECBOVERFLOW -101
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int ecbSendFunc(pECB self, int func)
|
|
{
|
|
unsigned char function, response;
|
|
int count, status;
|
|
|
|
/*
|
|
send function code
|
|
*/
|
|
function = (unsigned char) func;
|
|
count = 1;
|
|
status = GPIBsend(self->gpib, self->ecbDeviceID, &function, count);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
read acknowledge byte
|
|
*/
|
|
status = GPIBread(self->gpib, self->ecbDeviceID, &response, count);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
if (response != ACKN) {
|
|
self->lastError = ECBILLEGALFUNC;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
int ecbExecute(pECB self, int func, Z80_reg in, Z80_reg * out)
|
|
{
|
|
int count, status;
|
|
|
|
assert(self != NULL);
|
|
assert(self->gpib != NULL);
|
|
self->lastError = 0;
|
|
|
|
/*
|
|
send function code
|
|
*/
|
|
status = ecbSendFunc(self, func);
|
|
if (status <= 0) {
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
send input register
|
|
*/
|
|
count = 4;
|
|
status = GPIBsend(self->gpib, self->ecbDeviceID, &in, count);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
read result register
|
|
*/
|
|
status = GPIBread(self->gpib, self->ecbDeviceID, out, count);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
static int ecbPrepareIO(pECB self, int func, unsigned short address,
|
|
unsigned short byteCount)
|
|
{
|
|
Swap save, adr, count;
|
|
int status, bytes;
|
|
|
|
if (byteCount >= ECB_BYTES) {
|
|
self->lastError = ECBOVERFLOW;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Swap address and byteCount?? This may be a portability issue!
|
|
This may not be necessary on some platforms
|
|
*/
|
|
save.word = address; /* Swap address bytes */
|
|
adr.b.lsb = save.b.msb;
|
|
adr.b.msb = save.b.lsb;
|
|
save.word = byteCount; /* Swap byte count bytes */
|
|
count.b.lsb = save.b.msb;
|
|
count.b.msb = save.b.lsb;
|
|
|
|
status = ecbSendFunc(self, func);
|
|
if (status <= 0) {
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
send address
|
|
*/
|
|
bytes = 2;
|
|
status = GPIBsend(self->gpib, self->ecbDeviceID, &adr, bytes);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
send byte count
|
|
*/
|
|
status = GPIBsend(self->gpib, self->ecbDeviceID, &count, bytes);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
int ecbRead(pECB self, unsigned short address, void *buffer, int byteCount)
|
|
{
|
|
|
|
int status, count;
|
|
|
|
assert(self != NULL);
|
|
assert(self->gpib != NULL);
|
|
self->lastError = 0;
|
|
|
|
status =
|
|
ecbPrepareIO(self, READ_BYTES, address, (unsigned short) byteCount);
|
|
if (status <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
actual read
|
|
*/
|
|
status = GPIBread(self->gpib, self->ecbDeviceID, buffer, byteCount);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int ecbDMARead(pECB self, unsigned short address, void *buffer,
|
|
unsigned short byteCount)
|
|
{
|
|
int status, count;
|
|
|
|
assert(self != NULL);
|
|
assert(self->gpib != NULL);
|
|
self->lastError = 0;
|
|
|
|
status =
|
|
ecbPrepareIO(self, DMAREAD, address, (unsigned short) byteCount);
|
|
if (status <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
usleep(20 * 1000);
|
|
|
|
/*
|
|
actual read
|
|
*/
|
|
status = GPIBread(self->gpib, self->ecbDeviceID, buffer, byteCount);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int ecbWrite(pECB self, unsigned short address,
|
|
void *buffer, int byteCount)
|
|
{
|
|
|
|
int status, count;
|
|
|
|
assert(self != NULL);
|
|
assert(self->gpib != NULL);
|
|
self->lastError = 0;
|
|
|
|
status =
|
|
ecbPrepareIO(self, WRITE_BYTES, address, (unsigned short) byteCount);
|
|
if (status <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
actual read
|
|
*/
|
|
status = GPIBsend(self->gpib, self->ecbDeviceID, buffer, byteCount);
|
|
if (status < 0) {
|
|
self->lastError = status;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
void ecbErrorDescription(pECB self, char *buffer, int maxBuffer)
|
|
{
|
|
int positive;
|
|
|
|
switch (self->lastError) {
|
|
case ECBILLEGALFUNC:
|
|
strlcpy(buffer, "Illegal ECB function called", maxBuffer);
|
|
return;
|
|
case ECBOVERFLOW:
|
|
strlcpy(buffer,
|
|
"You tried to copy more then 64K onto the poor ECB, REFUSED!",
|
|
maxBuffer);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
GPIB error codes
|
|
*/
|
|
GPIBerrorDescription(self->gpib, self->lastError, buffer, maxBuffer);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
void ecbClear(pECB self)
|
|
{
|
|
GPIBclear(self->gpib, self->ecbDeviceID);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
int fixECBError(pECB self)
|
|
{
|
|
int pos;
|
|
|
|
switch (self->lastError) {
|
|
case ECBILLEGALFUNC:
|
|
case ECBOVERFLOW:
|
|
return HWFault;
|
|
}
|
|
|
|
/*
|
|
GPIB error
|
|
*/
|
|
pos = -self->lastError;
|
|
switch (pos) {
|
|
case GPIBEABO:
|
|
return HWRedo;
|
|
default:
|
|
return HWFault;
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
int ECBAction(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pECB self = (pECB) pData;
|
|
Z80_reg in, out;
|
|
char pBuffer[80], pError[132];
|
|
int status, iVal, func = 0;
|
|
|
|
assert(self != NULL);
|
|
|
|
/*
|
|
Only managers will be allowed to wrestle directly with ECB
|
|
controllers.
|
|
*/
|
|
if (!SCinMacro(pCon)) {
|
|
if (!SCMatchRights(pCon, usMugger)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (argc < 2) {
|
|
SCWrite(pCon, "ERROR: keyword required for ECB", eError);
|
|
return 0;
|
|
}
|
|
|
|
strtolower(argv[1]);
|
|
if (strcmp(argv[1], "func") == 0) {
|
|
if (argc < 7) {
|
|
SCWrite(pCon,
|
|
"ERROR: require function code and four register values",
|
|
eError);
|
|
return 0;
|
|
}
|
|
status = Tcl_GetInt(pSics->pTcl, argv[2], &func);
|
|
if (status != TCL_OK) {
|
|
SCWrite(pCon, "ERROR: failed to convert argument to int", eError);
|
|
return 0;
|
|
}
|
|
status = Tcl_GetInt(pSics->pTcl, argv[3], &iVal);
|
|
if (status != TCL_OK) {
|
|
SCWrite(pCon, "ERROR: failed to convert argument to int", eError);
|
|
return 0;
|
|
}
|
|
in.d = (unsigned char) iVal;
|
|
status = Tcl_GetInt(pSics->pTcl, argv[4], &iVal);
|
|
if (status != TCL_OK) {
|
|
SCWrite(pCon, "ERROR: failed to convert argument to int", eError);
|
|
return 0;
|
|
}
|
|
in.e = (unsigned char) iVal;
|
|
status = Tcl_GetInt(pSics->pTcl, argv[5], &iVal);
|
|
if (status != TCL_OK) {
|
|
SCWrite(pCon, "ERROR: failed to convert argument to int", eError);
|
|
return 0;
|
|
}
|
|
in.b = (unsigned char) iVal;
|
|
status = Tcl_GetInt(pSics->pTcl, argv[6], &iVal);
|
|
if (status != TCL_OK) {
|
|
SCWrite(pCon, "ERROR: failed to convert argument to int", eError);
|
|
return 0;
|
|
}
|
|
in.c = (unsigned char) iVal;
|
|
|
|
status = ecbExecute(self, func, in, &out);
|
|
if (status != 1) {
|
|
ecbErrorDescription(self, pBuffer, 79);
|
|
sprintf(pError, "ERROR: %s", pBuffer);
|
|
SCWrite(pCon, pError, eError);
|
|
return 0;
|
|
}
|
|
sprintf(pBuffer, "%d %d %d %d", out.d, out.e, out.b, out.c);
|
|
SCWrite(pCon, pBuffer, eValue);
|
|
return 1;
|
|
} else if (strcmp(argv[1], "clear") == 0) {
|
|
ecbClear(self);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
} else if (strcmp(argv[1], "toint") == 0) {
|
|
sprintf(pBuffer, "%d", argv[2][0]);
|
|
SCWrite(pCon, pBuffer, eValue);
|
|
return 1;
|
|
} else {
|
|
SCWrite(pCon, "ERROR: ECB does not understand keyword", eError);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
int ecbAssignEncoder(pECB self, int encoder, int motorNumber)
|
|
{
|
|
|
|
if (encoder <= 0 || encoder > 3) {
|
|
return 0;
|
|
}
|
|
|
|
self->encoder[encoder - 1] = motorNumber;
|
|
self->encoderDirty = 1;
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int ecbLoadEncoder(pECB self)
|
|
{
|
|
Z80_reg in, out;
|
|
int status;
|
|
|
|
if (self->encoderDirty != 1) {
|
|
/*
|
|
no need to do it if no change
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
if (self->encoder[0] != 0) {
|
|
in.d = self->encoder[0];
|
|
} else {
|
|
in.d = 0;
|
|
}
|
|
if (self->encoder[1] != 0) {
|
|
in.e = self->encoder[1];
|
|
} else {
|
|
in.e = 0;
|
|
}
|
|
if (self->encoder[2] != 0) {
|
|
in.b = self->encoder[2];
|
|
} else {
|
|
in.b = 0;
|
|
}
|
|
in.c = 1;
|
|
|
|
status = ecbExecute(self, 152, in, &out);
|
|
return status;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
void ECBKill(void *pData)
|
|
{
|
|
pECB self = (pECB) pData;
|
|
|
|
if (self == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
Detaching here may be dangerous: If the GPIB has been deleted first,
|
|
this makes a core dump. Best is the GPIB keeps a list of attached
|
|
things and cleans them itself.
|
|
|
|
GPIBdetach(self->gpib,self->ecbDeviceID);
|
|
*/
|
|
if (self->pDes) {
|
|
DeleteDescriptor(self->pDes);
|
|
}
|
|
free(self);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
MakeECB name gpibcontroller boardNo gpib-address
|
|
-----------------------------------------------------------------------*/
|
|
int MakeECB(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pECB self = NULL;
|
|
int address, status, boardNo;
|
|
pGPIB gpib = NULL;
|
|
char pError[132];
|
|
|
|
/*
|
|
we need a name, the GPIB controller and an address on the GPIB bus for
|
|
the ECB as arguments
|
|
*/
|
|
if (argc < 5) {
|
|
SCWrite(pCon, "ERROR: insufficient arguments to MakeECB", eError);
|
|
return 0;
|
|
}
|
|
gpib = FindCommandData(pSics, argv[2], "GPIB");
|
|
if (gpib == NULL) {
|
|
sprintf(pError, "ERROR: no GPIB controller %s found", argv[2]);
|
|
SCWrite(pCon, pError, eError);
|
|
return 0;
|
|
}
|
|
status = Tcl_GetInt(pSics->pTcl, argv[3], &boardNo);
|
|
if (status != TCL_OK) {
|
|
sprintf(pError, "ERROR: failed to convert %s to integer", argv[3]);
|
|
SCWrite(pCon, pError, eError);
|
|
return 0;
|
|
}
|
|
status = Tcl_GetInt(pSics->pTcl, argv[4], &address);
|
|
if (status != TCL_OK) {
|
|
sprintf(pError, "ERROR: failed to convert %s to integer", argv[4]);
|
|
SCWrite(pCon, pError, eError);
|
|
return 0;
|
|
}
|
|
if (address < 0 || address > 30) {
|
|
SCWrite(pCon, "ERROR: invalid GPIB address specified", eError);
|
|
return 0;
|
|
}
|
|
|
|
self = (pECB) malloc(sizeof(ECB));
|
|
if (self == NULL) {
|
|
SCWrite(pCon, "ERROR: no memory to allocate ECB", eError);
|
|
return 0;
|
|
}
|
|
memset(self, 0, sizeof(ECB));
|
|
self->pDes = CreateDescriptor("ECB");
|
|
if (self->pDes == NULL) {
|
|
SCWrite(pCon, "ERROR: no memory to allocate ECB", eError);
|
|
return 0;
|
|
}
|
|
self->gpib = gpib;
|
|
self->boardNumber = boardNo;
|
|
self->ecbAddress = address;
|
|
self->ecbDeviceID = GPIBattach(self->gpib, self->boardNumber,
|
|
self->ecbAddress, 0, 13, 0, 1);
|
|
if (self->ecbDeviceID <= 0) {
|
|
SCWrite(pCon, "ERROR: failed to initialize ECB connection", eError);
|
|
ECBKill(self);
|
|
return 0;
|
|
}
|
|
AddCommand(pSics, argv[1], ECBAction, ECBKill, self);
|
|
return 1;
|
|
}
|