diff --git a/src/drv/drvBBMsg.c b/src/drv/drvBBMsg.c new file mode 100644 index 000000000..6d13cbe59 --- /dev/null +++ b/src/drv/drvBBMsg.c @@ -0,0 +1,590 @@ +/* share/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 = BBMSG_DEFAULT_AGE; + + 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 */ +};