⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dmabuf.c

📁 freebsd v4.4内核源码
💻 C
📖 第 1 页 / 共 2 页
字号:
the other case, with a preferred constant transfer size equal torec_blocksize, and fallback to smaller sizes if no space is available. * *//* * dsp_rd_dmadone moves bytes in the input buffer from DMA region to * READY region. We assume it is called at spltty() and  with dl>0  */static voiddsp_rd_dmadone(snddev_info *d){    snd_dbuf *b = & (d->dbuf_in) ;    dsp_rd_dmaupdate(b);    wakeup(b) ;	/* wakeup possibly sleeping processes */    if (b->sel.si_pid &&	    ( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) )	selwakeup( & b->sel );}/* * The following function tracks the status of a (read) dma transfer, * and moves the boundary between the READY and the DMA regions. * It works under the following assumptions: *   - the DMA engine is running; *   - the function is called with interrupts blocked. */voiddsp_rd_dmaupdate(snd_dbuf *b){    int delta, tmp ;    tmp = b->bufsize - isa_dmastatus1(b->chan) ;    tmp &= DMA_ALIGN_MASK; /* align... */    delta = tmp - b->fp;    if (delta < 0) /* wrapped */	delta += b->bufsize ;    b->fp = tmp;    b->fl -= delta ;    b->rl += delta ;    b->total += delta ;}/* * read interrupt routine. Must be called with interrupts blocked. */voiddsp_rdintr(snddev_info *d){    snd_dbuf *b = & (d->dbuf_in) ;    if (b->dl) {		/* dma was active */	b->int_count++;	dsp_rd_dmadone(d);    }    DEB(printf("dsp_rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n",	b->dl, b->rp, b->rl, b->fp, b->fl));    /*     * Restart if have enough free space to absorb overruns;     */    if ( b->fl > 0x200 &&         (d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0 &&	 ( FULL_DUPLEX(d) || (d->flags & SND_F_WRITING) == 0 ) ) {	int l = min(b->fl - 0x100, d->rec_blocksize);	l &= DMA_ALIGN_MASK ; /* realign sizes */	DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)	if (l != b->dl) {	    /* for any reason, size has changed. Stop and restart */	    if (b->dl > 0 )		d->callback(d, SND_CB_RD | SND_CB_STOP );	    b->dl = l ;	    d->callback(d, SND_CB_RD | SND_CB_START );	}    } else {	if (b->dl > 0) { /* was active */	    b->dl = 0;	    d->callback(d, SND_CB_RD | SND_CB_STOP);	}	/*	 * if switching to write, start write dma engine	 */	if ( ! FULL_DUPLEX(d) && (d->flags & SND_F_WRITING) )	    dsp_wrintr(d) ;	DEB(printf("cannot start rd-dma rl %d fl %d\n",		b->rl, b->fl));    }}/* * body of user-read routine * * Start DMA if not active; wait for READY not empty. * Transfer data from READY region using uiomove(), advance boundary * between FREE and READY. Repeat until transfer is complete. * * To avoid excessive latency in freeing up space for the DMA * engine, transfers are done in blocks of increasing size, so that * the latency is proportional to the size of the smallest block, but * we have a low overhead and are able to feed the dma engine with * large blocks. * * When we enter this routine, we assume that d->flags |= SND_F_READING * was done before. * * NOTE: in the current version, read will not return more than * blocksize bytes at once (unless more are already available), to * avoid that requests using very large buffers block for too long. */intdsp_read_body(snddev_info *d, struct uio *buf){    int limit, l, n, bsz, ret = 0 ;    long s;    snd_dbuf *b = & (d->dbuf_in) ;    /*     * "limit" serves to return after at most one blocksize of data     * (unless more are already available).  Otherwise, things like     * cat /dev/audio would use a 64K buffer and would start returning     * data after a _very_ long time...     * Note -- some applications depend on reads not returning short     * blocks. But I believe these apps are broken, since interrupted     * system calls might return short reads anyways, and the     * application should better check that.     */    if (buf->uio_resid > d->rec_blocksize)	limit = buf->uio_resid - d->rec_blocksize;    else	limit = 0;    bsz = MIN_CHUNK_SIZE ;  /* the current transfer (doubles at each step) */    while ( (n = buf->uio_resid) > limit ) {	DEB(printf("dsp_read_body: start waiting for %d bytes\n", n));        l = min (n, bsz);        s = spltty();            /* no interrupts here !             */	dsp_rd_dmaupdate(b);        l = min( l, b->rl );     /* no more than avail. data     */        if (l == 0) {	    int timeout;	    /*	     * If there is no data ready, then we must sleep (unless	     * of course we have doing non-blocking i/o). But also	     * consider restarting the DMA engine.	     */	    if ( b->dl == 0 ) { /* dma was idle, start it  */		if ( d->flags & SND_F_INIT && d->dbuf_out.dl == 0 ) {		    /* want to init and there is no pending DMA activity */		    splx(s);		    d->callback(d, SND_CB_INIT); /* this is slow! */		    s = spltty();		}		dsp_rdintr(d);	    }	    if (d->flags & SND_F_NBIO) {		splx(s);		break;	    }	    if (n-limit > b->dl)		timeout = hz; /* we need to wait for an int. */	    else		timeout = 1; /* maybe data will be ready earlier */            ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ;	    if (ret == EINTR)		d->flags |= SND_F_ABORTING ;	    splx(s);	    if (ret == EINTR || ret == ERESTART)		break ;            continue;        }        splx(s);	/*	 * Do any necessary format conversion, and copy to user space.	 * NOTE: I _can_ use rp here because it is not modified by the	 * interrupt routines.	 */	if (b->rp + l > b->bufsize) { /* handle wraparounds */	    int l1 = b->bufsize - b->rp ;	    if (d->flags & SND_F_XLAT8) {		translate_bytes(dsp_ulaw, b->buf + b->rp, l1);		translate_bytes(dsp_ulaw, b->buf , l - l1);	    }	    uiomove(b->buf + b->rp, l1, buf) ;	    uiomove(b->buf, l - l1, buf) ;	} else {	    if (d->flags & SND_F_XLAT8)		translate_bytes(dsp_ulaw, b->buf + b->rp, l);	    uiomove(b->buf + b->rp, l, buf) ;	}        s = spltty();  /* no interrupts here ... */        b->fl += l ;    /* this more free bytes */        b->rl -= l ;    /* this less ready bytes */        b->rp += l ;    /* advance ready pointer */        if (b->rp >= b->bufsize)        /* handle wraps */            b->rp -= b->bufsize ;        splx(s) ;        bsz = min(b->bufsize, bsz*2);    }    s = spltty();          /* no interrupts here ... */    d->flags &= ~SND_F_READING ;    if (d->flags & SND_F_ABORTING) {        d->flags &= ~SND_F_ABORTING; /* XXX */	splx(s);	dsp_rdabort(d, 1 /* restart */);	/* XXX return EINTR ? */    }    splx(s) ;    return ret ;}/* * short routine to initialize a dma buffer descriptor (usually * located in the XXX_desc structure). The first parameter is * the buffer size, the second one specifies that a 16-bit dma channel * is used (hence the buffer must be properly aligned). */voidalloc_dbuf(snd_dbuf *b, int size){    if (size > 0x10000)	panic("max supported size is 64k");    b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT,	    0ul, 0xfffffful, 1ul, 0x10000ul);    /* should check that malloc does not fail... */    b->rp = b->fp = 0 ;    b->dl = b->rl = 0 ;    b->bufsize = b->fl = size ;}/* * this resets a buffer and starts the isa dma on that channel. * Must be called when the dma on the card is disabled (e.g. after init). */voidreset_dbuf(snd_dbuf *b, int chan){    DEB(printf("reset dbuf for chan %d\n", b->chan));    b->rp = b->fp = 0 ;    b->dl = b->rl = 0 ;    b->fl = b->bufsize ;    if (chan == SND_CHAN_NONE)	return ;    if (chan == SND_CHAN_WR)	chan = B_WRITE | B_RAW ;    else	chan = B_READ | B_RAW ;    isa_dmastart( chan , b->buf, b->bufsize, b->chan);}/* * snd_sync waits until the space in the given channel goes above * a threshold. chan = 1 : play, 2: capture. The threshold is * checked against fl or rl respectively. * Assume that the condition can become true, do not check here... */intsnd_sync(snddev_info *d, int chan, int threshold){    u_long s;    int ret;    snd_dbuf *b;    b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ;    for (;;) {	s=spltty();        if ( chan==1 )	     dsp_wr_dmaupdate(b);	else	     dsp_rd_dmaupdate(b);	if ( (chan == 1 && b->fl <= threshold) ||	     (chan == 2 && b->rl <= threshold) ) {		 ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1);	    splx(s);	    if (ret == ERESTART || ret == EINTR) {		printf("tsleep returns %d\n", ret);		return -1 ;	    }	} else	    break;    }    splx(s);    return 0 ;}/* * dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions * which abort a pending DMA transfer and flush the buffers. * They return the number of bytes that has not been transferred. * The second parameter is used to restart the engine if needed. */intdsp_wrabort(snddev_info *d, int restart){    long s;    int missing = 0;    snd_dbuf *b = & (d->dbuf_out) ;    s = spltty();    if ( b->dl ) {	b->dl = 0 ;	d->flags &= ~ SND_F_WRITING ;	if (d->callback)	    d->callback(d, SND_CB_WR | SND_CB_ABORT);	isa_dmastop(b->chan) ;	dsp_wr_dmadone(d);	DEB(printf("dsp_wrabort: stopped, %d bytes left\n", b->rl));    }    missing = b->rl;    isa_dmadone(B_WRITE, b->buf, b->bufsize, b->chan); /*free chan */    reset_dbuf(b, restart ? SND_CHAN_WR : SND_CHAN_NONE);    splx(s);    return missing;}intdsp_rdabort(snddev_info *d, int restart){    long s;    int missing = 0;    snd_dbuf *b = & (d->dbuf_in) ;    s = spltty();    if ( b->dl ) {	b->dl = 0 ;	d->flags &= ~ SND_F_READING ;	if (d->callback)	    d->callback(d, SND_CB_RD | SND_CB_ABORT);	isa_dmastop(b->chan) ;	dsp_rd_dmadone(d);    }    missing = b->rl ;    isa_dmadone(B_READ, b->buf, b->bufsize, b->chan);    reset_dbuf(b, restart ? SND_CHAN_RD : SND_CHAN_NONE);    splx(s);    return missing;}/* * this routine tries to flush the dma transfer. It is called * on a close. The caller must set SND_F_CLOSING, and insure that * interrupts are enabled. We immediately abort any read DMA * operation, and then wait for the play buffer to drain. */intsnd_flush(snddev_info *d){    int ret, count=10;    u_long s;    snd_dbuf *b = &(d->dbuf_out) ;    DEB(printf("snd_flush d->flags 0x%08x\n", d->flags));    dsp_rdabort(d, 0 /* no restart */);    /* close write */    while ( b->dl ) {	/*	 * still pending output data.	 */	ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dmafl1", hz);	dsp_wr_dmaupdate(b);	DEB( printf("snd_sync: now rl : fl  %d : %d\n", b->rl, b->fl ) );	if (ret == EINTR) {	    printf("tsleep returns %d\n", ret);	    return -1 ;	}	if ( ret && --count == 0) {	    printf("timeout flushing dbuf_out, chan %d cnt 0x%x flags 0x%08lx\n",		    b->chan,		    b->rl, d->flags);	    break;	}    }    s = spltty(); /* should not be necessary... */    d->flags &= ~SND_F_CLOSING ;    dsp_wrabort(d, 0 /* no restart */);    splx(s);    return 0 ;}/* * end of new code for dma buffer handling */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -