/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is 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 */ /* * Original Author: Marty Kraimer * Author: Jeff Hill * Date: 03-10-93 * * NOTES: * .01 06-14-93 joh needs devAllocInterruptVector() routine */ #include #include #include #include "dbDefs.h" #include "epicsMutex.h" #include "errlog.h" #include "ellLib.h" #define NO_DEVLIB_COMPAT #include "devLibVME.h" #include "devLibVMEImpl.h" static ELLLIST addrAlloc[atLast]; static ELLLIST addrFree[atLast]; static size_t addrLast[atLast] = { 0xffff, 0xffffff, 0xffffffff, 0xffffff, 0xffffff, }; static unsigned addrHexDig[atLast] = { 4, 6, 8, 6, 6 }; static long addrFail[atLast] = { S_dev_badA16, S_dev_badA24, S_dev_badA32, S_dev_badISA, S_dev_badCRCSR }; static epicsMutexId addrListLock; static char devLibInitFlag; const char *epicsAddressTypeName[] = { "VME A16", "VME A24", "VME A32", "ISA", "VME CR/CSR" }; 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 */ static long devLibInit(void); static long addrVerify( epicsAddressType addrType, size_t base, size_t size); static 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); static void report_conflict( epicsAddressType addrType, size_t base, size_t size, const char *pOwnerName); static void report_conflict_device( epicsAddressType addrType, const rangeItem *pRange); static void devInsertAddress( ELLLIST *pRangeList, rangeItem *pNewRange); static long devListAddressMap( ELLLIST *pRangeList); static long devCombineAdjacentBlocks( ELLLIST *pRangeList, rangeItem *pRange); static 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 /* * devBusToLocalAddr() */ long devBusToLocalAddr( epicsAddressType addrType, size_t busAddr, volatile void **ppLocalAddress) { long status; volatile void *localAddress; /* * Make sure that devLib has been initialized */ if (!devLibInitFlag) { status = devLibInit(); if(status){ return status; } } /* * Make sure we have a valid bus address */ status = addrVerify (addrType, busAddr, 4); if (status) { return status; } /* * Call the virtual os routine to map the bus address to a CPU address */ status = (*pdevLibVME->pDevMapAddr) (addrType, 0, busAddr, 4, &localAddress); if (status) { errPrintf (status, __FILE__, __LINE__, "%s bus address =0X%X\n", epicsAddressTypeName[addrType], (unsigned int)busAddr); return status; } /* * Return the local CPU address if the pointer is supplied */ if (ppLocalAddress) { *ppLocalAddress = localAddress; } return SUCCESS; }/*end devBusToLocalAddr()*/ /* * 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 isn't present */ long devReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevReadProbe) (wordSize, ptr, pValue); } /* * devWriteProbe * * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isn't present */ long devWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevWriteProbe) (wordSize, ptr, pValue); } /* * devInstallAddr() */ static 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 = (*pdevLibVME->pDevMapAddr) (addrType, 0, base, size, &pPhysicalAddress); if (status) { errPrintf (status, __FILE__, __LINE__, "%s base=0X%X size = 0X%X", epicsAddressTypeName[addrType], (unsigned int)base, (unsigned int)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() */ static 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], (unsigned int)base, (unsigned int)(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() */ static 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], (unsigned int)pRange->begin, (unsigned int)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)!=0) { s = S_dev_addressOverlap; errPrintf ( s, __FILE__, __LINE__, "unregister address for %s at 0X%X failed because %s owns it", pOwnerName, (unsigned int)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, "devCombineAdjacentBlocks error"); return s; } return SUCCESS; } /* * devCombineAdjacentBlocks() */ static 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() */ static 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 = 0; 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, pLocalAddress); return s; } /* * addrVerify() * * care has been taken here not to overflow type size_t */ static 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() */ static long devLibInit (void) { rangeItem *pRange; int i; if(devLibInitFlag) return(SUCCESS); if(!pdevLibVME) { epicsPrintf ("pdevLibVME 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 pdevLibVME->pDevInit(); } /* * devAddressMap() */ long devAddressMap(void) { return devListAddressMap(addrAlloc); } /* * devListAddressMap() */ static 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 * */ static 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 = (*pdevLibVME->pDevMapAddr) (addrType, 0, probe, wordSize, &pPhysical); if (s!=SUCCESS) { return s; } /* * verify that no device is present */ s = (*pdevLibVME->pDevReadProbe)(wordSize, pPhysical, &allWordSizes); if (s==SUCCESS) { return S_dev_addressOverlap; } } byteNo++; } return SUCCESS; } long devConnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *), void *parameter ) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, pFunction, parameter); } long devDisconnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *) ) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, pFunction); } int devInterruptInUseVME (unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevInterruptInUseVME) (level); } long devEnableInterruptLevelVME (unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); } long devDisableInterruptLevelVME (unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); } /* * devConnectInterrupt () * * !! DEPRECATED !! */ long devConnectInterrupt( epicsInterruptType intType, unsigned vectorNumber, void (*pFunction)(void *), void *parameter) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } switch(intType){ case intVME: case intVXI: return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, pFunction, parameter); default: return S_dev_uknIntType; } } /* * * devDisconnectInterrupt() * * !! DEPRECATED !! */ long devDisconnectInterrupt( epicsInterruptType intType, unsigned vectorNumber, void (*pFunction)(void *) ) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } switch(intType){ case intVME: case intVXI: return (*pdevLibVME->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 (*pdevLibVME->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 (*pdevLibVME->pDevDisableInterruptLevelVME) (level); default: return S_dev_uknIntType; } } /* * locationProbe * * !! DEPRECATED !! */ long locationProbe (epicsAddressType addrType, char *pLocation) { return devNoResponseProbe (addrType, (size_t) pLocation, sizeof(long)); } /****************************************************************************** * * The following 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(%u) entered\n", (unsigned int)size); ret = pdevLibVME->pDevA24Malloc(size); return(ret); } void devLibA24Free(void *pBlock) { if (devLibA24Debug) epicsPrintf("devLibA24Free(%p) entered\n", pBlock); pdevLibVME->pDevA24Free(pBlock); }