diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1997-10-07 11:05:02 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1997-10-07 11:05:02 +0000 |
commit | 21e5d641fc7dbc3e69a229a6c75c4bd9923674d9 (patch) | |
tree | db53f6099d67a0ed96e426a03a47867d8511d7f6 /sys | |
parent | c46d7526b86e9917ca4c8fe1d43a59b5b29a525c (diff) |
NetBSD's new MI audio framework, but still only available for amiga,
after release it will replace our old MI stuff. Things brings audio to
amiga out-of-the box.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/amiga/conf/GENERIC | 6 | ||||
-rw-r--r-- | sys/arch/amiga/conf/files.amiga | 17 | ||||
-rw-r--r-- | sys/arch/amiga/dev/aucc.c | 8 | ||||
-rw-r--r-- | sys/arch/amiga/dev/audio.c | 2377 | ||||
-rw-r--r-- | sys/arch/amiga/dev/audio_if.h | 172 | ||||
-rw-r--r-- | sys/arch/amiga/dev/audiovar.h | 130 | ||||
-rw-r--r-- | sys/arch/amiga/include/audioio.h | 281 |
7 files changed, 2982 insertions, 9 deletions
diff --git a/sys/arch/amiga/conf/GENERIC b/sys/arch/amiga/conf/GENERIC index 7abe3670427..72c11a46a3a 100644 --- a/sys/arch/amiga/conf/GENERIC +++ b/sys/arch/amiga/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.22 1997/09/19 17:16:17 niklas Exp $ +# $OpenBSD: GENERIC,v 1.23 1997/10/07 11:04:52 niklas Exp $ # $NetBSD: GENERIC,v 1.85 1997/08/27 19:32:49 is Exp $ # @@ -147,8 +147,8 @@ com* at drsupio? port ? # DraCo serial a2kbbc0 at mainbus0 # A2000 battery backed clock a34kbbc0 at mainbus0 # A3000/A4000 battery backed clock -#aucc* at mainbus0 # Amiga CC audio -#audio* at aucc? +aucc* at mainbus0 # Amiga CC audio +new_audio* at aucc? # Ethernet cards le* at zbus0 # Lance ethernet. diff --git a/sys/arch/amiga/conf/files.amiga b/sys/arch/amiga/conf/files.amiga index e05fec2244e..1ab7f597cd4 100644 --- a/sys/arch/amiga/conf/files.amiga +++ b/sys/arch/amiga/conf/files.amiga @@ -1,4 +1,4 @@ -# $OpenBSD: files.amiga,v 1.22 1997/09/18 13:39:40 niklas Exp $ +# $OpenBSD: files.amiga,v 1.23 1997/10/07 11:04:54 niklas Exp $ # $NetBSD: files.amiga,v 1.62 1997/08/27 19:32:47 is Exp $ @@ -7,6 +7,17 @@ maxpartitions 16 maxusers 2 8 64 +# Transient audio stuff that will end up in the MI parts +define new_audio {} +define auconv + +# audio device, attaches to audio hardware driver +device new_audio +attach new_audio at new_audio + +file dev/auconv.c auconv +file arch/amiga/dev/audio.c new_audio needs-flag + device mainbus {} attach mainbus at root @@ -51,8 +62,8 @@ device par attach par at mainbus file arch/amiga/dev/par.c par needs-count -# audio -device aucc: audio +# audio XXX s/new_audio/audio/ after the new audio is generally used. +device aucc: new_audio attach aucc at mainbus file arch/amiga/dev/aucc.c aucc needs-flag diff --git a/sys/arch/amiga/dev/aucc.c b/sys/arch/amiga/dev/aucc.c index ba60c5b9ba8..391e710a192 100644 --- a/sys/arch/amiga/dev/aucc.c +++ b/sys/arch/amiga/dev/aucc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucc.c,v 1.1 1997/09/18 13:39:42 niklas Exp $ */ +/* $OpenBSD: aucc.c,v 1.2 1997/10/07 11:04:56 niklas Exp $ */ /* $NetBSD: aucc.c,v 1.18 1997/08/24 22:31:23 augustss Exp $ */ #undef AUDIO_DEBUG /* @@ -40,13 +40,15 @@ #include <sys/ioctl.h> #include <sys/device.h> #include <sys/proc.h> + #include <machine/cpu.h> +#include <machine/audioio.h> -#include <sys/audioio.h> -#include <dev/audio_if.h> #include <amiga/amiga/cc.h> #include <amiga/amiga/custom.h> #include <amiga/amiga/device.h> + +#include <amiga/dev/audio_if.h> #include <amiga/dev/auccvar.h> #ifdef LEV6_DEFER diff --git a/sys/arch/amiga/dev/audio.c b/sys/arch/amiga/dev/audio.c new file mode 100644 index 00000000000..538615a72c0 --- /dev/null +++ b/sys/arch/amiga/dev/audio.c @@ -0,0 +1,2377 @@ +/* $OpenBSD: audio.c,v 1.1 1997/10/07 11:04:57 niklas Exp $ */ +/* $NetBSD: audio.c,v 1.71 1997/09/06 01:14:48 augustss Exp $ */ + +/* + * Copyright (c) 1991-1993 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This is a (partially) SunOS-compatible /dev/audio driver for Net- & OpenBSD. + * + * This code tries to do something half-way sensible with + * half-duplex hardware, such as with the SoundBlaster hardware. With + * half-duplex hardware allowing O_RDWR access doesn't really make + * sense. However, closing and opening the device to "turn around the + * line" is relatively expensive and costs a card reset (which can + * take some time, at least for the SoundBlaster hardware). Instead + * we allow O_RDWR access, and provide an ioctl to set the "mode", + * i.e. playing or recording. + * + * If you write to a half-duplex device in record mode, the data is + * tossed. If you read from the device in play mode, you get silence + * filled buffers at the rate at which samples are naturally + * generated. + * + * If you try to set both play and record mode on a half-duplex + * device, playing takes precedence. + */ + +/* + * Todo: + * - Add softaudio() isr processing for wakeup, poll, signals, + * and silence fill. + */ + +#include "new_audio.h" +#if NNEW_AUDIO > 0 + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/fcntl.h> +#include <sys/vnode.h> +#include <sys/select.h> +#include <sys/poll.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/syslog.h> +#include <sys/kernel.h> +#include <sys/signalvar.h> +#include <sys/conf.h> +#include <sys/device.h> + +#include <machine/audioio.h> +#include <machine/endian.h> + +#include <amiga/dev/audio_if.h> +#include <amiga/dev/audiovar.h> + +#include <vm/vm.h> +#include <vm/vm_prot.h> + +#ifdef AUDIO_DEBUG +#define DPRINTF(x) if (audiodebug) printf x +int audiodebug = 0; +#else +#define DPRINTF(x) +#endif + +#define ROUNDSIZE(x) x &= -16 /* round to nice boundary */ + +int audio_blk_ms = AUDIO_BLK_MS; + +int audiosetinfo __P((struct audio_softc *, struct audio_info *)); +int audiogetinfo __P((struct audio_softc *, struct audio_info *)); + +int audio_open __P((dev_t, int, int, struct proc *)); +int audio_close __P((dev_t, int, int, struct proc *)); +int audio_read __P((dev_t, struct uio *, int)); +int audio_write __P((dev_t, struct uio *, int)); +int audio_ioctl __P((dev_t, int, caddr_t, int, struct proc *)); +int audio_select __P((dev_t, int, struct proc *)); +int audio_mmap __P((dev_t, int, int)); + +int mixer_open __P((dev_t, int, int, struct proc *)); +int mixer_close __P((dev_t, int, int, struct proc *)); +int mixer_ioctl __P((dev_t, int, caddr_t, int, struct proc *)); +static void mixer_remove __P((struct audio_softc *, struct proc *p)); +static void mixer_signal __P((struct audio_softc *)); + +void audio_init_record __P((struct audio_softc *)); +void audio_init_play __P((struct audio_softc *)); +int audiostartr __P((struct audio_softc *)); +int audiostartp __P((struct audio_softc *)); +void audio_rint __P((void *)); +void audio_pint __P((void *)); +int audio_check_params __P((struct audio_params *)); + +void audio_calc_blksize __P((struct audio_softc *, int)); +void audio_fill_silence __P((struct audio_params *, u_char *, int)); +int audio_silence_copyout __P((struct audio_softc *, int, struct uio *)); + +void audio_init_ringbuffer __P((struct audio_ringbuffer *)); +int audio_initbufs __P((struct audio_softc *)); +void audio_calcwater __P((struct audio_softc *)); +static __inline int audio_sleep_timo __P((int *, char *, int)); +static __inline int audio_sleep __P((int *, char *)); +static __inline void audio_wakeup __P((int *)); +int audio_drain __P((struct audio_softc *)); +void audio_clear __P((struct audio_softc *)); +static __inline void audio_pint_silence __P((struct audio_softc *, struct audio_ringbuffer *, u_char *, int)); + +int audio_alloc_ring __P((struct audio_softc *, struct audio_ringbuffer *, int)); +void audio_free_ring __P((struct audio_softc *, struct audio_ringbuffer *)); + +int audioprint __P((void *, const char *)); + +int audioprobe __P((struct device *, void *, void *)); +void audioattach __P((struct device *, struct device *, void *)); + +/* The default audio mode: 8 kHz mono ulaw */ +struct audio_params audio_default = + { 8000, AUDIO_ENCODING_ULAW, 8, 1, 0, 1 }; + +struct cfattach new_audio_ca = { + sizeof(struct audio_softc), audioprobe, audioattach +}; + +struct cfdriver new_audio_cd = { + NULL, "audio", DV_DULL +}; + +int +audioprobe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct audio_attach_args *sa = aux; + + DPRINTF(("audioprobe: done=%d sa=%p hw=%p\n", + sa->audiodone, sa, sa->ahw)); + if (sa->audiodone) + return 0; + return sa->ahw != 0; +} + +void +audioattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct audio_softc *sc = (void *)self; + struct audio_attach_args *sa = aux; + struct audio_hw_if *hwp = sa->ahw; + void *hdlp = sa->hdl; + int error; + + printf("\n"); + + sa->audiodone++; + +#ifdef DIAGNOSTIC + if (hwp == 0 || + hwp->open == 0 || + hwp->close == 0 || + hwp->query_encoding == 0 || + hwp->set_params == 0 || + hwp->set_out_port == 0 || + hwp->get_out_port == 0 || + hwp->set_in_port == 0 || + hwp->get_in_port == 0 || + hwp->start_output == 0 || + hwp->start_input == 0 || + hwp->halt_output == 0 || + hwp->halt_input == 0 || + hwp->cont_output == 0 || + hwp->cont_input == 0 || + hwp->getdev == 0 || + hwp->set_port == 0 || + hwp->get_port == 0 || + hwp->query_devinfo == 0 || + hwp->get_props == 0) { + printf("audio: missing method\n"); + sc->hw_if = 0; + return; + } +#endif + + sc->hw_if = hwp; + sc->hw_hdl = hdlp; + sc->sc_dev = parent; + + error = audio_alloc_ring(sc, &sc->sc_pr, AU_RING_SIZE); + if (error) { + sc->hw_if = 0; + return; + } + error = audio_alloc_ring(sc, &sc->sc_rr, AU_RING_SIZE); + if (error) { + audio_free_ring(sc, &sc->sc_pr); + sc->hw_if = 0; + return; + } + + /* + * Set default softc params + */ + sc->sc_pparams = audio_default; + sc->sc_rparams = audio_default; + + /* Set up some default values */ + sc->sc_blkset = 0; + audio_calc_blksize(sc, AUMODE_RECORD); + audio_calc_blksize(sc, AUMODE_PLAY); + audio_init_ringbuffer(&sc->sc_rr); + audio_init_ringbuffer(&sc->sc_pr); + audio_calcwater(sc); +} + +/* + * Called from hardware driver. This is where the MI audio driver gets + * probed/attached to the hardware driver. + */ +void +audio_attach_mi(ahwp, mhwp, hdlp, dev) + struct audio_hw_if *ahwp; + struct midi_hw_if *mhwp; + void *hdlp; + struct device *dev; +{ + struct audio_attach_args arg; + + arg.ahw = ahwp; + arg.mhw = mhwp; + arg.hdl = hdlp; + arg.audiodone = arg.mididone = 0; + while(config_found(dev, &arg, 0)) + ; +} + +#ifdef AUDIO_DEBUG +void audio_printsc __P((struct audio_softc *)); +void audio_print_params __P((char *, struct audio_params *)); + +void +audio_printsc(sc) + struct audio_softc *sc; +{ + printf("hwhandle %p hw_if %p ", sc->hw_hdl, sc->hw_if); + printf("open 0x%x mode 0x%x\n", sc->sc_open, sc->sc_mode); + printf("rchan 0x%x wchan 0x%x ", sc->sc_rchan, sc->sc_wchan); + printf("rring used 0x%x pring used=%d\n", sc->sc_rr.used, sc->sc_pr.used); + printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_pbus); + printf("blksize %d", sc->sc_pr.blksize); + printf("hiwat %d lowat %d\n", sc->sc_pr.usedhigh, sc->sc_pr.usedlow); +} + +void +audio_print_params(s, p) + char *s; + struct audio_params *p; +{ + printf("audio: %s sr=%ld, enc=%d, chan=%d, prec=%d\n", s, + p->sample_rate, p->encoding, p->channels, p->precision); +} +#endif + +int +audio_alloc_ring(sc, r, bufsize) + struct audio_softc *sc; + struct audio_ringbuffer *r; + int bufsize; +{ + struct audio_hw_if *hw = sc->hw_if; + void *hdl = sc->hw_hdl; + /* + * Alloc DMA play and record buffers + */ + ROUNDSIZE(bufsize); + if (bufsize < AUMINBUF) + bufsize = AUMINBUF; + if (hw->round_buffersize) + bufsize = hw->round_buffersize(hdl, bufsize); + r->bufsize = bufsize; + if (hw->alloc) + r->start = hw->alloc(hdl, r->bufsize, M_DEVBUF, M_WAITOK); + else + r->start = malloc(bufsize, M_DEVBUF, M_WAITOK); + if (r->start == 0) + return ENOMEM; + return 0; +} + +void +audio_free_ring(sc, r) + struct audio_softc *sc; + struct audio_ringbuffer *r; +{ + if (sc->hw_if->free) { + sc->hw_if->free(sc->hw_hdl, r->start, M_DEVBUF); + } else { + free(r->start, M_DEVBUF); + } +} + +int +audioopen(dev, flags, ifmt, p) + dev_t dev; + int flags, ifmt; + struct proc *p; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + case AUDIOCTL_DEVICE: + return (audio_open(dev, flags, ifmt, p)); + case MIXER_DEVICE: + return (mixer_open(dev, flags, ifmt, p)); + default: + return (ENXIO); + } +} + +int +audioclose(dev, flags, ifmt, p) + dev_t dev; + int flags, ifmt; + struct proc *p; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + return (audio_close(dev, flags, ifmt, p)); + case MIXER_DEVICE: + return (mixer_close(dev, flags, ifmt, p)); + case AUDIOCTL_DEVICE: + return 0; + default: + return (ENXIO); + } +} + +int +audioread(dev, uio, ioflag) + dev_t dev; + struct uio *uio; + int ioflag; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + return (audio_read(dev, uio, ioflag)); + case AUDIOCTL_DEVICE: + case MIXER_DEVICE: + return (ENODEV); + default: + return (ENXIO); + } +} + +int +audiowrite(dev, uio, ioflag) + dev_t dev; + struct uio *uio; + int ioflag; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + return (audio_write(dev, uio, ioflag)); + case AUDIOCTL_DEVICE: + case MIXER_DEVICE: + return (ENODEV); + default: + return (ENXIO); + } +} + +int +audioioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + case AUDIOCTL_DEVICE: + return (audio_ioctl(dev, cmd, addr, flag, p)); + case MIXER_DEVICE: + return (mixer_ioctl(dev, cmd, addr, flag, p)); + default: + return (ENXIO); + } +} + +int +audioselect(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + return (audio_select(dev, events, p)); + case AUDIOCTL_DEVICE: + case MIXER_DEVICE: + return (0); + default: + return (0); + } +} + +int +audiommap(dev, off, prot) + dev_t dev; + int off, prot; +{ + + switch (AUDIODEV(dev)) { + case SOUND_DEVICE: + case AUDIO_DEVICE: + return (audio_mmap(dev, off, prot)); + case AUDIOCTL_DEVICE: + case MIXER_DEVICE: + return -1; + default: + return -1; + } +} + +/* + * Audio driver + */ +void +audio_init_ringbuffer(rp) + struct audio_ringbuffer *rp; +{ + int nblks; + int blksize = rp->blksize; + + if (blksize < AUMINBLK) + blksize = AUMINBLK; + nblks = rp->bufsize / blksize; + if (nblks < AUMINNOBLK) { + nblks = AUMINNOBLK; + blksize = rp->bufsize / nblks; + ROUNDSIZE(blksize); + } + DPRINTF(("audio_init_ringbuffer: blksize=%d\n", blksize)); + rp->blksize = blksize; + rp->maxblks = nblks; + rp->used = 0; + rp->end = rp->start + nblks * blksize; + rp->inp = rp->outp = rp->start; + rp->stamp = 0; + rp->drops = 0; + rp->pause = 0; + rp->copying = 0; + rp->needfill = 0; + rp->mmapped = 0; +} + +int +audio_initbufs(sc) + struct audio_softc *sc; +{ + struct audio_hw_if *hw = sc->hw_if; + int error; + + DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_mode)); + audio_init_ringbuffer(&sc->sc_rr); + if (hw->init_input && (sc->sc_mode & AUMODE_RECORD)) { + error = hw->init_input(sc->hw_hdl, sc->sc_rr.start, + sc->sc_rr.end - sc->sc_rr.start); + if (error) + return error; + } + + audio_init_ringbuffer(&sc->sc_pr); + sc->sc_sil_count = 0; + if (hw->init_output && (sc->sc_mode & AUMODE_PLAY)) { + error = hw->init_output(sc->hw_hdl, sc->sc_pr.start, + sc->sc_pr.end - sc->sc_pr.start); + if (error) + return error; + } + +#ifdef AUDIO_INTR_TIME + sc->sc_pnintr = 0; + sc->sc_pblktime = (u_long)( + (double)sc->sc_pr.blksize * 1e6 / + (double)(sc->sc_pparams.precision / NBBY * + sc->sc_pparams.channels * + sc->sc_pparams.sample_rate)); + DPRINTF(("audio: play blktime = %lu for %d\n", + sc->sc_pblktime, sc->sc_pr.blksize)); + sc->sc_rnintr = 0; + sc->sc_rblktime = (u_long)( + (double)sc->sc_rr.blksize * 1e6 / + (double)(sc->sc_rparams.precision / NBBY * + sc->sc_rparams.channels * + sc->sc_rparams.sample_rate)); + DPRINTF(("audio: record blktime = %lu for %d\n", + sc->sc_rblktime, sc->sc_rr.blksize)); +#endif + + return 0; +} + +void +audio_calcwater(sc) + struct audio_softc *sc; +{ + sc->sc_pr.usedhigh = sc->sc_pr.end - sc->sc_pr.start; + sc->sc_pr.usedlow = sc->sc_pr.usedhigh * 3 / 4; /* set lowater at 75% */ + if (sc->sc_pr.usedlow == sc->sc_pr.usedhigh) + sc->sc_pr.usedlow -= sc->sc_pr.blksize; + sc->sc_rr.usedhigh = sc->sc_pr.end - sc->sc_pr.start - sc->sc_pr.blksize; + sc->sc_rr.usedlow = 0; +} + +static __inline int +audio_sleep_timo(chan, label, timo) + int *chan; + char *label; + int timo; +{ + int st; + + if (!label) + label = "audio"; + + *chan = 1; + st = tsleep(chan, PWAIT | PCATCH, label, timo); + *chan = 0; +#ifdef AUDIO_DEBUG + if (st != 0) + printf("audio_sleep: %d\n", st); +#endif + return (st); +} + +static __inline int +audio_sleep(chan, label) + int *chan; + char *label; +{ + return audio_sleep_timo(chan, label, 0); +} + +/* call at splaudio() */ +static __inline void +audio_wakeup(chan) + int *chan; +{ + if (*chan) { + wakeup(chan); + *chan = 0; + } +} + +int +audio_open(dev, flags, ifmt, p) + dev_t dev; + int flags, ifmt; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc; + int error; + int mode; + struct audio_hw_if *hw; + struct audio_info ai; + + if (unit >= new_audio_cd.cd_ndevs || + (sc = new_audio_cd.cd_devs[unit]) == NULL) + return ENXIO; + + hw = sc->hw_if; + if (!hw) + return ENXIO; + + DPRINTF(("audio_open: dev=0x%x flags=0x%x sc=%p hdl=%p\n", dev, flags, sc, sc->hw_hdl)); + + if (ISDEVAUDIOCTL(dev)) + return 0; + + if ((sc->sc_open & (AUOPEN_READ|AUOPEN_WRITE)) != 0) + return (EBUSY); + + error = hw->open(sc->hw_hdl, flags); + if (error) + return (error); + + sc->sc_async_audio = 0; + sc->sc_rchan = 0; + sc->sc_wchan = 0; + sc->sc_blkset = 0; /* Block sizes not set yet */ + sc->sc_sil_count = 0; + sc->sc_rbus = 0; + sc->sc_pbus = 0; + sc->sc_eof = 0; + sc->sc_playdrop = 0; + + sc->sc_full_duplex = 0; +/* doesn't always work right on SB. + (flags & (FWRITE|FREAD)) == (FWRITE|FREAD) && + (hw->get_props(sc->hw_hdl) & AUDIO_PROP_FULLDUPLEX); +*/ + + mode = 0; + if (flags & FREAD) { + sc->sc_open |= AUOPEN_READ; + mode |= AUMODE_RECORD; + } + if (flags & FWRITE) { + sc->sc_open |= AUOPEN_WRITE; + mode |= AUMODE_PLAY | AUMODE_PLAY_ALL; + } + + /* + * Multiplex device: /dev/audio (MU-Law) and /dev/sound (linear) + * The /dev/audio is always (re)set to 8-bit MU-Law mono + * For the other devices, you get what they were last set to. + */ + if (ISDEVAUDIO(dev)) { + /* /dev/audio */ + sc->sc_rparams = audio_default; + sc->sc_pparams = audio_default; + } +#ifdef DIAGNOSTIC + /* + * Sample rate and precision are supposed to be set to proper + * default values by the hardware driver, so that it may give + * us these values. + */ + if (sc->sc_rparams.precision == 0 || sc->sc_pparams.precision == 0) { + printf("audio_open: 0 precision\n"); + return EINVAL; + } +#endif + + AUDIO_INITINFO(&ai); + ai.record.sample_rate = sc->sc_rparams.sample_rate; + ai.record.encoding = sc->sc_rparams.encoding; + ai.record.channels = sc->sc_rparams.channels; + ai.record.precision = sc->sc_rparams.precision; + ai.play.sample_rate = sc->sc_pparams.sample_rate; + ai.play.encoding = sc->sc_pparams.encoding; + ai.play.channels = sc->sc_pparams.channels; + ai.play.precision = sc->sc_pparams.precision; + ai.mode = mode; + sc->sc_pr.blksize = sc->sc_rr.blksize = 0; /* force recalculation */ + error = audiosetinfo(sc, &ai); + if (error) + goto bad; + + DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode)); + + return 0; + +bad: + hw->close(sc->hw_hdl); + sc->sc_open = 0; + sc->sc_mode = 0; + sc->sc_full_duplex = 0; + return error; +} + +/* + * Must be called from task context. + */ +void +audio_init_record(sc) + struct audio_softc *sc; +{ + int s = splaudio(); + + if (sc->hw_if->speaker_ctl && + (!sc->sc_full_duplex || (sc->sc_mode & AUMODE_PLAY) == 0)) + sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_OFF); + splx(s); +} + +/* + * Must be called from task context. + */ +void +audio_init_play(sc) + struct audio_softc *sc; +{ + int s = splaudio(); + + sc->sc_wstamp = sc->sc_pr.stamp; + if (sc->hw_if->speaker_ctl) + sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_ON); + splx(s); +} + +int +audio_drain(sc) + struct audio_softc *sc; +{ + int error, drops; + struct audio_ringbuffer *cb = &sc->sc_pr; + int s; + + if (sc->sc_pr.mmapped || sc->sc_pr.used <= 0) + return 0; + if (!sc->sc_pbus) { + /* We've never started playing, probably because the + * block was too short. Pad it and start now. + */ + int cc; + u_char *inp = cb->inp; + + cc = cb->blksize - (inp - cb->start) % cb->blksize; + audio_fill_silence(&sc->sc_pparams, inp, cc); + inp += cc; + if (inp >= cb->end) + inp = cb->start; + s = splaudio(); + cb->used += cc; + cb->inp = inp; + error = audiostartp(sc); + splx(s); + if (error) + return error; + } + /* + * Play until a silence block has been played, then we + * know all has been drained. + * XXX This should be done some other way to avoid + * playing silence. + */ +#ifdef DIAGNOSTIC + if (cb->copying) { + printf("audio_drain: copying in progress!?!\n"); + cb->copying = 0; + } +#endif + drops = cb->drops; + error = 0; + s = splaudio(); + while (cb->drops == drops && !error) { + DPRINTF(("audio_drain: used=%d, drops=%ld\n", sc->sc_pr.used, cb->drops)); + /* + * When the process is exiting, it ignores all signals and + * we can't interrupt this sleep, so we set a timeout just in case. + */ + error = audio_sleep_timo(&sc->sc_wchan, "aud_dr", 30*hz); + } + splx(s); + return error; +} + +/* + * Close an audio chip. + */ +/* ARGSUSED */ +int +audio_close(dev, flags, ifmt, p) + dev_t dev; + int flags, ifmt; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + struct audio_hw_if *hw = sc->hw_if; + int s; + + DPRINTF(("audio_close: unit=%d\n", unit)); + + /* + * Block until output drains, but allow ^C interrupt. + */ + sc->sc_pr.usedlow = sc->sc_pr.blksize; /* avoid excessive wakeups */ + s = splaudio(); + /* + * If there is pending output, let it drain (unless + * the output is paused). + */ + if (!sc->sc_pr.pause) { + if (!audio_drain(sc) && hw->drain) + (void)hw->drain(sc->hw_hdl); + } + + hw->close(sc->hw_hdl); + + if (flags & FREAD) + sc->sc_open &= ~AUOPEN_READ; + if (flags & FWRITE) + sc->sc_open &= ~AUOPEN_WRITE; + + sc->sc_async_audio = 0; + sc->sc_mode = 0; + sc->sc_full_duplex = 0; + splx(s); + DPRINTF(("audio_close: done\n")); + + return (0); +} + +int +audio_read(dev, uio, ioflag) + dev_t dev; + struct uio *uio; + int ioflag; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + struct audio_ringbuffer *cb = &sc->sc_rr; + u_char *outp; + int error, s, used, cc, n; + + if (cb->mmapped) + return EINVAL; + +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_read: cc=%d mode=%d\n", uio->uio_resid, sc->sc_mode); +#endif + + error = 0; + /* + * If hardware is half-duplex and currently playing, return + * silence blocks based on the number of blocks we have output. + */ + if (!sc->sc_full_duplex && + (sc->sc_mode & AUMODE_PLAY)) { + while (uio->uio_resid > 0 && !error) { + s = splaudio(); + for(;;) { + cc = sc->sc_pr.stamp - sc->sc_wstamp; + if (cc > 0) + break; + DPRINTF(("audio_read: stamp=%lu, wstamp=%lu\n", + sc->sc_pr.stamp, sc->sc_wstamp)); + if (ioflag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + error = audio_sleep(&sc->sc_rchan, "aud_hr"); + if (error) { + splx(s); + return error; + } + } + splx(s); + + if (uio->uio_resid < cc) + cc = uio->uio_resid; +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_read: reading in write mode, cc=%d\n", cc); +#endif + error = audio_silence_copyout(sc, cc, uio); + sc->sc_wstamp += cc; + } + return (error); + } + while (uio->uio_resid > 0 && !error) { + s = splaudio(); + while (cb->used <= 0) { + if (ioflag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + if (!sc->sc_rbus) { + error = audiostartr(sc); + if (error) { + splx(s); + return error; + } + } +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_read: sleep used=%d\n", cb->used); +#endif + error = audio_sleep(&sc->sc_rchan, "aud_rd"); + if (error) { + splx(s); + return error; + } + } + used = cb->used; + outp = cb->outp; + cb->copying = 1; + splx(s); + cc = used - cb->usedlow; /* maximum to read */ + n = cb->end - outp; + if (n < cc) + cc = n; /* don't read beyond end of buffer */ + + if (uio->uio_resid < cc) + cc = uio->uio_resid; /* and no more than we want */ + + if (sc->sc_rparams.sw_code) + sc->sc_rparams.sw_code(sc->hw_hdl, outp, cc); +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_read: outp=%p, cc=%d\n", outp, cc); +#endif + error = uiomove(outp, cc, uio); + used -= cc; + outp += cc; + if (outp >= cb->end) + outp = cb->start; + s = splaudio(); + cb->outp = outp; + cb->used = used; + cb->copying = 0; + splx(s); + } + return (error); +} + +void +audio_clear(sc) + struct audio_softc *sc; +{ + int s = splaudio(); + + if (sc->sc_rbus) { + audio_wakeup(&sc->sc_rchan); + sc->hw_if->halt_input(sc->hw_hdl); + sc->sc_rbus = 0; + } + if (sc->sc_pbus) { + audio_wakeup(&sc->sc_wchan); + sc->hw_if->halt_output(sc->hw_hdl); + sc->sc_pbus = 0; + } + splx(s); +} + +void +audio_calc_blksize(sc, mode) + struct audio_softc *sc; + int mode; +{ + struct audio_hw_if *hw = sc->hw_if; + struct audio_params *parm; + struct audio_ringbuffer *rb; + int bs; + + if (sc->sc_blkset) + return; + + if (mode == AUMODE_PLAY) { + parm = &sc->sc_pparams; + rb = &sc->sc_pr; + } else { + parm = &sc->sc_rparams; + rb = &sc->sc_rr; + } + + bs = parm->sample_rate * audio_blk_ms / 1000 * + parm->channels * parm->precision / NBBY * + parm->factor; + ROUNDSIZE(bs); + if (hw->round_blocksize) + bs = hw->round_blocksize(sc->hw_hdl, bs); + rb->blksize = bs; + + DPRINTF(("audio_calc_blksize: %s blksize=%d\n", + mode == AUMODE_PLAY ? "play" : "record", bs)); +} + +void +audio_fill_silence(params, p, n) + struct audio_params *params; + u_char *p; + int n; +{ + u_char auzero0, auzero1 = 0; /* initialize to please gcc */ + int nfill = 1; + + switch (params->encoding) { + case AUDIO_ENCODING_ULAW: + auzero0 = 0x7f; + break; + case AUDIO_ENCODING_ALAW: + auzero0 = 0x55; + break; + case AUDIO_ENCODING_ADPCM: /* is this right XXX */ + case AUDIO_ENCODING_SLINEAR_LE: + case AUDIO_ENCODING_SLINEAR_BE: + auzero0 = 0; /* fortunately this works for both 8 and 16 bits */ + break; + case AUDIO_ENCODING_ULINEAR_LE: + case AUDIO_ENCODING_ULINEAR_BE: + if (params->precision == 16) { + nfill = 2; + if (params->encoding == AUDIO_ENCODING_ULINEAR_LE) { + auzero0 = 0; + auzero1 = 0x80; + } else { + auzero0 = 0x80; + auzero1 = 0; + } + } else + auzero0 = 0x80; + break; + default: + printf("audio: bad encoding %d\n", params->encoding); + auzero0 = 0; + break; + } + if (nfill == 1) { + while (--n >= 0) + *p++ = auzero0; /* XXX memset */ + } else /* nfill must be 2 */ { + while (n > 1) { + *p++ = auzero0; + *p++ = auzero1; + n -= 2; + } + } +} + +int +audio_silence_copyout(sc, n, uio) + struct audio_softc *sc; + int n; + struct uio *uio; +{ + int error; + int k; + u_char zerobuf[128]; + + audio_fill_silence(&sc->sc_rparams, zerobuf, sizeof zerobuf); + + error = 0; + while (n > 0 && uio->uio_resid > 0 && !error) { + k = min(n, min(uio->uio_resid, sizeof zerobuf)); + error = uiomove(zerobuf, k, uio); + n -= k; + } + return (error); +} + +int +audio_write(dev, uio, ioflag) + dev_t dev; + struct uio *uio; + int ioflag; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + struct audio_ringbuffer *cb = &sc->sc_pr; + u_char *inp, *einp; + int error, s, n, cc, used; + + DPRINTF(("audio_write: sc=%p(unit=%d) count=%d used=%d(hi=%d)\n", sc, unit, + uio->uio_resid, sc->sc_pr.used, sc->sc_pr.usedhigh)); + + if (cb->mmapped) + return EINVAL; + + if (uio->uio_resid == 0) { + sc->sc_eof++; + return 0; + } + + /* + * If half-duplex and currently recording, throw away data. + */ + if (!sc->sc_full_duplex && + (sc->sc_mode & AUMODE_RECORD)) { + uio->uio_offset += uio->uio_resid; + uio->uio_resid = 0; + DPRINTF(("audio_write: half-dpx read busy\n")); + return (0); + } + + if (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) { + n = min(sc->sc_playdrop, uio->uio_resid); + DPRINTF(("audio_write: playdrop %d\n", n)); + uio->uio_offset += n; + uio->uio_resid -= n; + sc->sc_playdrop -= n; + if (uio->uio_resid == 0) + return 0; + } + +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_write: sr=%ld, enc=%d, prec=%d, chan=%d, sw=%p, fact=%d\n", + sc->sc_pparams.sample_rate, sc->sc_pparams.encoding, + sc->sc_pparams.precision, sc->sc_pparams.channels, + sc->sc_pparams.sw_code, sc->sc_pparams.factor); +#endif + + error = 0; + while (uio->uio_resid > 0 && !error) { + s = splaudio(); + while (cb->used >= cb->usedhigh) { + DPRINTF(("audio_write: sleep used=%d lowat=%d hiwat=%d\n", + cb->used, cb->usedlow, cb->usedhigh)); + if (ioflag & IO_NDELAY) { + splx(s); + return (EWOULDBLOCK); + } + error = audio_sleep(&sc->sc_wchan, "aud_wr"); + if (error) { + splx(s); + return error; + } + } + used = cb->used; + inp = cb->inp; + cb->copying = 1; + splx(s); + cc = cb->usedhigh - used; /* maximum to write */ + n = cb->end - inp; + if (sc->sc_pparams.factor != 1) { + /* Compensate for software coding expansion factor. */ + n /= sc->sc_pparams.factor; + cc /= sc->sc_pparams.factor; + } + if (n < cc) + cc = n; /* don't write beyond end of buffer */ + if (uio->uio_resid < cc) + cc = uio->uio_resid; /* and no more than we have */ + +#ifdef DIAGNOSTIC + /* + * This should never happen since the block size and and + * block pointers are always nicely aligned. + */ + if (cc == 0) { + printf("audio_write: cc == 0, swcode=%p, factor=%d\n", + sc->sc_pparams.sw_code, sc->sc_pparams.factor); + cb->copying = 0; + return EINVAL; + } +#endif +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_write: uiomove cc=%d inp=%p, left=%d\n", cc, inp, uio->uio_resid); +#endif + n = uio->uio_resid; + error = uiomove(inp, cc, uio); + cc = n - uio->uio_resid; /* number of bytes actually moved */ +#ifdef AUDIO_DEBUG + if (error) + printf("audio_write:(1) uiomove failed %d; cc=%d inp=%p\n", + error, cc, inp); +#endif + /* + * Continue even if uiomove() failed because we may have + * gotten a partial block. + */ + + if (sc->sc_pparams.sw_code) { + sc->sc_pparams.sw_code(sc->hw_hdl, inp, cc); + /* Adjust count after the expansion. */ + cc *= sc->sc_pparams.factor; +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_write: expanded cc=%d\n", cc); +#endif + } + + einp = cb->inp + cc; + if (einp >= cb->end) + einp = cb->start; + + s = splaudio(); + /* + * This is a very suboptimal way of keeping track of + * silence in the buffer, but it is simple. + */ + sc->sc_sil_count = 0; + + cb->inp = einp; + cb->used += cc; + /* If the interrupt routine wants the last block filled AND + * the copy did not fill the last block completely it needs to + * be padded. + */ + if (cb->needfill && + (inp - cb->start) / cb->blksize == + (einp - cb->start) / cb->blksize) { + /* Figure out how many bytes there is to a block boundary. */ + cc = cb->blksize - (einp - cb->start) % cb->blksize; + DPRINTF(("audio_write: partial fill %d\n", cc)); + } else + cc = 0; + cb->needfill = 0; + cb->copying = 0; + if (!sc->sc_pbus && !cb->pause) + error = audiostartp(sc); /* XXX should not clobber error */ + splx(s); + if (cc) { +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_write: fill %d\n", cc); +#endif + audio_fill_silence(&sc->sc_pparams, einp, cc); + } + } + return (error); +} + +int +audio_ioctl(dev, cmd, addr, flag, p) + dev_t dev; + int cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + struct audio_hw_if *hw = sc->hw_if; + struct audio_offset *ao; + int error = 0, s, offs, fd; + + DPRINTF(("audio_ioctl(%d,'%c',%d)\n", + IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd&0xff)); + switch (cmd) { + case FIONBIO: + /* All handled in the upper FS layer. */ + break; + + case FIOASYNC: + if (*(int *)addr) { + if (sc->sc_async_audio) + return (EBUSY); + sc->sc_async_audio = p; + DPRINTF(("audio_ioctl: FIOASYNC %p\n", p)); + } else + sc->sc_async_audio = 0; + break; + + case AUDIO_FLUSH: + DPRINTF(("AUDIO_FLUSH\n")); + audio_clear(sc); + s = splaudio(); + error = audio_initbufs(sc); + if (error) { + splx(s); + return error; + } + if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_pbus) + error = audiostartp(sc); + if (!error && + (sc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus) + error = audiostartr(sc); + splx(s); + break; + + /* + * Number of read (write) samples dropped. We don't know where or + * when they were dropped. + */ + case AUDIO_RERROR: + *(int *)addr = sc->sc_rr.drops; + break; + + case AUDIO_PERROR: + *(int *)addr = sc->sc_pr.drops; + break; + + /* + * Offsets into buffer. + */ + case AUDIO_GETIOFFS: + s = splaudio(); + /* figure out where next DMA will start */ + ao = (struct audio_offset *)addr; + ao->samples = sc->sc_rr.stamp; + ao->deltablks = (sc->sc_rr.stamp - sc->sc_rr.stamp_last) / sc->sc_rr.blksize; + sc->sc_rr.stamp_last = sc->sc_rr.stamp; + ao->offset = sc->sc_rr.inp - sc->sc_rr.start; + splx(s); + break; + + case AUDIO_GETOOFFS: + s = splaudio(); + /* figure out where next DMA will start */ + ao = (struct audio_offset *)addr; + offs = sc->sc_pr.outp - sc->sc_pr.start + sc->sc_pr.blksize; + if (sc->sc_pr.start + offs >= sc->sc_pr.end) + offs = 0; + ao->samples = sc->sc_pr.stamp; + ao->deltablks = (sc->sc_pr.stamp - sc->sc_pr.stamp_last) / sc->sc_pr.blksize; + sc->sc_pr.stamp_last = sc->sc_pr.stamp; + ao->offset = offs; + splx(s); + break; + + /* + * How many bytes will elapse until mike hears the first + * sample of what we write next? + */ + case AUDIO_WSEEK: + *(u_long *)addr = sc->sc_rr.used; + break; + + case AUDIO_SETINFO: + DPRINTF(("AUDIO_SETINFO mode=0x%x\n", sc->sc_mode)); + error = audiosetinfo(sc, (struct audio_info *)addr); + break; + + case AUDIO_GETINFO: + DPRINTF(("AUDIO_GETINFO\n")); + error = audiogetinfo(sc, (struct audio_info *)addr); + break; + + case AUDIO_DRAIN: + DPRINTF(("AUDIO_DRAIN\n")); + error = audio_drain(sc); + if (!error && hw->drain) + error = hw->drain(sc->hw_hdl); + break; + + case AUDIO_GETDEV: + DPRINTF(("AUDIO_GETDEV\n")); + error = hw->getdev(sc->hw_hdl, (audio_device_t *)addr); + break; + + case AUDIO_GETENC: + DPRINTF(("AUDIO_GETENC\n")); + error = hw->query_encoding(sc->hw_hdl, (struct audio_encoding *)addr); + break; + +#ifdef COMPAT_12 + /* GETPROPS contains the same info (and more) */ + case AUDIO_GETFD: + DPRINTF(("AUDIO_GETFD\n")); + *(int *)addr = + (hw->get_props(sc->hw_hdl) & AUDIO_PROP_FULLDUPLEX) != 0; + break; +#endif + case AUDIO_SETFD: + DPRINTF(("AUDIO_SETFD\n")); + fd = *(int *)addr; + if (hw->get_props(sc->hw_hdl) & AUDIO_PROP_FULLDUPLEX) { + if (hw->setfd) + error = hw->setfd(sc->hw_hdl, fd); + else + error = 0; + if (!error) + sc->sc_full_duplex = fd; + } else { + if (fd) + error = ENOTTY; + else + error = 0; + } + break; + + case AUDIO_GETPROPS: + DPRINTF(("AUDIO_GETPROPS\n")); + *(int *)addr = hw->get_props(sc->hw_hdl); + break; + + default: + DPRINTF(("audio_ioctl: unknown ioctl\n")); + error = EINVAL; + break; + } + DPRINTF(("audio_ioctl(%d,'%c',%d) result %d\n", + IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd&0xff, error)); + return (error); +} + +int +audio_select(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + int s = splaudio(); + +#if 0 + DPRINTF(("audio_select: rw=%d mode=%d rblks=%d rr.nblk=%d\n", + rw, sc->sc_mode, sc->sc_rblks, sc->rr.nblk)); +#endif + switch (rw) { + + case FREAD: + if ((sc->sc_mode & AUMODE_PLAY) ? + sc->sc_pr.stamp > sc->sc_wstamp : + sc->sc_rr.used > sc->sc_rr.usedlow) { + splx(s); + return (1); + } + selrecord(p, &sc->sc_rsel); + break; + + case FWRITE: + if (sc->sc_mode & AUMODE_RECORD || + sc->sc_pr.used <= sc->sc_pr.usedlow) { + splx(s); + return (1); + } + selrecord(p, &sc->sc_wsel); + break; + } + splx(s); + return (0); +} + +int +audio_mmap(dev, off, prot) + dev_t dev; + int off, prot; +{ + int s; + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + struct audio_hw_if *hw = sc->hw_if; + struct audio_ringbuffer *cb; + + DPRINTF(("audio_mmap: off=%d, prot=%d\n", off, prot)); + + if (!(hw->get_props(sc->hw_hdl) & AUDIO_PROP_MMAP) || !hw->mappage) + return -1; +#if 0 +/* XXX + * The idea here was to use the protection to determine if + * we are mapping the read or write buffer, but it fails. + * The VM system is broken in (at least) two ways. + * 1) If you map memory VM_PROT_WRITE you SIGSEGV + * when writing to it, so VM_PROT_READ|VM_PROT_WRITE + * has to be used for mmapping the play buffer. + * 2) Even if calling mmap() with VM_PROT_READ|VM_PROT_WRITE + * audio_mmap will get called at some point with VM_PROT_READ + * only. + * So, alas, we always map the play buffer for now. + */ + if (prot == (VM_PROT_READ|VM_PROT_WRITE) || + prot == VM_PROT_WRITE) + cb = &sc->sc_pr; + else if (prot == VM_PROT_READ) + cb = &sc->sc_rr; + else + return -1; +#else + cb = &sc->sc_pr; +#endif + + if (off >= cb->bufsize) + return -1; + if (!cb->mmapped) { + cb->mmapped = 1; + if (cb == &sc->sc_pr) { + audio_fill_silence(&sc->sc_pparams, cb->start, cb->bufsize); + s = splaudio(); + if (!sc->sc_pbus) + (void)audiostartp(sc); + splx(s); + } else { + s = splaudio(); + if (!sc->sc_rbus) + (void)audiostartr(sc); + splx(s); + } + } + + return hw->mappage(sc->hw_hdl, cb->start, off, prot); +} + +int +audiostartr(sc) + struct audio_softc *sc; +{ + int error; + + DPRINTF(("audiostartr: start=%p used=%d(hi=%d) mmapped=%d\n", + sc->sc_rr.start, sc->sc_rr.used, sc->sc_rr.usedhigh, + sc->sc_rr.mmapped)); + + error = sc->hw_if->start_input(sc->hw_hdl, sc->sc_rr.start, + sc->sc_rr.blksize, audio_rint, (void *)sc); + if (error) { + DPRINTF(("audiostartr failed: %d\n", error)); + return error; + } + sc->sc_rbus = 1; + return 0; +} + +int +audiostartp(sc) + struct audio_softc *sc; +{ + int error; + + DPRINTF(("audiostartp: start=%p used=%d(hi=%d) mmapped=%d\n", + sc->sc_pr.start, sc->sc_pr.used, sc->sc_pr.usedhigh, + sc->sc_pr.mmapped)); + + if (sc->sc_pr.used >= sc->sc_pr.blksize || sc->sc_pr.mmapped) { + error = sc->hw_if->start_output(sc->hw_hdl, sc->sc_pr.outp, + sc->sc_pr.blksize, audio_pint, (void *)sc); + if (error) { + DPRINTF(("audiostartp failed: %d\n", error)); + return error; + } + sc->sc_pbus = 1; + } + return 0; +} + +/* + * When the play interrupt routine finds that the write isn't keeping + * the buffer filled it will insert silence in the buffer to make up + * for this. The part of the buffer that is filled with silence + * is kept track of in a very approcimate way: it starts at sc_sil_start + * and extends sc_sil_count bytes. If the writer doesn't write sc_sil_count + * get to encompass the whole buffer after which no more filling needs + * to be done. When the writer starts again sc_sil_count is set to 0. + */ +/* XXX + * Putting silence into the output buffer should not really be done + * at splaudio, but there is no softaudio level to do it at yet. + */ +static __inline void +audio_pint_silence(sc, cb, inp, cc) + struct audio_softc *sc; + struct audio_ringbuffer *cb; + u_char *inp; + int cc; +{ + u_char *s, *e, *p, *q; + + if (sc->sc_sil_count > 0) { + s = sc->sc_sil_start; /* start of silence */ + e = s + sc->sc_sil_count; /* end of silence, may be beyond end */ + p = inp; /* adjusted pointer to area to fill */ + if (p < s) + p += cb->end - cb->start; + q = p+cc; + /* Check if there is already silence. */ + if (!(s <= p && p < e && + s <= q && q <= e)) { + if (s <= p) + sc->sc_sil_count = max(sc->sc_sil_count, q - s); +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_pint_silence: fill cc=%d inp=%p, count=%d size=%d\n", + cc, inp, sc->sc_sil_count, (int)(cb->end - cb->start)); +#endif + audio_fill_silence(&sc->sc_pparams, inp, cc); + } else { +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_pint_silence: already silent cc=%d inp=%p\n", cc, inp); +#endif + + } + } else { + sc->sc_sil_start = inp; + sc->sc_sil_count = cc; +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_pint_silence: start fill %p %d\n", inp, cc); +#endif + audio_fill_silence(&sc->sc_pparams, inp, cc); + } +} + +/* + * Called from HW driver module on completion of dma output. + * Start output of new block, wrap in ring buffer if needed. + * If no more buffers to play, output zero instead. + * Do a wakeup if necessary. + */ +void +audio_pint(v) + void *v; +{ + struct audio_softc *sc = v; + struct audio_hw_if *hw = sc->hw_if; + struct audio_ringbuffer *cb = &sc->sc_pr; + u_char *inp; + int cc, ccr; + int error; + + cb->outp += cb->blksize; + if (cb->outp >= cb->end) + cb->outp = cb->start; + cb->stamp += cb->blksize / sc->sc_pparams.factor; + if (cb->mmapped) { +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_pint: mmapped outp=%p cc=%d inp=%p\n", + cb->outp, cb->blksize, cb->inp); +#endif + (void)hw->start_output(sc->hw_hdl, cb->outp, cb->blksize, + audio_pint, (void *)sc); + return; + } + +#ifdef AUDIO_INTR_TIME + { + struct timeval tv; + u_long t; + microtime(&tv); + t = tv.tv_usec + 1000000 * tv.tv_sec; + if (sc->sc_pnintr) { + long lastdelta, totdelta; + lastdelta = t - sc->sc_plastintr - sc->sc_pblktime; + if (lastdelta > sc->sc_pblktime / 5) { + printf("audio: play interrupt(%d) off relative by %ld us (%lu)\n", + sc->sc_pnintr, lastdelta, sc->sc_pblktime); + } + totdelta = t - sc->sc_pfirstintr - sc->sc_pblktime * sc->sc_pnintr; + if (totdelta > sc->sc_pblktime / 2) { + sc->sc_pnintr++; + printf("audio: play interrupt(%d) off absolute by %ld us (%lu)\n", + sc->sc_pnintr, totdelta, sc->sc_pblktime); + sc->sc_pnintr++; /* avoid repeated messages */ + } + } else + sc->sc_pfirstintr = t; + sc->sc_plastintr = t; + sc->sc_pnintr++; + } +#endif + + cb->used -= cb->blksize; + if (cb->used < cb->blksize) { + /* we don't have a full block to use */ + if (cb->copying) { + /* writer is in progress, don't disturb */ + cb->needfill = 1; +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_pint: copying in progress\n"); +#endif + } else { + inp = cb->inp; + cc = cb->blksize - (inp - cb->start) % cb->blksize; + ccr = cc / sc->sc_pparams.factor; + if (cb->pause) + cb->pdrops += ccr; + else { + cb->drops += ccr; + sc->sc_playdrop += ccr; + } + audio_pint_silence(sc, cb, inp, cc); + inp += cc; + if (inp >= cb->end) + inp = cb->start; + cb->inp = inp; + cb->used += cc; + + /* Clear next block so we keep ahead of the DMA. */ + if (cb->used + cc < cb->usedhigh) + audio_pint_silence(sc, cb, inp, cb->blksize); + } + } + +#ifdef AUDIO_DEBUG + if (audiodebug > 3) + printf("audio_pint: outp=%p cc=%d\n", cb->outp, cb->blksize); +#endif + error = hw->start_output(sc->hw_hdl, cb->outp, cb->blksize, + audio_pint, (void *)sc); + if (error) { + /* XXX does this really help? */ + DPRINTF(("audio_pint restart failed: %d\n", error)); + audio_clear(sc); + } + +#ifdef AUDIO_DEBUG + if (audiodebug > 3) + printf("audio_pint: mode=%d pause=%d used=%d lowat=%d\n", + sc->sc_mode, cb->pause, cb->used, cb->usedlow); +#endif + if ((sc->sc_mode & AUMODE_PLAY) && !cb->pause) { + if (cb->used <= cb->usedlow) { + audio_wakeup(&sc->sc_wchan); + selwakeup(&sc->sc_wsel); + if (sc->sc_async_audio) { +#ifdef AUDIO_DEBUG + if (audiodebug > 3) + printf("audio_pint: sending SIGIO %p\n", + sc->sc_async_audio); +#endif + psignal(sc->sc_async_audio, SIGIO); + } + } + } + + /* Possible to return one or more "phantom blocks" now. */ + if (!sc->sc_full_duplex && sc->sc_rchan) { + audio_wakeup(&sc->sc_rchan); + selwakeup(&sc->sc_rsel); + if (sc->sc_async_audio) + psignal(sc->sc_async_audio, SIGIO); + } +} + +/* + * Called from HW driver module on completion of dma input. + * Mark it as input in the ring buffer (fiddle pointers). + * Do a wakeup if necessary. + */ +void +audio_rint(v) + void *v; +{ + struct audio_softc *sc = v; + struct audio_hw_if *hw = sc->hw_if; + struct audio_ringbuffer *cb = &sc->sc_rr; + int error; + + cb->inp += cb->blksize; + if (cb->inp >= cb->end) + cb->inp = cb->start; + cb->stamp += cb->blksize; + if (cb->mmapped) { +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_rint: mmapped inp=%p cc=%d\n", + cb->inp, cb->blksize); +#endif + (void)hw->start_input(sc->hw_hdl, cb->inp, cb->blksize, + audio_rint, (void *)sc); + return; + } + +#ifdef AUDIO_INTR_TIME + { + struct timeval tv; + u_long t; + microtime(&tv); + t = tv.tv_usec + 1000000 * tv.tv_sec; + if (sc->sc_rnintr) { + long lastdelta, totdelta; + lastdelta = t - sc->sc_rlastintr - sc->sc_rblktime; + if (lastdelta > sc->sc_rblktime / 5) { + printf("audio: record interrupt(%d) off relative by %ld us (%lu)\n", + sc->sc_rnintr, lastdelta, sc->sc_rblktime); + } + totdelta = t - sc->sc_rfirstintr - sc->sc_rblktime * sc->sc_rnintr; + if (totdelta > sc->sc_rblktime / 2) { + sc->sc_rnintr++; + printf("audio: record interrupt(%d) off absolute by %ld us (%lu)\n", + sc->sc_rnintr, totdelta, sc->sc_rblktime); + sc->sc_rnintr++; /* avoid repeated messages */ + } + } else + sc->sc_rfirstintr = t; + sc->sc_rlastintr = t; + sc->sc_rnintr++; + } +#endif + + cb->used += cb->blksize; + if (cb->pause) { +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_rint: pdrops %lu\n", cb->pdrops); +#endif + cb->pdrops += cb->blksize; + cb->outp += cb->blksize; + cb->used -= cb->blksize; + } else if (cb->used + cb->blksize >= cb->usedhigh && !cb->copying) { +#ifdef AUDIO_DEBUG + if (audiodebug > 1) + printf("audio_rint: drops %lu\n", cb->drops); +#endif + cb->drops += cb->blksize; + cb->outp += cb->blksize; + cb->used -= cb->blksize; + } + +#ifdef AUDIO_DEBUG + if (audiodebug > 2) + printf("audio_rint: inp=%p cc=%d used=%d\n", + cb->inp, cb->blksize, cb->used); +#endif + error = hw->start_input(sc->hw_hdl, cb->inp, cb->blksize, + audio_rint, (void *)sc); + if (error) { + /* XXX does this really help? */ + DPRINTF(("audio_rint: restart failed: %d\n", error)); + audio_clear(sc); + } + + audio_wakeup(&sc->sc_rchan); + selwakeup(&sc->sc_rsel); + if (sc->sc_async_audio) + psignal(sc->sc_async_audio, SIGIO); +} + +int +audio_check_params(p) + struct audio_params *p; +{ +#if defined(COMPAT_12) + if (p->encoding == AUDIO_ENCODING_PCM16) { + if (p->precision == 8) + p->encoding = AUDIO_ENCODING_ULINEAR; + else + p->encoding = AUDIO_ENCODING_SLINEAR; + } else if (p->encoding == AUDIO_ENCODING_PCM8) { + if (p->precision == 8) + p->encoding = AUDIO_ENCODING_ULINEAR; + else + return EINVAL; + } +#endif + + if (p->encoding == AUDIO_ENCODING_SLINEAR) +#if BYTE_ORDER == LITTLE_ENDIAN + p->encoding = AUDIO_ENCODING_SLINEAR_LE; +#else + p->encoding = AUDIO_ENCODING_SLINEAR_BE; +#endif + if (p->encoding == AUDIO_ENCODING_ULINEAR) +#if BYTE_ORDER == LITTLE_ENDIAN + p->encoding = AUDIO_ENCODING_ULINEAR_LE; +#else + p->encoding = AUDIO_ENCODING_ULINEAR_BE; +#endif + + switch (p->encoding) { + case AUDIO_ENCODING_ULAW: + case AUDIO_ENCODING_ALAW: + case AUDIO_ENCODING_ADPCM: + if (p->precision != 8) + return (EINVAL); + break; + case AUDIO_ENCODING_SLINEAR_LE: + case AUDIO_ENCODING_SLINEAR_BE: + case AUDIO_ENCODING_ULINEAR_LE: + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision != 8 && p->precision != 16) + return (EINVAL); + break; + default: + return (EINVAL); + } + + if (p->channels < 1 || p->channels > 8) /* sanity check # of channels */ + return (EINVAL); + + return (0); +} + +int +audiosetinfo(sc, ai) + struct audio_softc *sc; + struct audio_info *ai; +{ + struct audio_prinfo *r = &ai->record, *p = &ai->play; + int cleared; + int s, setmode; + int error; + struct audio_hw_if *hw = sc->hw_if; + mixer_ctrl_t ct; + struct audio_params pp, rp; + int np, nr; + unsigned int blks; + int oldpblksize, oldrblksize; + int rbus, pbus; + + if (hw == 0) /* HW has not attached */ + return(ENXIO); + + rbus = sc->sc_rbus; + pbus = sc->sc_pbus; + error = 0; + cleared = 0; + + pp = sc->sc_pparams; /* Temporary encoding storage in */ + rp = sc->sc_rparams; /* case setting the modes fails. */ + nr = np = 0; + + if (p->sample_rate != ~0) { + pp.sample_rate = p->sample_rate; + np++; + } + if (r->sample_rate != ~0) { + rp.sample_rate = r->sample_rate; + nr++; + } + if (p->encoding != ~0) { + pp.encoding = p->encoding; + np++; + } + if (r->encoding != ~0) { + rp.encoding = r->encoding; + nr++; + } + if (p->precision != ~0) { + pp.precision = p->precision; + np++; + } + if (r->precision != ~0) { + rp.precision = r->precision; + nr++; + } + if (p->channels != ~0) { + pp.channels = p->channels; + np++; + } + if (r->channels != ~0) { + rp.channels = r->channels; + nr++; + } +#ifdef AUDIO_DEBUG + if (audiodebug && nr) + audio_print_params("Setting record params", &rp); + if (audiodebug && np) + audio_print_params("Setting play params", &pp); +#endif + if (nr && (error = audio_check_params(&rp))) + return error; + if (np && (error = audio_check_params(&pp))) + return error; + setmode = 0; + if (nr) { + if (!cleared) + audio_clear(sc); + cleared = 1; + rp.sw_code = 0; + rp.factor = 1; + setmode |= AUMODE_RECORD; + } + if (np) { + if (!cleared) + audio_clear(sc); + cleared = 1; + pp.sw_code = 0; + pp.factor = 1; + setmode |= AUMODE_PLAY; + } + + if (ai->mode != ~0) { + if (!cleared) + audio_clear(sc); + cleared = 1; + sc->sc_mode = ai->mode; + if (sc->sc_mode & AUMODE_PLAY_ALL) + sc->sc_mode |= AUMODE_PLAY; + if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_full_duplex) + /* Play takes precedence */ + sc->sc_mode &= ~AUMODE_RECORD; + } + + if (setmode) { + int indep = hw->get_props(sc->hw_hdl) & AUDIO_PROP_INDEPENDENT; + if (!indep) { + if (setmode == AUMODE_RECORD) + pp = rp; + else if (setmode == AUMODE_PLAY) + rp = pp; + } + error = hw->set_params(sc->hw_hdl, setmode, sc->sc_mode, &pp, &rp); + if (error) + return (error); + if (!indep) { + if (setmode == AUMODE_RECORD) { + pp.sample_rate = rp.sample_rate; + pp.encoding = rp.encoding; + pp.channels = rp.channels; + pp.precision = rp.precision; + } else if (setmode == AUMODE_PLAY) { + rp.sample_rate = pp.sample_rate; + rp.encoding = pp.encoding; + rp.channels = pp.channels; + rp.precision = pp.precision; + } + } + if (setmode & AUMODE_RECORD) + sc->sc_rparams = rp; + if (setmode & AUMODE_PLAY) + sc->sc_pparams = pp; + } + + oldpblksize = sc->sc_pr.blksize; + oldrblksize = sc->sc_rr.blksize; + /* Play params can affect the record params, so recalculate blksize. */ + if (nr || np) { + audio_calc_blksize(sc, AUMODE_RECORD); + audio_calc_blksize(sc, AUMODE_PLAY); + } +#ifdef AUDIO_DEBUG + if (audiodebug > 1 && nr) + audio_print_params("After setting record params", &sc->sc_rparams); + if (audiodebug > 1 && np) + audio_print_params("After setting play params", &sc->sc_pparams); +#endif + + if (p->port != ~0) { + if (!cleared) + audio_clear(sc); + cleared = 1; + + error = hw->set_out_port(sc->hw_hdl, p->port); + if (error) + return(error); + } + if (r->port != ~0) { + if (!cleared) + audio_clear(sc); + cleared = 1; + + error = hw->set_in_port(sc->hw_hdl, r->port); + if (error) + return(error); + } + if (p->gain != ~0) { + ct.dev = hw->get_out_port(sc->hw_hdl); + ct.type = AUDIO_MIXER_VALUE; + ct.un.value.num_channels = 1; + ct.un.value.level[AUDIO_MIXER_LEVEL_MONO] = p->gain; + error = hw->set_port(sc->hw_hdl, &ct); + if (error) + return(error); + } + if (r->gain != ~0) { + ct.dev = hw->get_in_port(sc->hw_hdl); + ct.type = AUDIO_MIXER_VALUE; + ct.un.value.num_channels = 1; + ct.un.value.level[AUDIO_MIXER_LEVEL_MONO] = r->gain; + error = hw->set_port(sc->hw_hdl, &ct); + if (error) + return(error); + } + + if (p->pause != (u_char)~0) { + sc->sc_pr.pause = p->pause; + if (!p->pause && !sc->sc_pbus) { + s = splaudio(); + error = audiostartp(sc); + splx(s); + if (error) + return error; + } + } + if (r->pause != (u_char)~0) { + sc->sc_rr.pause = r->pause; + if (!r->pause && !sc->sc_rbus) { + s = splaudio(); + error = audiostartr(sc); + splx(s); + if (error) + return error; + } + } + + if (ai->blocksize != ~0) { + /* Block size specified explicitly. */ + if (!cleared) + audio_clear(sc); + cleared = 1; + + if (ai->blocksize == 0) { + audio_calc_blksize(sc, AUMODE_RECORD); + audio_calc_blksize(sc, AUMODE_PLAY); + sc->sc_blkset = 0; + } else { + int bs = ai->blocksize; + if (hw->round_blocksize) + bs = hw->round_blocksize(sc->hw_hdl, bs); + sc->sc_pr.blksize = sc->sc_rr.blksize = bs; + sc->sc_blkset = 1; + } + } + + if (ai->mode != ~0) { + if (sc->sc_mode & AUMODE_PLAY) + audio_init_play(sc); + if (sc->sc_mode & AUMODE_RECORD) + audio_init_record(sc); + } + + if (hw->commit_settings) { + error = hw->commit_settings(sc->hw_hdl); + if (error) + return (error); + if (p->gain != ~0 || r->gain != ~0) + mixer_signal(sc); + } + + if (cleared) { + s = splaudio(); + error = audio_initbufs(sc); + if (error) goto err; + if (sc->sc_pr.blksize != oldpblksize || + sc->sc_rr.blksize != oldrblksize) + audio_calcwater(sc); + if ((sc->sc_mode & AUMODE_PLAY) && + pbus && !sc->sc_pbus) + error = audiostartp(sc); + if (!error && + (sc->sc_mode & AUMODE_RECORD) && + rbus && !sc->sc_rbus) + error = audiostartr(sc); + err: + splx(s); + if (error) + return error; + } + + /* Change water marks after initializing the buffers. */ + if (ai->hiwat != ~0) { + blks = ai->hiwat; + if (blks > sc->sc_pr.maxblks) + blks = sc->sc_pr.maxblks; + if (blks < 1) + blks = 1; + sc->sc_pr.usedhigh = blks * sc->sc_pr.blksize; + } + if (ai->lowat != ~0) { + blks = ai->lowat; + if (blks > sc->sc_pr.maxblks - 1) + blks = sc->sc_pr.maxblks - 1; + sc->sc_pr.usedlow = blks * sc->sc_pr.blksize; + } + + return (0); +} + +int +audiogetinfo(sc, ai) + struct audio_softc *sc; + struct audio_info *ai; +{ + struct audio_prinfo *r = &ai->record, *p = &ai->play; + struct audio_hw_if *hw = sc->hw_if; + mixer_ctrl_t ct; + + if (hw == 0) /* HW has not attached */ + return(ENXIO); + + p->sample_rate = sc->sc_pparams.sample_rate; + r->sample_rate = sc->sc_rparams.sample_rate; + p->channels = sc->sc_pparams.channels; + r->channels = sc->sc_rparams.channels; + p->precision = sc->sc_pparams.precision; + r->precision = sc->sc_rparams.precision; + p->encoding = sc->sc_pparams.encoding; + r->encoding = sc->sc_rparams.encoding; + + r->port = hw->get_in_port(sc->hw_hdl); + p->port = hw->get_out_port(sc->hw_hdl); + + ct.dev = r->port; + ct.type = AUDIO_MIXER_VALUE; + ct.un.value.num_channels = 1; + if (hw->get_port(sc->hw_hdl, &ct) == 0) + r->gain = ct.un.value.level[AUDIO_MIXER_LEVEL_MONO]; + else + r->gain = AUDIO_MAX_GAIN/2; + + ct.dev = p->port; + ct.un.value.num_channels = 1; + if (hw->get_port(sc->hw_hdl, &ct) == 0) + p->gain = ct.un.value.level[AUDIO_MIXER_LEVEL_MONO]; + else + p->gain = AUDIO_MAX_GAIN/2; + + p->seek = sc->sc_pr.used; + r->seek = sc->sc_rr.used; + + p->samples = sc->sc_pr.stamp - sc->sc_pr.drops; + r->samples = sc->sc_rr.stamp - sc->sc_rr.drops; + + p->eof = sc->sc_eof; + r->eof = 0; + + p->pause = sc->sc_pr.pause; + r->pause = sc->sc_rr.pause; + + p->error = sc->sc_pr.drops != 0; + r->error = sc->sc_rr.drops != 0; + + p->waiting = r->waiting = 0; /* open never hangs */ + + p->open = (sc->sc_open & AUOPEN_WRITE) != 0; + r->open = (sc->sc_open & AUOPEN_READ) != 0; + + p->active = sc->sc_pbus; + r->active = sc->sc_rbus; + + ai->buffersize = sc->sc_pr.bufsize; + ai->blocksize = sc->sc_pr.blksize; + ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize; + ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize; + ai->backlog = 0; /* unused */ + ai->mode = sc->sc_mode; + + return (0); +} + +/* + * Mixer driver + */ +int +mixer_open(dev, flags, ifmt, p) + dev_t dev; + int flags, ifmt; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc; + + if (unit >= new_audio_cd.cd_ndevs || + (sc = new_audio_cd.cd_devs[unit]) == NULL) + return ENXIO; + + if (!sc->hw_if) + return (ENXIO); + + DPRINTF(("mixer_open: dev=0x%x flags=0x%x sc=%p\n", dev, flags, sc)); + + return (0); +} + +/* + * Remove a process from those to be signalled on mixer activity. + */ +static void +mixer_remove(sc, p) + struct audio_softc *sc; + struct proc *p; +{ + struct mixer_asyncs **pm, *m; + + for(pm = &sc->sc_async_mixer; *pm; pm = &(*pm)->next) { + if ((*pm)->proc == p) { + m = *pm; + *pm = m->next; + free(m, M_DEVBUF); + return; + } + } +} + +/* + * Signal all processes waitinf for the mixer. + */ +static void +mixer_signal(sc) + struct audio_softc *sc; +{ + struct mixer_asyncs *m; + + for(m = sc->sc_async_mixer; m; m = m->next) + psignal(m->proc, SIGIO); +} + +/* + * Close a mixer device + */ +/* ARGSUSED */ +int +mixer_close(dev, flags, ifmt, p) + dev_t dev; + int flags, ifmt; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + + DPRINTF(("mixer_close: unit %d\n", AUDIOUNIT(dev))); + + mixer_remove(sc, p); + + return (0); +} + +int +mixer_ioctl(dev, cmd, addr, flag, p) + dev_t dev; + int cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + int unit = AUDIOUNIT(dev); + struct audio_softc *sc = new_audio_cd.cd_devs[unit]; + struct audio_hw_if *hw = sc->hw_if; + int error = EINVAL; + + DPRINTF(("mixer_ioctl(%d,'%c',%d)\n", + IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd&0xff)); + + switch (cmd) { + case FIOASYNC: + mixer_remove(sc, p); /* remove old entry */ + if (*(int *)addr) { + struct mixer_asyncs *ma; + ma = malloc(sizeof (struct mixer_asyncs), M_DEVBUF, M_WAITOK); + ma->next = sc->sc_async_mixer; + ma->proc = p; + sc->sc_async_mixer = ma; + } + error = 0; + break; + + case AUDIO_GETDEV: + DPRINTF(("AUDIO_GETDEV\n")); + error = hw->getdev(sc->hw_hdl, (audio_device_t *)addr); + break; + + case AUDIO_MIXER_DEVINFO: + DPRINTF(("AUDIO_MIXER_DEVINFO\n")); + error = hw->query_devinfo(sc->hw_hdl, (mixer_devinfo_t *)addr); + break; + + case AUDIO_MIXER_READ: + DPRINTF(("AUDIO_MIXER_READ\n")); + error = hw->get_port(sc->hw_hdl, (mixer_ctrl_t *)addr); + break; + + case AUDIO_MIXER_WRITE: + DPRINTF(("AUDIO_MIXER_WRITE\n")); + error = hw->set_port(sc->hw_hdl, (mixer_ctrl_t *)addr); + if (!error && hw->commit_settings) + error = hw->commit_settings(sc->hw_hdl); + if (!error) + mixer_signal(sc); + break; + + default: + error = EINVAL; + break; + } + DPRINTF(("mixer_ioctl(%d,'%c',%d) result %d\n", + IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd&0xff, error)); + return (error); +} +#endif diff --git a/sys/arch/amiga/dev/audio_if.h b/sys/arch/amiga/dev/audio_if.h new file mode 100644 index 00000000000..7daecc79b34 --- /dev/null +++ b/sys/arch/amiga/dev/audio_if.h @@ -0,0 +1,172 @@ +/* $OpenBSD: audio_if.h,v 1.1 1997/10/07 11:04:59 niklas Exp $ */ +/* $NetBSD: audio_if.h,v 1.21 1997/09/06 01:14:49 augustss Exp $ */ + +/* + * Copyright (c) 1994 Havard Eidnes. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * Generic interface to hardware driver. + */ + +struct audio_softc; + +struct audio_params { + u_long sample_rate; /* sample rate */ + u_int encoding; /* e.g. ulaw, linear, etc */ + u_int precision; /* bits/sample */ + u_int channels; /* mono(1), stereo(2) */ + /* Software en/decode functions, set if SW coding required by HW */ + void (*sw_code)__P((void *, u_char *, int)); + int factor; /* coding space change */ +}; + +/* The default audio mode: 8 kHz mono ulaw */ +extern struct audio_params audio_default; + +struct audio_hw_if { + int (*open)__P((void *, int)); /* open hardware */ + void (*close)__P((void *)); /* close hardware */ + int (*drain)__P((void *)); /* Optional: drain buffers */ + + /* Encoding. */ + /* XXX should we have separate in/out? */ + int (*query_encoding)__P((void *, struct audio_encoding *)); + + /* Set the audio encoding parameters (record and play). + * Return 0 on success, or an error code if the + * requested parameters are impossible. + * The values in the params struct may be changed (e.g. rounding + * to the nearest sample rate.) + */ + int (*set_params)__P((void *, int, int, struct audio_params *, struct audio_params *)); + + /* Hardware may have some say in the blocksize to choose */ + int (*round_blocksize)__P((void *, int)); + + /* Ports (in/out ports) */ + int (*set_out_port)__P((void *, int)); + int (*get_out_port)__P((void *)); + int (*set_in_port)__P((void *, int)); + int (*get_in_port)__P((void *)); + + /* + * Changing settings may require taking device out of "data mode", + * which can be quite expensive. Also, audiosetinfo() may + * change several settings in quick succession. To avoid + * having to take the device in/out of "data mode", we provide + * this function which indicates completion of settings + * adjustment. + */ + int (*commit_settings)__P((void *)); + + /* Start input/output routines. These usually control DMA. */ + int (*init_output)__P((void *, void *, int)); + int (*init_input)__P((void *, void *, int)); + int (*start_output)__P((void *, void *, int, + void (*)(void *), void *)); + int (*start_input)__P((void *, void *, int, + void (*)(void *), void *)); + int (*halt_output)__P((void *)); + int (*halt_input)__P((void *)); + int (*cont_output)__P((void *)); + int (*cont_input)__P((void *)); + + int (*speaker_ctl)__P((void *, int)); +#define SPKR_ON 1 +#define SPKR_OFF 0 + + int (*getdev)__P((void *, struct audio_device *)); + int (*setfd)__P((void *, int)); + + /* Mixer (in/out ports) */ + int (*set_port)__P((void *, mixer_ctrl_t *)); + int (*get_port)__P((void *, mixer_ctrl_t *)); + + int (*query_devinfo)__P((void *, mixer_devinfo_t *)); + + /* Allocate/free memory for the ring buffer. Usually malloc/free. */ + void *(*alloc)__P((void *, unsigned long, int, int)); + void (*free)__P((void *, void *, int)); + unsigned long (*round_buffersize)__P((void *, unsigned long)); + int (*mappage)__P((void *, void *, int, int)); + + int (*get_props)__P((void *)); /* device properties */ +}; + +struct midi_info { + char *name; /* Name of MIDI hardware */ + int props; +}; +#define MIDI_PROP_OUT_INTR 1 + +struct midi_hw_if { + int (*open)__P((void *, int, /* open hardware */ + void (*)__P((void *, int)), + void (*)__P((void *)), + void *)); + void (*close)__P((void *)); /* close hardware */ + int (*output)__P((void *, int)); /* output a byte */ + void (*getinfo)__P((void *, struct midi_info *)); + int (*ioctl)__P((u_long, caddr_t, int, struct proc *)); +}; + +struct audio_attach_args { + struct audio_hw_if *ahw; + struct midi_hw_if *mhw; + void *hdl; + char audiodone, mididone; +}; + +/* Attach the MI driver(s) to the MD driver. */ +extern void audio_attach_mi __P((struct audio_hw_if *, struct midi_hw_if *, void *, struct device *)); + +/* Device identity flags */ +#define SOUND_DEVICE 0 +#define AUDIO_DEVICE 0x80 +#define AUDIOCTL_DEVICE 0xc0 +#define MIXER_DEVICE 0x10 + +#define AUDIOUNIT(x) (minor(x)&0x0f) +#define AUDIODEV(x) (minor(x)&0xf0) + +#define ISDEVSOUND(x) (AUDIODEV(minor(x)) == SOUND_DEVICE) +#define ISDEVAUDIO(x) (AUDIODEV(minor(x)) == AUDIO_DEVICE) +#define ISDEVAUDIOCTL(x) (AUDIODEV(minor(x)) == AUDIOCTL_DEVICE) +#define ISDEVMIXER(x) (AUDIODEV(minor(x)) == MIXER_DEVICE) + +#ifndef __i386__ +#define splaudio splbio /* XXX */ +#define IPL_AUDIO IPL_BIO /* XXX */ +#endif + diff --git a/sys/arch/amiga/dev/audiovar.h b/sys/arch/amiga/dev/audiovar.h new file mode 100644 index 00000000000..6e91e1c3b82 --- /dev/null +++ b/sys/arch/amiga/dev/audiovar.h @@ -0,0 +1,130 @@ +/* $OpenBSD: audiovar.h,v 1.1 1997/10/07 11:04:59 niklas Exp $ */ +/* $NetBSD: audiovar.h,v 1.16 1997/08/19 23:49:58 augustss Exp $ */ + +/* + * Copyright (c) 1991-1993 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * From: Header: audiovar.h,v 1.3 93/07/18 14:07:25 mccanne Exp (LBL) + */ + +/* + * Initial/default block duration is both configurable and patchable. + */ +#ifndef AUDIO_BLK_MS +#define AUDIO_BLK_MS 50 /* 50 ms */ +#endif + +#ifndef AU_RING_SIZE +#define AU_RING_SIZE 65536 +#endif + +#define AUMINBUF 512 +#define AUMINBLK 32 +#define AUMINNOBLK 3 +struct audio_ringbuffer { + int bufsize; /* allocated memory */ + int blksize; /* I/O block size */ + int maxblks; /* no of blocks in ring */ + u_char *start; /* start of buffer area */ + u_char *end; /* end of buffer area */ + u_char *inp; /* input pointer (to buffer) */ + u_char *outp; /* output pointer (from buffer) */ + int used; /* no of used bytes */ + int usedlow; /* start writer when used falls below this */ + int usedhigh; /* stop writer when used goes above this */ + u_long stamp; /* bytes transferred */ + u_long stamp_last; /* old value of bytes transferred */ + u_long drops; /* missed samples from over/underrun */ + u_long pdrops; /* paused samples */ + char pause; /* transfer is paused */ + char copying; /* data is being copied */ + char needfill; /* buffer needs filling when copying is done */ + char mmapped; /* device is mmap()-ed */ +}; + +/* + * Software state, per audio device. + */ +struct audio_softc { + struct device dev; + void *hw_hdl; /* Hardware driver handle */ + struct audio_hw_if *hw_if; /* Hardware interface */ + struct device *sc_dev; /* Hardware device struct */ + u_char sc_open; /* single use device */ +#define AUOPEN_READ 0x01 +#define AUOPEN_WRITE 0x02 + u_char sc_mode; /* bitmask for RECORD/PLAY */ + + struct selinfo sc_wsel; /* write selector */ + struct selinfo sc_rsel; /* read selector */ + struct proc *sc_async_audio; /* process who wants audio SIGIO */ + struct mixer_asyncs { + struct mixer_asyncs *next; + struct proc *proc; + } *sc_async_mixer; /* processes who want mixer SIGIO */ + + /* Sleep channels for reading and writing. */ + int sc_rchan; + int sc_wchan; + + /* Ring buffers, separate for record and play. */ + struct audio_ringbuffer sc_rr; /* Record ring */ + struct audio_ringbuffer sc_pr; /* Play ring */ + + u_char sc_blkset; /* Blocksize has been set */ + + u_char *sc_sil_start; /* start of silence in buffer */ + int sc_sil_count; /* # of silence bytes */ + + u_char sc_rbus; /* input dma in progress */ + u_char sc_pbus; /* output dma in progress */ + + struct audio_params sc_pparams; /* play encoding parameters */ + struct audio_params sc_rparams; /* record encoding parameters */ + + int sc_eof; /* EOF, i.e. zero sixed write, counter */ + u_long sc_wstamp; + u_long sc_playdrop; + + int sc_full_duplex; /* device in full duplex mode */ + +#ifdef AUDIO_INTR_TIME + u_long sc_pfirstintr; /* first time we saw a xmit interrupt */ + int sc_pnintr; /* number of interrupts */ + u_long sc_plastintr; /* last time we saw a xmit interrupt */ + long sc_pblktime; /* nominal time between interrupts */ + u_long sc_rfirstintr; /* first time we saw a rec interrupt */ + int sc_rnintr; /* number of interrupts */ + u_long sc_rlastintr; /* last time we saw a xrec interrupt */ + long sc_rblktime; /* nominal time between interrupts */ +#endif +}; diff --git a/sys/arch/amiga/include/audioio.h b/sys/arch/amiga/include/audioio.h new file mode 100644 index 00000000000..735f3ada0f7 --- /dev/null +++ b/sys/arch/amiga/include/audioio.h @@ -0,0 +1,281 @@ +/* $OpenBSD: audioio.h,v 1.1 1997/10/07 11:05:01 niklas Exp $ */ +/* $NetBSD: audioio.h,v 1.14 1997/08/26 19:03:57 augustss Exp $ */ + +/* + * Copyright (c) 1991-1993 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _SYS_AUDIOIO_H_ +#define _SYS_AUDIOIO_H_ + +/* + * Audio device + */ +struct audio_prinfo { + u_int sample_rate; /* sample rate in bit/s */ + u_int channels; /* number of channels, usually 1 or 2 */ + u_int precision; /* number of bits/sample */ + u_int encoding; /* data encoding (AUDIO_ENCODING_* above) */ + u_int gain; /* volume level */ + u_int port; /* selected I/O port */ + u_long seek; /* BSD extension */ + u_int ispare[3]; + /* Current state of device: */ + u_int samples; /* number of samples */ + u_int eof; /* End Of File (zero-size writes) counter */ + u_char pause; /* non-zero if paused, zero to resume */ + u_char error; /* non-zero if underflow/overflow ocurred */ + u_char waiting; /* non-zero if another process hangs in open */ + u_char cspare[3]; + u_char open; /* non-zero if currently open */ + u_char active; /* non-zero if I/O is currently active */ +}; +typedef struct audio_prinfo audio_prinfo_t; + +struct audio_info { + struct audio_prinfo play; /* Info for play (output) side */ + struct audio_prinfo record; /* Info for record (input) side */ + u_int buffersize; /* total size audio buffer */ + /* BSD extensions */ + u_int blocksize; /* H/W read/write block size */ + u_int hiwat; /* output high water mark */ + u_int lowat; /* output low water mark */ + u_int backlog; /* samples of output backlog to gen. */ + u_int mode; /* current device mode */ +#define AUMODE_PLAY 0x01 +#define AUMODE_RECORD 0x02 +#define AUMODE_PLAY_ALL 0x04 /* don't do real-time correction */ +}; +typedef struct audio_info audio_info_t; + +#ifdef _KERNEL +#define AUDIO_INITINFO(p)\ + { register int n = sizeof(struct audio_info); \ + register u_char *q = (u_char *) p; \ + while (n-- > 0) *q++ = 0xff; } + +#else +#define AUDIO_INITINFO(p)\ + (void)memset((void *)(p), 0xff, sizeof(struct audio_info)) +#endif + +/* + * Parameter for the AUDIO_GETDEV ioctl to determine current + * audio devices. + */ +#define MAX_AUDIO_DEV_LEN 16 +typedef struct audio_device { + char name[MAX_AUDIO_DEV_LEN]; + char version[MAX_AUDIO_DEV_LEN]; + char config[MAX_AUDIO_DEV_LEN]; +} audio_device_t; + +typedef struct audio_offset { + u_int samples; /* Total number of bytes transferred */ + u_int deltablks; /* Blocks transferred since last checked */ + u_int offset; /* Physical transfer offset in buffer */ +} audio_offset_t; + +/* + * Supported audio encodings + */ +/* Encoding ID's */ +#define AUDIO_ENCODING_NONE 0 /* no encoding assigned */ +#define AUDIO_ENCODING_ULAW 1 +#define AUDIO_ENCODING_ALAW 2 +#define AUDIO_ENCODING_PCM16 3 /* obsolete */ +#define AUDIO_ENCODING_LINEAR AUDIO_ENCODING_PCM16 /* obsolete */ +#define AUDIO_ENCODING_PCM8 4 /* obsolete */ +#define AUDIO_ENCODING_ADPCM 5 +#define AUDIO_ENCODING_SLINEAR_LE 6 +#define AUDIO_ENCODING_SLINEAR_BE 7 +#define AUDIO_ENCODING_ULINEAR_LE 8 +#define AUDIO_ENCODING_ULINEAR_BE 9 +#define AUDIO_ENCODING_SLINEAR 10 +#define AUDIO_ENCODING_ULINEAR 11 + +typedef struct audio_encoding { + int index; + char name[MAX_AUDIO_DEV_LEN]; + int encoding; + int precision; + int flags; +#define AUDIO_ENCODINGFLAG_EMULATED 1 /* software emulation mode */ +} audio_encoding_t; + +/* + * Audio device operations + */ +#define AUDIO_GETINFO _IOR('A', 21, struct audio_info) +#define AUDIO_SETINFO _IOWR('A', 22, struct audio_info) +#define AUDIO_DRAIN _IO('A', 23) +#define AUDIO_FLUSH _IO('A', 24) +#define AUDIO_WSEEK _IOR('A', 25, u_long) +#define AUDIO_RERROR _IOR('A', 26, int) +#define AUDIO_GETDEV _IOR('A', 27, struct audio_device) +#define AUDIO_GETENC _IOWR('A', 28, struct audio_encoding) +#define AUDIO_GETFD _IOR('A', 29, int) +#define AUDIO_SETFD _IOWR('A', 30, int) +#define AUDIO_PERROR _IOR('A', 31, int) +#define AUDIO_GETIOFFS _IOR('A', 32, struct audio_offset) +#define AUDIO_GETOOFFS _IOR('A', 33, struct audio_offset) +#define AUDIO_GETPROPS _IOR('A', 34, int) +#define AUDIO_PROP_FULLDUPLEX 0x01 +#define AUDIO_PROP_MMAP 0x02 +#define AUDIO_PROP_INDEPENDENT 0x04 + +/* + * Mixer device + */ +#define AUDIO_MIN_GAIN 0 +#define AUDIO_MAX_GAIN 255 + +typedef struct mixer_level { + int num_channels; + u_char level[8]; /* [num_channels] */ +} mixer_level_t; +#define AUDIO_MIXER_LEVEL_MONO 0 +#define AUDIO_MIXER_LEVEL_LEFT 0 +#define AUDIO_MIXER_LEVEL_RIGHT 1 + +/* + * Device operations + */ + +typedef struct audio_mixer_name { + char name[MAX_AUDIO_DEV_LEN]; + int msg_id; +} audio_mixer_name_t; + +typedef struct mixer_devinfo { + int index; + audio_mixer_name_t label; + int type; +#define AUDIO_MIXER_CLASS 0 +#define AUDIO_MIXER_ENUM 1 +#define AUDIO_MIXER_SET 2 +#define AUDIO_MIXER_VALUE 3 + int mixer_class; + int next, prev; +#define AUDIO_MIXER_LAST -1 + union { + struct audio_mixer_enum { + int num_mem; + struct { + audio_mixer_name_t label; + int ord; + } member[32]; + } e; + struct audio_mixer_set { + int num_mem; + struct { + audio_mixer_name_t label; + int mask; + } member[32]; + } s; + struct audio_mixer_value { + audio_mixer_name_t units; + int num_channels; + } v; + } un; +} mixer_devinfo_t; + + +typedef struct mixer_ctrl { + int dev; + int type; + union { + int ord; /* enum */ + int mask; /* set */ + mixer_level_t value; /* value */ + } un; +} mixer_ctrl_t; + +/* + * Mixer operations + */ +#define AUDIO_MIXER_READ _IOWR('M', 0, mixer_ctrl_t) +#define AUDIO_MIXER_WRITE _IOWR('M', 1, mixer_ctrl_t) +#define AUDIO_MIXER_DEVINFO _IOWR('M', 2, mixer_devinfo_t) + +/* + * Well known device names + */ +#define AudioNmicrophone "mic" +#define AudioNline "line" +#define AudioNcd "CD" +#define AudioNdac "DAC" +#define AudioNrecord "record" +#define AudioNvolume "volume" +#define AudioNmonitor "monitor" +#define AudioNtreble "treble" +#define AudioNbass "bass" +#define AudioNspeaker "speaker" +#define AudioNheadphone "headphones" +#define AudioNoutput "output" +#define AudioNinput "input" +#define AudioNmaster "master" +#define AudioNstereo "stereo" +#define AudioNmono "mono" +#define AudioNspatial "spatial" +#define AudioNsurround "surround" +#define AudioNpseudo "pseudo" +#define AudioNmute "mute" +#define AudioNenhanced "enhanced" +#define AudioNon "on" +#define AudioNoff "off" +#define AudioNmode "mode" +#define AudioNsource "source" +#define AudioNfmsynth "fmsynth" +#define AudioNwave "wave" +#define AudioNmidi "midi" +#define AudioNmixerout "mixerout" +#define AudioNswap "swap" /* swap left and right channels */ + +#define AudioEmulaw "mulaw" +#define AudioEalaw "alaw" +#define AudioEadpcm "ADPCM" +#define AudioElinear "linear" +#define AudioElinear_le "linear_le" +#define AudioElinear_be "linear_be" +#define AudioEulinear "ulinear" +#define AudioEulinear_le "ulinear_le" +#define AudioEulinear_be "ulinear_be" + +#define AudioCInputs "Inputs" +#define AudioCOutputs "Outputs" +#define AudioCRecord "Record" +#define AudioCMonitor "Monitor" +#define AudioCEqualization "Equalization" + +#endif /* !_SYS_AUDIOIO_H_ */ |