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

/**
 * \file devLibVME.h
 * \author Marty Kraimer, Jeff Hill
 * \brief API for accessing hardware devices, mostly over VMEbus.
 *
 * API for accessing hardware devices. The original APIs here were for
 * written for use with VMEbus but additional routines were added for
 * ISA-bus (but not fully implemented inside EPICS Base and may never
 * have been actually used).
 *
 * If all VMEbus drivers register with these routines then addressing
 * conflicts caused by multiple device/drivers trying to use the same
 * VME addresses will be detected. This API also makes it easy for a
 * single driver to be written that works on both VxWorks and RTEMS.
 */

#ifndef INCdevLibh
#define INCdevLibh 1

#include "dbDefs.h"
#include "osdVME.h"
#include "errMdef.h"
#include "libComAPI.h"
#include "devLib.h"

#ifdef __cplusplus
extern "C" {
#endif

/** \brief The available bus address types */
typedef enum {
    atVMEA16,   /**< \brief VME short I/O. */
    atVMEA24,   /**< \brief VME standard I/O. */
    atVMEA32,   /**< \brief VME extended I/O. */
    atISA,      /**< \brief Memory mapped ISA access. */
    atVMECSR,   /**< \brief VME-64 CR/CSR address space. */
    atLast      /**< \brief Invalid, must be the last entry. */
} epicsAddressType;

/** \brief A string representation of each of the bus address types */
LIBCOM_API extern const char *epicsAddressTypeName[];

#ifdef __cplusplus
}
#endif

/*
 * To retain compatibility include everything by default
 */
#ifndef NO_DEVLIB_COMPAT
#  include  "devLibVMEImpl.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

/** \brief Print a map of registered bus addresses.
 *
 * Display a table of registered bus address ranges, including the owner of
 * each registered address.
 * \return 0, or an error status value
 */
LIBCOM_API long devAddressMap(void);

/** \brief Translate a bus address to a pointer the CPU can use.
 *
 * Given a bus address, returns a pointer to that location in the CPU's
 * memory map, or an error if direct access isn't currently possible.
 * \param addrType The bus address type.
 * \param busAddr Bus address to be translated.
 * \param *ppLocalAddr Where to put the CPU pointer.
 * \return 0, or an error status value.
 */
LIBCOM_API long devBusToLocalAddr (
    epicsAddressType    addrType,
    size_t              busAddr,
    volatile void       **ppLocalAddr);

/** \brief Probe the bus for reading from a specific address.
 *
 * Performs a bus-error-safe \c wordSize atomic read from a specific
 * address and returns an error if this caused a bus error.
 * \param wordSize The word size to read: 1, 2, 4 or 8 bytes.
 * \param ptr Pointer to the location in the CPU's memory map to read.
 * \param pValueRead Where to put the value read.
 * \return 0, or an error status value if the location could not be
 * accessed or the read caused a bus error.
 */
LIBCOM_API long devReadProbe (
    unsigned wordSize, volatile const void *ptr, void *pValueRead);

/** \brief Read-probe a range of bus addresses, looking for empty space.
 *
 * Verifies that no device responds at any naturally aligned addresses
 * within the given range. Tries to read every aligned address at every
 * word size between char and long over the entire range, returning
 * success only if none of the reads succeed.
 * \warning This routine may be slow and have a very bad effect on a busy
 * VMEbus. Every read probe of an unused address will hold onto the VMEbus
 * for the global bus timeout period.
 * \param addrType The bus address type.
 * \param base First address base to probe.
 * \param size Range of bus addresses to test, in bytes.
 * \return 0 if no devices respond, or an error status value.
 */
LIBCOM_API long devNoResponseProbe(
    epicsAddressType    addrType,
    size_t              base,
    size_t              size
);

/** \brief Probe the bus for writing to a specific address.
 *
 * Performs a bus-error-safe \c wordSize atomic write to a specific
 * address and returns an error if this caused a bus error.
 * \param wordSize The word size to write: 1, 2, 4 or 8 bytes.
 * \param ptr Pointer to the location in the CPU's memory map to write to.
 * \param pValueWritten The value to write.
 * \return 0, or an error status value if the location could not be
 * accessed or the write caused a bus error.
 */
LIBCOM_API long devWriteProbe (
    unsigned wordSize, volatile void *ptr, const void *pValueWritten);

/** \brief Register a bus address range with a name.
 *
 * The devLib code keeps a list of all bus address ranges registered with
 * this routine and returns an error if a later call attempts to register
 * any addresses that overlap with a range already registered. The call to
 * registering a range also converts the given base address into a pointer
 * in the CPU address space for the driver to use (see devBusToLocalAddr()).
 * \param pOwnerName Name of a driver that will own this range.
 * \param addrType The bus address type.
 * \param logicalBaseAddress The bus start address.
 * \param size Number of bytes to reserve.
 * \param pPhysicalAddress Where to put the converted CPU pointer.
 * \return 0, or an error status.
 */
LIBCOM_API long devRegisterAddress(
    const char          *pOwnerName,
    epicsAddressType    addrType,
    size_t              logicalBaseAddress,
                        size_t size,
    volatile void       **pPhysicalAddress);

/** \brief Release a bus address range previously registered.
 *
 * Release an address range that was previously registered by a call to
 * devRegisterAddress() or devAllocAddress().
 * \param addrType The bus address type.
 * \param logicalBaseAddress The bus start address.
 * \param pOwnerName The name of the driver that owns this range.
 * \return 0, or an error status.
 */
LIBCOM_API long devUnregisterAddress(
    epicsAddressType    addrType,
    size_t              logicalBaseAddress,
    const char          *pOwnerName);

/** \brief Allocate and register an unoccupied address block.
 *
 * Asks devLib to allocate an address block of a particular address type.
 * This is useful for devices that appear in more than one address space
 * and can program the base address of one window using registers found
 * in another window. As with devRegisterAddress() this call also converts
 * the new base address into a pointer in the CPU address space for the
 * driver to use (see devBusToLocalAddr()).
 * \note This routine calls devNoResponseProbe() to find an unoccupied
 * block in the bus address space, so using it may have a bad effect on a
 * busy VMEbus at allocation time; see the warning above.
 * \param pOwnerName Name of a driver that will own this range.
 * \param addrType The bus address type.
 * \param size Number of bytes to be allocated.
 * \param alignment How many low bits in the address must all be zero.
 * \param pLocalAddress Where to put the CPU pointer.
 * \return 0, or an error status value.
 */
LIBCOM_API long devAllocAddress(
    const char          *pOwnerName,
    epicsAddressType    addrType,
    size_t              size,
    unsigned            alignment, /*n ls bits zero in addr*/
    volatile            void **pLocalAddress);

/** \name VME Interrupt Management
 * Routines to manage VME interrupts.
 * @{
 */
/** \brief Connect an ISR up to a VME interrupt vector.
 *
 * Interrupt Service Routines (ISRs) are normally written in C, and get
 * passed a context parameter given with them to this connection routine.
 *
 * There are many restrictions on the routines that an ISR may call; see
 * epicsEvent.h, epicsInterrupt.h, epicsMessageQueue.h, epicsRingBytes.h,
 * epicsRingPointer.h and epicsTime.h for some APIs known to be suitable.
 * It is safest just to trigger a high-priority task to handle any
 * complex work that must happen as a result of the interrupt.
 * \param vectorNumber VME interrupt vector number.
 * \param pFunction The ISR to be called.
 * \param parameter Context parameter for the ISR.
 * \return 0, or an error status value.
 */
LIBCOM_API long devConnectInterruptVME(
    unsigned            vectorNumber,
    void                (*pFunction)(void *),
    void                *parameter);

/** \brief Disconnect an ISR from its VME interrupt vector.
 *
 * Device drivers may disconnect an ISR they connected earlier using this
 * routine. In addition to taking the \c vectorNumber the ISR itself is
 * required and used as a check to prevent a driver from inadvertently
 * removing an interrupt handler that it didn't install.
 *
 * On a PowerPC target running VxWorks, this routine will always return
 * with an error status.
 * \param vectorNumber VME interrupt vector number.
 * \param pFunction The ISR to be disconnected.
 * \return 0, or an error status value.
 */
LIBCOM_API long devDisconnectInterruptVME(
    unsigned            vectorNumber,
    void                (*pFunction)(void *));

/** \brief Determine if a VME interrupt vector is in use.
 *
 * On a PowerPC target running VxWorks this routine will always return
 * false, indicating that a vector is unused.
 * \param vectorNumber Interrupt vector number.
 * \return True if vector has an ISR attached, otherwise false.
 */
LIBCOM_API int devInterruptInUseVME (unsigned vectorNumber);

/** \brief Enable a VME interrupt level onto the CPU.
 *
 * The VMEbus allows multiple CPU boards to be installed in the same
 * backplane. When this is done, the different VME interrupt levels
 * must be assigned to the CPUs since they cannot be shared. This
 * routine tells the VME interface that it should connect interrupts
 * from the indicated interrupt level to a CPU interrupt line.
 * \param level VMEbus interrupt level to enable, 1-7.
 * \return 0, or an error status value.
 */
LIBCOM_API long devEnableInterruptLevelVME (unsigned level);

/** \brief Disable a VME interrupt level.
 *
 * This routine is the reverse of devEnableInterruptLevelVME().
 * \note This routine should not normally be used, even by a
 * driver that enabled the interrupt level. Disabling a VME
 * interrupt level should only be done by software that knows
 * for certain that no other interrupting device is also using
 * that VME interrupt level.
 * \param level VMEbus interrupt level to disable, 1-7.
 * \return 0, or an error status value.
 */
LIBCOM_API long devDisableInterruptLevelVME (unsigned level);
/** @} */

/** \name Memory for VME DMA Operations
 * These routines manage memory that can be directly accessed
 * from the VMEbus in the A24 address space by another bus master
 * such as a DMA controller.
 * @{
 */
/** \brief malloc() for VME drivers that support DMA.
 *
 * Allocate memory of a given size from a region that can be
 * accessed from the VMEbus in the A24 address space.
 * \param size How many bytes to allocate
 * \return A pointer to the memory allocated, or NULL.
 */
LIBCOM_API void *devLibA24Malloc(size_t size);

/** \brief calloc() for VME drivers that support DMA.
 *
 * Allocate and zero-fill a block of memory of a given size from
 * a region that can be accessed from the VMEbus in the A24
 * address space.
 * \param size How many bytes to allocate and zero.
 * \return A pointer to the memory allocated, or NULL.
 */
LIBCOM_API void *devLibA24Calloc(size_t size);

/** \brief free() for VME drivers that support DMA.
 *
 * Free a block of memory that was allocated using either
 * devLibA24Malloc() or devLibA24Calloc().
 * \param pBlock Block to be released.
 */
LIBCOM_API void devLibA24Free(void *pBlock);
/** @} */

/** \name ISA Interrupt Management
 * Routines to manage ISAbus interrupts.
 * \note These routines may not have been used for a very long time;
 * some appear to have never been implemented at all. They may vanish
 * with no notice from future versions of EPICS Base. Nobody is using
 * the PC's ISA-bus any more are they?
 * @{
 */

/**
 * Connect ISR to a ISA interrupt.
 * \warning Not implemented!
 * \param interruptLevel Bus interrupt level to connect to.
 * \param pFunction C function pointer to connect to.
 * \param parameter Parameter to the called function.
 * \return Returns success or error.
 */
LIBCOM_API long devConnectInterruptISA(
    unsigned            interruptLevel,
    void                (*pFunction)(void *),
    void                *parameter);

/**
 * Disconnect ISR from a ISA interrupt level.
 * \warning Not implemented!
 * \param interruptLevel Interrupt level.
 * \param pFunction C function pointer that was connected.
 * \return returns success or error.
 */
LIBCOM_API long devDisconnectInterruptISA(
    unsigned            interruptLevel,
    void                (*pFunction)(void *));

/**
 * Determine if an ISA interrupt level is in use
 * \warning Not implemented!
 * \param interruptLevel Interrupt level.
 * \return Returns True/False.
 */
LIBCOM_API int devInterruptLevelInUseISA (unsigned interruptLevel);

/**
 * Enable ISA interrupt level
 * \param level Interrupt level.
 * \return Returns True/False.
 */
LIBCOM_API long devEnableInterruptLevelISA (unsigned level);

/**
 * Disable ISA interrupt level
 * \param level Interrupt level.
 * \return Returns True/False.
 */
LIBCOM_API long devDisableInterruptLevelISA (unsigned level);
/** @} */

#ifndef NO_DEVLIB_OLD_INTERFACE

/**
 * \name Deprecated Interfaces
 * @{
 */
typedef enum {intVME, intVXI, intISA} epicsInterruptType;

/**
 * \note This routine has been deprecated. It exists
 * for backwards compatibility purposes only.
 * Please use one of devConnectInterruptVME(), devConnectInterruptPCI(),
 * devConnectInterruptISA() instead. devConnectInterrupt() will be removed
 * in a future release.
 */
LIBCOM_API long devConnectInterrupt(
    epicsInterruptType  intType,
    unsigned            vectorNumber,
    void                (*pFunction)(void *),
    void                *parameter);

/**
 * \note This routine has been deprecated. It exists
 * for backwards compatibility purposes only.
 * Please use one of devDisconnectInterruptVME(), devDisconnectInterruptPCI(),
 * devDisconnectInterruptISA() instead. devDisconnectInterrupt() will
 * be removed in a future release.
 */
LIBCOM_API long devDisconnectInterrupt(
    epicsInterruptType  intType,
    unsigned            vectorNumber,
    void                (*pFunction)(void *));

/**
 * \note This routine has been deprecated. It exists
 * for backwards compatibility purposes only.
 * Please use one of devEnableInterruptLevelVME(), devEnableInterruptLevelPCI(),
 * devEnableInterruptLevelISA() instead. devEnableInterruptLevel() will
 * be removed in a future release.
 */
LIBCOM_API 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() instead. devDisableInterruptLevel() will
 * be removed in a future release.
 */
LIBCOM_API long devDisableInterruptLevel (
    epicsInterruptType intType, unsigned level);

/**
 * \note This routine has been deprecated. It exists
 * for backwards compatibility purposes only.
 * Please use devNoResponseProbe() instead. locationProbe() will be removed
 * in a future release.
 */
LIBCOM_API long locationProbe (epicsAddressType addrType, char *pLocation);

/** @} */

#endif /* NO_DEVLIB_OLD_INTERFACE */

#ifdef __cplusplus
}
#endif

#endif  /* INCdevLibh.h*/
