/*----------------------------------------------------------------------- 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 "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 ECB_BYTES 65536L 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 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: strncpy(buffer,"Illegal ECB function called",maxBuffer); return; case ECBOVERFLOW: strncpy(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; assert(self != NULL); /* Only managers will be allowed to wrestle directly with ECB controllers. */ 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,"%x %x %x %x", out.d, out.e, out.c, out.c); 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; }