Home | History | Annotate | Line # | Download | only in iomd
vidcaudio.c revision 1.4.2.4
      1 /*	$NetBSD: vidcaudio.c,v 1.4.2.4 2002/06/23 17:34:54 jdolecek Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995 Melvin Tang-Richardson
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *	This product includes software developed by the RiscBSD team.
     17  * 4. The name of the author may not be used to endorse or promote products
     18  *    derived from this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * audio driver for the RiscPC 16 bit sound
     34  *
     35  * Interfaces with the NetBSD generic audio driver to provide SUN
     36  * /dev/audio (partial) compatibility.
     37  */
     38 
     39 #include <sys/param.h>	/* proc.h */
     40 
     41 __KERNEL_RCSID(0, "$NetBSD: vidcaudio.c,v 1.4.2.4 2002/06/23 17:34:54 jdolecek Exp $");
     42 
     43 #include <sys/conf.h>   /* autoconfig functions */
     44 #include <sys/device.h> /* device calls */
     45 #include <sys/proc.h>	/* device calls */
     46 #include <sys/audioio.h>
     47 #include <sys/errno.h>
     48 #include <sys/systm.h>
     49 
     50 #include <uvm/uvm_extern.h>
     51 
     52 #include <dev/audio_if.h>
     53 
     54 #include <machine/intr.h>
     55 #include <arm/arm32/katelib.h>
     56 
     57 #include <arm/iomd/vidcaudiovar.h>
     58 #include <arm/iomd/iomdreg.h>
     59 #include <arm/iomd/iomdvar.h>
     60 #include <arm/iomd/vidc.h>
     61 #include <arm/mainbus/mainbus.h>
     62 #include <arm/iomd/waveform.h>
     63 #include "vidcaudio.h"
     64 
     65 extern int *vidc_base;
     66 
     67 #undef DEBUG
     68 
     69 struct audio_general {
     70 	vaddr_t silence;
     71 	irqhandler_t ih;
     72 
     73 	void (*intr) (void *);
     74 	void *arg;
     75 
     76 	paddr_t next_cur;
     77 	paddr_t next_end;
     78 	void (*next_intr) (void *);
     79 	void *next_arg;
     80 
     81 	int buffer;
     82 	int in_progress;
     83 
     84 	int open;
     85 } ag;
     86 
     87 struct vidcaudio_softc {
     88 	struct device device;
     89 	int iobase;
     90 
     91 	int open;
     92 };
     93 
     94 int  vidcaudio_probe	__P((struct device *parent, struct cfdata *cf, void *aux));
     95 void vidcaudio_attach	__P((struct device *parent, struct device *self, void *aux));
     96 int  vidcaudio_open	__P((void *addr, int flags));
     97 void vidcaudio_close	__P((void *addr));
     98 
     99 int vidcaudio_intr	__P((void *arg));
    100 int vidcaudio_dma_program	__P((vaddr_t cur, vaddr_t end, void (*intr)(void *), void *arg));
    101 void vidcaudio_dummy_routine	__P((void *arg));
    102 int vidcaudio_stereo	__P((int channel, int position));
    103 int vidcaudio_rate	__P((int rate));
    104 void vidcaudio_shutdown	__P((void));
    105 
    106 static int sound_dma_intr;
    107 
    108 struct cfattach vidcaudio_ca = {
    109 	sizeof(struct vidcaudio_softc), vidcaudio_probe, vidcaudio_attach
    110 };
    111 
    112 int    vidcaudio_query_encoding  __P((void *, struct audio_encoding *));
    113 int    vidcaudio_set_params	 __P((void *, int, int, struct audio_params *, struct audio_params *));
    114 int    vidcaudio_round_blocksize __P((void *, int));
    115 int    vidcaudio_start_output	 __P((void *, void *, int, void (*)(void *),
    116 					 void *));
    117 int    vidcaudio_start_input	 __P((void *, void *, int, void (*)(void *),
    118 					 void *));
    119 int    vidcaudio_halt_output	 __P((void *));
    120 int    vidcaudio_halt_input 	 __P((void *));
    121 int    vidcaudio_speaker_ctl	 __P((void *, int));
    122 int    vidcaudio_getdev		 __P((void *, struct audio_device *));
    123 int    vidcaudio_set_port	 __P((void *, mixer_ctrl_t *));
    124 int    vidcaudio_get_port	 __P((void *, mixer_ctrl_t *));
    125 int    vidcaudio_query_devinfo	 __P((void *, mixer_devinfo_t *));
    126 int    vidcaudio_get_props	 __P((void *));
    127 
    128 struct audio_device vidcaudio_device = {
    129 	"VidcAudio 8-bit",
    130 	"x",
    131 	"vidcaudio"
    132 };
    133 
    134 struct audio_hw_if vidcaudio_hw_if = {
    135 	vidcaudio_open,
    136 	vidcaudio_close,
    137 	0,
    138 	vidcaudio_query_encoding,
    139 	vidcaudio_set_params,
    140 	vidcaudio_round_blocksize,
    141 	0,
    142 	0,
    143 	0,
    144 	vidcaudio_start_output,
    145 	vidcaudio_start_input,
    146 	vidcaudio_halt_output,
    147 	vidcaudio_halt_input,
    148 	vidcaudio_speaker_ctl,
    149 	vidcaudio_getdev,
    150 	0,
    151 	vidcaudio_set_port,
    152 	vidcaudio_get_port,
    153 	vidcaudio_query_devinfo,
    154 	0,
    155 	0,
    156 	0,
    157 	0,
    158 	vidcaudio_get_props,
    159 	0,
    160 	0,
    161 	0,
    162 };
    163 
    164 
    165 void
    166 vidcaudio_beep_generate()
    167 {
    168 	vidcaudio_dma_program(ag.silence, ag.silence+sizeof(beep_waveform)-16,
    169 	    vidcaudio_dummy_routine, NULL);
    170 }
    171 
    172 
    173 int
    174 vidcaudio_probe(parent, cf, aux)
    175 	struct device *parent;
    176 	struct cfdata* cf;
    177 	void *aux;
    178 {
    179 	int id;
    180 
    181 	id = IOMD_ID;
    182 
    183 	/* So far I only know about this IOMD */
    184 	switch (id) {
    185 	case RPC600_IOMD_ID:
    186 		return(1);
    187 		break;
    188 	case ARM7500_IOC_ID:
    189 	case ARM7500FE_IOC_ID:
    190 		return(1);
    191 		break;
    192 	default:
    193 		printf("vidcaudio: Unknown IOMD id=%04x", id);
    194 		break;
    195 	}
    196 
    197 	return (0);
    198 }
    199 
    200 
    201 void
    202 vidcaudio_attach(parent, self, aux)
    203 	struct device *parent;
    204 	struct device *self;
    205 	void *aux;
    206 {
    207 	struct mainbus_attach_args *mb = aux;
    208 	struct vidcaudio_softc *sc = (void *)self;
    209 	int id;
    210 
    211 	sc->iobase = mb->mb_iobase;
    212 
    213 	sc->open = 0;
    214 	ag.in_progress = 0;
    215 
    216 	ag.next_cur = 0;
    217 	ag.next_end = 0;
    218 	ag.next_intr = NULL;
    219 	ag.next_arg = NULL;
    220 
    221 	vidcaudio_rate(32); /* 24*1024*/
    222 
    223 	/* Program the silence buffer and reset the DMA channel */
    224 	ag.silence = uvm_km_alloc(kernel_map, NBPG);
    225 	if (ag.silence == NULL)
    226 		panic("vidcaudio: Cannot allocate memory\n");
    227 
    228 	memset((char *)ag.silence, 0, NBPG);
    229 	memcpy((char *)ag.silence, (char *)beep_waveform, sizeof(beep_waveform));
    230 
    231 	ag.buffer = 0;
    232 
    233 	/* Install the irq handler for the DMA interrupt */
    234 	ag.ih.ih_func = vidcaudio_intr;
    235 	ag.ih.ih_arg = NULL;
    236 	ag.ih.ih_level = IPL_AUDIO;
    237 	ag.ih.ih_name = "vidcaudio";
    238 
    239 	ag.intr = NULL;
    240 /*	ag.nextintr = NULL;*/
    241 
    242 	id = IOMD_ID;
    243 
    244 	switch (id) {
    245 	case RPC600_IOMD_ID:
    246 		sound_dma_intr = IRQ_DMASCH0;
    247 		break;
    248 	case ARM7500_IOC_ID:
    249 	case ARM7500FE_IOC_ID:
    250 		sound_dma_intr = IRQ_SDMA;
    251 		break;
    252 	}
    253 
    254 	disable_irq(sound_dma_intr);
    255 
    256 	if (irq_claim(sound_dma_intr, &(ag.ih)))
    257 		panic("vidcaudio: couldn't claim IRQ %d\n", sound_dma_intr);
    258 
    259 	disable_irq(sound_dma_intr);
    260 
    261 	printf("\n");
    262 
    263 	vidcaudio_dma_program(ag.silence, ag.silence+NBPG-16,
    264 	    vidcaudio_dummy_routine, NULL);
    265 
    266 	audio_attach_mi(&vidcaudio_hw_if, sc, &sc->device);
    267 
    268 #ifdef DEBUG
    269 	printf(" UNDER DEVELOPMENT (nuts)\n");
    270 #endif
    271 }
    272 
    273 int
    274 vidcaudio_open(addr, flags)
    275 	void *addr;
    276 	int flags;
    277 {
    278 	struct vidcaudio_softc *sc = addr;
    279 
    280 #ifdef DEBUG
    281 	printf("DEBUG: vidcaudio_open called\n");
    282 #endif
    283 
    284 	if (sc->open)
    285 		return EBUSY;
    286 
    287 	sc->open = 1;
    288 	ag.open = 1;
    289 
    290 	return 0;
    291 }
    292 
    293 void
    294 vidcaudio_close(addr)
    295 	void *addr;
    296 {
    297 	struct vidcaudio_softc *sc = addr;
    298 
    299 	vidcaudio_shutdown();
    300 
    301 #ifdef DEBUG
    302 	printf("DEBUG: vidcaudio_close called\n");
    303 #endif
    304 
    305 	sc->open = 0;
    306 	ag.open = 0;
    307 }
    308 
    309 /* ************************************************************************* *
    310  | Interface to the generic audio driver                                     |
    311  * ************************************************************************* */
    312 
    313 int
    314 vidcaudio_query_encoding(addr, fp)
    315 	void *addr;
    316 	struct audio_encoding *fp;
    317 {
    318 	switch (fp->index) {
    319 	case 0:
    320 		strcpy(fp->name, "vidc");
    321 		fp->encoding = AUDIO_ENCODING_ULAW;
    322 		fp->precision = 8;
    323 		fp->flags = 0;
    324 		break;
    325 
    326 	default:
    327 		return(EINVAL);
    328 	}
    329 	return 0;
    330 }
    331 
    332 int
    333 vidcaudio_set_params(addr, setmode, usemode, p, r)
    334 	void *addr;
    335 	int setmode, usemode;
    336 	struct audio_params *p, *r;
    337 {
    338 	if (p->encoding != AUDIO_ENCODING_ULAW ||
    339 	    p->channels != 8)
    340 		return EINVAL;
    341 	vidcaudio_rate(4 * p->sample_rate / (3 * 1024)); /* XXX probably wrong */
    342 
    343 	return 0;
    344 }
    345 
    346 int
    347 vidcaudio_round_blocksize(addr, blk)
    348 	void *addr;
    349 	int blk;
    350 {
    351 	if (blk > NBPG)
    352 		blk = NBPG;
    353 	return (blk);
    354 }
    355 
    356 #define ROUND(s)  ( ((int)s) & (~(NBPG-1)) )
    357 
    358 int
    359 vidcaudio_start_output(addr, p, cc, intr, arg)
    360 	void *addr;
    361 	void *p;
    362 	int cc;
    363 	void (*intr)(void *);
    364 	void *arg;
    365 {
    366 	/* I can only DMA inside 1 page */
    367 
    368 #ifdef DEBUG
    369 	printf("vidcaudio_start_output (%d) %08x %08x\n", cc, intr, arg);
    370 #endif
    371 
    372 	if (ROUND(p) != ROUND(p+cc)) {
    373 		/*
    374 		 * If it's over a page I can fix it up by copying it into
    375 		 * my buffer
    376 		 */
    377 
    378 #ifdef DEBUG
    379 		printf("vidcaudio: DMA over page boundary requested."
    380 		    "  Fixing up\n");
    381 #endif
    382 		memcpy(p, (char *)ag.silence, cc > NBPG ? NBPG : cc);
    383 		p = (void *)ag.silence;
    384 
    385 		/*
    386 		 * I can't DMA any more than that, but it is possible to
    387 		 * fix it up by handling multiple buffers and only
    388 		 * interrupting the audio driver after sending out all the
    389 		 * stuff it gave me.  That it more than I can be bothered
    390 		 * to do right now and it probablly wont happen so I'll just
    391 		 * truncate the buffer and tell the user.
    392 		 */
    393 
    394 		if (cc > NBPG) {
    395 			printf("vidcaudio: DMA buffer truncated. I could fix this up\n");
    396 			cc = NBPG;
    397 		}
    398 	}
    399 	vidcaudio_dma_program((vaddr_t)p, (vaddr_t)((char *)p+cc),
    400 	    intr, arg);
    401 	return 0;
    402 }
    403 
    404 int
    405 vidcaudio_start_input(addr, p, cc, intr, arg)
    406 	void *addr;
    407 	void *p;
    408 	int cc;
    409 	void (*intr)(void *);
    410 	void *arg;
    411 {
    412 	return EIO;
    413 }
    414 
    415 int
    416 vidcaudio_halt_output(addr)
    417 	void *addr;
    418 {
    419 #ifdef DEBUG
    420 	printf("DEBUG: vidcaudio_halt_output\n");
    421 #endif
    422 	return EIO;
    423 }
    424 
    425 int
    426 vidcaudio_halt_input(addr)
    427 	void *addr;
    428 {
    429 #ifdef DEBUG
    430 	printf("DEBUG: vidcaudio_halt_input\n");
    431 #endif
    432 	return EIO;
    433 }
    434 
    435 int
    436 vidcaudio_speaker_ctl(addr, newstate)
    437 	void *addr;
    438 	int newstate;
    439 {
    440 #ifdef DEBUG
    441 	printf("DEBUG: vidcaudio_speaker_ctl\n");
    442 #endif
    443 	return 0;
    444 }
    445 
    446 int
    447 vidcaudio_getdev(addr, retp)
    448 	void *addr;
    449 	struct audio_device *retp;
    450 {
    451 	*retp = vidcaudio_device;
    452 	return 0;
    453 }
    454 
    455 
    456 int
    457 vidcaudio_set_port(addr, cp)
    458 	void *addr;
    459 	mixer_ctrl_t *cp;
    460 {
    461 	return EINVAL;
    462 }
    463 
    464 int
    465 vidcaudio_get_port(addr, cp)
    466 	void *addr;
    467 	mixer_ctrl_t *cp;
    468 {
    469 	return EINVAL;
    470 }
    471 
    472 int
    473 vidcaudio_query_devinfo(addr, dip)
    474 	void *addr;
    475 	mixer_devinfo_t *dip;
    476 {
    477 	return ENXIO;
    478 }
    479 
    480 int
    481 vidcaudio_get_props(addr)
    482 	void *addr;
    483 {
    484 	return 0;
    485 }
    486 void
    487 vidcaudio_dummy_routine(arg)
    488 	void *arg;
    489 {
    490 #ifdef DEBUG
    491 	printf("vidcaudio_dummy_routine\n");
    492 #endif
    493 }
    494 
    495 int
    496 vidcaudio_rate(rate)
    497 	int rate;
    498 {
    499 	WriteWord(vidc_base, VIDC_SFR | rate);
    500 	return 0;
    501 }
    502 
    503 int
    504 vidcaudio_stereo(channel, position)
    505 	int channel;
    506 	int position;
    507 {
    508 	if (channel < 0) return EINVAL;
    509 	if (channel > 7) return EINVAL;
    510 	channel = channel<<24 | VIDC_SIR0;
    511 	WriteWord(vidc_base, channel | position);
    512 	return 0;
    513 }
    514 
    515 #define PHYS(x, y) pmap_extract(pmap_kernel(), ((x)&L2_S_FRAME), (paddr_t *)(y))
    516 
    517 /*
    518  * Program the next buffer to be used
    519  * This function must be re-entrant, maximum re-entrancy of 2
    520  */
    521 
    522 #define FLAGS (0)
    523 
    524 int
    525 vidcaudio_dma_program(cur, end, intr, arg)
    526 	vaddr_t cur;
    527 	vaddr_t end;
    528 	void (*intr)(void *);
    529 	void *arg;
    530 {
    531 	paddr_t pa1, pa2;
    532 
    533 	/* If there isn't a transfer in progress then start a new one */
    534 	if (ag.in_progress == 0) {
    535 		ag.buffer = 0;
    536 		IOMD_WRITE_WORD(IOMD_SD0CR, 0x90);	/* Reset State Machine */
    537 		IOMD_WRITE_WORD(IOMD_SD0CR, 0x30);	/* Reset State Machine */
    538 
    539 		PHYS(cur, &pa1);
    540 		PHYS(end - 16, &pa2);
    541 
    542 		IOMD_WRITE_WORD(IOMD_SD0CURB, pa1);
    543 		IOMD_WRITE_WORD(IOMD_SD0ENDB, pa2|FLAGS);
    544 		IOMD_WRITE_WORD(IOMD_SD0CURA, pa1);
    545 		IOMD_WRITE_WORD(IOMD_SD0ENDA, pa2|FLAGS);
    546 
    547 		ag.in_progress = 1;
    548 
    549 		ag.next_cur = ag.next_end = 0;
    550 		ag.next_intr = ag.next_arg = 0;
    551 
    552 		ag.intr = intr;
    553 		ag.arg = arg;
    554 
    555 		/*
    556 		 * The driver 'clicks' between buffer swaps, leading me
    557 		 * to think  that the fifo is much small than on other
    558 		 * sound cards so I'm going to have to do some tricks here
    559 		 */
    560 
    561 		(*ag.intr)(ag.arg);			/* Schedule the next buffer */
    562 		ag.intr = vidcaudio_dummy_routine;	/* Already done this        */
    563 		ag.arg = NULL;
    564 
    565 #ifdef PRINT
    566 		printf("vidcaudio: start output\n");
    567 #endif
    568 #ifdef DEBUG
    569 		printf("SE");
    570 #endif
    571 		enable_irq(sound_dma_intr);
    572 	} else {
    573 		/* Otherwise schedule the next one */
    574 		if (ag.next_cur != 0) {
    575 			/* If there's one scheduled then complain */
    576 			printf("vidcaudio: Buffer already Q'ed\n");
    577 			return EIO;
    578 		} else {
    579 			/* We're OK to schedule it now */
    580 			ag.buffer = (++ag.buffer) & 1;
    581 			PHYS(cur, &ag.next_cur);
    582 			PHYS(end - 16, &ag.next_end);
    583 			ag.next_intr = intr;
    584 			ag.next_arg = arg;
    585 #ifdef DEBUG
    586 			printf("s");
    587 #endif
    588 		}
    589 	}
    590 	return 0;
    591 }
    592 
    593 void
    594 vidcaudio_shutdown(void)
    595 {
    596 	/* Shut down the channel */
    597 	ag.intr = NULL;
    598 	ag.in_progress = 0;
    599 #ifdef PRINT
    600 	printf("vidcaudio: stop output\n");
    601 #endif
    602 	IOMD_WRITE_WORD(IOMD_SD0CURB, ag.silence);
    603 	IOMD_WRITE_WORD(IOMD_SD0ENDB, (ag.silence + NBPG - 16) | (1<<30));
    604 	disable_irq(sound_dma_intr);
    605 }
    606 
    607 int
    608 vidcaudio_intr(arg)
    609 	void *arg;
    610 {
    611 	int status = IOMD_READ_BYTE(IOMD_SD0ST);
    612 	void (*nintr)(void *);
    613 	void *narg;
    614 	void (*xintr)(void *);
    615 	void *xarg;
    616 	int xcur, xend;
    617 	IOMD_WRITE_WORD(IOMD_DMARQ, 0x10);
    618 
    619 #ifdef PRINT
    620 	printf ( "I" );
    621 #endif
    622 
    623 	if (ag.open == 0) {
    624 		vidcaudio_shutdown();
    625 		return 0;
    626 	}
    627 
    628 	/* Have I got the generic audio device attached */
    629 
    630 #ifdef DEBUG
    631 	printf ( "[B%01x]", status );
    632 #endif
    633 
    634 	nintr = ag.intr;
    635 	narg = ag.arg;
    636 	ag.intr = NULL;
    637 
    638 	xintr = ag.next_intr;
    639 	xarg = ag.next_arg;
    640 	xcur = ag.next_cur;
    641 	xend = ag.next_end;
    642 	ag.next_cur = 0;
    643 	ag.intr = xintr;
    644 	ag.arg = xarg;
    645 
    646 	if (nintr) {
    647 #ifdef DEBUG
    648 		printf("i");
    649 #endif
    650 		(*nintr)(narg);
    651 	}
    652 
    653 	if (xcur == 0) {
    654 		vidcaudio_shutdown ();
    655 	} else {
    656 #define OVERRUN 	(0x04)
    657 #define INTERRUPT	(0x02)
    658 #define BANK_A		(0x00)
    659 #define BANK_B		(0x01)
    660 		switch (status & 0x7) {
    661 		case (INTERRUPT|BANK_A):
    662 #ifdef PRINT
    663 			printf("B");
    664 #endif
    665 			IOMD_WRITE_WORD(IOMD_SD0CURB, xcur);
    666 			IOMD_WRITE_WORD(IOMD_SD0ENDB, xend|FLAGS);
    667 			break;
    668 
    669 		case (INTERRUPT|BANK_B):
    670 #ifdef PRINT
    671 			printf("A");
    672 #endif
    673 			IOMD_WRITE_WORD(IOMD_SD0CURA, xcur);
    674 			IOMD_WRITE_WORD(IOMD_SD0ENDA, xend|FLAGS);
    675 			break;
    676 
    677 		case (OVERRUN|INTERRUPT|BANK_A):
    678 #ifdef PRINT
    679 			printf("A");
    680 #endif
    681 			IOMD_WRITE_WORD(IOMD_SD0CURA, xcur);
    682 			IOMD_WRITE_WORD(IOMD_SD0ENDA, xend|FLAGS);
    683 			break;
    684 
    685 		case (OVERRUN|INTERRUPT|BANK_B):
    686 #ifdef PRINT
    687 			printf("B");
    688 #endif
    689 			IOMD_WRITE_WORD(IOMD_SD0CURB, xcur);
    690 			IOMD_WRITE_WORD(IOMD_SD0ENDB, xend|FLAGS);
    691 			break;
    692 		}
    693 /*
    694 	ag.next_cur = 0;
    695 	ag.intr = xintr;
    696 	ag.arg = xarg;
    697 */
    698 	}
    699 #ifdef PRINT
    700 	printf ( "i" );
    701 #endif
    702 
    703 	if (ag.next_cur == 0) {
    704 		(*ag.intr)(ag.arg);			/* Schedule the next buffer */
    705 		ag.intr = vidcaudio_dummy_routine;	/* Already done this        */
    706 		ag.arg = NULL;
    707 	}
    708 	return(0);	/* Pass interrupt on down the chain */
    709 }
    710