581 lines
14 KiB
C
581 lines
14 KiB
C
/* $Id$
|
||
* DESCRIPTION: Channel access interface for sequencer.
|
||
*
|
||
* Author: Andy Kozubal
|
||
* Date: July, 1991
|
||
*
|
||
* 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:
|
||
* -----------------
|
||
* 03jul91,ajk .
|
||
* 11dec91,ajk Cosmetic changes (comments & names)
|
||
* 13feb92,ajk All seqLog() calls compile only if DEBUG is defined.
|
||
* 28apr92,ajk Implemented new event flag mode.
|
||
* 21may92,ajk Will periodically announce number of connected channels
|
||
* if waiting form some to connect.
|
||
* 17feb93,ajk Implemented code to allow sharing of a single CA task by
|
||
* all state programs. Added seq_disconnect() and ca_import_cancel().
|
||
DB name resolution was moved to seq_main.c.
|
||
* 19feb93,ajk Added patched version of VxWorks 5.02b taskVarDelete().
|
||
*/
|
||
|
||
#define ANSI
|
||
#include "seq.h"
|
||
|
||
LOCAL VOID *getPtrToValue(union db_access_val *, chtype);
|
||
|
||
/*#define DEBUG*/
|
||
|
||
/*
|
||
* seq_connect() - Connect to all database channels through channel access.
|
||
*/
|
||
seq_connect(pSP)
|
||
SPROG *pSP;
|
||
{
|
||
CHAN *pDB;
|
||
int status, i;
|
||
extern VOID seq_conn_handler();
|
||
extern int seqInitialTaskId;
|
||
|
||
pSP->conn_count = 0;
|
||
|
||
/*
|
||
* For each channel: substitute macros, connect to db,
|
||
* & isssue monitor (if monitor request flaf is TRUE).
|
||
*/
|
||
pDB = pSP->channels;
|
||
for (i = 0; i < pSP->nchan; i++)
|
||
{
|
||
#ifdef DEBUG
|
||
printf("connect to \"%s\"\n", pDB->db_name);
|
||
#endif /* DEBUG */
|
||
/* Connect to it */
|
||
status = ca_build_and_connect(
|
||
pDB->db_name, /* DB channel name */
|
||
TYPENOTCONN, /* Don't get initial value */
|
||
0, /* get count (n/a) */
|
||
&(pDB->chid), /* ptr to chid */
|
||
0, /* ptr to value (n/a) */
|
||
seq_conn_handler, /* connection handler routine */
|
||
pDB); /* user ptr is CHAN structure */
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_search");
|
||
ca_task_exit();
|
||
return -1;
|
||
}
|
||
/* Clear monitor indicator */
|
||
pDB->monitored = FALSE;
|
||
|
||
/*
|
||
* Issue monitor request
|
||
*/
|
||
if (pDB->mon_flag)
|
||
{
|
||
seq_pvMonitor(pSP, 0, pDB);
|
||
}
|
||
pDB++;
|
||
}
|
||
ca_flush_io();
|
||
return 0;
|
||
}
|
||
/*
|
||
* seq_event_handler() - Channel access events (monitors) come here.
|
||
* args points to CA event handler argument structure. args.usr contains
|
||
* a pointer to the channel structure (CHAN *).
|
||
*/
|
||
VOID seq_event_handler(args)
|
||
struct event_handler_args args;
|
||
{
|
||
SPROG *pSP;
|
||
CHAN *pDB;
|
||
struct dbr_sts_char *dbr_sts_ptr;
|
||
void *pVal;
|
||
int i, nbytes;
|
||
|
||
/* User arg is ptr to db channel structure */
|
||
pDB = (CHAN *)args.usr;
|
||
|
||
/* Copy value returned into user variable */
|
||
pVal = getPtrToValue((union db_access_val *)args.dbr, pDB->get_type);
|
||
nbytes = pDB->size * pDB->count;
|
||
bcopy(pVal, pDB->var, nbytes);
|
||
|
||
/* Copy status & severity */
|
||
dbr_sts_ptr = (struct dbr_sts_char *)args.dbr;
|
||
pDB->status = dbr_sts_ptr->status;
|
||
pDB->severity = dbr_sts_ptr->severity;
|
||
|
||
/* Process event handling in each state set */
|
||
pSP = pDB->sprog; /* State program that owns this db entry */
|
||
|
||
/* Wake up each state set that is waiting for event processing */
|
||
seq_efSet(pSP, 0, pDB->index + 1);
|
||
|
||
return;
|
||
}
|
||
/* Disconnect all database channels */
|
||
seq_disconnect(pSP)
|
||
SPROG *pSP;
|
||
{
|
||
CHAN *pDB;
|
||
STATUS status;
|
||
int i;
|
||
extern int seqAuxTaskId;
|
||
|
||
/* Did we already disconnect? */
|
||
if (pSP->conn_count < 0)
|
||
return 0;
|
||
|
||
/* Import Channel Access context from the auxillary seq. task */
|
||
ca_import(seqAuxTaskId);
|
||
|
||
pDB = pSP->channels;
|
||
for (i = 0; i < pSP->nchan; i++)
|
||
{
|
||
#ifdef DEBUG
|
||
printf("disconnect \"%s\"\n", pDB->db_name);
|
||
#endif /* DEBUG */
|
||
/* Disconnect this channel */
|
||
status = ca_clear_channel(pDB->chid);
|
||
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_clear_chan");
|
||
ca_task_exit();
|
||
return -1;
|
||
}
|
||
|
||
/* Clear monitor & connect indicators */
|
||
pDB->monitored = FALSE;
|
||
pDB->connected = FALSE;
|
||
|
||
pDB++;
|
||
}
|
||
|
||
pSP->conn_count = -1; /* flag to indicate all disconnected */
|
||
|
||
ca_flush_io();
|
||
|
||
/* Cancel CA import */
|
||
ca_import_cancel(taskIdSelf());
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* seq_conn_handler() - Sequencer connection handler.
|
||
* Called each time a connection is established or broken.
|
||
*/
|
||
VOID seq_conn_handler(args)
|
||
struct connection_handler_args args;
|
||
{
|
||
CHAN *pDB;
|
||
SPROG *pSP;
|
||
|
||
/* User argument is db ptr (specified at ca_search() ) */
|
||
pDB = (CHAN *)ca_puser(args.chid);
|
||
|
||
/* State program that owns this db entry */
|
||
pSP = pDB->sprog;
|
||
|
||
/* Check for connected */
|
||
if (ca_field_type(args.chid) == TYPENOTCONN)
|
||
{
|
||
pDB->connected = FALSE;
|
||
pSP->conn_count--;
|
||
#ifdef DEBUG
|
||
seq_log(pSP, "Channel \"%s\" disconnected\n", pDB->db_name);
|
||
#endif /* DEBUG */
|
||
}
|
||
else
|
||
{
|
||
pDB->connected = TRUE;
|
||
pSP->conn_count++;
|
||
#ifdef DEBUG
|
||
seq_log(pSP, "Channel \"%s\" connected\n", pDB->db_name);
|
||
#endif /* DEBUG */
|
||
if (pDB->count > ca_element_count(args.chid))
|
||
{
|
||
pDB->count = ca_element_count(args.chid);
|
||
#ifdef DEBUG
|
||
seq_log(pSP, "\"%s\": reset count to %d\n",
|
||
pDB->db_name, pDB->count);
|
||
#endif /* DEBUG */
|
||
}
|
||
}
|
||
|
||
/* Wake up each state set that is waiting for event processing */
|
||
seq_efSet(pSP, 0, 0);
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* seq_efSet() - Set an event flag, then wake up each state
|
||
* set that might be waiting on that event flag.
|
||
*/
|
||
VOID seq_efSet(pSP, dummy, ev_flag)
|
||
SPROG *pSP;
|
||
int dummy;
|
||
int ev_flag; /* event flag */
|
||
{
|
||
SSCB *pSS;
|
||
int nss;
|
||
|
||
/* Set this bit (apply resource lock) */
|
||
semTake(pSP->caSemId, WAIT_FOREVER);
|
||
bitSet(pSP->events, ev_flag);
|
||
|
||
/* Check flag against mask for all state sets: */
|
||
for (nss = 0, pSS = pSP->sscb; nss < pSP->nss; nss++, pSS++)
|
||
{
|
||
/* Test for possible event trig based on bit mask for this state */
|
||
if ( (ev_flag == 0) || bitTest(pSS->pMask, ev_flag) )
|
||
{
|
||
semGive(pSS->syncSemId); /* wake up the ss task */
|
||
}
|
||
|
||
}
|
||
|
||
/* Unlock resource */
|
||
semGive(pSP->caSemId);
|
||
}
|
||
|
||
/*
|
||
* seq_efTest() - Test event flag against outstanding events.
|
||
*/
|
||
int seq_efTest(pSP, pSS, ev_flag)
|
||
SPROG *pSP;
|
||
SSCB *pSS;
|
||
int ev_flag; /* event flag */
|
||
{
|
||
return bitTest(pSP->events, ev_flag);
|
||
}
|
||
|
||
/*
|
||
* seq_efClear() - Test event flag against outstanding events, then clear it.
|
||
*/
|
||
int seq_efClear(pSP, pSS, ev_flag)
|
||
SPROG *pSP;
|
||
SSCB *pSS;
|
||
int ev_flag; /* event flag */
|
||
{
|
||
int isSet;
|
||
|
||
isSet = bitTest(pSP->events, ev_flag);
|
||
bitClear(pSP->events, ev_flag);
|
||
return isSet;
|
||
}
|
||
/*
|
||
* seq_pvGet() - Get DB value (uses channel access).
|
||
*/
|
||
seq_pvGet(pSP, pSS, pDB)
|
||
SPROG *pSP; /* ptr to state program */
|
||
SSCB *pSS; /* ptr to current state set */
|
||
CHAN *pDB; /* ptr to channel struct */
|
||
{
|
||
int status, sem_status;
|
||
extern VOID seq_callback_handler();
|
||
|
||
/* Check for channel connected */
|
||
if (!pDB->connected)
|
||
return ECA_DISCONN;
|
||
|
||
/* Flag this pvGet() as not completed */
|
||
pDB->get_complete = FALSE;
|
||
|
||
/* If synchronous pvGet then clear the pvGet pend semaphore */
|
||
if ( !(pSP->options & OPT_ASYNC) )
|
||
{
|
||
pDB->getSemId = pSS->getSemId;
|
||
semTake(pSS->getSemId, NO_WAIT);
|
||
}
|
||
|
||
/* Perform the CA get operation with a callback routine specified */
|
||
status = ca_array_get_callback(
|
||
pDB->get_type, /* db request type */
|
||
pDB->count, /* element count */
|
||
pDB->chid, /* chid */
|
||
seq_callback_handler, /* callback handler */
|
||
pDB); /* user arg */
|
||
|
||
if ( (pSP->options & OPT_ASYNC) || (status != ECA_NORMAL) )
|
||
return status;
|
||
|
||
/* Synchronous pvGet() */
|
||
|
||
ca_flush_io();
|
||
|
||
/* Wait for completion (10s timeout) */
|
||
sem_status = semTake(pSS->getSemId, 600);
|
||
if (sem_status == ERROR)
|
||
status = ECA_TIMEOUT;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* seq_callback_handler() - Sequencer callback handler.
|
||
* Called when a "get" completes.
|
||
* args.usr points to the db structure (CHAN *) for tis channel.
|
||
*/
|
||
VOID seq_callback_handler(args)
|
||
struct event_handler_args args;
|
||
{
|
||
SPROG *pSP;
|
||
CHAN *pDB;
|
||
struct dbr_sts_char *dbr_sts_ptr;
|
||
int i, nbytes;
|
||
void *pVal;
|
||
|
||
/* User arg is ptr to db channel structure */
|
||
pDB = (CHAN *)args.usr;
|
||
|
||
/* Copy value returned into user variable */
|
||
pVal = getPtrToValue((union db_access_val *)args.dbr, pDB->get_type);
|
||
nbytes = pDB->size * pDB->count;
|
||
bcopy(pVal, pDB->var, nbytes);
|
||
|
||
/* Copy status & severity */
|
||
dbr_sts_ptr = (struct dbr_sts_char *)args.dbr;
|
||
pDB->status = dbr_sts_ptr->status;
|
||
pDB->severity = dbr_sts_ptr->severity;
|
||
|
||
/* Set get complete flag */
|
||
pDB->get_complete = TRUE;
|
||
|
||
/* Wake up each state set that is waiting for event processing) */
|
||
pSP = pDB->sprog; /* State program that owns this db entry */
|
||
seq_efSet(pSP, 0, pDB->index + 1);
|
||
|
||
/* If syncronous pvGet then notify pending state set */
|
||
if ( !(pSP->options & OPT_ASYNC) )
|
||
semGive(pDB->getSemId);
|
||
|
||
return;
|
||
}
|
||
|
||
/* Flush outstanding CA requests */
|
||
VOID seq_pvFlush()
|
||
{
|
||
ca_flush_io();
|
||
}
|
||
|
||
/*
|
||
* seq_pvPut() - Put DB value (uses channel access).
|
||
*/
|
||
seq_pvPut(pSP, pSS, pDB)
|
||
SPROG *pSP; /* ptr to state program */
|
||
SSCB *pSS; /* ptr to current state set */
|
||
CHAN *pDB; /* ptr to channel struct */
|
||
{
|
||
int status;
|
||
|
||
if (!pDB->connected)
|
||
return ECA_DISCONN;
|
||
|
||
status = ca_array_put(pDB->put_type, pDB->count,
|
||
pDB->chid, pDB->var);
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
#ifdef DEBUG
|
||
seq_log(pSP, "pvPut on \"%s\" failed (%d)\n",
|
||
pDB->db_name, status);
|
||
seq_log(pSP, " put_type=%d\n", pDB->put_type);
|
||
seq_log(pSP, " size=%d, count=%d\n", pDB->size, pDB->count);
|
||
#endif /* DEBUG */
|
||
}
|
||
|
||
return status;
|
||
}
|
||
/*
|
||
* seq_pvMonitor() - Initiate a monitor on a channel.
|
||
*/
|
||
seq_pvMonitor(pSP, pSS, pDB)
|
||
SPROG *pSP; /* ptr to state program */
|
||
SSCB *pSS; /* ptr to current state set */
|
||
CHAN *pDB; /* ptr to channel struct */
|
||
{
|
||
int status;
|
||
extern VOID seq_event_handler();
|
||
|
||
#ifdef DEBUG
|
||
printf("monitor \"%s\"\n", pDB->db_name);
|
||
#endif /* DEBUG */
|
||
|
||
if (pDB->monitored)
|
||
return;
|
||
|
||
status = ca_add_array_event(
|
||
pDB->get_type, /* requested type */
|
||
pDB->count, /* element count */
|
||
pDB->chid, /* chid */
|
||
seq_event_handler, /* function to call */
|
||
pDB, /* user arg (db struct) */
|
||
pDB->delta, /* pos. delta value */
|
||
pDB->delta, /* neg. delta value */
|
||
pDB->timeout, /* timeout */
|
||
&pDB->evid); /* where to put event id */
|
||
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_add_array_event");
|
||
ca_task_exit(); /* this is serious */
|
||
return;
|
||
}
|
||
ca_flush_io();
|
||
|
||
pDB->monitored = TRUE;
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* seq_pvStopMonitor() - Cancel a monitor
|
||
*/
|
||
seq_pvStopMonitor(pSP, pSS, pDB)
|
||
SPROG *pSP; /* ptr to state program */
|
||
SSCB *pSS; /* ptr to current state set */
|
||
CHAN *pDB; /* ptr to channel struct */
|
||
{
|
||
int status;
|
||
|
||
if (!pDB->monitored)
|
||
return -1;
|
||
|
||
status = ca_clear_event(pDB->evid);
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_clear_event");
|
||
return status;
|
||
}
|
||
|
||
pDB->monitored = FALSE;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* getPtr() - Given ptr to value structure & type, return ptr to value.
|
||
*/
|
||
LOCAL VOID *getPtrToValue(pBuf, dbrType)
|
||
union db_access_val *pBuf;
|
||
chtype dbrType;
|
||
{
|
||
switch (dbrType) {
|
||
case DBR_STRING: return (void *)pBuf->strval;
|
||
case DBR_STS_STRING: return (void *)pBuf->sstrval.value;
|
||
|
||
case DBR_SHORT: return (void *)&pBuf->shrtval;
|
||
case DBR_STS_SHORT: return (void *)&pBuf->sshrtval.value;
|
||
|
||
case DBR_FLOAT: return (void *)&pBuf->fltval;
|
||
case DBR_STS_FLOAT: return (void *)&pBuf->sfltval.value;
|
||
|
||
case DBR_ENUM: return (void *)&pBuf->enmval;
|
||
case DBR_STS_ENUM: return (void *)&pBuf->senmval.value;
|
||
|
||
case DBR_CHAR: return (void *)&pBuf->charval;
|
||
case DBR_STS_CHAR: return (void *)&pBuf->schrval.value;
|
||
|
||
case DBR_LONG: return (void *)&pBuf->longval;
|
||
case DBR_STS_LONG: return (void *)&pBuf->slngval.value;
|
||
|
||
case DBR_DOUBLE: return (void *)&pBuf->doubleval;
|
||
case DBR_STS_DOUBLE: return (void *)&pBuf->sdblval.value;
|
||
default: return NULL;
|
||
}
|
||
}
|
||
#include "memLib.h"
|
||
#include "taskVarLib.h"
|
||
#include "taskLib.h"
|
||
/*******************************************************************************
|
||
* P A T C H E D 5.02b -- allows call from taskDeleteHook routine -- ajk
|
||
* taskVarDelete - remove a task variable from a task
|
||
*
|
||
* This routine removes the specified task variable from the calling
|
||
* task's context. The private value of that variable is lost.
|
||
*
|
||
* RETURNS
|
||
* OK, or
|
||
* ERROR if the calling task does not own the specified task variable.
|
||
*
|
||
* SEE ALSO: taskVarAdd(2), taskVarGet(2), taskVarSet(2)
|
||
*/
|
||
|
||
LOCAL STATUS taskVarDelete (tid, pVar)
|
||
int tid; /* task id whose task variable is to be retrieved */
|
||
int *pVar; /* pointer to task variable to be removed from task */
|
||
|
||
{
|
||
FAST TASK_VAR **ppTaskVar; /* ptr to ptr to next node */
|
||
FAST TASK_VAR *pTaskVar;
|
||
WIND_TCB *pTcb = (WIND_TCB *)tid; /* P A T C H -- ajk 19feb93*/
|
||
|
||
if (pTcb == NULL) /* check that task is valid */
|
||
return (ERROR);
|
||
|
||
/* find descriptor for specified task variable */
|
||
|
||
for (ppTaskVar = &pTcb->pTaskVar;
|
||
*ppTaskVar != NULL;
|
||
ppTaskVar = &((*ppTaskVar)->next))
|
||
{
|
||
pTaskVar = *ppTaskVar;
|
||
|
||
if (pTaskVar->address == pVar)
|
||
{
|
||
/* if active task, replace background value */
|
||
|
||
if (taskIdCurrent == pTcb)
|
||
*pVar = pTaskVar->value;
|
||
|
||
*ppTaskVar = pTaskVar->next;/* delete variable from list */
|
||
|
||
free ((char *)pTaskVar); /* free storage of deleted cell */
|
||
|
||
return (OK);
|
||
}
|
||
}
|
||
|
||
/* specified address is not a task variable for specified task */
|
||
|
||
errnoSet (S_taskLib_TASK_VAR_NOT_FOUND);
|
||
return (ERROR);
|
||
}
|
||
/* Temporary routine to cancel ca_import() -- THIS SHOULD GO INTO CHANNEL ACCESS! */
|
||
ca_import_cancel(tid)
|
||
int tid;
|
||
{
|
||
extern int ca_static;
|
||
int status;
|
||
|
||
status = taskVarDelete(tid, &ca_static);
|
||
if (status != OK)
|
||
{
|
||
logMsg("Seq: taskVarDelete failed for tid = 0x%x\n", tid);
|
||
}
|
||
|
||
return status;
|
||
}
|