Files
epics-base/src/db/devLib.c
1999-01-27 18:42:36 +00:00

1104 lines
21 KiB
C

/* devLib.c - support for allocation of common device resources */
/* @(#)$Id$*/
/*
* Original Author: Marty Kraimer
* Author: Jeff Hill
* Date: 03-10-93
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 03-10-93 joh original
* .02 03-18-93 joh index address alloc array by fundamental type
* .03 03-23-93 joh changed input parameter to be a fund
* address type in favor of the argument
* that the BSP will be reconfigured
* to use an EPICS standard address
* mode
* .04 04-08-93 joh made unsolicitedHandlerEPICS() external
* .05 04-08-92 joh better diagnostic if we cant find
* a default interrupt handler
* .06 05-06-93 joh added new parameter to devDisconnectInterrupt().
* See comment below.
* .07 05-28-93 joh Added block probe routines
* .08 05-28-93 joh Added an argument to devRegisterAddress()
* .09 05-28-93 joh Added devAddressMap()
* .10 06-14-93 joh Added devAllocAddress()
* .11 02-21-95 joh Fixed warning messages
*
* NOTES:
* .01 06-14-93 joh needs devAllocInterruptVector() routine
*/
static char *sccsID = "@(#) $Id$";
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dbDefs.h"
#include "errlog.h"
#include "fast_lock.h"
#include "epicsDynLink.h"
#define devLibGlobal
#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 FAST_LOCK 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);
LOCAL struct devLibVirtualOS *pVirtOS;
#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
FASTLOCK(&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);
}
FASTUNLOCK(&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 (*pVirtOS->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 (*pVirtOS->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 = (*pVirtOS->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) {
FASTLOCK(&addrListLock);
ellDelete(&addrFree[addrType], &pRange->node);
FASTUNLOCK(&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 = "<fragmented block>";
pNewRange->pPhysical = NULL;
pRange->end = base - 1;
/*
* add the node after the old item on the free list
* (blocks end up ordered by address)
*/
FASTLOCK(&addrListLock);
ellInsert(&addrFree[addrType], &pRange->node, &pNewRange->node);
FASTUNLOCK(&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;
}
FASTLOCK(&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);
}
FASTUNLOCK(&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;
}
FASTLOCK (&addrListLock);
ellDelete (&addrAlloc[addrType], &pRange->node);
FASTUNLOCK (&addrListLock);
pRange->pOwnerName = "<released fragment>";
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) {
FASTLOCK(&addrListLock);
pRange->begin = pBefore->begin;
ellDelete (pRangeList, &pBefore->node);
FASTUNLOCK(&addrListLock);
free ((void *)pBefore);
}
}
if (pAfter) {
if (pAfter->begin == pRange->end+1) {
FASTLOCK(&addrListLock);
pRange->end = pAfter->end;
ellDelete (pRangeList, &pAfter->node);
FASTUNLOCK(&addrListLock);
free((void *)pAfter);
}
}
return SUCCESS;
}
/*
* devInsertAddress()
*/
LOCAL void devInsertAddress(
ELLLIST *pRangeList,
rangeItem *pNewRange)
{
rangeItem *pBefore;
rangeItem *pAfter;
FASTLOCK(&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);
}
FASTUNLOCK(&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;
}
FASTLOCK(&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;
}
FASTUNLOCK(&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;
if (!devLibInitFlag) {
unsigned i;
SYM_TYPE stype;
const char *pSymName = "_devLibVirtualOS";
/*
* dynamic bind to the virtual os layer for devLib
*/
if (symFindByNameEPICS (sysSymTbl, "_devLibVirtualOS",
(char**)&pVirtOS, &stype)==ERROR)
{
epicsPrintf ("unable to locate symbol \"%s\" - unable to initialize devLib\n", pSymName);
return S_dev_internal;
}
if (NELEMENTS(addrAlloc) != NELEMENTS(addrFree)) {
return S_dev_internal;
}
FASTLOCKINIT(&addrListLock);
FASTLOCK(&addrListLock);
for (i=0; i<NELEMENTS(addrAlloc); i++) {
ellInit (&addrAlloc[i]);
ellInit (&addrFree[i]);
}
for (i=0; i<NELEMENTS(addrAlloc); i++) {
pRange = (rangeItem *) malloc (sizeof(*pRange));
if (!pRange) {
return S_dev_noMemory;
}
pRange->pOwnerName = "<Vacant>";
pRange->pPhysical = NULL;
pRange->begin = 0;
pRange->end = addrLast[i];
ellAdd (&addrFree[i], &pRange->node);
}
FASTUNLOCK(&addrListLock);
devLibInitFlag = TRUE;
}
return SUCCESS;
}
/*
* 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;
}
}
FASTLOCK(&addrListLock);
for (i=0; i<NELEMENTS(addrAlloc); i++) {
pri = (rangeItem *) ellFirst(&pRangeList[i]);
if (pri) {
printf ("%s Address Map\n", epicsAddressTypeName[i]);
}
while (pri) {
printf ("\t0X%0*lX - 0X%0*lX physical base %p %s\n",
addrHexDig[i],
(unsigned long) pri->begin,
addrHexDig[i],
(unsigned long) pri->end,
pri->pPhysical,
pri->pOwnerName);
pri = (rangeItem *) ellNext (&pri->node);
}
}
FASTUNLOCK(&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 = (*pVirtOS->pDevMapAddr) (addrType, 0, probe, wordSize, &pPhysical);
if (s!=SUCCESS) {
return s;
}
/*
* verify that no device is present
*/
s = (*pVirtOS->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 (*pVirtOS->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 (*pVirtOS->pDevDisconnectInterruptVME) (vectorNumber,
pFunction);
break;
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 (*pVirtOS->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 (*pVirtOS->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.
*
*/
void *sysA24Malloc(size_t size);
STATUS sysA24Free(void *pBlock);
/******************************************************************************
*
* Routines to use to allocate and free memory present in the A24 region.
*
******************************************************************************/
static void * (*A24MallocFunc)(size_t) = NULL;
static void (*A24FreeFunc)(void *) = NULL;
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)
{
SYM_TYPE stype;
static int UsingBSP = 0;
void *ret;
if (devLibA24Debug)
epicsPrintf ("devLibA24Malloc(%d) entered\n", size);
if (A24MallocFunc == NULL)
{
/* See if the sysA24Malloc() function is present. */
if(symFindByNameEPICS (sysSymTbl,"_sysA24Malloc", (char**)&A24MallocFunc, &stype)==ERROR)
{ /* Could not find sysA24Malloc... use the malloc one and hope we are OK */
if (devLibA24Debug)
epicsPrintf ("devLibA24Malloc() using regular malloc\n");
A24MallocFunc = malloc;
A24FreeFunc = free;
}
else
{
if(symFindByNameEPICS(sysSymTbl,"_sysA24Free", (char**)&A24FreeFunc, &stype) == ERROR)
{ /* That's strange... we have malloc, but no free! */
if (devLibA24Debug)
epicsPrintf ("devLibA24Malloc() using regular malloc\n");
A24MallocFunc = malloc;
A24FreeFunc = free;
}
else
UsingBSP = 1;
}
}
ret = A24MallocFunc(size);
if ((ret == NULL) && (UsingBSP))
errMessage(S_dev_noMemory, "devLibA24Malloc ran out of A24 memory, try sysA24MapRam(size)");
return(ret);
}
void devLibA24Free(void *pBlock)
{
if (devLibA24Debug)
epicsPrintf("devLibA24Free(%p) entered\n", pBlock);
A24FreeFunc(pBlock);
}