Files
pcas/src/ca/vxWorks_depen.c
T
Jeff Hill 971e71c28d improved the way that socket error numbers are converted to strings,
changed () to (void) in func proto, and fixed missing parameter to
checkConnWatchdogs() bug resulting from this
1999-09-02 21:44:50 +00:00

1014 lines
21 KiB
C

/*
* $Id$
* Author: Jeffrey O. Hill
* hill@luke.lanl.gov
* (505) 665 1831
* Date: 9-93
*
* 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:
* -----------------
* $Log$
* Revision 1.40 1999/07/16 21:08:02 jhill
* fixed bug occurring when connection dropped while waiting to send
*
* Revision 1.39.4.1 1999/07/15 21:07:33 jhill
* fixed bug where client disconnects while waiting to send TCP
*
* Revision 1.39 1998/09/24 21:22:55 jhill
* subtle changes related to efficency when checking connection timers
*
* Revision 1.38 1998/06/16 00:56:03 jhill
* moved code from here to libCom
*
* Revision 1.37 1998/05/08 00:20:56 jhill
* added missing call to freeListCleanup()
*
* Revision 1.36 1998/05/05 16:07:59 jhill
* Use lock macros and task variable in ca_extra_event_labor()
*
* Revision 1.35 1998/04/13 19:14:35 jhill
* fixed task variable problem
*
* Revision 1.34 1998/02/05 22:39:46 jhill
* use inversion safe mutex
*
* Revision 1.33 1997/08/04 23:37:19 jhill
* added beacon anomaly flag init/allow ip 255.255.255.255
*
* Revision 1.31 1997/06/13 09:14:28 jhill
* connect/search proto changes
*
* Revision 1.30 1997/04/29 06:13:49 jhill
* use free lists
*
* Revision 1.29 1997/04/23 17:05:10 jhill
* pc port changes
*
* Revision 1.28 1997/04/10 19:26:19 jhill
* asynch connect, faster connect, ...
*
* Revision 1.27 1996/11/02 00:51:10 jhill
* many pc port, const in API, and other changes
*
* Revision 1.26 1996/09/16 16:39:20 jhill
* local except => except handler
*
* Revision 1.25 1996/08/13 23:16:23 jhill
* removed os specific code
*
* Revision 1.23 1996/08/05 19:18:56 jhill
* better msg for lack of fp
*
* Revision 1.22 1996/06/19 17:59:31 jhill
* many 3.13 beta changes
*
* Revision 1.21 1995/10/18 16:44:36 jhill
* select time out must be greater than a vxWorks tick
*
* Revision 1.20 1995/10/12 01:35:31 jhill
* Moved cac_mux_io() to iocinf.c
*
* Revision 1.19 1995/08/22 00:27:58 jhill
* added cvs style mod log
*
*
*/
#include "callback.h"
#include "iocinf.h"
#include "remLib.h"
#include "dbEvent.h"
#include "freeList.h"
LOCAL void ca_repeater_task();
LOCAL void ca_task_exit_tcb(WIND_TCB *ptcb);
LOCAL void ca_extra_event_labor(void *pArg);
LOCAL int cac_os_depen_exit_tid (struct CA_STATIC *pcas, int tid);
LOCAL int cac_add_task_variable (struct CA_STATIC *ca_temp);
LOCAL void deleteCallBack(CALLBACK *pcb);
LOCAL void ca_check_for_fp();
LOCAL int event_import(int tid);
/*
* order of ops is important here
*
* NOTE: large OS dependent SYFREQ might cause an overflow
* NOTE: POLLDELAY must be less than TICKSPERSEC
*/
#define POLLDELAY 50 /* milli sec */
#define TICKSPERSEC 1000 /* milli sec per sec */
#define LOCALTICKS ((sysClkRateGet()*POLLDELAY)/TICKSPERSEC)
/*
* cac_gettimeval()
*/
void cac_gettimeval(struct timeval *pt)
{
unsigned long sec;
unsigned long current;
static unsigned long rate;
static unsigned long last;
static unsigned long offset;
static SEM_ID sem;
int status;
assert(pt);
/*
* Lazy Init
*/
if(!rate){
sem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
rate = sysClkRateGet();
assert(rate);
assert(sem!=NULL);
}
else {
status = semTake(sem, WAIT_FOREVER);
assert(status==OK);
}
current = tickGet();
if(current<last){
offset += (~0UL)/rate;
}
last = current;
status = semGive(sem);
assert(status==OK);
sec = current/rate;
pt->tv_sec = sec + offset;
pt->tv_usec = ((current-sec*rate)*USEC_PER_SEC)/rate;
}
/*
* cac_block_for_io_completion()
*/
void cac_block_for_io_completion(struct timeval *pTV)
{
struct timeval itimeout;
int ticks;
int rate = sysClkRateGet();
#ifdef NOASYNCRECV
cac_mux_io(pTV, TRUE);
#else
/*
* flush outputs
* (recv occurs in another thread)
*/
itimeout.tv_usec = 0;
itimeout.tv_sec = 0;
cac_mux_io (&itimeout, TRUE);
ticks = (int) (pTV->tv_sec*rate + (pTV->tv_usec*rate)/USEC_PER_SEC);
ticks = min(LOCALTICKS, ticks);
semTake (io_done_sem, ticks);
/*
* force a time update because we are not
* going to get one with a nill timeout in
* ca_mux_io()
*/
cac_gettimeval (&ca_static->currentTime);
#endif
}
/*
* os_specific_sg_create()
*/
void os_specific_sg_create(CASG *pcasg)
{
pcasg->sem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
assert (pcasg->sem);
}
/*
* os_specific_sg_delete()
*/
void os_specific_sg_delete(CASG *pcasg)
{
int status;
status = semDelete(pcasg->sem);
assert (status == OK);
}
/*
* os_specific_sg_io_complete()
*/
void os_specific_sg_io_complete(CASG *pcasg)
{
int status;
status = semGive(pcasg->sem);
assert (status == OK);
}
/*
* cac_block_for_sg_completion()
*/
void cac_block_for_sg_completion(CASG *pcasg, struct timeval *pTV)
{
struct timeval itimeout;
int ticks;
int rate = sysClkRateGet();
#ifdef NOASYNCRECV
cac_mux_io(pTV, TRUE);
#else
/*
* flush outputs
* (recv occurs in another thread)
*/
itimeout.tv_usec = 0;
itimeout.tv_sec = 0;
cac_mux_io(&itimeout, TRUE);
ticks = (int) (pTV->tv_sec*rate + (pTV->tv_usec*rate)/USEC_PER_SEC);
ticks = min(LOCALTICKS, ticks);
semTake (pcasg->sem, ticks);
/*
* force a time update because we are not
* going to get one with a nill timeout in
* ca_mux_io()
*/
cac_gettimeval (&ca_static->currentTime);
#endif
}
/*
* CAC_ADD_TASK_VARIABLE()
*/
LOCAL int cac_add_task_variable (struct CA_STATIC *ca_temp)
{
static char ca_installed;
TVIU *ptviu;
int status;
ca_check_for_fp();
# ifdef DEBUG
ca_printf("CAC: adding task variable\n");
# endif
status = taskVarGet(VXTHISTASKID, (int *)&ca_static);
if(status == OK){
ca_printf("task variable already installed?\n");
return ECA_INTERNAL;
}
/*
* only one delete hook for all CA tasks
*/
if (vxTas(&ca_installed)) {
/*
*
* This guarantees that vxWorks's task
* variable delete (at task exit) handler runs
* after the CA task exit handler. This ensures
* that CA's task variable will still exist
* when it's exit handler runs.
*
* That is taskVarInit() must run prior to your
* taskDeleteHookAdd() if you use a task variable
* in a task exit handler.
*/
# ifdef DEBUG
ca_printf("CAC: adding delete hook\n");
# endif
status = taskVarInit();
if (status != OK)
return ECA_INTERNAL;
status = taskDeleteHookAdd((FUNCPTR)ca_task_exit_tcb);
if (status != OK) {
ca_printf("ca_init_task: could not add CA delete routine\n"
);
return ECA_INTERNAL;
}
}
ptviu = (TVIU *) calloc(1, sizeof(*ptviu));
if(!ptviu){
return ECA_INTERNAL;
}
ptviu->tid = taskIdSelf();
status = taskVarAdd(VXTHISTASKID, (int *)&ca_static);
if (status != OK){
free(ptviu);
return ECA_INTERNAL;
}
/*
* Care is taken not to set the value of the
* ca static task variable until after it has been installed
* so that we dont change the value for other tasks.
*/
ca_static = ca_temp;
ellAdd(&ca_temp->ca_taskVarList, &ptviu->node);
return ECA_NORMAL;
}
/*
* CA_TASK_EXIT_TCBX()
*
*/
LOCAL void ca_task_exit_tcb(WIND_TCB *ptcb)
{
int status;
struct CA_STATIC *ca_temp;
# ifdef DEBUG
ca_printf("CAC: entering the exit handler %x\n", ptcb);
# endif
/*
* NOTE: vxWorks provides no method at this time
* to get the task id from the ptcb so I am
* taking the liberty of using the ptcb as
* the task id - somthing which may not be true
* on future releases of vxWorks
*/
ca_temp = (struct CA_STATIC *)
taskVarGet((int)ptcb, (int *) &ca_static);
if (ca_temp == (struct CA_STATIC *) ERROR){
return;
}
/*
* Add CA task var for the exit handler
*/
if (ptcb != taskIdCurrent) {
status = taskVarAdd (VXTHISTASKID, (int *)&ca_static);
if (status == ERROR){
ca_printf ("Couldnt add task var to CA exit task\n");
return;
}
}
/*
* normal CA shut down
*/
cac_os_depen_exit_tid (ca_temp, (int) ptcb);
if (ptcb != taskIdCurrent) {
status = taskVarDelete(VXTHISTASKID, (int *)&ca_static);
if (status == ERROR){
ca_printf ("Couldnt remove task var from CA exit task\n");
return;
}
}
}
/*
* ca_task_initialize()
*/
int ca_task_initialize ()
{
struct CA_STATIC *pcas;
char name[15];
int status;
pcas = (struct CA_STATIC *)
taskVarGet (taskIdSelf(), (int *)&ca_static);
if (pcas != (struct CA_STATIC *) ERROR) {
return ECA_NORMAL;
}
pcas = (struct CA_STATIC *)
calloc(1, sizeof(*pcas));
if (!pcas) {
return ECA_ALLOCMEM;
}
ellInit (&pcas->ca_local_chidlist);
ellInit (&pcas->ca_lcl_buff_list);
ellInit (&pcas->ca_taskVarList);
ellInit (&pcas->ca_putNotifyQue);
freeListInitPvt (&pcas->ca_dbMonixFreeList,
db_sizeof_event_block ()+sizeof(struct pending_event),256);
pcas->ca_tid = taskIdSelf ();
pcas->ca_client_lock = semMCreate (SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY);
assert (pcas->ca_client_lock);
pcas->ca_putNotifyLock = semMCreate (SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY);
assert (pcas->ca_putNotifyLock);
pcas->ca_io_done_sem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
assert (pcas->ca_io_done_sem);
pcas->ca_blockSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
assert (pcas->ca_blockSem);
status = cac_add_task_variable (pcas);
if (status != ECA_NORMAL) {
freeListCleanup (pcas->ca_dbMonixFreeList);
semDelete (pcas->ca_client_lock);
semDelete (pcas->ca_putNotifyLock);
semDelete (pcas->ca_io_done_sem);
semDelete (pcas->ca_blockSem);
return status;
}
status = ca_os_independent_init ();
if (status != ECA_NORMAL){
ca_import_cancel (taskIdSelf());
freeListCleanup (pcas->ca_dbMonixFreeList);
semDelete (pcas->ca_client_lock);
semDelete (pcas->ca_putNotifyLock);
semDelete (pcas->ca_io_done_sem);
semDelete (pcas->ca_blockSem);
return status;
}
evuser = (void *) db_init_events();
assert(evuser);
status = db_add_extra_labor_event(
(struct event_user *)evuser,
ca_extra_event_labor,
pcas);
assert(status==0);
strcpy(name, "EV ");
strncat(
name,
taskName(VXTHISTASKID),
sizeof(name) - strlen(name) - 1);
status = db_start_events(
(struct event_user *)evuser,
name,
event_import,
taskIdSelf(),
-1); /* higher priority */
assert(status == OK);
return ECA_NORMAL;
}
/*
* ca_task_exit ()
*/
int epicsShareAPI ca_task_exit (void)
{
struct CA_STATIC *pcas;
pcas = (struct CA_STATIC *)
taskVarGet (taskIdSelf(), (int *)&ca_static);
if (pcas == (struct CA_STATIC *) ERROR) {
return ECA_NOCACTX;
}
return cac_os_depen_exit_tid (pcas, 0);
}
/*
* cac_os_depen_exit_tid ()
*/
LOCAL int cac_os_depen_exit_tid (struct CA_STATIC *pcas, int tid)
{
int status;
ciu chix;
miu monix;
TVIU *ptviu;
CALLBACK *pcb;
# ifdef DEBUG
ca_printf("CAC: entering the exit routine %x %x\n",
tid, pcas);
# endif
ca_static = pcas;
LOCK;
/*
* stop the socket recv task
* !! only after we get the LOCK here !!
*/
if (taskIdVerify (pcas->recv_tid)==OK) {
taskwdRemove (pcas->recv_tid);
/*
* dont do a task suspend if the exit handler is
* running for this task - it botches vxWorks -
*/
if (pcas->recv_tid != tid) {
status = taskSuspend (pcas->recv_tid);
if (status<0) {
ca_printf ("taskSuspend() error = %s\n",
strerror (errno) );
}
}
}
/*
* Cancel all local events
* (and put call backs)
*
*/
chix = (ciu) & pcas->ca_local_chidlist.node;
while ( (chix = (ciu) chix->node.next) ) {
while ( (monix = (miu) ellGet(&chix->eventq)) ) {
/*
* temp release lock so that the event task
* can finish
*/
UNLOCK;
status = db_cancel_event((struct event_block *)(monix+1));
LOCK;
assert(status == OK);
freeListFree (ca_static->ca_dbMonixFreeList, monix);
}
if (chix->ppn) {
CACLIENTPUTNOTIFY *ppn;
ppn = chix->ppn;
if (ppn->busy) {
dbNotifyCancel (&ppn->dbPutNotify);
}
free (ppn);
}
}
/*
* set ca_static for access.c
* (run this before deleting the task variable)
*/
ca_process_exit();
/*
* cancel task vars for other tasks so this
* only runs once
*
* This is done only after all oustanding events
* are drained so that the event task still has a CA context
*
* db_close_events() does not require a CA context.
*/
while ( (ptviu = (TVIU *)ellGet(&pcas->ca_taskVarList)) ) {
# ifdef DEBUG
ca_printf("CAC: removing task var %x\n", ptviu->tid);
# endif
status = taskVarDelete(
ptviu->tid,
(int *)&ca_static);
if(status<0){
ca_printf(
"tsk var del err %x\n",
ptviu->tid);
}
free(ptviu);
}
if (taskIdVerify(pcas->recv_tid)==OK) {
if(pcas->recv_tid != tid){
pcb = (CALLBACK *) calloc(1,sizeof(*pcb));
if (pcb) {
pcb->callback = deleteCallBack;
pcb->priority = priorityHigh;
pcb->user = (void *) pcas->recv_tid;
callbackRequest (pcb);
}
}
}
/*
* All local events must be canceled prior to closing the
* local event facility
*/
status = db_close_events(pcas->ca_evuser);
assert(status == OK);
ellFree(&pcas->ca_lcl_buff_list);
/*
* remove local chid blocks, paddr blocks, waiting ev blocks
*/
ellFree(&pcas->ca_local_chidlist);
freeListCleanup(pcas->ca_dbMonixFreeList);
/*
* remove semaphores here so that ca_process_exit()
* can use them.
*/
assert(semDelete(pcas->ca_client_lock)==OK);
assert(semDelete(pcas->ca_putNotifyLock)==OK);
assert(semDelete(pcas->ca_io_done_sem)==OK);
assert(semDelete(pcas->ca_blockSem)==OK);
ca_static = NULL;
free ((char *)pcas);
return ECA_NORMAL;
}
/*
* deleteCallBack()
*/
LOCAL void deleteCallBack(CALLBACK *pcb)
{
int status;
status = taskDelete ((int)pcb->user);
if (status < 0) {
ca_printf ("CAC: tak delete at exit failed: %s\n",
strerror(errno));
}
free (pcb);
}
/*
*
* localUserName() - for vxWorks
*
* o Indicates failure by setting ptr to nill
*/
char *localUserName()
{
char *pTmp;
int length;
char pName[MAX_IDENTITY_LEN];
remCurIdGet(pName, NULL);
length = strlen(pName)+1;
pTmp = malloc(length);
if(!pTmp){
return NULL;
}
strncpy(pTmp, pName, length-1);
pTmp[length-1] = '\0';
return pTmp;
}
/*
* event_import()
*/
LOCAL int event_import(int tid)
{
int status;
status = ca_import(tid);
if (status==ECA_NORMAL) {
return OK;
}
else {
return ERROR;
}
}
/*
* CA_IMPORT()
*
*
*/
int ca_import (int tid)
{
int status;
struct CA_STATIC *pcas;
TVIU *ptviu;
ca_check_for_fp();
/*
* just return success if they have already done
* a ca import for this task
*/
pcas = (struct CA_STATIC *)
taskVarGet (taskIdSelf(), (int *)&ca_static);
if (pcas != (struct CA_STATIC *) ERROR) {
return ECA_NORMAL;
}
pcas = (struct CA_STATIC *)
taskVarGet (tid, (int *)&ca_static);
if (pcas == (struct CA_STATIC *) ERROR) {
ca_static = NULL;
return ECA_NOCACTX;
}
ptviu = (TVIU *) calloc (1, sizeof(*ptviu));
if(!ptviu){
ca_static = NULL;
return ECA_ALLOCMEM;
}
status = taskVarAdd (VXTHISTASKID, (int *)&ca_static);
if (status == ERROR){
ca_static = NULL;
free (ptviu);
return ECA_ALLOCMEM;
}
/*
* Care is taken not to set the value of the
* ca static task variable until after it has been installed
* so that we dont change the value for other tasks.
*/
ca_static = pcas;
ptviu->tid = taskIdSelf();
LOCK;
ellAdd(&ca_static->ca_taskVarList, &ptviu->node);
UNLOCK;
return ECA_NORMAL;
}
/*
* CA_IMPORT_CANCEL()
*/
int ca_import_cancel(int tid)
{
int status;
TVIU *ptviu;
struct CA_STATIC *pcas;
if (tid == taskIdSelf()) {
pcas = NULL;
}
else {
pcas = ca_static;
}
/*
* Attempt to attach to the specified context
*/
ca_static = (struct CA_STATIC *)
taskVarGet(tid, (int *)&ca_static);
if (ca_static == (struct CA_STATIC *) ERROR){
ca_static = pcas;
return ECA_NOCACTX;
}
LOCK;
ptviu = (TVIU *) ellFirst(&ca_static->ca_taskVarList);
while (ptviu) {
if(ptviu->tid == tid){
break;
}
ptviu = (TVIU *) ellNext(&ptviu->node);
}
if(!ptviu){
ca_static = pcas;
UNLOCK;
return ECA_NOCACTX;
}
ellDelete(&ca_static->ca_taskVarList, &ptviu->node);
free(ptviu);
UNLOCK;
status = taskVarDelete(tid, (void *)&ca_static);
assert (status == OK);
ca_static = pcas;
return ECA_NORMAL;
}
/*
* ca_check_for_fp()
*/
LOCAL void ca_check_for_fp()
{
int options;
assert(taskOptionsGet(taskIdSelf(), &options) == OK);
if (!(options & VX_FP_TASK)) {
genLocalExcep (ECA_NEEDSFP, taskName(taskIdSelf()));
}
}
/*
* ca_spawn_repeater()
*
* Spawn the repeater task as needed
*/
void ca_spawn_repeater()
{
int status;
status = taskSpawn(
CA_REPEATER_NAME,
CA_REPEATER_PRI,
CA_REPEATER_OPT,
CA_REPEATER_STACK,
(FUNCPTR)ca_repeater_task,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0);
if (status==ERROR){
SEVCHK(ECA_NOREPEATER, NULL);
}
}
/*
* ca_repeater_task()
*/
LOCAL void ca_repeater_task()
{
taskwdInsert((int)taskIdCurrent, NULL, NULL);
ca_repeater();
}
/*
* CA_EXTRA_EVENT_LABOR
*/
LOCAL void ca_extra_event_labor (void *pArg)
{
int status;
CACLIENTPUTNOTIFY *ppnb;
struct event_handler_args args;
while (TRUE) {
/*
* independent lock used here in order to
* avoid any possibility of blocking
* the database (or indirectly blocking
* one client on another client).
*/
semTake (ca_static->ca_putNotifyLock, WAIT_FOREVER);
ppnb = (CACLIENTPUTNOTIFY *) ellGet (&ca_static->ca_putNotifyQue);
semGive (ca_static->ca_putNotifyLock);
/*
* break to loop exit
*/
if (!ppnb) {
break;
}
/*
* setup arguments and call user's function
*/
args.usr = ppnb->caUserArg;
args.chid = ppnb->dbPutNotify.usrPvt;
args.type = ppnb->dbPutNotify.dbrType;
args.count = ppnb->dbPutNotify.nRequest;
args.dbr = NULL;
if (ppnb->dbPutNotify.status) {
if (ppnb->dbPutNotify.status == S_db_Blocked) {
args.status = ECA_PUTCBINPROG;
}
else {
args.status = ECA_PUTFAIL;
}
}
else {
args.status = ECA_NORMAL;
}
LOCK;
(*ppnb->caUserCallback) (args);
UNLOCK;
ppnb->busy = FALSE;
}
/*
* wakeup the TCP thread if it is waiting for a cb to complete
*/
status = semGive (ca_static->ca_blockSem);
if (status != OK) {
logMsg ("CA block sem corrupted\n", 0, 0, 0, 0, 0, 0);
}
}
/*
* CAC_RECV_TASK()
*
*/
void cac_recv_task(int tid)
{
struct timeval timeout;
int status;
int count;
taskwdInsert((int) taskIdCurrent, NULL, NULL);
status = ca_import(tid);
SEVCHK(status, "cac_recv_task()");
/*
* once started, does not exit until
* ca_task_exit() is called.
*/
while (TRUE) {
#ifdef NOASYNCRECV
taskDelay (60);
#else
/*
* first check for pending recv's with a
* zero time out so that
* 1) flow control works correctly (and)
* 2) we queue up sends resulting from recvs properly
*/
while (TRUE) {
CLR_CA_TIME(&timeout);
count = cac_select_io (&timeout, CA_DO_RECVS);
if (count<=0) {
break;
}
ca_process_input_queue ();
}
manage_conn ();
/*
* flush out all pending io prior to blocking
*
* NOTE: this must be longer than one vxWorks
* tick or we will infinite loop
*/
timeout.tv_usec = (4/*ticks*/ * USEC_PER_SEC)/sysClkRateGet();
timeout.tv_sec = 0;
count = cac_select_io (&timeout, CA_DO_RECVS|CA_DO_SENDS);
ca_process_input_queue ();
checkConnWatchdogs (TRUE);
#endif
}
}
/*
* caSetDefaultPrintfHandler()
*
* replace the default printf handler with a
* vxWorks specific one that calls logMsg ()
* so that:
*
* o messages go to the log file
* o messages dont get intermixed
*/
void caSetDefaultPrintfHandler ()
{
ca_static->ca_printf_func = epicsVprintf;
}