Next | Prev | Up | Top | Contents | Index

Complete EISA Character Driver

The code in this section displays a complete character device driver for an EISA card, the Roland RAP-10 synthesizer. This inexpensive synthesizer card can be installed in an Indigo2 and driven by a program through this device driver.

*
* rap  - Roland RAP-10 Musical Board 
*
* $Revision: 1.1 $
*
*FLAG   PREFIX  SOFT    #DEV    DEPENDENCIES
c   rap     61      - 

$$$

Example 17-3 : Configuration File /var/sysgen/rap.sm for RAP-10 Driver

VECTOR: bustype=EISA module=rap ctlr=0 adapter=0 iospace=(EISAIO,0x330,16) probe_space=(EISAIO,0x330,1)

Example 17-4 : Installation Script for RAP-10 Driver

#!/bin/csh

if [ `whoami` != "root" ]
then
  echo "You must be root to run this script.\n"
  exit 1
fi

echo "cp rap.o /var/sysgen/boot/rap.o\n"
cp rap.o /var/sysgen/boot/rap.o

echo "cp rap.master /var/sysgen/master.d/rap\n"
cp rap.master /var/sysgen/master.d/rap

echo "cp rap.sm /var/sysgen/system/rap.sm"
cp rap.sm /var/sysgen/system/rap.sm

echo "mknod /dev/rap c 62 0\n"
mknod /dev/rap c 62 0

echo "Make a new kernel anytime by typing: autoconfig -f -v\n"

Example 17-5 : Program to Test RAP-10 Driver

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include "rap.h"    
/*  
 *  record.c
 *
 *  This program plays song from a previuosly recorded file
 *  using RAP-10 board. 
 *
 */

#define    BUF_SIZE    4096
#define    FILE_HDR    "RAP-10 WAVE FILE"
#define    RAP_FILE    "/dev/rap"
#define    MAX_BUF     10 
#define    FOREVER     for(;;)

uchar_t    buf[BUF_SIZE];
uchar_t    *fname;
void       endProg( int );

main (int argc, char **argv)
{
    register int    fd, rapfd, bytes;
    if ( argc <= 1 ) {
        printf ("play: Usage: play <file_name>\n");
        exit(0);
    }
    fname = argv[1];
    printf ("play: opening file %s\n", fname); 
    fd = open (fname, O_RDONLY);
    if ( fd == -1 ) {
        printf ("play: Cannot create file, errno = %d\n", errno);
        close(rapfd);
        exit(0);
    }
    printf ("play: Checking RAP-10 File ID\n"); 
    if ( read(fd, buf, strlen(FILE_HDR)) <= 0 ) {
        printf ("play: Could not read the file ID, errno = %d\n",
        errno);
        close(fd);
        exit(0);
    }
    if ( strcmp(buf, FILE_HDR) ) {
        printf ("play: File is not a RAP file\n");
        close(fd);
        exit(0);
    }
    printf ("play: opening RAP card\n");
    rapfd = open (RAP_FILE, O_WRONLY);
    if ( rapfd <= 0 ) {
        printf ("play: Cannot open RAP card, errno = %d\n",
        errno);
        exit(0);
    }
    printf ("play: Playing ..please wait\n");      
    /*   ignore Interrupt   */
    sigset (SIGINT, SIG_IGN ); 
    FOREVER {     
        bytes = read(fd, buf, BUF_SIZE);  
        if ( bytes < 0 ) {
            printf ("play: error reading data, errno = %d",
                errno);
            close(fd);
            close(rapfd);
            exit(0);
        }
        if ( bytes == 0 )
            break;
        bytes = write(rapfd, buf, BUF_SIZE);
        if ( bytes <= 0 ) {
            printf ("play: Cannot read from RAP, errno = %d\n",
            errno);
            close (rapfd);
            close (fd);
            exit(0);
        }
    }
    printf ("play: waiting for Play to End\n");
    if ( ioctl (rapfd, RAPIOCTL_END_PLAY) ) {
           printf ("play: Ioctl error %d", errno );
    }
    else printf ("play: Song succesfully played\n");
    close(rapfd);
    close (fd);
}

Example 17-6 : Complete EISA Character Driver for RAP-10

/*****************************************************************************
 *
 *          Roland RAP-10 Music Card Device Driver for Eisa Bus
 *          ---------------------------------------------------
 *
 *   INTRODUCTION: 
 *   -------------
 *   This file contains the device driver for Roland RAP-10
 *   Music Card. Currently it contains necessary routines to Record and
 *   Playback a  Wave file. The MIDI Implementation is to be defined and
 *   implemented at later time.
 *
 *   DESIGN OVERVIEW:
 *   ----------------
 *   We will use DMA for wave data movements. At any given time, the card
 *   can be either playing or recording and both operations are not allowed.
 *   Also no more than one process at a time can access the card.
 *
 *   Circular Buffers:
 *   -----------------
 *   Since DMA operation is performed independently of the processor,
 *   we will buffer the user's data and release the user's process to
 *   do other things (i.e. preparing more data). Internally we use a
 *   circular queue (rwQue) to store the data to be played or recorded.
 *   Each entry in this queue is of the type rwBuf_t where the data will
 *   be stored.  Each entry can store up to RW_BUF_SIZE bytes of data.
 *   At the init time, we try to allocate two DMA channels for the card:
 *   Channel 5 and 6. If we can only allocate Channel 5, we will use the
 *   card in Mono mode, otherwise, we will use it as Stereo. DMA has two
 *   buffers of its own: dmaRigh[] and dmaLeft[] for each Channel. For
 *   Stereo play, the data user provides us is of the format:
 *
 *       <Left Byte><Right Byte><Left Byte><Right Byte>.....
 *
 *   So for playing, we have to move all Left_Bytes to dmaLeft buffer
 *   and all Right_Bytes to the dmaRight buffer (in Stereo mode only).
 *   In mono mode, we will use dmaLeft[] buffer and all the user's data
 *   are moved to dmaLeft[].
 *
 *   The basic operation of the Card are as follow:
 *
 *   Playing:
 *   --------
 *   For playing wave data, the user must first open the card through
 *   open() system call.The call comes to us as rapopen(). This
 *   routine resets all global values, states and counters, prepares
 *   necessary DMA structures for each channel, disables RAP-10
 *   interrupts and establishes this process as the owner of the card.
 *
 *   The user provides us with the wave data by issuing write()
 *   system calls. This call comes to us as rapwrite(). We will
 *   move the data from user's address space into an empty rwQue[]
 *   entry and will retrun so that the user can issue another call.
 *   If there is no DMA going, we will start one and the data will
 *   start to be moved to the Card to be played.
 *   The user can issue as many write() as necessary. The playing
 *   operation will be done by either closing the card or issuing
 *   an Ioctl call. Issuing Ioctl, will leave this process as owner
 *   still while closing the card will release the card.
 *
 *   Recording:
 *   ----------
 *   Assuming that the user has opened the card and is the current
 *   owner, user will issue read() system call. The call comes to
 *   us as rapread(). If no DMA Record is going on, we will start
 *   one. We will move data from rwQue[] entries (as they are filled)
 *   to user's address space. The recording is done either by a
 *   close() or ioctl() call.
 *
 *   DMA Starting:
 *   -------------
 *   For Playing, we will start DMA when we have a full circular buffer.
 *   This is done so that we have enough data available for a fast DMA
 *   operation to be busy with. For recording, we will start DMA
 *   immediatly.
 *
 *   Interrupts:
 *   -----------
 *   For each DMA transfer, we will receive two interrupts: One when 1st
 *   half the buffer is transfered, one when 2nd half of the buffer is
 *   transfered.  We must fill the half that has just been transfered with
 *   fresh data.  Note that in Stereo mode, there are two DMA operation
 *   going. So when we receive Interrupt for one DMA, we must wait for the
 *   exact interrupt from the other DMA and service both DMA's half buffers.
 *
 *   Card Address and IRQ
 *   --------------------
 *   We will use the default bus address of 0x330 and IRQ 5. Change in
 *   bus address should also be reflected in /var/sysgen/system/rap.sm
 *   file.  Changes in IRQ should be reflected in the source code and
 *   the program must be recomplied.
 *
 *   ISSUES:
 *   -------
 *   1. The DMA processing and transfer of data from/to user's buffer
 *      are independent of each other. When we are servicing the
 *      one half of the dma buffer that just been transfered, there is
 *      no guarantee that we can fill that half of the buffer BEFORE
 *      dma is done with the other half. In this case, dma plays the
 *      fist half of buffer WHILE we are writing into it.
 *
 *   2. Currently eisa_dma_disable() routine does not actually
 *      releases the Dma channels. This is the reason why we access
 *      the Dma channel table (e_ch[]) ourselves and release the
 *      channel.
 *
 *   3. Somehow because of number 2, the Play program cannot be
 *      stopped with a Ctrl-C. In Play program this signal is
 *      explicitly ignored. Trapping a Ctrl-C causes a kernel panic.
 *      Once we have a workable eisa_dma_disable(), this problem will
 *      be resolved.
 *
 *   TECHNICAL REFERENCES:
 *   ---------------------
 *   Roland RAP-10 Technical Reference and Programmer's Guide, Ver. 1.1
 *   IRIX Device Driver Programming Guide
 *   IRIX Device Driver Reference Pages.
 *   Intel 82357 Preliminary Reference, Section: 3.7.8 Mode Register (pp: 223)
 *
 ******************************************************************************
 ***                                                                        ***
 ***   Copyright 1994, Silicon Graphics Inc., Mountain View, CA.            ***
 ***                                                                        ***
 ******************************************************************************
 */
#include "sys/types.h"
#include "sys/file.h"
#include "sys/errno.h"
#include "sys/open.h"
#include "sys/conf.h"
#include "sys/cmn_err.h"
#include "sys/debug.h"
#include "sys/param.h"
#include "sys/edt.h"
#include "sys/pio.h"
#include "sys/uio.h"
#include "sys/proc.h"
#include "sys/user.h"
#include "sys/eisa.h"
#include "sys/sema.h"
#include "sys/buf.h"
#include "sys/cred.h"
#include "sys/kmem.h"
#include "sys/ddi.h"
#include "./rap.h"
/*
 *  Macros to Read/Write 8 and 16-bit values from an address
 */
#define OUTB(addr, b)  ( *(volatile uchar_t *)(addr) = (b) )
#define INPB(addr)     ( *(volatile uchar_t *)(addr) )
#define OUTW(addr, w)  ( *(volatile ushort_t *)(addr) = (w) )
#define INPW(addr)     ( *(volatile ushort_t *)(addr) )
/*
 *  Raising and lowering CPU interrupt
 */
#define LOCK()     spl5()
#define UNLOCK(s)  splx(s)
#define FROM_INTR  1
#define FROM_USR   0
#define User_pid   u.u_procp->p_pid
/*
 *   IRQ and DMA channels we need.
 *
 */
#define IRQ_MASK    0x0020
#define DMAC_CH5        0x20     /*  DMA Channel 5   */
#define DMAC_CH6        0x40     /*  DMA Channel 6   */
/*=======================================*
 *       MIDI and RAP Registers      *
 *=======================================*
 *
 *  The following is a description of RAP-10 registers. The same
 *  names used throughout this program. Some of these registers are
 *  8-bit and some are 16-bit long.
 *
 *      mdrd:    MIDI Receive Data
 *      mdtd:    MIDI Transmit Data
 *      mdst:    MIDI Status
 *      mdcm:    MIDI Command
 *      pwmd:    Pulse Width Modulation Data
 *      timm:    Timer MSB data
 *      gpcm:    GPCC Command
 *      dtci:    DMA Transfer Count Buffer Interrupt Status
 *      adcm:    GPCC Analog to Digital Command
 *      dacm:    D/A Command and DMA Transfer Configuration
 *      gpis:    GPCC Interrupt Status
 *      gpdi:    GPCC DMA/Interrupt Enable
 *      gpst:    GPCC Status
 *      dad0:    Digital to Analog Data Channel 0
 *      addt:    A/D Data Transfer
 *      dad1:    Digital to Analog Data Channel 1
 *      timd:    Timer Data
 *      cmp0:    Compare Register Channel 0
 *      dtcd:    DMA Transfer Count Data
 *      cmp1:    Compare Register Channel 1
 *
 *    These defines indicate the offsets of the above registers
 *    from the Drive's base address:
 */
#define MDRD    0x0
#define MDTD    0x0
#define MDST    0x1
#define MDCM    0x1
#define PWMD    0x2
#define TIMM    0x3
#define GPCM    0x3
#define DTCI    0x4
#define ADCM    0x4
#define DACM    0x5
#define GPIS    0x6
#define GPDI    0x6
#define GPST    0x8
#define DAD0    0x8
#define ADDT    0xa
#define DAD1    0xa
#define TIMD    0xc
#define CMP0    0xc
#define DTCD    0xe
#define CMP1    0xe

typedef struct rapReg {
    uchar_t  mdrd;
    uchar_t  mdtd;
    uchar_t  mdst;
    uchar_t  mdcm;
    uchar_t  pwmd;
    uchar_t  timm;
    uchar_t  gpcm;
    uchar_t  dtci;
    uchar_t  adcm;
    uchar_t  dacm;
    ushort_t gpis;
    ushort_t gpdi;
    ushort_t gpst;
    ushort_t dad0;
    ushort_t addt;
    ushort_t dad1;
    ushort_t timd;
    ushort_t cmp0;
    ushort_t dtcd;
    ushort_t cmp1;
} rapReg_t;
/*==========================================================*
 *      dtct  (DMA Transfer Count)                          *
 *==========================================================*/
#define DTCD_DRQ0  0x00FF   /*  DRQ 0 bits (0-7)  */
#define DTCD_DRQ1  0xFF00   /*  DRQ 1 bits (8-15) */
/*==========================================================*
 *      gpst  (GPCC Status)                                 *
 *==========================================================*/
#define GPST_PWM2 0x0800  /* PWM2 Busy (0=Write Enable, 1=Busy) */
#define GPST_PWM1 0x0400  /* PWM1 Busy (0=Write Enable, 1=Busy) */
#define GPST_PWM0 0x0200  /* PWM0 Busy (0=Write Enable, 1=Busy) */
#define GPST_EPB  0x0100  /* EP Convertor Busy (0=Write Enable, 1=Busy) */
#define GPST_GP1  0x0080  /* GP-chip, Ch 1 Acess (1 = Access)   */
#define GPST_GP0  0x0040  /* GP-chip, Ch 0 Acess (1 = Access)   */
#define GPST_MTE  0x0020  /* MIDI Tx Enable (0=Tx_Fifo buff full) */
#define GPST_ORE  0x0010  /* MIDI Overrun Error (1 = error) */
#define GPST_FE   0x0008  /* MIDI Framing Error (1 = error) */
#define GPST_ADE  0x0004  /* A/D Error (1 = error) */
#define GPST_DE1  0x0002  /* D/A Ch 1 Write Error (1 = error) */
#define GPST_DE0  0x0001  /* D/A Ch 0 Write Error (1 = error) */
/*==========================================================*
 *        gpdi  (GPCC DMA/Interrupt Enable (pp: 4-18)       *
 *==========================================================*/
#define GPDI_ITC  0x8000  /* DMA Transfer Cnt Match (0=Disable) */
#define GPDI_DC2  0x4000  /* DMA Chann. Assignment, bit2 (pp:4-18) */
#define GPDI_DC1  0x2000  /* DMA Chann. Assignment, bit1 (pp:4-18) */
#define GPDI_DC0  0x1000  /* DMA Chann. Assignment, bit0 (pp:4-18) */
#define GPDI_DT1  0x0800  /* DMA Trans. Mode, bit:1 (pp: 4-18) */
#define GPDI_DT0  0x0400  /* DMA Trans. Mode, bit:0 (pp: 4-18) */
#define GPDI_OVF  0x0200  /* Free Run.Cntr (FCR) Ov.Flow (0=Disable)*/
#define GPDI_TC1  0x0100  /* Timer 1 Compare Match (0=Disable) */
#define GPDI_TC0  0x0080  /* Timer 0 Compare Match (0=Disable) */
#define GPDI_RXD  0x0040  /* MIDI Data Read Request (0=Disable) */
#define GPDI_TXD  0x0020  /* MIDI Tx_fifo Buf Empty (0=Disable) */
#define GPDI_ADD  0x0010  /* A/D Data Ready (0=Disable)  */
#define GPDI_DN1  0x0008  /* D/A Ch1 Note ON Ready (0=Disable) */
#define GPDI_DN0  0x0004  /* D/A Ch0 Note ON Ready (0=Disable) */
#define GPDI_DQ1  0x0002  /* D/A Ch1 Data Request  (0=Disable) */
#define GPDI_DQ0  0x0001  /* D/A Ch0 Data Request  (0=Disable) */
/*==========================================================*
 *         gpis  (GPCC Interrupt Status .. pp: 4-16)        *
 *==========================================================*/
#define GPIS_ITC  0x8000  /*  DMA Transfer Count Match   */
#define GPIS_JSD  0x0400  /*  Joystick Data Ready */
#define GPIS_OVF  0x0200  /*  Free Running Countr Overflow */
#define GPIS_TC1  0x0100  /*  Timer1 Compare Match */
#define GPIS_TC0  0x0080  /*  Timer0 Compare Match */
#define GPIS_RXD  0x0040  /*  MIDI Data Read Request */
#define GPIS_TXD  0x0020  /*  MIDI Tx_fifo Buf. Empty */
#define GPIS_ADD  0x0010  /*  A/D Data Ready */
#define GPIS_DN1  0x0008  /*  D/A Ch1 Note ON Ready */
#define GPIS_DN0  0x0004  /*  D/A Ch0 Note ON Ready */
#define GPIS_DQ1  0x0002  /*  D/A Ch1 Data Request */
#define GPIS_DQ0  0x0001  /*  D/A Ch0 Data Request */
/*===================================================================*
 *         dacm (Digital To Analogue Cmd and DMA Transfer Config)    *
 *===================================================================*/
#define DACM_SCC   0x80  /* DMA Size Cmp. Cnt (0=in Sample, 1=in Bytes)*/
#define DACM_TS2   0x40  /* DMA Trnsfr Size, bit 2 (pp: 4-14) */
#define DACM_TS1   0x20  /* DMA Trnsfr Size, bit 1 (pp: 4-14) */
#define DACM_TS0   0x10  /* DMA Trnsfr Size, bit 0 (pp: 4-14) */
#define DACM_DL1   0x08  /* Ch1 DA Data Len (0=8 bit, 1=17 bit) */
#define DACM_DL0   0x04  /* Ch0 DA Data Len (0=8 bit, 1=17 bit) */
#define DACM_DS1   0x02  /* Ch1 DA Convrsion (0=Stop, 1=Start) */
#define DACM_DS0   0x01  /* Ch0 DA Convrsion (0=Stop, 1=Start) */
/*=====================================================*
 *             adcm (  GPCC AD Command )               *
 *=====================================================*/
#define  ADCM_MON    0x40  /* Monitor MIC (0=Monitor Off) */
#define  ADCM_GIN    0x20  /* Gain Input (0=Line, 1=Mic) */
#define  ADCM_AF1    0x10  /* Analog Freq Selection bit 1 (pp: 4-13) */
#define  ADCM_AF0    0x08  /* Analog Freq Selection bit 0 (pp: 4-13) */
#define  ADCM_ADL    0x04  /* Analog Data Length (0=8, 1=16) */
#define  ADCM_ADM    0x02  /* Analog Data Conv. Mode (0=Mono,1=Stereo) */
#define  ADCM_ADS    0x01  /* Analog Data Conv. Start(0=Stop,1=Start) */
/*=====================================================*
 *            dtci ( DMA Trans.Count Buf Intr. Stat    *
 *=====================================================*/
#define DTCI_BF1        0x08  /* DMA DRQ1 buff full (1 = full) */
#define DTCI_BH1        0x04  /* DMA DRQ1 buff half (1 = full) */
#define DTCI_BF0        0x02  /* DMA DRQ0 buff full (1 = full) */
#define DTCI_BH0        0x01  /* DMA DRQ0 buff half (1 = full) */
/*========================================*
 *             gpcm  ( GPCC Command )     *
 *========================================*/
#define GPCM_RST   0x80    /*   Reset bit */
#define GPCM_PWM2  0x10    /*   Select PWM channel 2 */
#define GPCM_PWM1  0x08    /*   Select PWM channel 1 */
#define GPCM_PWM0  0x04    /*   Select PWM channel 0 */
#define GPCM_FRCM  0x02    /*   Free Run. Counter (1=Start) */
#define GPCM_MTT   0x01    /*   MIDI Timed Trans */
                           /*   ( 1 = Timer INT enabled )   */
/*======================================*
 *       timm  (Timer MSB data)         *
 *======================================*/
#define TIMM_FRC   0x04    /*   Free Running Counter Bit 16 */
#define TIMM_CR1   0x02    /*   Compare Reg 1 Bit 16 */
#define TIMM_CR0   0x01    /*   Compare Reg 0 Bit 16 */
/*===================================*
 *      mdcm (MIDI Command)          *
 *===================================*/
#define MDCM_UART      0x3f   /*   UART mode */
#define MDCM_MPU       0xff   /*   MPU Reset */
#define MDCM_VERSION   0xac   /*   Version  */
#define MDCM_REVISION  0xad   /*   Revision */
/*===================================*
 *        mdst (MIDI Status)         *
 *===================================*/
#define  MDST_DSR  0x80 /*  DSR = 0 if ready */
#define  MDST_DDR  0x40 /*  DDR = 0 if ready */
/*====================================*
 *      RAP Card Info                 *
 *====================================*
 *
 *   These are the information regarding the RAP Card.
 *   The info being tracked are:
 *
 *  ci_state:   Our state (Installed, Opened, Playing, Recording)
 *  ci_pid:     PID of process opened us.
 *  ci_addr[]:  EISA  Addresses
 *  ci_irq:     EISA Interrupt number we use
 *  ci_ctl:     Controller number we save from edt struct
 *  ci_adap:    Adaptor number we save from edt struct.
 *  ci_dmaCh6:  DMA Channel 6
 *  ci_dmaCh5:  DMA Channel 5
 *  ci_dmaBuf6: EISA DMA Buffer struct for Channel 6
 *  ci_dmaBuf5: EISA DMA Buffer struct for Channel 5
 *  ci_dmaCb6:  EISA DMA Control Block for Channel 6
 *  ci_dmaCb5:  EISA DMA Control Block for Channel 5
 *  di_state:   DMA buffers state (Idle, Progress)
 *  di_idx:     Current rwQue[] entry being used.
 *  di_ptr:     Address in rwQue buffer
 *  di_which:   Which half of DMA buffer (0=1st half, 1=2nd Half)
 *  di_bh:      Total DMA Buffer Half (BH) Interrupt received.
 *  di_bf:      Total DMA Buffer Full (BF) Interrupt received.
 *  ri_state:   State of Circular buffer (Wanted_Empty, etc.)
 *  ri_free:    Total Free entries in rwQue[]
 *  ri_full:    Total Full entries in rwQue[]
 *  ri_idx:     Current rwBuf for Read/Write
 *  ri_tout;    =1 if Timed out on read/write
 *  ri_note;    number of Note_On received
 *  ri_ptr:     Pointer in current rwBuf
 */
typedef  struct eisa_dma_buf   dmaBuf_t;
typedef  struct eisa_dma_cb    dmaCb_t;
typedef struct cardInfo_s {
    /*   Card Installation Info */
    ushort_t   ci_state;
    pid_t      ci_pid;
    caddr_t    ci_addr[NBASE];
    int    ci_irq;
    int    ci_ctl;
    int    ci_adap;
    int    ci_dmaCh6;
    int    ci_dmaCh5;
    dmaBuf_t   *ci_dmaBuf6;
    dmaBuf_t   *ci_dmaBuf5;
    dmaCb_t    *ci_dmaCb6;
    dmaCb_t    *ci_dmaCb5;
    /*   DMA Buffer Information data */
    uchar_t   di_state;
    short     di_idx;
    uchar_t   di_which;
    caddr_t   di_ptr;
    uchar_t   di_bh;
    uchar_t   di_bf;
    /*   Circular buffer Information data */
    uchar_t   ri_state;
    short     ri_free;
    short     ri_full;
    short     ri_idx;
    uchar_t   ri_tout;
    uchar_t   ri_note;
    caddr_t   ri_ptr;
} cardInfo_t;
/*   ci_state  values   */
#define  CARD_INSTALLED    0x0001
#define  CARD_STEREO       0x0002
#define  CARD_OPENED       0x0004
#define  CARD_PLAYING      0x0010
#define  CARD_RECORDING    0x0020
/*   di_state values    */
#define  DI_DMA_IDLE       0x00
#define  DI_DMA_PLAYING    0x01
#define  DI_DMA_RECORDING  0x02
#define  DI_DMA_END_PLAY   0x04
#define  DI_DMA_END_RECORD 0x08
/*   ri_state values   */
#define RI_WANTED_EMPTY    0x01
/*====================================*
 *      Read/Write Circular Buffers   *
 *====================================*
 *  This is the description of our circular buffers used
 *  to store D/A and A/D values. D/A values are stored from
 *  user's buffer and then moved to DMA buffers. A/D data is
 *  moved from DMA buffers to these buffers and then moved
 *  to user's buffer. The fields are as follow:
 *    rw_state:    buffer state (Empty, Busy, Full)
 *    rw_idx:      Index of this buffer in rwQue[];
 *    rw_count:    Total bytes in the buffer
 *    rw_buf[]:    The buffer itself.
 *      RW_MIN_FULL:  We will start a D/A DMA when we have this many
 *          full buffer on hand. This is done so that we can
 *          provide enough full buffers for DMA to process.
 */
#define  RW_BUF_SIZE    8192
#define  RW_BUF_COUNT   20
#define  RW_MIN_FULL    1
#define  RW_TIMEOUT     1600
typedef struct rwBuf_s {
    uchar_t     rw_state;
    short       rw_idx;
    int     rw_count;
    uchar_t     rw_buf[RW_BUF_SIZE];
} rwBuf_t;
/*   rw_state  values   */
#define RW_EMPTY        0x00  /*  used as parameter only  */
#define RW_FULL         0x01
#define RW_WANTED_FULL  0x02
#define RW_WANTED_EMPTY 0x04
/*==================================*
 *         Global values            *
 *==================================*/
#define   DMA_BUF_SIZE        8192
#define   DMA_HALF_SIZE       4096
int                  rapdevflag = 0;
static cardInfo_t    cardInfo;
static caddr_t       dmaRight;
static caddr_t       dmaLeft;
static paddr_t       dmaRightPhys;
static paddr_t       dmaLeftPhys;
static rwBuf_t       rwQue[RW_BUF_COUNT];
static caddr_t       eisa_addr;
/*
 *  Eisam Dma Channel semaphores..shoule be removed when
 *  proper way of releasing channels found
 */
extern struct eisa_ch_state {
    sema_t chan_sem;                /* inuse semaphore for each channel */
    sema_t dma_sem;                 /* dma completion semaphore */
    struct eisa_dma_buf *cur_buf;   /* current eisa_dma_buf being dma'ed */
    struct eisa_dma_cb *cur_cb;     /* ptr to current command block */
    int    count;
} e_ch[];
/*=========================================*
 *        Driver Entry routines Data       *
 *=========================================*/
int    rapopen  ( dev_t *, int, int, cred_t  * );
int    rapread ( dev_t, uio_t *, cred_t * );
int    rapwrite ( dev_t, uio_t *, cred_t * );
int    rapclose ( dev_t, int, int, cred_t  * );
void   rapedtinit ( struct edt  * );
void   rapintr ( int );
int    rapioctl (dev_t, int, void *, int, cred_t *, int *);
/*=======================================*
 *  Misc and Internal routines           *
 *=======================================*/
static void   rapDisInt (cardInfo_t  *);
static int    rapGetDma( dmaBuf_t  **,  dmaCb_t **, int );
static int    rapClose(uchar_t);
static short  rapGetNextEmpty (short, uchar_t);
static short  rapGetNextFull (short, uchar_t);
static void   rapPrepEisa( dmaBuf_t *, dmaCb_t *, uchar_t, paddr_t);
static int    rapStart(uchar_t);
static void   rapStop(uchar_t);
static void   rapStartDA();
static void   rapStartAD();
static void   rapBufToDma( int );
static void   rapDmaToBuf( int );
static void   rapMarkBuf(rwBuf_t *, cardInfo_t *, uchar_t);
static int    rapKernMem(uchar_t);
static void   rapSetAutoInit(cardInfo_t *, uchar_t);
static void   rapTimeOut( void *);
static void   rapNoteOn(cardInfo_t *, ushort_t );
static void   rapNoteOff(cardInfo_t *);
static void   rapZeroDma(cardInfo_t *, int);
static void   rapReleaseDma (cardInfo_t *);
/*************************************************************************
 *    r a p e d t i n i t
 *************************************************************************
 *   Name:      rapedtint
 *   Purpose:   Initializes the driver. Called once for each controller.
 *    Called only once.
 *   Returns:   None.
 *************************************************************************/
void
rapedtinit ( struct edt *e )
{
    int      ctl, iospace, dmac, eirq;
    cardInfo_t   *ci;
    piomap_t      *pmap;
    iospace_t     eisa_io;

    ci = &cardInfo;
    cmn_err (CE_NOTE, "rapedtinit: Installing RAP board.");
    bzero ((void *)ci, sizeof(cardInfo_t) );
    dmaRight = dmaLeft = (caddr_t)NULL;
    ci->ci_ctl = e->e_ctlr;
    ci->ci_adap = e->e_adap;
    /*
     *    Get the base address of Eisa bus (for rapSetAutoInit)
     */
    bzero (&eisa_io, sizeof(iospace_t));
    eisa_io.ios_iopaddr = 0;
    eisa_io.ios_size    = 1000;
    pmap = pio_mapalloc (e->e_bus_type, 0, &eisa_io, PIOMAP_FIXED, "eisa");
    if ( pmap == (piomap_t *)NULL ) {
        cmn_err (CE_WARN, "rapedtinit: Cannot get Eisa bus address");
        return;
    }
    eisa_addr = pio_mapaddr (pmap, eisa_io.ios_iopaddr);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapedtinit: Eisa base address = %x", eisa_addr);
    #endif
    /*===================================================*
     *   map EISA IO/Memory addresses for RAP-10 card    *
     *===================================================*/
    for ( iospace = 0; iospace < NBASE; iospace++ ) {
        /*  any address to map ?    */
        if ( !e->e_space[iospace].ios_iopaddr )
            continue;
        pmap = pio_mapalloc ( e->e_bus_type, e->e_adap,
                &e->e_space[iospace],
                PIOMAP_FIXED, "rap10" );
        ci->ci_addr[iospace] = pio_mapaddr ( pmap,
                     e->e_space[iospace].ios_iopaddr );
    }
    /*  is Card still there  ? */
    if ( badaddr(ci->ci_addr[0], 1) ) {
        cmn_err (CE_WARN, "rapedtinit: RAP board not installed.");
        return;
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapedtinit: First Load..allocating IRQ");
    #endif
    eirq = eisa_ivec_alloc( e->e_adap, IRQ_MASK, EISA_EDGE_IRQ );
    if ( eirq < 0 ) {
        cmn_err (CE_WARN,
        "rapedtinit: Could not allocate IRQ for RAP card.");
        return;
    }
    /*    set Interrupt handler     */
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapedtinit: Setting Interrupt Handler for IRQ %d",
              eirq);
    #endif
    if ( eisa_ivec_set(e->e_adap, eirq, rapintr, e->e_ctlr) == -1 ) {
        cmn_err (CE_NOTE,
        "rapedtinit: Could not set Interrupt handler for Irq %d", eirq);
        ci->ci_state = 0;
        return;
    }
    ci->ci_irq = eirq;
    /*======================================*
     *  DMA Channels Allocation             *
     *======================================*/
    /*   DMA channel 5 */
    dmac = eisa_dmachan_alloc ( e->e_adap, DMAC_CH5 );
    if ( dmac < 0 ) {
        cmn_err (CE_WARN,
             "rapedtinit: Could not allocate DMA Channel 5.");
        return;
    }
    ci->ci_dmaCh5 = dmac;
    /*   DMA channel 6  */
    dmac = eisa_dmachan_alloc ( e->e_adap, DMAC_CH6 );
    if ( dmac < 0 ) {
        cmn_err (CE_WARN,
            "rapedtinit: Could not allocate DMA Chann 6.");
        cmn_err (CE_WARN,
            "rapedtinit: RAP is initialized as Mono.");
    }
    else {
        ci->ci_dmaCh6 = dmac;
        ci->ci_state |= CARD_STEREO;
    }
    /*==============================*
     *      DMA Buffer allocation   *
     *==============================*/
    if ( rapKernMem (1) ) {
        cmn_err (CE_WARN, "rapedtinit: Did not install RAP-10.");
        return;
    }
    ci->ci_state |= CARD_INSTALLED;
#ifdef DEBUG
    cmn_err (CE_NOTE, "rapedtinit: RAP installed, Addr: %x, Irq: %d.",
            ci->ci_addr[0], ci->ci_irq );
    cmn_err (CE_NOTE, "rapedtinit: Init as %s, Dma 1 = %d, Dma 0 = %d",
            (ci->ci_state & CARD_STEREO ? "Stereo":"Mono"),
            ci->ci_dmaCh5, ci->ci_dmaCh6);
#endif
    return;
}  /***   End rapedtinit    ***/
/*************************************************************************
 *     r a p o p e n
 *************************************************************************
 *  Name:      rapopen
 *  Purpose:   Opens the RAP board and initializes necessary data
 *  Returns:   0 = Success, or appropriate error number.
 *************************************************************************/
int
rapopen ( dev_t  *dev, int oflag, int otyp, cred_t  *cred)
{
    register int    i;
    cardInfo_t      *ci;
    rwBuf_t         *rw;
    dmaBuf_t    *dmaB;
    dmaCb_t         *dmaC;
    ci = &cardInfo;
#ifdef DEBUG
    cmn_err (CE_NOTE, "rapopen: Opening, Addr = %x, ci_state = %x",
         ci->ci_addr[0], ci->ci_state );
#endif
    /*
     *  No card is installed or card is already opened
     */
    if ( !(ci->ci_state & CARD_INSTALLED) )
        return (ENODEV);
    if ( ci->ci_state & CARD_OPENED )
        return (EBUSY);
    /*   Allocate DMA Buf and Cb for Channel 5  */
    if ( ci->ci_dmaBuf5 == (dmaBuf_t *)NULL ) {
        if ( rapGetDma(&dmaB, &dmaC, ci->ci_dmaCh5) ) {
            cmn_err (CE_WARN,"rapopen: Could not allocate DMA Buf 5.");
            return (ENOMEM);
        }
        ci->ci_dmaBuf5 = dmaB;
        ci->ci_dmaCb5  = dmaC;
    }
    /*   if in stereo, do the same for Channel 6  */
    if ( ci->ci_state & CARD_STEREO ) {
        if ( rapGetDma(&dmaB, &dmaC, ci->ci_dmaCh6) ) {
            cmn_err (CE_WARN,
                 "rapopen: Could not allocate DMA Buf 6.");
            return (ENOMEM);
        }
        ci->ci_dmaBuf6 = dmaB;
        ci->ci_dmaCb6  = dmaC;
    }
    /*   Initialize Card Info structure  */
    ci->ri_idx   = 0;
    ci->di_idx   = 0;
    ci->ri_state = 0;
    ci->di_state = 0;
    ci->di_ptr   = 0;
    ci->ri_ptr   = 0;
    ci->ri_free  = RW_BUF_COUNT;
    ci->ri_full  = 0;
    ci->ci_state &= ~(CARD_PLAYING | CARD_RECORDING );
    ci->ci_state |= CARD_OPENED;
    ci->ci_pid    = User_pid;
    /*   Initialize Circular Buffers   */
    for ( i = 0; i < RW_BUF_COUNT; i++ ) {
        rw = &rwQue[i];
        rw->rw_count = 0;
        rw->rw_state = 0;
        rw->rw_idx   = i;
        bzero (rw->rw_buf, RW_BUF_SIZE);
    }
    rapDisInt(ci);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapopen: Opened succesfully");
    #endif
    return(0);
}  /***  End rapopen  ***/
/*************************************************************************
 *        r a p w r i t e
 *************************************************************************
 *  Name:       rapwrite
 *  Purpose:    Write entry routine. This routine will transfer user's
 *      data to current or an empty entry in rwQue[] and starts
 *      DMA if none is going.
 *  Returns:    0 = Success, or errno
 *************************************************************************/
int
rapwrite (dev_t  dev, uio_t  *uio, cred_t  *cred)
{
    cardInfo_t    *ci;
    rwBuf_t       *rw;
    toid_t        to_id;
    int       avail, size, totBytes, err, s;
    ci = &cardInfo;
    /*=========================*
     *  Error Checking     *
     *=========================*/
    /*  no card is installed  */
    if ( !(ci->ci_state & CARD_INSTALLED) )
        return (ENODEV);
    /*  card is not opened */
    if ( !(ci->ci_state & CARD_OPENED) )
        return (EACCES);
    /*  we are not the owner  */
    if ( ci->ci_pid != User_pid )
        return (EACCES);
    /*  is busy recording  */
    if ( ci->ci_state & CARD_RECORDING )
        return (EACCES);
    ci->ci_state |= CARD_PLAYING;
    rw = &rwQue[ci->ri_idx];
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapwrite: %d bytes, buf = %d, rw_count = %d, free = %d, full = %d",
        uio->uio_resid, ci->ri_idx, rw->rw_count, ci->ri_free, ci->ri_full);
    #endif
    /*  if it is full, wait till it is Empty   */
    s = LOCK();
    if ( rw->rw_state & RW_FULL ) {
        ci->ri_ptr = NULL;
        ci->ri_tout = 0;
        to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0);
        while ( (rw->rw_state & RW_FULL) && !ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "rapwrite: waiting for buf %d to be Empty",
                            rw->rw_idx );
            #endif
            rw->rw_state |= RW_WANTED_EMPTY;
            if ( sleep (rw, PUSER | PCATCH) ) {
                untimeout(to_id);
                #ifdef DEBUG
                cmn_err (CE_NOTE, "rapwrite: Interrupted");
                #endif
                rw->rw_state &= ~RW_WANTED_EMPTY;
                UNLOCK(s);
                return (EINTR);
            }
        } /* while */
        untimeout(to_id);
        /*   we timed out ..couldn't get the buffer  */
        if ( ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "rapwrite: Timed out");
            #endif
            rw->rw_state &= ~RW_WANTED_EMPTY;
            UNLOCK(s);
            return (EIO);
        }
    } /*  if (rw->rw_state & RW_FULL */
    UNLOCK(s);
    /*  adjuest the read/write address if necessary */
    if ( ci->ri_ptr == NULL )
        ci->ri_ptr = rw->rw_buf;
    totBytes = uio->uio_resid;
    while ( totBytes > 0 )  {
        avail = RW_BUF_SIZE - rw->rw_count;
        /*  if this buffer is full, get next buffer */
        if ( avail <= 0 ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapwrite: Buffer %d is Full now, rw_count = %d",
            rw->rw_idx, rw->rw_count);
            #endif
            s = LOCK();
            rapMarkBuf(rw, ci, RW_FULL);
            /* wake anyone wanted this buffer full  */
            if ( rw->rw_state & RW_WANTED_FULL ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,"rapwrite: Buffer %d is Wanted_Full",
                                    rw->rw_idx );
                #endif
                rw->rw_state &= ~RW_WANTED_FULL;
                wakeup(rw);
            }
            /*
             *   start DMA if none is going and we filled the
             *   entire buffers.
             */
            if ( (ci->di_state == DI_DMA_IDLE) &&
                 (rw->rw_idx >= RW_MIN_FULL )  ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,"rapwrite: Starting Play Dma");
                #endif
                err = rapStart(DI_DMA_PLAYING);
                if ( err )   {
                    cmn_err (CE_WARN,
                        "rapwrite: Could not start playing error %d",err );
                    UNLOCK(s);
                    return(err);
                }
            }
            /*    get next empty buffer  */
            ci->ri_idx = rapGetNextEmpty(ci->ri_idx, FROM_USR);
            rw = &rwQue[ci->ri_idx];
            ci->ri_ptr = rw->rw_buf;
            UNLOCK(s);
            continue;
        }
        /*  start filling this buffer   */
        size = (totBytes > avail ? avail: totBytes);
        err = uiomove (ci->ri_ptr, size, UIO_WRITE, uio);
        if ( err ) {
            cmn_err (CE_NOTE, "rapwrite: uiomov error %d", err);
            return(err);
        }
        rw->rw_count += size;
        ci->ri_ptr   += size;
        totBytes = uio->uio_resid;
        #ifdef DEBUG
        cmn_err (CE_NOTE,
            "rapwrite: Wrote  %d to Buffer %d, Left = %d, rw_count = %d",
            size, rw->rw_idx, totBytes, rw->rw_count );
        #endif
    }
    return (0);
} /*** end rapwrite ***/
/*************************************************************************
 *       r a p r e a d
 *************************************************************************
 *
 *  Name:      rapread
 *
 *  Purpose:   Reads data from rwQue[] into user's buffer.
 *         This routine waits for current DMA operation to end
 *         and then starts a A/D Dma (recording). If A/D is already
 *         going then it simply moves data from current Full buffer
 *         into user's buffer. If buffer is not full, it waits for
 *         it to get full.
 *
 *  Returns:   0 = Success, or errno.
 *
 *************************************************************************/
int
rapread (dev_t  dev, uio_t *uio, cred_t *cred)
{
    cardInfo_t      *ci;
    rwBuf_t         *rw;
    toid_t          to_id;
    int             avail, size, totBytes, err, s;
    ci = &cardInfo;
    /*===============================*
     *      Error Checking           *
     *===============================*/
    /*  card is not installed */
    if ( !(ci->ci_state & CARD_INSTALLED) )
        return (ENODEV);
    /*  card is not opened  */
    if ( !(ci->ci_state & CARD_OPENED) )
        return (EACCES);
    /*  we do not own the card */
    if ( ci->ci_pid != User_pid )
        return (EACCES);
    /*  card is in middle of a Play operation  */
    if ( ci->ci_state & CARD_PLAYING )
        return (EIO);
    ci->ci_state |= CARD_RECORDING;
    /*   start a A/D Dma if none is going on  */
    if ( ci->di_state == DI_DMA_IDLE ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapread: Idle DMA. Starting one");
        #endif
        if ( rapStart(DI_DMA_RECORDING) ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "rapread: Could not start A/D");
            #endif
            ci->ci_state &= ~CARD_RECORDING;
            UNLOCK(s);
            return (EIO);
        }
    }
    /*
     *  get the buffer we should be using and
     *  wait for it to become Full
     */
    rw = &rwQue[ci->ri_idx];
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapread: %d bytes, buf = %d, rw_count = %d, free = %d, full = %d",
        uio->uio_resid, ci->ri_idx, rw->rw_count, ci->ri_free, ci->ri_full);
    #endif
    s = LOCK();
    if ( !(rw->rw_state & RW_FULL) ) {
        ci->ri_ptr = NULL;
        ci->ri_tout = 0;
        to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0);
        while ( !(rw->rw_state & RW_FULL) && !ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "rapread: wating for buf %d to become Full",
            rw->rw_idx );
            #endif
            rw->rw_state |= RW_WANTED_FULL;
            if ( sleep (rw, PUSER | PCATCH) ) {
                untimeout (to_id);
                #ifdef DEBUG
                cmn_err (CE_NOTE, "rapread: Interrupted");
                #endif
                rw->rw_state &= ~RW_WANTED_FULL;
                UNLOCK(s);
                return(EINTR);
            }
        } /*  while  */
        untimeout (to_id);
        if ( ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "rapread: Timed out");
            #endif
            rw->rw_state &= ~RW_WANTED_FULL;
            UNLOCK(s);
            return (EIO);
        }
    }  /*  if !rw->rw_state & RW_FULL  */
    UNLOCK(s);
    /*  adjust read/write pointer if necessary   */
    if ( ci->ri_ptr == NULL )
        ci->ri_ptr = rw->rw_buf;
    /*===================================*
     *     Actual Read (Data movement)   *
     *===================================*/
    totBytes = uio->uio_resid;
    while ( totBytes > 0 )  {
        avail = rw->rw_count;
        /*  if this buffer is Empty, get next Full buffer */
        if ( avail <= 0 ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
                "rapread: Buffer %d is Empty now, rw_count = %d",
                    rw->rw_idx, rw->rw_count );
            #endif
            s = LOCK();
            rapMarkBuf(rw, ci, RW_EMPTY);
            /* wake anyone wanted this buffer Empty */
            if ( rw->rw_state & RW_WANTED_EMPTY ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,"rapread: Buffer %d is Wanted_Empty",
                        rw->rw_idx );
                #endif
                rw->rw_state &= ~RW_WANTED_FULL;
                wakeup(rw);
            }
            /*    get next Full buffer  */
            ci->ri_idx = rapGetNextFull(ci->ri_idx, FROM_USR);
            rw = &rwQue[ci->ri_idx];
            ci->ri_ptr = rw->rw_buf;
            UNLOCK(s);
            continue;
        }
        /*  start filling this buffer   */
        size = (totBytes > avail ? avail: totBytes);
        err = uiomove (ci->ri_ptr, size, UIO_READ, uio);
        if ( err ) {
            cmn_err (CE_PANIC, "rapread: uiomov error %d", err);
            return(err);
        }
        rw->rw_count -= size;
        ci->ri_ptr   += size;
        totBytes = uio->uio_resid;
        #ifdef DEBUG
        cmn_err (CE_NOTE,
            "rapread: Read %d, Buffer %d, Left = %d, rw_count = %d",
            size, rw->rw_idx, totBytes, rw->rw_count );
        #endif
    }
    return (0);
}  /***   End rapread    ***/
/*************************************************************************
 *          r a p c l o s e
 *************************************************************************
 *  Name:       rapclose
 *  Purpose:    closes connection to the card and makes it available
 *      for next process to open it.
 *  Returns:    0 = Success, or errno
 *************************************************************************/
int
rapclose (dev_t dev, int flag, int otyp, cred_t *cred)
{
    cardInfo_t      *ci;
    ci = &cardInfo;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapclose: ci_state = %x, di_state = %x, full = %d, empty = %d",
        ci->ci_state, ci->di_state, ci->ri_full, ci->ri_free );
    #endif
    /*=========================*
     *  Error Checking     *
     *=========================*/
    /*  card is not installed */
    if ( !(ci->ci_state & CARD_INSTALLED) )
        return (ENODEV);
    /*  card is not opened  */
    if ( !(ci->ci_state & CARD_OPENED) )
        return (EACCES);
    /*  we do not own the card  */
    if ( ci->ci_pid != User_pid )
        return (EACCES);
    return ( rapClose(1) );
}
/*************************************************************************
 *          r a p i n t r
 *************************************************************************
 *  Name:       rapintr
 *  Purpose:    Interrupt handling routine
 *  Returns:    None.
 *************************************************************************/
void
rapintr ( int ctl )
{
    ushort_t        gpis;
    uchar_t     dtci;
    uchar_t     stereo;
    uchar_t     totreq;
    uchar_t     playing;
    uchar_t     moveData;
    cardInfo_t  *ci;
    caddr_t     addr;
    ci = &cardInfo;
    addr = ci->ci_addr[0];
    /*
     *   moveData:  0 = we should move data between Buf/DMA  to DMA/Buf.
     *   totreq:    In stereo, we have to wait for 2 BF or BH interrupt
     *          but in Mono we have to wait for only one.
     *   playing:   1 = Playing, 0= Recording.
     */
    moveData = 0;
    totreq = (ci->ci_state & CARD_STEREO? 2:1); /* No. of Ints. we need */
    playing = ci->ci_state & CARD_PLAYING;
    gpis = INPW(addr+GPIS);
    /*
     *  First, check for stray interrupts and ignore them
     */
    if ( !(ci->ci_state & (CARD_PLAYING | CARD_RECORDING)) ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE,
            "rapintr: Stray interupt, gpis = %x, ci_state = %x",
            ci->ci_state );
        #endif
        return;
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapintr: New ..Gpis = %x", gpis );
    #endif
    /**********************************
     *     DMA Buffers Half/Full      *
     **********************************/
    while ( gpis & GPIS_ITC ) {
        /*   see which buffer is half/full   */
        dtci = INPB(addr+DTCI);
        #ifdef DEBUG
        cmn_err (CE_NOTE,
            "rapintr: Dma buffer status..Gpis = %x, Dtci = %x", gpis, dtci);
        #endif
        if ( dtci & DTCI_BF0 )
            ci->di_bf++;
        if ( dtci & DTCI_BF1 )
            ci->di_bf++;
        if (dtci & DTCI_BH0 )
            ci->di_bh++;
        if (dtci & DTCI_BH1 )
            ci->di_bh++;
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapintr: di_bf = %d, di_bh = %d",
            ci->di_bf, ci->di_bh );
        #endif
        /*  1st half of dma needs service  */
        if ( ci->di_bh == totreq ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
                "rapintr: DMA First_Half needs service");
            #endif
            ci->di_bh    = 0;
            ci->di_which = 0;  /* 1st half of DMA buffer */
            moveData     = 1;
        }
        /*   2nd half of dma needs service  */
        else if ( ci->di_bf == totreq ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapintr: DMA Second_Half needs service");
            #endif
            ci->di_bf    = 0;
            ci->di_which = 1;   /*  2nd half of DMA buffer */
            moveData     = 1;
        }
        /*
         *   Move data if needed
         */
        if ( moveData ) {
            /*  move data for Play if only data available */
            if ( playing ) {
                /*  No more data..end of play  */
                if ( ci->ri_full <= 0 ) {
                    if (ci->di_state & DI_DMA_END_PLAY ) {
                        #ifdef DEBUG
                        cmn_err (CE_NOTE,"rapintr: End of Play Reached");
                        #endif
                        if ( ci->ri_state & RI_WANTED_EMPTY ) {
                            #ifdef DEBUG
                            cmn_err (CE_NOTE,
                            "rapintr: Cir.Buff is Wanted Empty");
                            #endif
                            ci->ri_state &= ~RI_WANTED_EMPTY;
                            wakeup (ci);
                        }
                        else rapStop(DI_DMA_PLAYING);
                            return;
                    } else {
                        #ifdef DEBUG
                        cmn_err (CE_NOTE,
                            "rapintr: Playing but no Full buffers");
                        #endif
                        return;
                    }
                }
                /*  Data is available to play  */
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                "rapintr: Playing..which = %d, idx = %d, full = %d, Empty = %d",
                ci->di_which, ci->di_idx, ci->ri_full, ci->ri_free);
                #endif
                rapBufToDma(DMA_HALF_SIZE);
            } /* if playing  */
            else  {   /*  recording  */
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                    "rapintr: Recording..which = %d, full = %d, Empty = %d",
                        ci->di_which, ci->ri_full, ci->ri_free);
                #endif
                rapDmaToBuf(DMA_HALF_SIZE);
            }
        }  /*  if move data  */
        else { /*   no need to move data  */
            #ifdef DEBUG
            cmn_err (CE_NOTE,
                "rapintr: Waiting for next interrupt, bf = %d, bh = %d",
                    ci->di_bf, ci->di_bh);
            #endif
        }
        gpis = INPW(addr+GPIS);
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapintr: next Gpis = %x", gpis);
        #endif
    } /* while ( gpis & ..  */
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapintr: finished ...");
    #endif
}  /*** End rapintr   ***/
/*************************************************************************
 *          r a p i o c t l
 *************************************************************************
 *  Name:       rapioctl
 *  Purpose:    handles IOCTL calls for RAP-10.
 *  Returns:    0 = Success, or errno
 *************************************************************************/
int
rapioctl (dev_t dev, int cmd, void *arg, int mode, cred_t *cred, int *ret)
{
    cardInfo_t      *ci;
    ci = &cardInfo;
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapioctl: Cmd = %d, full = %d, Empty = %d",
        cmd, ci->ri_full, ci->ri_free );
    #endif
    /*
     *  No card is installed or card is already opened
     */
    if ( !(ci->ci_state & CARD_INSTALLED) )
        return (ENODEV);
    if ( !(ci->ci_state & CARD_OPENED) )
        return (EACCES);
    if ( ci->ci_pid != User_pid )
        return (EACCES);
    *ret = 0;
    switch ( cmd ) {
        case RAPIOCTL_END_PLAY:
        /*=======================*
         *   End PLAY            *
         *=======================*/
            if ( !(ci->ci_state & CARD_PLAYING) ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                    "rapioctl: End_PLay command in wrong state");
                #endif
                return (EACCES);
            }
            return (rapClose (0) );
        case RAPIOCTL_END_RECORD:
        /*=======================*
         *   End RECORD          *
         *=======================*/
            if ( !(ci->ci_state & CARD_RECORDING) ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                    "rapioctl: End_Recrd command in wrong state");
                #endif
                return (EACCES);
            }
            return (rapClose (0) );
    } /* switch  */
    return (0);
} /** End rapioctl  **/
/****************************************************************************
 ******              I n t e r n a l   R o u t i n e s                *******
 ****************************************************************************/
/*************************************************************************
 *                      r a p C l o s e
 *************************************************************************
 *  Name:       rapClose
 *  Purpose:    Routine to actually ends current operation and releases
 *              the card. It is written as a separate routine here so
 *              it can be shared by rapclose() and rapioctl() routines.
 *              One frees up the card, one does not. Also if we are called
 *              from ioctl, we will wait till all buffers are played (if
 *              in Playback mode).
 *  Returns:    0 = Success, or errno
 *************************************************************************/
int
rapClose( uchar_t relCard )
{
    cardInfo_t  *ci;
    rwBuf_t     *rw;
    int         s, totLeft;
    ci = &cardInfo;
    s = LOCK();
    rw = &rwQue[ci->ri_idx];
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapClose: relCard = %d, ci_state = %x, di_state = %x",
        relCard, ci->ci_state, ci->di_state );
    #endif
    /*
     *  if we are not recording and are not playing
     *  then simply mark the card as not opened and return
     */
    if ( !(ci->ci_state & (CARD_RECORDING | CARD_PLAYING)) ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapClose: Idle card ..closing");
        #endif
        if ( relCard ) {
            ci->ci_state &= ~CARD_OPENED;
            ci->ci_pid    = 0;
        }
        UNLOCK(s);
        return(0);
    }
    /*
     *  Recording ? end it.
     */
    if ( ci->ci_state & CARD_RECORDING ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE,"rapClose: Ending Record (A/D)");
        #endif
        rapStop(DI_DMA_RECORDING);
        if ( relCard ) {
            ci->ci_state &= ~CARD_OPENED;
            ci->ci_pid    = 0;
        }
        UNLOCK(s);
        return(0);
    }
    /*
     *  playback and called from close() routine ?
     *  End the playback
     */
    if ( relCard ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE,
        "rapClose: Ending Playback (D/A");
        #endif
        rapStop(DI_DMA_PLAYING);
        ci->ci_state &= ~CARD_OPENED;
        ci->ci_pid    = 0;
        UNLOCK(s);
        return(0);
    }
    /*
     *  Called from Ioctl.
     *  Closing in middle of play is different based on we
     *  have been called from close() routine or not.
     *  If called from Ioctl (relCard = 0), we will wait till
     *  all buffers are played back.
     */
    if ( !(rw->rw_state & RW_FULL) && (rw->rw_count > 0) ) {
        totLeft = RW_BUF_SIZE - rw->rw_count;
        #ifdef DEBUG
        cmn_err (CE_NOTE,
            "rapClose: Current Buf %d has %d data. Filled with %d zeros",
            rw->rw_idx, rw->rw_count, totLeft );
        #endif
        if ( totLeft > 0 ) {
            bzero (ci->ri_ptr, totLeft);
            ci->ri_ptr += totLeft;
        }
        rapMarkBuf(rw, ci, RW_FULL);
    }
    /*   some buffers to play  */
    if ( ci->ri_full > 0 )  {
        /*   Playback has not started yet */
        if ( ci->di_state == DI_DMA_IDLE ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapClose: Starting playback, full = %d, empty = %d",
            ci->ri_full, ci->ri_free);
            #endif
            rapStart(DI_DMA_PLAYING);
        }
        ci->di_state  = DI_DMA_IDLE;
        ci->di_state |= DI_DMA_END_PLAY;
        /*  wait till buffers are all played back */
        while ( ci->ri_full > 0 ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
                "rapClose: waiting for Play to end..full = %d, empty = %d, ri_idx = %d, di_idx = %d",
                ci->ri_full, ci->ri_free, ci->ri_idx, ci->di_idx);
            #endif
            ci->ri_state |= RI_WANTED_EMPTY;
            if ( sleep (ci, PUSER | PCATCH) ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE, "rapClose: Interrupted");
                #endif
                rapStop(DI_DMA_PLAYING);
                ci->ci_state &= ~CARD_OPENED;
                ci->ci_pid    = 0;
                UNLOCK(s);
                return (EINTR);
            }
        }
        rapStop(DI_DMA_PLAYING);
    }
    else {
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapClose: Circular buffer empty..closing");
        #endif
        rapStop(DI_DMA_PLAYING);
    }
    UNLOCK(s);
    return(0);
}  /***    End rapClose   ***/
/*************************************************************************
 *                    r a p S t o p
 *************************************************************************
 *  Name:       rapStop
 *  Purpose:    Stops D/A and A/D conversion.
 *  Returns:    None.
 *************************************************************************/
static void
rapStop( uchar_t what )
{
    cardInfo_t  *ci;
    rwBuf_t     *rw;
    caddr_t     addr;
    uchar_t     dacm, adcm;
    ushort_t    gpdi;
    int         s, i;
    s = LOCK();
    ci = &cardInfo;
    addr = ci->ci_addr[0];
    gpdi = adcm = dacm = 0;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapStop:  Stoping %s, full = %d, Empty = %d",
        (what == DI_DMA_PLAYING ? "Playback(D/A)":"Record(A/D)"),
        ci->ri_full, ci->ri_free);
    #endif
    switch ( what ) {
        /*  stop D/A Conversion (Playing)  */
        case DI_DMA_PLAYING:
            ci->di_which = 0;
            rapZeroDma(ci, DMA_BUF_SIZE);
            OUTB(addr+DACM, dacm);
            rapNoteOff (ci);
            break;
        /*  stop A/D Conversion (recording) */
        case DI_DMA_RECORDING:
            OUTB(addr+ADCM, adcm);
            OUTB(addr+DACM, dacm);
            break;
    }
    OUTW(addr+GPDI, gpdi);
    rapReleaseDma(ci);
    /*   Initialize Card Info structure  */
    ci->ci_state &= ~(CARD_PLAYING | CARD_RECORDING);
    ci->ri_idx   = 0;
    ci->di_idx   = 0;
    ci->ri_state = 0;
    ci->di_state = 0;
    ci->di_ptr   = rwQue[0].rw_buf;
    ci->ri_ptr   = rwQue[0].rw_buf;
    ci->ri_free  = RW_BUF_COUNT;
    ci->ri_full  = 0;
    /*   Initialize Circular Buffers   */
    for ( i = 0; i < RW_BUF_COUNT; i++ ) {
        rw = &rwQue[i];
        rw->rw_count = 0;
        rw->rw_state = 0;
        rw->rw_idx   = i;
        bzero (rw->rw_buf, RW_BUF_SIZE);
    }

    /*   clear out any hanging GPIS and DACM   */
    gpdi = INPW(addr+GPIS);
    UNLOCK(s);
} /** End rapStop  **/
/*************************************************************************
 *              r a p S t a r t
 *************************************************************************
 *  Name:       rapStart
 *  Purpose:    Prepares Eisa DMA buffers/Control block for Playing/Recording
 *              This function is called when DMA is Idle.
 *  Returns:    0 = Success or Error number.
 *************************************************************************/
static int
rapStart (uchar_t  what)
{
    cardInfo_t  *ci;
    dmaBuf_t    *dmaB;
    dmaCb_t     *dmaC;
    uchar_t     stereo;
    int         err;
    ci = &cardInfo;
    stereo = (ci->ci_state & CARD_STEREO);
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapStart: Starting %s, full = %d, empty = %d",
        (what == DI_DMA_PLAYING ? "Playback(D/A)":"Record(A/D)"),
        ci->ri_full,  ci->ri_free );
    #endif
    /*    clear Dma buffers   */
    ci->di_which = 0;
    rapZeroDma(ci, DMA_BUF_SIZE);
    /*   check for Dma buffer addresses  */
    if ( (ci->ci_dmaBuf5 == (dmaBuf_t *)0) ||
         (ci->ci_dmaCb5  == (dmaCb_t  *)0) ) {
        cmn_err (CE_WARN,
            "rapStart: Chan 5 dmaBuf/dmaCb is NULL, what = %d", what);
        return(EIO);
    }
    if ( (ci->ci_dmaBuf6 == (dmaBuf_t *)0) ||
         (ci->ci_dmaCb6  == (dmaCb_t  *)0) ) {
        cmn_err (CE_WARN,
            "rapStart: Chan 6 dmaBuf/dmaCb is NULL, what = %d", what);
        return(EIO);
    }
    /*
     *  Prepare Eisa Buf and Cb for Channel 5. If in
     *  stereo mode, do the same for Channel 6.
     */
    dmaB = ci->ci_dmaBuf5;
    dmaC = ci->ci_dmaCb5;
    rapPrepEisa (dmaB, dmaC, what, dmaLeftPhys );
    if ( stereo ) {
        dmaB = ci->ci_dmaBuf6;
        dmaC = ci->ci_dmaCb6;
        rapPrepEisa (dmaB, dmaC, what, dmaRightPhys );
    }
    /*
     *   Program Eisa DMA Channels
     */
    err = eisa_dma_prog (ci->ci_adap, ci->ci_dmaCb5, ci->ci_dmaCh5,
                           EISA_DMA_NOSLEEP);
    if ( err == 0 ) {
        cmn_err (CE_WARN, "rapStart: DMA Channel %d is busy",
                ci->ci_dmaCh5 );
        return (EBUSY);
    }
    if ( stereo ) {
        err = eisa_dma_prog (ci->ci_adap, ci->ci_dmaCb6, ci->ci_dmaCh6,
            EISA_DMA_NOSLEEP);
        if ( err == 0 ) {
            cmn_err (CE_WARN,
                "rapStart: DMA Channel %d is busy",
                ci->ci_dmaCh6 );
            return (EBUSY);
        }
    }
    /*   enable hardware recognition on Eisa Dma Channels */
    eisa_dma_enable (ci->ci_adap, ci->ci_dmaCb5, ci->ci_dmaCh5,
                EISA_DMA_NOSLEEP);
    if ( stereo ) {
        eisa_dma_enable (ci->ci_adap, ci->ci_dmaCb6, ci->ci_dmaCh6,
                EISA_DMA_NOSLEEP);
    }
    /*  set Eisa DMA register for Autoinit mode  */
    rapSetAutoInit(ci, what);
    ci->di_state |= what;
    /*  let's do it ! */
    if ( what == DI_DMA_PLAYING ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapStart: Starting DMA for D/A Play");
        #endif
        rapStartDA();
    }
    else {
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapStart: Starting DMA for A/D Record");
        #endif
        rapStartAD();
    }
    return(0);
} /**  End rapStart  **/
/************************************************************************
 *            r a p P r e p E i s a     
 *************************************************************************
 *  Name:        rapPrepEisa
 *  Purpose:     prepares EISA Buf and Cb structures.
 *  Returns:     None.
 *************************************************************************/
static void
rapPrepEisa( dmaBuf_t *dmaB, dmaCb_t *dmaC, uchar_t  what, paddr_t addr)
{
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapPrepEisa: Preparing Eisa DMA buffers for %s",
        (what == DI_DMA_PLAYING ? "Playback(D/A)" : "Record(A/D)" ) );
    #endif
    /*   prepare Eisa DMA Buf struct   */
    bzero (dmaB, sizeof(dmaBuf_t) );
    dmaB->count = DMA_BUF_SIZE;
    dmaB->address = addr;
    /*  prepare Eisa DMA Control Block  */
    bzero (dmaC, sizeof(dmaCb_t) );
    dmaC->reqrbufs   = dmaB;
    dmaC->reqr_path  = EISA_DMA_PATH_16;
    dmaC->trans_type = EISA_DMA_TRANS_DMND;
    dmaC->targ_step  = EISA_DMA_STEP_INC;
    dmaC->bufprocess = EISA_DMA_BUF_SNGL;
    if ( what == DI_DMA_PLAYING )
        dmaC->cb_cmd = EISA_DMA_CMD_READ;   /*  mem -> rap10     */
    else
        dmaC->cb_cmd = EISA_DMA_CMD_WRITE;  /*  rap10 -> mem     */
} /*** End rapPrepEisa  ***/
/*************************************************************************
 *             r a p S t a r t D A
 *************************************************************************
 *  Name:         rapStartDA
 *  Purpose:      Enables appropriate RAP interrupts and starts D/A Dma.
 *  Returns:     None
 *************************************************************************/
static void
rapStartDA()
{
    cardInfo_t  *ci;
    caddr_t     addr;
    ushort_t    gpdi, gpis, gpst, dtcd, mask;
    uchar_t     gpcm, pwmd, adcm, dacm;
    uchar_t     stereo;
    int         s;
     ci = &cardInfo;
    addr = ci->ci_addr[0];
    stereo = ci->ci_state & CARD_STEREO;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapStartDA: Starting D/A Dma, full = %d, empty = %d",
        ci->ri_full, ci->ri_free );
    #endif
    /*
     *  Prepare the board for Record (A/D)
     *  Here is what we will do (in exact order):
     *
     *   GPDI:  Stereo = 0xA800, Mono = 0x9800
     *    itc = 1, dma transfer match count
     *    Stereo:   Drq1->Dma5, Drq0->Dma6
     *    Mono:     Drq1->Dma5
     *      Dt1, Dt0 = 10, Chan 1 ->Drq1, Chan 0 ->Drq0
     *    Left Chan->Drq1, Right Chan->Drq0
     *
     *   DACM:  Stereo: BF, Mono: BE
     *    scc = 1, Dma size in byte
     *    ts1 = ts2 = 1, transfer size of 4096 bytes
     *      dl1 = dl0 = 1; Data length of 16 bits for both Channels.
     *    Stereo ? ds1 = ds0 = 1  Start D/A on both Channels.
     *      Mono   ? ds1 = 1        Start D/A on Channel 1
     *
     *   GPCM:   Select Mike level = 0x04
     *         Aux  level = 0x08
     *   PWMD:   0xFF (Max level)
     */
    gpdi = (stereo ? 0xA800: 0x9800);
    dacm = (stereo ? 0xBF:0xBE);
    gpcm = 0x04;
    pwmd = 0xFF;
    mask = (stereo ? (GPIS_DN1|GPIS_DN0): GPIS_DN1);
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapStartDA: gpdi = %x, dacm = %x", gpdi, dacm);
    #endif
    /*  Set Rap-10 card   */
    OUTB(addr+GPCM, gpcm);
    OUTB(addr+PWMD, pwmd);
    OUTW(addr+GPDI, gpdi);
    OUTB(addr+DACM, dacm);
    /*
     *  Busy-wait for both Note_On interrupts
     *  The interrupt version is commenetd out for now.
     */
    gpis = INPW(addr+GPIS);
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapStartDA: Waiting for Note_On, gpis = %x, mask = %x",
         gpis, mask);
    #endif
    while ( !(gpis & mask) ) {
        gpis = INPW(addr+GPIS);
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapStartDA: Waiting ..new gpis = %x", gpis);
        #endif
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapStartDA:  Note_On Interrupt Received, gpis = %x",
    gpis );
    #endif
    rapNoteOn(ci, gpis);
}  /***   End rapStartDA   ***/
/*************************************************************************
 *                      r a p S t a r t A D
 *************************************************************************
 *  Name:         rapStartAD
 *  Purpose:      Enables appropriate RAP interrupts and starts A/D Dma.
 *  Returns:     None
 *************************************************************************/
static void
rapStartAD()
{
    cardInfo_t  *ci;
    caddr_t     addr;
    ushort_t    gpdi;
    uchar_t     gpcm, pwmd, adcm, dacm;
    uchar_t     stereo, mic;
    ci = &cardInfo;
    addr = ci->ci_addr[0];
    stereo = ci->ci_state & CARD_STEREO;
#ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapStartAD: Starting A/D Dma in %s, full = %d, empty = %d",
        (stereo ? "Stereo":"Mono"), ci->ri_full, ci->ri_free );
#endif
    /*
     *  Prepare the board for Record (A/D)
     *  Here is what we will do (in exact order):
     *
     *   GPDI:  Stereo = 0xA400, Mono = 0x9400
     *    itc = 1, dma transfer match count
     *    Stereo:   Drq1->Dma5, Drq0->Dma6
     *    Mono:     Drq1->Dma5
     *      Dt1, Dt0 = 01, Left Chan->Drq1, Right Chan->Drq0
     *
     *   DACM:  0xB0
     *    scc = 1, Dma size in byte
     *    ts1 = ts2 = 1, transfer size of 4096 bytes
     *
     *   GPCM:   Select Mic level = 0x04
     *        Aux level = 0x08
     *   PWMD:   0xFF (Max level)
     *
     *   ADCM:  Stereo: Mic 0x6F, line 0x4F,
     *    Mono:   Mic 0x6D, line 0x4D
     *      Mon = 1, Monitor ON
     *      Gin = 1, Head Amp Gain to Mic.
     *      Af1, Af0 = 01, 22.05 KHz
     *      Adl = 1, 16 bit data length
     *      Stereo,  Adm = 1, else = 0
     *      Ads = 1, Start A/D
     */
    gpdi = (stereo ? 0xA400: 0x9400);
    gpcm = 0x08;
    adcm = (stereo ? 0x6F:0x6D);
    dacm = 0xB0;
    gpcm = 0x04;
    pwmd = 0xFF;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapStartAD: Rap init as: gpdi = %x, dacm = %x, gpcm = %x, adcm = %x",
    gpdi, dacm, gpcm, adcm);
    #endif
    OUTW(addr+GPDI, gpdi);
    OUTB(addr+DACM, dacm);
    OUTB(addr+GPCM, gpcm);
    OUTB(addr+PWMD, pwmd);
    OUTB(addr+ADCM, adcm);
}  /***  End rapStartAD  ***/
/*************************************************************************
 *          r a p B u f T o D m a     
 *************************************************************************
 *  Name:       rapBufToDma
 *  Purpose:    moves data from current rwQue[] entry to DMA buffers.
 *              This routine is called by INterrupt handler only except
 *              once before we startd D/A (when no DMA is programmed yet)
 *  Returns:    None
 *************************************************************************/
static void
rapBufToDma( int  bytes)
{
    cardInfo_t    *ci;
    rwBuf_t       *rw;
    uchar_t       *dmaR;
    uchar_t       *dmaL;
    uchar_t       stereo;
    int           i, j, s;
    ci = &cardInfo;
    rw = &rwQue[ci->di_idx];
    stereo = ci->ci_state & CARD_STEREO;
    /*
     *      filling 1st half or 2nd half of the buffers ?
     */
    if ( ci->di_which ) {
        dmaR = &dmaRight[DMA_HALF_SIZE];
        dmaL = &dmaLeft[DMA_HALF_SIZE];
        if ( bytes == DMA_BUF_SIZE ) {
            bytes = DMA_HALF_SIZE;
        }
    }
    /*  filling 1st half of dma buffers */
    else {
        dmaR = &dmaRight[0];
        dmaL = &dmaLeft[0];
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapBufToDma: Bytes = %d, which = %d, Idx = %d, rw_count = %d, Full = %d, Empty = %d",
    bytes, ci->di_which, ci->di_idx, rw->rw_count, ci->ri_full,
    ci->ri_free);
    #endif
    /*
     *   if buffer is not Full, we zero out dma buffers and
     *   return. We cannot wait till it gets Full.
     */
    if ( !(rw->rw_state & RW_FULL) ) {
        rapZeroDma(ci, bytes);
        ci->di_ptr = NULL;
        #ifdef DEBUG
        cmn_err (CE_NOTE,
            "rapBufToDma: Buf %d is not Full, rw_state = %x",
        rw->rw_idx, rw->rw_state );
        #endif
        return;
    }
    /*  buffer is full of data ..readjust the buffer pointer */
    if ( ci->di_ptr == NULL )
        ci->di_ptr = rw->rw_buf;
    /*
     *  Fill buffers ...
     */
    for ( i = 0; i < bytes; i++ ) {
        /*
         *  First check if buffer is empty. If it is, mark it
         *  as empty, wake anyone up who wants it and get the
         *  next full buffer.
         */
        if ( rw->rw_count <= 0 ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapBufToDma: Buf %d is Empty now, rw_count = %d",
             rw->rw_idx, rw->rw_count );
            #endif
            rapMarkBuf(rw, ci, RW_EMPTY);
            ci->di_ptr = NULL;
            if ( rw->rw_state & RW_WANTED_EMPTY ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                "rapBufToDma: Buf %d is Wanted_Empty",
                rw->rw_idx );
                #endif
                rw->rw_state &= ~RW_WANTED_EMPTY;
                wakeup(rw);
            }
            j = rapGetNextFull (ci->di_idx, FROM_INTR);
            if ( j == -1 ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                "rapBufToDma: Could not get next Full buffer");
                #endif
                break;
            }
            ci->di_idx = j;
            rw = &rwQue[ci->di_idx];
            ci->di_ptr = rw->rw_buf;
            continue;
        }
        /*  buffer still has some data ..move them  */
        if ( stereo ) {
            dmaL[i] = *(ci->di_ptr++);
            dmaR[i] = *(ci->di_ptr++);
            rw->rw_count -= 2;
        }
        else {
            dmaL[i] = *(ci->di_ptr++);
            rw->rw_count--;
        }
    } /* for .. */
    /*  Flush the cache line so that Dma buffers contain all data */
    dki_dcache_wbinval (dmaL, (unsigned)bytes);
    if ( stereo )
        dki_dcache_wbinval (dmaR, (unsigned)bytes);
}  /*** end rapBufToDma  ***/
/*************************************************************************
 *                      r a p D m a T o B u f
 *************************************************************************
 *  Name:       rapDmaToBuf
 *  Purpose:    Moves data from DMA buffers (Right and Left in stereo)
 *              into a rwQue entry.  This routine is called only by
 *              Interrupt Handler.
 *  Returns:    None
 *************************************************************************/
static void
rapDmaToBuf( int  bytes)
{
    cardInfo_t    *ci;
    rwBuf_t       *rw;
    uchar_t       *dmaR;
    uchar_t       *dmaL;
    uchar_t       stereo;
    int    i, j, s, inc;
    ci = &cardInfo;
    rw = &rwQue[ci->di_idx];
    stereo = ci->ci_state & CARD_STEREO;
    /*
     *      filling 1st half or 2nd half of the buffers ?
     */
    if ( ci->di_which ) {
        dmaR = &dmaRight[DMA_HALF_SIZE];
        dmaL = &dmaLeft[DMA_HALF_SIZE];
        if ( bytes == DMA_BUF_SIZE ) {
            bytes = DMA_HALF_SIZE;
        }
    }
    /*  filling 1st half of dma buffers */
    else {
        dmaR = &dmaRight[0];
        dmaL = &dmaLeft[0];
    }
    /*   Invalidate the Cache    */
    dki_dcache_inval (dmaL, (unsigned)bytes);
    if ( stereo )
        dki_dcache_inval (dmaR, (unsigned)bytes);
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapDmaToBuf: Bytes= %d, Idx = %d, rw_count = %d, Full = %d, Empty= %d",
    bytes, ci->di_idx, rw->rw_count, ci->ri_full, ci->ri_free);
    #endif
    /*
     *  if buffer is Full ..we cannot wait ! Ignore new data
     *  by simply returning.
     */
    if ( rw->rw_state & RW_FULL ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE,
        "rapDmaToBuf: Buf %d is not Empty ..Ignoring data",
        rw->rw_idx );
        #endif
        return;
    }
    /*  buffer is Empty ..calculate the end address */
    if ( ci->di_ptr == NULL )
        ci->di_ptr = rw->rw_buf;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapDmaToBuf: Moving %s of DMA buffers in %s, rw_count = %x",
    (ci->di_which ? "Second Half" : "First Half"),
    (stereo ? "Stereo":"Monoe"), rw->rw_count);
    #endif
    /*
     *      Fill buffers ...in stereo bytes are Left:Right:Left:Right...
     */
    for ( i = 0; i < bytes; i++ ) {
        /*
         *  First check if this buffer is Full or not.
         *  If it is, mark it as Full and wake anyone up who is
         *  waiting for it. Then get the next Empty buffer.
         */
        if ( rw->rw_count >= RW_BUF_SIZE ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapDmaToBuf: Buf %d is Full now, rw_count = %d",
            rw->rw_idx, rw->rw_count );
            #endif
            rapMarkBuf(rw, ci, RW_FULL);
            if ( rw->rw_state & RW_WANTED_FULL ) {
                #ifdef DEBUG
                cmn_err (CE_NOTE,
                "rapDmaToBuf: Buf %d is Wanted_Full",
                rw->rw_idx );
                #endif
                rw->rw_state &= ~RW_WANTED_FULL;
                wakeup(rw);
            }
            j = rapGetNextEmpty(ci->di_idx, FROM_INTR);
            if ( j == -1 ) {
                cmn_err (CE_NOTE,
                      "rapDmaToBuf: Could not get next empty");
                return;
            }
            ci->di_idx = j;
            rw = &rwQue[ci->di_idx];
            ci->di_ptr = rw->rw_buf;
            continue;
        }
        /*  buffer still has room ...move data  */
        if ( stereo ) {
            *(ci->di_ptr++) = dmaL[i];
            *(ci->di_ptr++) = dmaR[i];
            rw->rw_count     += 2;
        }
        else {
            *(ci->di_ptr++) = dmaL[i];
            rw->rw_count++;
        }
    } /* while bytes ... */
}  /*** end rapDmaToBuf  ***/
/*************************************************************************
 *                      r a p G e t N e x t F u l l
 *************************************************************************
 *  Name:        rapGetNextFull
 *  Purpose:     returns the index of next Full  entry in rwQue[],
 *               starting from a given index. Sleeps if the entry
 *               is not Full.
 *  Returns:     the index of the empty entry.
 *************************************************************************/
static short
rapGetNextFull (short idx, uchar_t fromIntr)
{
    cardInfo_t   *ci;
    int          s;
    toid_t       to_id;
    rwBuf_t      *rw;
    ci = &cardInfo;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapGetNextFull: Getting Next Full Buffer..idx = %d, fromIntr: %d",
    idx, fromIntr );
    #endif
    /*  go to beginning if at the end of the queu  */
    idx++;
    if ( idx >= RW_BUF_COUNT )
        idx = 0;
    rw = &rwQue[idx];
    /*
     *    if buffer is not available and we were called from Intrupt
     *    handler, simply ignore the request and return error
     */
    s = LOCK();
    if ( !(rw->rw_state & RW_FULL) && (fromIntr) ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE,
        "rapGetNextFull: Buffer %d is not Full. ..Cannot Wait",
        rw->rw_idx);
        #endif
        UNLOCK(s);
        return(-1);
    }
    /*   wait for the buffer to become Full  */
    if ( !(rw->rw_state & RW_FULL) ) {
        ci->ri_tout = 0;
        to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0);
        while ( !(rw->rw_state & RW_FULL) && !ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapGetNextFull:  Waiting for Buf %d to become Full",
            rw->rw_idx );
            #endif
            rw->rw_state |= RW_WANTED_FULL;
            if ( sleep(rw, PUSER | PCATCH) ) {
                untimeout(to_id);
                #ifdef DEBUG
                cmn_err (CE_NOTE, "rapGetNextFull: Interrupted");
                #endif
                rw->rw_state &= ~RW_WANTED_FULL;
                UNLOCK(s);
                return(-1);
            }
        }
        untimeout (to_id);
        if ( ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "raGetNextFull: Timed out");
            #endif
            rw->rw_state &= ~RW_WANTED_FULL;
            UNLOCK(s);
            return (-1);
        }
    }  /*  if !(rw->rw_state & RW_FULL)  */
    UNLOCK(s);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapGetNextFull: next Full Buffer is %d", idx);
    #endif
    return(idx);
}  /***   End rapGetNextFull   ***/
/*************************************************************************
 *          r a p G e t N e x t E m p t y
 *************************************************************************
 *  Name:        rapGetNextEmpty
 *
 *  Purpose:     returns the index of next empty entry in rwQue[],
 *               starting from a given index. Sleeps if the entry
 *               is not empty.
 *  Returns:     the index of the empty entry.
 *************************************************************************/
static short
rapGetNextEmpty (short idx, uchar_t fromIntr)
{
    cardInfo_t  *ci;
    int         s;
    toid_t      to_id;
    rwBuf_t     *rw;
    ci = &cardInfo;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapGetNextEmpty: Getting Next Empty Buffer..idx = %d, fromIntr: %d",
        idx, fromIntr );
    #endif
    /*  go to beginning if at the end of the queu  */
    idx++;
    if ( idx >= RW_BUF_COUNT )
        idx = 0;
    rw = &rwQue[idx];
    s = LOCK();
    /*
     *    if buffer is nit available and we were called from Intrupt
     *    handler, simply ignore the request and return error
     */
    if ( (rw->rw_state & RW_FULL) && (fromIntr) ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE,
        "rapGetNextEmpty: Buffer %d is not Empty ..Cannot Wait",
        rw->rw_idx);
        #endif
        UNLOCK(s);
        return(-1);
    }
    /*   wait for the buffer to become Empty   */
    if ( rw->rw_state & RW_FULL ) {
        ci->ri_tout = 0;
        to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0);
        while ( (rw->rw_state &  RW_FULL) && !ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapGetNextEmpty: Waiting for Buf %d to become Empty",
            rw->rw_idx );
            #endif
            rw->rw_state |= RW_WANTED_EMPTY;
            if ( sleep(rw, PUSER | PCATCH) ) {
                untimeout(to_id);
                #ifdef DEBUG
                cmn_err (CE_NOTE, "rapGetNextEmpty: Interrupted");
                #endif
                rw->rw_state &= ~RW_WANTED_EMPTY;
                UNLOCK(s);
                return(-1);
            }
        }  /*  while .. */
        untimeout (to_id);
        if ( ci->ri_tout ) {
            #ifdef DEBUG
            cmn_err (CE_NOTE, "raGetNextEmpty: Timed out");
            #endif
            rw->rw_state &= ~RW_WANTED_EMPTY;
            UNLOCK(s);
            return (-1);
        }
    }  /*  if (rw->rw_state & RW_FULL) */
    UNLOCK(s);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapGetNextEmpty: next Empty Buffer is %d", idx);
    #endif
    return(idx);
}  /***    End rapGetNextEmpty   ***/
/*************************************************************************
 *          r a p D i s I n t
 *************************************************************************
 *  Name:        rapDisInt
 *  Purpose:     Disables RAP-10 interrupts.
 *  Returns:     None.
 *************************************************************************/
static void
rapDisInt( cardInfo_t   *ci)
{
    caddr_t     addr;
    ushort_t    s;
    uchar_t     c;
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapDisInt: full = %d, empty = %d, di_state = %d",
    ci->ri_full, ci->ri_free, ci->di_state );
    #endif
    addr = ci->ci_addr[0];
    /*   disable all Interrupts   */
    s = 0;
    OUTW(addr+GPDI, s);
    OUTB(addr+DACM, 0x00);
    OUTB(addr+ADCM, 0x00);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapDisInt: Rap is set");
    #endif
} /*** End rapDisInt  ***/
/**************************************************************************
 *                         r a p G e t D m a                              *
 **************************************************************************
 *     Name:       rapGetDma
 *     Purpose:    allocates dma Buf and Cb structures
 *     Returns:    0 = Success,  1 = Error
 **************************************************************************/
static int
rapGetDma ( dmaBuf_t  **dmaB,  dmaCb_t  **dmaC, int ch )
{
    #ifdef DEBUG
    cmn_err (CE_NOTE,
    "rapGetDma: Getting Eisa Dma Buf and Cb for Channel %d", ch);
    #endif
    *dmaB = eisa_dma_get_buf (EISA_DMA_SLEEP);
    if ( *dmaB == NULL )
        return (1);
    *dmaC = eisa_dma_get_cb ( EISA_DMA_SLEEP );
    if ( *dmaC == NULL )
        return (1);
    return (0);
} /***  End rapGetDma   ***/
/*************************************************************************
 *          r a p M a r k B u f
 *************************************************************************
 *  Name:       rapMarkBuf
 *  Purpose:    Marks a buffer (Empty, Busy, Full) and increments/decrements
 *              appropriate counters. Buffers status changed as:
 *              Empty -> Busy -> Full -> Empty -> Busy ..
 *  Returns:    None.
 *************************************************************************/
static void
rapMarkBuf (rwBuf_t  *rw, cardInfo_t  *ci, uchar_t  m)
{
    int s;
    s = LOCK();
    switch ( m ) {
        case RW_EMPTY:
            rw->rw_state &= ~RW_FULL;
            if ( ci->ri_full )
                ci->ri_full--;
            ci->ri_free++;
            rw->rw_count = 0;
            #ifdef DEBUG
            cmn_err (CE_NOTE,
            "rapMarkBuf: Buf %d set EMPTY. Full = %d, Emp = %d",
            rw->rw_idx, ci->ri_full, ci->ri_free );
            #endif
            break;
        case RW_FULL:
            rw->rw_state |= RW_FULL;
            ci->ri_full++;
            if ( ci->ri_free )
               ci->ri_free--;
            rw->rw_count = RW_BUF_SIZE;
            #ifdef DEBUG
            cmn_err (CE_NOTE,
                "rapMarkBuf: Buf %d set FULL. Full = %d, Emp = %d",
                rw->rw_idx, ci->ri_full, ci->ri_free );
            #endif
            break;
    }
    UNLOCK(s);
}  /***   End rapMarkBuf   ***/
/*************************************************************************
 *          r a p K e r n M e m
 *************************************************************************
 *  Name:       rapKernMem
 *  Purpose:    Allocates/Disallocates Kernel memory for Right and
 *              Left DMA channels.
 *  Returns:    0 = Success,  1 = Failure.
 *************************************************************************/
static int
rapKernMem ( uchar_t what)
{
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapKernMem: %s Kernel Contigious Memory",
         (what == 1 ? "Allocating" : "Deallocating") );
    #endif
    switch ( what ) {
        /*=======================================*
         *     Allocate Right/Left DMA Channels  *
         *=======================================*/
        case 1:
            dmaRight = kmem_alloc (DMA_BUF_SIZE,
                            KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN );
            if ( dmaRight == (caddr_t)NULL ) {
                cmn_err (CE_WARN,
                 "rapKernMem: Cannot allocate DMA memory for R_chann");
                 return(1);
            }
             dmaLeft = kmem_alloc (DMA_BUF_SIZE,
                            KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN );
            if ( dmaLeft == (caddr_t)NULL ) {
                cmn_err (CE_WARN,
                    "rapKernMem: Cannot allocate DMA memory for L_chann");
                kmem_free (dmaRight, DMA_BUF_SIZE);
                return(1);
            }
            /*   get the physicall address  */
            dmaRightPhys = kvtophys(dmaRight);
            dmaLeftPhys  = kvtophys(dmaLeft);
            return(0);
        /*=======================================*
         *   Deallocate Right/Left DMA Channels  *
         *=======================================*/
        case 2:
            if ( dmaRight != NULL ) {
                kmem_free (dmaRight, DMA_BUF_SIZE);
                dmaRight = (caddr_t)NULL;
            }
            if ( dmaLeft != NULL ) {
                kmem_free (dmaLeft, DMA_BUF_SIZE);
                dmaLeft = (caddr_t)NULL;
            }
            return(0);
    }  /* switch */
}  /***   End rapKernMem   ***/
/*************************************************************************
 *          r a p T i m e O u t       
 *************************************************************************
 *  Name:       rapTimeOut
 *  Purpose:    is called when Read/Write waiting for buffers time out.
 *  Returns:
 *************************************************************************/
static void
rapTimeOut( void *addr )
{
    cardInfo_t    *ci;
    ci = &cardInfo;
    /*   indicate a timeout  */
    ci->ri_tout = 1;
    wakeup (addr);
}
/*************************************************************************
 *                      r a p N o t e O n
 *************************************************************************
 *  Name:       rapNoteOn
 *  Purpose:    Sends a MIDI Note_On message.
 *              This code is taken from RAP-10 manual.
 *  Returns:    None.
 *************************************************************************/
static void
rapNoteOn ( cardInfo_t  *ci, ushort_t orig_gpis)
{
    int         s, stereo;
    uchar_t     c, pan, rank, chksum, sum;
    caddr_t     addr;
    ushort_t    gpis;
    addr = ci->ci_addr[0];
    stereo = ci->ci_state & CARD_STEREO;
    pan = 0x40;
    rank = 0x01;    /* for 22050 Hz  */
    gpis = orig_gpis;
    /*
     *      Busy wait till Txd Fifo is empty
     *  The interrupt version is commenetd out below
     */
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapNoteOn: Waiting for Txd Fifo Empty, gpis = %x",
            gpis);
    #endif
    while ( !(gpis & GPIS_TXD) ) {
        gpis = INPW(addr+GPIS);
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapNoteOn: Waiting ..new gpis = %x", gpis);
        #endif
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapNoteOn: Issuing a Note_On SysEx Cmd");
    #endif
    /*   send Note_On   */
    c = 0xf0; OUTB(addr+MDTD, c);
    c = 0x41; OUTB(addr+MDTD, c);
    c = 0x10; OUTB(addr+MDTD, c);
    c = 0x56; OUTB(addr+MDTD, c);
    c = 0x12; OUTB(addr+MDTD, c);
    if ( stereo ) {
        c = 0x03; OUTB(addr+MDTD, c);
        c = 0x00; OUTB(addr+MDTD, c);
        c = 0x01; OUTB(addr+MDTD, c);
        sum = 0x03 + 0x01;
    }
    else {
        c = 0x02; OUTB(addr+MDTD, c);
        c = 0x00; OUTB(addr+MDTD, c);
        c = 0x0A+0x01; OUTB(addr+MDTD, c);
        sum = 0x02+0x0A+0x01;
    }
    c = 0x01; OUTB(addr+MDTD, c);
    c = 0x7F; OUTB(addr+MDTD, c);
    c = 0x7F; OUTB(addr+MDTD, c);
              OUTB(addr+MDTD, rank);
    sum += (0x01+0x7F+0x7F+rank);
    c = 0x40; OUTB(addr+MDTD, c);
    c = 0x00; OUTB(addr+MDTD, c);
    c = 0x40; OUTB(addr+MDTD, c);
              OUTB(addr+MDTD, pan);
    sum += (0x40+0x40+pan);
    /*  calculate the checksum  */
    chksum = (0x80 - (sum % 0x80)) & 0x7F;
    OUTB(addr+MDTD, chksum);
    c = 0xF7; OUTB(addr+MDTD, c);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapNoteOn: Note_On Issued, chksum = %x", chksum);
    #endif
}  /* end rapNoteOn  */

/*************************************************************************
 *                      r a p N o t e O f f
 *************************************************************************
 *  Name:       rapNoteOff
 *  Purpose:    Sends a MIDI Note_Off message.
 *              This code is taken from RAP-10 manual.
 *  Returns:    None.
 *************************************************************************/
static void
rapNoteOff ( cardInfo_t  *ci)
{
    int         s, stereo;
    uchar_t     pan, b, rank, sum, chksum;
    caddr_t     addr;
    ushort_t    gpis;
    addr = ci->ci_addr[0];
    stereo = ci->ci_state & CARD_STEREO;
    pan = 0x40;
    rank = 0x01;    /* for 22050 Hz  */
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapNoteOff: Waiting for Txd Empty");
    #endif
    /*  wait till Txd is Empty   */
    gpis = INPW(addr+GPIS);
    while ( !(gpis & GPIS_TXD) ) {
        us_delay(10);
        gpis = INPW(addr+GPIS);
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapNoteOff: Waiting ..new gpis = %x", gpis);
        #endif
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapNoteOff: Issuing Note_Off");
    #endif
    /*   send Note_On   */
    OUTB(addr+MDTD, 0xF0);
    OUTB(addr+MDTD, 0x41);
    OUTB(addr+MDTD, 0x10);
    OUTB(addr+MDTD, 0x56);
    OUTB(addr+MDTD, 0x12);
    if ( stereo ) {
        OUTB(addr+MDTD, 0x03);
        OUTB(addr+MDTD, 0x00);
        OUTB(addr+MDTD, 0x01);
        sum = 0x03 + 0x01;
    }
    else {
        OUTB(addr+MDTD, 0x02);
        OUTB(addr+MDTD, 0x00);
        OUTB(addr+MDTD, 0x0A+0x01);
        sum = 0x02 + 0x0A + 0x01;
    }
    OUTB(addr+MDTD, 0x00);
    OUTB(addr+MDTD, 0x7F);
    OUTB(addr+MDTD, 0x7F);
    OUTB(addr+MDTD, 0x00);
    sum += 0x7F + 0x7F;
    OUTB(addr+MDTD, 0x40);
    OUTB(addr+MDTD, 0x00);
    OUTB(addr+MDTD, 0x40);
    OUTB(addr+MDTD, pan);
    sum += 0x40 + 0x40 + pan;
    /*   calculate checksum   */
    chksum = (0x80 - (sum % 0x80)) & 0x7F;
    OUTB(addr+MDTD, chksum);
    OUTB(addr+MDTD, 0x7F);
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapNoteOff: Note_On Issued, chksum = %x", chksum);
    #endif
}  /*  end rapNoteOff  */
/*************************************************************************
 *                      r a p Z e r o D m a
 *************************************************************************
 *  Name:      rapZeroDma
 *  Purpose:   Zero outs DMA buffers.
 *  Returns:   None.
 *************************************************************************/
static void
rapZeroDma (cardInfo_t *ci, int bytes)
{
    caddr_t dmaL, dmaR;
    int     stereo, s;
    s = LOCK();
    stereo = ci->ci_state & CARD_STEREO;
    /*
     *     Zero out which half ?
     */
    if ( ci->di_which ) {
        dmaR = &dmaRight[DMA_HALF_SIZE];
        dmaL = &dmaLeft[DMA_HALF_SIZE];
        if ( bytes == DMA_BUF_SIZE ) {
            bytes = DMA_HALF_SIZE;
        }
    }
    /*  Zer out 1st half of dma buffers */
    else {
        dmaR = &dmaRight[0];
        dmaL = &dmaLeft[0];
    }
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapZeroDma: Zeroing out %s of Dma buffers in %s for %d bytes",
        (ci->di_which ? "2nd half":"1st half"),
         (stereo ? "Stereo":"Mono"),
         bytes);
    #endif
    bzero (dmaL, bytes);
    dki_dcache_wbinval (dmaL, (unsigned)bytes);
    if ( stereo ) {
        bzero (dmaR, bytes);
        dki_dcache_wbinval (dmaR, (unsigned)bytes);
    }
    UNLOCK(s);
}  /***  end rapZeroDma   ***/
/*************************************************************************
 *                      r a p R e l e a s e D m a
 *************************************************************************
 *  Name:      rapReleaseDma
 *  Purpose:   Releases Dma channel(s).
 *             Note that we access kernel's Dma structure and later on
 *             a routine will be provided for us to avoid this.
 *  Returns:   None.
 *************************************************************************/
static void
rapReleaseDma (cardInfo_t *ci)
{
    /*   disable Eisa Dma  */
    #ifdef DEBUG
    cmn_err (CE_NOTE, "rapReleaseDma: Releasing Eisa Dma Chann %d",
        ci->ci_dmaCh5);
    #endif
    eisa_dma_disable(0, ci->ci_dmaCh5);
    if ( ci->ci_state & CARD_STEREO ) {
        #ifdef DEBUG
        cmn_err (CE_NOTE, "rapReleaseDma: Releasing Eisa Dma Chann %d",
                ci->ci_dmaCh6);
        #endif
        eisa_dma_disable(0, ci->ci_dmaCh6);
    }
} /***  end rapReleaseDma    ***/
/*************************************************************************
 *          r a p S e t A u t o I n i t
 *************************************************************************
 *  Name:      rapSetAutoInit
 *  Purpose:   sets Eisa DMA register for Autoinit. In Autoinit, DMA
 *             starts over from the beginning of the buffer again once it
 *             has transfered all bytes in the buffer.
 *  Returns:   None.
 *************************************************************************/
#define  EISA_MODE_REG  0xd6
#define  EISA_CH5       0x01
#define  EISA_CH6       0x02
#define  EISA_WRITE     0x04
#define  EISA_READ      0x08
#define  EISA_AUTO      0x10
static void
rapSetAutoInit( cardInfo_t  *ci, uchar_t what)
{
    uchar_t    b;
    #ifdef DEBUG
    cmn_err (CE_NOTE,
        "rapSetAutoInit: setting Autoinit DMA for %s, Eisa Addr = %x",
        ( what == DI_DMA_PLAYING ? "Playback(D/A)" : "Record(A/D)" ),
        eisa_addr );
    #endif
    b = 0;
    if ( what == DI_DMA_PLAYING )
        b |= EISA_READ;        /*  Memory -> Device  */
    else
        b |= EISA_WRITE;       /*  Device -> Memory  */
    /*   Autoinit for Channel 5 - Demand Mode select is default    */
    b |= (EISA_AUTO | EISA_CH5);
    OUTB(eisa_addr+EISA_MODE_REG, b);
    /*   Autoinit for Channel 6 (if in stereo mode)  */
    if ( ci->ci_state & CARD_STEREO ) {
        b &= ~EISA_CH5;
        b |= EISA_CH6;
        OUTB(eisa_addr+EISA_MODE_REG, b);
    }
}  /***   End rapSetAutoInit    ***/


Next | Prev | Up | Top | Contents | Index