diff --git a/src/libCom/osi/devLib.c b/src/libCom/osi/devLib.c new file mode 100644 index 000000000..a8e9e5fe3 --- /dev/null +++ b/src/libCom/osi/devLib.c @@ -0,0 +1,1022 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.c - support for allocation of common device resources */ +/* @(#)$Id$*/ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + * + * NOTES: + * .01 06-14-93 joh needs devAllocInterruptVector() routine + */ + +static char *sccsID = "@(#) $Id$"; + +#include +#include +#include + +#include "dbDefs.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "devLib.h" + +LOCAL ELLLIST addrAlloc[atLast]; +LOCAL ELLLIST addrFree[atLast]; + +LOCAL size_t addrLast[atLast] = { + 0xffff, + 0xffffff, + 0xffffffff, + 0xffffff + }; + +LOCAL unsigned addrHexDig[atLast] = { + 4, + 6, + 8, + 6 + }; + +LOCAL long addrFail[atLast] = { + S_dev_badA16, + S_dev_badA24, + S_dev_badA32, + S_dev_badA24 + }; + +LOCAL epicsMutexId addrListLock; +LOCAL char devLibInitFlag; + +const char *epicsAddressTypeName[] + = { + "VME A16", + "VME A24", + "VME A32", + "ISA" + }; + +typedef struct{ + ELLNODE node; + const char *pOwnerName; + volatile void *pPhysical; + /* + * first, last is used here instead of base, size + * so that we can store a block that is the maximum size + * available in type size_t + */ + size_t begin; + size_t end; +}rangeItem; + +/* + * These routines are not exported + */ + +LOCAL long devLibInit(void); + +LOCAL long addrVerify( + epicsAddressType addrType, + size_t base, + size_t size); + +LOCAL long blockFind ( + epicsAddressType addrType, + const rangeItem *pRange, + /* size needed */ + size_t requestSize, + /* n ls bits zero in base addr */ + unsigned alignment, + /* base address found */ + size_t *pFirst); + +LOCAL void report_conflict( + epicsAddressType addrType, + size_t base, + size_t size, + const char *pOwnerName); + +LOCAL void report_conflict_device( + epicsAddressType addrType, + const rangeItem *pRange); + +LOCAL void devInsertAddress( + ELLLIST *pRangeList, + rangeItem *pNewRange); + +LOCAL long devListAddressMap( + ELLLIST *pRangeList); + +LOCAL long devCombineAdjacentBlocks( + ELLLIST *pRangeList, + rangeItem *pRange); + +LOCAL long devInstallAddr( + rangeItem *pRange, /* item on the free list to be split */ + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress); + +#define SUCCESS 0 + +/* + * devRegisterAddress() + */ +long devRegisterAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress) +{ + rangeItem *pRange; + long s; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s){ + return s; + } + } + + s = addrVerify (addrType, base, size); + if (s) { + return s; + } + + if (size == 0) { + return S_dev_lowValue; + } + +#ifdef DEBUG + printf ("Req Addr 0X%X Size 0X%X\n", base, size); +#endif + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst(&addrFree[addrType]); + while (TRUE) { + if (pRange->begin > base) { + pRange = NULL; +# ifdef DEBUG + printf ("Unable to locate a free block\n"); + devListAddressMap (&addrFree[addrType]); +# endif + break; + } + else if (base + (size - 1) <= pRange->end) { +# ifdef DEBUG + printf ("Found free block Begin 0X%X End 0X%X\n", + pRange->begin, pRange->end); +# endif + break; + } + + pRange = (rangeItem *) ellNext (&pRange->node); + } + epicsMutexUnlock(addrListLock); + + if (pRange==NULL) { + report_conflict (addrType, base, size, pOwnerName); + return S_dev_addressOverlap; + } + + s = devInstallAddr( + pRange, /* item on the free list to be split */ + pOwnerName, + addrType, + base, + size, + ppPhysicalAddress); + + return s; +} + +/* + * devReadProbe() + * + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVirtualOS->pDevReadProbe) (wordSize, ptr, pValue); +} + +/* + * devWriteProbe + * + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVirtualOS->pDevWriteProbe) (wordSize, ptr, pValue); +} + +/* + * devInstallAddr() + */ +LOCAL long devInstallAddr ( + rangeItem *pRange, /* item on the free list to be split */ + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress) +{ + volatile void *pPhysicalAddress; + rangeItem *pNewRange; + size_t reqEnd = base + (size-1); + long status; + + /* + * does it start below the specified block + */ + if (base < pRange->begin) { + return S_dev_badArgument; + } + + /* + * does it end above the specified block + */ + if (reqEnd > pRange->end) { + return S_dev_badArgument; + } + + /* + * always map through the virtual os in case the memory + * management is set up there + */ + status = (*pdevLibVirtualOS->pDevMapAddr) (addrType, 0, base, + size, &pPhysicalAddress); + if (status) { + errPrintf (status, __FILE__, __LINE__, "%s base=0X%X size = 0X%X", + epicsAddressTypeName[addrType], base, size); + return status; + } + + /* + * set the callers variable if the pointer is supplied + */ + if (ppPhysicalAddress) { + *ppPhysicalAddress = pPhysicalAddress; + } + + /* + * does it start at the beginning of the block + */ + if (pRange->begin == base) { + if (pRange->end == reqEnd) { + epicsMutexMustLock(addrListLock); + ellDelete(&addrFree[addrType], &pRange->node); + epicsMutexUnlock(addrListLock); + free ((void *)pRange); + } + else { + pRange->begin = base + size; + } + } + /* + * does it end at the end of the block + */ + else if (pRange->end == reqEnd) { + pRange->end = base-1; + } + /* + * otherwise split the item on the free list + */ + else { + + pNewRange = (rangeItem *) calloc (1, sizeof(*pRange)); + if(!pNewRange){ + return S_dev_noMemory; + } + + pNewRange->begin = base + size; + pNewRange->end = pRange->end; + pNewRange->pOwnerName = ""; + pNewRange->pPhysical = NULL; + pRange->end = base - 1; + + /* + * add the node after the old item on the free list + * (blocks end up ordered by address) + */ + epicsMutexMustLock(addrListLock); + ellInsert(&addrFree[addrType], &pRange->node, &pNewRange->node); + epicsMutexUnlock(addrListLock); + } + + /* + * allocate a new address range entry and add it to + * the list + */ + pNewRange = (rangeItem *)calloc (1, sizeof(*pRange)); + if (!pNewRange) { + return S_dev_noMemory; + } + + pNewRange->begin = base; + pNewRange->end = reqEnd; + pNewRange->pOwnerName = pOwnerName; + pNewRange->pPhysical = pPhysicalAddress; + + devInsertAddress (&addrAlloc[addrType], pNewRange); + + return SUCCESS; +} + +/* + * report_conflict() + */ +LOCAL void report_conflict ( + epicsAddressType addrType, + size_t base, + size_t size, + const char *pOwnerName +) +{ + const rangeItem *pRange; + + errPrintf ( + S_dev_addressOverlap, + __FILE__, + __LINE__, + "%10s 0X%08X - OX%08X Requested by %s", + epicsAddressTypeName[addrType], + base, + base+size-1, + pOwnerName); + + pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); + while (pRange) { + + if (pRange->begin <= base + (size-1) && pRange->end >= base) { + report_conflict_device (addrType, pRange); + } + + pRange = (rangeItem *) pRange->node.next; + } +} + +/* + * report_conflict_device() + */ +LOCAL void report_conflict_device(epicsAddressType addrType, const rangeItem *pRange) +{ + errPrintf ( + S_dev_identifyOverlap, + __FILE__, + __LINE__, + "%10s 0X%08X - 0X%08X Owned by %s", + epicsAddressTypeName[addrType], + pRange->begin, + pRange->end, + pRange->pOwnerName); +} + +/* + * devUnregisterAddress() + */ +long devUnregisterAddress( + epicsAddressType addrType, + size_t baseAddress, + const char *pOwnerName) +{ + rangeItem *pRange; + int s; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s) { + return s; + } + } + + s = addrVerify (addrType, baseAddress, 1); + if (s != SUCCESS) { + return s; + } + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); + while (pRange) { + if (pRange->begin == baseAddress) { + break; + } + if (pRange->begin > baseAddress) { + pRange = NULL; + break; + } + pRange = (rangeItem *) ellNext(&pRange->node); + } + epicsMutexUnlock(addrListLock); + + if (!pRange) { + return S_dev_addressNotFound; + } + + if (strcmp(pOwnerName,pRange->pOwnerName)) { + s = S_dev_addressOverlap; + errPrintf ( + s, + __FILE__, + __LINE__, + "unregister address for %s at 0X%X failed because %s owns it", + pOwnerName, + baseAddress, + pRange->pOwnerName); + return s; + } + + epicsMutexMustLock(addrListLock); + ellDelete (&addrAlloc[addrType], &pRange->node); + epicsMutexUnlock(addrListLock); + + pRange->pOwnerName = ""; + devInsertAddress (&addrFree[addrType], pRange); + s = devCombineAdjacentBlocks (&addrFree[addrType], pRange); + if(s){ + errMessage (s, NULL); + return s; + } + + return SUCCESS; +} + +/* + * devCombineAdjacentBlocks() + */ +LOCAL long devCombineAdjacentBlocks( + ELLLIST *pRangeList, + rangeItem *pRange) +{ + rangeItem *pBefore; + rangeItem *pAfter; + + pBefore = (rangeItem *) ellPrevious (&pRange->node); + pAfter = (rangeItem *) ellNext (&pRange->node); + + /* + * combine adjacent blocks + */ + if (pBefore) { + if (pBefore->end == pRange->begin-1) { + epicsMutexMustLock(addrListLock); + pRange->begin = pBefore->begin; + ellDelete (pRangeList, &pBefore->node); + epicsMutexUnlock(addrListLock); + free ((void *)pBefore); + } + } + + if (pAfter) { + if (pAfter->begin == pRange->end+1) { + epicsMutexMustLock(addrListLock); + pRange->end = pAfter->end; + ellDelete (pRangeList, &pAfter->node); + epicsMutexUnlock(addrListLock); + free((void *)pAfter); + } + } + + return SUCCESS; +} + +/* + * devInsertAddress() + */ +LOCAL void devInsertAddress( +ELLLIST *pRangeList, +rangeItem *pNewRange) +{ + rangeItem *pBefore; + rangeItem *pAfter; + + epicsMutexMustLock(addrListLock); + pAfter = (rangeItem *) ellFirst (pRangeList); + while (pAfter) { + if (pNewRange->end < pAfter->begin) { + break; + } + pAfter = (rangeItem *) ellNext (&pAfter->node); + } + + if (pAfter) { + pBefore = (rangeItem *) ellPrevious (&pAfter->node); + ellInsert (pRangeList, &pBefore->node, &pNewRange->node); + } + else { + ellAdd (pRangeList, &pNewRange->node); + } + epicsMutexUnlock(addrListLock); +} + +/* + * devAllocAddress() + */ +long devAllocAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t size, + unsigned alignment, /* n ls bits zero in base addr*/ + volatile void **pLocalAddress) +{ + int s; + rangeItem *pRange; + size_t base; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s){ + return s; + } + } + + s = addrVerify (addrType, 0, size); + if(s){ + return s; + } + + if (size == 0) { + return S_dev_lowValue; + } + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst (&addrFree[addrType]); + while (pRange) { + if ((pRange->end - pRange->begin) + 1 >= size){ + s = blockFind ( + addrType, + pRange, + size, + alignment, + &base); + if (s==SUCCESS) { + break; + } + } + pRange = (rangeItem *) pRange->node.next; + } + epicsMutexUnlock(addrListLock); + + if(!pRange){ + s = S_dev_deviceDoesNotFit; + errMessage(s, epicsAddressTypeName[addrType]); + return s; + } + + s = devInstallAddr (pRange, pOwnerName, addrType, base, + size, NULL); + + return s; +} + +/* + * addrVerify() + * + * care has been taken here not to overflow type size_t + */ +LOCAL long addrVerify(epicsAddressType addrType, size_t base, size_t size) +{ + if (addrType>=atLast) { + return S_dev_uknAddrType; + } + + if (size == 0) { + return addrFail[addrType]; + } + + if (size-1 > addrLast[addrType]) { + return addrFail[addrType]; + } + + if (base > addrLast[addrType]) { + return addrFail[addrType]; + } + + if (size - 1 > addrLast[addrType] - base) { + return addrFail[addrType]; + } + + return SUCCESS; +} + +/* + * devLibInit() + */ +LOCAL long devLibInit (void) +{ + rangeItem *pRange; + int i; + + + if(devLibInitFlag) return(SUCCESS); + if(!pdevLibVirtualOS) { + epicsPrintf ("pdevLibVirtualOS is NULL\n"); + return S_dev_internal; + } + + if (NELEMENTS(addrAlloc) != NELEMENTS(addrFree)) { + return S_dev_internal; + } + + addrListLock = epicsMutexMustCreate(); + + epicsMutexMustLock(addrListLock); + for (i=0; ipOwnerName = ""; + pRange->pPhysical = NULL; + pRange->begin = 0; + pRange->end = addrLast[i]; + ellAdd (&addrFree[i], &pRange->node); + } + epicsMutexUnlock(addrListLock); + devLibInitFlag = TRUE; + return pdevLibVirtualOS->pDevInit(); +} + +/* + * devAddressMap() + */ +long devAddressMap(void) +{ + return devListAddressMap(addrAlloc); +} + +/* + * devListAddressMap() + */ +LOCAL long devListAddressMap(ELLLIST *pRangeList) +{ + rangeItem *pri; + int i; + long s; + + if (!devLibInitFlag) { + s = devLibInit (); + if (s) { + return s; + } + } + + epicsMutexMustLock(addrListLock); + for (i=0; ibegin, + addrHexDig[i], + (unsigned long) pri->end, + pri->pPhysical, + pri->pOwnerName); + pri = (rangeItem *) ellNext (&pri->node); + } + } + epicsMutexUnlock(addrListLock); + + return SUCCESS; +} + + +/* + * + * blockFind() + * + * Find unoccupied block in a large block + * + */ +LOCAL long blockFind ( + epicsAddressType addrType, + const rangeItem *pRange, + /* size needed */ + size_t requestSize, + /* n ls bits zero in base addr */ + unsigned alignment, + /* base address found */ + size_t *pBase) +{ + int s = SUCCESS; + size_t bb; + size_t mask; + size_t newBase; + size_t newSize; + + /* + * align the block base + */ + mask = devCreateMask (alignment); + newBase = pRange->begin; + if ( mask & newBase ) { + newBase |= mask; + newBase++; + } + + if ( requestSize == 0) { + return S_dev_badRequest; + } + + /* + * align size of block + */ + newSize = requestSize; + if (mask & newSize) { + newSize |= mask; + newSize++; + } + + if (pRange->end - pRange->begin + 1 < newSize) { + return S_dev_badRequest; + } + + bb = pRange->begin; + while (bb <= (pRange->end + 1) - newSize) { + s = devNoResponseProbe (addrType, bb, newSize); + if (s==SUCCESS) { + *pBase = bb; + return SUCCESS; + } + bb += newSize; + } + + return s; +} + +/* + * devNoResponseProbe() + */ +long devNoResponseProbe (epicsAddressType addrType, + size_t base, size_t size) +{ + volatile void *pPhysical; + size_t probe; + size_t byteNo; + unsigned wordSize; + union { + char charWord; + short shortWord; + int intWord; + long longWord; + }allWordSizes; + long s; + + if (!devLibInitFlag) { + s = devLibInit(); + if (s) { + return s; + } + } + + byteNo = 0; + while (byteNo < size) { + + probe = base + byteNo; + + /* + * for all word sizes + */ + for (wordSize=1; wordSize<=sizeof(allWordSizes); wordSize <<= 1) { + /* + * only check naturally aligned words + */ + if ( (probe&(wordSize-1)) != 0 ) { + break; + } + + if (byteNo+wordSize>size) { + break; + } + + /* + * every byte in the block must + * map to a physical address + */ + s = (*pdevLibVirtualOS->pDevMapAddr) (addrType, 0, probe, wordSize, &pPhysical); + if (s!=SUCCESS) { + return s; + } + + /* + * verify that no device is present + */ + s = (*pdevLibVirtualOS->pDevReadProbe)(wordSize, pPhysical, &allWordSizes); + if (s==SUCCESS) { + return S_dev_addressOverlap; + } + } + byteNo++; + } + return SUCCESS; +} + +/* + * devConnectInterrupt () + * + * !! DEPRECATED !! + */ +long devConnectInterrupt( +epicsInterruptType intType, +unsigned vectorNumber, +void (*pFunction)(), +void *parameter) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVirtualOS->pDevConnectInterruptVME) (vectorNumber, + pFunction, parameter); + default: + return S_dev_uknIntType; + } +} + +/* + * + * devDisconnectInterrupt() + * + * !! DEPRECATED !! + */ +long devDisconnectInterrupt( +epicsInterruptType intType, +unsigned vectorNumber, +void (*pFunction)() +) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVirtualOS->pDevDisconnectInterruptVME) (vectorNumber, + pFunction); + default: + return S_dev_uknIntType; + } +} + +/* + * devEnableInterruptLevel() + * + * !! DEPRECATED !! + */ +long devEnableInterruptLevel( +epicsInterruptType intType, +unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVirtualOS->pDevEnableInterruptLevelVME) (level); + default: + return S_dev_uknIntType; + } +} + +/* + * devDisableInterruptLevel() + * + * !! DEPRECATED !! + */ +long devDisableInterruptLevel ( +epicsInterruptType intType, +unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVirtualOS->pDevDisableInterruptLevelVME) (level); + default: + return S_dev_uknIntType; + } +} + +/* + * locationProbe + * + * !! DEPRECATED !! + */ +long locationProbe (epicsAddressType addrType, char *pLocation) +{ + return devNoResponseProbe (addrType, (size_t) pLocation, sizeof(long)); +} + +/****************************************************************************** + * + * The follwing may, or may not be present in the BSP for the CPU in use. + * + */ +/****************************************************************************** + * + * Routines to use to allocate and free memory present in the A24 region. + * + ******************************************************************************/ + +int devLibA24Debug = 0; /* Debugging flag */ + +void *devLibA24Calloc(size_t size) +{ + void *ret; + + ret = devLibA24Malloc(size); + + if (ret == NULL) + return (NULL); + + memset(ret, 0x00, size); + return(ret); +} + +void *devLibA24Malloc(size_t size) +{ + void *ret; + + if (devLibA24Debug) + epicsPrintf ("devLibA24Malloc(%d) entered\n", size); + + ret = pdevLibVirtualOS->pDevA24Malloc(size); + return(ret); +} + +void devLibA24Free(void *pBlock) +{ + if (devLibA24Debug) + epicsPrintf("devLibA24Free(%p) entered\n", pBlock); + + pdevLibVirtualOS->pDevA24Free(pBlock); +} diff --git a/src/libCom/osi/devLib.h b/src/libCom/osi/devLib.h new file mode 100644 index 000000000..f53b8fffa --- /dev/null +++ b/src/libCom/osi/devLib.h @@ -0,0 +1,422 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.h */ +/* $Id$ */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + * + * Notes: + * ------ + * .01 03-23-93 joh We will only have problems with mod .03 + * above if the CPU maintains the different + * address modes in different address spaces + * and we have a card or a user that insists + * on not using the default address type + * .02 06-14-93 joh needs devAllocInterruptVector() routine + */ + +#ifndef INCdevLibh +#define INCdevLibh 1 + +#include + +/* + * epdevAddressType & EPICStovxWorksAddrType + * devLib.c must change in unison + */ +typedef enum { + atVMEA16, + atVMEA24, + atVMEA32, + atISA, /* memory mapped ISA access (until now only on PC) */ + atLast /* atLast must be the last enum in this list */ + } epicsAddressType; + +/* + * pointer to an array of strings for each of + * the above address types + */ +extern const char *epicsAddressTypeName[]; + +long devAddressMap(void); /* print an address map */ + +/* + * devReadProbe() + * + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devReadProbe (unsigned wordSize, volatile const void *ptr, void *pValueRead); + +/* + * devNoResponseProbe() + * + * Verifies that no devices respond at naturally aligned words + * within the specified address range. Return success if no devices + * respond. Returns an error if a device does respond or if + * a physical address for a naturally aligned word cant be mapped. + * Checks all naturally aligned word sizes between char and long for + * the entire specified range of bytes. + */ +long devNoResponseProbe( + epicsAddressType addrType, + size_t base, + size_t size +); + +/* + * devWriteProbe + * + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValueWritten); + +long devRegisterAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t logicalBaseAddress, + size_t size, /* bytes */ + volatile void **pPhysicalAddress); + +long devUnregisterAddress( + epicsAddressType addrType, + size_t logicalBaseAddress, + const char *pOwnerName); + +/* + * allocate and register an unoccupied address block + */ +long devAllocAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t size, + unsigned alignment, /*n ls bits zero in addr*/ + volatile void **pLocalAddress); + +/* + * connect ISR to a VME interrupt vector + */ +long devConnectInterruptVME( + unsigned vectorNumber, + void (*pFunction)(void *), + void *parameter); + +/* + * connect ISR to an ISA interrupt level + * (not implemented) + * (API should be reviewed) + */ +long devConnectInterruptISA( + unsigned interruptLevel, + void (*pFunction)(void *), + void *parameter); + +/* + * connect ISR to a PCI interrupt + * (not implemented) + * (API should be reviewed) + */ +long devConnectInterruptPCI( + unsigned bus, + unsigned device, + unsigned function, + void (*pFunction)(void *), + void *parameter); + +/* + * disconnect ISR from a VME interrupt vector + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +long devDisconnectInterruptVME( + unsigned vectorNumber, + void (*pFunction)(void *)); + +/* + * disconnect ISR from an ISA interrupt level + * (not implemented) + * (API should be reviewed) + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +long devDisconnectInterruptISA( + unsigned interruptLevel, + void (*pFunction)(void *)); + +/* + * disconnect ISR from a PCI interrupt + * (not implemented) + * (API should be reviewed) + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +long devDisconnectInterruptPCI( + unsigned bus, + unsigned device, + unsigned function, + void (*pFunction)(void *)); + +/* + * determine if a VME interrupt vector is in use + * + * returns boolean + */ +int devInterruptInUseVME (unsigned vectorNumber); + +/* + * determine if an ISA interrupt level is in use + * (not implemented) + * + * returns boolean + */ +int devInterruptLevelInUseISA (unsigned interruptLevel); + +/* + * determine if a PCI interrupt is in use + * (not implemented) + * + * returns boolean + */ +int devInterruptInUsePCI (unsigned bus, unsigned device, + unsigned function); + +typedef enum {intVME, intVXI, intISA} epicsInterruptType; + +/* + * enable VME interrupt level + */ +long devEnableInterruptLevelVME (unsigned level); + +/* + * enable ISA interrupt level + */ +long devEnableInterruptLevelISA (unsigned level); + +/* + * not implemented - API needs to be reviewed + */ +long devEnableInterruptLevelPCI (unsigned level, + unsigned bus, unsigned device, unsigned function); + +/* + * disable VME interrupt level + */ +long devDisableInterruptLevelVME (unsigned level); + +/* + * disable ISA interrupt level + */ +long devDisableInterruptLevelISA (unsigned level); + +/* + * not implemented - API needs to be reviewed + */ +long devDisableInterruptLevelPCI (unsigned level, + unsigned bus, unsigned device, unsigned function); + +/* + * Routines to allocate and free memory in the A24 memory region. + * + */ +void *devLibA24Malloc(size_t); +void *devLibA24Calloc(size_t); +void devLibA24Free(void *pBlock); + +/* + * Normalize a digital value and convert it to type TYPE + * + * Ex: + * float f; + * int d; + * f = devNormalizeDigital(d,12) + * + */ +#define devCreateMask(NBITS) ((1<<(NBITS))-1) +#define devDigToNml(DIGITAL,NBITS) \ + (((double)(DIGITAL))/devCreateMask(NBITS)) +#define devNmlToDig(NORMAL,NBITS) \ + (((long)(NORMAL)) * devCreateMask(NBITS)) + +/* + * + * Alignment mask + * (for use when testing to see if the proper number of least + * significant bits are zero) + * + */ +#define devCreateAlignmentMask(CTYPE)\ +(sizeof(CTYPE)>sizeof(double)?sizeof(double)-1:sizeof(CTYPE)-1) + +/* + * pointer aligned test + * (returns true if the pointer is on the worst case alignemnt + * boundary for its type) + */ +#define devPtrAlignTest(PTR) (!(devCreateAlignmentMask(*PTR)&(long)(PTR))) + +/* + * virtual OS layer for devLib.c + */ +typedef struct devLibVirtualOS { + /* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ + long (*pDevMapAddr) (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + + /* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ + long (*pDevReadProbe) (unsigned wordSize, volatile const void *ptr, void *pValueRead); + + /* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ + long (*pDevWriteProbe) (unsigned wordSize, volatile void *ptr, const void *pValueWritten); + + /* + * connect ISR to a VME interrupt vector + * (required for backwards compatibility) + */ + long (*pDevConnectInterruptVME) (unsigned vectorNumber, + void (*pFunction)(), void *parameter); + + /* + * disconnect ISR from a VME interrupt vector + * (required for backwards compatibility) + */ + long (*pDevDisconnectInterruptVME) (unsigned vectorNumber, + void (*pFunction)(void *)); + + /* + * enable VME interrupt level + */ + long (*pDevEnableInterruptLevelVME) (unsigned level); + + /* + * disable VME interrupt level + */ + long (*pDevDisableInterruptLevelVME) (unsigned level); + /* malloc/free A24 address space */ + void *(*pDevA24Malloc)(size_t nbytes); + void (*pDevA24Free)(void *pBlock); + long (*pDevInit)(void); +}devLibVirtualOS; +extern devLibVirtualOS *pdevLibVirtualOS; + +/* + * error codes (and messages) associated with devLib.c + */ +#define S_dev_success 0 +#define S_dev_vectorInUse (M_devLib| 1) /*interrupt vector in use*/ +#define S_dev_vecInstlFail (M_devLib| 2) /*interrupt vector install failed*/ +#define S_dev_uknIntType (M_devLib| 3) /*Unrecognized interrupt type*/ +#define S_dev_vectorNotInUse (M_devLib| 4) /*Interrupt vector not in use by caller*/ +#define S_dev_badA16 (M_devLib| 5) /*Invalid VME A16 address*/ +#define S_dev_badA24 (M_devLib| 6) /*Invalid VME A24 address*/ +#define S_dev_badA32 (M_devLib| 7) /*Invalid VME A32 address*/ +#define S_dev_uknAddrType (M_devLib| 8) /*Unrecognized address space type*/ +#define S_dev_addressOverlap (M_devLib| 9) /*Specified device address overlaps another device*/ +#define S_dev_identifyOverlap (M_devLib| 10) /*This device already owns the address range*/ +#define S_dev_addrMapFail (M_devLib| 11) /*unable to map address*/ +#define S_dev_intDisconnect (M_devLib| 12) /*Interrupt at vector disconnected from an EPICS device*/ +#define S_dev_internal (M_devLib| 13) /*Internal failure*/ +#define S_dev_intEnFail (M_devLib| 14) /*unable to enable interrupt level*/ +#define S_dev_intDissFail (M_devLib| 15) /*unable to disable interrupt level*/ +#define S_dev_noMemory (M_devLib| 16) /*Memory allocation failed*/ +#define S_dev_addressNotFound (M_devLib| 17) /*Specified device address unregistered*/ +#define S_dev_noDevice (M_devLib| 18) /*No device at specified address*/ +#define S_dev_wrongDevice (M_devLib| 19) /*Wrong device type found at specified address*/ +#define S_dev_badSignalNumber (M_devLib| 20) /*Signal number (offset) to large*/ +#define S_dev_badSignalCount (M_devLib| 21) /*Signal count to large*/ +#define S_dev_badRequest (M_devLib| 22) /*Device does not support requested operation*/ +#define S_dev_highValue (M_devLib| 23) /*Parameter to high*/ +#define S_dev_lowValue (M_devLib| 24) /*Parameter to low*/ +#define S_dev_multDevice (M_devLib| 25) /*Specified address is ambiguous (more than one device responds)*/ +#define S_dev_badSelfTest (M_devLib| 26) /*Device self test failed*/ +#define S_dev_badInit (M_devLib| 27) /*Device failed during initialization*/ +#define S_dev_hdwLimit (M_devLib| 28) /*Input exceeds Hardware Limit*/ +#define S_dev_deviceDoesNotFit (M_devLib| 29) /*Unable to locate address space for device*/ +#define S_dev_deviceTMO (M_devLib| 30) /*device timed out*/ +#define S_dev_badFunction (M_devLib| 31) /*bad function pointer*/ +#define S_dev_badVector (M_devLib| 32) /*bad interrupt vector*/ +#define S_dev_badArgument (M_devLib| 33) /*bad function argument*/ +#define S_dev_vxWorksIntEnFail S_dev_intEnFail + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devConnectInterruptVME, devConnectInterruptPCI, + * devConnectInterruptISA etc. devConnectInterrupt will be removed + * in a future release. + */ +long devConnectInterrupt( + epicsInterruptType intType, + unsigned vectorNumber, + void (*pFunction)(), + void *parameter); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devDisconnectInterruptVME, devDisconnectInterruptPCI, + * devDisconnectInterruptISA etc. devDisconnectInterrupt will be removed + * in a future release. + */ +long devDisconnectInterrupt( + epicsInterruptType intType, + unsigned vectorNumber, + void (*pFunction)()); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devEnableInterruptLevelVME, devEnableInterruptLevelPCI, + * devEnableInterruptLevelISA etc. devEnableInterruptLevel will be removed + * in a future release. + */ +long devEnableInterruptLevel(epicsInterruptType intType, unsigned level); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devDisableInterruptLevelVME, devDisableInterruptLevelISA, + * devDisableInterruptLevelPCI etc. devDisableInterruptLevel will be removed + * in a future release. + */ +long devDisableInterruptLevel (epicsInterruptType intType, unsigned level); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use devNoResponseProbe(). locationProbe() will be removed + * in a future release. + */ +long locationProbe (epicsAddressType addrType, char *pLocation); + +#endif /* devLib.h*/ diff --git a/src/libCom/osi/os/RTEMS/devLibOSD.c b/src/libCom/osi/os/RTEMS/devLibOSD.c new file mode 100644 index 000000000..727a2966b --- /dev/null +++ b/src/libCom/osi/os/RTEMS/devLibOSD.c @@ -0,0 +1,318 @@ +/* devLibRTEMS.c */ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* RTEMS port by Till Straumann, + * 3/2002 + * + */ + +#include +#include +#include +#include + +#include "devLib.h" +#include + +typedef void myISR (void *pParam); + +LOCAL myISR *isrFetch(unsigned vectorNumber, void **parg); + +/* + * this routine needs to be in the symbol table + * for this code to work correctly + */ +void unsolicitedHandlerEPICS(int vectorNumber); + +LOCAL myISR *defaultHandlerAddr[]={ + (myISR*)unsolicitedHandlerEPICS, +}; + +/* + * we use a translation between an EPICS encoding + * and a vxWorks encoding here + * to reduce dependency of drivers on vxWorks + * + * we assume that the BSP are configured to use these + * address modes by default + */ +#define EPICSAddrTypeNoConvert -1 +int EPICStovxWorksAddrType[] + = { + VME_AM_SUP_SHORT_IO, + VME_AM_STD_SUP_DATA, + VME_AM_EXT_SUP_DATA, + EPICSAddrTypeNoConvert + }; + +/* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ +LOCAL long rtmsDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +long rtmsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +long rtmsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); + +/* RTEMS specific init */ + +/*devA24Malloc and devA24Free are not implemented*/ +LOCAL void *devA24Malloc(size_t size) { return 0;} +LOCAL void devA24Free(void *pBlock) {}; +LOCAL long rtmsDevInit(void); + +/* + * used by bind in devLib.c + */ +const struct devLibVirtualOS devLibRTEMSOS = + {rtmsDevMapAddr, rtmsDevReadProbe, rtmsDevWriteProbe, + devConnectInterruptVME, devDisconnectInterruptVME, + devEnableInterruptLevelVME, devDisableInterruptLevelVME, + devA24Malloc,devA24Free,rtmsDevInit}; + +/* RTEMS specific initialization */ +LOCAL long +rtmsDevInit(void) +{ + /* assume the vme bridge has been initialized by bsp */ + /* init BSP extensions [memProbe etc.] */ + return bspExtInit(); +} + +/* + * devConnectInterruptVME + * + * wrapper to minimize driver dependency on OS + */ +long devConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter) +{ + int status; + + + if (devInterruptInUseVME(vectorNumber)) { + return S_dev_vectorInUse; + } + status = BSP_installVME_isr( + vectorNumber, + pFunction, + parameter); + if (status) { + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * + * devDisconnectInterruptVME() + * + * wrapper to minimize driver dependency on OS + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from removing + * an interrupt handler that was installed by another driver + * + */ +long devDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +) +{ + void (*psub)(); + void *arg; + int status; + + /* + * If pFunction not connected to this vector + * then they are probably disconnecting from the wrong vector + */ + psub = isrFetch(vectorNumber, &arg); + if(psub != pFunction){ + return S_dev_vectorNotInUse; + } + + status = BSP_removeVME_isr( + vectorNumber, + psub, + arg) || + BSP_installVME_isr( + vectorNumber, + (BSP_VME_ISR_t)unsolicitedHandlerEPICS, + (void*)vectorNumber); + if(status){ + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * enable VME interrupt level + */ +long devEnableInterruptLevelVME (unsigned level) +{ + return BSP_enableVME_int_lvl(level); +} + +/* + * disable VME interrupt level + */ +long devDisableInterruptLevelVME (unsigned level) +{ + return BSP_disableVME_int_lvl(level); +} + +/* + * rtmsDevMapAddr () + */ +LOCAL long rtmsDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) +{ + long status; + + if (ppPhysicalAddress==NULL) { + return S_dev_badArgument; + } + + if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) + { + *ppPhysicalAddress = (void *) logicalAddress; + } + else + { + status = BSP_vme2local_adrs(EPICStovxWorksAddrType[addrType], + logicalAddress, (unsigned long *)ppPhysicalAddress); + if (status) { + return S_dev_addrMapFail; + } + } + + return 0; +} + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +long rtmsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = bspExtMemProbe ((void*)ptr, 0/*read*/, wordSize, pValue); + if (status!=RTEMS_SUCCESSFUL) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +long rtmsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = bspExtMemProbe ((void*)ptr, 1/*write*/, wordSize, (void*)pValue); + if (status!=RTEMS_SUCCESSFUL) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * isrFetch() + */ +LOCAL myISR *isrFetch(unsigned vectorNumber, void **parg) +{ + /* + * fetch the handler or C stub attached at this vector + */ + return (myISR *) BSP_getVME_isr(vectorNumber,parg); +} + +/* + * determine if a VME interrupt vector is in use + */ +int devInterruptInUseVME (unsigned vectorNumber) +{ + int i; + myISR *psub; + void *arg; + + psub = isrFetch (vectorNumber,&arg); + + if (!psub) + return FALSE; + + /* + * its a C routine. Does it match a default handler? + */ + for (i=0; i + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsFindSymbol.h" +#include "devLib.h" + +/* + * A list of the names of the unexpected interrupt handlers + * ( some of these are provided by wrs ) + */ +LOCAL char *defaultHandlerNames[] = { + "excStub", + "excIntStub", + "unsolicitedHandlerEPICS"}; + +typedef void myISR (void *pParam); +LOCAL myISR *defaultHandlerAddr[NELEMENTS(defaultHandlerNames)]; + +LOCAL myISR *isrFetch(unsigned vectorNumber); + +/* + * this routine needs to be in the symbol table + * for this code to work correctly + */ +void unsolicitedHandlerEPICS(int vectorNumber); + +/* + * this is in veclist.c + */ +int cISRTest(void (*)(), void (**)(), void **); + +/* + * we use a translation between an EPICS encoding + * and a vxWorks encoding here + * to reduce dependency of drivers on vxWorks + * + * we assume that the BSP are configured to use these + * address modes by default + */ +#define EPICSAddrTypeNoConvert -1 +int EPICStovxWorksAddrType[] + = { + VME_AM_SUP_SHORT_IO, + VME_AM_STD_SUP_DATA, + VME_AM_EXT_SUP_DATA, + EPICSAddrTypeNoConvert + }; + +LOCAL void initHandlerAddrList(void); + +/* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ +LOCAL long vxDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); + +static void *devA24Malloc(size_t size); +static void devA24Free(void *pBlock); +static void devInit(void) { return 0;} + +/* + * used by dynamic bind in devLib.c + */ +static devLibVirtualOS virtualOS = { + vxDevMapAddr, vxDevReadProbe, vxDevWriteProbe, + devConnectInterruptVME, devDisconnectInterruptVME, + devEnableInterruptLevelVME, devDisableInterruptLevelVME, + devA24Malloc,devA24Free,devInit +}; +devLibVirtualOS *pdevLibVirtualOS = &virtualOS; + +/* + * devConnectInterruptVME + * + * wrapper to minimize driver dependency on vxWorks + */ +long devConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter) +{ + int status; + + + if (devInterruptInUseVME(vectorNumber)) { + return S_dev_vectorInUse; + } + status = intConnect( + (void *)INUM_TO_IVEC(vectorNumber), + pFunction, + (int) parameter); + if (status<0) { + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * + * devDisconnectInterruptVME() + * + * wrapper to minimize driver dependency on vxWorks + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from removing + * an interrupt handler that was installed by another driver + * + */ +long devDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +) +{ + void (*psub)(); + int status; + +# if CPU_FAMILY == PPC + return S_dev_vecInstlFail; +# endif + + /* + * If pFunction not connected to this vector + * then they are probably disconnecting from the wrong vector + */ + psub = isrFetch(vectorNumber); + if(psub != pFunction){ + return S_dev_vectorNotInUse; + } + + status = intConnect( + (void *)INUM_TO_IVEC(vectorNumber), + unsolicitedHandlerEPICS, + (int) vectorNumber); + if(status<0){ + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * enable VME interrupt level + */ +long devEnableInterruptLevelVME (unsigned level) +{ +# if CPU_FAMILY != I80X86 + int s; + s = sysIntEnable (level); + if (s!=OK) { + return S_dev_intEnFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * enable ISA interrupt level + */ +long devEnableInterruptLevelISA (unsigned level) +{ +# if CPU_FAMILY == I80X86 + int s; + s = sysIntEnablePIC (level); + if (s!=OK) { + return S_dev_intEnFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * disable ISA interrupt level + */ +long devDisableInterruptLevelISA (unsigned level) +{ +# if CPU_FAMILY == I80X86 + int s; + s = sysIntDisablePIC (level); + if (s!=OK) { + return S_dev_intEnFail; + } +# else + return S_dev_intEnFail; +# endif + + return 0; +} + +/* + * disable VME interrupt level + */ +long devDisableInterruptLevelVME (unsigned level) +{ +# if CPU_FAMILY != I80X86 + int s; + s = sysIntDisable (level); + if (s!=OK) { + return S_dev_intDissFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * vxDevMapAddr () + */ +LOCAL long vxDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) +{ + long status; + + if (ppPhysicalAddress==NULL) { + return S_dev_badArgument; + } + + if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) + { + *ppPhysicalAddress = (void *) logicalAddress; + } + else + { + status = sysBusToLocalAdrs (EPICStovxWorksAddrType[addrType], + (char *) logicalAddress, (char **)ppPhysicalAddress); + if (status) { + return S_dev_addrMapFail; + } + } + + return 0; +} + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = vxMemProbe ((char *)ptr, VX_READ, wordSize, (char *) pValue); + if (status!=OK) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = vxMemProbe ((char *)ptr, VX_READ, wordSize, (char *) pValue); + if (status!=OK) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * isrFetch() + */ +LOCAL myISR *isrFetch(unsigned vectorNumber) +{ + myISR *psub; + myISR *pCISR; + void *pParam; + int s; + + /* + * fetch the handler or C stub attached at this vector + */ + psub = (myISR *) intVecGet((FUNCPTR *)INUM_TO_IVEC(vectorNumber)); + + if ( psub ) { + /* + * from libvxWorks/veclist.c + * + * checks to see if it is a C ISR + * and if so finds the function pointer and + * the parameter passed + */ + s = cISRTest(psub, &pCISR, &pParam); + if(!s){ + psub = pCISR; + } + } + + return psub; +} + +/* + * determine if a VME interrupt vector is in use + */ +int devInterruptInUseVME (unsigned vectorNumber) +{ + static int init; + int i; + myISR *psub; + +# if CPU_FAMILY == PPC + return FALSE; +# else + if (!init) { + initHandlerAddrList(); + init = TRUE; + } + + psub = isrFetch (vectorNumber); + + /* + * its a C routine. Does it match a default handler? + */ + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "envDefs.h" +#include "envLib.h" +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" +#include "drvTS.h" +#include "osiSock.h" +#include "iocClock.h" +#include "epicsDynLink.h" + +/* Use TSprintf() for anything that should be logged, else printf() */ +#define TSprintf epicsPrintf + +#define DEFAULT_TIME 0 +#define NO_EVENT_SYSTEM 1 + +/* functions used by this driver */ +static long TSgetUnixTime(struct timespec*); +static long TSgetMasterTime(struct timespec*); +static long TSgetBroadcastAddr(int soc, struct sockaddr*); +static int TSgetSocket(int port, struct sockaddr_in* sin); +static int TSgetBroadcastSocket(int port, struct sockaddr_in* sin); +static void TSsyncServer(); +static void TSsyncClient(); +static long TSasyncClient(); +static long TSsyncTheTime(struct timespec* cts, struct timespec* ts); +static long TSgetData(char* buf, int buf_size, int soc, + struct sockaddr* to_sin, struct sockaddr* from_sin, + struct timespec* round_trip); +static void TSaddStamp( struct timespec* result, + struct timespec* op1, struct timespec* op2); +static void TSwdIncTime(); +static void TSwdIncTimeSync(); +static void TSstampServer(); +static void TSeventHandler(int Card,int EventNum,unsigned long Ticks); +static void TSerrorHandler(int Card, int ErrorNum); +static long TSsetClockFromMaster(); +static void TSstartSoftClock(); +static long TScalcDiff(struct timespec* a, struct timespec* b, + struct timespec* diff); + +static long TSgetCurrentTime(struct timespec* ts); +static long TSforceSoftSync(int Card); +static long TSstartSyncServer(); +static long TSstartSyncClient(); +static long TSstartAsyncClient(); +static long TSstartStampServer(); +static long TSuserGetJunk(int event_number,struct timespec* sp); + +/* event system and time clock functions */ +static long (*TSregisterEventHandler)(int Card, void(*func)()); +static long (*TSregisterErrorHandler)(int Card, void(*func)()); +static long (*TSgetTicks)(int Card, unsigned long *Ticks); +static long (*TShaveReceiver)(int Card); +static long (*TSforceSync)(int Card); +static long (*TSgetTime)(struct timespec*); +static long (*TSsyncEvent)(); +static long (*TSdirectTime)(); +static long (*TSdriverInit)(); +static long (*TSuserGet)(int event_number,struct timespec* sp); + +static int TSinitialized = 0; + +/* global functions */ +#ifdef __cplusplus +extern "C" { +#endif + long TSinit(void); /* called by iocInit currently */ + long TSreport(); /* callable from vxWorks shell */ + + /* test functions */ + void TSprintRealTime(); + void TSprintUnixTime(); + void TSprintMasterTime(); + void TSprintTimeStamp(int event_number); + void TSprintCurrentTime(); +#ifdef __cplusplus +} +#endif + +/* data used by all */ +TSinfo TSdata = +{ TS_master_dead, TS_async_slave, TS_async_none, +0,NULL, +TS_SYNC_RATE_SEC,TS_CLOCK_RATE_HZ,0,TS_TIME_OUT_MS,0, +TS_MASTER_PORT,TS_SLAVE_PORT,1,0,0,0,0, +NULL, {NULL}, {NULL} +}; + +extern char* sysBootLine; + +int TSdirectTimeVar = 0; /* aother way to indicate direct time */ +int TSgoodTimeStamps = 0; /* a way to force use of accurate time stamps */ + +static WDOG_ID wd; +static long correction_factor = 0; +static long correction_count = 0; + + +/* static long TSreturnError() { return -1; } */ +static long TSdirectTimeError() { return -1; } +static long TShaveReceiverError(int i) { return -1; } +static long TSgetTicksError(int i,unsigned long* t) { return -1; } + +static long TSregisterErrorHandlerError(int i, void(*f)()) +{ if(TSdata.has_direct_time==1) return 0; else return -1; } +static long TSregisterEventHandlerError(int i, void(*f)()) +{ if(TSdata.has_direct_time==1) return 0; else return -1; } + + +long TSdriverInitError() +{ + struct timespec ts; + time_t t; + + clock_gettime(CLOCK_REALTIME,&ts); + time(&t); + Debug(1,"vxWorks time = %s\n",ctime(&t)); + + return 0; +} + +unsigned long TSepochNtpToUnix(struct timespec* ts) +{ + unsigned long nfssecs = (unsigned long)ts->tv_sec; + unsigned long secs = 0; + + if(!TSinitialized) TSinit(); + /*If high order bit is not set then nfssecs has overflowed */ + if(!(nfssecs & 0x80000000ul)) { + /*secs = nfssecs - TS_1900_TO_VXWORKS_EPOCH + 2**32 */ + /* in order to prevent overflows rearrange as */ + /* secs = (2**32 -1) - TS_1900_TO_VXWORKS_EPOCH + nfssecs +1 */ + secs = 0xffffffffUL - TS_1900_TO_VXWORKS_EPOCH + nfssecs +1; + } else { + secs = nfssecs - TS_1900_TO_VXWORKS_EPOCH; + } + return(secs); +} + +unsigned long TSfractionToNano(unsigned long fraction) +{ + double value; + + /*value = 1e9 * fraction / 2**32 */ + value = (1e9 * (double)fraction)/4294967296.0; + return((unsigned long)value); +} + +unsigned long TSepochNtpToEpics(struct timespec* ts) +{ + unsigned long nfssecs = (unsigned long)ts->tv_sec; + unsigned long secs = 0; + + /*If high order bit is not set then nfssecs has overflowed */ + if(!(nfssecs & 0x80000000ul)) { + /*secs = nfssecs - TS_1900_TO_EPICS_EPOCH + 2**32 */ + /* in order to prevent overflows rearrange as */ + /* secs = (2**32 -1) - TS_1900_TO_EPICS_EPOCH + nfssecs +1 */ + secs = 0xffffffffUL - TS_1900_TO_EPICS_EPOCH + nfssecs +1; + } else { + secs = nfssecs - TS_1900_TO_EPICS_EPOCH; + } + return(secs); +} + +unsigned long TSepochUnixToEpics(struct timespec* ts) +{ + unsigned long unixsecs = (unsigned long)ts->tv_sec; + unsigned long secs; + + secs = unixsecs - TS_VXWORKS_TO_EPICS_EPOCH; + return(secs); +} + +unsigned long TSepochEpicsToUnix(struct timespec* ts) +{ + unsigned long epicssecs = (unsigned long)ts->tv_sec; + unsigned long secs; + + secs = epicssecs + TS_VXWORKS_TO_EPICS_EPOCH; + return(secs); +} + +/*-----------------------------------------------------------------------*/ +/* +TSreport() - report information about the state of the time stamp +support software +*/ +long TSreport() +{ + char buf[64]; + + switch(TSdata.type) + { + case TS_direct_master: printf("Direct timing master\n"); break; + case TS_sync_master: printf("Event timing master\n"); break; + case TS_async_master: printf("Soft timing master\n"); break; + case TS_direct_slave: printf("Direct timing slave\n"); break; + case TS_sync_slave: printf("Event timing slave\n"); break; + case TS_async_slave: printf("Soft timing slave\n"); break; + default: break; + } + switch(TSdata.state) + { + case TS_master_alive: printf("Master timing IOC alive\n"); break; + case TS_master_dead: printf("Master timing IOC dead\n"); break; + default: break; + } + switch(TSdata.async_type) + { + case TS_async_none: printf("No clock synchronization\n"); break; + case TS_async_private: printf("Sync protocol with master\n"); break; + case TS_async_ntp: printf("NTP sync with unix server\n"); break; + case TS_async_time: printf("Time protocol sync with unix\n"); break; + default: break; + } + printf("Clock Rate in Hertz = %lu\n",TSdata.clock_hz); + printf("Sync Rate in Seconds = %lu\n",TSdata.sync_rate); + printf("Master communications port = %d\n",TSdata.master_port); + printf("Slave communications port = %d\n",TSdata.slave_port); + printf("Total events supported = %d\n",TSdata.total_events); + printf("Request Time Out = %lu milliseconds\n",TSdata.time_out); + + ipAddrToDottedIP ((struct sockaddr_in*)&TSdata.hunt, buf, sizeof(buf)); + printf("Broadcast address: %s\n", buf); + ipAddrToDottedIP ((struct sockaddr_in*)&TSdata.master, buf, sizeof(buf)); + printf("Master address: %s\n", buf); + + if(TSdata.UserRequestedType) + printf("\nForced to not use the event system\n"); + + if(TSdata.has_direct_time) + printf("Event system has time directly available\n"); + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* +TSconfigure() - This is the configuration routine which is meant to +be called from the vxWorks startup.cmd script before iocInit. +It's job is to set operating parameters for the time stamp support code. + + JRW -- if type = 0, then try to config using event system + if type = 1, then permanantly inhibit use of the event system +*/ +void TSconfigure(int master, int sync_rate_sec, int clock_rate_hz, + int master_port, int slave_port, unsigned long time_out, int type) +{ + if(master) TSdata.master_timing_IOC=1; + else TSdata.master_timing_IOC=0; + + if(sync_rate_sec) TSdata.sync_rate=sync_rate_sec; + else TSdata.sync_rate=TS_SYNC_RATE_SEC; + + if(clock_rate_hz) TSdata.clock_hz=clock_rate_hz; + else TSdata.clock_hz=TS_CLOCK_RATE_HZ; + + TSdata.clock_conv= TS_BILLION / TSdata.clock_hz; + + if(master_port) TSdata.master_port=master_port; + else TSdata.master_port=TS_MASTER_PORT; + + if(slave_port) TSdata.slave_port=slave_port; + else TSdata.slave_port=TS_SLAVE_PORT; + + if(time_out) TSdata.time_out=time_out; + else TSdata.time_out=TS_TIME_OUT_MS; + + switch(type) + { + case DEFAULT_TIME: + case NO_EVENT_SYSTEM: + TSdata.UserRequestedType = type; + break; + default: + TSprintf("Invalid type parameter <%d> must be:\n",type); + TSprintf(" 0 = default time system\n"); + TSprintf(" 1 = force no event system\n"); + TSdata.UserRequestedType = 0; + break; + } + + return; +} + +/* +this sucks, but who cares, user should not be using event number if not +prepared to handle them by defining an ErUserGetTimeStamp() routine. +*/ +static long TSuserGetJunk(int event_number,struct timespec* sp) +{ + return TSgetTimeStamp(0,sp); +} + +/*-----------------------------------------------------------------------*/ +/* +TSgetTimeStamp() - This routine returns the time stamp which represents +the time when an event event_number occurred. Soft timing will always +return the current time. Event zero will always return the current time +also. +*/ +long TSgetTimeStamp(int event_number,struct timespec* sp) +{ +/* this is questionable, a sync slave with a dead master will have +an invalid time stamp, so use the vxworks clock. Also remember that +the IOC vxWorks clock may not be in sync with time stamps if + default TSgetTime() in use and master failure is detected */ + if(!TSinitialized) TSinit(); + + if(event_numbertv_sec += ts.tv_sec; + sp->tv_nsec += ts.tv_nsec; + + /* adjust seconds if needed */ + if(sp->tv_nsec >= TS_BILLION) + { + sp->tv_sec++; + sp->tv_nsec -= TS_BILLION; + } + } + break; + default: + if(TSdata.state==TS_master_dead) + TSgetTime(sp); + else + *sp = TSdata.event_table[event_number]; + break; + } + break; + default: + if(event_number==epicsTimeEventCurrentTime + || event_number==epicsTimeEventBestTime) { + *sp = TSdata.event_table[TSdata.sync_event]; + } else { + *sp = TSdata.event_table[event_number]; + } + } + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* +TSinit() - initialize the driver, determine mode. +*/ +static int getEvent(TS_STAMP *pDest, int eventNumber) +{ + return(TSgetTimeStamp(eventNumber,(struct timespec*)pDest)); +} + +long TSinit(void) +{ + SYM_TYPE stype; + + Debug0(5,"In TSinit()\n"); + + if(TSinitialized) return(0); + /*register with iocClock */ + iocClockRegister((pepicsTimeGetCurrent)TScurrentTimeStamp,getEvent); + TSinitialized = 1; + /* 0=default, 1=none, 2=direct */ + + if( TSdata.UserRequestedType==DEFAULT_TIME) + { + /* default configuration probe */ + /* ------------------------------------------------------------- */ + /* find the lower level event system functions */ + if(symFindByNameEPICS(sysSymTbl,"_ErHaveReceiver", + (char**)&TShaveReceiver,&stype)==ERROR) + TShaveReceiver = TShaveReceiverError; + if(symFindByNameEPICS(sysSymTbl,"_ErGetTicks", + (char**)&TSgetTicks,&stype)==ERROR) + TSgetTicks = TSgetTicksError; + + if(symFindByNameEPICS(sysSymTbl,"_ErRegisterEventHandler", + (char**)&TSregisterEventHandler,&stype)==ERROR) + TSregisterEventHandler = TSregisterEventHandlerError; + + if(symFindByNameEPICS(sysSymTbl,"_ErRegisterErrorHandler", + (char**)&TSregisterErrorHandler,&stype)==ERROR) + TSregisterErrorHandler = TSregisterErrorHandlerError; + + if(symFindByNameEPICS(sysSymTbl,"_ErForceSync", + (char**)&TSforceSync,&stype)==ERROR) + TSforceSync = TSforceSoftSync; + + if(symFindByNameEPICS(sysSymTbl,"_ErDirectTime", + (char**)&TSdirectTime,&stype)==ERROR) + TSdirectTime = TSdirectTimeError; + + if(symFindByNameEPICS(sysSymTbl,"_ErDriverInit", + (char**)&TSdriverInit,&stype)==ERROR) + TSdriverInit = TSdriverInitError; + + if(symFindByNameEPICS(sysSymTbl,"_ErGetTime", + (char**)&TSgetTime,&stype)==ERROR) + TSgetTime = TSgetCurrentTime; + + if(symFindByNameEPICS(sysSymTbl,"_ErUserGetTimeStamp", + (char**)&TSuserGet,&stype)==ERROR) + TSuserGet = TSuserGetJunk; + + if(symFindByNameEPICS(sysSymTbl,"_ErSyncEvent", + (char**)&TSsyncEvent,&stype)==ERROR) + TSdata.sync_event=ER_EVENT_RESET_TICK; + else + TSdata.sync_event=TSsyncEvent(); + + /* ------------------------------------------------------------- */ + } + else + { + /* inhibit probe and use of the event system */ + TSprintf("WARNING: drvTS event hardware probe inhibited by user\n"); + TShaveReceiver = TShaveReceiverError; + TSgetTicks = TSgetTicksError; + TSregisterEventHandler = TSregisterEventHandlerError; + TSregisterErrorHandler = TSregisterErrorHandlerError; + TSforceSync = TSforceSoftSync; + TSgetTime = TSgetCurrentTime; + TSdriverInit = TSdriverInitError; + TSdirectTime = TSdirectTimeError; + TSuserGet = TSuserGetJunk; + TSdata.sync_event=ER_EVENT_RESET_TICK; + } + + /* set all the known information about the system */ + TSdata.event_table=NULL; + TSdata.ts_sync_valid=0; /* the sync time stamp invalid */ + TSdata.state=TS_master_dead; + TSdata.sync_occurred = semBCreate(SEM_Q_PRIORITY,SEM_EMPTY); + TSdata.has_event_system = 0; + TSdata.has_direct_time = 0; + TSdata.async_type=TS_async_none; + + if( (TSdata.total_events=TShaveReceiver(0))<=0) + { + Debug0(5,"TSinit() - no event receiver\n"); + TSdata.total_events=1; + TSdata.sync_event=0; + TSdata.clock_hz=sysClkRateGet(); + TSdata.clock_conv=TS_BILLION / TSdata.clock_hz; + TSdata.has_event_system = 0; + } + else + TSdata.has_event_system = 1; + + if(TSdirectTime()>0 || TSdirectTimeVar>0) TSdata.has_direct_time=1; + + /* allocate the event table */ + TSdata.event_table=(struct timespec*)malloc( + TSdata.total_events*sizeof(struct timespec)); + + if(TSdata.master_timing_IOC) + { + /* master */ + Debug0(5,"TSinit() - I am master\n"); + if(TSdata.has_direct_time) + { + TSdata.type=TS_direct_master; + } + else + { + if(TSdata.has_event_system) + TSdata.type=TS_sync_master; + else + TSdata.type=TS_async_master; + } + } + else + { + /* slave */ + Debug0(5,"TSinit() - I am slave\n"); + if(TSdata.has_direct_time) + { + TSdata.type=TS_direct_slave; + } + else + { + if(TSdata.has_event_system) + TSdata.type=TS_sync_slave; + else + TSdata.type=TS_async_slave; + } + } + + /* set up the event system hooks */ + if(TSdata.has_event_system) + { + /* register the event handler function */ + if(TSregisterEventHandler(0,TSeventHandler)!=0) + { + TSprintf("Failed to register event handler\n"); + return -1; + } + + /* register the error handler function */ + if(TSregisterErrorHandler(0,TSerrorHandler)!=0) + { + TSprintf("Failed to register error handler\n"); + return -1; + } + } + + /* always start the soft clock */ + if(TSdata.has_direct_time==0) TSstartSoftClock(); + + /* get time from boot server Unix system */ + if(TSdata.master_timing_IOC) + { + /* master */ + if(TSsetClockFromUnix()<0) + { + /* bad, cannot get time - accessing starts ticking */ + struct timespec tp; + clock_gettime(CLOCK_REALTIME,&tp); + TSprintf("Failed to set clock from Unix server\n"); + } + Debug0(5,"TSinit() - tried to get clock from unix\n"); + + /* start the time stamp info server */ + if(TSstartStampServer()==ERROR) + { + TSprintf("Failed to start stamp server\n"); + return -1; + } + Debug0(5,"TSinit() - stamp server started \n"); + + TSdata.state = TS_master_alive; + + /* a direct master may be capable of delivering sync time stamps? */ + if(TSdata.type==TS_sync_master) + { + /* start the sync udp server */ + if(TSstartSyncServer()==ERROR) + { + TSprintf("Failed to start sync server\n"); + return -1; + } + Debug0(5,"TSinit() - sync server started \n"); + } + } + else + { + /* slave */ + if(TSsetClockFromUnix()<0) + { + struct timespec tp; + clock_gettime(CLOCK_REALTIME,&tp); + /* this should work */ + TSprintf("Failed to set time from Unix server\n"); + } + + if( TSsetClockFromMaster()<0 ) + { + /* do nothing here */ + /* TSprintf("Could not contact a master timing IOC\n"); */ + } + else + TSdata.state = TS_master_alive; + + if(TSdata.type==TS_async_slave) + { + /* this task syncs with master or unix */ + if(TSstartAsyncClient()==ERROR) + { + TSprintf("Failed to start async client\n"); + return -1; + } + Debug0(5,"TSinit() - async client started \n"); + } + else + { + /* this task sync with master */ + if(TSstartSyncClient()==ERROR) + { + TSprintf("Failed to start sync client\n"); + return -1; + } + Debug0(5,"TSinit() - sync client started \n"); + } + } + /* if TIMEZONE not defined set TIMEZONE from EPICS_TIMEZONE */ + if(getenv("TIMEZONE")==(char*)NULL) { + char timezone[80]; + char envtimezone[100]; + if(envGetConfigParam(&EPICS_TIMEZONE,sizeof(timezone),timezone)==NULL + || strlen(timezone)==0) { + printf("iocClockInit: No Time Zone Information\n"); + } else { + sprintf(envtimezone,"TIMEZONE=%s",timezone); + if(putenv(envtimezone)==ERROR) { + printf("iocClockInit: TIMEZONE putenv failed\n"); + } + } + } + TSdriverInit(); /* Call the user's driver initialization if supplied */ + return 0; +} + +/* following are watch dog routines for soft time support */ + +void TSstartSoftClock() /*start the soft clock watch dog*/ +{ + /* simple watch dog to fire off syncs to slaves */ + Debug(5,"start watch dog at rate %ld\n", + (long) sysClkRateGet()*TSdata.sync_rate); + wd = wdCreate(); + if(TSdata.has_event_system) + { + Debug0(8,"Starting sync time watch dog\n"); + wdStart(wd,1,(FUNCPTR)TSwdIncTimeSync,NULL); + } + else + { + Debug0(8,"Starting async time watch dog\n"); + wdStart(wd,1,(FUNCPTR)TSwdIncTime,NULL); + } + return; +} + +/* NOTE: TSwdIncTime called at interrupt level! */ +static void TSwdIncTime() /*increment the time stamp at a 60 Hz rate*/ +{ + int key; + + wdStart(wd,1, (FUNCPTR)TSwdIncTime,NULL); + /* update the event table */ + key=intLock(); + + if(correction_count) + { + long nsec = TSdata.event_table[TSdata.sync_event].tv_nsec + + TSdata.clock_conv; + nsec += correction_factor; + if(nsec<0) { + nsec += TS_BILLION; + TSdata.event_table[TSdata.sync_event].tv_sec--; + } + TSdata.event_table[TSdata.sync_event].tv_nsec = nsec; + if(--correction_count == 0) correction_factor = 0; + } + else + TSdata.event_table[TSdata.sync_event].tv_nsec += + TSdata.clock_conv; + + /* adjust seconds if needed */ + if(TSdata.event_table[TSdata.sync_event].tv_nsec >= TS_BILLION) + { + TSdata.event_table[TSdata.sync_event].tv_sec++; + TSdata.event_table[TSdata.sync_event].tv_nsec -= TS_BILLION; + } + intUnlock(key); + return; +} + +static void TSwdIncTimeSync() +{ + wdStart(wd,1, (FUNCPTR)TSwdIncTimeSync,NULL); + TSaccurateTimeStamp(&TSdata.event_table[0]); +} + +/* following are all interrupt service routines */ + +/* +TSeventHandler() - receive events from event system; update the event table + + Note: called at interrupt level! +*/ +static void TSeventHandler(int Card,int EventNum,unsigned long Ticks) +{ + struct timespec ts; + struct timespec* st; + int key; + +#ifdef DIRECT_WITH_EVENTS + if(TSdata.has_direct_time==1) + { + TSgetTime(&ts); + key=intLock(); + TSdata.event_table[EventNum].tv_sec = ts.tv_sec; + TSdata.event_table[EventNum].tv_nsec = ts.tv_nsec; + intUnlock(key); + return; + } +#endif + + /* calculate a time stamp from the Tick count */ + ts.tv_sec = Ticks / TSdata.clock_hz; + ts.tv_nsec = (Ticks - (ts.tv_sec * TSdata.clock_hz)) * TSdata.clock_conv; + /* obtain a copy of the last sync time */ + st = &TSdata.event_table[TSdata.sync_event]; + /* update the event table */ + key=intLock(); + TSdata.event_table[EventNum].tv_sec = st->tv_sec + ts.tv_sec; + TSdata.event_table[EventNum].tv_nsec = st->tv_nsec + ts.tv_nsec; + /* adjust seconds if needed */ + if(TSdata.event_table[EventNum].tv_nsec >= TS_BILLION) + { + TSdata.event_table[EventNum].tv_sec++; + TSdata.event_table[EventNum].tv_nsec -= TS_BILLION; + } + intUnlock(key); + /* signal a time stamp sync if this is sync event */ + if(TSdata.type==TS_sync_master && EventNum==TSdata.sync_event) + { + /* validate the soft time for back off in case of event system + failure, this will not be done in first implementation */ + + /* tell broadcast server to send out sync */ + semGive(TSdata.sync_occurred); + } + return; +} + +/* TSerrorHandler() - receive errors from event system +Note: called at interrupt level! +*/ +static void TSerrorHandler(int Card, int ErrorNum) +{ + /* + probably should do the following: + mark a "bad" state - timestamp is invalid, + send a request for sync to master, + if sync doesn't come when in bad state, then install + clock hook to increment time stamp until event system back up + keep a count of errors + Could put the slave on the vxworks timer until next sync + */ + + if(MAKE_DEBUG) + { + switch(ErrorNum) + { + case 1: + logMsg("***TSerrorHandler: event system error: TAXI violation", + 0,0,0,0,0,0); + break; + case 2: + logMsg("***TSerrorHandler: event system error: lost heartbeat", + 0,0,0,0,0,0); + break; + case 3: + logMsg("***TSerrorHandler: event system error: lost events", + 0,0,0,0,0,0); + break; + default: + logMsg("***TSerrorHandler: unknown error %d from event system", + ErrorNum,0,0,0,0,0); + } + } + return; +} + +/**************************************************************************/ +/* the following are utilities for initially getting and setting the time */ +/**************************************************************************/ +/* +TSgetUnixTime() - ask the boot server for the time using the +time protocol. This is only the time to the nearest second +A better way would be to use the NTP transactions to boot server +*/ + +static long TSgetUnixTime(struct timespec* ts) +{ + BOOT_PARAMS bootParms; + unsigned long buf_data,timeValue; + TS_NTP buf_ntp; + struct sockaddr_in sin; + int soc; + char host_addr[BOOT_ADDR_LEN]; + int status; + + Debug0(2,"in TSgetUnixTime()\n"); + if(envGetConfigParam(&EPICS_TS_NTP_INET,BOOT_ADDR_LEN,host_addr)==NULL || + strlen(host_addr)==0) + { + /* use boot host if the environment variable not set */ + bootStringToStruct(sysBootLine,&bootParms); + /* bootParms.had = host IP address */ + strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN); + } + if( (soc=TSgetSocket(0,&sin)) <0) + { Debug0(1,"TSgetsocket failed\n"); return -1; } + + /* set up for ntp transaction to boot server */ + Debug(5,"host addr = %s\n",host_addr); + + /* well known registered NTP port - or whatever port they specify */ + status = aToIPAddr (host_addr, UDP_NTP_PORT, &sin); + if (status) { + Debug0(2,"bad host name or IP address\n"); + TSdata.async_type=TS_async_none; + close(soc); + return -1; + } + + TSdata.async_type=TS_async_ntp; + memset(&buf_ntp,0,sizeof(buf_ntp)); + buf_ntp.info[0]=0x0b; + if(TSgetData((char*)&buf_ntp,sizeof(buf_ntp),soc, + (struct sockaddr*)&sin,NULL,NULL)<0) + { + Debug0(2,"no reply from NTP server\n"); + /* well known registered time port */ + sin.sin_port = htons(UDP_TIME_PORT); + TSdata.async_type=TS_async_time; + buf_data=0; + if(TSgetData((char*)&buf_data,sizeof(buf_data),soc, + (struct sockaddr*)&sin,NULL,NULL)<0) + { + Debug0(2,"no reply from Time server\n"); + TSdata.async_type=TS_async_none; + close(soc); + return -1; + } + } + /* remember: time and NTP protocol return seconds since 1900, not 1970 */ + /* to convert sec = timeValue - TS_1900_TO_EPICS_EPOCH */ + if(TSdata.async_type==TS_async_time) + { + /* this is time protocol */ + timeValue=ntohl(buf_data); + ts->tv_sec = timeValue; + ts->tv_nsec = 0; + Debug(2,"got the Time protocol time %lu= \n",timeValue); + } + else + { + /* this is ntp - need to convert to ns */ + ts->tv_sec=ntohl(buf_ntp.transmit_ts.tv_sec); + timeValue=ntohl(buf_ntp.transmit_ts.tv_nsec); + ts->tv_nsec = TSfractionToNano(timeValue); + if(MAKE_DEBUG>=2) + printf("got the NTP time %9.9lu.%9.9lu\n",ts->tv_sec,timeValue); + } + close(soc); + return 0; +} + +/* +TSgetMasterTime() - query the master timing IOC for it's current time +This routine will be converted to a task and block on a semaphore, +when it gets the semaphore, it will go to the master timing IOC +and get a time stamp, the time stamp will be used to adjust the +vxworks clock. To use it for software timing, just put a watchdog +out there that pokes the semaphore. Another way to manage soft +timing is to catch broadcasts from the master, round trip +calculations cannot be made when doing this. +*/ +static long TSgetMasterTime(struct timespec* tsp) +{ + TSstampTrans stran; + struct timespec curr_time,tran_time; + struct sockaddr_in sin; + struct sockaddr fs; + int soc; + + Debug0(3,"TSgetMasterTime() called\n"); + + if( (soc=TSgetBroadcastSocket(0,&sin)) <0) + { Debug0(1,"TSgetBroadcastSocket failed\n"); return -1; } + sin.sin_port = htons(TSdata.master_port); + memcpy(&TSdata.hunt,&sin,sizeof(sin)); + stran.type=(TStype)htonl(TS_time_request); + stran.magic=htonl(TS_MAGIC); + + if(TSgetData((char*)&stran,sizeof(stran),soc, + (struct sockaddr*)&sin,&fs,&tran_time)<0) + { Debug0(2,"no reply from master server\n"); close(soc); return -1; } + + /* check the magic number */ + if(ntohl(stran.magic)!=(TS_MAGIC)) + { + + TSprintf("TSgetMasterTime: invalid packet received\n"); + close(soc); + return -1; + } + /* we now have several pieces of information: + the master's address, + the approximate round trip time, + and the master's processing time. */ + /* set the global data structure for information from master */ + Debug(8,"master port=%d\n",ntohs(((struct sockaddr_in*)&fs)->sin_port)); + TSdata.master = fs; + TSdata.state = TS_master_alive; + if(TSdata.type==TS_sync_slave) + { + TSdata.clock_hz = ntohl(stran.clock_hz); + TSdata.sync_rate = ntohl(stran.sync_rate); + TSdata.clock_conv = TS_BILLION / TSdata.clock_hz; + } + + if(MAKE_DEBUG>=6) + { + printf("round trip time: %9.9lu.%9.9lu\n", + tran_time.tv_sec,tran_time.tv_nsec); + printf("master time: %9.9lu.%9.9lu\n", + stran.master_time.tv_sec,stran.master_time.tv_nsec); + } + + /* Halve the round-trip time to estimate the time stamp error */ + tran_time.tv_nsec >>= 1; + if (tran_time.tv_sec & 1) tran_time.tv_nsec += (TS_BILLION >> 1); + tran_time.tv_sec >>= 1; + + /* add the error estimate to the time stamp from master */ + curr_time.tv_sec = ntohl(stran.current_time.tv_sec); + curr_time.tv_nsec = ntohl(stran.current_time.tv_nsec); + TSaddStamp(tsp,&curr_time,&tran_time); + close(soc); + return 0; +} + +/* +TSsetClockFromUnix() - query the time from boot server and set the +vxworks clock. +*/ +long TSsetClockFromUnix(void) +{ + struct timespec tp; + int key; + unsigned long ulongtemp; + + if(!TSinitialized) TSinit(); + Debug0(3,"in TSsetClockFromUnix()\n"); + + if(TSgetUnixTime(&tp)!=0) return -1; + + ulongtemp = TSepochNtpToUnix(&tp); + tp.tv_sec = (long)ulongtemp; + + if(MAKE_DEBUG>=9) + printf("set time: %9.9lu.%9.9lu\n", tp.tv_sec,tp.tv_nsec); + + /* set the vxWorks clock to the correct time */ + if(clock_settime(CLOCK_REALTIME,&tp)<0) + { Debug0(1,"clock_settime failed\n"); } + + /* adjust time to use the EPICS EPOCH of 1990 */ + /* this is wrong if leap seconds accounted for */ + ulongtemp = TSepochUnixToEpics(&tp); + tp.tv_sec = ulongtemp; + + /* set the EPICS event time table sync entry (current time) */ + key=intLock(); + TSdata.event_table[TSdata.sync_event]=tp; + TSdata.event_table[0]=tp; + intUnlock(key); + + if(MAKE_DEBUG>=9) + printf("epics time: %9.9lu.%9.9lu\n", + TSdata.event_table[TSdata.sync_event].tv_sec, + TSdata.event_table[TSdata.sync_event].tv_nsec); + + return 0; +} + +/* +TSsetClockFromMaster() - set the vxworks clock using the time from +the master timing IOC +*/ +static long TSsetClockFromMaster() +{ + struct timespec tp; + int key; + unsigned long secs; + + Debug0(3,"in TSsetClockFromMaster()\n"); + + if(TSgetMasterTime(&tp)<0) return -1; + + key=intLock(); + TSdata.event_table[TSdata.sync_event]=tp; + TSdata.event_table[0]=tp; + intUnlock(key); + + /* adjust time to use the Unix EPOCH of 1900 - not to good */ + /* making this adjustment is not so good */ + secs = TSepochEpicsToUnix(&tp); + tp.tv_sec = secs; + clock_settime(CLOCK_REALTIME,&tp); + + return 0; +} + +/**************************************************************************/ +/* broadcast socket creation utilites */ +/**************************************************************************/ + +/* +TSgetBroadcastSocket() - return a broadcast socket for a port, return +a sockaddr also. +*/ +int TSgetBroadcastSocket(int port, struct sockaddr_in* sin) +{ + int on=1; + int soc; + + sin->sin_port=htons(port); + sin->sin_family=AF_INET; + sin->sin_addr.s_addr=htonl(INADDR_ANY); + if( (soc=epicsSocketCreate(AF_INET,SOCK_DGRAM,0)) < 0 ) + { perror("socket create failed"); return -1; } + + setsockopt(soc,SOL_SOCKET,SO_BROADCAST,(char*)&on,sizeof(on)); + + if( bind(soc,(struct sockaddr*)sin,sizeof(struct sockaddr_in)) < 0 ) + { perror("socket bind failed"); close(soc); return -1; } + + if( TSgetBroadcastAddr(soc,(struct sockaddr*)sin) < 0 ) return -1; + return soc; +} + +/* +TSgetBroadcastAddr() - use the libCom/osi/osiSock.h calls +*/ +long TSgetBroadcastAddr(int soc, struct sockaddr* sin) +{ + ELLLIST tmpList; + osiIntfAddrNode *pNode; + + ellInit ( &tmpList ); + osiSockDiscoverInterfaceAddresses ( &tmpList,soc,(const osiSockAddr*)sin); + /* take first */ + pNode = (osiIntfAddrNode *)ellFirst(&tmpList); + if(!pNode) { + printf("no broadcast address found\n"); return -1; + } + memcpy((char*)sin,(char*)&pNode->allPointsAddr,sizeof(struct sockaddr)); + /* cleanup */ + while((pNode = (osiIntfAddrNode *)ellFirst(&tmpList))) { + ellDelete(&tmpList,(ELLNODE *)pNode); + free(pNode); + } + return(0); +} + +/**************************************************************************/ +/* time stamp utility routines */ +/**************************************************************************/ + +/* +TSsyncTheTime - given the current time (cts), and a compare time +stamp (ts), correct the current time clock to match ts. +*/ +static long TSsyncTheTime(struct timespec* cts, struct timespec* ts) +{ + int key; + long diffSec; + long diffNsec; + long diffSecAbs; + + diffSec = (long)ts->tv_sec -(long) cts->tv_sec; + diffNsec = (long)ts->tv_nsec - (long)cts->tv_nsec; + diffSecAbs = (diffSec<0) ? -diffSec : diffSec; + /* see if clock can be corrected in sync window - up to 1 secs per tick */ + if(diffSecAbs <= TSdata.sync_rate) + { + long diff; + long count; + count=sysClkRateGet()*TSdata.sync_rate; + diff = (TS_BILLION/count)*diffSec + diffNsec/count; + if(diff) { + key=intLock(); + correction_count = count; + correction_factor = diff; + intUnlock(key); + } + Debug(5,"Correction Factor=.%9.9ld\n",correction_factor); + } + else + { + /* clock can not be corrected - jam it for now */ + TSdata.event_table[TSdata.sync_event]=*ts; + if(MAKE_DEBUG>=7) + { + printf("Slave not in sync: mine=%9.9lu.%9.9lu!=%lu.%lu=other\n", + cts->tv_sec, cts->tv_nsec, ts->tv_sec, ts->tv_nsec); + printf("slave diff time: %9.9ld.%9.9ld\n", + diffSec,diffNsec); + } + } + return 0; +} + +/**************************************************************************/ +/* varies server that can be running */ +/**************************************************************************/ + +long TSstartSyncServer() +{ + /* run at priority just above CA */ + return taskSpawn("ts_syncS",TS_SYNC_SERVER_PRI,VX_FP_TASK|VX_STDIO,5000, + (FUNCPTR)TSsyncServer,0,0,0,0,0,0,0,0,0,0); +} + +/* +TSsyncServer() - Server task that broadcasts the sync time stamp every +time a sync event is receive by the event system. +*/ +static void TSsyncServer() +{ + TSstampTrans stran; + struct sockaddr_in sin; + int soc; + + if( (soc=TSgetBroadcastSocket(0,&sin)) <0) + { Debug0(1,"TSgetBroadcastSocket failed\n"); return; } + sin.sin_port = htons(TSdata.slave_port); + stran.type=(TStype)htonl(TS_sync_msg); + stran.magic=htonl(TS_MAGIC); + stran.sync_rate=htonl(TSdata.sync_rate); + stran.clock_hz=htonl(TSdata.clock_hz); + while(1) + { + /* wait for a sync to occur */ + semTake(TSdata.sync_occurred,WAIT_FOREVER); + + if(!TSdata.ts_sync_valid) + { + int key; + struct timespec ts; + /* remember, the ts table has only an approximation to the + correct time before the event is actually up and running, + so validate the ts if this is the first sync - the vxWorks + clock has a good time in it + */ + if(TSgetTime(&ts)==0) TSdata.ts_sync_valid=1; + key=intLock(); + /* don't adjust time from getTime() to EPICS epoch */ + TSdata.event_table[TSdata.sync_event].tv_nsec=ts.tv_nsec; + TSdata.event_table[TSdata.sync_event].tv_sec=ts.tv_sec; + intUnlock(key); + } + stran.master_time.tv_sec= + htonl(TSdata.event_table[TSdata.sync_event].tv_sec); + stran.master_time.tv_nsec= + htonl(TSdata.event_table[TSdata.sync_event].tv_nsec); + if(sendto(soc,(char*)&stran,sizeof(stran),0, + (struct sockaddr*)&sin,sizeof(sin))<0) + { perror("sendto failed"); } + Debug(8,"time send secs = %lu\n",stran.master_time.tv_sec); + Debug(8,"time send nsecs = %lu\n",stran.master_time.tv_nsec); + } + close(soc); + return; +} + +/*-----------------------------------------------------------------------*/ +long TSstartAsyncClient() +{ + /* run at priority just above CA */ + return taskSpawn("ts_Casync",TS_ASYNC_CLIENT_PRI,VX_FP_TASK|VX_STDIO,5000, + (FUNCPTR)TSasyncClient,0,0,0,0,0,0,0,0,0,0); +} + +/* +TSasyncClient() - keep the async slave in sync with ntp server +while checking for master timing IOC to come up + + algorithm: + - sync with master if available + - sync with unix if no master, checking every so often for master + - sync with unix if master goes away until master comes back + - continually check for master if no unix sync available +*/ +long TSasyncClient() +{ + BOOT_PARAMS bootParms; + TSstampTrans stran; + TS_NTP buf_ntp; + struct sockaddr_in sin_unix,sin_bc,sin_master; + int count,soc_unix,soc_master,soc_bc,buf_size; + struct timespec ts,diff_time,cts,curr_time; + unsigned long nsecs; + char host_addr[BOOT_ADDR_LEN]; + int status; + + Debug0(2,"in TSasyncClient()\n"); + + /* could open two sockets here, one to contact unix, one to find master */ + + /*------socket for unix server----------*/ + if(envGetConfigParam(&EPICS_TS_NTP_INET,BOOT_ADDR_LEN,host_addr)==NULL + || strlen(host_addr)==0) + { + /* use boot host if the environment variable not set */ + bootStringToStruct(sysBootLine,&bootParms); + /* bootParms.had = host IP address */ + strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN); + } + + if( (soc_unix=TSgetSocket(0,&sin_unix)) <0) + { printf("TSgetSocket failed\n"); return -1; } + + status = aToIPAddr (host_addr, UDP_NTP_PORT, &sin_unix); + if (status) { + printf("TSasyncClient: bad host name or IP address host %s port %d\n", + host_addr,UDP_NTP_PORT); + close (soc_unix); + return -1; + } + + /*------socket for finding master----------*/ + if( (soc_bc=TSgetBroadcastSocket(0,&sin_bc)) <0) + { printf("TSgetBroadcastSocket failed\n"); close(soc_unix); return -1; } + + sin_bc.sin_port = htons(TSdata.master_port); + /*-----------------------------------------*/ + + while(1) + { + /* stop the clock from correcting here - probably better to use + a watch dog to stop the clock correction */ + + if(TSdata.state==TS_master_alive) + { + /* get socket to master */ + Debug0(5,"async_client(): master_alive\n"); + if( (soc_master=TSgetSocket(0,&sin_master)) <0) + { Debug0(1,"TSgetSocket failed\n"); } + + while(TSdata.state==TS_master_alive) + { + /* sync with the master as long as it is up */ + Debug0(5,"async_client(): syncing with master\n"); + stran.type=(TStype)htonl(TS_time_request); + stran.magic=htonl(TS_MAGIC); + + if(TSgetData((char*)&stran,sizeof(stran),soc_master, + &TSdata.master,NULL,&diff_time)<0) + { + Debug0(2,"no reply from master server\n"); + TSdata.state=TS_master_dead; + close(soc_master); + } + else + { + if(ntohl(stran.magic)==TS_MAGIC) + { + /* sync the time with master's here - 2 ticks */ + cts=TSdata.event_table[TSdata.sync_event]; + curr_time.tv_sec=ntohl(stran.current_time.tv_sec); + curr_time.tv_nsec=ntohl(stran.current_time.tv_nsec); + TSsyncTheTime(&cts,&curr_time); + Debug(8,"master sec = %lu\n",curr_time.tv_sec); + Debug(8,"my sec = %lu\n", cts.tv_sec); + } + else + { + TSprintf("TSasyncClient: invalid packet recved\n"); + } + taskDelay(sysClkRateGet()*TSdata.sync_rate); + } + } + } + else if(TSdata.async_type==TS_async_ntp) + { + /* sync with unix server using NTP - master check now and then */ + Debug0(5,"async_client(): using NTP sync\n"); + count=0; + while(TSdata.state==TS_master_dead) + { + Debug0(5,"async_client(): syncing with unix\n"); + memset(&buf_ntp,0,sizeof(buf_ntp)); + buf_ntp.info[0]=0x0b; + buf_size=sizeof(buf_ntp); + + if(TSgetData((char*)&buf_ntp,sizeof(buf_ntp),soc_unix, + (struct sockaddr*)&sin_unix,NULL,&diff_time)<0) + { + Debug0(2,"no reply from NTP server\n"); + } + else + { + unsigned long ulongtemp; + /* get the current time */ + cts=TSdata.event_table[TSdata.sync_event]; + /* adjust the ntp time */ + ts.tv_sec = ntohl(buf_ntp.transmit_ts.tv_sec); + ulongtemp = TSepochNtpToEpics(&ts); + ts.tv_sec = (long)ulongtemp; + nsecs=ntohl(buf_ntp.transmit_ts.tv_nsec); + ts.tv_nsec = TSfractionToNano(nsecs); + TSsyncTheTime(&cts,&ts); + } + if(count==0) + { + stran.type=(TStype)htonl(TS_time_request); + stran.magic=htonl(TS_MAGIC); + + if(TSgetData((char*)&stran,sizeof(stran),soc_bc, + (struct sockaddr*)&sin_bc,&TSdata.master,&diff_time)<0) + { Debug0(2,"no reply from master server\n"); } + else + { + TSdata.state=TS_master_alive; + Debug(8,"master port = %d\n", + ntohs( ((struct sockaddr_in*) + &TSdata.master)->sin_port)); + } + + count=(TSdata.sync_rate>TS_SECS_ASYNC_TRY_MASTER)? +TS_SECS_ASYNC_TRY_MASTER: + TS_SECS_ASYNC_TRY_MASTER/TSdata.sync_rate; + } + else count--; + taskDelay(sysClkRateGet()*TSdata.sync_rate); + } + } + else + { + /* try to find a master */ + while(TSdata.state==TS_master_dead) + { + stran.type=(TStype)htonl(TS_time_request); + stran.magic=htonl(TS_MAGIC); + + if(TSgetData((char*)&stran,sizeof(stran),soc_bc, + (struct sockaddr*)&sin_bc,&TSdata.master,&diff_time)<0) + { Debug0(2,"no reply from master server\n"); } + else + TSdata.state=TS_master_alive; + taskDelay(sysClkRateGet()*TS_SECS_SYNC_TRY_MASTER); + } + } + } +} + +static long TSstartSyncClient() +{ + return taskSpawn("ts_syncC",TS_SYNC_CLIENT_PRI,VX_FP_TASK|VX_STDIO,5000, + (FUNCPTR)TSsyncClient,0,0,0,0,0,0,0,0,0,0); +} + +/* +TSsyncClient() - Client task that listens for sync time stamp on a port +and verifies with this IOC sync time stamp. This is only to be used +with event system timing. +*/ +static void TSsyncClient() +{ + TSstampTrans stran; + struct sockaddr_in sin; + struct sockaddr fs; + int num,mlen,soc,fl; + struct timespec mast_time; + fd_set readfds; + int key; + + /* if no master then use unix and ntp to sync time + all aync slaves will operate in polled mode */ + + if(TSdata.type!=TS_sync_slave) return; + + /* check for master to be there every so many minutes */ + if(TSdata.state != TS_master_alive) + while( TSsetClockFromMaster()<0 ) + taskDelay(sysClkRateGet()*TS_SECS_SYNC_TRY_MASTER); + if( (soc=TSgetSocket(TSdata.slave_port,&sin)) <0) + { Debug0(1,"TSgetSocket failed\n"); return; } + while(1) + { + FD_ZERO(&readfds); + FD_SET(soc,&readfds); + + /* could set up timeout for (sync_rate + epsilon) + to see if one is missed */ + + num=select(FD_SETSIZE,&readfds,(fd_set*)NULL,(fd_set*)NULL,NULL); + if(num==ERROR) { perror("select failed"); continue; } + + fl = sizeof(fs); + if((mlen=recvfrom(soc,(char*)&stran,sizeof(stran),0,&fs,&fl))<0) + { perror("recvfrom failed"); continue; } + + if(ntohl(stran.magic)!=TS_MAGIC) + { + TSprintf("TSsyncClient: invalid packet received\n"); + continue; + } + + /* get the master time out of packet */ + mast_time.tv_sec=ntohl(stran.master_time.tv_sec); + mast_time.tv_nsec=ntohl(stran.master_time.tv_nsec); + + Debug0(6,"Received sync request from master\n"); + if(MAKE_DEBUG>=8) + { + printf("time received=%9.9lu.%9.9lu\n", + mast_time.tv_sec,mast_time.tv_nsec); + } + + /* verify the sync event with this one */ + if( TSdata.event_table[TSdata.sync_event].tv_sec!=mast_time.tv_sec + || TSdata.event_table[TSdata.sync_event].tv_nsec!=mast_time.tv_nsec) + { + TSprintf("sync Slave not in sync: %lu,%lu != %lu,%lu\n", + TSdata.event_table[TSdata.sync_event].tv_sec, + TSdata.event_table[TSdata.sync_event].tv_nsec, + mast_time.tv_sec, mast_time.tv_nsec); + key=intLock(); + TSdata.event_table[TSdata.sync_event].tv_sec=mast_time.tv_sec; + TSdata.event_table[TSdata.sync_event].tv_nsec=mast_time.tv_nsec; + intUnlock(key); + } + } + close(soc); + return; +} + +static long TSstartStampServer() +{ + return taskSpawn("ts_stamp",TS_STAMP_SERVER_PRI,VX_FP_TASK|VX_STDIO,5000, + (FUNCPTR)TSstampServer,0,0,0,0,0,0,0,0,0,0); +} + +/* +TSstampServer() - Server task on master timing IOC that listens for +time stamp requests and sync request from slaves. +*/ +static void TSstampServer() +{ + TSstampTrans stran; + struct sockaddr_in sin; + struct sockaddr fs; + struct timespec ts; + int mlen,soc,fl; + TStype type; + + if( (soc=TSgetSocket(TSdata.master_port,&sin)) <0) + { Debug0(1,"TSgetSocket failed\n"); return; } + + stran.type=(TStype)htonl(TS_time_request); + stran.magic=htonl(TS_MAGIC); + + while(1) + { + fl=sizeof(fs); + if((mlen=recvfrom(soc,(char*)&stran,sizeof(stran),0,&fs,&fl))<0) + { perror("recvfrom failed"); continue; } + + if(ntohl(stran.magic)!=TS_MAGIC) + { + TSprintf("TSstampServer(): invalid packet received\n"); + continue; + } + + type=(TStype)ntohl(stran.type); + switch(type) + { + case TS_time_request: + TSgetTimeStamp(TSdata.sync_event,&ts); + stran.master_time.tv_sec=htonl(ts.tv_sec); + stran.master_time.tv_nsec=htonl(ts.tv_nsec); + + if(TSdata.has_event_system) + TSaccurateTimeStamp(&ts); + else + TScurrentTimeStamp(&ts); + stran.current_time.tv_sec=htonl(ts.tv_sec); + stran.current_time.tv_nsec=htonl(ts.tv_nsec); + stran.sync_rate = htonl(TSdata.sync_rate); + stran.clock_hz = htonl(TSdata.clock_hz); + Debug0(4,"Slave requesting time\n"); + if(sendto(soc,(char*)&stran,sizeof(stran),0,&fs,fl)<0) + { perror("sendto to slave failed"); } + break; + case TS_sync_request: TSforceSync(0); break; + default: + Debug0(1,"Unknown transaction type from slave\n"); + } + } + close(soc); + return; +} + +/*************************************************************************/ +/* utility routines for managing time retrieval */ +/*************************************************************************/ + +/* get the current time from time stamp support software */ +long TScurrentTimeStamp(struct timespec* sp) +{ + TSgetTimeStamp(0,sp); + return 0; +} + +/* get the current time from time stamp support software */ +long TSaccurateTimeStamp(struct timespec* sp) +{ + struct timespec ts; + unsigned long ticks; + + if(!TSinitialized) TSinit(); + if(TSdata.has_direct_time==1) + { + TSgetTime(sp); + return 0; + } + + TSgetTicks(0,&ticks); /* add in the board time */ + *sp = TSdata.event_table[TSdata.sync_event]; + + /* calculate a time stamp from the tick count */ + ts.tv_sec = ticks / TSdata.clock_hz; + ts.tv_nsec=(ticks-(ts.tv_sec*TSdata.clock_hz))*TSdata.clock_conv; + + sp->tv_sec += ts.tv_sec; + sp->tv_nsec += ts.tv_nsec; + + /* adjust seconds if needed */ + if(sp->tv_nsec >= TS_BILLION) + { + sp->tv_sec++; + sp->tv_nsec -= TS_BILLION; + } + return 0; +} + +/* get the current time from vxWorks time clock */ +static long TSgetCurrentTime(struct timespec* ts) +{ + long rc; + rc=clock_gettime(CLOCK_REALTIME,ts); + ts->tv_sec = TSepochUnixToEpics(ts); + return rc; +} + +/* routine for causing sync to occur (not a hardware one) */ +static long TSforceSoftSync(int Card) +{ + semGive(TSdata.sync_occurred); + return 0; +} + +/* +TSaddStamp - Add time stamp op1 to time stamp op2 giving time stamp +result +*/ +static void TSaddStamp( struct timespec* result, + struct timespec* op1, struct timespec* op2) +{ + result->tv_sec = op1->tv_sec + op2->tv_sec; + result->tv_nsec = op1->tv_nsec + op2->tv_nsec; + + /* adjust seconds if needed */ + if(result->tv_nsec >= TS_BILLION) + { + result->tv_sec++; + result->tv_nsec -= TS_BILLION; + } + return; +} + +/* +TScalcDiff() - Calculate the difference between to time stamps. The +difference between arguments 'a' and 'b' is returned in 'diff'. The +return value from this routine is the direction: (1)=a>b, (-1)=atv_sec < b->tv_sec) { dir=-1; big_time=b; small_time=a; } + else if(a->tv_sec > b->tv_sec) { dir=1; small_time=b; big_time=a; } + else if(a->tv_nsec == b->tv_nsec){ dir=0; small_time=b; big_time=a;} + else if(a->tv_nsec < b->tv_nsec) { dir=-1; big_time=b; small_time=a; } + else { dir=1; small_time=b; big_time=a; } + + diff->tv_sec=big_time->tv_sec-small_time->tv_sec; + + if(big_time->tv_nsec>=small_time->tv_nsec) + diff->tv_nsec=big_time->tv_nsec-small_time->tv_nsec; + else + { + diff->tv_nsec = TS_BILLION + big_time->tv_nsec-small_time->tv_nsec; + diff->tv_sec--; + } + /* ---------------------this block sucks--------------------*/ + return dir; +} + +/**************************************************************************/ +/* more routines for managing sockets */ +/**************************************************************************/ + +/* get a UDP socket for a port, return a sockaddr for it. */ +static int TSgetSocket(int port, struct sockaddr_in* sin) +{ + int soc; + + sin->sin_port=htons(port); + sin->sin_family=AF_INET; + sin->sin_addr.s_addr=htonl(INADDR_ANY); + if( (soc=epicsSocketCreate(AF_INET,SOCK_DGRAM,0)) < 0 ) + { perror("socket create failed"); return -1; } + Debug(5,"sizeof sin = %d\n", (int) sizeof(struct sockaddr_in)); + if( bind(soc,(struct sockaddr*)sin,sizeof(struct sockaddr_in)) < 0 ) + { perror("socket bind failed"); close(soc); return -1; } + + return soc; +} + +/* attempt to get data from a socket after sending a request to it. +0ptionally return round trip time and the sockaddr of the responsedent. +*/ +static long TSgetData(char* buf, int buf_size, int soc, + struct sockaddr* to_sin, struct sockaddr* from_sin, + struct timespec* round_trip) +{ + int retry_count=0; + int num,mlen,flen; + fd_set readfds; + volatile unsigned long s,us; + struct timeval timeOut; + struct timespec send_time,recv_time; + struct sockaddr local_sin; + + if (!from_sin) { + /* Tornado 2.2 doesn't like NULLs in recvfrom() */ + from_sin = &local_sin; + } + flen = sizeof(*from_sin); + + /* + * joh 08-26-99 + * added this code which removes responses laying around from + * requests made in the past that timed out + */ + Debug(8,"removing stale responses %s\n", ""); + while (1) { + int status; + + status = ioctl (soc, FIONREAD, (int) &mlen); + if (status<0) { + Debug(1,"ioctl FIONREAD failed because \"%s\"?\n", strerror(errno)); + break; + } + else if (mlen==0) { + break; + } + Debug(1,"removing stale response of %d bytes\n", mlen); + recvfrom(soc,buf,buf_size,0,from_sin,&flen); + } + + /* convert millisecond time out to seconds/microseconds */ + s=TSdata.time_out/1000; us=(TSdata.time_out-(s*1000))*1000; + Debug(6,"time_out Second=%lu\n",s); + Debug(6,"time_out Microsecond=%lu\n",us); + do + { + Debug(8,"sendto port %d\n", + ntohs(((struct sockaddr_in*)to_sin)->sin_port)); + if(round_trip) clock_gettime(CLOCK_REALTIME,&send_time); + if( sendto(soc,buf,buf_size,0,to_sin,sizeof(struct sockaddr)) < 0 ) + { perror("sendto failed"); return -1; } + FD_ZERO(&readfds); FD_SET(soc,&readfds); + timeOut.tv_sec=s; timeOut.tv_usec=us; + num=select(FD_SETSIZE,&readfds,(fd_set*)NULL,(fd_set*)NULL,&timeOut); + Debug(9,"select returned %d\n", num); + if(round_trip) clock_gettime(CLOCK_REALTIME,&recv_time); + if(num==ERROR) { perror("select failed"); return -1; } + } + while(num==0 && ++retry_count= TS_RETRY_COUNT) + { Debug0(5,"TSgetData - retry count exceeded\n"); return -1; } + else + { + /* data available */ + mlen=recvfrom(soc,buf,buf_size,0,from_sin,&flen); + if(mlen < 0) { perror("recvfrom failed"); return -1; } + if(from_sin) + { + Debug(8,"recvfrom port %d\n", + ntohs(((struct sockaddr_in*)from_sin)->sin_port)); + Debug(8,"flen = %d\n",flen); + Debug(8,"mlen = %d\n",mlen); + } + if(round_trip) TScalcDiff(&send_time,&recv_time,round_trip); + } + return mlen; +} + +/*************************************************************************/ +/* all the functions that follow are test functions */ +/*************************************************************************/ + +/* test function to print the IOC real time clock value. */ +void TSprintRealTime() +{ + struct timespec tp; + + TSgetTime(&tp); + printf("real time clock = %lu,%lu\n",tp.tv_sec,tp.tv_nsec); + printf("EPICS clock = %lu,%lu\n", + TSdata.event_table[TSdata.sync_event].tv_sec, + TSdata.event_table[TSdata.sync_event].tv_nsec); + return; +} + +/* test function to print an event time stamp */ +void TSprintTimeStamp(int num) +{ + struct timespec tp; + + TSgetTimeStamp(num,&tp); + printf("event %d occurred: %lu.%lu\n",num,tp.tv_sec,tp.tv_nsec); + return; +} + +/* test function to print the current time using event system */ +void TSprintCurrentTime() +{ + struct timespec tp; + + TScurrentTimeStamp(&tp); + printf("Current Event System time: %lu.%lu\n",tp.tv_sec,tp.tv_nsec); + TSaccurateTimeStamp(&tp); + printf("Accurate Event System time: %lu.%lu\n",tp.tv_sec,tp.tv_nsec); + return; +} + +/* test function to query the boot server for the current time. */ +void TSprintUnixTime() +{ + struct timespec ts; + + if(TSgetUnixTime(&ts)!=0) + { + printf("Could not get Unix time\n"); + return; + } + printf("boot server time clock = %lu, %lu\n",ts.tv_sec,ts.tv_nsec); + return; +} + +/* test function to print the master timing IOC time stamp. */ +void TSprintMasterTime() +{ + struct timespec ts; + + if(TSgetMasterTime(&ts)!=0) + { + printf("Could not get Unix time\n"); + return; + } + printf("master time clock = %lu, %lu\n",ts.tv_sec,ts.tv_nsec); + return; +} + +/* gross and horrid example */ +long TSgetFirstOfYearVx(struct timespec* ts) +{ + time_t tloc; + struct tm t; + + if(!TSinitialized) TSinit(); + time(&tloc); /* retrieve the current time */ + localtime_r(&tloc,&t); + t.tm_sec=0; + t.tm_min=0; + t.tm_hour=t.tm_isdst?1:0; + t.tm_mday=1; + t.tm_mon=0; + tloc=mktime(&t); + ts->tv_sec=tloc; + ts->tv_nsec=0; + return 0; +} diff --git a/src/libCom/osi/os/vxWorks/drvTS.h b/src/libCom/osi/os/vxWorks/drvTS.h new file mode 100644 index 000000000..1c891a5f6 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/drvTS.h @@ -0,0 +1,204 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef __DRVTS_h__ +#define __DRVTS_h__ + +/************************************************************************** + * + * Author: Jim Kowalkowski + * + ***********************************************************************/ + +#include +#include +#include +#include +#include + +#include "epicsTime.h" + +#ifdef TS_DRIVER +#define TS_EXTERN +#else +#define TS_EXTERN extern +#endif + +#define ER_EVENT_RESET_TICK 0x7d /* Reset the tick counter */ + +#define TS_MAGIC ('T'<<24|'S'<<16|'d'<<8|'r') +#define TS_SLAVE_PORT 18322 +#define TS_MASTER_PORT 18323 +#define TS_RETRY_COUNT 4 +#define TS_TIME_OUT_MS 250 +#define TS_SECS_ASYNC_TRY_MASTER (60*5) /* every five minutes */ +#define TS_SECS_SYNC_TRY_MASTER (60*1) /* every one minute */ + +#define UDP_TIME_PORT 37 +#define UDP_NTP_PORT 123 + +#define TS_BILLION 1000000000 +#define TS_SYNC_RATE_SEC 10 +#define TS_CLOCK_RATE_HZ 1000 +#define TS_TOTAL_EVENTS 128 +/*Following is (SEC_IN_YEAR*90)+(22*SEC_IN_DAY) */ +/*22 is leap years from 1900 to 1990*/ +#define TS_1900_TO_EPICS_EPOCH 2840140800UL +/*Following is (SEC_IN_YEAR*70)+(17*SEC_IN_DAY) */ +/*17 is leap years from 1900 to 1970*/ +#define TS_1900_TO_VXWORKS_EPOCH 2208988800UL +/*Following is (SEC_IN_YEAR*20)+(5*SEC_IN_DAY) */ +/*5 is leap years from 1970 to 1990*/ +#define TS_VXWORKS_TO_EPICS_EPOCH 631152000UL + +#define TS_STAMP_SERVER_PRI 70 +#define TS_SYNC_SERVER_PRI 70 +#define TS_SYNC_CLIENT_PRI 70 +#define TS_ASYNC_CLIENT_PRI 70 + +typedef enum { TS_time_request, TS_sync_request, TS_sync_msg } TStype; +typedef enum { TS_master_alive, TS_master_dead } TSstate; +typedef enum { TS_async_none, TS_async_private, + TS_async_ntp, TS_async_time } TStime_protocol; +typedef enum { TS_sync_master, TS_async_master, + TS_sync_slave, TS_async_slave, + TS_direct_master, TS_direct_slave} TStime_type; + +struct TSstampTransStruct { + unsigned long magic; /* identifier */ + TStype type; /* transaction type */ + struct timespec master_time; /* master time stamp - last sync time */ + struct timespec current_time; /* master current time stamp 1990 epoch */ + struct timespec unix_time; /* time using 1900 epoch */ + unsigned long sync_rate; /* master sends sync at this rate */ + unsigned long clock_hz; /* master clock this frequency (tick rate) */ +}; +typedef struct TSstampTransStruct TSstampTrans; + +struct TSinfoStruct { + TSstate state; + TStime_type type; + TStime_protocol async_type; + int ts_sync_valid; + + struct timespec *event_table; /* timestamp table */ + + unsigned long sync_rate; /* master send sync at this rate */ + unsigned long clock_hz; /* master clock is this frequency */ + unsigned long clock_conv;/* conversion factor for tick_rate->ns */ + unsigned long time_out; /* udp packet time-out in milliseconds */ + int master_timing_IOC; /* 1=master, 0=slave */ + int master_port; /* port that master listens on */ + int slave_port; /* port that slave listens on */ + int total_events; /* this is the total event in the event system*/ + int sync_event; /* this is the sync event number */ + int has_event_system; /* 1=has event system, 0=no event system */ + int has_direct_time; /* 1=has direct time, 0=no direct time */ + int UserRequestedType; /* let user force the setting of type */ + + SEM_ID sync_occurred; + + struct sockaddr hunt; /* broadcast address info */ + struct sockaddr master; /* socket info for contacting master */ +}; +typedef struct TSinfoStruct TSinfo; + +/* global functions */ +#ifdef __cplusplus +extern "C" { +#endif +TS_EXTERN long TSinit(void); +TS_EXTERN long TSgetTimeStamp(int event_number,struct timespec* ts); +TS_EXTERN unsigned long TSepochNtpToUnix(struct timespec* ts); +TS_EXTERN unsigned long TSfractionToNano(unsigned long fraction); +TS_EXTERN unsigned long TSepochNtpToEpics(struct timespec* ts); +TS_EXTERN unsigned long TSepochUnixToEpics(struct timespec* ts); +TS_EXTERN unsigned long TSepochEpicsToUnix(struct timespec* ts); +TS_EXTERN long TScurrentTimeStamp(struct timespec* ts); +TS_EXTERN long TSaccurateTimeStamp(struct timespec* ts); +TS_EXTERN long TSgetFirstOfYearVx(struct timespec* ts); +TS_EXTERN void TSconfigure(int master, int sync_rate_sec, int clock_rate_hz, + int master_port, int slave_port, + unsigned long millisecond_request_time_out, int type); +TS_EXTERN long TSsetClockFromUnix(void); + +#ifndef TS_DRIVER +TS_EXTERN TSinfo TSdata; +TS_EXTERN TSdirectTimeVar; /* set to !=0 to indicate direct time available*/ +TS_EXTERN TSgoodTimeStamps; /* force best time stamps by setting != 0 */ +#endif + +#ifdef __cplusplus +}; +#endif + +/* NTP information - all this is backwards and documentation only */ +#define VN_SHIFT 2 /* Version - 3 bits */ +#define VN_version 3< EPICS and avoid architecture knowledge + */ + +#include "epicsDynLink.h" + +STATUS symFindByNameEPICS( + SYMTAB_ID symTblId, + char *name, + char **ppvalue, + SYM_TYPE *pType ) +{ + static int leadingUnderscore = 1; + static int init = 0; + STATUS status = ERROR; + + if (!init) { + char *pSymValue; + SYM_TYPE type; + status = symFindByName ( symTblId, "symFindByNameEPICS", &pSymValue, &type ); + if (status==OK) { + leadingUnderscore = 0; + } + init = 1; + } + + if (name[0] != '_' || leadingUnderscore) { + status = symFindByName ( symTblId, name, ppvalue, pType ); + } + else { + status = symFindByName ( symTblId, (name+1), ppvalue, pType ); + } + + return status; +} + +STATUS symFindByNameAndTypeEPICS( + SYMTAB_ID symTblId, + char *name, + char **ppvalue, + SYM_TYPE *pType, + SYM_TYPE sType, + SYM_TYPE mask ) +{ + static int leadingUnderscore = 1; + static int init = 0; + STATUS status = ERROR; + + if (!init) { + char *pSymValue; + SYM_TYPE type; + status = symFindByName (symTblId, "symFindByNameAndTypeEPICS", &pSymValue, &type ); + if (status==OK) { + leadingUnderscore = 0; + } + init = 1; + } + + if (name[0] != '_' || leadingUnderscore) { + status = symFindByNameAndType ( symTblId, name, ppvalue, pType, sType, mask ); + } + else if (leadingUnderscore) { + status = symFindByNameAndType ( symTblId, (name+1), ppvalue, pType, sType, mask ); + } + + return status; +} + + + diff --git a/src/libCom/osi/os/vxWorks/epicsDynLink.h b/src/libCom/osi/os/vxWorks/epicsDynLink.h new file mode 100644 index 000000000..b99b80fe3 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/epicsDynLink.h @@ -0,0 +1,42 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * These routines will eventually need to be made OS independent + * (currently this is vxWorks specific) + */ + +#ifndef epicsDynLinkh +#define epicsDynLinkh + +#ifdef symFindByName +#undef symFindByName +#endif + +#include "vxWorks.h" +#include "symLib.h" +#include "sysSymTbl.h" + +STATUS symFindByNameEPICS( + SYMTAB_ID symTblId, + char *name, + char **pvalue, + SYM_TYPE *pType); + +STATUS symFindByNameAndTypeEPICS( + SYMTAB_ID symTblId, + char *name, + char **pvalue, + SYM_TYPE *pType, + SYM_TYPE sType, + SYM_TYPE mask); + +#endif /* ifdef epicsDynLinkh */ + diff --git a/src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp b/src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp new file mode 100644 index 000000000..ad32e0d0d --- /dev/null +++ b/src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * $Id$ + * + * route vxWorks logMsg messages into the EPICS logging system + * + * Author: Jeff Hill + * + */ + +#include +#include + +#include +#include +#include + +#include "errlog.h" + +// vxCommonLibrary calls logMsgToErrlog so that logMsgToErrlog gets loaded +extern "C" { + int logMsgToErrlog(); +} + +int logMsgToErrlog() { return 0;} + +static class errlogDevTimeInit +{ +public: + errlogDevTimeInit (); +} errlogDevInstance; + +static int errlogOpen ( DEV_HDR *, const char *, int ) +{ + return OK; +} + +static int errlogWrite ( DEV_HDR *, const char * pInBuf, int nbytes ) +{ + errlogPrintf ( "%.*s", nbytes, pInBuf ); + return nbytes; +} + +errlogDevTimeInit::errlogDevTimeInit () +{ + int errlogNo = iosDrvInstall ( + 0, // create not supported + 0, // remove not supported + reinterpret_cast < FUNCPTR > ( errlogOpen ), + 0, // close is a noop + 0, // read not supported + reinterpret_cast < FUNCPTR > ( errlogWrite ), + 0 // ioctl not supported + ); + if ( errlogNo == ERROR ) { + errlogPrintf ( + "Unable to install driver routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + DEV_HDR * pDev = static_cast < DEV_HDR * > ( calloc ( 1, sizeof ( *pDev ) ) ); + if ( ! pDev ) { + errlogPrintf ( + "Unable to create driver data structure for routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + int status = iosDevAdd ( pDev, "/errlog/", errlogNo ); + if ( status < 0 ) { + errlogPrintf ( + "Unable to install device routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + free ( pDev ); + return; + } + int fd = open ( "/errlog/any", O_WRONLY, 0 ); + if ( fd < 0 ) { + errlogPrintf ( + "Unable to open fd routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + status = logFdAdd ( fd ); + if ( status != OK) { + errlogPrintf ( + "Unable to install fd routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + close ( fd ); + return; + } +} + diff --git a/src/libCom/osi/os/vxWorks/module_types.h b/src/libCom/osi/os/vxWorks/module_types.h new file mode 100644 index 000000000..32f5a3cd6 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/module_types.h @@ -0,0 +1,495 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* module_types.h */ +/* base/include $Id$ */ +/* + * Author: Bob Dalesio + * Date: 12-07-88 + */ + +#ifndef INCLmodule_typesh +#define INCLmodule_typesh + +/* Device module types */ +/* + * all devices have corresponding entries in ~operator/db/src/menus.c + * changes must be made in both areas to keep the database and drivers in sync + */ +/* & in comment indicates tested with card 0 */ +/* % in comment indicates tested with card other than card 0 */ +/* # in comment indicates that the Nth card has been tested */ +/* !! never been tested */ + +/* + * @# If any changes are made to this file, check the procedures + * ab_card, and vme_card in signallist.c, and get_address in sigmenu.c. + */ + +#ifdef MODULE_TYPES_INIT +#define MODULE_TYPES_DEF(MT_DEF_PARM) MT_DEF_PARM +#else +#define MODULE_TYPES_DEF(MT_DEF_PARM) extern MT_DEF_PARM; +#endif + +/* Number of columns used in io_report. */ +#define IOR_MAX_COLS 4 + +/* I/O types */ +#define IO_AI 0 +#define IO_AO 1 +#define IO_BI 2 +#define IO_BO 3 +#define IO_SM 4 +#define IO_WF 5 +#define IO_TIMER 6 +#define MAX_IO_TYPE IO_TIMER + +/* bus types */ +/* must correspond to the values in link types */ +/* these defines are in ~gta/dbcon/h/link.h */ + + +/* equates for the Allen-Bradley cards. */ +#define AB_BASE_ADDR 0xc00000 /* base addr of first AB6008SV */ +#define AB_MAX_LINKS 2 /* number of serial links from VME */ +#define AB_MAX_ADAPTERS 8 /* number of physical adapters on a link */ +#define AB_MAX_CARDS 16 /* max number of IO cards per adapter */ +#define AB_CARD_ADAPTER 16 /* cards per logical adapter */ +#define AB_CHAN_CARD 16 /* max channels per card */ + +/* analog inputs */ +#define AB1771IL 0 /* &% Allen-Bradley low level analog input */ +#define AB1771IFE 1 /* &% Allen-Bradley low level analog input */ +#define AB1771IXE 2 /* &% Allen-Bradley millivolt input */ +#define XY566SE 3 /* & Xycom 12-bit Single Ended Scanned*/ +#define XY566DI 4 /* &% Xycom 12-bit Differential Scanned */ +#define XY566DIL 5 /* &% Xycom 12-bit Differential Latched */ +#define VXI_AT5_AI 6 /* % AT-5 VXI module's Analog Inputs */ +#define AB1771IFE_SE 7 /* % A-B IFE in 16 single-ended input mode */ +#define AB1771IFE_4to20MA 8 /* % A-B IFE in 8 double-ended 4to20Ma */ +#define DVX2502 9 /* &% DVX_2502 128 chan 16 bit differential */ +#define AB1771IFE_0to5V 10 /* % A-B IFE in 8 double-ended 4to20Ma */ +#define KSCV215 11 /* % KSC V215 VXI 16 bit differential */ +#define AB1771IrPlatinum 12 /* % A-B RTD Platinum */ +#define AB1771IrCopper 13 /* % A-B RTD Copper */ +#define MAX_AI_TYPES AB1771IrCopper +MODULE_TYPES_DEF(short ai_num_cards[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={12,12,12, 4, 4, 6,32,12,12, 1, 12, 32, 12,12}; +#endif +MODULE_TYPES_DEF(short ai_num_channels[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 8, 8, 8,32,16,16, 8,16, 8, 127, 8, 32,6,6}; +#endif +MODULE_TYPES_DEF(short ai_interruptable[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,0,0}; +#endif +MODULE_TYPES_DEF(short ai_bus[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 4, 2, 2, 2, 2, 4, 4, 2, 4, 2,4,4}; +#endif +MODULE_TYPES_DEF(unsigned short ai_addrs[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0,0x6000,0x7000,0xe000, 0xc014,0,0, 0xff00, 0, 0,0,0}; +#endif +MODULE_TYPES_DEF(long ai_memaddrs[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0,0,0,0x000000,0x040000,0x0c0000, 0,0,0, 0x100000, 0, 0,0,0}; +#endif + +/* analog outputs */ +#define AB1771OFE 0 /* &% Allen-Bradley 12 bit Analog Output */ +#define VMI4100 1 /* & VMIC VMIVME 4100 */ +#define ZIO085 2 /* & Ziomek 085 */ +#define VXI_AT5_AO 3 /* !! AT-5 VXI modules analog outputs */ +#define MAX_AO_TYPES VXI_AT5_AO +MODULE_TYPES_DEF(short ao_num_cards[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = {12, 4, 1, 32}; +#endif +MODULE_TYPES_DEF(short ao_num_channels[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = { 4, 16, 32, 16}; +#endif +MODULE_TYPES_DEF(short ao_interruptable[MAX_AO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 0, 0, 0, 1}; +#endif +MODULE_TYPES_DEF(short ao_bus[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 2, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short ao_addrs[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0x4100,0x0800, 0xc000}; +#endif + +/* binary inputs */ +#define ABBI_08_BIT 0 /* &% Allen-Bradley generic Binary In 8 bit */ +#define ABBI_16_BIT 1 /* &% Allen-Bradley generic Binary In 16 bit */ +#define BB910 2 /* & BURR BROWN MPV 910 (relay) */ +#define XY210 3 /* &% XYcom 32 bit binary in */ +#define VXI_AT5_BI 4 /* !! AT-5 VXI modules binary inputs */ +#define HPE1368A_BI 5 /* !! HP E1368A video switch */ +#define AT8_FP10S_BI 6 /* !! AT8 FP10 slave fast protect */ +#define XY240_BI 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ +#define MAX_BI_TYPES XY240_BI +MODULE_TYPES_DEF(short bi_num_cards[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 12, 12, 4, 4, 32, 32, 8, 2}; +#endif +MODULE_TYPES_DEF(short bi_num_channels[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 16, 32, 32, 32, 16, 32, 32}; +#endif +MODULE_TYPES_DEF(short bi_interruptable[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 1, 1, 0, 0, 1, 1, 1, 1}; +#endif +MODULE_TYPES_DEF(short bi_bus[MAX_BI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 2, 2, 2, 2, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short bi_addrs[MAX_BI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0xb800,0xa000, 0xc000, 0xc000, 0x0e00, 0xd000}; +#endif + +/* binary outputs */ +#define ABBO_08_BIT 0 /* &% Allen-Bradley 8 bit binary out */ +#define ABBO_16_BIT 1 /* &% Allen-Bradley 16 bit binary out */ +#define BB902 2 /* &% BURR BROWN MPV 902 (relay) */ +#define XY220 3 /* &% XYcom 32 bit binary out */ +#define VXI_AT5_BO 4 /* !! AT-5 VXI modules binary outputs */ +#define HPE1368A_BO 5 /* !! HP E1368A video switch */ +#define AT8_FP10M_BO 6 /* !! AT8 FP10 master fast protect */ +#define XY240_BO 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ +#define MAX_BO_TYPES XY240_BO +MODULE_TYPES_DEF(short bo_num_cards[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={12, 12, 4, 1, 32, 32, 2, 2}; +#endif +MODULE_TYPES_DEF(short bo_num_channels[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 16, 32, 32, 32, 16, 32, 32}; +#endif +MODULE_TYPES_DEF(short bo_interruptable[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 0, 0, 0, 0, 1, 0, 0, 1 }; +#endif +MODULE_TYPES_DEF(short bo_bus[MAX_BO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 2, 2, 2, 2, 2, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short bo_addrs[MAX_BO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0xd800,0xc800, 0xc000, 0xc000, 0x0c00, 0xd000}; +#endif + +/* stepper motor drivers */ +#define CM57_83E 0 /* & Compumotor 57-83E motor controller */ +#define OMS_6AXIS 1 /* & OMS six axis motor controller */ +#define MAX_SM_TYPES OMS_6AXIS +MODULE_TYPES_DEF(short sm_num_cards[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 8 }; +#endif +MODULE_TYPES_DEF(short sm_num_channels[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 1, 8}; +#endif +MODULE_TYPES_DEF(short sm_interruptable[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 0, 0 }; +#endif +MODULE_TYPES_DEF(short sm_bus[MAX_SM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 2, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short sm_addrs[MAX_SM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0x8000, 0xfc00 }; +#endif + +/* waveforms */ +#define XY566WF 0 /* & Xycom 566 as a waveform */ +#define CAMAC_THING 1 /* !! CAMAC waveform digitizer */ +#define JGVTR1 2 /* & Joerger transient recorder */ +#define COMET 3 /* !! COMET transient recorder */ +#define MAX_WF_TYPES COMET +MODULE_TYPES_DEF(short wf_num_cards[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={4, 4, 8, 4}; +#endif +MODULE_TYPES_DEF(short wf_num_channels[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={1, 1, 1, 4}; +#endif +MODULE_TYPES_DEF(short wf_interruptable[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = {0, 0, 0, 0}; +#endif +MODULE_TYPES_DEF(short wf_bus[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={2, 3, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short wf_addrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0x9000, 0, 0xB000, 0xbc00}; +#endif +MODULE_TYPES_DEF(unsigned short wf_armaddrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = {0x5400, 0, 0, 0}; +#endif +MODULE_TYPES_DEF(long wf_memaddrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0x080000, 0, 0xb80000, 0xe0000000}; +#endif + + +/* timing cards */ +#define MZ8310 0 /* &% Mizar Timing Module */ +#define DG535 1 /* !! GPIB timing instrument */ +#define VXI_AT5_TIME 2 /* !! AT-5 VXI modules timing channels */ +#define MAX_TM_TYPES VXI_AT5_TIME +MODULE_TYPES_DEF(short tm_num_cards[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 4, 1, 32 }; +#endif +MODULE_TYPES_DEF(short tm_num_channels[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={10, 1, 10}; +#endif +MODULE_TYPES_DEF(short tm_interruptable[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 1, 0, 1 }; +#endif +MODULE_TYPES_DEF(short tm_bus[MAX_TM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 2, 5, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short tm_addrs[MAX_TM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0xf800, 0, 0xc000 }; +#endif + +/* AT830X clock cards */ +MODULE_TYPES_DEF(long AT830X_1_addrs ) +#ifdef MODULE_TYPES_INIT + = 0x0400; +#endif +MODULE_TYPES_DEF(short AT830X_1_num_cards ) +#ifdef MODULE_TYPES_INIT + = 2; +#endif +MODULE_TYPES_DEF(long AT830X_addrs ) +#ifdef MODULE_TYPES_INIT + = 0xaa0000; +#endif +MODULE_TYPES_DEF(short AT830X_num_cards ) +#ifdef MODULE_TYPES_INIT + = 2; +#endif + +/* + * system controller cards. + * (driver looks for only one card) + */ +MODULE_TYPES_DEF(long xy010ScA16Base) +#ifdef MODULE_TYPES_INIT + = 0x0000; +#endif +/* + * limit the size of the VXI logical address space + * + * = + 0xc000 + * + * LA VME address + * 0 + * EPICS_VXI_LA_COUNT + (EPICS_VXI_LA_COUNT-1)*64 + */ +MODULE_TYPES_DEF(unsigned char EPICS_VXI_LA_COUNT) +#ifdef MODULE_TYPES_INIT + = 32; +#endif + +/* + * + * address ranges for VXI A24 and A32 devices + * + */ +MODULE_TYPES_DEF(char *EPICS_VXI_A24_BASE) +#ifdef MODULE_TYPES_INIT + = (char *) 0x900000; +#endif +MODULE_TYPES_DEF(unsigned long EPICS_VXI_A24_SIZE) +#ifdef MODULE_TYPES_INIT + = 0x100000; +#endif +MODULE_TYPES_DEF(char *EPICS_VXI_A32_BASE) +#ifdef MODULE_TYPES_INIT + = (char *) 0x90000000; +#endif +MODULE_TYPES_DEF(unsigned long EPICS_VXI_A32_SIZE) +#ifdef MODULE_TYPES_INIT + = 0x10000000; +#endif + + +/****************************************************************************** + * + * Interrupt vector locations used by the MV167 CPU board. + * These are defined in mv167.h + * + * PCC2_INT_VEC_BASE 0x40 PCC interrupt vector base number + * any multiple of 0x10 + * UTIL_INT_VEC_BASE0 0x50 VMEchip2 utility interrupt + * vector base number + * any multiple of 0x10 + * UTIL_INT_VEC_BASE1 0x60 VMEchip2 utility interrupt + * vector base number + * any multiple of 0x10 + * + * INT_VEC_CD2400_A 0x90 int vec for channel A + * INT_VEC_CD2400_B 0x94 int vec for channel B + * INT_VEC_CD2400_C 0x98 int vec for channel C + * INT_VEC_CD2400_D 0x9c int vec for channel D + * + * LANC_IRQ_LEVEL 3 LNANC IRQ level + * MPCC_IRQ_LEVEL 4 serial comm IRQ level + * SYS_CLK_LEVEL 6 interrupt level for sysClk + * AUX_CLK_LEVEL 5 interrupt level for auxClk + * SCSI_IRQ_LEVEL 2 scsi interrupt level + * + ******************************************************************************/ + +/* interrupt vector allocation - one for each XY566 DIL card */ +MODULE_TYPES_DEF(int AI566_VNUM) +#ifdef MODULE_TYPES_INIT + =0xf8; /* Xycom 566 Differential Latched */ +#endif + +/* interrupt vector allocation - one for each DVX card */ +MODULE_TYPES_DEF(int DVX_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd0; +#endif + +/* stepper motor interrupt vector - one for each motor */ +MODULE_TYPES_DEF(int MD_INT_BASE) +#ifdef MODULE_TYPES_INIT + =0xf0; /* base of the motor int vector */ +#endif + +/* I reserve from here up to num_cards * 4 interrupting chans/card - joh */ +MODULE_TYPES_DEF(int MZ8310_INT_VEC_BASE) +#ifdef MODULE_TYPES_INIT + =0xe8; +#endif + +/* Allen-Bradley Serial Driver - MAX_AB_LINKS number of vectors */ +MODULE_TYPES_DEF(int AB_VEC_BASE) +#ifdef MODULE_TYPES_INIT + =0x60; +#endif + +/* only one interrupt vector allocated for all Joerger VTR1 boards joh */ +MODULE_TYPES_DEF(int JGVTR1_INT_VEC) +#ifdef MODULE_TYPES_INIT + =0xe0; +#endif + +/* AT830X_1 cards have 1 intr vector for each AT830X_1_num_cards (presently 2) */ +MODULE_TYPES_DEF(int AT830X_1_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd4; +#endif + +/* AT830X cards have 1 intr vector for each AT830X_num_cards (presently 2) */ +MODULE_TYPES_DEF(int AT830X_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd6; +#endif + +/* AT8 fast protect interrupt vector base */ +MODULE_TYPES_DEF(int AT8FP_IVEC_BASE) +#ifdef MODULE_TYPES_INIT + =0xa2; +#endif + + +MODULE_TYPES_DEF(int AT8FPM_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + =0xaa; +#endif + + +/****************************************************************************** + * + * Addresses and IRQ information used by the XVME402 bitbus cards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short BB_SHORT_OFF ) +#ifdef MODULE_TYPES_INIT + = 0x1800; /* the first address of link 0's region */ +#endif +#define BB_NUM_LINKS 4 /* max number of BB ports allowed */ +MODULE_TYPES_DEF(int BB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 0xa0; /* vectored interrupts (2 used for each link) */ +#endif +MODULE_TYPES_DEF(int BB_IRQ_LEVEL ) +#ifdef MODULE_TYPES_INIT + = 5; /* IRQ level */ +#endif + +/****************************************************************************** + * + * Information for the PEP modular Bitbus boards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short PEP_BB_SHORT_OFF ) +#ifdef MODULE_TYPES_INIT + = 0x1c00; +#endif +MODULE_TYPES_DEF(int PEP_BB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 0xe8; +#endif + +/****************************************************************************** + * + * Addresses and IRQ information used by the NI1014 and NI1014D bitbus cards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short NIGPIB_SHORT_OFF) +#ifdef MODULE_TYPES_INIT + = 0x5000;/* First address of link 0's region */ +#endif + /* Each link uses 0x0200 bytes */ +#define NIGPIB_NUM_LINKS 4 /* Max number of NI GPIB ports allowed */ +MODULE_TYPES_DEF(int NIGPIB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 100; /* Vectored interrupts (2 used for each link) */ +#endif +MODULE_TYPES_DEF(int NIGPIB_IRQ_LEVEL ) +#ifdef MODULE_TYPES_INIT + =5; /* IRQ level */ +#endif + +#if 0 /* JRW */ +#define NI1014_LINK_NUM_BASE 0 +#endif + +/* + * nothing after this endif + */ +#endif /*INCLmodule_typesh*/ diff --git a/src/libCom/osi/os/vxWorks/task_params.h b/src/libCom/osi/os/vxWorks/task_params.h new file mode 100644 index 000000000..60a93f680 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/task_params.h @@ -0,0 +1,185 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Id$ */ + +/* Parameters for tasks on IOC */ +/* + * Authors: Andy Kozubal, Jeff Hill, and Bob Dalesio + * Date: 2-24-89 + */ + +#ifndef INCtaskLibh +#include +#endif + +#define VXTASKIDSELF 0 + +/* Task Names */ +#define EVENTSCAN_NAME "scanEvent" +#define SCANONCE_NAME "scanOnce" +#define SMCMD_NAME "smCommand" +#define SMRESP_NAME "smResponse" +#define ABDONE_NAME "abDone" +#define ABSCAN_NAME "abScan" +#define ABCOS_NAME "abBiCosScanner" +#define MOMENTARY_NAME "momentary" +#define WFDONE_NAME "wfDone" +#define SEQUENCER_NAME "sequencer" +#define BKPT_CONT_NAME "bkptCont" +#define SCANNER_NAME "scanner" +#define REQ_SRVR_NAME "CA_TCP" +#define CA_CLIENT_NAME "CA_client" +#define CA_EVENT_NAME "CA_event" +#define CAST_SRVR_NAME "CA_UDP" +#define CA_REPEATER_NAME "CA_repeater" +#define CA_ONLINE_NAME "CA_online" +#define TASKWD_NAME "taskwd" +#define SMIOTEST_NAME "smInout" +#define SMROTTEST_NAME "smRotate" +#define EVENT_PEND_NAME "event_task" +#define XY240_NAME "xy_240_scan" +#define GPIBLINK_NAME "gpibLink" +#define BBLINK_NAME "bbLinkTask" +#define BBTXLINK_NAME "bbTx" +#define BBRXLINK_NAME "bbRx" +#define BBWDTASK_NAME "bbwd" +#define ERRLOG_NAME "errlog" +#define LOG_RESTART_NAME "logRestart" + +/* Task priorities */ +#define SCANONCE_PRI 129 /* scan one time */ +/*DO NOT RUN ANY RECORD PROCESSING TASKS AT HIGHER PRIORITY THAN _netTask=50*/ +#define CALLBACK_PRI_LOW 140 /* callback task - generall callback task */ +#define CALLBACK_PRI_MEDIUM 135 /* callback task - generall callback task */ +#define CALLBACK_PRI_HIGH 128 /* callback task - generall callback task */ +#define EVENTSCAN_PRI 129 /* Event Scanner - Runs on a global event */ +#define SMCMD_PRI 120 /* Stepper Motor Command Task - Waits for cmds */ +#define SMRESP_PRI 121 /* Stepper Motor Resp Task - Waits for resps */ +#define ABCOS_PRI 121 /* Allen-Bradley Binary Input COS io_event wakeup */ +#define ABDONE_PRI 122 /* Allen-Bradley Resp Task - Interrupt Driven */ +#define ABSCAN_PRI 123 /* Allen-Bradley Scan Task - Base Rate .1 secs */ +#define BBLINK_PRI 124 +#define BBWDTASK_PRI 123 /* BitBus watchdog task */ +#define BBRXLINK_PRI 124 /* BitBus link task */ +#define BBTXLINK_PRI 125 /* BitBus link task */ +#define GPIBLINK_PRI 125 /* GPIB link task */ +#define MOMENTARY_PRI 126 /* Momentary output - posted from watchdog */ +#define WFDONE_PRI 127 /* Waveform Task - Base Rate of .1 second */ +#define PERIODSCAN_PRI 139 /* Periodic Scanners - Slowest rate */ +#define DB_CA_PRI 149 /*database to channel access*/ +#define SEQUENCER_PRI 151 +#define XY240_PRI 160 /* xy 240 dio scanner */ +#define SCANNER_PRI 170 +#define REQ_SRVR_PRI 181 /* Channel Access TCP request server*/ +#define CA_CLIENT_PRI 180 /* Channel Access clients */ +#define CA_REPEATER_PRI 181 /* Channel Access repeater */ +#define ERRLOG_PRI CA_REPEATER_PRI /*error logger task*/ +#define CAST_SRVR_PRI 182 /* Channel Access broadcast server */ +#define CA_ONLINE_PRI 183 /* Channel Access server online notify */ +#define TASKWD_PRI 200 /* Watchdog Scan Task - runs every 6 seconds */ +#define SMIOTEST_PRI 205 /* Stepper Mtr in/out test - runs every .1 sec */ +#define SMROTTEST_PRI 205 /* Stepper Mtr rotate test - runs every .1 sec */ +#define LOG_RESTART_PRI 200 /* Log server connection watch dog */ + +/* Task delay times (seconds) */ +#define TASKWD_DELAY 6 + +/* Task delay times (tics) */ +#define ABSCAN_RATE (sysClkRateGet()/6) +#define SEQUENCER_DELAY (sysClkRateGet()/5) +#define SCANNER_DELAY (sysClkRateGet()/5) +#define CA_ONLINE_DELAY (sysClkRateGet()*15) +#define LOG_RESTART_DELAY (sysClkRateGet()*30) + +/* Task creation options */ +#define ERRLOG_OPT VX_FP_TASK +#define EVENTSCAN_OPT VX_FP_TASK +#define SCANONCE_OPT VX_FP_TASK +#define CALLBACK_OPT VX_FP_TASK +#define SMCMD_OPT VX_FP_TASK +#define SMRESP_OPT VX_FP_TASK +#define ABDONE_OPT VX_FP_TASK +#define ABCOS_OPT VX_FP_TASK +#define ABSCAN_OPT VX_FP_TASK +#define MOMENTARY_OPT VX_FP_TASK +#define PERIODSCAN_OPT VX_FP_TASK +#define WFDONE_OPT VX_FP_TASK +#define SEQUENCER_OPT VX_FP_TASK | VX_STDIO +#define BKPT_CONT_OPT VX_FP_TASK +#define SCANNER_OPT VX_FP_TASK +#define REQ_SRVR_OPT VX_FP_TASK +#define CAST_SRVR_OPT VX_FP_TASK +#define CA_CLIENT_OPT VX_FP_TASK +#define CA_REPEATER_OPT VX_FP_TASK +#define CA_ONLINE_OPT VX_FP_TASK +#define TASKWD_OPT VX_FP_TASK +#define SMIOTEST_OPT VX_FP_TASK +#define SMROTTEST_OPT VX_FP_TASK +#define EVENT_PEND_OPT VX_FP_TASK +#define GPIBLINK_OPT VX_FP_TASK|VX_STDIO +#define BBLINK_OPT VX_FP_TASK|VX_STDIO +#define BBTXLINK_OPT VX_FP_TASK|VX_STDIO +#define BBRXLINK_OPT VX_FP_TASK|VX_STDIO +#define BBWDTASK_OPT VX_FP_TASK|VX_STDIO +#define DB_CA_OPT (VX_FP_TASK | VX_STDIO) +#define XY_240_OPT (0) /* none */ +#define LOG_RESTART_OPT (VX_FP_TASK) + + +/* + * Task stack sizes + * + * (original stack sizes are appropriate for the 68k) + * ARCH_STACK_FACTOR allows scaling the stacks on particular + * processor architectures + */ +#if CPU_FAMILY == MC680X0 +#define ARCH_STACK_FACTOR 1 +#elif CPU_FAMILY == SPARC +#define ARCH_STACK_FACTOR 2 +#else +#define ARCH_STACK_FACTOR 2 +#endif + +#define ERRLOG_STACK (4000*ARCH_STACK_FACTOR) +#define EVENTSCAN_STACK (11000*ARCH_STACK_FACTOR) +#define SCANONCE_STACK (11000*ARCH_STACK_FACTOR) +#define CALLBACK_STACK (11000*ARCH_STACK_FACTOR) +#define SMCMD_STACK (3000*ARCH_STACK_FACTOR) +#define SMRESP_STACK (3000*ARCH_STACK_FACTOR) +#define ABCOS_STACK (3000*ARCH_STACK_FACTOR) +#define ABDONE_STACK (3000*ARCH_STACK_FACTOR) +#define ABSCAN_STACK (3000*ARCH_STACK_FACTOR) +#define MOMENTARY_STACK (2000*ARCH_STACK_FACTOR) +#define PERIODSCAN_STACK (11000*ARCH_STACK_FACTOR) +#define WFDONE_STACK (9000*ARCH_STACK_FACTOR) +#define SEQUENCER_STACK (5500*ARCH_STACK_FACTOR) +#define BKPT_CONT_STACK (11000*ARCH_STACK_FACTOR) +#define SCANNER_STACK (3048*ARCH_STACK_FACTOR) +#define RSP_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define REQ_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define CAST_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define CA_CLIENT_STACK (11000*ARCH_STACK_FACTOR) +#define CA_REPEATER_STACK (5096*ARCH_STACK_FACTOR) +#define CA_ONLINE_STACK (3048*ARCH_STACK_FACTOR) +#define TASKWD_STACK (2000*ARCH_STACK_FACTOR) +#define SMIOTEST_STACK (2000*ARCH_STACK_FACTOR) +#define SMROTTEST_STACK (2000*ARCH_STACK_FACTOR) +#define EVENT_PEND_STACK (5096*ARCH_STACK_FACTOR) +#define TIMESTAMP_STACK (4000*ARCH_STACK_FACTOR) +#define GPIBLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBRXLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBTXLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBWDTASK_STACK (5000*ARCH_STACK_FACTOR) +#define DB_CA_STACK (11000*ARCH_STACK_FACTOR) +#define XY_240_STACK (4096*ARCH_STACK_FACTOR) +#define LOG_RESTART_STACK (0x1000*ARCH_STACK_FACTOR) + diff --git a/src/libCom/osi/os/vxWorks/veclist.c b/src/libCom/osi/os/vxWorks/veclist.c new file mode 100644 index 000000000..54e97b560 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/veclist.c @@ -0,0 +1,227 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * $Id$ + * @(#)veclist.c 1.10 + * + * list fuctions attached to the interrupt vector table + * + * Created 28Mar89 Jeffrey O. Hill + * johill@lanl.gov + * (505) 665 1831 + * + */ + +/* + * + * makefile + * + * + * V5VW = /.../vx/v502b + * + * veclist.o: + * cc68k -c -DCPU_FAMILY=MC680X0 -I$(V5VW)/h veclist.c + * + * + */ + +#include "vxWorks.h" +#include "stdio.h" +#include "string.h" +#include "intLib.h" +#include "vxLib.h" +#include "iv.h" +#include "ctype.h" +#include "sysSymTbl.h" + +static char *sccsID = + "@(#) $Id$"; + +/* + * + * VME bus dependent + * + */ +#define NVEC 0x100 + +static char *ignore_list[] = {"_excStub","_excIntStub"}; + +int veclist(int); +int cISRTest(FUNCPTR proutine, FUNCPTR *ppisr, void **pparam); +static void *fetch_pointer(unsigned char *); + + +/* + * + * veclist() + * + */ +int veclist(int all) +{ + int vec; + int value; + SYM_TYPE type; + char name[MAX_SYS_SYM_LEN]; + char function_type[10]; + FUNCPTR proutine; + FUNCPTR pCISR; + int cRoutine; + void *pparam; + int status; + unsigned i; + + for(vec=0; vec