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