/* ------------------------------------------------------------------------
 *      Audio Interface -- "ai"  REVISION 'A' for MTU
 *
 *              Digital Audio Interface
 *	
 *	This version is written for the MicroTechnology Unlimited
 *	DS16 d-to-a convertor.  Many thanks to Robert Gross, Bob
 *	VanValzah, Larry Issacs and Virgil Decarvalho for their
 *	assistance and advice -- bgg


Modification History
--------------------
30Nov1986       rmg,jmr written
--Dec1986       rmg     revised with new, faster allocation scheme
02Feb1987       rjd     tripled allowable file size on record
--Aug1987	bgg	Yikes!
--Dec1987	bgg	modified to work with MTU ds16
--Mar1988	bgg	modified to work with MTU ds16 w/ interrupts
--May1988	bgg	fixed start-up bug, reduced DMA bufsize to
			16k, multiple and spl stuff added to reduce
			buffer overruns.

--------------------------------------------------------------------------*/

/* #define CONTIQBUF */
/* #define AIDEBUG */
static char SccsID[] = "%W%     %G%";

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/kernel.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/map.h"

#include "../machine/psl.h"
#include "../machine/mmu.h"
#include "../machine/pte.h"
#include "../sun/dklabel.h"
#include "../sun/dkio.h"
#include "../sundev/mbvar.h"

#include "../sundev/aireg.h"
#include "../sundev/aivar.h"

#include "ai.h"
#if NAI > 0

#define AIPRI   (PZERO) /* software sleep priority for ai */
#define AIUNIT(x)       (minor(x) & 07)

struct  buf     raibuf[NAI];    /* static buffer headers for physio() */
int ai_debug = 0;

int     aiprobe(), aiintr(), aiattach(), aiioctl(), airead(), aiwrite();
int     aistrategy(), aistart();

struct  mb_device *aidinfo[NAI];
struct  mb_driver aidriver = { 
                aiprobe,                /* int (*mdr_probe)();  */
                0,                      /* int (*mdr_slave)();  */
                aiattach,               /* int (*mdr_attach)(); */
                0,                      /* int (*mdr_go)();     */
                0,                      /* int (*mdr_done)();   */
                0,                      /* int (*mdr_intr)();   polling */
                sizeof(struct ai_reg),  /* int mdr_size;        */
                "ai",                   /* char *mdr_dname;     */
                aidinfo,                /* struct mb_device **mdr_dinfo; */
                0,                      /* char *mdr_cname;     */
                0,                      /* struct mb_ctlr **mdr_cinfo;  */
                0,                      /* short mdr_flags;     */
                0,                      /* interrupt routine linked list */
};

struct ai_device {
        short   ai_present;             /* Controller exists */
        int     ai_open;                /* exclusive open flag */
        struct  buf *ai_bp;             /* current sample buffer */
        int     ai_count;               /* number of samples to transfer */
        long    *ai_cp;                 /* next sample to transfer */
        char    ai_busy;                /* device busy flag */
        struct  ai_convert ai_convert;
	caddr_t ai_bufloc[NUMBUFS];	/* address of DMA xfer buffer */
	caddr_t ai_ctrlloc;		/* address of ctrl info buffer */
	int	ai_mbinfo;		/* mbsetup info for DMA */
	int	ai_mbctrl;		/* mbsetup infor for ctrl words */
	dev_t	ai_dev;			/* ds16 device */
} ai_device[NAI];

aiprobe (reg, unit)
        caddr_t reg;
        int unit;
{
        register struct ai_reg *ai_reg;
        register int c;


        ai_reg = (struct ai_reg *)reg;

        c = peek((int *)&ai_reg->ai_csr);
        if (c == -1)
                return (0);

        ai_device[unit].ai_present = 1;

        return(sizeof(struct ai_reg));
}

aiattach(md)
        register struct mb_device *md;
{
        register struct ai_reg *ai_reg;
	int j;

        ai_reg = (struct ai_reg *)md->md_addr;

        /* perform any board initialization here. Reset board and set vector */ 

        ai_reg->ai_csr = IK_MSTR_CLR;
	
	ai_reg->ai_vector = md->md_intr->v_vec & 0xFF;
	/* the ikon VMEbus addr modifier -- got this from ikon -- BG */
	ai_reg->ai_addr_mod = 0x3D;

	/* allocate DMA, setup, ctrl buffers from kernel heap */
	for (j = 0; j < NUMBUFS-1; j++)
		ai_device[md->md_unit].ai_bufloc[j] = kmem_alloc(KERN_BUFSIZE);
	/* last one is 2x because of strange initial loading */
	ai_device[md->md_unit].ai_bufloc[NUMBUFS-1] = kmem_alloc(KERN_BUFSIZE*2);
	/* 256 bytes is more than we need, but maybe some day */
	ai_device[md->md_unit].ai_ctrlloc = kmem_alloc(256);

        aireset(ai_reg); 

}

aireset(ai_reg)
        register struct ai_reg *ai_reg;
{

	/* reset attention, perror, berror, disable interrupts on Ikon */
	ai_reg->ai_csr &= IK_MSTR_CLR; 	/* do it all, what the hell... */

	/* reset ds16 */
	ai_reg->ai_csr = IK_DSRESET;
	DELAY(7777); /* ds16 settling */
	ai_reg->ai_csr = IK_WBUF;

}

aiopen(dev, flags)
        dev_t dev;
        int flags;
{
        register struct mb_device *md;
        register struct ai_reg *ai_reg;
        int c;


        if ((md = aidinfo[AIUNIT(dev)]) == 0 || md->md_alive == 0) {
                if (ai_debug > 1)
                        printf("\naiopen: dev=0x%x, alive=%d\n", dev,
                                                                md->md_alive);
                return(ENXIO);
        }

        if (ai_device[AIUNIT(dev)].ai_open == 1 && !suser())
                return(EACCES);

        ai_reg = (struct ai_reg *)md->md_addr;

        /* Make sure board is plugged in */
        c = peek((int *)&ai_reg->ai_csr);
        if (c == -1)
                return (ENXIO);

        /* exclusive use */
        if (ai_device[AIUNIT(dev)].ai_open == 1) {
                if (u.u_uid != 0)
                        return(EACCES);
        }

        ai_device[AIUNIT(dev)].ai_open = 1;
	ai_device[AIUNIT(dev)].ai_dev = dev;

        return(0);
}

aiclose (dev, flags)
        dev_t dev;
        int flags;
{
        register struct mb_device *md;
        register struct ai_reg *ai_reg;
	int unit;

	unit = AIUNIT(dev);
        md = aidinfo[unit];

        ai_device[unit].ai_open = 0;

}


aiminphys (bp)
        struct buf *bp;
{
        register struct mb_device *md;
        register struct ai_reg *ai_reg;

        md = aidinfo[AIUNIT(bp->b_dev)];
        ai_reg = (struct ai_reg *)md->md_addr;

/* This should check the size of the buffer in the register set */
        if (bp->b_bcount > 2 * CONV_BUFSIZE)
                bp->b_bcount = 2 * CONV_BUFSIZE;
 
}

aistrategy (bp)
        register struct buf *bp;
{
        register struct mb_device *md;
        register struct ai_reg *ai_reg;
        register struct ai_device *ai;
        int s;

        md = aidinfo[AIUNIT(bp->b_dev)];
        ai = &ai_device[AIUNIT(bp->b_dev)];

        s = splx(pritospl(md->md_intpri));
        while (ai->ai_busy)
                sleep((caddr_t)ai, AIPRI);

        ai->ai_busy = 1;
        ai->ai_bp = bp;
        ai->ai_cp = (long *)bp->b_un.b_addr;
        ai->ai_count = bp->b_bcount;
        aistart (ai, (struct ai_reg *)md->md_addr);
        ai->ai_busy = 0;
        wakeup((caddr_t)ai);
        (void)splx(s);
}

aiwrite (dev, uio)
        dev_t dev;
        struct uio *uio;
{
        return(physio (aistrategy, &raibuf[AIUNIT(dev)], dev, B_WRITE, 
                                                        aiminphys, uio));

}

airead (dev, uio)
        dev_t dev;
        struct uio *uio;
{
        return(physio (aistrategy, &raibuf[AIUNIT(dev)], dev, B_READ, 
                                                        aiminphys, uio));
        
}

aistart (ai, ai_reg)
        struct ai_device *ai;
        struct ai_reg *ai_reg;
{
/*      register unsigned char *data;   */
        register long *data;

/*        data = (long *) ai_reg->ai_buf;    set to DMA address? bg 
					below is kludge for now */		
        data = (long *) ai_reg->ai_data;

        if (ai->ai_bp->b_flags & B_READ) {
                do {
                        *ai->ai_cp++ = *data++;
                        ai->ai_count -= sizeof(*data);
                } while (ai->ai_count);
        }
        else {
                do {
                        *data++ = *ai->ai_cp++;
                        ai->ai_count -= sizeof(*data);
                } while (ai->ai_count);
        }
        iodone(ai->ai_bp);
}

aiintr (unit)
{
        register struct mb_device *md;
        register struct ai_reg *ai_reg;
        register struct ai_convert *ai_convert;
	int s;

        md = aidinfo[unit];
        ai_reg = (struct ai_reg *)md->md_addr;
        ai_convert = &ai_device[unit].ai_convert; 
	
	s = spl6();

	ai_convert->conv_sigs |= IK_INTR;
	wakeup((caddr_t)ai_convert);

	splx(s);
        return (1);
}


aiioctl(dev, cmd, data, flag)
        dev_t dev;
        int cmd;
        caddr_t data;
        int flag;
{
        struct mb_device *md;
        register struct ai_reg *ai_reg;
        register struct ai_device *ai;
        struct aud_conv *ac_ptr, *uac_ptr;
        struct ai_convert *ai_convert;
        int s, i, unit;
        int err = 0;

	int j,srate,chans;
	struct buf *bp,xbuf;
	short *ctrl_word,*scanner,peakval,*shuttle_word;
	short srate_loword,srate_hiword,*srptr,chandata;
	int *clearer,endclear,scanflag,halfbuf;
	long xfer_addr;
	int saveit();

	ushort *smptr;
	char *mover;
	int DMA_buf, disk_buf, pop_bufs;
	short fill_level,red_level;

        unit = AIUNIT(dev);

        md = aidinfo[unit];
        ai_reg = (struct ai_reg *)md->md_addr;
        ai = &ai_device[unit];
        ai_convert = &ai_device[unit].ai_convert; 


        switch (cmd) {
                case AIO_GETSTAT:	/* get ikon csr for reading level */
                        s = splx(pritospl(md->md_intpri));
                        *(short *)data = ai_reg->ai_csr | ai_convert->conv_sigs;
                        splx(s);
                        return(0);

                case AIO_RESET:
                        s = spl6();
			conv_quit(unit);
			untimeout(saveit, (caddr_t)ai_convert);
			ai_convert->conv_sigs |= IK_ONES; /* set 'em all! */
			wakeup((caddr_t)ai_convert);
                        splx(s);
                        return(0);
		
		case AIO_STOP:
			ai_convert->conv_sigs |= IK_DONE;
			return(0);

                case AIO_GETSCAN:	/* scan buffers for peak */
			s=spl4();
			peakval = 0;
			halfbuf = KERN_BUFSIZE/2;
			if (ai_convert->can_scan & SCAN_YES) {
				scanner = (short *)ai->ai_bufloc[0];

				for (j = 0; j < halfbuf; j++) {
					*scanner = ABS(*scanner);
					peakval = (peakval > *scanner) ? peakval : *scanner++;
					}
				}

                        *(short *)data = peakval;
			ai_convert->can_scan = SCAN_NO;
			splx(s);
                        return(0);

                case AIO_SET_DAC:
                        s = splx(pritospl(md->md_intpri));
                        uac_ptr = (struct aud_conv *)data;

                        ai_convert->c_fd = uac_ptr->a_fd;
                        ai_convert->c_flags = uac_ptr->a_flags;
                        ai_convert->c_xfer_size = uac_ptr->a_nbytes;
                        ai_convert->c_numdbs = uac_ptr->a_numdbs;

			chans = uac_ptr->a_nchans;
			chandata = DA_MONO;
			if (chans == 2) chandata = DA_STEREO;
			ai_convert->chandata = chandata;

			srate = uac_ptr->a_srate;
			srptr = (short *)&srate;
			srate_hiword = *srptr++;
			srate_loword = *srptr;
		    	ai_convert->conv_sigs = IK_ZEROS; /* start clear */

                        /* verify that c_numdbs is <= MAXBLOCKS */
                        if (ai_convert->c_numdbs > MAXBLOCKS) {
                                splx(s);
                                return(EINVAL);
                        }

                        /* initialize status info */
                        ai_convert->c_status &= ~AI_BUSY;
                        ai_convert->c_status |= AI_DONE;
                        ai_convert->c_blksdone = 0;
                        ai_convert->c_offset = 0;
                        ai_convert->c_type = DA;

                        /* build block list for file */
                        if ((err = build_blocklist(ai_convert)) != 0) {
                                splx(s);
                                conv_quit(unit);
                                return(err);
                        }

			setds16(ai_convert,dev,srate_loword,srate_hiword,DA);
			/* twice?!?!?!? */
			setds16(ai_convert,dev,srate_loword,srate_hiword,DA);

			for (j = 0; j < NUMBUFS; j++)
                        	setup_buf(dev,j);

			/* advance blocks for skip */
			for (j = 0; j < uac_ptr->a_bufskip; j++) {
                		ai_convert->c_curblock++;
                		ai_convert->c_blksdone++;
				}

                        ai_convert->c_status |= AI_READY;

                        splx(s);
		
			for (j = 0; j < 3; j++) {
				/* preload ds16 buffer */
                       		/* load kernel buffer 0 for transfer */
                       		dobuf(ai_convert,0);

				if (j == 0) {
					if (ai_convert->c_offset) {
					printf("clearing offset...\n");
						clear_offset(ai->ai_bufloc[0],ai_convert);
						}

				/* clear out bytes for header or skip */
				endclear = uac_ptr->a_byteskip/4;
				clearer = (int *)ai->ai_bufloc[0];
				for (i = 0; i < endclear; i++)
					*clearer++ = 0x00000000;
				}
			
				/* load buffer into ds16 */

				/* spread data for "cycling" */
				/* last buf alloc'd is the big one */
				shuttle_word = (short *)ai->ai_bufloc[NUMBUFS-1];
				mover = (char *)ai->ai_bufloc[0];
				for (i = 0; i < KERN_BUFSIZE; i++) {
					smptr = (u_short *)mover++;
					*shuttle_word++ = *smptr;
					}


				/* DMA setup */
				bp = &xbuf;
		                bp->b_un.b_addr = ai->ai_bufloc[NUMBUFS-1];
       		       		bp->b_error = 0;
			        bp->b_bcount = KERN_BUFSIZE*2;
		       		bp->b_bufsize = bp->b_bcount;
       			        bp->b_proc = u.u_procp;
		                bp->b_dev = dev;

	       	       		bp->b_flags = B_BUSY | 
       		                 (ai_convert->c_type == DA ? B_WRITE : B_READ);
		                bp->b_flags |= (B_DONE);

				ai->ai_mbinfo = mbsetup(md->md_hd, bp, 0);
				xfer_addr = MBI_ADDR(ai->ai_mbinfo);
				if(xfer_addr > (BITS_24 - KERN_BUFSIZE*2)) {
					printf("mbsetup exceeded 24 bits\n");
					ai_reg->ai_csr = IK_MSTR_CLR;
					return(-1);
					}

				ai_reg->ai_DMA_range = (bp->b_bcount/2) - 1;
				ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
				ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

				ai_reg->ai_csr = IK_CLEOR; /* data, clear EOR */
				for (i = 0; i < KERN_BUFSIZE; i++)
					ai_reg->ai_pulse = IK_CYC; /* send it */

				mbrelse(md->md_hd,&ai->ai_mbinfo);
				}

			ai_convert->endbufs = 0;
                       	/* preload kernel buffers */
			for (j = 0; j < NUMBUFS; j++) {
				if( !(ai_convert->conv_sigs & IK_STOP) )
                			dobuf(ai_convert,j);
					ai_convert->endbufs++;
				}

			return(0);

                case AIO_SET_ADC:
                        s = splx(pritospl(md->md_intpri));
                        uac_ptr = (struct aud_conv *)data;

                        ai_convert->c_fd = uac_ptr->a_fd;
                        ai_convert->c_flags = uac_ptr->a_flags;
			/* worried about that last buffer! - BG */
                        ai_convert->c_xfer_size = uac_ptr->a_nbytes-KERN_BUFSIZE;
                        ai_convert->c_numdbs = uac_ptr->a_numdbs;

			chans = uac_ptr->a_nchans;
			chandata = AD_MONO;
			if (chans == 2) chandata = AD_STEREO;
			ai_convert->chandata = chandata;

			srate = uac_ptr->a_srate;
			srptr = (short *)&srate;
			srate_hiword = *srptr++;
			srate_loword = *srptr;
			scanflag = uac_ptr->a_scanflag; /* 1 means scan */
			ai_convert->conv_sigs = IK_ZEROS;/* start clear */

                        /* verify that c_numdbs is <= MAXBLOCKS */
                        if (ai_convert->c_numdbs > MAXBLOCKS) {
                                splx(s);
                                return(EINVAL);
                        }

                        /* initialize status info */
                        ai_convert->c_status &= ~AI_BUSY;
                        ai_convert->c_status |= AI_DONE;
                        ai_convert->c_blksdone = 0;
                        ai_convert->c_offset = 0;
                        ai_convert->c_type = AD;

			setds16(ai_convert,dev,srate_loword,srate_hiword,AD);
			/* twice?!?!?!? */
			setds16(ai_convert,dev,srate_loword,srate_hiword,AD);

			if (!scanflag) {
	                        /* build block list for file */
		                if ((err = build_blocklist(ai_convert)) != 0) {
	                        	conv_quit(unit);
                                	return(err);
                        		}

				for (j = 0; j < NUMBUFS; j++)
                        		setup_buf(dev,j);

				/* skip over header, if present */
				for (j = 0; j < uac_ptr->a_bufskip; j++) {
                			ai_convert->c_curblock++;
                			ai_convert->c_blksdone++;
					}
				}

                        ai_convert->c_status |= AI_READY;

                        splx(s);

			return(0);

                case AIO_GO:
			/* set ctrl info to go */
			ctrl_word = (short *)ai->ai_ctrlloc;
			*ctrl_word = ai_convert->chandata;

			/* DMA setup */
			bp = &xbuf;
	                bp->b_un.b_addr = ai->ai_ctrlloc;
               		bp->b_error = 0;
		        bp->b_bcount = 1;
               		bp->b_bufsize = bp->b_bcount;
       		        bp->b_proc = u.u_procp;
	                bp->b_dev = dev;

               		bp->b_flags = B_BUSY | 
       		                 (ai_convert->c_type == DA ? B_WRITE : B_READ);
		                bp->b_flags |= (B_DONE);

			ai->ai_mbinfo = mbsetup(md->md_hd, bp, 0);
			xfer_addr = MBI_ADDR(ai->ai_mbinfo);
			if(xfer_addr > (BITS_24 - KERN_BUFSIZE)) {
				printf("mbsetup exceeded 24 bit address\n");
				ai_reg->ai_csr = IK_MSTR_CLR;
				return(-1);
				}

			ai_reg->ai_DMA_range = 0; /* only one word to send */
			ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
			ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

#ifdef AIDEBUG
printf("ready to roll... csr: %x\n",ai_reg->ai_csr);
printf("bufloc: %x   ctrlloc: %x   xfer: %x  lo: %x  hi: %x  range:%x\n",ai->ai_bufloc[0],ai->ai_ctrlloc,xfer_addr,ai_reg->ai_DMA_rloaddr,ai_reg->ai_DMA_rhiaddr,ai_reg->ai_DMA_range);
#endif AIDEBUG

			/* GO DADDY-OH! */
			ai_reg->ai_csr = IK_CLR | IK_WCSR; /*clear, write csr*/
			if (ai_convert->c_type == DA) {
				ai_reg->ai_pulse = IK_CYC; /* send them bits! */
				ai_reg->ai_csr = IK_WBUF; /* write to buffer */
				}
			else {
				ai_reg->ai_pulse = IK_CYC; /* send them bits! */
				ai_reg->ai_csr = IK_RBUF; /* read from buffer */
				}
			mbrelse(md->md_hd,&ai->ai_mbinfo);

			DMA_buf = 0; /* the first one to be sent over */
			disk_buf = 0; /* the first one to do new DMA into */
			pop_bufs = 0; /* initially all is well... */

			/* set ds16 buffer operating levels */
			if (ai_convert->c_type == DA) {
				fill_level = BUF_HI;
				red_level = BUF_LO;
				}
			else {
				fill_level = BUF_LO;
				red_level = BUF_HI;
				}

		loop:
			s = spl4();
			while (ai_reg->ai_csr & fill_level) { /* running ok */

				/* catch up on reserve bufs, if needed */
				if (pop_bufs) {
					s = splx(s);
					if( !(ai_convert->conv_sigs & IK_STOP) )
						dobuf(ai_convert,disk_buf);
					s = spl4();
					pop_bufs--;
					if (++disk_buf == NUMBUFS) disk_buf = 0;

				/* is everything cool? */
				if (ai_reg->ai_csr & red_level)
					goto condition_red;

					}
				}

			/* finish it off */
			if(ai_convert->conv_sigs & IK_DONE) {
				if (ai_convert->c_type == DA) {
					while( !(ai_reg->ai_csr & BUF_MT) )
						;
					
					conv_quit(unit);
					splx(s);
					return(0);
					}
				else {
					conv_quit(unit);
					splx(s);
					return(0);
					}
				}
			
			/* DMA setup */
			bp = &xbuf;
	                bp->b_un.b_addr = ai->ai_bufloc[DMA_buf];
			if (++DMA_buf == NUMBUFS) DMA_buf = 0;
               		bp->b_error = 0;
		        bp->b_bcount = KERN_BUFSIZE;
               		bp->b_bufsize = bp->b_bcount;
       		        bp->b_proc = u.u_procp;
	                bp->b_dev = dev;
               		bp->b_flags = B_BUSY | 
       		                 (ai_convert->c_type == DA ? B_WRITE : B_READ);
		                bp->b_flags |= (B_DONE);

			ai->ai_mbinfo = mbsetup(md->md_hd, bp, 0);
			xfer_addr = MBI_ADDR(ai->ai_mbinfo);
			if(xfer_addr > (BITS_24 - KERN_BUFSIZE)) {
				printf("mbsetup exceeded 24 bit address\n");
				ai_reg->ai_csr = IK_MSTR_CLR;
				splx(s);
				return(-1);
				}

			ai_reg->ai_DMA_range = (bp->b_bcount/2) - 1;
			ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
			ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

			/* recover from hung driver */
			timeout(saveit, (caddr_t)ai_convert, (1*hz));

			if (ai_convert->c_type == DA) {
				/* clear flags, set interrupt, write buffer */
				ai_reg->ai_csr = IK_CLR | IK_INT;
				ai_reg->ai_pulse = IK_GO; /* DMAAAAAA!!!!!!! */
				}
			else {
				/* clear flags, set interrupt, read buffer */
				ai_reg->ai_csr = IK_CLR | IK_RBUF | IK_INT;
				ai_reg->ai_pulse = IK_GO; /* DMAAAAAA!!!!!!! */
				}

			while( !(ai_reg->ai_csr & IK_EOR) ) {
				sleep((caddr_t)ai_convert,AIPRI);
				if (ai_convert->conv_sigs & IK_TIMEOUT) {
					splx(s);
					mbrelse(md->md_hd,&ai->ai_mbinfo);
					conv_quit(unit);
					return(-1);
					}
				}
			mbrelse(md->md_hd,&ai->ai_mbinfo);
			untimeout(saveit, (caddr_t)ai_convert);  /* reset timeout */

			splx(s);

			if( !(ai_convert->conv_sigs & IK_STOP) ) {
				/* refill system bufs */
				dobuf(ai_convert,disk_buf);
				if (++disk_buf == NUMBUFS) disk_buf = 0;
				}
			else {
				if (ai_convert->endbufs - pop_bufs <= 1) {
					/* signal the VERY end */
					ai_convert->conv_sigs |= IK_DONE;
					}
				else {
					ai_convert->endbufs--;
					}
				}

			/* is everything cool? */
			if ( !(ai_reg->ai_csr & red_level) )
				goto loop;
		
		condition_red: /* AAACK!  send backup bufs! */
			
			s = spl6();

			/* send 3 over, that's all ds16 can do */
			for (j = 0; j < 3; j++) {
				if (++pop_bufs == NUMBUFS) {
					/* uh-oh, we caught up */
					pop_bufs--;
					splx(s);
					goto loop; /* sigh... */
					}

			/* DMA setup */
			bp = &xbuf;
	                bp->b_un.b_addr = ai->ai_bufloc[DMA_buf];
			if (++DMA_buf == NUMBUFS) DMA_buf = 0;
               		bp->b_error = 0;
		        bp->b_bcount = KERN_BUFSIZE;
               		bp->b_bufsize = bp->b_bcount;
       		        bp->b_proc = u.u_procp;
	                bp->b_dev = dev;
               		bp->b_flags = B_BUSY | 
       		                 (ai_convert->c_type == DA ? B_WRITE : B_READ);
		                bp->b_flags |= (B_DONE);

			ai->ai_mbinfo = mbsetup(md->md_hd, bp, 0);
			xfer_addr = MBI_ADDR(ai->ai_mbinfo);
			if(xfer_addr > (BITS_24 - KERN_BUFSIZE)) {
				printf("mbsetup exceeded 24 bit address\n");
				ai_reg->ai_csr = IK_MSTR_CLR;
				splx(s);
				return(-1);
				}

			ai_reg->ai_DMA_range = (bp->b_bcount/2) - 1;
			ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
			ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

			if (ai_convert->c_type == DA) {
				/* clear flags, write buffer (NO interrupt!) */
				ai_reg->ai_csr = IK_CLR;
				ai_reg->ai_pulse = IK_GO; /* DMAAAAAA!!!!!!! */
				}
			else {
				/* clear flags, read buffer (NO interrupt!) */
				ai_reg->ai_csr = IK_CLR | IK_RBUF;
				ai_reg->ai_pulse = IK_GO; /* DMAAAAAA!!!!!!! */
				}

			while( !(ai_reg->ai_csr & IK_EOR) )
				;

			mbrelse(md->md_hd,&ai->ai_mbinfo);
			}

			splx(s);

			goto loop;
			
		
		case AIO_SCAN:
			ai_convert->can_scan = SCAN_NO;
			/* set ctrl info to go */
			ctrl_word = (short *)ai->ai_ctrlloc;
			*ctrl_word = (ai_convert->chandata | HI_ENABLE);

			/* DMA setup */
			bp = &xbuf;
	                bp->b_un.b_addr = ai->ai_ctrlloc;
               		bp->b_error = 0;
		        bp->b_bcount = 0;
               		bp->b_bufsize = bp->b_bcount;
       		        bp->b_proc = u.u_procp;
	                bp->b_dev = dev;

               		bp->b_flags = B_BUSY | 
       		                 (ai_convert->c_type == DA ? B_WRITE : B_READ);
		                bp->b_flags |= (B_DONE);

			ai->ai_mbinfo = mbsetup(md->md_hd, bp, 0);
			xfer_addr = MBI_ADDR(ai->ai_mbinfo);
			if(xfer_addr > (BITS_24 - KERN_BUFSIZE)) {
				printf("mbsetup exceeded 24 bit address\n");
				conv_quit(unit);
				ai_reg->ai_csr = IK_MSTR_CLR;
				exit(-1);
				}

			ai_reg->ai_DMA_range = 1; /* throw away */
			ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
			ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

#ifdef AIDEBUG
printf("ready to roll... csr: %x\n",ai_reg->ai_csr);
printf("bufloc: %x   ctrlloc: %x   xfer: %x  lo: %x  hi: %x  range:%x\n",ai->ai_bufloc[0],ai->ai_ctrlloc,xfer_addr,ai_reg->ai_DMA_rloaddr,ai_reg->ai_DMA_rhiaddr,ai_reg->ai_DMA_range);
#endif AIDEBUG

			/* GO DADDY-OH! */
			/* clear flags, write ds 16 csr */
			ai_reg->ai_csr = IK_CLR | IK_WCSR;
			ai_reg->ai_pulse = IK_CYC; /* send them bits! */
			ai_reg->ai_csr = IK_RBUF; /* read from buffer */

			mbrelse(md->md_hd,&ai->ai_mbinfo);

			ai_convert->can_scan = SCAN_YES;

		scanloop:
			/* enable interrupt */
			ai_reg->ai_csr = IK_CLR | IK_INT | IK_RBUF;

			timeout(saveit, (caddr_t)ai_convert, (4*hz));

			/* wait for interrupt at 3/4 full level */
			while( !(ai_convert->conv_sigs & IK_INTR) ) {
				sleep((caddr_t)ai_convert,AIPRI);
				if (ai_convert->conv_sigs & IK_TIMEOUT) {
					conv_quit(unit);
					return(-1);
					}
				}

			untimeout(saveit, (caddr_t)ai_convert); /* reset timeout */

			/* mask out interrupt flag */
			ai_convert->conv_sigs &= 0xF0FF;


			/* DMA setup */
			bp = &xbuf;
	                bp->b_un.b_addr = ai->ai_bufloc[0];
               		bp->b_error = 0;
		        bp->b_bcount = KERN_BUFSIZE;
               		bp->b_bufsize = bp->b_bcount;
       		        bp->b_proc = u.u_procp;
	                bp->b_dev = dev;
               		bp->b_flags = B_BUSY | 
       		                 (ai_convert->c_type == DA ? B_WRITE : B_READ);
		                bp->b_flags |= (B_DONE);

			ai->ai_mbinfo = mbsetup(md->md_hd, bp, 0);
			xfer_addr = MBI_ADDR(ai->ai_mbinfo);
			if(xfer_addr > (BITS_24 - KERN_BUFSIZE)) {
				printf("mbsetup exceeded 24 bit address\n");
				conv_quit(unit);
				ai_reg->ai_csr = IK_MSTR_CLR;
				exit(-1);
				}

			ai_reg->ai_DMA_range = (bp->b_bcount/2) - 1;
			ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
			ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

			ai_convert->can_scan = SCAN_NO;

			/* recover from hung driver */
			timeout(saveit, (caddr_t)ai_convert, (1*hz));

			/* clear attention */
			do {
				/* clear flags, read from buffer */
				ai_reg->ai_csr = IK_CLR | IK_RBUF;
				}
			while (ai_reg->ai_csr & 0x2000);

			ai_reg->ai_pulse = IK_GO; /* DMAAAAAA!!!!!!! */

			while( !(ai_reg->ai_csr & IK_EOR) ) {
				if (ai_convert->conv_sigs & IK_TIMEOUT) {
					mbrelse(md->md_hd,&ai->ai_mbinfo);
					conv_quit(unit);
					return(-1);
					}
				}

			mbrelse(md->md_hd,&ai->ai_mbinfo);
			untimeout(saveit, (caddr_t)ai_convert);  /* reset timeout */

			ai_convert->can_scan = SCAN_YES;

			if(ai_convert->conv_sigs & IK_STOP) { /*user interrupt*/
				conv_quit(unit);
				return(0);
				}
			
			goto scanloop;

                default:
                        return(ENOTTY);
        }
}



conv_quit(unit)
{

        register struct mb_device *md;
        register struct ai_reg *ai_reg;
        struct ai_convert *ai_convert;

        md = aidinfo[unit];
        ai_reg = (struct ai_reg *)md->md_addr;
        ai_convert = &ai_device[unit].ai_convert; 

        ai_convert->c_status &= ~AI_BUSY;
        ai_convert->c_status |= AI_DONE;
        ai_convert->c_curblock = 0;
        ai_convert->conv_sigs = IK_ZEROS;

	ai_reg->ai_csr = IK_MSTR_CLR;

        /* Free up memory for disk map */
        if(ai_convert->c_blist) {
                kmem_free(ai_convert->c_blist, (unsigned int) 
                        (ai_convert->c_allocblocks * sizeof(daddr_t)));
                ai_convert->c_blist = NULL;
                ai_convert->c_allocblocks = 0;
        }
        return(0);
}

/* Do all things that need to be done before conversion starts */
setup_buf(dev,bnum)

dev_t dev;
int bnum;

{
        struct mb_device *md;
        struct ai_reg *ai_reg;
        struct ai_device *ai;
        register struct ai_convert *ai_convert;
        int s, i, unit = AIUNIT(dev);
        register struct buf *bp;


        md = aidinfo[unit];
        ai_reg = (struct ai_reg *)md->md_addr;
        ai = &ai_device[unit];
        ai_convert = &ai_device[unit].ai_convert; 


        for(i = 0, bp = ai_convert->c_buf[bnum]; i < ai_convert->c_numdbs; i++,bp++) {

		/* point the bp's into the kmem_alloc'd buffer */
                bp->b_un.b_addr = ai->ai_bufloc[bnum] + (i * ai_convert->c_dbsize);
                bp->b_error = 0;
                bp->b_bcount = ai_convert->c_dbsize;
                bp->b_bufsize = bp->b_bcount;
                bp->b_proc = u.u_procp;
                bp->b_dev = ai_convert->c_dev;

                bp->b_flags = B_BUSY | 
                        (ai_convert->c_type == DA ? B_READ : B_WRITE) | 
                        bdevsw[major(bp->b_dev)].d_flags;
                /* This will let the first call to dobuf succeed */
                bp->b_flags |= (B_DONE);
        }

}

setds16(ai_convert,dev,sr1,sr2,dtoa)
        register struct ai_convert *ai_convert;
	int dtoa;
	short sr1,sr2;
	dev_t dev;

{
	register struct ai_device *ai;
	register struct mb_device *md;
        register struct ai_reg *ai_reg;
	long xfer_addr;
	short *ctrl_word;
	struct buf *bp,ctrlbuf;

	ai = &ai_device[AIUNIT(dev)];
	md = aidinfo[AIUNIT(dev)];
        ai_reg = (struct ai_reg *)md->md_addr;

	/* set up buf for ctrl info */
	bp = &ctrlbuf; /* buffer for ds16 ctrl transfers */
        bp->b_un.b_addr = ai->ai_ctrlloc;
        bp->b_error = 0;
        bp->b_bcount = 14;
        bp->b_bufsize = bp->b_bcount;
        bp->b_proc = u.u_procp;
        bp->b_dev = dev;

        bp->b_flags = B_BUSY | 
                (ai_convert->c_type == DA ? B_WRITE : B_READ);
                bp->b_flags |= (B_DONE);

	ai->ai_mbctrl = mbsetup(md->md_hd, bp, 0);
	xfer_addr = MBI_ADDR(ai->ai_mbctrl);
	if(xfer_addr > (BITS_24 - 48)) {
		printf("mbsetup exceeded 24 bit address\n");
		ai_reg->ai_csr = IK_MSTR_CLR;
		exit(-1);
		}

	/* set it up */
	ai_reg->ai_csr = IK_MSTR_CLR;
	ai_reg->ai_csr = IK_DSRESET; /* reset ds16 */
	DELAY(7777); /* ds16 needs this to reset */
	ai_reg->ai_csr = IK_WBUF;

	ctrl_word = (short *)ai->ai_ctrlloc;

	ai_reg->ai_DMA_range = 12;
	ai_reg->ai_DMA_wloaddr = (xfer_addr & BITS_24) >> 1;
	ai_reg->ai_DMA_whiaddr = (xfer_addr & BITS_24) >> 17;

#ifdef AIDEBUG
printf("in setds16    csr: %x\n",ai_reg->ai_csr);
printf("bufloc: %x   ctrlloc: %x   xfer: %x  lo: %x  hi: %x  range:%x\n",ai->ai_bufloc[0],ai->ai_ctrlloc,xfer_addr,ai_reg->ai_DMA_rloaddr,ai_reg->ai_DMA_rhiaddr,ai_reg->ai_DMA_range);
#endif AIDEBUG

	/* this is grungy -- load ds16 csr data "by hand", cycle each over */

	ai_reg->ai_csr = IK_WCSR; /* write to csr */
	*ctrl_word++ = 0x0000; /* clear ds16 buffer, reset counter, etc */
	ai_reg->ai_pulse = IK_CYC; /* send it */
	*ctrl_word++ = 0x004E; /* connect to SR1, etc */
	ai_reg->ai_pulse = IK_CYC; /* send it */
	ai_reg->ai_csr = IK_WBUF; /* write to data buffer */
	*ctrl_word++ = sr1;
	ai_reg->ai_pulse = IK_CYC; /* send it */
	ai_reg->ai_csr = IK_WCSR; /* write to csr */
	*ctrl_word++ =  0x004F; /* connect to SR2 */
	ai_reg->ai_pulse = IK_CYC; /* send it */
	ai_reg->ai_csr = IK_WBUF; /* write to data buffer */
	*ctrl_word++ = sr2;
	ai_reg->ai_pulse = IK_CYC; /* send it */
	ai_reg->ai_csr = IK_WCSR; /* write to csr */
	if (dtoa)
		*ctrl_word = 0x004C; /* 16 bit, data */
	else
		*ctrl_word = 0x005C; /* 16 bit, data, */
	ai_reg->ai_pulse = IK_CYC; /* send it */

        bp->b_flags |= (B_DONE);
	mbrelse(md->md_hd,&ai->ai_mbctrl);

	ai_reg->ai_csr = IK_CLR;

#ifdef AIDEBUG
printf("end ofsetds16    csr: %x\n",ai_reg->ai_csr);
printf("bufloc: %x   ctrlloc: %x   xfer: %x  lo: %x  hi: %x  range:%x\n",ai->ai_bufloc[0],ai->ai_ctrlloc,xfer_addr,ai_reg->ai_DMA_rloaddr,ai_reg->ai_DMA_rhiaddr,ai_reg->ai_DMA_range);
#endif AIDEBUG

	return(14);
}

saveit(aic)
	caddr_t aic;

{
	struct ai_convert *ai_convert;
	int s;

        printf("ai0: timeout!!!!!!!\n");
        ai_convert = (struct ai_convert *)aic;

        s = spl6();

    	ai_convert->conv_sigs = IK_TIMEOUT; /* signal timeout */
	wakeup((caddr_t)ai_convert);

	splx(s);
}


#include "../h/vnode.h"
#include "../h/vfs.h"
#include "../ufs/inode.h"
#include "../ufs/fs.h"

/* Grab a disk block access list for the fd in question. If open for
   writing then fastbmap will allocate blocks that are missing or marked
   as holes. The file must be on a local file system or this method
   cannot work. Blocks will not be allocated which lay beyond EOF
   This means that one must do an lseek to the end of file and at least
   write one byte. */

build_blocklist(ai_convert)
        struct ai_convert *ai_convert;
{
        register int i, loc; 
        register struct inode *ip;
        register struct vnode *vp;
        register int rw = ai_convert->c_type == AD ? B_WRITE : B_READ;
        int count, nblist;
        struct file *fp;
        struct vattr vattr;
        extern struct vnodeops ufs_vnodeops;
        daddr_t fastbmap(), *dptr;

        /* loc should be set to the file offset into the size of blocks */
        u.u_error = getvnodefp(ai_convert->c_fd, &fp);
        if (u.u_error)
                return(u.u_error);

        vp = (struct vnode *)fp->f_data;
        if (vp->v_type != VREG) 
                return(EINVAL);

        if(vp->v_op != &ufs_vnodeops)
                return(EINVAL);

        u.u_error =
            VOP_GETATTR((struct vnode *) fp->f_data, &vattr, u.u_cred);

        if (u.u_error)
                return(u.u_error);

        ip = VTOI(vp);

        /* This assumes that an lseek has been done to set the starting spot */
        loc = fp->f_offset / vp->v_vfsp->vfs_bsize;
        ai_convert->c_offset = fp->f_offset % vp->v_vfsp->vfs_bsize;
        ai_convert->c_dbsize = vp->v_vfsp->vfs_bsize;

        nblist = roundup(vattr.va_size, vp->v_vfsp->vfs_bsize) /
                vp->v_vfsp->vfs_bsize;
        nblist -= loc;

        ai_convert->c_numblocks = 
                roundup(ai_convert->c_xfer_size + ai_convert->c_offset, vp->v_vfsp->vfs_bsize) /
                vp->v_vfsp->vfs_bsize;

        /* We can't go begond EOF */
        if (ai_convert->c_numblocks > nblist)
                if(rw == B_WRITE)
                        return(EINVAL);
                else
                        ai_convert->c_numblocks = nblist;

        ai_convert->c_dev = vattr.va_fsid;
        ai_convert->c_strat = bdevsw[major(ai_convert->c_dev)].d_strategy;

#define MAXALLOC 120000
        if(ai_convert->c_numblocks * sizeof(daddr_t) > MAXALLOC)
                return(-1);

        /* Lock inode any return from here must unlock() it */
        ILOCK(ip);

        if(rw == B_READ)
                imark(ip, IACC);

#define YOUNGTIME 60
        /* If the file had just been worked on sync out the buffer cache */
        if(rw == B_READ && ip->i_mtime.tv_sec + YOUNGTIME > time.tv_sec) {
                if(ai_debug)
                        printf("syncing file\n");
                syncip(ip);
        }

        ai_convert->c_allocblocks = ai_convert->c_numblocks;
        ai_convert->c_blist = (daddr_t *) kmem_alloc((unsigned int)
                (ai_convert->c_allocblocks * sizeof(daddr_t)));


        for(dptr = ai_convert->c_blist, i = 0; i < ai_convert->c_numblocks; i++,  dptr++) {
                *dptr = fastbmap(ip, loc + i, rw, vp->v_vfsp->vfs_bsize, 0);
                if(ai_debug > 2)
                        printf("for logival %d fastbmap returned %d\n", loc, *dptr);

                switch((int) *dptr) {
                case 0: /* bmap should have set EFBIG */
                        IUNLOCK(ip);
                        if(u.u_error)
                                return(u.u_error);
                        return(EACCES);
                        break;
                case -1: /* Failed allocation for write or hole for read */
                        if(rw == B_WRITE) { /* Allocation fails */
                                printf("fastbmap allocation ran out of space\n");
                                IUNLOCK(ip);
                                return(EACCES);
                        }
                        *dptr = 0; /* Use boot block for holes */
                        break;
                default:
                        *dptr = fsbtodb(ip->i_fs, *dptr);
                        if(ai_debug > 2)
                                printf("diskblock = %d\n", *dptr);
                }

                if (ai_debug > 1) {
                        printf("%d ", *dptr);
                        if(((i % 10) == 0) && (i != 0))
                                printf("\n");
                }

		if ( (*dptr < 3) && (rw == B_WRITE) ) {
                        IUNLOCK(ip);
			printf("would have trashed the label... try again\n");
			exit(-1);
			}
        }
        IUNLOCK(ip);

        if (ai_debug > 1) 
                printf("\n");

        if (ai_debug) 
                printf("Kernel has %d blocks in the inode\n",ip->i_blocks);

        ai_convert->c_curblock = ai_convert->c_blist;
        return(0);
}


#ifndef CONTIQBUF
/* Fill/empty a buffer full of subbuffers */
dobuf(ai_convert,bnum)
        register struct ai_convert *ai_convert;
	int bnum;


{
        register struct buf *bp;
        register int i;
        register long *cp;
        int tail;


        for(bp = ai_convert->c_buf[bnum]; bp < (ai_convert->c_buf[bnum] + 
                                        ai_convert->c_numdbs); bp++) {

                if(bp->b_flags & B_ERROR) {
                        printf("error on buf %d\n",
                                ai_convert->c_curblock - ai_convert->c_blist);
                        return(-1);
                }
                if((bp->b_flags & B_DONE) == 0) {
                        printf("buf %d not completed disk\n", 
                                ai_convert->c_curblock - ai_convert->c_blist);
                        return(-1);
                }
                bp->b_blkno = *ai_convert->c_curblock++;
                ai_convert->c_blksdone++;
                bp->b_flags &= ~ (B_DONE | B_ERROR);
                (*ai_convert->c_strat)(bp);
                while((bp->b_flags & B_DONE) == 0)      /* Wait for Iodone */ 
                        ; 

            /* Do work to clear memory on the last block */
            if(ai_convert->c_blksdone == ai_convert->c_numblocks) {
                    long n;

		    ai_convert->conv_sigs |= IK_STOP; /* signal end */

                    tail = (ai_convert->c_xfer_size + ai_convert->c_offset) 
                                        % ai_convert->c_dbsize;
                    cp = (long *) (bp->b_un.b_addr +  tail);
                    n = ai_convert->c_numdbs - (bp - ai_convert->c_buf[bnum]); 
                    n *= ai_convert->c_dbsize;
		    n -= tail;
                    n /= sizeof(long);
                if(n > 16384)
                        printf("Bad VALUE\n");
                else {
				/* Wait for Iodone */ 
                                while((bp->b_flags & B_DONE) == 0)
                                        ; 
                                for (i = 0; i < n; i++)
                                        *cp++ = 0L;
                        }
                }
        }


}


#else CONTIQBUF
dobuf(ai,dev)
        register struct ai_convert *ai;
	dev_t dev;
{
        register struct buf *bp, *wbp;
        int dblocks = btodb(ai->c_dbsize);
        register int i = 0, numbuffs_left, tail;
        register long *cp;
        register *last_addr;

        for(bp = ai->c_buf; bp < (ai->c_buf + ai->c_numdbs);  bp++) {

                wbp = bp;
                numbuffs_left = &ai->c_buf[ai->c_numdbs] - bp;
                if(ai->c_blksdone == ai->c_numblocks)   /* On last block */
                        break;

                while(i++ < numbuffs_left) {
                        if(bp->b_flags & B_ERROR) {
                                printf("error on buf %d\n",
                                        ai->c_curblock - ai->c_blist);
                                return(-1);
                        }
                        if((bp->b_flags & B_DONE) == 0) {
                                printf("buf %d not completed disk\n", 
                                        ai->c_curblock - ai->c_blist);
                                return(-1);
                        }
                        bp->b_flags &= ~ (B_DONE | B_ERROR);
                        bp++;
                        if(i == 1)
                                wbp->b_blkno = *ai->c_curblock++;

                        if(i < numbuffs_left && wbp->b_blkno + i * dblocks 
                                == *ai->c_curblock) {
                                        wbp->b_bcount += ai->c_dbsize;
                                        ai->c_curblock++;
                        }
                        else
                                break;
                }
                wbp->b_flags &= ~ (B_DONE | B_ERROR);
                ai->c_blksdone += i;
                (*ai->c_strat)(wbp);
        }

        /* Do work to clear memory on the last block */
        if(ai->c_blksdone == ai->c_numblocks) { /* On last block */
                tail = (ai->c_xfer_size + ai->c_offset) % ai->c_dbsize;
                tail = wbp->b_bcount - tail; /* unused part */ 
                cp = (long *) (wbp->b_un.b_addr +  tail);
                last_addr = (long *) (wbp->b_un.b_addr +  wbp->b_bcount);
                while((wbp->b_flags & B_DONE) == 0)     /* Wait for Iodone */ 
                        ; 
                while (cp < last_addr)
                        *cp++ = 0L;
printf("unused part of last block = %d\n",tail);
        }
        return(0);
}
#endif CONTIQBUF

/* Put silence into the first buffer for the byte offset */
clear_offset(memloc, ai_convert)
        caddr_t memloc;
        struct ai_convert *ai_convert;
{
        struct buf *bp;
        register long *cp = (long *)memloc;
        register *last_addr;

        /* Wait for first buf to complete */
        bp = ai_convert->c_buf[0];
        while((bp->b_flags & B_DONE) == 0)
                ;
        
        last_addr = (long *)((long)memloc + (long)ai_convert->c_offset);

        while (cp < last_addr)
                *cp++ = 0L;
}
/*      @(#)ufs_alloc.c 1.1 86/02/03 SMI; from UCB 6.3 84/02/06 */

#ifdef QUOTA
#include "../ufs/quota.h"
#endif

extern u_long           hashalloc();
extern ino_t            ialloccg();
extern daddr_t          alloccg();
extern daddr_t          alloccgblk();
extern daddr_t          fragextend();
extern daddr_t          blkpref();
extern daddr_t          mapsearch();
extern int              inside[], around[];
extern unsigned char    *fragtbl[];

/*
 * Allocate a block in the sound file system.
 * 
 * The size of the requested block is given, which must be some
 * multiple of fs_fsize and <= fs_bsize.
 * A preference may be optionally specified. If a preference is given
 * the following hierarchy is used to allocate a block:
 *   1) allocate the requested block.
 *   2) allocate a rotationally optimal block in the same cylinder.
 *   3) allocate a block in the same cylinder group.
 *   4) quadradically rehash into other cylinder groups, until an
 *      available block is located.
 * If no block preference is given the following heirarchy is used
 * to allocate a block:
 *   1) allocate a block in the cylinder group that contains the
 *      inode for the file.
 *   2) quadradically rehash into other cylinder groups, until an
 *      available block is located.
 */

/* If you want to have any contiguos allocation you must set the file
        system paramter for maxcontiguous to > 1, also you might want to
        also change the value of nbpg to more than the default. This
        will not gaurentee continguous files but will give them a chance.
        Any device driver will have to adapt to the number of blocks that
        it MIGHT be able to read contiguously. Fast alloc avoids clearing the
        block and fastbmap avoids writing the block out to the disk.  */

struct buf *
fastalloc(ip, bpref, size)
        register struct inode *ip;
        daddr_t bpref;
        int size;
{
        daddr_t bno;
        register struct fs *fs;
        register struct buf *bp;
        int cg;
        
        fs = ip->i_fs;
        if ((unsigned)size > fs->fs_bsize || fragoff(fs, size) != 0) {
                printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n",
                    ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);
                panic("alloc: bad size");
        }
        if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
                goto nospace;
        if (u.u_uid != 0 && freespace(fs, fs->fs_minfree) <= 0)
                goto nospace;
#ifdef QUOTA
        u.u_error = chkdq(ip, (long)btodb(size), 0);
        if (u.u_error)
                return (NULL);
#endif
        if (bpref >= fs->fs_size)
                bpref = 0;
        if (bpref == 0)
                cg = itog(fs, ip->i_number);
        else
                cg = dtog(fs, bpref);

	if (cg == 0) {
		printf("fastalloc, cg: %d (label?)\n",cg);
		return (NULL);
		}

        bno = (daddr_t)hashalloc(ip, cg, (long)bpref, size,
                (u_long (*)())alloccg);
        if (bno <= 0)
                goto nospace;
        ip->i_blocks += btodb(size);
        if(ai_debug > 2) 
                printf("allocated file block %d ip->i_blocks = %d\n", 
                        bno, ip->i_blocks);
        imark(ip, IUPD|ICHG);
        bp = getblk(ip->i_devvp, (daddr_t)fsbtodb(fs, bno), size);
/* This is the only difference with alloc(), don't bother to clear the
   block out. */
#ifdef notdef
        clrbuf(bp);
#endif notdef
        return (bp);
nospace:
        fserr(fs, "file system full");
        uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
        u.u_error = ENOSPC;
        return (NULL);
}

/*
 * fastbmap defines the structure of file system storage
 * by returning the file system block number on a device given the
 * inode and the logical block number in a file.
 * When convenient, it also leaves the physical
 * block number of the next block of the file in rablock
 * for use in read-ahead. This should only be used for blocks that will be
 * 'RECORDED' over as there is no writing out (or clearing via fastalloc())
 * of the block itself. It also will not allocate directory blocks or blocks
 * That might be blocken up into fragments and it will not handle extending
 * files that live on file systems using fragments smaller than the
 * block size. Return values are -1 for a empty READ block (for a hole
 * in a file, 0 for Error (since no one should have the boot block), and 
 * > 0 for the file system block number of the logical file block 'bn'. This
 * Can be turned into a physical offset into a disk partition using fsbtodb().
 */
/*VARARGS3*/
daddr_t
fastbmap(ip, bn, rwflg, size, sync)
        register struct inode *ip;
        daddr_t bn;
        int rwflg;
        int size;       /* supplied only when rwflg == B_WRITE */
        int sync;       /* supplied only when rwflg == B_WRITE */
{
        register int i;
        int osize, nsize;
        struct buf *bp, *nbp;
        struct fs *fs;
        int j, sh;
        daddr_t nb, ob, lbn, *bap, pref, blkpref();

        if (bn < 0) {
                u.u_error = EFBIG;
                return ((daddr_t)0);
        }
        fs = ip->i_fs;
        rablock = 0;
        rasize = 0;             /* conservative */

        /*
         * If the next write will extend the file into a new block,
         * and the file is currently composed of a fragment
         * this fragment has to be extended to be a full block and fastbmap
         * will not do this.
         */
        lbn = lblkno(fs, ip->i_size);
        if (rwflg == B_WRITE && lbn < NDADDR && lbn < bn) {
                osize = blksize(fs, ip, lbn);
                if (osize < fs->fs_bsize && osize > 0) {
                        printf("fastbmap error 1, lbn = %d bn = %d\n",lbn,bn);
                        return((daddr_t) 0);
                }
        }
        /*
         * The first NDADDR blocks are direct blocks
         */
        if (bn < NDADDR) {
                nb = ip->i_db[bn];
                if (rwflg == B_READ) {
                        if (nb == 0)
                                return ((daddr_t)-1);
                        goto gotit;
                }
                if (nb == 0 || ip->i_size < (bn + 1) * fs->fs_bsize) {
                        if (nb != 0) {
                                osize = fragroundup(fs, blkoff(fs, ip->i_size));
                                nsize = fragroundup(fs, size);
                                if(nsize <= osize)
                                        goto gotit;
                                if(ai_debug)
                                        printf("fastbmap error 2, nb = %d, osize = %d, nsize = %d\n",
                                                nb, osize, nsize);
                                return((daddr_t) -1);
                        } else {
                                if (ip->i_size < (bn + 1) * fs->fs_bsize)
                                        nsize = fragroundup(fs, size);
                                else
                                        nsize = fs->fs_bsize;
                                bp = fastalloc(ip,
                                        blkpref(ip, bn, (int)bn, &ip->i_db[0]),
                                        nsize);
                                if (bp == NULL) {
                                        if(ai_debug)
                                                printf("fastalloc returned null\n");
                                        return ((daddr_t)-1);
                                }
                                nb = dbtofsb(fs, bp->b_blkno);
                                ip->i_db[bn] = nb;
                                imark(ip, IUPD|ICHG);
                                if ((ip->i_mode&IFMT) == IFDIR)
                                        return((daddr_t) -1);
                                else {
                                        bp->b_flags |= B_DONE;
                                        brelse(bp);
                                }
                        }
                }
gotit:
                if (bn < NDADDR - 1) {
                        rablock = fsbtodb(fs, ip->i_db[bn + 1]);
                        rasize = blksize(fs, ip, bn + 1);
                }
                return (nb);
        }

        /*
         * Determine how many levels of indirection.
         */
        pref = 0;
        sh = 1;
        lbn = bn;
        bn -= NDADDR;
        for (j = NIADDR; j>0; j--) {
                sh *= NINDIR(fs);
                if (bn < sh)
                        break;
                bn -= sh;
        }
        if (j == 0) {
                u.u_error = EFBIG;
                return ((daddr_t)0);
        }

        /*
         * fetch the first indirect block
         */
        nb = ip->i_ib[NIADDR - j];
        if (nb == 0) {
                if (rwflg == B_READ)
                        return ((daddr_t)-1);
                pref = blkpref(ip, lbn, 0, (daddr_t *)0);
                bp = alloc(ip, pref, (int)fs->fs_bsize);
                if (bp == NULL)
                        return ((daddr_t)-1);
                nb = dbtofsb(fs, bp->b_blkno);
                /*
                 * Write synchronously so that indirect blocks
                 * never point at garbage.
                 */
                bwrite(bp);
                ip->i_ib[NIADDR - j] = nb;
                imark(ip, IUPD|ICHG);
        }

        /*
         * fetch through the indirect blocks
         */
        for (; j <= NIADDR; j++) {
                bp = bread(ip->i_devvp, (daddr_t)fsbtodb(fs, nb),
                    (int)fs->fs_bsize);
                if (bp->b_flags & B_ERROR) {
                        brelse(bp);
                        return ((daddr_t)0);
                }
                bap = bp->b_un.b_daddr;
                sh /= NINDIR(fs);
                i = (bn / sh) % NINDIR(fs);
                nb = bap[i];
                if (nb == 0) {
                        if (rwflg==B_READ) {
                                brelse(bp);
                                return ((daddr_t)-1);
                        }
                        if (pref == 0)
                                if (j < NIADDR)
                                        pref = blkpref(ip, lbn, 0,
                                                (daddr_t *)0);
                                else
                                        pref = blkpref(ip, lbn, i, &bap[0]);

                        /* Make sure we only fast alloc on the real target
                                block and not on indirect blocks getting to it */
                        if (j < NIADDR || (ip->i_mode&IFMT) == IFDIR || sync) 
                                nbp = alloc(ip, pref, (int)fs->fs_bsize);
                        else
                                nbp = fastalloc(ip, pref, (int)fs->fs_bsize);

                        if (nbp == NULL) {
                                printf("fastalloc or alloc returned null\n");
                                brelse(bp);
                                return ((daddr_t)-1);
                        }
                        nb = dbtofsb(fs, nbp->b_blkno);
                        if (j < NIADDR || (ip->i_mode&IFMT) == IFDIR || sync) {
                                /*
                                 * Write synchronously so indirect blocks
                                 * never point at garbage and blocks
                                 * in directories never contain garbage.
                                 */
                                bwrite(nbp);
                        } else {
                                nbp->b_flags |= B_DONE;
                                brelse(nbp);
                        }
                        bap[i] = nb;
                        if (sync) {
                                bwrite(bp);
                        } else {
                                bdwrite(bp);
                        }
                } else {
                        brelse(bp);
                }
        }

        /*
         * calculate read-ahead.
         */
        if (i < NINDIR(fs) - 1) {
                rablock = fsbtodb(fs, bap[i+1]);
                rasize = fs->fs_bsize;
        }

	if (nb < 3) nb = -1; /* protect the label */

        return (nb);
}
#endif NAI
