/*----------------------------------------------------------------------- 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 #include #include #include #include #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; }