/*************************************************************************\
* 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	
 *
 */

static char *sccsID = "@(#) $Id$";


#include	<vxWorks.h>
#include        <types.h>
#include        <iv.h>
#include        <vme.h>
#include	<lstLib.h>
#include	<sysLib.h>
#include 	<sysSymTbl.h>
#include	<memLib.h>
#include        <intLib.h>
#include	<logLib.h>
#include 	<string.h>
#include 	<stdioLib.h>
#include	<stdlib.h>

#include	"dbDefs.h"
#include	"errlog.h"
#include	"fast_lock.h"
#define devLibGlobal
#include	"devLib.h"


LOCAL LIST addrAlloc[atLast];
LOCAL LIST addrFree[atLast];

LOCAL void	*addrLast[atLast] = {
			(void *) 0xffff,
			(void *) 0xffffff,
			(void *) 0xffffffff,
			(void *) 0xffffff
			};

LOCAL long	addrFail[atLast] = {
			S_dev_badA16,
			S_dev_badA24,
			S_dev_badA32,
			S_dev_badA24
			};

LOCAL FAST_LOCK	addrListLock;
LOCAL char	addrListInit;
 
typedef struct{
	NODE		node;
	const char	*pOwnerName;
        char		*pFirst;
	char		*pLast;
}rangeItem;

/*
 * 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)];

/*
 * These routines are not exported
 */
LOCAL void	initHandlerAddrList(void);
LOCAL int 	vectorInUse(unsigned vectorNumber);
LOCAL long	initAddrList(void);
LOCAL long    	addrVerify(epicsAddressType addrType, void *address);
LOCAL myISR	*isrFetch(unsigned vectorNumber);
LOCAL long 	blockFind(
			epicsAddressType	addrType,
			char			*pBlockFirst,
			char			*pBlockLast,
			/* size needed */
			unsigned long		size,
			/* n ls bits zero in base addr */
			unsigned 		alignment,
			/* base address found */
			char			**ppBase);
LOCAL long 	report_conflict(
			epicsAddressType        addrType,
			char			*pFirst,
			char			*pLast,
			const char		*pOwnerName);
LOCAL long    	devInsertAddress(
			LIST			*pRangeList,
			rangeItem		*pNewRange);
LOCAL long 	devListAddressMap(
			LIST 			*pRangeList);
LOCAL long    	devCombineAdjacentBlocks(
			LIST		*pRangeList,
			rangeItem	*pRange);
LOCAL long 	devInstallAddr(
			rangeItem		*pRange, 
			const char		*pOwnerName,
			epicsAddressType        addrType,
			char			*pFirst,
			char			*pLast,
			void                    **pLocalAddress);
LOCAL long 	blockDivide(
			epicsAddressType	addrType,
			char			*pBlockFirst,
			char			*pBlockLast,
			/* base address found */
			char			**ppBase,	
			unsigned long		requestSize
);
LOCAL long 	blockProbe(
			epicsAddressType	addrType,
			char			*pFirst,
			char			*pLast
);
long locationProbe(
			epicsAddressType	addrType,
			char			*pLocation
);

/*
 * 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 **);

#define SUCCESS 0



/*
 *
 *      devConnectInterrupt
 *
 *	coded to support other interrupting types in the future
 *
 *	wrapper to minimize driver dependency on vxWorks
 */
long    devConnectInterrupt(
epicsInterruptType      intType,
unsigned                vectorNumber,
void                    (*pFunction)(),
void                    *parameter)
{
	int	status;

	switch(intType){
	case intCPU:
	case intVME:
	case intVXI:
		if(vectorInUse(vectorNumber)){
			return S_dev_vectorInUse; 
		}
		status = intConnect(
				(void *)INUM_TO_IVEC(vectorNumber),
				pFunction,
				(int) parameter);		
		if(status<0){
			return S_dev_vxWorksVecInstlFail;
		}
		break;
	default:
		return S_dev_uknIntType;
	}

	return SUCCESS;
}


/*
 *
 *	devDisconnectInterrupt()
 *
 *	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    devDisconnectInterrupt(
epicsInterruptType      intType,
unsigned                vectorNumber,
void			(*pFunction)() 
)
{
	void	(*psub)();
	int	status;

#   if CPU_FAMILY == PPC
        return S_dev_vxWorksVecInstlFail;
#   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;
	}

	switch(intType){
	case intCPU:
	case intVME:
	case intVXI:
		status = intConnect(
				(void *)INUM_TO_IVEC(vectorNumber),
				unsolicitedHandlerEPICS,
				(int) vectorNumber);		
		if(status<0){
			return S_dev_vxWorksVecInstlFail;
		}
		break;
	default:
		return S_dev_uknIntType;
	}

	return SUCCESS;
}


/*
 *
 *      devEnableInterruptLevel()
 *
 *	wrapper to minimize driver dependency on vxWorks
 *
 */
long    devEnableInterruptLevel(
epicsInterruptType      intType,
unsigned                level)
{

	switch(intType){
	case intCPU:
	case intVME:
	case intVXI:
#		if CPU == I80386 || CPU == I80486 || CPU == PENTIUM
            return S_dev_vxWorksIntEnFail;
#       else
        {
	        int	s;
		    s = sysIntEnable (level);
		    if(s!=OK){
			    return S_dev_vxWorksIntEnFail;
		    }
        }
#       endif
		break;
	default:
		return S_dev_uknIntType;
	}

	return SUCCESS;
}


/*
 *
 *      devDisableInterruptLevel()
 *
 *	wrapper to minimize driver dependency on vxWorks
 *
 */
long    devDisableInterruptLevel(
epicsInterruptType      intType,
unsigned                level)
{

	switch(intType){
	case intCPU:
	case intVME:
	case intVXI:
#		if CPU == I80386 || CPU == I80486 || CPU == PENTIUM
            return S_dev_vxWorksIntDissFail;
#       else
        {
	        int s;
		    s = sysIntDisable(level);
		    if(s<0){
                return S_dev_vxWorksIntDissFail;
		    }
        }
#       endif
		break;
	default:
		return S_dev_uknIntType; 
	}

	return SUCCESS;
}


/*
 *
 *      devRegisterAddress()
 *
 *
 */
long    devRegisterAddress(
const char		*pOwnerName,
epicsAddressType        addrType,
void                    *baseAddress,
unsigned long		size,
void                    **pLocalAddress)
{
	char		*pFirst;
	char		*pLast;
        rangeItem      	*pRange;
	long		s;

	if(!addrListInit){
		s = initAddrList();
		if(s){
			return s;
		}
	}

	s = addrVerify(addrType, (void *) size);
	if(s){
		return s;
	}

	if(size == 0){
		return S_dev_lowValue;
	}
 
	pFirst = (char *) baseAddress;
	pLast = pFirst + size - 1;

	FASTLOCK(&addrListLock);
        pRange = (rangeItem *) addrFree[addrType].node.next;
        while(pRange){
 
		if(pRange->pFirst > pLast){
			pRange = NULL;
			break;
		}

		if(pRange->pFirst <= pFirst && pRange->pLast >= pLast){
			break;
		}

		pRange = (rangeItem *) pRange->node.next;
	}
	FASTUNLOCK(&addrListLock);

	if(!pRange){
		s = report_conflict(addrType, pFirst, pLast, pOwnerName);
		if(s){
			return s;
		}
		else{
			return S_dev_internal;
		}
	}

	s = devInstallAddr(
			pRange,
			pOwnerName,
			addrType,
			pFirst,
			pLast,
			pLocalAddress);
	return s;
}


/*
 *
 *	devInstallAddr()
 *
 */
LOCAL long devInstallAddr(
rangeItem		*pRange, /* item on the free list to be split */
const char		*pOwnerName,
epicsAddressType        addrType,
char			*pFirst,
char			*pLast,
void			**ppLocalAddress)
{
	rangeItem 	*pNewRange;
	int		s;

	if(ppLocalAddress){
		char	*pAddr;
		int 	s1;
		int 	s2;

		if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert)
		{
			*ppLocalAddress = pFirst;
		}
		else
		{
			s1 = sysBusToLocalAdrs(
	               	        EPICStovxWorksAddrType[addrType],
	                        pLast,
	                        &pAddr);
			s2 = sysBusToLocalAdrs(
	               	        EPICStovxWorksAddrType[addrType],
	                        pFirst,
	                        &pAddr);
			if(s1 || s2){
				errPrintf(
					S_dev_vxWorksAddrMapFail,
					__FILE__,
					__LINE__,
					"%s base=0X %X size = 0X %X",
					epicsAddressTypeName[addrType],
					pFirst,
					pLast-pFirst+1);
				return S_dev_vxWorksAddrMapFail;
			}

			*ppLocalAddress = (void *) pAddr;
		}
	}

	/*
	 * split the item on the free list
	 * (when required)
	 */
	if(pRange->pFirst == pFirst && pRange->pLast == pLast){
		FASTLOCK(&addrListLock);
		lstDelete(&addrFree[addrType], &pRange->node);
		FASTUNLOCK(&addrListLock);
		free((void *)pRange);
	}
	else if(pRange->pFirst == pFirst){
		pRange->pFirst = pLast+1;
	}
	else if(pRange->pLast == pLast){
		pRange->pLast = pFirst-1;
	}
	else{

		pNewRange = (rangeItem *) malloc(sizeof(*pRange));
		if(!pNewRange){
			return S_dev_noMemory;
		}
		pNewRange->pFirst = pLast+1;
		pNewRange->pLast = pRange->pLast;
		pNewRange->pOwnerName = "<fragmented block>";
		pRange->pLast = pFirst-1;
		/*
		 * add the node after the old item on the free list
		 * (blocks end up ordered by address)
		 */
		FASTLOCK(&addrListLock);
		lstInsert(&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->pFirst = pFirst;
	pNewRange->pLast = pLast;
	pNewRange->pOwnerName = pOwnerName;

	s = devInsertAddress(&addrAlloc[addrType], pNewRange);
	if(s){
		free((void *)pNewRange);
		return s;
	}

	return SUCCESS;
}


/*
 *
 * report_conflict()
 *
 *
 */
LOCAL long report_conflict(
epicsAddressType        addrType,
char			*pFirst,
char			*pLast,
const char		*pOwnerName
)
{
        rangeItem      	*pRange;

        pRange = (rangeItem *) addrAlloc[addrType].node.next;
        while(pRange){
 
		if(pRange->pFirst <= pFirst && pRange->pLast >= pFirst){
			break;
		}
		if(pRange->pFirst <= pLast && pRange->pLast >= pLast){
			break;
		}
		if(pRange->pFirst > pLast){
			pRange = NULL;
			break;
		}

		pRange = (rangeItem *) pRange->node.next;
	}
	
	if(pRange){
		errPrintf(
				S_dev_addressOverlap,
				__FILE__,
				__LINE__,
				"%10s 0X %08X - %08X Requested by %s",
				epicsAddressTypeName[addrType],
				pFirst,
				pLast,
				pOwnerName);

		errPrintf(
				S_dev_identifyOverlap,
				__FILE__,
				__LINE__,
				"%10s 0X %08X - %08X Owned by %s",
				epicsAddressTypeName[addrType],
				pRange->pFirst,
				pRange->pLast,
				pRange->pOwnerName);

		return S_dev_addressOverlap;
        }

	return S_dev_internal;
}


/*
 *
 *	devUnregisterAddress()
 *
 */
long devUnregisterAddress(
epicsAddressType        addrType,
void                    *baseAddress,
const char		*pOwnerName)
{
	char		*charAddress = (char *) baseAddress;
        rangeItem       *pRange;
	int 		s;

	if(!addrListInit){
		s = initAddrList();
		if(s){
			return s;
		}
	}

	s = addrVerify(addrType, charAddress);
	if(s != SUCCESS){
		return s;
	}

	FASTLOCK(&addrListLock);
        pRange = (rangeItem *) addrAlloc[addrType].node.next;
        while(pRange){
		if(pRange->pFirst == charAddress){
			break;
		}
		if(pRange->pFirst > charAddress){
			pRange = NULL;
			break;
		}
		pRange = (rangeItem *) pRange->node.next;
	}
	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,
			charAddress,
			pRange->pOwnerName);
		return s;
	}	

	FASTLOCK(&addrListLock);
	lstDelete(
		&addrAlloc[addrType],
		&pRange->node);
	FASTUNLOCK(&addrListLock);

	pRange->pOwnerName = "<released fragment>";
	s = devInsertAddress(&addrFree[addrType], pRange);
	if(s){
		free((void *)pRange);
		errMessage(s, "Allocated Device Address Leak");
		return s;
	}
	s = devCombineAdjacentBlocks(&addrFree[addrType], pRange);
	if(s){
		errMessage(s, NULL);
		return s;
	}

	return SUCCESS;
}


/*
 *
 *      devCombineAdjacentBlocks()
 *
 *
 */
LOCAL long    	devCombineAdjacentBlocks(
LIST		*pRangeList,
rangeItem	*pRange)
{
	rangeItem	*pBefore;
	rangeItem	*pAfter;

	pBefore = (rangeItem *) pRange->node.previous;
	pAfter = (rangeItem *) pRange->node.next;

	/*
	 * combine adjacent blocks
	 */
	if(pBefore){
		if(pBefore->pLast == pRange->pFirst-1){
			FASTLOCK(&addrListLock);
			pRange->pFirst = pBefore->pFirst;
			lstDelete(pRangeList, &pBefore->node);
			FASTUNLOCK(&addrListLock);
			free((void *)pBefore);
		}
	}

	if(pAfter){
		if(pAfter->pFirst-1 == pRange->pLast){
			FASTLOCK(&addrListLock);
			pRange->pLast = pAfter->pLast;
			lstDelete(pRangeList, &pAfter->node);
			FASTUNLOCK(&addrListLock);
			free((void *)pAfter);
		}
	}

	return SUCCESS;
}


/*
 *
 *      devInsertAddress()
 *
 *
 */
LOCAL long    devInsertAddress(
LIST		*pRangeList,
rangeItem	*pNewRange)
{
	rangeItem	*pBefore;
	rangeItem	*pAfter;

	FASTLOCK(&addrListLock);
        pAfter = (rangeItem *) pRangeList->node.next;
	while(pAfter){
		if(pNewRange->pLast < pAfter->pFirst){
			break;
		}
		pAfter = (rangeItem *) pAfter->node.next;
	}

	if(pAfter){
		pBefore = (rangeItem *) pAfter->node.previous;
		lstInsert(pRangeList, &pBefore->node, &pNewRange->node);
	}
	else{
		lstAdd(pRangeList, &pNewRange->node);
	}
	FASTUNLOCK(&addrListLock);

	return SUCCESS;
}



/*
 *
 *      devAllocAddress()
 *
 *
 */
long    devAllocAddress(
const char		*pOwnerName,
epicsAddressType        addrType,
unsigned long		size,
unsigned		alignment, /* n ls bits zero in base addr*/
void                    **pLocalAddress)
{
	int		s;
	rangeItem	*pRange;
	char		*pBase;

	s = addrVerify(addrType, (void *)size);
	if(s){
		return s;
	}

	if(size == 0){
		return S_dev_lowValue;
	}

	FASTLOCK(&addrListLock);
        pRange = (rangeItem *) addrFree[addrType].node.next;
	while(pRange){
		if(pRange->pLast-pRange->pFirst>=size-1){
			s = blockFind(
				addrType,
				pRange->pFirst,
				pRange->pLast,
				size,
				alignment,
				&pBase);
			if(!s){
				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,
			pBase,
			pBase + size - 1,
			pLocalAddress);
	return s;
}



/*
 *	addrVerify()
 */
LOCAL long    addrVerify(
epicsAddressType        addrType,
void                    *address)
{
	if(addrType>=atLast){
		return S_dev_uknAddrType;
	}

	if(address > addrLast[addrType]){
		return addrFail[addrType];
	}

	return SUCCESS;
}



/*
 * 	initAddrList()
 */
LOCAL long initAddrList(void)
{
        rangeItem       *pRange;

	if(NELEMENTS(addrAlloc) != NELEMENTS(addrFree)){
		return S_dev_internal;
	}

	if(!addrListInit){
		int i;

		FASTLOCKINIT(&addrListLock);

		FASTLOCK(&addrListLock);
		for(i=0; i<NELEMENTS(addrAlloc); i++){
			lstInit(&addrAlloc[i]);
			lstInit(&addrFree[i]);
		}

		addrListInit = TRUE;

		for(i=0; i<NELEMENTS(addrAlloc); i++){
			pRange = (rangeItem *)malloc(sizeof *pRange);
			if(!pRange){
				return S_dev_noMemory;
			}
			pRange->pOwnerName = "<Vacant>";
			pRange->pFirst = 0;
			pRange->pLast = addrLast[i];
			lstAdd(&addrFree[i], &pRange->node);
		}
		FASTUNLOCK(&addrListLock);
	}

	return SUCCESS;
}


/*
 * 	devAddressMap()
 */
long devAddressMap(void)
{
	return devListAddressMap(addrAlloc);
}


/*
 * 	devListAddressMap()
 */
LOCAL long devListAddressMap(LIST *pRangeList)
{
	rangeItem	*pri;
	int 		i;
	long		s;

	if(!addrListInit){
		s = initAddrList();
		if(s){
			return s;
		}
	}

	FASTLOCK(&addrListLock);
	for(i=0; i<NELEMENTS(addrAlloc); i++){
		pri = (rangeItem *) lstFirst(&pRangeList[i]);
		if(pri){
			printf("%s Address Map\n", epicsAddressTypeName[i]);
		}
		while(pri){
			printf("0X %0*lX - %0*lX %s\n",
				(int) (sizeof (pri->pFirst) * 2U),
				(unsigned long) pri->pFirst,
				(int) (sizeof (pri->pFirst) * 2U),
				(unsigned long) pri->pLast,
				pri->pOwnerName);
			pri = (rangeItem *) lstNext(&pri->node);
		}
	}
	FASTUNLOCK(&addrListLock);

	return SUCCESS;
}


/*
 *
 *	unsolicitedHandlerEPICS()
 *     	what gets called if they disconnect from an
 *	interrupt and an interrupt arrives on the
 *	disconnected vector
 *
 */
void 	unsolicitedHandlerEPICS(int vectorNumber)
{
	/*
 	 * call logMsg() and not errMessage()
	 * so we are certain that printf()
	 * does not get called at interrupt level
	 */
	logMsg(
		"%s: line=%d: Interrupt to EPICS disconnected vector = 0X %X",
		(int)__FILE__,
		__LINE__,
		vectorNumber,
		NULL,
		NULL,
		NULL);
}
 

/*
 *
 *	initHandlerAddrList()
 *      init list of interrupt handlers to ignore
 *
 */
LOCAL 
void initHandlerAddrList(void)
{
        int     	i;
        SYM_TYPE	type;
        int     	status;
 
        for(i=0; i<NELEMENTS(defaultHandlerNames); i++){
                status =
                        symFindByNameEPICS(  sysSymTbl,
                                        defaultHandlerNames[i],
                                        (char **)&defaultHandlerAddr[i],
                                        &type);
                if(status != OK){
			errPrintf(
				S_dev_internal,
				__FILE__,
				__LINE__,
				"initHandlerAddrList() %s not in sym table",
				defaultHandlerNames[i]);
		}
        }
}


/*
 *
 *      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));

	/*
	 * from libvxWorks/veclist.c
	 *
	 * checks to see if it is a C ISR
	 * and if so finds the function pointer and
	 * the parameter passed
	 */
    if ( psub ) {
	    s = cISRTest(psub, &pCISR, &pParam);
	    if(!s){
		    psub = pCISR;
	    }
    }

	return psub;
}


/*
 *
 *	vectorInUse()
 *
 *
 */
LOCAL int
vectorInUse(
unsigned       vectorNumber
)
{
	static int	init;
    int     	i;
    myISR		*psub;

#   if CPU_FAMILY == PPC
        return FALSE;
#   endif

	if(!init){
		initHandlerAddrList();
		init = TRUE;
	}
 
	psub = isrFetch(vectorNumber);

	/*
	 * its a C routine. Does it match a default handler?
	 */
    for(i=0; i<NELEMENTS(defaultHandlerAddr); i++){
        if(defaultHandlerAddr[i] == psub){
			return FALSE;
		}
	}

    return TRUE;
}




/*
 *
 * blockFind()
 *
 * Find unoccupied block in a large block
 *
 */
LOCAL long blockFind(
epicsAddressType	addrType,
char			*pBlockFirst,
char			*pBlockLast,
unsigned long		size,		/* size needed */
unsigned 		alignment,	/* n ls bits zero in base addr */
char			**ppBase	/* base address found */
)
{
	int		s;
	unsigned long	mask;

	/*
	 * align the block base
	 */
	mask = devCreateMask(alignment);
	if(mask&(long)pBlockFirst){
		pBlockFirst = (char *) (mask | (unsigned long) pBlockFirst);
		pBlockFirst++;
	}

	if(pBlockFirst == 0){
		return S_dev_badRequest;
	}

	if(pBlockLast < pBlockFirst){
		return S_dev_badRequest;
	}

	/*
	 * align size of block
	 */
	if(mask & size){ 
		size |= mask;
		size++;
	}

	if(size == 0){
		return S_dev_badRequest;
	}

	if(pBlockLast-pBlockFirst < size-1){
		return S_dev_badRequest;
	}

	s = blockDivide(
		addrType,
		pBlockFirst,
		pBlockFirst + (size-1),
		ppBase,
		size);

	return s;
}


/*
 * blockDivide()
 * (binary search within a block)
 * End up at a valid block without stepping through
 * the entire address range.
 */
LOCAL long blockDivide(
epicsAddressType	addrType,
char			*pBlockFirst,
char			*pBlockLast,
char			**ppBase,	/* base address found */
unsigned long		requestSize
)
{
	char		*pBlock;
	unsigned long	bs;
	int		s;

	s = blockProbe(addrType, pBlockFirst, pBlockFirst+(requestSize-1));
	if(!s){
		*ppBase = pBlockFirst;
		return SUCCESS;
	}

	/*
	 * prevent unsigned long overflow
	 */
	bs = pBlockLast - pBlockFirst + 1;
	if(bs == 0){
		bs = 0x80000000;
	}	
	else{
		bs = bs >> 1;
	}

	while(bs > requestSize){
		pBlock = pBlockFirst;
		while(pBlock <= pBlockLast-bs+1){
			pBlock += bs;
			s = blockProbe(addrType, pBlock, pBlock+(requestSize-1));
			if(!s){
				*ppBase = pBlock;
				return SUCCESS;
			}
			pBlock += bs;
		}
		bs = bs>>1;
	}
	return S_dev_deviceDoesNotFit;
}


/*
 * blockProbe()
 */
LOCAL long blockProbe(
epicsAddressType	addrType,
char			*pFirst,
char			*pLast
)
{
	char	*pProbe;
	int	s;

	pProbe = pFirst;
	while(pProbe <= pLast){
		s = locationProbe(addrType, pProbe);
		if(s){
			return s;
		}
		pProbe++;
	}
	return SUCCESS;
}


/*
 * locationProbe
 */
long locationProbe(
epicsAddressType	addrType,
char			*pLocation
)
{
	char	*pPhysical;
	int	s;

	/*
	 * every byte in the block must 
	 * map to a physical address
	 */
	if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert)
	{
		pPhysical = pLocation;
	}
	else
	{
		s = sysBusToLocalAdrs(
             	        EPICStovxWorksAddrType[addrType],
			pLocation,
                        &pPhysical);
		if(s<0){
			return S_dev_vxWorksAddrMapFail;
		}
	}


	{
		int8_t	*pChar;
		int8_t	byte;

		pChar = (int8_t *) pPhysical;
		if(devPtrAlignTest(pChar)){
			s = vxMemProbe(
				(char *) pChar,
				READ,
				sizeof(byte),
				(char *) &byte);
			if(s!=ERROR){
				return S_dev_addressOverlap;
			}			
		}
	}
	{
		int16_t	*pWord;
		int16_t	word;

		pWord = (int16_t *)pPhysical;
		if(devPtrAlignTest(pWord)){
			s = vxMemProbe(
				(char *)pWord,
				READ,
				sizeof(word),
				(char *) &word);
			if(s!=ERROR){
				return S_dev_addressOverlap;
			}			
		}
	}
	{
		int32_t	*pLongWord;
		int32_t	longWord;

		pLongWord = (int32_t *) pPhysical;
		if(devPtrAlignTest(pLongWord)){
			s = vxMemProbe(
				(char *)pLongWord,
				READ,
				sizeof(longWord),
				(char *)&longWord);
			if(s!=ERROR){
				return S_dev_addressOverlap;
			}			
		}
	}

	return SUCCESS;
}

/******************************************************************************
 *
 * The follwing may, or may not be present in the BSP for the CPU in use.
 *
 */
void *sysA24Malloc(unsigned long 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)
    logMsg("devLibA24Malloc(%d) entered\n", size, 0,0,0,0,0);

  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)
	logMsg("devLibA24Malloc() using regular malloc\n",0,0,0,0,0,0);
      A24MallocFunc = malloc;
      A24FreeFunc   = free;
    }
    else
    {
      if(symFindByNameEPICS(sysSymTbl,"_sysA24Free", (char**)&A24FreeFunc, &stype) == ERROR)
      { /* That's strange... we have malloc, but no free! */
        if (devLibA24Debug)
	  logMsg("devLibA24Malloc() using regular malloc\n",0,0,0,0,0,0);
        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)
    logMsg("devLibA24Free(%p) entered\n", (unsigned long)pBlock,0,0,0,0,0);

  A24FreeFunc(pBlock);
}
