/**
 * @file avmaic_misc.c
 *
 * AvmAic-Misc - Helper functions for AVM active controllers.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmaic_misc.c,v 1.1.2.1 2006/02/28 21:35:07 thomas Exp $
 * Project  CAPI for BSD
 * Target   avmaic - CAPI manager driver for AVM active ISDN controllers
 * @date    01.01.2000
 * @author  "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/errno.h>
#include <machine/bus.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <machine/clock.h>

/* import includes */
#include <c4b/kcapimgr/capi_drv.h>

#define __AVMAIC_MISC__

/* local includes */
#include <c4b/avmaic/avmaic_var.h>
#include <c4b/avmaic/avmio.h>
#include <c4b/avmaic/avmdma.h>
#include <c4b/avmaic/avmarm.h>
#include <c4b/avmaic/avmt1.h>
#include <c4b/avmaic/avmaic_misc.h>





/* === public definitions ================================================ */





/* === private definitions =============================================== */





/**
 * Names for card types.
 *
 * @attention This array is indexed by the enum AvmAicCardType_t in
 *            avmaic_global.h. If this enumeration is modified this names array
 *            must be changed accordingly!
 */
static char *g_apszCardTypeNames [] =
   {
      "Unknown AVM active ISDN controller",
      "AVM B1 ISA",
      "AVM B1 PCI",
      "AVM B1 PCI v4",
      "AVM C1",
      "AVM C2",
      "AVM C4",
      "AVM T1 ISA",
      "AVM T1 PCI",
      "AVM B1 PCMCIA",
      "AVM M1",
      "AVM M2"
   };





/* === prototypes for private functions ================================== */





/**
 * Bus DMA callback function to obtain physical address of DMA memory.
 */
static bus_dmamap_callback_t avmmisc_dma_map_addr;





/* === definition of public functions ==================================== */





/**
 * Detach an AVM active card of any type from its bus.
 *
 * A detach operation comprises of releasing all resources and all dynamic
 * memory allocated for the board specified in the argument. Additionally the
 * board will be reset. So after this function call the board will not be
 * operational any more.
 *
 * @param dev                    I: The device entry for the board to shut down.
 *
 * @return Nothing.
 */

void avmmisc_detach
   (device_t dev)
{
   AvmAicSc_t           *pSc;
   AvmAicResourceInfo_t *pRes;
   AvmAicPortData_t     *pPort;
   int                   i;
   
   pSc = device_get_softc (dev);
   pRes = &(pSc->resInfo);
   /* Note: The mutex will be released in this function, so we cannot release
    *       controller access any more.
    */
   (void) avmmisc_get_ctlr_access (pSc);
   
   pSc->state = AVMAIC_STATE_DOWN;
   
   /* reset the board and disable interrupts */
   avmmisc_disable (pSc);
   
   /* now wait some time to let a pending interrupt call complete before all
    * members of the softc structure get invalidated
    */
   if (mtx_initialized (&(pSc->mtxAccess)))
   {
      msleep (pSc, &(pSc->mtxAccess), PZERO, AVMIO_READY_WAIT_MSG, hz / 100);
   }
   
   /* release allocated memory and unregister at the CAPI manager */
   for (i = 0; i < pSc->nPorts; i++)
   {
      pPort = &(pSc->aPortData [i]);
      pPort->state = AVMAIC_STATE_DOWN;
      
      if (pPort->uUniqueCapiCtlrNum != 0)
      {
         (void) kcapi_ctlr_release (pPort->uUniqueCapiCtlrNum);
         e_apMapCapiCtlrToPortData [pPort->uUniqueCapiCtlrNum] = NULL;
         pPort->uUniqueCapiCtlrNum = 0;
      }
   }
   
   /* release all assigned hardware resources */
   if (pRes->pResIrq != NULL)
   {
      (void) bus_teardown_intr (dev, pRes->pResIrq, pRes->hIrqCookie);
      (void) bus_release_resource (dev, SYS_RES_IRQ,
                                   pRes->iRidIrq, pRes->pResIrq);
      pRes->pResIrq = NULL;
   }
   if (pRes->pResIo != NULL)
   {
      (void) bus_release_resource (dev, SYS_RES_IOPORT,
                                   pRes->iRidIo, pRes->pResIo);
      pRes->pResIo = NULL;
   }
   if (pRes->pResMem != NULL)
   {
      (void) bus_release_resource (dev, SYS_RES_MEMORY,
                                   pRes->iRidMem, pRes->pResMem);
      pRes->pResMem = NULL;
   }
   
   if (pSc->paucRecvMsgBuf != NULL && ! pSc->dmaRecv.fDmaOk)
   {
      FREE (pSc->paucRecvMsgBuf, M_DEVBUF);
      pSc->paucRecvMsgBuf = NULL;
   }
   if (pSc->dmaRecv.fDmaOk)
   {
      pSc->dmaRecv.fDmaOk = 0;
      bus_dmamap_unload (pSc->dmaRecv.dmat, pSc->dmaRecv.map);
      bus_dmamem_free (pSc->dmaRecv.dmat,
                       pSc->dmaRecv.pVirtAddr, pSc->dmaRecv.map);
      (void) bus_dma_tag_destroy (pSc->dmaRecv.dmat);
   }
   if (pSc->dmaSend.fDmaOk)
   {
      pSc->dmaSend.fDmaOk = 0;
      bus_dmamap_unload (pSc->dmaSend.dmat, pSc->dmaSend.map);
      bus_dmamem_free (pSc->dmaSend.dmat,
                       pSc->dmaSend.pVirtAddr, pSc->dmaSend.map);
      (void) bus_dma_tag_destroy (pSc->dmaSend.dmat);
   }
   
   if (mtx_initialized (&(pSc->mtxAccess)))
   {
      cv_destroy (&(pSc->cvNotify));
      mtx_destroy (&(pSc->mtxAccess));
   }
   
   pSc->fOpInProgress = 0;
   
} /* avmmisc_detach */





/**
 * Reset an AVM active card of any known board type.
 *
 * This function call will perform a normal reset operation on any known type of
 * board. The board type stored into *pSc will be used to redirect the call to
 * a board type specific function for a reset.
 *
 * @param pSc                    I/O: The softc structure for the board to reset.
 *
 * @return Nothing.
 */

void avmmisc_reset
   (AvmAicSc_t *pSc)
{
   switch (pSc->cardType)
   {
      case AVMAIC_CARD_TYPE_B1_ISA:
      case AVMAIC_CARD_TYPE_B1_PCI_V3:
      case AVMAIC_CARD_TYPE_T1_ISA:
         if (pSc->resInfo.pResIo)
         {
            avmio_reset (pSc->resInfo.ioTag, pSc->resInfo.ioHandle);
         }
         break;
         
      case AVMAIC_CARD_TYPE_B1_PCI_V4:
      case AVMAIC_CARD_TYPE_T1_PCI:
         if (pSc->resInfo.pResMem && pSc->resInfo.pResIo)
         {
            avmdma_reset (pSc->resInfo.memTag, pSc->resInfo.memHandle,
                          pSc->resInfo.ioTag, pSc->resInfo.ioHandle,
                          pSc->cardType);
            pSc->ulDmaIcsr = 0;
         }
         break;
         
      case AVMAIC_CARD_TYPE_C1:
      case AVMAIC_CARD_TYPE_C2:
      case AVMAIC_CARD_TYPE_C4:
         if (pSc->resInfo.pResMem)
         {
            avmarm_reset (pSc->resInfo.memTag, pSc->resInfo.memHandle);
         }
         break;
         
      default:
         break;
   }
   
} /* avmmisc_reset */





/*
        reset an AVM active card and disable interrupts
        -----------------------------------------------
*/

void avmmisc_disable
   (AvmAicSc_t *pSc)
{
   /* some boards require explicitly disabling interrupts */
   switch (pSc->cardType)
   {
      case AVMAIC_CARD_TYPE_B1_ISA:
      case AVMAIC_CARD_TYPE_B1_PCI_V3:
         if (pSc->resInfo.pResIo)
         {
            avmio_disable_interrupt (pSc->resInfo.ioTag, pSc->resInfo.ioHandle);
         }
         break;

      case AVMAIC_CARD_TYPE_T1_ISA:
         if (pSc->resInfo.pResIo)
         {
            avmio_t1_disable_interrupt
               (pSc->resInfo.ioTag, pSc->resInfo.ioHandle);
         }
         break;

      default:
         break;
   }
   
   /* perform a controller reset */
   avmmisc_reset (pSc);
   
} /* avmmisc_disable */





/*
        determine the controller port addressed by a CAPI controller number
        -------------------------------------------------------------------
*/

AvmAicPortData_t *avmmisc_get_port_from_capi_ctlr_num
   (AvmAicSc_t *pSc,
    unsigned    uCapiCtlr)
{
   int i;
   
   for (i = 0; i < pSc->nPorts; i++)
   {
      if (uCapiCtlr == pSc->aPortData [i].uDrvCapiCtlrNum)
      {
         return (&(pSc->aPortData [i]));
      }
   }
   
   return (NULL);
} /* avmmisc_get_port_from_capi_ctlr_num */





/**
 * Initialise data for bus DMA.
 */

int avmmisc_init_busdma
   (device_t    dev,
    AvmAicSc_t *pSc)
{
   int iRes;

   iRes = bus_dma_tag_create (NULL, PAGE_SIZE, 0,
                              BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
                              NULL, NULL,
                              AVMAIC_MSGBUF_SIZE, 1, AVMAIC_MSGBUF_SIZE,
                              BUS_DMA_ALLOCNOW,
                              busdma_lock_mutex, &Giant,
                              &(pSc->dmaSend.dmat));
   if (iRes != 0)
   {
      device_printf (dev,
                     "Attach failed, error creating dma tag for sending: %d\n",
                     iRes);
      return (ENOMEM);
   }
   iRes = bus_dmamem_alloc (pSc->dmaSend.dmat,
                            (void **) &(pSc->dmaSend.pVirtAddr),
                            BUS_DMA_WAITOK | BUS_DMA_COHERENT,
                            &(pSc->dmaSend.map));
   if (iRes != 0)
   {
      device_printf (dev,
                     "Attach failed, error allocating dma memory for sending: %d\n",
                     iRes);
      (void) bus_dma_tag_destroy (pSc->dmaSend.dmat);
      return (ENOMEM);
   }
   iRes = bus_dmamap_load (pSc->dmaSend.dmat,
                           pSc->dmaSend.map,
                           pSc->dmaSend.pVirtAddr,
                           AVMAIC_MSGBUF_SIZE,
                           avmmisc_dma_map_addr,
                           &(pSc->dmaSend.ulPhysAddr),
                           0);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Attach failed, error loading dma map for sending: %d\n",
                     iRes);
      bus_dmamem_free (pSc->dmaSend.dmat,
                       pSc->dmaSend.pVirtAddr, pSc->dmaSend.map);
      (void) bus_dma_tag_destroy (pSc->dmaSend.dmat);
      return (ENOMEM);
   }
   pSc->dmaSend.fDmaOk = 1;

   iRes = bus_dma_tag_create (NULL, PAGE_SIZE, 0,
                              BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
                              NULL, NULL,
                              AVMAIC_MSGBUF_SIZE, 1, AVMAIC_MSGBUF_SIZE,
                              BUS_DMA_ALLOCNOW,
                              busdma_lock_mutex, &Giant,
                              &(pSc->dmaRecv.dmat));
   if (iRes != 0)
   {
      device_printf (dev,
                     "Attach failed, error creating dma tag for receiving: %d\n",
                     iRes);
      return (ENOMEM);
   }
   iRes = bus_dmamem_alloc (pSc->dmaRecv.dmat,
                            (void **) &(pSc->dmaRecv.pVirtAddr),
                            BUS_DMA_WAITOK | BUS_DMA_COHERENT,
                            &(pSc->dmaRecv.map));
   if (iRes != 0)
   {
      device_printf (dev,
                     "Attach failed, error allocating dma memory for receiving: %d\n",
                     iRes);
      (void) bus_dma_tag_destroy (pSc->dmaRecv.dmat);
      return (ENOMEM);
   }
   iRes = bus_dmamap_load (pSc->dmaRecv.dmat,
                           pSc->dmaRecv.map,
                           pSc->dmaRecv.pVirtAddr,
                           AVMAIC_MSGBUF_SIZE,
                           avmmisc_dma_map_addr,
                           &(pSc->dmaRecv.ulPhysAddr),
                           0);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Attach failed, error loading dma map for receiving: %d\n",
                     iRes);
      bus_dmamem_free (pSc->dmaRecv.dmat,
                       pSc->dmaRecv.pVirtAddr, pSc->dmaRecv.map);
      (void) bus_dma_tag_destroy (pSc->dmaRecv.dmat);
      return (ENOMEM);
   }
   pSc->dmaRecv.fDmaOk = 1;

   pSc->paucRecvMsgBuf = pSc->dmaRecv.pVirtAddr;

   return (0);
} /* avmmisc_init_busdma */





/*
        evaluate version info returned by AVMAIC_RECEIVE_INIT message
        -------------------------------------------------------------
*/

void avmmisc_parse_version
   (AvmAicPortData_t *pPortData)
{
   AvmAicSc_t *pSc = pPortData->pSc;
   char       *pc;
   size_t      nLen;

   pc = &(pPortData->acVersionBuf [0]);
   nLen = (size_t) (*pc);
   if (nLen >= sizeof (pPortData->szDriverVersion))
   {
      nLen = sizeof (pPortData->szDriverVersion) - 1;
   }
   strncpy (pPortData->szDriverVersion, pc + 1, nLen);
   pPortData->szDriverVersion [nLen] = '\0';

   pc += *pc + 1;
   nLen = (size_t) (*pc);
   if (nLen >= sizeof (pPortData->szCardType))
   {
      nLen = sizeof (pPortData->szCardType) - 1;
   }
   strncpy (pPortData->szCardType, pc + 1, nLen);
   pPortData->szCardType [nLen] = '\0';

   pc += *pc + 1;
   nLen = (size_t) (*pc);
   if (nLen >= sizeof (pPortData->szHardwareID))
   {
      nLen = sizeof (pPortData->szHardwareID) - 1;
   }
   strncpy (pPortData->szHardwareID, pc + 1, nLen);
   pPortData->szHardwareID [nLen] = '\0';

   pc += *pc + 1;
   nLen = (size_t) (*pc);
   if (nLen >= sizeof (pPortData->szSerialNo))
   {
      nLen = sizeof (pPortData->szSerialNo) - 1;
   }
   strncpy (pPortData->szSerialNo, pc + 1, nLen);
   pPortData->szSerialNo [nLen] = '\0';

   pc += *pc + 1;
   nLen = (size_t) (*pc);
   if (nLen > sizeof (pPortData->acCapability))
   {
      nLen = sizeof (pPortData->acCapability);
   }
   memcpy (pPortData->acCapability, pc + 1, nLen);
   pPortData->nLenCapability = nLen;

   pc += *pc + 1;
   nLen = (size_t) (*pc);
   if (nLen >= sizeof (pPortData->szDChnProt))
   {
      nLen = sizeof (pPortData->szDChnProt) - 1;
   }
   strncpy (pPortData->szDChnProt, pc + 1, nLen);
   pPortData->szDChnProt [nLen] = '\0';

   pc += *pc + 1;
   nLen = (size_t) (*pc);
   if (nLen > sizeof (pPortData->abCapiProfile))
   {
      nLen = sizeof (pPortData->abCapiProfile);
   }
   else
   {
      bzero (pPortData->abCapiProfile, sizeof (pPortData->abCapiProfile));
   }
   memcpy (pPortData->abCapiProfile, pc + 1, nLen);

   DBG (LOG_INFO, pSc->iUnit,
        "Result of parsing the version information for port %u:",
        pPortData->uPortIdx);
   DBG (LOG_INFO, pSc->iUnit,
        "   board driver version: %s", pPortData->szDriverVersion);
   DBG (LOG_INFO, pSc->iUnit,
	"   card type: %s", pPortData->szCardType);
   DBG (LOG_INFO, pSc->iUnit,
	"   hardware id: %s", pPortData->szHardwareID);
   DBG (LOG_INFO, pSc->iUnit,
	"   serial number: %s", pPortData->szSerialNo);
   DBG (LOG_INFO, pSc->iUnit,
	"   D-channel protocol: %s", pPortData->szDChnProt);

   DBG (LOG_INFO, pSc->iUnit,
        "Info from CAPI profile:");
   DBG (LOG_INFO, pSc->iUnit,
	"   manu [1]: 0x%02x, manu [3]: 0x%02x, manu [5]: 0x%02x\n",
	pPortData->abCapiProfile [45],
	pPortData->abCapiProfile [47],
	pPortData->abCapiProfile [49]);

} /* avmmisc_parse_version */





/*
        get print name for card type
        ----------------------------
*/

const char *avmmisc_get_card_type_name
   (AvmAicCardType_t cardType)
{
   if ((size_t) cardType >= ARRAY_COUNT (g_apszCardTypeNames))
   {
      cardType = AVMAIC_CARD_TYPE_UNKNOWN;
   }
   return (g_apszCardTypeNames [(int) cardType]);
} /* avmmisc_get_card_type_name */





/*
	load T4 file onto non-arm based controllers
	-------------------------------------------
*/

unsigned avmmisc_load_t4_file
   (AvmAicSc_t    *pSc,
    size_t         nLenData,
    unsigned char *paucData)
{
   bus_space_tag_t    t = pSc->resInfo.ioTag;
   bus_space_handle_t h = pSc->resInfo.ioHandle;
   int                i;
   int                iRes;

   if (nLenData == 0 || ! paucData)
   {
      printf ("%s%d: %s: Illegal arguments: nLenData=%zu, paucData=%p\n",
              pSc->szDriverName, pSc->iUnit, __FUNCTION__, nLenData, paucData);
      return (CME_INVALID_PARAM);
   }

   DBG (LOG_TRACE, pSc->iUnit, "Start downloading firmware");
   
   for (i = 0, iRes = 0; (size_t) i < nLenData && iRes >= 0; i++)
   {
      iRes = avmio_safe_put_byte (t, h, paucData [i]);
   }

   if (iRes < 0)
   {
      printf ("%s%d: Failed to write firmware to controller (%zu of %zu bytes written)\n",
	      pSc->szDriverName, pSc->iUnit, (size_t) i, nLenData);
      return (CME_OS_RESOURCE_ERROR);
   }

   DBG (LOG_TRACE, pSc->iUnit, "Firmware download complete");
   
   return (CAPI_OK);
} /* avmmisc_load_t4_file */





/*
	load configuration onto non-arm based controller
	------------------------------------------------
*/

unsigned avmmisc_load_config
   (AvmAicSc_t    *pSc,
    size_t         nLenData,
    unsigned char *paucData)
{
   bus_space_tag_t    t = pSc->resInfo.ioTag;
   bus_space_handle_t h = pSc->resInfo.ioHandle;
   int                i;
   int                j;
   int                iRes;

   if (nLenData == 0 || ! paucData)
   {
      printf ("%s%d: %s: illegal arguments: nLenData=%zu, paucData=%p\n",
	      pSc->szDriverName, pSc->iUnit, __FUNCTION__, nLenData, paucData);
      return (CME_INVALID_PARAM);
   }

   DBG (LOG_TRACE, pSc->iUnit, "Start downloading configuration");
   
   if (avmio_safe_put_byte (t, h, AVMAIC_SEND_CONFIG) < 0 ||
       avmio_put_word (t, h, 1) < 0 ||
       avmio_safe_put_byte (t, h, AVMAIC_SEND_CONFIG) < 0 ||
       avmio_put_word (t, h, nLenData))
   {
      printf ("%s%d: failed to start writing configuration to controller\n",
              pSc->szDriverName, pSc->iUnit);
      return (CME_OS_RESOURCE_ERROR);
   }
   
   for (i = 0, iRes = 0; (size_t) i < nLenData && iRes >= 0; )
   {
      iRes = avmio_safe_put_byte (t, h, AVMAIC_SEND_CONFIG);
      if (iRes < 0)
      {
         break;
      }
      for (j = 0; j < 4 && iRes >= 0; j++)
      {
         if (i >= nLenData)
         {
            iRes = avmio_safe_put_byte (t, h, 0);
         }
         else
         {
            iRes = avmio_safe_put_byte (t, h, paucData [i]);
            i++;
         }
      }
   }

   if (iRes < 0)
   {
      printf ("%s%d: failed to write configuration to controller (%zu of %zu bytes written)\n",
	           pSc->szDriverName, pSc->iUnit, (size_t) i, nLenData);
      return (CME_OS_RESOURCE_ERROR);
   }

   return (CAPI_OK);
} /* avmmisc_load_config */





/*
	check if non-arm based controller is loaded
	-------------------------------------------
        Important: The generation of interrupts must be disabled before calling
                   this function!
*/

int avmmisc_is_loaded
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t     t = pSc->resInfo.ioTag;
   bus_space_handle_t  h = pSc->resInfo.ioHandle;
   struct timeval      tvStart;
   struct timeval      tvCurr;
   int                 iCurrTime;
   int                 iTimeout;
   int                 rsp;
   unsigned char       ucCmd;
   unsigned char       ucResultExpected;
   char               *pszCmdStr;
   char               *pszResultExpectedStr;

   /* wait for empty transfer buffer so that controller is not busy any more;
    * use timeout of 5 seconds
    */
   getmicrotime (&tvStart);
   iCurrTime = 0;
   iTimeout =  5 * hz;
   while (! avmio_tx_empty (t, h) && iTimeout >= iCurrTime)
   {
      DELAY (AVMAIC_IO_POLL_DELAY);
      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;
   }
   if (! avmio_tx_empty (t, h))
   {
      DBG (LOG_INFO, pSc->iUnit,
           "Controller is not ready after loading (timeout after %d ticks), corrupted T4 file?",
           iCurrTime);
      return (0);
   }

   /* send test command AVMAIC_SEND_POLL(ACK) to the controller, it must respond
    * with AVMAIC_RECEIVE_POLL(DWORD)
    */
   switch (pSc->cardType)
   {
      case AVMAIC_CARD_TYPE_B1_ISA:
      case AVMAIC_CARD_TYPE_B1_PCI_V3:
      case AVMAIC_CARD_TYPE_T1_ISA:
      case AVMAIC_CARD_TYPE_B1_PCMCIA:
      case AVMAIC_CARD_TYPE_M1:
      case AVMAIC_CARD_TYPE_M2:
      default:
         ucCmd = AVMAIC_SEND_POLL;
         pszCmdStr = "AVMAIC_SEND_POLL";
         ucResultExpected = AVMAIC_RECEIVE_POLL;
         pszResultExpectedStr = "AVMAIC_RECEIVE_POLL";
         break;

      case AVMAIC_CARD_TYPE_B1_PCI_V4:
      case AVMAIC_CARD_TYPE_T1_PCI:
         ucCmd = AVMAIC_SEND_POLLACK;
         pszCmdStr = "AVMAIC_SEND_POLLACK";
         ucResultExpected = AVMAIC_RECEIVE_POLLDWORD;
         pszResultExpectedStr = "AVMAIC_RECEIVE_POLLDWORD";
         break;
   }
   DBG (LOG_DEBUG, pSc->iUnit, "send message %s to controller", pszCmdStr);
   rsp = avmio_safe_put_byte (t, h, ucCmd);

   /* now wait for the response */
   /* Note: Interrupts are not active. */
   getmicrotime (&tvStart);
   iCurrTime = 0;
   iTimeout =  5 * hz;
   while (iTimeout >= iCurrTime)
   {
      DELAY (AVMAIC_IO_POLL_DELAY);
      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;

      if (avmio_rx_full (t, h))
      {
         rsp = avmio_inp (t, h, AVMAIC_REG_READ);
         if (rsp == ucResultExpected)
         {
            /* controller responds correctly, ready */
            DBG (LOG_DEBUG, pSc->iUnit,
                 "Message %s received from controller after %d ticks",
                 pszResultExpectedStr, iCurrTime);
            return (1);
         }
         printf ("%s%d: Illegal controller response 0x%02X after loading (0x%02X expected) and after %d ticks, firmware not running\n",
         pSc->szDriverName, pSc->iUnit, rsp, (int) ucResultExpected, iCurrTime);
         return (0);
      }
   }
   printf ("%s%d: Timeout (%d ticks), controller does not respond to command %s\n",
           pSc->szDriverName, pSc->iUnit, iCurrTime, pszCmdStr);

   return (0);
} /* avmmisc_is_loaded */





/*
        get access to a controller for an operation
        -------------------------------------------
*/

int avmmisc_get_ctlr_access
   (AvmAicSc_t *pSc)
{
   /* The mutex might not be initialized when calling this function. But in this
    * case no real operation will be pending, but the controller device will be
    * detached from its bus. So it does not matter, but a lock operation must
    * not be performed.
    */
   if (! mtx_initialized (&(pSc->mtxAccess)))
   {
      DBG (LOG_DEBUG, pSc->iUnit, "Allow direct controller access because of uninitialized mutex");
      pSc->fOpInProgress = 1;
      return (0);
   }
   
   DBG (LOG_DEBUG, pSc->iUnit, "Gain controller access");
   mtx_lock (&(pSc->mtxAccess));
   DBG (LOG_DEBUG, pSc->iUnit,
        "Got preliminary controller access, wait for no other operation in progress");
   while (pSc->fOpInProgress || pSc->fIntrActive)
   {
      cv_wait (&(pSc->cvNotify), &(pSc->mtxAccess));
   }
   
   if (pSc->fOpInProgress != 0)
   {
      DBG (LOG_ERROR, pSc->iUnit, "Still operation in progress");
   }
   if (pSc->fIntrActive != 0)
   {
      DBG (LOG_ERROR, pSc->iUnit, "Interrupt handler still active");
   }
   
   pSc->fOpInProgress = 1;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Got controller access");
   
   return (0);
} /* avmmisc_get_ctlr_access */





/*
        release access to a controller after an operation
        -------------------------------------------------
*/

void avmmisc_release_ctlr_access
   (AvmAicSc_t *pSc,
    int         sPrivLevel)
{
   /* The mutex might not be initialized when calling this function. But in this
    * case no real operation will be pending, but the controller device will be
    * detached from its bus. So it does not matter, but an unlock operation must
    * not be performed.
    */
   if (! mtx_initialized (&(pSc->mtxAccess)))
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Directly release controller access because of uninitialized mutex");
      pSc->fOpInProgress = 0;
      return;
   }
   
   pSc->fOpInProgress = 0;
   cv_broadcast (&(pSc->cvNotify));
   mtx_unlock (&(pSc->mtxAccess));
   
   /* suppress warning */
   (void) sPrivLevel;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Released controller access");

} /* avmmisc_release_ctlr_access */




/* === definition of private functions =================================== */





/**
 * Bus DMA callback function to obtain physical address of DMA memory.
 */

static void avmmisc_dma_map_addr
   (void              *pCallbackArg,
    bus_dma_segment_t *paSegs,
    int                iNumSegs,
    int                iError)
{
   u_int32_t *p;

   KASSERT (iNumSegs <= 1,
            __FUNCTION__ ": Invalid number of segments, only 1 expected");
   
   DBG (LOG_DEBUG, -1, "Called with %d segments, segment array %p, error value %d",
        iNumSegs, paSegs, iError);

   if (iError != 0 || iNumSegs < 1 || paSegs == NULL)
   {
      return;
   }

   p = (u_int32_t *) pCallbackArg;
   *p = (u_int32_t) (paSegs [0].ds_addr);
   
} /* avmmisc_dma_map_addr */
