1157 lines
25 KiB
C
1157 lines
25 KiB
C
/*************************************************************************\
|
|
* 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 <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#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 (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)
|
|
*/
|
|
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 = "<released fragment>";
|
|
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 (pBefore);
|
|
}
|
|
}
|
|
|
|
if (pAfter) {
|
|
if (pAfter->begin == pRange->end+1) {
|
|
epicsMutexMustLock(addrListLock);
|
|
pRange->end = pAfter->end;
|
|
ellDelete (pRangeList, &pAfter->node);
|
|
epicsMutexUnlock(addrListLock);
|
|
free(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; 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);
|
|
}
|
|
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; 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);
|
|
}
|
|
}
|
|
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);
|
|
}
|