/* base/src/drv $Id$ */ /* * Author: John Winans * Date: 05-21-92 * EPICS BITBUS driver for message based I/O * * Experimental Physics and Industrial Control System (EPICS) * * Copyright 1991, the Regents of the University of California, * and the University of Chicago Board of Governors. * * This software was produced under U.S. Government contracts: * (W-7405-ENG-36) at the Los Alamos National Laboratory, * and (W-31-109-ENG-38) at Argonne National Laboratory. * * Initial development by: * The Controls and Automation Group (AT-8) * Ground Test Accelerator * Accelerator Technology Division * Los Alamos National Laboratory * * Co-developed with * The Controls and Computing Group * Accelerator Systems Division * Advanced Photon Source * Argonne National Laboratory * * Modification Log: * ----------------- * .01 08-10-92 jrw created * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int drvBBMsgDebug = 0; extern struct drvBitBusEt drvBitBus; /****************************************************************************** * ******************************************************************************/ #define BBMSGLINK_PRI 50 #define BBMSGLINK_OPT VX_FP_TASK|VX_STDIO #define BBMSGLINK_STACK 5000 /****************************************************************************** * ******************************************************************************/ static long report() { printf("Report for BITBUS message driver\n"); return(OK); } /****************************************************************************** * ******************************************************************************/ static long init(pparms) msgDrvIniParm *pparms; { if (drvBBMsgDebug) printf("Init for BITBUS message driver\n"); return(OK); } /****************************************************************************** * * This function is called to allocate any structures needed to hold * device-specific data that is added to the msgXact structure. * * It is also responsible for filling in the msgXact.phwpvt pointer. * ******************************************************************************/ static long genXact(p) msgDrvGenXParm *p; { long stat = -1; char message[100]; drvBBMsgLink *pdrvBBMsgLink; struct dpvtBitBusHead *pdpvtBitBusHead; if (p->plink->type != BITBUS_IO) { p->pmsgXact->prec->pact = TRUE; sprintf("%s: invalid device type in devSup.ascii (%d)\n", p->pmsgXact->prec->name, p->plink->type); errMessage(S_db_badField, message); return(ERROR); } if (drvBBMsgDebug) printf("BBMsg genXact entered for link %d, bug %d, port %d, parm %s\n", p->plink->value.bitbusio.link, p->plink->value.bitbusio.node, p->plink->value.bitbusio.port, p->plink->value.bitbusio.parm); p->pmsgXact->phwpvt = p->pmsgXact->pparmBlock->pmsgHwpvt; /* try to find the hwpvt structure for the required link, bug, and port */ while ((p->pmsgXact->phwpvt != NULL) && (stat == -1)) { pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgXact->phwpvt->pmsgLink->p); if ((pdrvBBMsgLink->link == p->plink->value.bitbusio.link) && (pdrvBBMsgLink->node == p->plink->value.bitbusio.node) && (pdrvBBMsgLink->port == p->plink->value.bitbusio.port)) { if (pdrvBBMsgLink->pparmBlock != p->pmsgXact->pparmBlock) { sprintf(message, "%s: Two different devices on same BBMsg port\n", p->pmsgXact->prec->name); errMessage(S_db_badField, message); return(ERROR); } else stat = 0; /* Found the correct hwpvt structure */ } else p->pmsgXact->phwpvt = p->pmsgXact->phwpvt->next; } if (stat != 0) { /* Could not find a msgHwpvt for the right link, create a new one */ if ((p->pmsgXact->phwpvt = drvMsg_genHwpvt(p->pmsgXact->pparmBlock, p->plink)) == NULL) return(ERROR); } /* Set again in case hwpvt was just allocated (and to make compiler happy) */ pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgXact->phwpvt->pmsgLink->p); p->pmsgXact->callback.callback = NULL; p->pmsgXact->psyncSem = NULL; /* Create the skeleton bitbus driver message */ pdpvtBitBusHead = (struct dpvtBitBusHead *) malloc(sizeof(struct dpvtBitBusHead)); (struct dpvtBitBusHead *)(p->pmsgXact->p) = pdpvtBitBusHead; pdpvtBitBusHead->finishProc = NULL; pdpvtBitBusHead->psyncSem = malloc(sizeof(SEM_ID)); *(pdpvtBitBusHead->psyncSem) = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY); pdpvtBitBusHead->link = pdrvBBMsgLink->link; pdpvtBitBusHead->txMsg.route = BB_STANDARD_TX_ROUTE; pdpvtBitBusHead->txMsg.node = pdrvBBMsgLink->node; pdpvtBitBusHead->txMsg.tasks = BB_MSG_TASK; pdpvtBitBusHead->txMsg.cmd = 0xff; pdpvtBitBusHead->rxMsg.data = (unsigned char *) malloc(BB_MAX_DAT_LEN); /* in case I read before write */ pdpvtBitBusHead->rxMsg.cmd = 0; pdpvtBitBusHead->rxMsg.length = BB_MSG_HEADER_SIZE; pdpvtBitBusHead->status = BB_OK; pdpvtBitBusHead->rxMaxLen = 0; pdpvtBitBusHead->ageLimit = 0; if (sscanf(p->plink->value.bitbusio.parm,"%d", &(p->pmsgXact->parm)) != 1) { p->pmsgXact->prec->pact = TRUE; sprintf("%s: invalid parameter string >%s<\n", p->pmsgXact->prec->name, p->plink->value.bitbusio.parm); errMessage(S_db_badField, message); return(ERROR); } return(OK); } /****************************************************************************** * * This function is called to allocate any structures needed to hold * device-specific data that is added to the msgHwpvt structure. * * It is also responsible for filling in the msgHwpvt.pmsgLink pointer. * ******************************************************************************/ static long genHwpvt(p) msgDrvGenHParm *p; { int stat = ERROR; if (drvBBMsgDebug) printf("BBMSG genHwpvt entered\n"); p->pmsgHwpvt->pmsgLink = drvBBMSGBlock.pmsgLink; while ((p->pmsgHwpvt->pmsgLink != NULL) && (stat == ERROR)) { if ((((drvBBMsgLink *)(p->pmsgHwpvt->pmsgLink->p))->link == p->plink->value.bitbusio.link) && (((drvBBMsgLink *)(p->pmsgHwpvt->pmsgLink->p))->node == p->plink->value.bitbusio.node) && (((drvBBMsgLink *)(p->pmsgHwpvt->pmsgLink->p))->port == p->plink->value.bitbusio.port)) stat = OK; else p->pmsgHwpvt->pmsgLink = p->pmsgHwpvt->pmsgLink->next; } if (stat != OK) { if ((p->pmsgHwpvt->pmsgLink = drvMsg_genLink(p->pparmBlock, p->plink)) == NULL) return(ERROR); } return(OK); } /****************************************************************************** * * This function is called to allocate any structures needed to hold * device-specific data that is added to the msgLink structure. * ******************************************************************************/ static long genLink(p) msgDrvGenLParm *p; { char name[20]; char message[100]; drvBBMsgLink *pdrvBBMsgLink; if (drvBBMsgDebug) printf("BBMsg genLink, link = %d, node = %d\n", p->plink->value.bitbusio.link, p->plink->value.bitbusio.node, p->plink->value.bitbusio.port); switch (p->op) { case MSG_GENLINK_CREATE: /* BUG -- verify that the link and node numbers are within range! */ if (p->plink->value.bitbusio.port & (~DD_MSG_PORT)) { sprintf(message, "drvBBMsg: port number %d out of range\n", p->plink->value.bitbusio.port); errMessage(S_db_badField, message); return(ERROR); } if ((p->pmsgLink->p = malloc(sizeof(drvBBMsgLink))) == NULL) return(ERROR); pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgLink->p); pdrvBBMsgLink->link = p->plink->value.bitbusio.link; pdrvBBMsgLink->node = p->plink->value.bitbusio.node; pdrvBBMsgLink->port = p->plink->value.bitbusio.port; pdrvBBMsgLink->pparmBlock = p->pparmBlock; return(OK); case MSG_GENLINK_ABORT: if (drvBBMsgDebug) printf("BBMsg genLink: called with abort status\n"); pdrvBBMsgLink = (drvBBMsgLink *)(p->pmsgLink->p); /* BUG - free(p->pmsgLink->p); Don't forget to take it out of the list */ return(OK); } return(ERROR); } /****************************************************************************** * * This function is called to allow the device to indicate that a device-related * event has occurred. If an event had occurred, it would have to place the * address of a valid transaction structure in pxact. This xact structure * will then be processed by the message link task as indicated by the * return code of this function. * * MSG_EVENT_NONE = no event has occurred, pxact not filled in. * MSG_EVENT_WRITE = event occurred, process the writeOp assoc'd w/pxact. * MSG_EVENT_READ = event occurred, process the readOp assoc'd w/pxact. * * Note that MSG_EVENT_READ only makes sense when returned with a transaction * that has deferred readback specified for its readOp function. Because if * it is NOT a deferred readback, it will be done immediately after the writeOp. * Even if that writeOp was due to a MSG_EVENT_WRITE from this function. * ******************************************************************************/ static long checkEvent(p) msgChkEParm *p; { return(MSG_EVENT_NONE); } /****************************************************************************** * * This IOCTL function is used to handle non-I/O related operations required * to operate the RS-MSG interface. * ******************************************************************************/ static long command(p) ioctlCommand *p; { msgXact *pxact; struct dpvtBitBusHead *pdpvtBitBusHead; drvBBMsgLink *pdrvBBMsgLink; switch (p->cmd) { case IOCTLMSG_BREAK: /* send a BREAK signal to the serial port. */ /* Build a break message to send */ pxact = (msgXact *) (p->pparm); pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; pdrvBBMsgLink = (drvBBMsgLink *)(pxact->phwpvt->pmsgLink->p); pdpvtBitBusHead->status = BB_OK; pdpvtBitBusHead->rxMaxLen = 0; pdpvtBitBusHead->txMsg.length = BB_MSG_HEADER_SIZE; pdpvtBitBusHead->txMsg.cmd = BB_MSG_BREAK + pdrvBBMsgLink->port; pdpvtBitBusHead->rxMsg.cmd = 0; if (drvBBMsgDebug > 7) { printf("drvBBMsg.control (tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); } (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); if ((pdpvtBitBusHead->status == BB_OK) && !(pdpvtBitBusHead->rxMsg.cmd & 1)) return(OK); else return(ERROR); } if (drvBBMsgDebug) printf("drvBBMsg.control: bad command 0x%02.2X\n", p->cmd); return(ERROR); } /****************************************************************************** * * This function is used to write a string of characters out to an BB_MSG * device. * * Note that the BUGs reply to the write messages with the first 13 bytes * of the machine's response string (if there is one.) This response string * will be left in the pdpvtBitBusHead->rxMsg buffer when the drvRead() * function is called later. * ******************************************************************************/ static long drvWrite(pxact, pwrParm) msgXact *pxact; msgStrParm *pwrParm; { int len; drvBBMsgLink *pdrvBBMsgLink = (drvBBMsgLink *)(pxact->phwpvt->pmsgLink->p); struct dpvtBitBusHead *pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; unsigned char loopLen; /* build a bitbus message and call the bitbus driver */ if (pwrParm->len == -1) len = strlen(pwrParm->buf); else len = pwrParm->len; pdpvtBitBusHead->txMsg.data = (unsigned char *) pwrParm->buf; pdpvtBitBusHead->status = BB_OK; pdpvtBitBusHead->rxMaxLen = BB_MAX_MSG_LENGTH; pdpvtBitBusHead->txMsg.cmd = BB_MSG_CMD + pdrvBBMsgLink->port; pdpvtBitBusHead->rxMsg.cmd = 0; while (len && (pdpvtBitBusHead->status == BB_OK) && !(pdpvtBitBusHead->rxMsg.cmd & 1)) { if (len > BB_MAX_DAT_LEN) loopLen = BB_MAX_DAT_LEN; else loopLen = len; pdpvtBitBusHead->txMsg.length = loopLen + BB_MSG_HEADER_SIZE; if (softBBMsg) { printf("drvBBMsg.drvWrite(tx L%d N%d P%d):\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); } else { (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); /* do it */ semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); /* wait for completion */ if (drvBBMsgDebug > 10) { printf("drvBBMsg.drvWrite (tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); } } pdpvtBitBusHead->txMsg.data += loopLen; len -= loopLen; } if ((pdpvtBitBusHead->status != BB_OK) || (pdpvtBitBusHead->rxMsg.cmd & 1)) { if (drvBBMsgDebug) printf("BBMsg write error on link %d, node %d, port %d, driver %02.2X, message %02.2X\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port, pdpvtBitBusHead->status, pdpvtBitBusHead->rxMsg.cmd); pxact->status = XACT_IOERR; } return(pxact->status); } /****************************************************************************** * * This function is used to read a string of characters from an RS-Msg device. * * It is assumed that pdpvtBitBusHead->rxMsg has already got up to 13 * bytes of the response data already filled in (left there by the prior * call to drvWrite().) If a call to this function is made when there is * garbage in the initial pdpvtBitBusHead->rxMsg buffer, set the length field * to 0 and the status field to BB_OK. * ******************************************************************************/ static long drvRead(pxact, prdParm) msgXact *pxact; msgStrParm *prdParm; { drvBBMsgLink *pdrvBBMsgLink = (drvBBMsgLink *)(pxact->phwpvt->pmsgLink->p); struct dpvtBitBusHead *pdpvtBitBusHead = (struct dpvtBitBusHead *) pxact->p; int len = prdParm->len; int loopLen = 0; unsigned char *tmp = pdpvtBitBusHead->rxMsg.data; /* check if data already loaded into pdpvtBitBusHead->rxMsg */ if ((pdpvtBitBusHead->rxMsg.length > BB_MSG_HEADER_SIZE) && (pdpvtBitBusHead->status == BB_OK)) { loopLen = pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; if (prdParm->len < loopLen) loopLen = prdParm->len; if (drvBBMsgDebug > 9) { printf("drvBBMsg.drvRead (rx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); } bcopy(pdpvtBitBusHead->rxMsg.data, prdParm->buf, loopLen); len -= loopLen; } pdpvtBitBusHead->txMsg.length = BB_MSG_HEADER_SIZE; pdpvtBitBusHead->txMsg.cmd = BB_MSG_CMD + pdrvBBMsgLink->port; pdpvtBitBusHead->rxMsg.data = (unsigned char *) (&(prdParm->buf[loopLen])); if (!(pdpvtBitBusHead->rxMsg.cmd & BB_MSG_EOI)) { /* If we did not hit EOI yet, keep reading */ while (len && (pdpvtBitBusHead->status == BB_OK)) { if (len > BB_MAX_DAT_LEN) loopLen = BB_MAX_DAT_LEN; else loopLen = len; pdpvtBitBusHead->rxMaxLen = loopLen + BB_MSG_HEADER_SIZE; if (softBBMsg) { printf("drvBB232.drvRead(tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); pdpvtBitBusHead->status = BB_232_EOI; } else { if (drvBBMsgDebug > 10) { printf("drvBB232.drvRead(tx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->txMsg)); } (*(drvBitBus.qReq))(pdpvtBitBusHead, BB_Q_LOW); /* do it */ semTake(*(pdpvtBitBusHead->psyncSem), WAIT_FOREVER); /* wait for completion */ if (drvBBMsgDebug > 9) { printf("drvBB232.drvRead (rx L%d N%d P%d)\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); } } if (pdpvtBitBusHead->rxMsg.cmd & (~BB_232_EOI)) { /* Something is wrong... */ if (drvBBMsgDebug > 6) { printf("drvBB232.drvRead (rx L%d N%d P%d) Error response from BUG\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); drvBitBusDumpMsg(&(pdpvtBitBusHead->rxMsg)); } pxact->status = XACT_IOERR; break; /* note that we will fall thru to the null-term code */ } if (pdpvtBitBusHead->rxMsg.cmd & BB_232_EOI) { pdpvtBitBusHead->rxMsg.data += pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; len -= pdpvtBitBusHead->rxMsg.length - BB_MSG_HEADER_SIZE; break; } pdpvtBitBusHead->rxMsg.data += loopLen; len -= loopLen; } } /* Null-terminate the input string (if there is room) */ if (len) *(pdpvtBitBusHead->rxMsg.data) = '\0'; else pxact->status = XACT_LENGTH; /* Note that the following error check, takes priority over a length error */ if (pdpvtBitBusHead->status != BB_OK) { if (drvBBMsgDebug) printf("BB232 read error on link %d, node %d, port %d\n", pdrvBBMsgLink->link, pdrvBBMsgLink->node, pdrvBBMsgLink->port); pxact->status = XACT_IOERR; } /* BUG -- this can print unterminated strings !! */ if (drvBBMsgDebug > 7) printf("drvRead: got >%s<\n", prdParm->buf); pdpvtBitBusHead->rxMsg.length = BB_MSG_HEADER_SIZE; /* in case keep reading */ return(pxact->status); } /****************************************************************************** * * This handles any IOCTL calls made to BB-MSG devices. * ******************************************************************************/ static long drvIoctl(cmd, pparm) int cmd; void *pparm; { switch (cmd) { case MSGIOCTL_REPORT: return(report()); case MSGIOCTL_INIT: return(init(pparm)); case MSGIOCTL_INITREC: if (drvBBMsgDebug) printf("drvBBMsgBlock.drvIoctl got a MSGIOCTL_INITREC request!\n"); return(ERROR); case MSGIOCTL_GENXACT: return(genXact(pparm)); case MSGIOCTL_GENHWPVT: return(genHwpvt(pparm)); case MSGIOCTL_GENLINK: return(genLink(pparm)); case MSGIOCTL_CHECKEVENT: return(checkEvent(pparm)); case MSGIOCTL_COMMAND: return(command(pparm)); } return(ERROR); } msgDrvBlock drvBBMsgBlock = { "BBMSG", /* taskName */ BBMSGLINK_PRI, /* taskPri */ BBMSGLINK_OPT, /* taskOpt */ BBMSGLINK_STACK, /* taskStack */ NULL, /* pmsgLink (linked list header) */ drvWrite, /* drvWrite */ drvRead, /* drvRead */ drvIoctl /* drvIoctl */ };