/* modification for DeCarvalho DACs, PL 2/86- wish us luck! */ /* modification for DRQ3B board, in place of DRV11-W, wish us more luck! */ /* * DSC System 200 driver * via DSC dma11. * vax 4.2bsd version (can play both CARL and 4.2 files) * (4.2 files must have fragsize = blksize and be regular). * Problems: * 1). the number of BDP on the 750 and using more than 2 buffers * Solutions would be to try direct DMA rather than BDP transfers, get * uba resources on the fly, use only one BDP and doing the partition * ourself (#define ONEBDP). * VAX 730 don't have this problem as they don't have * buffered data paths and ubasetup turns it off anyway. * * 2). To play files with holes (no actual disk block allocated) you * need to be able to use a known disk block with zeros in it as * uba resourses are locked and from the interrupt routine you * can't use copyout() to user space as who knows who is the user. * A solution is to use the boot block which is always allocated * from mkfs, and can be cleared. It is of no use on a soundfile * file system. We expanded the file system size to 16K without * changing the location of the super block (8K into file system) * so there is a check to see (if there are empty blocks) if the * byte count for transfer is greater than BBSIZE. At this time we * are using the first data block available to a new file system * and locking it up with zeros via an altered mkfs. */ /* PL 11/11/85 ^ Robert says that MIT used 8k, standard file system with no problem. No alteration needed to mkfs. fragsize = blksize = 8k. ****update, BV says MIT switched to 16k and got 10% improvement**** below -- Robert says probably unnecessary. but Q-bus priority systems are different so we will have to wait and see */ /* * !!! WARNING !!! WARNING !!! WARNING !!! * * since the disk driver strategy routine * is called from the converters' interrupt * level the processor level must not be set * to 0 in the disk driver. also where the * processor level is raised before fiddling * with the buffer list it must not be "raised" * to lower than the dma11's interrupt level. * change the code in the strategy routine to * look something like this: * * # define spl5 spl6 (* converters run at level 6 *) * * opl = spl5(); (* CHANGED *) * dp = &xxutab[ui->ui_unit]; * disksort(dp, bp); * if (dp->b_active == 0) { * xxustart(ui); * bp = &ui->ui_mi->um_tab; * if ((bp->b_actf != NULL) && (bp->b_active == 0)) * xxstart(ui->ui_mi); * } * (void) splx(opl); (* CHANGED *) * * you should also bracket the entire disk interrupt routine * with "opl = spl6() ... splx(opl)" and put an "splx(opl)" * prior to each return in it. * * the other alternative is to fix your dma11 so that it * interrupts at level 5 instead of level 6. dsc can give * you the information on how to do this. in either e*//*vent * the strategy routine must be fixed as shown above. */ #include "../h/param.h" #include "../h/systm.h" #include "../h/mount.h" #include "../h/dir.h" #include "../h/user.h" #include "../machine/pte.h" #include "../h/map.h" #include "../h/buf.h" #include "../h/kernel.h" #include "../vaxuba/ubavar.h" #include "../h/conf.h" #include "../h/proc.h" #include "../h/uio.h" #include "../h/file.h" #include "../vaxuba/dsc.h" #include "../vaxuba/drreg.h" #ifdef UNIXFILES #include "../h/inode.h" #include "../h/fs.h" #include "../machine/cpu.h" #include "../vaxuba/ubareg.h" #endif short Dmaset; short Cur_Op_Count,pc_Dma_En,pc_Trans_En,Ch_Mode_Lo,Cur_Addr_Seg,Cur_Addr_Off,Command,cmlo_Flip_Bit,Chain_Addr_Seg,Chain_Addr_Off,Base_Op_Count,Base_Addr_Seg,Base_Addr_Off,pc_Reset,com_Ch; # define WATCHDOG 8 #define JIFFIES 4 /* will now interrupt every 4 seconds */ /* * reset device */ # define RESETDEV bit(4) # define DSUNIT(dev) (minor(dev) & ~ RESETDEV) # define DSRESETUNIT(dev) (minor(dev) & RESETDEV) extern daddr_t drblock(); # define size(sf, cnt) ((cnt > (sf)->f_bsize) ? (sf)->f_bsize : cnt) /* * ds flags */ # define DS_CLOSED 0 # define DS_OPEN bit(0) # define DS_BSY bit(1) # define DS_NDSK bit(2) # define DS_MON bit(3) # define DS_BRD bit(4) # define DS_42BSD bit(7) # define DS_LOADING bit(8) # define DS_RESET bit(9) # define CHAIN_REGISTERS 2 int LOADING_TIMOUT = 10; int drdebug = 0; int ndsb; /* * params to driver */ # define A_TODO bit(0) # define A_BNO bit(1) # define A_CNT bit(2) # define A_SEQ bit(3) # define A_DEV bit(4) # define A_NBLKS bit(5) # define A_BLIST bit(6) # define A_DSWORD bit(7) # define MINARG (A_BNO | A_CNT | A_DEV | A_DSWORD) # define DSPRI (PZERO-1) /* size of the buffer holding the block list */ # define LISTSIZE MAXBSIZE /* number of disk addresses per buffer */ # define NDADDRS (LISTSIZE / sizeof(daddr_t)) /* * relevant information * about the dma and asc */ struct dr_softc { int c_dmacsr; /* copy of dma csr on error */ int c_asccsr; /* copy of asc csr on error */ int c_flags; /* internal flags */ int c_errs; /* errors, returned via ioctl */ int c_bufno; /* drubinfo/buffer */ int c_uid; /* user id */ int c_args; /* args received from user */ int c_wticks; /* watch dog */ int c_ubinfo[NDSB]; /* uba info */ int c_bubinfo; /* uba info, 1st buffer, once only */ int c_nblist; /* length of block list */ int c_blkno; /* current block in blist */ int c_mode; /* mode opened for */ struct buf c_dsb[NDSB]; /* bs buffers */ struct buf c_blist; /* block list */ struct buf *c_curb; /* current buffer in block list */ } dr_softc[1]; /* * relevant information * about the disking and * other truck */ struct dr_softf { daddr_t f_bno; /* starting block */ off_t f_todo; /* amnt. of file to convert */ off_t f_ccnt; /* amnt. of data not converted */ off_t f_dcnt; /* amnt. of file not diskio'd */ off_t f_icnt; /* amnt. of file not seen by drintr */ off_t f_blr[NDSB]; /* amnt. last converted */ int f_bsize; /* size of each buffer */ int f_boff; /* offset into buffer of first i/o */ int f_dev; /* bs device to use */ int (*f_strat)(); /* Strategy routine for samples */ int f_fd; /* 4.2 file descriptor */ off_t f_extra; /* silence at EOF */ } dr_softf[1]; int drprobe(), drattach(), drintr(); struct uba_device *drdinfo[1]; u_short drstd[] = { 0761040, 0 }; struct uba_driver drdriver = { drprobe, 0, drattach, 0, drstd, "dr", drdinfo }; /* ARGSUSED */ dropen(dev, mode) dev_t dev; { register struct uba_device *ui; register struct dr_softc *sc; register int unit; if ((unit = DSUNIT(dev)) >= 1) goto bad; if ((ui = drdinfo[unit]) == NULL) goto bad; if (ui->ui_alive == 0) goto bad; sc = &dr_softc[ui->ui_unit]; /* * if this is the reset device * then just do a reset and return. */ if (DSRESETUNIT(dev)) { /* * if the converters are in use then * only the current user or root can * do a reset. */ if (sc->c_flags & DS_OPEN) { if ((sc->c_uid != u.u_ruid) && (u.u_uid != 0)) { return(ENXIO); } } if (drinit(unit)) { uprintf("dr%d: asc Offline\n", ui->ui_unit); return(EIO); } return(0); } /* * only one person can use it * at a time */ if (sc->c_flags & DS_OPEN) bad: return(ENXIO); sc->c_mode = mode; /* * initialize */ if (drinit(unit)) { uprintf("dr%d: asc OFfline\n", ui->ui_unit); return(EIO); } sc->c_uid = u.u_ruid; sc->c_flags = DS_OPEN; /* Will reset SYNC that may have been left */ return(0); } /* ARGSUSED */ drclose(dev, flag) { register int unit; unit = DSUNIT(dev); dr_softc[unit].c_flags = DS_CLOSED; /* Will turn off all flags */ (void) drinit(unit); return(0); } drinit(unit) { register struct drdevice *draddr; register struct uba_device *ui; register struct dr_softc *sc; register struct dr_softf *sf; register short dummy; register int offl; int flts; int odd; int loadtime; if (unit >= 1) { uprintf("in drinit, unit and NDR = %d %d\n",unit,1); return(1); } if ((ui = drdinfo[unit]) == NULL) { uprintf("in drinit, drdinfo is NULL\n"); return(1); } draddr = (struct drdevice *) ui->ui_addr; offl = 0; sc = &dr_softc[ui->ui_unit]; sf = &dr_softf[ui->ui_unit]; loadtime = 0; while(sc->c_flags & DS_LOADING) { /* Sensitive loading */ if(loadtime++ < LOADING_TIMOUT) sleep((char *) &lbolt,DSPRI); else { sc->c_flags &= ~ DS_LOADING; printf("dr%d: loading timeout, flags = %O\n", ui->ui_unit,sc->c_flags); } } odd = 0; /* * flush out last remaining buffer */ if((sc->c_flags & DS_RESET) == 0) /* only if not reseting unibus */ if ((sc->c_mode & FREAD) && (sf->f_dcnt > 0) && (sc->c_flags & DS_BSY) && (! (sc->c_flags & DS_NDSK))) { register struct buf *bp; if(sc->c_flags & DS_42BSD) /* Adjust acording to uio */ ndsb = NDSB; else ndsb = 2; bp = &sc->c_dsb[sc->c_bufno % ndsb]; /* BEGIN DEBUG */ if (sf->f_bsize == 0) { uprintf("drinit: zero bsize\n"); sc->c_errs |= EDS_CERR; goto out; } /* END DEBUG */ /* sometimes dmawc is odd? */ draddr->address_window = Cur_Op_Count; bp->b_bcount = -draddr->data_window; if ((bp->b_bcount % sizeof(short)) != 0) odd = bp->b_bcount; bp->b_bcount -= bp->b_bcount % sizeof(short); bp->b_blkno = drblock(sf, sc); bp->b_flags &= ~ (B_DONE | B_ERROR); if(sf->f_strat != 0) (*sf->f_strat)(bp); else printf("dr: Zero strategy routine\n"); sf->f_dcnt -= sf->f_bsize; out:; } drfreeall(&sc->c_blist); /* reset dr_softc */ sc->c_dmacsr = 0; sc->c_asccsr = 0; sc->c_flags = 0; sc->c_errs = 0; sc->c_bufno = 0; sc->c_args = 0; sc->c_blkno = 0; sc->c_mode = 0; /* reset dr_softf */ sf = &dr_softf[ui->ui_unit]; sf->f_bno = 0; sf->f_todo = 0; sf->f_ccnt = 0; sf->f_dcnt = 0; sf->f_icnt = 0; sf->f_bsize = 0; sf->f_boff = 0; sf->f_dev = 0; sf->f_strat = 0; sf->f_fd = -1; sf->f_extra = 0; /* * terminate current run */ sc->c_flags &= ~ DS_BSY; /* Stupid redundant! */ wakeup((caddr_t) &dr_softc[ui->ui_unit]); if (sc->c_flags & DS_BSY) sc->c_errs |= EDS_RST; if (odd) printf("dr%d: drinit: odd dmawc (%d)\n", ui->ui_unit, odd); return(offl); } /* * f_bsize is the size of the buffers used for the i/o. the buffers are * obtained by taking the user buffer and splitting it into NDSB buffers. * For CARL files the user buffer should be a multiple of the track * size of the disk. For 4.2 it should be a multiple of the file system * block size. * * using uio.uio_len, uio.uio_base, and f_bsize each buffer header is set up. * the converters only need the base address of the buffer and the word * count. the disk needs these in addition to the block number. if we * are doing d/a conversions then we start the disk up to fill up the * buffers. For 4.2 files the f_offset (file pointer) is used so one can 'seek' * in to the file. * * after everything is ready to go we turn on the RUN bit and let 'em rip. */ drstart(unit, rw, uio) struct uio *uio; { register struct drdevice *draddr; register struct uba_device *ui; register struct dr_softc *sc; register struct dr_softf *sf; register struct iovec *iov; register struct buf *bp; register struct chain *ch; extern int drwatch(); int dummy; int bits; int blr; int opl; int i ; int config_word; u_int saveclockreg; int cbufsize; short dumword; sc = &dr_softc[unit]; sf = &dr_softf[unit]; ui = drdinfo[unit]; draddr = (struct drdevice *) ui->ui_addr; iov = uio->uio_iov; /* * make sure we have all * necessary info. */ sc->c_flags |= DS_LOADING; /* Don't let drinit get to us */ if(sc->c_flags & DS_42BSD) { /* 4.2 file setup. */ int ret ; ndsb = NDSB; /* Disk operation is opposite of DAC/ADC operation */ if(ret = build42lst(sc,sf,uio, (rw == B_READ ? B_WRITE : B_READ))) { sc->c_flags &= ~ DS_LOADING; return(ret); } if(drdebug) uprintf("4.2 soundfile. start blk = %d, byte off = %d\n", drblock(sf,sc),sf->f_boff); } else { ndsb = 2; /* Standard for CARL files */ sf->f_bsize = (iov->iov_len / ndsb); } if ((sc->c_args & MINARG) != MINARG) { sc->c_errs |= EDS_ARGS; goto bad; } /* * either the converters will be writing * to memory or the disk will be. */ cbufsize = (sc->c_flags & DS_42BSD ? sf->f_bsize * ndsb : (int) iov->iov_len); if (useracc(iov->iov_base, cbufsize, B_WRITE) == NULL) { sc->c_flags &= ~ DS_LOADING; sc->c_errs |= EDS_ACC; return(EFAULT); } /* * check for misaligned buffer. * * the "% DEV_BSIZE" check isn't correct. it doesn't hurt * though. it probably should be deleted. */ if (((cbufsize % DEV_BSIZE) != 0) || (cbufsize & 01)) { sc->c_errs |= EDS_MOD; goto bad; } /* * check for tiny buffer */ if (sf->f_bsize < DEV_BSIZE) { sc->c_errs |= EDS_SIZE; goto bad; } /* * check for unreasonable buffer * offset for first buffer */ if (sf->f_boff >= sf->f_bsize) { sc->c_errs |= EDS_ARGS; goto bad; } sf->f_dcnt = sf->f_ccnt + sf->f_extra + sf->f_boff; u.u_procp->p_flag |= SPHYSIO; sc->c_flags |= DS_BSY; vslock(iov->iov_base, (int) cbufsize); /* * point each c_dsb somewhere into * the user's buffer, set up each c_dsb. * the buffer's rw flag is set to the * inverse of our operation since it * applies to the bulk storage device * i/o to be done. */ for (i = 0; i < ndsb; i++) { bp = &sc->c_dsb[i]; bp->b_un.b_addr = iov->iov_base + (i * sf->f_bsize); bp->b_error = 0; bp->b_proc = u.u_procp; bp->b_bcount = sf->f_bsize; if(sc->c_flags & DS_42BSD) { bp->b_flags =B_BUSY |bdevsw[major(sf->f_dev)].d_flags| B_PHYS | ((rw == B_READ) ? B_WRITE : B_READ); bp->b_dev = sf->f_dev; sc->c_ubinfo[i] = ubasetup(ui->ui_ubanum, &sc->c_dsb[i], UBA_CANTWAIT|UBA_MAPANYWAY|UBA_NEEDBDP); if(drdebug) if(sc->c_ubinfo[i] == 0) { printf ("Zero return from ubasetup (buf %d)\n",i); goto out; } } if (rw == B_WRITE) { if ((sc->c_flags & DS_NDSK) == 0) { bp->b_blkno = drblock(sf, sc); sc->c_blkno++; bp->b_flags &= ~ (B_DONE | B_ERROR); if(drdebug) uprintf("drstart: blk=%ld, count=%d, ubinfo = %X\n", bp->b_blkno, bp->b_bcount,sc->c_ubinfo[i]); if(sf->f_strat != 0) (*sf->f_strat)(bp); else { printf("Zero disk strat routine for DACS\n"); sc->c_errs |= EDS_DISK; goto out; } /* iodone will wake us up */ while ((bp->b_flags & B_DONE) == 0) sleep((caddr_t) bp, DSPRI); /* oops */ if (bp->b_flags & B_ERROR) { sc->c_errs |= EDS_DISK; goto out; } } else bp->b_flags |= B_DONE; sf->f_dcnt -= sf->f_bsize; } else bp->b_flags |= B_DONE; } opl = spl6(); sc->c_flags &= ~ DS_LOADING; /* * if reading then going to * memory so set W2M */ if ((blr = drsize(sf)) <= 0) { sc->c_errs |= EDS_ARGS; splx(opl); goto out; } sf->f_blr[0] = blr; ch = (struct chain *) iov->iov_base; /* I can use beginning of first i/o buffer since this is skipped over. It contains the header information (1k) and is not needed. */ sc->c_bubinfo = sc->c_ubinfo[0] + sf->f_boff; sc->c_flags &= ~ DS_LOADING; sf->f_ccnt -= blr; /* * all done with boff; nail it. */ sf->f_boff = 0; /* do a total reset of DMA registers-overkill, but who cares*/ DMAput(Command,com_CH_0); DMAput(Command,com_CH_1); draddr->port_config = (pc_RESET_0|pc_RESET_1); /* clear FIFOs */ draddr->port_config = 0; DMAput(Cur_Addr_Seg,(sc->c_bubinfo >> 8) & 01400); DMAput(Cur_Addr_Off,sc->c_bubinfo); DMAput(Chain_Addr_Seg,(sc->c_ubinfo[0] >> 8) & 01400); DMAput(Chain_Addr_Off,sc->c_ubinfo[0]); DMAput(Cur_Op_Count,blr >> 1); DMAput(Base_Op_Count,sf->f_bsize >> 1); /* not actually correct, but ok for now (see below)*/ DMAput(Base_Addr_Seg,(sc->c_ubinfo[1] >> 8) & 01400); DMAput(Base_Addr_Off,sc->c_ubinfo[1]); config_word = pc_FUNBITS_EN; draddr->port_config = config_word; /* * as they say in california, * ``go for it'' */ ch->control = chain_CHANNEL_MODE; ch->word1 = BIT2; /* channelmodehi reg */ ch->word2 = cmlo_FIFO_BIT |BIT6| cmlo_Flip_Bit | cmlo_BTOC_TRANS_REL | cmlo_TRANS_INT_ENABLE; /* channelmodelo reg */ config_word |= pc_DMA_BLOCK | pc_Dma_En | pc_TRANS_0_EN | pc_TRANS_1_EN; /* I have to enable both port's transievers since control word is always written to port1 (output port) */ draddr->function = ofn_FN0; draddr->function = 0; draddr->port1_data = Dmaset; /* load control word */ DMAput(Command,com_Ch | com_SET | com_INT_EN_BIT | com_INT_CNTL); DMAput(Command,com_Ch | com_START_CHAIN); DMAput(MASTERMODE,mm_DMA_ENABLE); draddr->port_config = config_word; printf("controlword %o %o\n",Dmaset,draddr->port0_data); /* load control word */ printf("status reg and configword %o %o\n",draddr->status,config_word); DMAprint(CH_MODE_HI_0); DMAprint(CH_MODE_HI_1); DMAprint(CH_MODE_LO_0); DMAprint(CH_MODE_LO_1); DMAprint(DMA_STATUS_0); DMAprint(DMA_STATUS_1); DMAprint(INT_SAVE_0); DMAprint(INT_SAVE_1); printf("status reg %o\n",draddr->status); /* startup watchdog */ timeout(drwatch, (caddr_t) 0, JIFFIES*hz); /* * if the file is so small and only uses the first * buffer we would load dmawc with 0 but we can't * do that because we get errors (data late, external * will shut down the converters before it tries to play * the 2nd (bogus) block. */ if ((blr = drsize(sf)) <= 0) { blr = sf->f_bsize; } sf->f_blr[1] = blr; sf->f_ccnt -= blr; splx(opl); /* * wait for interrupt routine to signal * end of conversions. Non interruptable by signal */ printf("status reg %o\n",draddr->status); while (sc->c_flags & DS_BSY) sleep((caddr_t) &dr_softc[ui->ui_unit], DSPRI); /* * wait for disk i/o to complete. * release unibus resources. */ out: for (i = 0; i < ndsb; i++) { if ((sc->c_dsb[i].b_flags & B_DONE) == 0) { int waittime; for (waittime = 0; waittime < 7; waittime++) { sleep((caddr_t) &lbolt, DSPRI); if (sc->c_dsb[i].b_flags & B_DONE) goto done; } printf("dr%d: drb%d: wait timeout\n", ui->ui_unit, i); } done: if (sc->c_ubinfo[i] != 0) { if(drdebug) uprintf("ubarelse: %X\n",sc->c_ubinfo[i]); ubarelse(ui->ui_ubanum, &sc->c_ubinfo[i]); sc->c_ubinfo[i] = 0; } } /* * either the disk or the * converters were writing * to memory, thus the B_READ. */ vsunlock(iov->iov_base, (int) cbufsize, B_READ); /* stop DMA */ DMAand(Ch_Mode_Lo, ~cmlo_BTOC_TRANS_REL); DMAput(Command, com_Ch | com_HARD_MASK); /* Sleep for 1/2 second allowing things to settle down */ (void)timeout(wakeup, (caddr_t)&dr_softc[ui->ui_unit], hz>>1) ; (void)sleep((caddr_t)&dr_softc[ui->ui_unit], PZERO); /************************************************************/ /* this is supposed to reset DACs */ draddr->port_config |= pc_Reset; /* clear FIFOs */ draddr->port_config &= ~pc_Reset; /* clear resetbit */ draddr->function = 0; draddr->port1_data = ALLONES; draddr->function = ofn_FN1; draddr->function = 0; /************************************************************/ u.u_procp->p_flag &= ~ SPHYSIO; if (sc->c_errs) { bad: sc->c_flags &= ~ DS_LOADING; return(EIO); } return(0); } /* * this is where the real work is done. we copy any device registers that * we will be looking at to decrease the amount of traffic on the ds 200 * bus. also you can't do a "read-modify-write" (I think that this is called * a DATIP followed by a DATO in DEC manuals) on any of the dr registers * while the converters are running. * * note that you must write something to the dma11 clear status register * or you'll never get any more interrupts. * * c_wticks gets cleared on each interrupt. drwatch() increments * c_wticks and if c_wticks gets over a threshold then the * addacs are assumed to have hung and we do a reset. */ drintr(dev) dev_t dev; { register struct drdevice *draddr; register struct dr_softc *sc; register struct dr_softf *sf; register struct buf *bp; register int bufno; register int i; int unit; int blr; u_short dumword; unit = DSUNIT(dev); draddr = (struct drdevice *) drdinfo[unit]->ui_addr; /* first clear interrupt pending bit */ DMAput(Command, com_Ch | com_INT_PEND_BIT | com_INT_CNTL); draddr->status = st_CLR_DMA_PEND; sc = &dr_softc[unit]; sf = &dr_softf[unit]; sc->c_dmacsr = draddr->status; /* * reset c_wticks */ sc->c_wticks = 0; /* * get current buffer */ bufno = sc->c_bufno % ndsb; bp = &sc->c_dsb[bufno]; /* * check to see if any disking * needs to be done */ if (sf->f_dcnt > 0) { if ((bp->b_flags & B_DONE) == 0) { sc->c_errs |= EDS_DISK; goto out; } if ((sc->c_flags & DS_NDSK) == 0) { if (sf->f_bsize == 0) { uprintf("drintr: zero bsize\n"); sc->c_errs |= EDS_CERR; goto out; } bp->b_blkno = drblock(sf, sc); sc->c_blkno++; bp->b_flags &= ~ (B_DONE | B_ERROR); (*sf->f_strat)(bp); } else bp->b_flags |= B_DONE; sf->f_dcnt -= sf->f_bsize; } /* * check to see if converting is finished. * we need to decrement by the actual amount * converted, not just the buffer size. */ if ((sf->f_icnt -= sf->f_blr[bufno]) <= 0) goto out; /* * check for converter error. * all errors and normal termination * come here. */ if (sc->c_dmacsr & st_NONEX_MEM) { sc->c_errs |= EDS_CERR; printf("dr%d error: dmacsr=%o\n", unit, sc->c_dmacsr); out: sc->c_flags &= ~ DS_BSY; wakeup((caddr_t) &dr_softc[unit]); return(0); } /* set up words for next interrupt */ bufno = (sc->c_bufno + CHAIN_REGISTERS) % ndsb; blr = drsize(sf); if(sf->f_ccnt > 0) { DMAput(Base_Op_Count,blr >> 1); DMAput(Base_Addr_Seg,(sc->c_ubinfo[bufno] >> 8) & 01400); DMAput(Base_Addr_Off,sc->c_ubinfo[bufno]); } sf->f_blr[bufno] = blr; /* * try to catch disk errors */ for (i = 0; i < ndsb; i++) { if (sc->c_dsb[i].b_flags & B_ERROR) { sc->c_errs |= EDS_DISK; goto out; } } /* * update converted byte count. * update buffers converted count. */ sf->f_ccnt -= blr; sc->c_bufno++; return(0); } /* * a/d conversion */ drread(dev, uio) dev_t dev; struct uio *uio; { int unit; /* setup to do A-D conversion through port 0 */ Cur_Op_Count = CUR_OP_COUNT_0; Base_Op_Count = BASE_OP_COUNT_0; pc_Dma_En = pc_DMA_0_EN; pc_Trans_En = pc_TRANS_0_EN; Ch_Mode_Lo = CH_MODE_LO_0; Cur_Addr_Seg = CUR_ADDR_SEG_B_0; Cur_Addr_Off = CUR_ADDR_OFF_B_0; Base_Addr_Seg = BASE_ADDR_SEG_B_0; Base_Addr_Off = BASE_ADDR_OFF_B_0; Chain_Addr_Seg = CHAIN_ADDR_SEG_0; Chain_Addr_Off = CHAIN_ADDR_OFF_0; Command = COMMAND_0; com_Ch = com_CH_0; cmlo_Flip_Bit = cmlo_FLIP_BIT; pc_Reset = pc_RESET_0; unit = DSUNIT(dev); return(drstart(unit, B_READ, uio)); /* writes on disk */ } /* * d/a conversion */ drwrite(dev, uio) dev_t dev; struct uio *uio; { int unit; /* setup to do D-A conversion through port 1 */ Cur_Op_Count = CUR_OP_COUNT_1; Base_Op_Count = BASE_OP_COUNT_1; pc_Dma_En = pc_DMA_1_EN; pc_Trans_En = pc_TRANS_1_EN; Ch_Mode_Lo = CH_MODE_LO_1; Cur_Addr_Seg = CUR_ADDR_SEG_A_1; Cur_Addr_Off = CUR_ADDR_OFF_A_1; Base_Addr_Seg = BASE_ADDR_SEG_A_1; Base_Addr_Off = BASE_ADDR_OFF_A_1; Chain_Addr_Seg = CHAIN_ADDR_SEG_1; Chain_Addr_Off = CHAIN_ADDR_OFF_1; Command = COMMAND_1; com_Ch = com_CH_1; cmlo_Flip_Bit = 0; pc_Reset = pc_RESET_1; unit = DSUNIT(dev); return(drstart(unit, B_WRITE, uio)); /* reads from disk */ } /* ARGSUSED */ drioctl(dev, cmd, addr, flag) dev_t dev; caddr_t addr; { register struct drdevice *draddr; register struct dr_softc *sc; register struct dr_softf *sf; register struct ds_seq *dq; register struct ds_err *de; register struct ds_fs *df; struct uba_device *ui; struct ds_seq ds_seq; struct ds_err ds_err; struct ds_fs ds_fs; int unit; int i; unit = DSUNIT(dev); sc = &dr_softc[unit]; sf = &dr_softf[unit]; ui = drdinfo[unit]; draddr = (struct drdevice *) ui->ui_addr; switch (cmd) { /* starting block number */ case DSBNO: df = &ds_fs; bcopy(addr, (caddr_t) df, sizeof(struct ds_fs)); if (df->bnosiz < 0) return(EINVAL); sf->f_bno = df->bnosiz; sc->c_args |= A_BNO; break; /* no. of bytes to convert */ case DSCOUNT: df = &ds_fs; bcopy(addr, (caddr_t) df, sizeof(struct ds_fs)); /* * DSCOUNT is in bytes so * must be modulo sizeof(short) */ if ((df->bnosiz & 01) || (df->bnosiz <= 0)) return(EINVAL); sf->f_todo = sf->f_ccnt = sf->f_icnt = df->bnosiz; sc->c_args |= A_CNT; break; /* set sample rate */ case DSRATE: dq = &ds_seq; bcopy(addr, (caddr_t) dq, sizeof(struct ds_seq)); break; /* no bs device i/o */ case DSNODSK: sc->c_flags |= DS_NDSK; sc->c_args |= A_DEV | A_BNO; break; /* fetch errors */ case DSERRS: de = &ds_err; de->dma_csr = sc->c_dmacsr; de->asc_csr = sc->c_asccsr; de->errors = sc->c_errs; bcopy((caddr_t) de, addr, sizeof(struct ds_err)); break; /* byte offset into 1st buffer */ case DSBOFF: df = &ds_fs; bcopy(addr, (caddr_t) df, sizeof(struct ds_fs)); /* * DSBOFF is in bytes so * must be modulo sizeof(short) */ if ((df->bnosiz & 01) || (df->bnosiz < 0)) return(EINVAL); sf->f_boff = df->bnosiz; break; /* set Dma functions */ case DSWORD: df = &ds_fs; bcopy(addr, (caddr_t) df, sizeof(struct ds_fs)); Dmaset = df->bnosiz; sc->c_args |= A_DSWORD; break; /* how many samples actually converted */ case DSDONE: /* just to check result at end ???? */ df = &ds_fs; df->bnosiz = sf->f_todo - sf->f_icnt; draddr->address_window = Cur_Op_Count; df->bnosiz += draddr->data_window; bcopy((caddr_t) df, addr, sizeof(struct ds_fs)); break; case DS42BSD: /* Use a 4.2 file */ df = &ds_fs; bcopy(addr, (caddr_t) df, sizeof(struct ds_fs)); /* file descriptor */ if (df->bnosiz < 0) return(EINVAL); sf->f_fd = df->bnosiz; sc->c_flags |= DS_42BSD; break; case DSNBLKS: df = &ds_fs; bcopy(addr, (caddr_t) df, sizeof(struct ds_fs)); if (df->bnosiz < 1) return(EINVAL); sc->c_nblist = df->bnosiz; sc->c_args |= A_NBLKS; break; case DSBLKS: /* what do I do here ?? */ if ((sc->c_args & A_NBLKS) == 0) { sc->c_errs |= EDS_ARGS; return(EINVAL); } for (i = sc->c_nblist * sizeof(daddr_t); i > 0; i -= LISTSIZE) { struct buf *bp; unsigned int amt; bp = geteblk(LISTSIZE); clrbuf(bp); amt = i > LISTSIZE ? LISTSIZE : i; if (copyin(*(caddr_t *)addr, bp->b_un.b_addr, amt)) { drfreeall(&sc->c_blist); return(EFAULT); } *(caddr_t *)addr += amt; drlink(&sc->c_blist, bp); } sc->c_curb = sc->c_blist.av_forw; sc->c_args |= A_BNO | A_BLIST; break; default: return(ENOTTY); break; } return(0); } /* * link a buffer onto the buffer list * of blocks */ drlink(dp, bp) register struct buf *dp, *bp; { dp->av_back->av_forw = bp; bp->av_back = dp->av_back; dp->av_back = bp; bp->av_forw = dp; } /* * return all of the buffers to * the system */ drfreeall(dp) register struct buf *dp; { register struct buf *bp; for (bp = dp->av_forw; bp != dp; bp = dp->av_forw) { bp->av_back->av_forw = bp->av_forw; bp->av_forw->av_back = bp->av_back; brelse(bp); } } drsize(sf) register struct dr_softf *sf; { int tmp; /* * normal case. * f_boff is zeroed in the drstart routine * so its affect is only seen the first time. */ if (sf->f_ccnt >= sf->f_bsize) return(sf->f_bsize - sf->f_boff); /* * convoluted code follows. * * this code handles the situation where we are playing * something that is less than a track (f_bsize) but * there are three situations that have to be handled: * 1. the section to be played lies entirely within * a track with unplayed space both in front of it * and in back of it. * 2. the section to be played lies entirely within * a track but has unplayed space only in front of it. * (a special case of 1). * 3. the section to be played extends across a track * boundary. * * f_todo was set to the number of bytes to be converted; * it never changes. */ if (sf->f_todo == sf->f_ccnt) { if (sf->f_ccnt <= (sf->f_bsize - sf->f_boff)) return(sf->f_ccnt); /* 1 & 2 */ else return(sf->f_bsize - sf->f_boff); /* 3 */ } /* * this is executed for the last track */ /* Is there added silence? */ if(sf->f_extra > 0) { tmp = sf->f_ccnt; sf->f_ccnt += sf->f_extra; sf->f_extra = 0; return(tmp); } return(sf->f_ccnt); } daddr_t drblock(sf, sc) register struct dr_softf *sf; register struct dr_softc *sc; { daddr_t blkno; daddr_t *daddrs; if (sc->c_args & A_BLIST) { /* circular block list */ blkno = sc->c_blkno % sc->c_nblist; /* * if we're at the end of this buffer then * move on to the next one. */ if ((blkno % NDADDRS) == 0) { /* special case for first time only */ if (sc->c_bufno != 0) sc->c_curb = sc->c_curb->av_forw; } /* * convert the buffer data from an array of * bytes to an array of longs, then index * into that to get the block. */ daddrs = (daddr_t *) sc->c_curb->b_un.b_addr; blkno = daddrs[blkno % NDADDRS]; } else blkno = ((sf->f_todo - sf->f_dcnt) / 512) + sf->f_bno; return(blkno); } drprobe(reg) caddr_t reg; { register int br, cvec; /* value-result */ register struct drdevice *draddr; int dummy; draddr = (struct drdevice *) reg; /* according to manual an interrupt should occur if pend-bit and enable-bit are both set in Dma-status register */ draddr->port_config = pc_RESET_0 | pc_RESET_1; /* clear FIFOs */ draddr->port_config = pc_DMA_BLOCK | pc_DMA_0_EN | pc_DMA_1_EN | pc_TRANS_0_EN | pc_TRANS_1_EN | pc_TRANSCOUNT_RESET | pc_FUNBITS_EN | pc_MASTER_INT; DMAput(COMMAND_1,com_CH_1 | com_SET | com_INT_PEND_BIT | com_INT_EN_BIT | com_INT_CNTL); DELAY(40000); DMAprint(DMA_STATUS_1); DMAput(COMMAND_1,com_CH_1 | com_CLEAR | com_INT_PEND_BIT | com_INT_EN_BIT | com_INT_CNTL); draddr->status = st_CLR_DMA_PEND; /* 310 is vector for fifo interrupts, 314 for dma interrupts */ /***** cvec = 0314; /*!!!!!!!!!!!!!!!!!!!!!!!!*/ return(sizeof(struct drdevice)); } drattach(ui) struct uba_device *ui; { /*******extern int drwatch(); static int drwstart = 0;******************/ register struct dr_softc *sc; /* * set up the blist linked list * to the empty list. this must * be done first because drinit() * calls drfreeall(). */ sc = &dr_softc[ui->ui_unit]; sc->c_blist.av_forw = &sc->c_blist; sc->c_blist.av_back = &sc->c_blist; (void) drinit(ui->ui_unit); /* * start watchdog */ /****** if (drwstart == 0) { timeout(drwatch, (caddr_t) 0, hz); drwstart++; }***********/ return(0); } /* * c_wticks gets cleared on each interrupt. drwatch() * increments c_wticks and if c_wticks gets over * a threshold then the addacs are assumed to have * hung and we do a reset. */ drwatch() { register struct uba_device *ui; register struct dr_softc *sc; register int dr; /* requeue us */ /****** timeout(drwatch, (caddr_t) 0, hz); *********/ for (dr = 0; dr < 1; dr++) { if ((ui = drdinfo[dr]) == NULL) continue; if (ui->ui_alive == 0) continue; sc = &dr_softc[dr]; if ((sc->c_flags & DS_BSY) == 0) { sc->c_wticks = 0; continue; } timeout(drwatch, (caddr_t) 0, JIFFIES*hz); /* requeue only if converting*/ sc->c_wticks++; if (sc->c_wticks >= WATCHDOG) { sc->c_flags |= DS_RESET; sc->c_wticks = 0; printf("dr%d: lost interrupt\n", dr); /* ubareset(ui->ui_ubanum); */ /* ubareset seems to be choking on qe device*/ drreset(ui->ui_ubanum); } } return(0); } /* * zero uba vector. * shut off converters. * set error bit. */ drreset(uban) { register struct uba_device *ui; register struct dr_softc *sc; register int dr; register int i; for (dr = 0; dr < 1; dr++) { if ((ui = drdinfo[dr]) == NULL) continue; if (ui->ui_alive == 0) continue; if (ui->ui_ubanum != uban) continue; printf(" dr%d", dr); sc = &dr_softc[dr]; sc->c_flags &= ~ DS_LOADING; /* * this used to be after the ubarelse's, * seems like it should go before them */ if (drinit(dr)) printf(""); /* * release unibus resources */ for (i = 0; i < NDSB; i++) { if (sc->c_ubinfo[i] != 0) { printf("releasing %o\n",sc->c_ubinfo[i]); ubarelse(ui->ui_ubanum, &sc->c_ubinfo[i]); sc->c_ubinfo[i] = 0; } } } return(0); } /* Build a list of file system size block numbers just like the CARL cylinder list. All we need is the users 'fd' for the file. The list will be in units of disk blocks (512 byte blocks) not file system blocks */ build42lst(sc,sf,uio,rw) register struct dr_softc *sc; register struct dr_softf *sf; register struct uio *uio; int rw; { int i,count,loc; register struct inode *ip; register struct file *fp; register struct fs *fs; daddr_t bmap(); struct file *getinode(); struct iovec *iov = uio->uio_iov; /* Fetch inode and file system */ if((fp = getinode(sf->f_fd)) == NULL) return(EBADF); ip = (struct inode *) fp->f_data; fs = ip->i_fs; /* Validate file and file system for conversion */ if((fp->f_offset & 01) || (iov->iov_len & 01)) /* bytes */ return(EINVAL); if(fs->fs_fsize != fs->fs_bsize) /* frag != block */ return(EINVAL); if((ip->i_mode & IFMT) != IFREG) /* Nothing but normal files allowed */ return(EINVAL); /* Individual buffers (NDSB of them) are file system block size */ sf->f_bsize = fs->fs_bsize; loc = lblkno(fs,fp->f_offset); /* Start at this block */ sf->f_boff = fp->f_offset % sf->f_bsize; /* byte block offset */ sc->c_nblist = lblkno(fs,blkroundup(fs,sf->f_boff + iov->iov_len)); sf->f_todo = sf->f_icnt = iov->iov_len; sf->f_extra = fp->f_offset + iov->iov_len - ip->i_size; /* Is there requested silence? */ if(sf->f_extra > 0) sf->f_ccnt = iov->iov_len - sf->f_extra; else { sf->f_extra = 0; sf->f_ccnt = sf->f_todo; } if(drdebug) uprintf("file system blocks = %d\n",sc->c_nblist); if(sc->c_nblist <= 0) return(EBADF); count = 0; /* Flush buffers for young files (just like fsync()) */ ilock(ip); #define YOUNGTIME 60 if(ip->i_mtime + YOUNGTIME > time.tv_sec) { if(drdebug) uprintf("syncing file\n"); syncip(ip); } if(rw == B_READ) /* Record could fill in holes with bmap() */ iunlock(ip); for (i = sc->c_nblist * sizeof(daddr_t); i > 0; i -= LISTSIZE) { struct buf *bp; unsigned int amt; daddr_t tmp; bp = geteblk(LISTSIZE); clrbuf(bp); amt = i > LISTSIZE ? LISTSIZE : i; for(; amt > 0; loc++,count++ ,amt -= sizeof(daddr_t)) { /* For writes blocks will get filled in */ tmp = bmap(ip,loc,rw,fs->fs_bsize); /* File systm block */ /* #ifdef USEBBLOCK if tmp < 0 then it is an empty block and we will leave the list entry = 0 as this is the BOOT block and will have been zeroed on soundfile systems. #else USEBBLOCK if tmp < 0 then it is an empty block and we will use a known location relative to the file system that will have been zeroed on soundfile systems. #endif */ if(tmp < 0) { if(rw == B_WRITE) { if(drdebug) uprintf("Illegal empty disk blocks\n"); drfreeall(&sc->c_blist); iunlock(ip); return(EFAULT); } /* Hack for now to known block */ tmp = fs->fs_dblkno + 3; /* inode4 */ } *((daddr_t *) bp->b_un.b_addr + (count % NDADDRS)) = fsbtodb(fs,tmp); } drlink(&sc->c_blist, bp); } if(rw == B_WRITE) iunlock(ip); sc->c_curb = sc->c_blist.av_forw; sf->f_dev = ip->i_dev; sf->f_strat = bdevsw[major(ip->i_dev)].d_strategy; sc->c_args |= A_BLIST | A_BNO | A_DEV | A_CNT; return(0); }