xref: /csrg-svn/sys/sparc/dev/bsd_audio.c (revision 55499)
155102Storek /*
255102Storek  * Copyright (c) 1991, 1992 The Regents of the University of California.
355102Storek  * All rights reserved.
455102Storek  *
555102Storek  * This software was developed by the Computer Systems Engineering group
655102Storek  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
755102Storek  * contributed to Berkeley.
855102Storek  *
9*55499Sbostic  * All advertising materials mentioning features or use of this software
10*55499Sbostic  * must display the following acknowledgement:
11*55499Sbostic  *	This product includes software developed by the University of
12*55499Sbostic  *	California, Lawrence Berkeley Laboratories.
13*55499Sbostic  *
1455102Storek  * %sccs.include.redist.c%
1555102Storek  *
16*55499Sbostic  *	@(#)bsd_audio.c	7.2 (Berkeley) 07/21/92
1755102Storek  *
1855102Storek  * from: $Header: bsd_audio.c,v 1.14 92/07/03 23:21:23 mccanne Exp $ (LBL)
1955102Storek  */
2055102Storek #include "bsdaudio.h"
2155102Storek #if NBSDAUDIO > 0
2255102Storek 
2355102Storek #include "sys/param.h"
2455102Storek #include "sys/systm.h"
2555102Storek 
2655102Storek #if BSD < 199103
2755102Storek #ifndef SUNOS
2855102Storek #define SUNOS
2955102Storek #endif
3055102Storek #endif
3155102Storek 
3255102Storek #include "sys/errno.h"
3355102Storek #include "sys/file.h"
3455102Storek #include "sys/proc.h"
3555102Storek #include "sys/user.h"
3655102Storek #include "sys/vnode.h"
3755102Storek #include "sys/ioctl.h"
3855102Storek #include "sys/time.h"
3955102Storek #ifndef SUNOS
4055102Storek #include "sys/tty.h"
4155102Storek #endif
4255102Storek #include "sys/uio.h"
4355102Storek 
4455102Storek #ifdef SUNOS
4555102Storek #include <sundev/mbvar.h>
4655102Storek #include <sun4c/intreg.h>
4755102Storek #else
4855102Storek #include "sys/device.h"
4955102Storek #include "machine/autoconf.h"
5055102Storek #endif
5155102Storek #include "machine/cpu.h"
5255102Storek 
5355102Storek /*
5455102Storek  * Avoid name clashes with SunOS so we can config either the bsd or sun
5555102Storek  * streams driver in a SunOS kernel.
5655102Storek  */
5755102Storek #ifdef SUNOS
5855102Storek #include "sbusdev/bsd_audioreg.h"
5955102Storek #include "sbusdev/bsd_audiovar.h"
6055102Storek #include "sbusdev/bsd_audioio.h"
6155102Storek struct selinfo {
6255102Storek 	struct proc *si_proc;
6355102Storek 	int si_coll;
6455102Storek };
6555102Storek #else
6655102Storek #include "../dev/bsd_audioreg.h"
6755102Storek #include "../dev/bsd_audiovar.h"
6855102Storek #include "machine/bsd_audioio.h"
6955102Storek #endif
7055102Storek 
7155102Storek #ifdef SUNOS
7255102Storek #include "bsd_audiocompat.h"
7355102Storek #endif
7455102Storek 
7555102Storek /*
7655102Storek  * Initial/default block size is patchable.
7755102Storek  */
7855102Storek int audio_blocksize = DEFBLKSIZE;
7955102Storek 
8055102Storek /*
8155102Storek  * Software state, per AMD79C30 audio chip.
8255102Storek  */
8355102Storek struct audio_softc {
8455102Storek #ifndef SUNOS
8555102Storek 	struct	device sc_dev;		/* base device */
8655102Storek 	struct	intrhand sc_hwih;	/* hardware interrupt vector */
8755102Storek 	struct	intrhand sc_swih;	/* software interrupt vector */
8855102Storek #endif
8955102Storek 	int	sc_interrupts;		/* number of interrupts taken */
9055102Storek 
9155102Storek 	int	sc_open;		/* single use device */
9255102Storek 	u_long	sc_wseek;		/* timestamp of last frame written */
9355102Storek 	u_long	sc_rseek;		/* timestamp of last frame read */
9455102Storek 	struct	mapreg sc_map;		/* current contents of map registers */
9555102Storek 	struct	selinfo sc_wsel;	/* write selector */
9655102Storek 	struct	selinfo sc_rsel;	/* read selector */
9755102Storek 	/*
9855102Storek 	 * keep track of levels so we don't have to convert back from
9955102Storek 	 * MAP gain constants
10055102Storek 	 */
10155102Storek 	int	sc_rlevel;		/* record level */
10255102Storek 	int	sc_plevel;		/* play level */
10355102Storek 	int	sc_mlevel;		/* monitor level */
10455102Storek 
10555102Storek 	/* sc_au is special in that the hardware interrupt handler uses it */
10655102Storek 	struct	auio sc_au;		/* recv and xmit buffers, etc */
10755102Storek 
10855102Storek };
10955102Storek 
11055102Storek /* interrupt interfaces */
11155102Storek #ifndef AUDIO_C_HANDLER
11255102Storek int	audiohwintr __P((void *));
11355102Storek #endif
11455102Storek int	audioswintr __P((void *));
11555102Storek 
11655102Storek /* forward declarations */
11755102Storek int	audio_sleep __P((struct aucb *, int));
11855102Storek void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
11955102Storek 
12055102Storek static void init_amd();
12155102Storek 
12255102Storek #if !defined(AUDIO_C_HANDLER) || defined(SUNOS)
12355102Storek struct auio *audio_au;
12455102Storek extern void audio_trap();
12555102Storek #endif
12655102Storek 
12755102Storek #ifdef SUNOS
12855102Storek struct audio_softc audio_softc;
12955102Storek #define SOFTC(dev) &audio_softc
13055102Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio)
13155102Storek 
13255102Storek #define AUDIOOPEN(d, f, i, p)\
13355102Storek 	audioopen(d, f, i)\
13455102Storek 	dev_t d; int f, i;
13555102Storek #define AUDIOCLOSE(d, f, i, p)\
13655102Storek 	audioclose(d, f, i)\
13755102Storek 	dev_t d; int f, i;
13855102Storek #define AUDIOREAD(d, u, f) \
13955102Storek 	audioread(d, u) dev_t d; struct uio *u;
14055102Storek #define AUDIOWRITE(d, u, f) \
14155102Storek 	audiowrite(d, u) dev_t d; struct uio *u;
14255102Storek #define AUDIOIOCTL(d, c, a, f, o)\
14355102Storek 	audioioctl(d, c, a, f)\
14455102Storek 	dev_t d; int c; caddr_t a; int f;
14555102Storek #define AUDIOSELECT(d, r, p)\
14655102Storek 	audio_select(d, r, p)\
14755102Storek 	dev_t d; int r; struct proc *p;
14855102Storek 
14955102Storek 
15055102Storek #define AUDIO_SET_SWINTR set_intreg(IR_SOFT_INT4, 1)
15155102Storek 
15255102Storek int
15355102Storek audioselect(dev, rw)
15455102Storek 	register dev_t dev;
15555102Storek 	int rw;
15655102Storek {
15755102Storek 	return (audio_select(dev, rw, u.u_procp));
15855102Storek }
15955102Storek 
16055102Storek static void
16155102Storek selrecord(p, si)
16255102Storek 	struct proc *p;
16355102Storek 	struct selinfo *si;
16455102Storek {
16555102Storek 	if (si->si_proc != 0)
16655102Storek 		si->si_coll = 1;
16755102Storek 	else
16855102Storek 		si->si_proc = p;
16955102Storek }
17055102Storek #define SELWAKEUP(si) \
17155102Storek {\
17255102Storek 	 if ((si)->si_proc != 0) {\
17355102Storek 		selwakeup((si)->si_proc, (si)->si_coll); \
17455102Storek 		(si)->si_proc = 0;\
17555102Storek 		(si)->si_coll = 0;\
17655102Storek 	}\
17755102Storek }
17855102Storek 
17955102Storek 
18055102Storek static int audioattach();
18155102Storek static int audioidentify();
18255102Storek 
18355102Storek struct dev_ops bsdaudio_ops = {
18455102Storek 	0,
18555102Storek 	audioidentify,
18655102Storek 	audioattach,
18755102Storek };
18855102Storek 
18955102Storek static int
19055102Storek audioidentify(cp)
19155102Storek 	char *cp;
19255102Storek {
19355102Storek 	return (strcmp(cp, "audio") == 0);
19455102Storek }
19555102Storek 
19655102Storek static int
19755102Storek audioattach(dev)
19855102Storek 	struct dev_info *dev;
19955102Storek {
20055102Storek 	register struct audio_softc *sc;
20155102Storek 	register volatile struct amd7930 *amd;
20255102Storek 	struct dev_reg *reg;
20355102Storek 
20455102Storek 	sc = &audio_softc;
20555102Storek 	if (dev->devi_nreg != 1 || dev->devi_nintr != 1) {
20655102Storek 		printf("audio: bad config\n");
20755102Storek                 return (-1);
20855102Storek         }
20955102Storek 	reg = dev->devi_reg;
21055102Storek 	amd = (struct amd7930 *)map_regs(reg->reg_addr, reg->reg_size,
21155102Storek 					 reg->reg_bustype);
21255102Storek 	sc->sc_au.au_amd = amd;
21355102Storek 	init_amd(amd);
21455102Storek 
21555102Storek 	audio_au = &sc->sc_au;
21655102Storek #ifndef AUDIO_C_HANDLER
21755102Storek 	settrap(dev->devi_intr->int_pri, audio_trap);
21855102Storek #else
21955102Storek 	/* XXX */
22055102Storek 	addintr(dev->devi_intr->int_pri, audiohwintr, dev->devi_name,
22155102Storek 		dev->devi_unit);
22255102Storek #endif
22355102Storek 	addintr(4, audioswintr, dev->devi_name, dev->devi_unit);
22455102Storek 	report_dev(dev);
22555102Storek 
22655102Storek 	return (0);
22755102Storek }
22855102Storek #else
22955102Storek #define AUDIOOPEN(d, f, i, p) audioopen(dev_t d, int f, int i, struct proc *p)
23055102Storek #define AUDIOCLOSE(d, f, i, p) audioclose(dev_t d, int f, int i, \
23155102Storek 					  struct proc *p)
23255102Storek #define AUDIOREAD(d, u, f) audioread(dev_t d, struct uio *u, int f)
23355102Storek #define AUDIOWRITE(d, u, f) audiowrite(dev_t d, struct uio *u, int f)
23455102Storek #define AUDIOIOCTL(d, c, a, f, o)\
23555102Storek 	audioioctl(dev_t dev, int c, caddr_t a, int f, struct proc *p)
23655102Storek #define AUDIOSELECT(d, r, p) audioselect(dev_t dev, int rw, struct proc *p)
23755102Storek #define SELWAKEUP selwakeup
23855102Storek 
23955102Storek #define AUDIO_SET_SWINTR ienab_bis(IE_L6)
24055102Storek 
24155102Storek /* autoconfiguration driver */
24255102Storek void	audioattach(struct device *, struct device *, void *);
24355102Storek struct	cfdriver audiocd =
24455102Storek     { NULL, "audio", matchbyname, audioattach,
24555102Storek       DV_DULL, sizeof(struct audio_softc) };
24655102Storek #define SOFTC(dev) audiocd.cd_devs[minor(dev)]
24755102Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio)
24855102Storek 
24955102Storek /*
25055102Storek  * Audio chip found.
25155102Storek  */
25255102Storek void
25355102Storek audioattach(parent, self, args)
25455102Storek 	struct device *parent, *self;
25555102Storek 	void *args;
25655102Storek {
25755102Storek 	register struct audio_softc *sc = (struct audio_softc *)self;
25855102Storek 	register struct romaux *ra = args;
25955102Storek 	register volatile struct amd7930 *amd;
26055102Storek 	register int pri;
26155102Storek 
26255102Storek 	if (ra->ra_nintr != 1) {
26355102Storek 		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
26455102Storek 		return;
26555102Storek 	}
26655102Storek 	pri = ra->ra_intr[0].int_pri;
26755102Storek 	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
26855102Storek 	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
26955102Storek 	    ra->ra_vaddr : mapiodev(ra->ra_paddr, sizeof *amd));
27055102Storek 	sc->sc_au.au_amd = amd;
27155102Storek 
27255102Storek 	init_amd(amd);
27355102Storek 
27455102Storek #ifndef AUDIO_C_HANDLER
27555102Storek 	audio_au = &sc->sc_au;
27655102Storek 	intr_fasttrap(pri, audio_trap);
27755102Storek #else
27855102Storek 	sc->sc_hwih.ih_fun = audiohwintr;
27955102Storek 	sc->sc_hwih.ih_arg = &sc->sc_au;
28055102Storek 	intr_establish(pri, &sc->sc_hwih);
28155102Storek #endif
28255102Storek 	sc->sc_swih.ih_fun = audioswintr;
28355102Storek 	sc->sc_swih.ih_arg = sc;
28455102Storek 	intr_establish(PIL_AUSOFT, &sc->sc_swih);
28555102Storek }
28655102Storek #endif
28755102Storek 
28855102Storek static void
28955102Storek init_amd(amd)
29055102Storek 	register volatile struct amd7930 *amd;
29155102Storek {
29255102Storek 	/* disable interrupts */
29355102Storek 	amd->cr = AMDR_INIT;
29455102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
29555102Storek 
29655102Storek 	/*
29755102Storek 	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
29855102Storek 	 * through channel Bb.  MCR1 and MCR2 are unused.
29955102Storek 	 * Setting the INT enable bit in MCR4 will generate an interrupt
30055102Storek 	 * on each converted audio sample.
30155102Storek 	 */
30255102Storek 	amd->cr = AMDR_MUX_1_4;
30355102Storek  	amd->dr = 0;
30455102Storek 	amd->dr = 0;
30555102Storek 	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
30655102Storek 	amd->dr = AMD_MCR4_INT_ENABLE;
30755102Storek }
30855102Storek 
30955102Storek static int audio_default_level = 150;
31055102Storek static void ausetrgain __P((struct audio_softc *, int));
31155102Storek static void ausetpgain __P((struct audio_softc *, int));
31255102Storek static int audiosetinfo __P((struct audio_softc *, struct audio_info *));
31355102Storek static int audiogetinfo __P((struct audio_softc *, struct audio_info *));
31455102Storek struct sun_audio_info;
31555102Storek static int sunaudiosetinfo __P((struct audio_softc *,
31655102Storek 				struct sun_audio_info *));
31755102Storek static int sunaudiogetinfo __P((struct audio_softc *,
31855102Storek 				struct sun_audio_info *));
31955102Storek static void audio_setmmr2 __P((volatile struct amd7930 *, int));
32055102Storek 
32155102Storek int
32255102Storek AUDIOOPEN(dev, flags, ifmt, p)
32355102Storek {
32455102Storek 	register struct audio_softc *sc;
32555102Storek 	register volatile struct amd7930 *amd;
32655102Storek 	int unit = minor(dev), error, s;
32755102Storek 
32855102Storek #ifdef SUNOS
32955102Storek 	if (unit > 0)
33055102Storek 		return (ENXIO);
33155102Storek 	sc = &audio_softc;
33255102Storek #else
33355102Storek 	if (unit >= audiocd.cd_ndevs || (sc = audiocd.cd_devs[unit]) == NULL)
33455102Storek 		return (ENXIO);
33555102Storek #endif
33655102Storek 	if (sc->sc_open)
33755102Storek 		return (EBUSY);
33855102Storek 	sc->sc_open = 1;
33955102Storek 
34055102Storek 	sc->sc_au.au_lowat = audio_blocksize;
34155102Storek 	sc->sc_au.au_hiwat = AUCB_SIZE - sc->sc_au.au_lowat;
34255102Storek 	sc->sc_au.au_blksize = audio_blocksize;
34355102Storek 
34455102Storek 	/* set up read and write blocks and `dead sound' zero value. */
34555102Storek 	AUCB_INIT(&sc->sc_au.au_rb);
34655102Storek 	sc->sc_au.au_rb.cb_thresh = AUCB_SIZE;
34755102Storek 	AUCB_INIT(&sc->sc_au.au_wb);
34855102Storek 	sc->sc_au.au_wb.cb_thresh = -1;
34955102Storek 
35055102Storek 	/* nothing read or written yet */
35155102Storek 	sc->sc_rseek = 0;
35255102Storek 	sc->sc_wseek = 0;
35355102Storek 
35455102Storek 	bzero((char *)&sc->sc_map, sizeof sc->sc_map);
35555102Storek 	/* default to speaker */
35655102Storek 	sc->sc_map.mr_mmr2 = AMD_MMR2_AINB | AMD_MMR2_LS;
35755102Storek 
35855102Storek 	/* enable interrupts and set parameters established above */
35955102Storek 	amd = sc->sc_au.au_amd;
36055102Storek 	audio_setmmr2(amd, sc->sc_map.mr_mmr2);
36155102Storek 	ausetrgain(sc, audio_default_level);
36255102Storek 	ausetpgain(sc, audio_default_level);
36355102Storek 	amd->cr = AMDR_INIT;
36455102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE;
36555102Storek 
36655102Storek 	return (0);
36755102Storek }
36855102Storek 
36955102Storek static int
37055102Storek audio_drain(sc)
37155102Storek 	register struct audio_softc *sc;
37255102Storek {
37355102Storek 	register int error;
37455102Storek 
37555102Storek 	while (!AUCB_EMPTY(&sc->sc_au.au_wb))
37655102Storek 		if ((error = audio_sleep(&sc->sc_au.au_wb, 0)) != 0)
37755102Storek 			return (error);
37855102Storek 	return (0);
37955102Storek }
38055102Storek 
38155102Storek /*
38255102Storek  * Close an audio chip.
38355102Storek  */
38455102Storek /* ARGSUSED */
38555102Storek int
38655102Storek AUDIOCLOSE(dev, flags, ifmt, p)
38755102Storek {
38855102Storek 	register struct audio_softc *sc = SOFTC(dev);
38955102Storek 	register volatile struct amd7930 *amd;
39055102Storek 	register struct aucb *cb;
39155102Storek 	register int s;
39255102Storek 
39355102Storek 	/*
39455102Storek 	 * Block until output drains, but allow ^C interrupt.
39555102Storek 	 */
39655102Storek 	sc->sc_au.au_lowat = 0;	/* avoid excessive wakeups */
39755102Storek 	s = splaudio();
39855102Storek 	/*
39955102Storek 	 * If there is pending output, let it drain (unless
40055102Storek 	 * the output is paused).
40155102Storek 	 */
40255102Storek 	cb = &sc->sc_au.au_wb;
40355102Storek 	if (!AUCB_EMPTY(cb) && !cb->cb_pause)
40455102Storek 		(void)audio_drain(sc);
40555102Storek 	/*
40655102Storek 	 * Disable interrupts, clear open flag, and done.
40755102Storek 	 */
40855102Storek 	amd = sc->sc_au.au_amd;
40955102Storek 	amd->cr = AMDR_INIT;
41055102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
41155102Storek 	splx(s);
41255102Storek 	sc->sc_open = 0;
41355102Storek 	return (0);
41455102Storek }
41555102Storek 
41655102Storek int
41755102Storek audio_sleep(cb, thresh)
41855102Storek 	register struct aucb *cb;
41955102Storek 	register int thresh;
42055102Storek {
42155102Storek 	register int error;
42255102Storek 
42355102Storek 	cb->cb_thresh = thresh;
42455102Storek 	error = tsleep((caddr_t)cb, (PZERO + 1) | PCATCH, "audio", 0);
42555102Storek 	return (error);
42655102Storek }
42755102Storek 
42855102Storek int
42955102Storek AUDIOREAD(dev, uio, ioflag)
43055102Storek {
43155102Storek 	register struct audio_softc *sc = SOFTC(dev);
43255102Storek 	register struct aucb *cb;
43355102Storek 	register int s, n, head, taildata, error;
43455102Storek 	register int blocksize = sc->sc_au.au_blksize;
43555102Storek 
43655102Storek 	if (uio->uio_resid == 0)
43755102Storek 		return (0);
43855102Storek 	cb = &sc->sc_au.au_rb;
43955102Storek 	error = 0;
44055102Storek 	s = splaudio();
44155102Storek 	cb->cb_drops = 0;
44255102Storek 	sc->sc_rseek = sc->sc_au.au_stamp - AUCB_LEN(cb);
44355102Storek 	do {
44455102Storek 		while (AUCB_LEN(cb) < blocksize) {
44555102Storek #ifndef SUNOS
44655102Storek 			if (ioflag & IO_NDELAY) {
44755102Storek 				error = EWOULDBLOCK;
44855102Storek 				goto out;
44955102Storek 			}
45055102Storek #endif
45155102Storek 			if ((error = audio_sleep(cb, blocksize)) != 0)
45255102Storek 				goto out;
45355102Storek 		}
45455102Storek 		splx(s);
45555102Storek 		/*
45655102Storek 		 * The space calculation can only err on the short
45755102Storek 		 * side if an interrupt occurs during processing:
45855102Storek 		 * only cb_tail is altered in the interrupt code.
45955102Storek 		 */
46055102Storek 		head = cb->cb_head;
46155102Storek 		if ((n = AUCB_LEN(cb)) > uio->uio_resid)
46255102Storek 			n = uio->uio_resid;
46355102Storek 		taildata = AUCB_SIZE - head;
46455102Storek 		if (n > taildata) {
46555102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head,
46655102Storek 					taildata, UIO_READ, uio);
46755102Storek 			if (error == 0)
46855102Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
46955102Storek 						n - taildata, UIO_READ, uio);
47055102Storek 		} else
47155102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head, n,
47255102Storek 					UIO_READ, uio);
47355102Storek 		if (error)
47455102Storek 			return (error);
47555102Storek 		head = AUCB_MOD(head + n);
47655102Storek 		(void) splaudio();
47755102Storek 		cb->cb_head = head;
47855102Storek 	} while (uio->uio_resid >= blocksize);
47955102Storek out:
48055102Storek 	splx(s);
48155102Storek 	return (error);
48255102Storek }
48355102Storek 
48455102Storek int
48555102Storek AUDIOWRITE(dev, uio, ioflag)
48655102Storek {
48755102Storek 	register struct audio_softc *sc = SOFTC(dev);
48855102Storek 	register struct aucb *cb = &sc->sc_au.au_wb;
48955102Storek 	register int s, n, tail, tailspace, error, first, watermark, drops;
49055102Storek 
49155102Storek 	error = 0;
49255102Storek 	first = 1;
49355102Storek 	s = splaudio();
49455102Storek 	while (uio->uio_resid > 0) {
49555102Storek 		watermark = sc->sc_au.au_hiwat;
49655102Storek 		while (AUCB_LEN(cb) > watermark) {
49755102Storek #ifndef SUNOS
49855102Storek 			if (ioflag & IO_NDELAY) {
49955102Storek 				error = EWOULDBLOCK;
50055102Storek 				goto out;
50155102Storek 			}
50255102Storek #endif
50355102Storek 			if ((error = audio_sleep(cb, watermark)) != 0)
50455102Storek 				goto out;
50555102Storek 			watermark = sc->sc_au.au_lowat;
50655102Storek 		}
50755102Storek 		splx(s);
50855102Storek 		/*
50955102Storek 		 * The only value that can change on an interrupt is
51055102Storek 		 * cb->cb_head.  We only pull that out once to decide
51155102Storek 		 * how much to write into cb_data; if we lose a race
51255102Storek 		 * and cb_head changes, we will merely be overly
51355102Storek 		 * conservative.  For a legitimate time stamp,
51455102Storek 		 * however, we need to synchronize the accesses to
51555102Storek 		 * au_stamp and cb_head at a high ipl below.
51655102Storek 		 */
51755102Storek 		if ((n = AUCB_SIZE - AUCB_LEN(cb) - 1) > uio->uio_resid)
51855102Storek 			n = uio->uio_resid;
51955102Storek 		tail = cb->cb_tail;
52055102Storek 		tailspace = AUCB_SIZE - tail;
52155102Storek 		if (n > tailspace) {
52255102Storek 			/* write first part at tail and rest at head */
52355102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail,
52455102Storek 					tailspace, UIO_WRITE, uio);
52555102Storek 			if (error == 0)
52655102Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
52755102Storek 						n - tailspace, UIO_WRITE, uio);
52855102Storek 		} else
52955102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail, n,
53055102Storek 					UIO_WRITE, uio);
53155102Storek 		if (error)
53255102Storek 			return (error);
53355102Storek 		/*
53455102Storek 		 * We cannot do this outside the loop because if the
53555102Storek 		 * buffer is empty, an indeterminate amount of time
53655102Storek 		 * will pass before the output starts to drain.
53755102Storek 		 */
53855102Storek 		(void)splaudio();
53955102Storek 		tail = AUCB_MOD(tail + n);
54055102Storek 		if (first) {
54155102Storek 			first = 0;
54255102Storek 			sc->sc_wseek = sc->sc_au.au_stamp + AUCB_LEN(cb) + 1;
54355102Storek 			/*
54455102Storek 			 * To guarantee that a write is contiguous in the
54555102Storek 			 * sample space, we clear the drop count the first
54655102Storek 			 * time through.  If we later get drops, we will
54755102Storek 			 * break out of the loop below, before writing
54855102Storek 			 * a new frame.
54955102Storek 			 * XXX I think we're one iteration too late!
55055102Storek 			 */
55155102Storek 			cb->cb_drops = 0;
55255102Storek 		}
55355102Storek 		cb->cb_tail = tail;
55455102Storek 		if (cb->cb_drops != 0)
55555102Storek 			break;
55655102Storek 	}
55755102Storek out:
55855102Storek 	splx(s);
55955102Storek 	return (error);
56055102Storek }
56155102Storek 
56255102Storek /* Sun audio compatibility */
56355102Storek struct sun_audio_prinfo {
56455102Storek 	u_int	sample_rate;
56555102Storek 	u_int	channels;
56655102Storek 	u_int	precision;
56755102Storek 	u_int	encoding;
56855102Storek 	u_int	gain;
56955102Storek 	u_int	port;
57055102Storek 	u_int	reserved0[4];
57155102Storek 	u_int	samples;
57255102Storek 	u_int	eof;
57355102Storek 	u_char	pause;
57455102Storek 	u_char	error;
57555102Storek 	u_char	waiting;
57655102Storek 	u_char	reserved1[3];
57755102Storek 	u_char	open;
57855102Storek 	u_char	active;
57955102Storek };
58055102Storek struct sun_audio_info {
58155102Storek 	struct sun_audio_prinfo play;
58255102Storek 	struct sun_audio_prinfo record;
58355102Storek 	u_int monitor_gain;
58455102Storek 	u_int reserved[4];
58555102Storek };
58655102Storek 
58755102Storek #ifndef SUNOS
58855102Storek #define SUNAUDIO_GETINFO	_IOR('A', 1, struct sun_audio_info)
58955102Storek #define SUNAUDIO_SETINFO	_IOWR('A', 2, struct sun_audio_info)
59055102Storek #else
59155102Storek #define SUNAUDIO_GETINFO	_IOR(A, 1, struct sun_audio_info)
59255102Storek #define SUNAUDIO_SETINFO	_IOWR(A, 2, struct sun_audio_info)
59355102Storek #endif
59455102Storek 
59555102Storek int
59655102Storek AUDIOIOCTL(dev, cmd, addr, flag, p)
59755102Storek {
59855102Storek 	register struct audio_softc *sc = SOFTC(dev);
59955102Storek 	int error = 0, i, s;
60055102Storek 
60155102Storek 	switch (cmd) {
60255102Storek 
60355102Storek 	case AUDIO_GETMAP:
60455102Storek 		bcopy((caddr_t)&sc->sc_map, addr, sizeof(sc->sc_map));
60555102Storek 		break;
60655102Storek 
60755102Storek 	case AUDIO_SETMAP:
60855102Storek 		bcopy(addr, (caddr_t)&sc->sc_map, sizeof(sc->sc_map));
60955102Storek 		sc->sc_map.mr_mmr2 &= 0x7f;
61055102Storek 		audio_setmap(sc->sc_au.au_amd, &sc->sc_map);
61155102Storek 		break;
61255102Storek 
61355102Storek 	case AUDIO_FLUSH:
61455102Storek 		s = splaudio();
61555102Storek 		AUCB_INIT(&sc->sc_au.au_rb);
61655102Storek 		AUCB_INIT(&sc->sc_au.au_wb);
61755102Storek 		splx(s);
61855102Storek 		sc->sc_wseek = 0;
61955102Storek 		sc->sc_rseek = 0;
62055102Storek 		break;
62155102Storek 
62255102Storek 	/*
62355102Storek 	 * Number of read samples dropped.  We don't know where or
62455102Storek 	 * when they were dropped.
62555102Storek 	 */
62655102Storek 	case AUDIO_RERROR:
62755102Storek 		*(int *)addr = sc->sc_au.au_rb.cb_drops != 0;
62855102Storek 		break;
62955102Storek 
63055102Storek 	/*
63155102Storek 	 * Timestamp of last frame written.
63255102Storek 	 */
63355102Storek 	case AUDIO_WSEEK:
63455102Storek 		*(u_long *)addr = sc->sc_wseek;
63555102Storek 		break;
63655102Storek 
63755102Storek 	case AUDIO_SETINFO:
63855102Storek 		error = audiosetinfo(sc, (struct audio_info *)addr);
63955102Storek 		break;
64055102Storek 
64155102Storek 	case AUDIO_GETINFO:
64255102Storek 		error = audiogetinfo(sc, (struct audio_info *)addr);
64355102Storek 		break;
64455102Storek 
64555102Storek 	case SUNAUDIO_GETINFO:
64655102Storek 		error = sunaudiogetinfo(sc, (struct sun_audio_info *)addr);
64755102Storek 		break;
64855102Storek 
64955102Storek 	case SUNAUDIO_SETINFO:
65055102Storek 		error = sunaudiosetinfo(sc, (struct sun_audio_info *)addr);
65155102Storek 		break;
65255102Storek 
65355102Storek 	case AUDIO_DRAIN:
65455102Storek 		s = splaudio();
65555102Storek 		error = audio_drain(sc);
65655102Storek 		splx(s);
65755102Storek 		break;
65855102Storek 
65955102Storek 	default:
66055102Storek 		error = EINVAL;
66155102Storek 		break;
66255102Storek 	}
66355102Storek 	return (error);
66455102Storek }
66555102Storek 
66655102Storek int
66755102Storek AUDIOSELECT(dev, rw, p)
66855102Storek {
66955102Storek 	register struct audio_softc *sc = SOFTC(dev);
67055102Storek 	register struct aucb *cb;
67155102Storek 	register int s = splaudio();
67255102Storek 
67355102Storek 	switch (rw) {
67455102Storek 
67555102Storek 	case FREAD:
67655102Storek 		cb = &sc->sc_au.au_rb;
67755102Storek 		if (AUCB_LEN(cb) >= sc->sc_au.au_blksize) {
67855102Storek 			splx(s);
67955102Storek 			return (1);
68055102Storek 		}
68155102Storek 		selrecord(p, &sc->sc_rsel);
68255102Storek 		cb->cb_thresh = sc->sc_au.au_blksize;
68355102Storek 		break;
68455102Storek 
68555102Storek 	case FWRITE:
68655102Storek 		cb = &sc->sc_au.au_wb;
68755102Storek 		if (AUCB_LEN(cb) <= sc->sc_au.au_lowat) {
68855102Storek 			splx(s);
68955102Storek 			return (1);
69055102Storek 		}
69155102Storek 		selrecord(p, &sc->sc_wsel);
69255102Storek 		cb->cb_thresh = sc->sc_au.au_lowat;
69355102Storek 		break;
69455102Storek 	}
69555102Storek 	splx(s);
69655102Storek 	return (0);
69755102Storek }
69855102Storek 
69955102Storek #ifdef AUDIO_C_HANDLER
70055102Storek int
70155102Storek audiohwintr(au0)
70255102Storek 	void *au0;
70355102Storek {
70455102Storek #ifdef SUNOS
70555102Storek 	register struct auio *au = audio_au;
70655102Storek #else
70755102Storek 	register struct auio *au = au0;
70855102Storek #endif
70955102Storek 	register volatile struct amd7930 *amd = au->au_amd;
71055102Storek 	register struct aucb *cb;
71155102Storek 	register int h, t, k;
71255102Storek 
71355102Storek 	k = amd->ir;		/* clear interrupt */
71455102Storek 	++au->au_stamp;
71555102Storek 
71655102Storek 	/* receive incoming data */
71755102Storek 	cb = &au->au_rb;
71855102Storek 	h = cb->cb_head;
71955102Storek 	t = cb->cb_tail;
72055102Storek 	k = AUCB_MOD(t + 1);
72155102Storek 	if (h == k)
72255102Storek 		cb->cb_drops++;
72355102Storek 	else if  (cb->cb_pause != 0)
72455102Storek 		cb->cb_pdrops++;
72555102Storek 	else {
72655102Storek 		cb->cb_data[t] = amd->bbrb;
72755102Storek 		cb->cb_tail = t = k;
72855102Storek 	}
72955102Storek 	if (AUCB_MOD(t - h) >= cb->cb_thresh) {
73055102Storek 		cb->cb_thresh = AUCB_SIZE;
73155102Storek 		cb->cb_waking = 1;
73255102Storek 		AUDIO_SET_SWINTR;
73355102Storek 	}
73455102Storek 	/* send outgoing data */
73555102Storek 	cb = &au->au_wb;
73655102Storek 	h = cb->cb_head;
73755102Storek 	t = cb->cb_tail;
73855102Storek 	if (h == t)
73955102Storek 		cb->cb_drops++;
74055102Storek 	else if (cb->cb_pause != 0)
74155102Storek 		cb->cb_pdrops++;
74255102Storek 	else {
74355102Storek 		cb->cb_head = h = AUCB_MOD(h + 1);
74455102Storek 		amd->bbtb = cb->cb_data[h];
74555102Storek 	}
74655102Storek 	if (AUCB_MOD(t - h) <= cb->cb_thresh) {
74755102Storek 		cb->cb_thresh = -1;
74855102Storek 		cb->cb_waking = 1;
74955102Storek 		AUDIO_SET_SWINTR;
75055102Storek 	}
75155102Storek 	return (1);
75255102Storek }
75355102Storek #endif
75455102Storek 
75555102Storek int
75655102Storek audioswintr(sc0)
75755102Storek 	void *sc0;
75855102Storek {
75955102Storek 	register struct audio_softc *sc;
76055102Storek 	register int s, ret = 0;
76155102Storek #ifdef SUNOS
76255102Storek 	sc = &audio_softc;
76355102Storek #else
76455102Storek 	sc = sc0;
76555102Storek #endif
76655102Storek 	s = splaudio();
76755102Storek 	if (sc->sc_au.au_rb.cb_waking != 0) {
76855102Storek 		sc->sc_au.au_rb.cb_waking = 0;
76955102Storek 		splx(s);
77055102Storek 		ret = 1;
77155102Storek 		wakeup((caddr_t)&sc->sc_au.au_rb);
77255102Storek 		SELWAKEUP(&sc->sc_rsel);
77355102Storek 		(void) splaudio();
77455102Storek 	}
77555102Storek 	if (sc->sc_au.au_wb.cb_waking != 0) {
77655102Storek 		sc->sc_au.au_wb.cb_waking = 0;
77755102Storek 		splx(s);
77855102Storek 		ret = 1;
77955102Storek 		wakeup((caddr_t)&sc->sc_au.au_wb);
78055102Storek 		SELWAKEUP(&sc->sc_wsel);
78155102Storek 	} else
78255102Storek 		splx(s);
78355102Storek 	return (ret);
78455102Storek }
78555102Storek 
78655102Storek /* Write 16 bits of data from variable v to the data port of the audio chip */
78755102Storek 
78855102Storek #define	WAMD16(amd, v) ((amd)->dr = v, (amd)->dr = v >> 8)
78955102Storek 
79055102Storek void
79155102Storek audio_setmap(amd, map)
79255102Storek 	register volatile struct amd7930 *amd;
79355102Storek 	register struct mapreg *map;
79455102Storek {
79555102Storek 	register int i, s, v;
79655102Storek 
79755102Storek 	s = splaudio();
79855102Storek 	amd->cr = AMDR_MAP_1_10;
79955102Storek 	for (i = 0; i < 8; i++) {
80055102Storek 		v = map->mr_x[i];
80155102Storek 		WAMD16(amd, v);
80255102Storek 	}
80355102Storek 	for (i = 0; i < 8; ++i) {
80455102Storek 		v = map->mr_r[i];
80555102Storek 		WAMD16(amd, v);
80655102Storek 	}
80755102Storek 	v = map->mr_gx; WAMD16(amd, v);
80855102Storek 	v = map->mr_gr; WAMD16(amd, v);
80955102Storek 	v = map->mr_ger; WAMD16(amd, v);
81055102Storek 	v = map->mr_stgr; WAMD16(amd, v);
81155102Storek 	v = map->mr_ftgr; WAMD16(amd, v);
81255102Storek 	v = map->mr_atgr; WAMD16(amd, v);
81355102Storek 	amd->dr = map->mr_mmr1;
81455102Storek 	amd->dr = map->mr_mmr2;
81555102Storek 	splx(s);
81655102Storek }
81755102Storek 
81855102Storek /*
81955102Storek  * Set the mmr1 register and one other 16 bit register in the audio chip.
82055102Storek  * The other register is indicated by op and val.
82155102Storek  */
82255102Storek void
82355102Storek audio_setmmr1(amd, mmr1, op, val)
82455102Storek 	register volatile struct amd7930 *amd;
82555102Storek 	register int mmr1;
82655102Storek 	register int op;
82755102Storek 	register int val;
82855102Storek {
82955102Storek 	register int s = splaudio();
83055102Storek 
83155102Storek 	amd->cr = AMDR_MAP_MMR1;
83255102Storek 	amd->dr = mmr1;
83355102Storek 	amd->cr = op;
83455102Storek 	WAMD16(amd, val);
83555102Storek 	splx(s);
83655102Storek }
83755102Storek 
83855102Storek /*
83955102Storek  * Set only the mmr1 regsiter, and one other.
84055102Storek  */
84155102Storek static void
84255102Storek audio_setmmr2(amd, mmr2)
84355102Storek 	register volatile struct amd7930 *amd;
84455102Storek 	register int mmr2;
84555102Storek {
84655102Storek 	register int s = splaudio();
84755102Storek 
84855102Storek 	amd->cr = AMDR_MAP_MMR2;
84955102Storek 	amd->dr = mmr2;
85055102Storek 	splx(s);
85155102Storek }
85255102Storek 
85355102Storek static u_short ger_coeff[] = {
85455102Storek 	0xaaaa, 0x9bbb, 0x79ac, 0x099a, 0x4199, 0x3199, 0x9cde, 0x9def,
85555102Storek 	0x749c, 0x549d, 0x6aae, 0xabcd, 0xabdf, 0x7429, 0x64ab, 0x6aff,
85655102Storek 	0x2abd, 0xbeef, 0x5cce, 0x75cd, 0x0099, 0x554c, 0x43dd, 0x33dd,
85755102Storek 	0x52ef, 0x771b, 0x5542, 0x41dd, 0x31dd, 0x441f, 0x431f, 0x331f,
85855102Storek 	0x40dd, 0x11dd, 0x440f, 0x411f, 0x311f, 0x5520, 0x10dd, 0x4211,
85955102Storek 	0x410f, 0x111f, 0x600b, 0x00dd, 0x4210, 0x400f, 0x110f, 0x2210,
86055102Storek 	0x7200, 0x4200, 0x2110, 0x100f, 0x2200, 0x1110, 0x000b, 0x2100,
86155102Storek 	0x000f,
86255102Storek #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
86355102Storek };
86455102Storek 
86555102Storek static u_short gx_coeff[] = {
86655102Storek 	0x0808, 0x4cb2, 0x3dac, 0x2ae5, 0x2533, 0x2222, 0x2122, 0x1fd3,
86755102Storek 	0x12a2, 0x121b, 0x113b, 0x0bc3, 0x10f2, 0x03ba, 0x02ca, 0x021d,
86855102Storek 	0x015a, 0x0122, 0x0112, 0x00ec, 0x0032, 0x0021, 0x0013, 0x0011,
86955102Storek 	0x000e,
87055102Storek #define NGX (sizeof(gx_coeff) / sizeof(gx_coeff[0]))
87155102Storek };
87255102Storek 
87355102Storek static u_short stg_coeff[] = {
87455102Storek 	0x8b7c, 0x8b44, 0x8b35, 0x8b2a, 0x8b24, 0x8b22, 0x9123, 0x912e,
87555102Storek 	0x912a, 0x9132, 0x913b, 0x914b, 0x91f9, 0x91c5, 0x91b6, 0x9212,
87655102Storek 	0x91a4, 0x9222, 0x9232, 0x92fb, 0x92aa, 0x9327, 0x93b3, 0x94b3,
87755102Storek 	0x9f91, 0x9cea, 0x9bf9, 0x9aac, 0x9a4a, 0xa222, 0xa2a2, 0xa68d,
87855102Storek 	0xaaa3, 0xb242, 0xbb52, 0xcbb2, 0x0808,
87955102Storek #define NSTG (sizeof(stg_coeff) / sizeof(stg_coeff[0]))
88055102Storek };
88155102Storek 
88255102Storek static void
88355102Storek ausetrgain(sc, level)
88455102Storek 	register struct audio_softc *sc;
88555102Storek 	register int level;
88655102Storek {
88755102Storek 	level &= 0xff;
88855102Storek 	sc->sc_rlevel = level;
88955102Storek 	if (level != 0)
89055102Storek 		sc->sc_map.mr_mmr1 |= AMD_MMR1_GX;
89155102Storek 	else
89255102Storek 		sc->sc_map.mr_mmr1 &=~ AMD_MMR1_GX;
89355102Storek 
89455102Storek 	sc->sc_map.mr_gx = gx_coeff[(level * NGX) / 256];
89555102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
89655102Storek 		      AMDR_MAP_GX, sc->sc_map.mr_gx);
89755102Storek }
89855102Storek 
89955102Storek static void
90055102Storek ausetpgain(sc, level)
90155102Storek 	register struct audio_softc *sc;
90255102Storek 	register int level;
90355102Storek {
90455102Storek 	level &= 0xff;
90555102Storek 	sc->sc_plevel = level;
90655102Storek 	if (level != 0)
90755102Storek 		sc->sc_map.mr_mmr1 |= AMD_MMR1_GER;
90855102Storek 	else
90955102Storek 		sc->sc_map.mr_mmr1 &=~ AMD_MMR1_GER;
91055102Storek 
91155102Storek 	sc->sc_map.mr_ger = ger_coeff[(level * NGER) / 256];
91255102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
91355102Storek 		      AMDR_MAP_GER, sc->sc_map.mr_ger);
91455102Storek }
91555102Storek 
91655102Storek static void
91755102Storek ausetmgain(sc, level)
91855102Storek 	register struct audio_softc *sc;
91955102Storek 	register int level;
92055102Storek {
92155102Storek 	level &= 0xff;
92255102Storek 	sc->sc_mlevel = level;
92355102Storek 	if (level != 0)
92455102Storek 		sc->sc_map.mr_mmr1 |= AMD_MMR1_STG;
92555102Storek 	else
92655102Storek 		sc->sc_map.mr_mmr1 &=~ AMD_MMR1_STG;
92755102Storek 
92855102Storek 	sc->sc_map.mr_stgr = stg_coeff[(level * NSTG) / 256];
92955102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
93055102Storek 		      AMDR_MAP_STG, sc->sc_map.mr_stgr);
93155102Storek }
93255102Storek 
93355102Storek static int
93455102Storek audiosetinfo(sc, ai)
93555102Storek 	struct audio_softc *sc;
93655102Storek 	struct audio_info *ai;
93755102Storek {
93855102Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
93955102Storek 	register int s, bsize;
94055102Storek 
94155102Storek 	if (p->gain != ~0)
94255102Storek 		ausetpgain(sc, p->gain);
94355102Storek 	if (r->gain != ~0)
94455102Storek 		ausetrgain(sc, r->gain);
94555102Storek 	if (ai->monitor_gain != ~0)
94655102Storek 		ausetmgain(sc, p->gain);
94755102Storek 	if (p->port == AUDIO_SPEAKER) {
94855102Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
94955102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
95055102Storek 	} else if (p->port == AUDIO_HEADPHONE) {
95155102Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
95255102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
95355102Storek 	}
95455102Storek 	if (p->pause != (u_char)~0)
95555102Storek 		sc->sc_au.au_wb.cb_pause = p->pause;
95655102Storek 	if (r->pause != (u_char)~0)
95755102Storek 		sc->sc_au.au_rb.cb_pause = r->pause;
95855102Storek 
95955102Storek 	if (ai->blocksize != ~0) {
96055102Storek 		if (ai->blocksize == 0)
96155102Storek 			bsize = ai->blocksize = DEFBLKSIZE;
96255102Storek 		else if (ai->blocksize > MAXBLKSIZE)
96355102Storek 			bsize = ai->blocksize = MAXBLKSIZE;
96455102Storek 		else
96555102Storek 			bsize = ai->blocksize;
96655102Storek 
96755102Storek 		s = splaudio();
96855102Storek 		sc->sc_au.au_blksize = bsize;
96955102Storek 		/* AUDIO_FLUSH */
97055102Storek 		AUCB_INIT(&sc->sc_au.au_rb);
97155102Storek 		AUCB_INIT(&sc->sc_au.au_wb);
97255102Storek 		splx(s);
97355102Storek 
97455102Storek 	}
97555102Storek 	if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE)
97655102Storek 		sc->sc_au.au_hiwat = ai->hiwat;
97755102Storek 	if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE)
97855102Storek 		sc->sc_au.au_lowat = ai->lowat;
97955102Storek 
98055102Storek 	return (0);
98155102Storek }
98255102Storek 
98355102Storek static int
98455102Storek sunaudiosetinfo(sc, ai)
98555102Storek 	struct audio_softc *sc;
98655102Storek 	struct sun_audio_info *ai;
98755102Storek {
98855102Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
98955102Storek 
99055102Storek 	if (p->gain != ~0)
99155102Storek 		ausetpgain(sc, p->gain);
99255102Storek 	if (r->gain != ~0)
99355102Storek 		ausetrgain(sc, r->gain);
99455102Storek 	if (ai->monitor_gain != ~0)
99555102Storek 		ausetmgain(sc, p->gain);
99655102Storek 	if (p->port == AUDIO_SPEAKER) {
99755102Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
99855102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
99955102Storek 	} else if (p->port == AUDIO_HEADPHONE) {
100055102Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
100155102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
100255102Storek 	}
100355102Storek 	/*
100455102Storek 	 * The bsd driver does not distinguish between paused and active.
100555102Storek 	 * (In the sun driver, not active means samples are not ouput
100655102Storek 	 * at all, but paused means the last streams buffer is drained
100755102Storek 	 * and then output stops.)  If either are 0, then when stop output.
100855102Storek 	 * Otherwise, if either are non-zero, we resume.
100955102Storek 	 */
101055102Storek 	if (p->pause == 0 || p->active == 0)
101155102Storek 		sc->sc_au.au_wb.cb_pause = 0;
101255102Storek 	else if (p->pause != (u_char)~0 || p->active != (u_char)~0)
101355102Storek 		sc->sc_au.au_wb.cb_pause = 1;
101455102Storek 	if (r->pause == 0 || r->active == 0)
101555102Storek 		sc->sc_au.au_rb.cb_pause = 0;
101655102Storek 	else if (r->pause != (u_char)~0 || r->active != (u_char)~0)
101755102Storek 		sc->sc_au.au_rb.cb_pause = 1;
101855102Storek 
101955102Storek 	return (0);
102055102Storek }
102155102Storek 
102255102Storek static int
102355102Storek audiogetinfo(sc, ai)
102455102Storek 	struct audio_softc *sc;
102555102Storek 	struct audio_info *ai;
102655102Storek {
102755102Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
102855102Storek 
102955102Storek 	p->sample_rate = r->sample_rate = 8000;
103055102Storek 	p->channels = r->channels = 1;
103155102Storek 	p->precision = r->precision = 8;
103255102Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
103355102Storek 
103455102Storek 	ai->monitor_gain = sc->sc_mlevel;
103555102Storek 	r->gain = sc->sc_rlevel;
103655102Storek 	p->gain = sc->sc_plevel;
103755102Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
103855102Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
103955102Storek 
104055102Storek 	p->pause = sc->sc_au.au_wb.cb_pause;
104155102Storek 	r->pause = sc->sc_au.au_rb.cb_pause;
104255102Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
104355102Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
104455102Storek 
104555102Storek 	p->open = sc->sc_open;
104655102Storek 	r->open = sc->sc_open;
104755102Storek 
104855102Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
104955102Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
105055102Storek 
105155102Storek 	p->seek = sc->sc_wseek;
105255102Storek 	r->seek = sc->sc_rseek;
105355102Storek 
105455102Storek 	ai->blocksize = sc->sc_au.au_blksize;
105555102Storek 	ai->hiwat = sc->sc_au.au_hiwat;
105655102Storek 	ai->lowat = sc->sc_au.au_lowat;
105755102Storek 
105855102Storek 	return (0);
105955102Storek }
106055102Storek 
106155102Storek static int
106255102Storek sunaudiogetinfo(sc, ai)
106355102Storek 	struct audio_softc *sc;
106455102Storek 	struct sun_audio_info *ai;
106555102Storek {
106655102Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
106755102Storek 
106855102Storek 	p->sample_rate = r->sample_rate = 8000;
106955102Storek 	p->channels = r->channels = 1;
107055102Storek 	p->precision = r->precision = 8;
107155102Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
107255102Storek 
107355102Storek 	ai->monitor_gain = sc->sc_mlevel;
107455102Storek 	r->gain = sc->sc_rlevel;
107555102Storek 	p->gain = sc->sc_plevel;
107655102Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
107755102Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
107855102Storek 
107955102Storek 	p->active = p->pause = sc->sc_au.au_wb.cb_pause;
108055102Storek 	r->active = r->pause = sc->sc_au.au_rb.cb_pause;
108155102Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
108255102Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
108355102Storek 
108455102Storek 	p->waiting = 0;
108555102Storek 	r->waiting = 0;
108655102Storek 	p->eof = 0;
108755102Storek 	r->eof = 0;
108855102Storek 
108955102Storek 	p->open = sc->sc_open;
109055102Storek 	r->open = sc->sc_open;
109155102Storek 
109255102Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
109355102Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
109455102Storek 
109555102Storek 	return (0);
109655102Storek }
109755102Storek #endif
1098