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