Files
epics-base/src/ca/vxWorks_depen.c
1997-06-13 09:14:29 +00:00

976 lines
21 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* $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.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);
#else
/*
* flush outputs
* (recv occurs in another thread)
*/
itimeout.tv_usec = 0;
itimeout.tv_sec = 0;
cac_mux_io(&itimeout);
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);
#else
/*
* flush outputs
* (recv occurs in another thread)
*/
itimeout.tv_usec = 0;
itimeout.tv_sec = 0;
cac_mux_io(&itimeout);
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;
}
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;
}
}
}
/*
* cac_os_depen_init()
*/
int cac_os_depen_init(struct CA_STATIC *pcas)
{
char name[15];
int status;
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);
assert(pcas->ca_client_lock);
pcas->ca_event_lock = semMCreate(SEM_DELETE_SAFE);
assert(pcas->ca_event_lock);
pcas->ca_putNotifyLock = semMCreate(SEM_DELETE_SAFE);
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) {
return status;
}
status = ca_os_independent_init ();
if (status != ECA_NORMAL){
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;
}
/*
* cac_os_depen_exit ()
*/
void cac_os_depen_exit (struct CA_STATIC *pcas)
{
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_event_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;
}
/*
* caHostFromInetAddr()
*/
void caHostFromInetAddr(
const struct in_addr *pnet_addr,
char *pBuf,
unsigned size)
{
char str[INET_ADDR_LEN];
inet_ntoa_b(*pnet_addr, str);
/*
* force null termination
*/
strncpy(pBuf, str, size-1);
pBuf[size-1] = '\0';
return;
}
/*
* 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;
}
ptviu = (TVIU *) calloc(1, sizeof(*ptviu));
if(!ptviu){
return ECA_ALLOCMEM;
}
pcas = (struct CA_STATIC *)
taskVarGet(tid, (int *)&ca_static);
if (pcas == (struct CA_STATIC *) ERROR){
free(ptviu);
return ECA_NOCACTX;
}
ca_static = NULL;
status = taskVarAdd(VXTHISTASKID, (int *)&ca_static);
if (status == ERROR){
free(ptviu);
return ECA_ALLOCMEM;
}
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 CA_STATIC *pcas;
struct event_handler_args args;
pcas = pArg;
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(pcas->ca_putNotifyLock, WAIT_FOREVER);
ppnb = (CACLIENTPUTNOTIFY *)ellGet(&pcas->ca_putNotifyQue);
semGive(pcas->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;
}
LOCKEVENTS;
(*ppnb->caUserCallback) (args);
UNLOCKEVENTS;
ppnb->busy = FALSE;
}
/*
* wakeup the TCP thread if it is waiting for a cb to complete
*/
status = semGive(pcas->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, NULL);
/*
* once started, does not exit until
* ca_task_exit() is called.
*/
while(TRUE){
#ifdef NOASYNCRECV
taskDelay(60);
#else
cac_clean_iiu_list();
/*
* 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) {
LD_CA_TIME (0.0, &timeout);
count = cac_select_io(&timeout, CA_DO_RECVS);
if (count<=0) {
break;
}
ca_process_input_queue();
}
/*
* 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();
manage_conn();
#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;
}