diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2015-06-25 06:43:47 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2015-06-25 06:43:47 +0000 |
commit | d3c89d57cc9744309198d69d69ad7451818f4756 (patch) | |
tree | 3bc9bda508be4272edac36844e21d1d1118ae356 | |
parent | 19f7d9bf41229ddef9c786f31c523116638e1475 (diff) |
Reimplement the audio driver in a simpler way, removing unused/unusable
functionality. Same API and ABI except for the removed bits and no
behaviour change for programs using libsndio. With help from armani@
and mpi@, thanks.
-rw-r--r-- | sys/arch/luna88k/cbus/nec86hw.c | 9 | ||||
-rw-r--r-- | sys/dev/audio.c | 4631 | ||||
-rw-r--r-- | sys/dev/audio_if.h | 27 | ||||
-rw-r--r-- | sys/dev/isa/ad1848.c | 9 | ||||
-rw-r--r-- | sys/dev/isa/ess.c | 9 | ||||
-rw-r--r-- | sys/dev/isa/sbdsp.c | 9 | ||||
-rw-r--r-- | sys/dev/mulaw.c | 10 | ||||
-rw-r--r-- | sys/dev/mulaw.h | 12 | ||||
-rw-r--r-- | sys/dev/pci/envy.c | 24 | ||||
-rw-r--r-- | sys/sys/audioio.h | 67 | ||||
-rw-r--r-- | sys/sys/conf.h | 4 |
11 files changed, 1544 insertions, 3267 deletions
diff --git a/sys/arch/luna88k/cbus/nec86hw.c b/sys/arch/luna88k/cbus/nec86hw.c index 1e60ab8ff1c..ba6718f2165 100644 --- a/sys/arch/luna88k/cbus/nec86hw.c +++ b/sys/arch/luna88k/cbus/nec86hw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nec86hw.c,v 1.2 2015/05/11 06:46:21 ratchov Exp $ */ +/* $OpenBSD: nec86hw.c,v 1.3 2015/06/25 06:43:45 ratchov Exp $ */ /* $NecBSD: nec86hw.c,v 1.13 1998/03/14 07:04:54 kmatsuda Exp $ */ /* $NetBSD$ */ @@ -94,6 +94,9 @@ static int nec86hw_rate_table[NEC86HW_NRATE_TYPE][NEC86_NRATE] = { { 44100, 33075, 22050, 16000, 11025, 8000, 5513, 4000 }, }; +static struct audio_params nec86hw_audio_default = + {44100, AUDIO_ENCODING_SLINEAR_LE, 16, 2, 1, 2}; + int nec86hw_set_output_block(struct nec86hw_softc *, int); int nec86hw_set_input_block(struct nec86hw_softc *, int); @@ -130,9 +133,9 @@ nec86hw_attach(struct nec86hw_softc *sc) sc->func_fifo_output = nec86fifo_output_mono_8_direct; sc->func_fifo_input = nec86fifo_input_mono_8_direct; (void) nec86hw_set_params(sc, AUMODE_RECORD, 0, - &audio_default, &audio_default); + &nec86hw_audio_default, &nec86hw_audio_default); (void) nec86hw_set_params(sc, AUMODE_PLAY, 0, - &audio_default, &audio_default); + &nec86hw_audio_default, &nec86hw_audio_default); /* Set default ports. */ (void) nec86hw_set_in_port(sc, NEC86HW_INPUT_MIXER); diff --git a/sys/dev/audio.c b/sys/dev/audio.c index 712c2ed7ff3..427222758c7 100644 --- a/sys/dev/audio.c +++ b/sys/dev/audio.c @@ -1,348 +1,137 @@ -/* $OpenBSD: audio.c,v 1.131 2015/05/22 12:46:38 jsg Exp $ */ -/* $NetBSD: audio.c,v 1.119 1999/11/09 16:50:47 augustss Exp $ */ - +/* $OpenBSD: audio.c,v 1.132 2015/06/25 06:43:45 ratchov Exp $ */ /* - * Copyright (c) 1991-1993 Regents of the University of California. - * All rights reserved. + * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org> * - * 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. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 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. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - #include <sys/param.h> -#include <sys/ioctl.h> #include <sys/fcntl.h> -#include <sys/vnode.h> -#include <sys/selinfo.h> -#include <sys/poll.h> -#include <sys/malloc.h> #include <sys/systm.h> -#include <sys/syslog.h> -#include <sys/kernel.h> -#include <sys/signalvar.h> +#include <sys/ioctl.h> #include <sys/conf.h> -#include <sys/audioio.h> -#include <sys/device.h> +#include <sys/poll.h> +#include <sys/kernel.h> #include <sys/task.h> -#include <sys/endian.h> - +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/audioio.h> #include <dev/audio_if.h> #include <dev/mulaw.h> -#include <dev/rndvar.h> - -#include "wskbd.h" /* NWSKBD (mixer tuning using keyboard) */ +#include "audio.h" +#include "wskbd.h" #ifdef AUDIO_DEBUG -#define DPRINTF(x) if (audiodebug) printf x -#define DPRINTFN(n,x) if (audiodebug>(n)) printf x -int audiodebug = 0; +#define DPRINTF(...) \ + do { \ + if (audio_debug) \ + printf(__VA_ARGS__); \ + } while(0) +#define DPRINTFN(n, ...) \ + do { \ + if (audio_debug > (n)) \ + printf(__VA_ARGS__); \ + } while(0) #else -#define DPRINTF(x) -#define DPRINTFN(n,x) +#define DPRINTF(...) do {} while(0) +#define DPRINTFN(n, ...) do {} while(0) #endif -#define ROUNDSIZE(x) x &= -16 /* round to nice boundary */ +#define DEVNAME(sc) ((sc)->dev.dv_xname) +#define AUDIO_UNIT(n) (minor(n) & 0x0f) +#define AUDIO_DEV(n) (minor(n) & 0xf0) +#define AUDIO_DEV_SOUND 0 /* minor of /dev/sound0 */ +#define AUDIO_DEV_MIXER 0x10 /* minor of /dev/mixer0 */ +#define AUDIO_DEV_AUDIO 0x80 /* minor of /dev/audio0 */ +#define AUDIO_DEV_AUDIOCTL 0xc0 /* minor of /dev/audioctl */ +#define AUDIO_BUFSZ 65536 /* buffer size in bytes */ /* - * Initial/default block duration is both configurable and patchable. + * dma buffer */ -#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 2 -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 mmapped; /* device is mmap()-ed */ - u_char blkset; /* blksize has been set, for stickiness */ -}; - -#define AUDIO_N_PORTS 4 - -struct au_mixer_ports { - int index; - int master; - int nports; - u_char isenum; - u_int allports; - u_int aumask[AUDIO_N_PORTS]; - u_int misel [AUDIO_N_PORTS]; - u_int miport[AUDIO_N_PORTS]; +struct audio_buf { + unsigned char *data; /* DMA memory block */ + size_t datalen; /* size of DMA memory block */ + size_t len; /* size of DMA FIFO */ + size_t start; /* first byte used in the FIFO */ + size_t used; /* bytes used in the FIFO */ + size_t blksz; /* DMA block size */ + unsigned long pos; /* bytes transferred */ + unsigned long xrun; /* bytes lost by xruns */ + struct selinfo sel; /* to record & wakeup poll(2) */ + int blocking; /* read/write blocking */ }; -struct audio_emu { - void (*sw_code)(void *, u_char *, int); /* conv routine */ - int encoding; /* emulated encoding */ +#if NWSKBD > 0 +struct wskbd_vol +{ + int val; /* index of the value control */ + int mute; /* index of the mute control */ + int step; /* increment/decrement step */ + int nch; /* channels in the value control */ + int val_pending; /* pending change of val */ + int mute_pending; /* pending mute toggles */ }; +#endif /* - * Software state, per audio device. + * device structure */ 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_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 */ - - u_char sc_rqui; /* input dma quiesced */ - u_char sc_pqui; /* output dma quiesced */ - - struct audio_params sc_pparams; /* play encoding parameters */ - struct audio_params sc_rparams; /* record encoding parameters */ - - struct audio_emu sc_pemu; /* play conversion params */ - struct audio_emu sc_remu; /* record conversion params */ - - int sc_eof; /* EOF, i.e. zero sized write, counter */ - u_long sc_wstamp; - u_long sc_playdrop; - - int sc_full_duplex; /* device in full duplex mode */ - - struct au_mixer_ports sc_inports, sc_outports; - int sc_monitor_port; - - int sc_refcnt; - int sc_dying; - - int sc_quiesce; -#define AUDIO_QUIESCE_START 1 -#define AUDIO_QUIESCE_SILENT 2 - struct task sc_mixer_task; - u_char sc_mute; - -#ifdef AUDIO_INTR_TIME - u_long sc_pfirstintr; /* first time we saw a play interrupt */ - int sc_pnintr; /* number of interrupts */ - u_long sc_plastintr; /* last time we saw a play 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 rec interrupt */ - long sc_rblktime; /* nominal time between interrupts */ + struct device dev; + struct audio_hw_if *ops; /* driver funcs */ + void *arg; /* first arg to driver funcs */ + int mode; /* bitmask of AUMODE_* */ + int quiesce; /* device suspended */ + struct audio_buf play, rec; + unsigned int sw_enc; /* user exposed AUDIO_ENCODING_* */ + unsigned int hw_enc; /* harware AUDIO_ENCODING_* */ + unsigned int bits; /* bits per sample */ + unsigned int bps; /* bytes-per-sample */ + unsigned int msb; /* sample are MSB aligned */ + unsigned int rate; /* rate in Hz */ + unsigned int round; /* block size in frames */ + unsigned int nblks; /* number of play blocks */ + unsigned int pchan, rchan; /* number of channels */ + unsigned char silence[4]; /* a sample of silence */ + int pause; /* not trying to start DMA */ + int active; /* DMA in process */ + void (*conv_enc)(unsigned char *, int); /* encode to native */ + void (*conv_dec)(unsigned char *, int); /* decode to user */ +#if NWSKBD > 0 + struct wskbd_vol spkr, mic; + struct task wskbd_task; + int wskbd_taskset; #endif }; -int audio_blk_ms = AUDIO_BLK_MS; - -int audiosetinfo(struct audio_softc *, struct audio_info *); -int audiogetinfo(struct audio_softc *, struct audio_info *); -int audiogetbufinfo(struct audio_softc *, struct audio_bufinfo *, int); -int audio_open(dev_t, struct audio_softc *, int, int, struct proc *); -int audio_close(dev_t, int, int, struct proc *); -int audio_read(dev_t, struct uio *, int); -int audio_write(dev_t, struct uio *, int); -int audio_ioctl(dev_t, u_long, caddr_t, int, struct proc *); -int audio_poll(dev_t, int, struct proc *); -paddr_t audio_mmap(dev_t, off_t, int); - -int mixer_open(dev_t, struct audio_softc *, int, int, struct proc *); -int mixer_close(dev_t, int, int, struct proc *); -int mixer_ioctl(dev_t, u_long, caddr_t, int, struct proc *); -static void mixer_remove(struct audio_softc *, struct proc *p); -static void mixer_signal(struct audio_softc *); - -void audio_init_record(struct audio_softc *); -void audio_init_play(struct audio_softc *); -int audiostartr(struct audio_softc *); -int audiostartp(struct audio_softc *); -void audio_rint(void *); -void audio_pint(void *); -int audio_check_params(struct audio_params *); -void audio_emu_setup(int, struct audio_params *, struct audio_emu *); - -void audio_set_blksize(struct audio_softc *, int, int); -void audio_calc_blksize(struct audio_softc *, int); -void audio_fill_silence(struct audio_params *, u_char *, u_char *, int); -int audio_silence_copyout(struct audio_softc *, int, struct uio *); - -void audio_init_ringbuffer(struct audio_ringbuffer *); -int audio_initbufs(struct audio_softc *); -void audio_calcwater(struct audio_softc *); -static __inline int audio_sleep_timo(int *, char *, int); -static __inline int audio_sleep(int *, char *); -static __inline void audio_wake(int *); -void audio_selwakeup(struct audio_softc *sc, int play); -int audio_drain(struct audio_softc *); -void audio_clear(struct audio_softc *); -static __inline void audio_pint_silence(struct audio_softc *, struct audio_ringbuffer *, u_char *, int); - -int audio_quiesce(struct audio_softc *); -void audio_wakeup(struct audio_softc *); - -int audio_alloc_ring(struct audio_softc *, struct audio_ringbuffer *, int, int); -void audio_free_ring(struct audio_softc *, struct audio_ringbuffer *); - -int audioprint(void *, const char *); - -int audioprobe(struct device *, void *, void *); -void audioattach(struct device *, struct device *, void *); -int audiodetach(struct device *, int); -int audioactivate(struct device *, int); - -struct portname { - char *name; - int mask; -}; -static struct portname itable[] = { - { AudioNmicrophone, AUDIO_MICROPHONE }, - { AudioNline, AUDIO_LINE_IN }, - { AudioNcd, AUDIO_CD }, - { 0 } -}; -static struct portname otable[] = { - { AudioNspeaker, AUDIO_SPEAKER }, - { AudioNheadphone, AUDIO_HEADPHONE }, - { AudioNline, AUDIO_LINE_OUT }, - { 0 } -}; -struct gainpref { - char *class, *device; -}; -static struct gainpref ipreftab[] = { - { AudioCinputs, AudioNvolume }, - { AudioCinputs, AudioNinput }, - { AudioCinputs, AudioNrecord }, - { AudioCrecord, AudioNvolume }, - { AudioCrecord, AudioNrecord }, - { NULL, NULL} -}; -static struct gainpref opreftab[] = { - { AudioCoutputs, AudioNoutput }, - { AudioCoutputs, AudioNdac }, - { AudioCinputs, AudioNdac }, - { AudioCoutputs, AudioNmaster }, - { NULL, NULL} -}; -static struct gainpref mpreftab[] = { - { AudioCoutputs, AudioNmonitor }, - { AudioCmonitor, AudioNmonitor }, - { NULL, NULL} -}; +int audio_match(struct device *, void *, void *); +void audio_attach(struct device *, struct device *, void *); +int audio_activate(struct device *, int); +int audio_detach(struct device *, int); +#if NWSKBD > 0 +void wskbd_mixer_init(struct audio_softc *); +#endif -void au_gain_match(struct audio_softc *, struct gainpref *, - mixer_devinfo_t *, mixer_devinfo_t *, int *, int *); -void au_check_ports(struct audio_softc *, struct au_mixer_ports *, - mixer_devinfo_t *, mixer_devinfo_t *, - char *, char *, struct portname *); -int au_set_gain(struct audio_softc *, struct au_mixer_ports *, - int, int); -void au_get_gain(struct audio_softc *, struct au_mixer_ports *, - u_int *, u_char *); -int au_set_port(struct audio_softc *, struct au_mixer_ports *, - u_int); -int au_get_port(struct audio_softc *, struct au_mixer_ports *); -int au_set_mute(struct audio_softc *, struct au_mixer_ports *, u_char); -int au_get_mute(struct audio_softc *, struct au_mixer_ports *, u_char *); -int au_get_lr_value(struct audio_softc *, mixer_ctrl_t *, - int *, int *r); -int au_set_lr_value(struct audio_softc *, mixer_ctrl_t *, - int, int); -int au_portof(struct audio_softc *, char *); - - -/* The default audio mode: 8 kHz mono ulaw */ -struct audio_params audio_default = - {8000, AUDIO_ENCODING_ULAW, 8, 1, 1, 1}; - -struct cfattach audio_ca = { - sizeof(struct audio_softc), audioprobe, audioattach, - audiodetach, audioactivate +const struct cfattach audio_ca = { + sizeof(struct audio_softc), audio_match, audio_attach, + audio_detach, audio_activate }; struct cfdriver audio_cd = { NULL, "audio", DV_DULL }; -void filt_audiowdetach(struct knote *); -int filt_audiowrite(struct knote *, long); - -struct filterops audiowrite_filtops = - { 1, NULL, filt_audiowdetach, filt_audiowrite}; - -void filt_audiordetach(struct knote *); -int filt_audioread(struct knote *, long); - -struct filterops audioread_filtops = - { 1, NULL, filt_audiordetach, filt_audioread}; - -#if NWSKBD > 0 -/* Mixer manipulation using keyboard */ -int wskbd_set_mixervolume(long, long); -void wskbd_set_mixervolume_callback(void *); -#endif - /* * This mutex protects data structures (including registers on the * sound-card) that are manipulated by both the interrupt handler and @@ -359,3212 +148,1788 @@ void wskbd_set_mixervolume_callback(void *); */ struct mutex audio_lock = MUTEX_INITIALIZER(IPL_AUDIO); -int -audioprobe(struct device *parent, void *match, void *aux) -{ - struct audio_attach_args *sa = aux; - - DPRINTF(("audioprobe: type=%d sa=%p hw=%p\n", - sa->type, sa, sa->hwif)); - return (sa->type == AUDIODEV_TYPE_AUDIO) ? 1 : 0; -} - -void -audioattach(struct device *parent, struct device *self, void *aux) -{ - struct audio_softc *sc = (void *)self; - struct audio_attach_args *sa = aux; - struct audio_hw_if *hwp = sa->hwif; - void *hdlp = sa->hdl; - int error; - mixer_devinfo_t mi, cl; - int ipref, opref, mpref; - - printf("\n"); - -#ifdef DIAGNOSTIC - if (hwp == 0 || - hwp->open == 0 || - hwp->close == 0 || - hwp->query_encoding == 0 || - hwp->set_params == 0 || - (hwp->start_output == 0 && hwp->trigger_output == 0) || - (hwp->start_input == 0 && hwp->trigger_input == 0) || - hwp->halt_output == 0 || - hwp->halt_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; - } +#ifdef AUDIO_DEBUG +/* + * 0 - nothing, as if AUDIO_DEBUG isn't defined + * 1 - initialisations & setup + * 2 - blocks & interrupts + */ +int audio_debug = 1; #endif - sc->hw_if = hwp; - sc->hw_hdl = hdlp; - sc->sc_dev = parent; - sc->sc_async_mixer = NULL; - - error = audio_alloc_ring(sc, &sc->sc_pr, AUMODE_PLAY, AU_RING_SIZE); - if (error) { - sc->hw_if = 0; - printf("audio: could not allocate play buffer\n"); - return; - } - error = audio_alloc_ring(sc, &sc->sc_rr, AUMODE_RECORD, AU_RING_SIZE); - if (error) { - audio_free_ring(sc, &sc->sc_pr); - sc->hw_if = 0; - printf("audio: could not allocate record buffer\n"); - return; - } - - /* - * Set default softc params - */ - if (hwp->get_default_params) { - hwp->get_default_params(hdlp, AUMODE_PLAY, &sc->sc_pparams); - hwp->get_default_params(hdlp, AUMODE_RECORD, &sc->sc_rparams); - } else { - sc->sc_pparams = audio_default; - sc->sc_rparams = audio_default; - } - sc->sc_pemu.encoding = sc->sc_pparams.encoding; - sc->sc_pemu.sw_code = NULL; - sc->sc_remu.encoding = sc->sc_rparams.encoding; - sc->sc_remu.sw_code = NULL; - - /* Set up some default values */ - sc->sc_rr.blkset = sc->sc_pr.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); - - ipref = opref = mpref = -1; - sc->sc_inports.index = -1; - sc->sc_inports.nports = 0; - sc->sc_inports.isenum = 0; - sc->sc_inports.allports = 0; - sc->sc_inports.master = -1; - sc->sc_outports.index = -1; - sc->sc_outports.nports = 0; - sc->sc_outports.isenum = 0; - sc->sc_outports.allports = 0; - sc->sc_outports.master = -1; - sc->sc_monitor_port = -1; - for(mi.index = 0; ; mi.index++) { - if (hwp->query_devinfo(hdlp, &mi) != 0) - break; - if (mi.type == AUDIO_MIXER_CLASS) - continue; - cl.index = mi.mixer_class; - if (hwp->query_devinfo(hdlp, &cl) != 0) - continue; - - au_gain_match(sc, ipreftab, &cl, &mi, &sc->sc_inports.master, &ipref); - au_gain_match(sc, opreftab, &cl, &mi, &sc->sc_outports.master, &opref); - au_gain_match(sc, mpreftab, &cl, &mi, &sc->sc_monitor_port, &mpref); - - au_check_ports(sc, &sc->sc_inports, &cl, &mi, - AudioCrecord, AudioNsource, itable); - au_check_ports(sc, &sc->sc_outports, &cl, &mi, - AudioCoutputs, AudioNselect, otable); - } - DPRINTF(("audio_attach: inputs ports=0x%x, output ports=0x%x\n", - sc->sc_inports.allports, sc->sc_outports.allports)); -} - -int -audioactivate(struct device *self, int act) +unsigned int +audio_gcd(unsigned int a, unsigned int b) { - struct audio_softc *sc = (struct audio_softc *)self; + unsigned int r; - switch (act) { - case DVACT_DEACTIVATE: - sc->sc_dying = 1; - break; - case DVACT_QUIESCE: - audio_quiesce(sc); - break; - case DVACT_WAKEUP: - audio_wakeup(sc); - break; + while (b > 0) { + r = a % b; + a = b; + b = r; } - return (0); + return a; } int -audiodetach(struct device *self, int flags) +audio_buf_init(struct audio_softc *sc, struct audio_buf *buf, int dir) { - struct audio_softc *sc = (struct audio_softc *)self; - int maj, mn; - - DPRINTF(("audio_detach: sc=%p flags=%d\n", sc, flags)); - - sc->sc_dying = 1; - - wakeup(&sc->sc_quiesce); - wakeup(&sc->sc_wchan); - wakeup(&sc->sc_rchan); - mtx_enter(&audio_lock); - if (--sc->sc_refcnt >= 0) { - if (msleep(&sc->sc_refcnt, &audio_lock, PZERO, "auddet", hz * 120)) - printf("audiodetach: %s didn't detach\n", - sc->dev.dv_xname); - } - mtx_leave(&audio_lock); - - /* free resources */ - audio_free_ring(sc, &sc->sc_pr); - audio_free_ring(sc, &sc->sc_rr); - - /* locate the major number */ - for (maj = 0; maj < nchrdev; maj++) - if (cdevsw[maj].d_open == audioopen) - break; - - /* Nuke the vnodes for any open instances (calls close). */ - mn = self->dv_unit; - vdevgone(maj, mn | SOUND_DEVICE, mn | SOUND_DEVICE, VCHR); - vdevgone(maj, mn | AUDIO_DEVICE, mn | AUDIO_DEVICE, VCHR); - vdevgone(maj, mn | AUDIOCTL_DEVICE, mn | AUDIOCTL_DEVICE, VCHR); - vdevgone(maj, mn | MIXER_DEVICE, mn | MIXER_DEVICE, VCHR); - - return (0); + if (sc->ops->round_buffersize) { + buf->datalen = sc->ops->round_buffersize(sc->arg, + dir, AUDIO_BUFSZ); + } else + buf->datalen = AUDIO_BUFSZ; + if (sc->ops->allocm) { + buf->data = sc->ops->allocm(sc->arg, dir, buf->datalen, + M_DEVBUF, M_WAITOK); + } else + buf->data = malloc(buf->datalen, M_DEVBUF, M_WAITOK); + if (buf->data == NULL) + return ENOMEM; + return 0; } -int -au_portof(struct audio_softc *sc, char *name) +void +audio_buf_done(struct audio_softc *sc, struct audio_buf *buf) { - mixer_devinfo_t mi; - - for(mi.index = 0; - sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0; - mi.index++) - if (strcmp(mi.label.name, name) == 0) - return mi.index; - return -1; + if (sc->ops->freem) + sc->ops->freem(sc->arg, buf->data, M_DEVBUF); + else + free(buf->data, M_DEVBUF, buf->datalen); } -void -au_check_ports(struct audio_softc *sc, struct au_mixer_ports *ports, - mixer_devinfo_t *cl, mixer_devinfo_t *mi, char *cname, char *mname, - struct portname *tbl) +/* + * return the reader pointer and the number of bytes available + */ +unsigned char * +audio_buf_rgetblk(struct audio_buf *buf, size_t *rsize) { - int i, j; + size_t count; - if (strcmp(cl->label.name, cname) != 0 || - strcmp(mi->label.name, mname) != 0) - return; - if (mi->type == AUDIO_MIXER_ENUM) { - ports->index = mi->index; - for(i = 0; tbl[i].name; i++) { - for(j = 0; j < mi->un.e.num_mem; j++) { - if (strcmp(mi->un.e.member[j].label.name, - tbl[i].name) == 0) { - ports->aumask[ports->nports] = tbl[i].mask; - ports->misel [ports->nports] = mi->un.e.member[j].ord; - ports->miport[ports->nports++] = - au_portof(sc, mi->un.e.member[j].label.name); - ports->allports |= tbl[i].mask; - } - } - } - ports->isenum = 1; - } else if (mi->type == AUDIO_MIXER_SET) { - ports->index = mi->index; - for(i = 0; tbl[i].name; i++) { - for(j = 0; j < mi->un.s.num_mem; j++) { - if (strcmp(mi->un.s.member[j].label.name, - tbl[i].name) == 0) { - ports->aumask[ports->nports] = tbl[i].mask; - ports->misel [ports->nports] = mi->un.s.member[j].mask; - ports->miport[ports->nports++] = - au_portof(sc, mi->un.s.member[j].label.name); - ports->allports |= tbl[i].mask; - } - } - } - } + count = buf->len - buf->start; + if (count > buf->used) + count = buf->used; + *rsize = count; + return buf->data + buf->start; } /* - * check if the given (class, device) is better - * than the current setting (*index), if so, set the - * current setting. + * discard "count" bytes at the start postion. */ void -au_gain_match(struct audio_softc *sc, struct gainpref *tbl, - mixer_devinfo_t *cls, mixer_devinfo_t *dev, int *index, int *pref) +audio_buf_rdiscard(struct audio_buf *buf, size_t count) { - int i; - - for (i = *pref + 1; tbl[i].class != NULL; i++) { - if (strcmp(tbl[i].class, cls->label.name) == 0 && - strcmp(tbl[i].device, dev->label.name) == 0) { - if (*pref < i) { - DPRINTF(("au_gain_match: found %s.%s\n", - cls->label.name, dev->label.name)); - *index = dev->index; - *pref = i; - } - break; - } +#ifdef AUDIO_DEBUG + if (count > buf->used) { + panic("audio_buf_rdiscard: bad count = %zu\n", count); } +#endif + buf->used -= count; + buf->start += count; + if (buf->start >= buf->len) + buf->start -= buf->len; } /* - * Called from hardware driver. This is where the MI audio driver gets - * probed/attached to the hardware driver. + * advance the writer pointer by "count" bytes */ -struct device * -audio_attach_mi(struct audio_hw_if *ahwp, void *hdlp, struct device *dev) +void +audio_buf_wcommit(struct audio_buf *buf, size_t count) { - struct audio_attach_args arg; - -#ifdef DIAGNOSTIC - if (ahwp == NULL) { - printf ("audio_attach_mi: NULL\n"); - return 0; +#ifdef AUDIO_DEBUG + if (count > (buf->len - buf->used)) { + panic("audio_buf_wcommit: bad count = %zu\n", count); } #endif - - arg.type = AUDIODEV_TYPE_AUDIO; - arg.hwif = ahwp; - arg.hdl = hdlp; - return config_found(dev, &arg, audioprint); + buf->used += count; } -int -audioprint(void *aux, const char *pnp) +/* + * get writer pointer and the number of bytes writable + */ +unsigned char * +audio_buf_wgetblk(struct audio_buf *buf, size_t *rsize) { - struct audio_attach_args *arg = aux; - const char *type; + size_t end, avail, count; - if (pnp != NULL) { - switch (arg->type) { - case AUDIODEV_TYPE_AUDIO: - type = "audio"; - break; - case AUDIODEV_TYPE_OPL: - type = "opl"; - break; - case AUDIODEV_TYPE_MPU: - type = "mpu"; - break; - default: - panic("audioprint: unknown type %d", arg->type); - } - printf("%s at %s", type, pnp); - } - return (UNCONF); + end = buf->start + buf->used; + if (end >= buf->len) + end -= buf->len; + avail = buf->len - buf->used; + count = buf->len - end; + if (count > avail) + count = avail; + *rsize = count; + return buf->data + end; } -#ifdef AUDIO_DEBUG -void audio_printsc(struct audio_softc *); -void audio_print_params(char *, struct audio_params *); - void -audio_printsc(struct audio_softc *sc) +audio_calc_sil(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("pblksz %d, rblksz %d", sc->sc_pr.blksize, sc->sc_rr.blksize); - printf("hiwat %d lowat %d\n", sc->sc_pr.usedhigh, sc->sc_pr.usedlow); + unsigned char *q; + unsigned int s, i; + int d, e; + + e = sc->sw_enc; +#ifdef AUDIO_DEBUG + switch (e) { + case AUDIO_ENCODING_SLINEAR_LE: + case AUDIO_ENCODING_ULINEAR_LE: + case AUDIO_ENCODING_SLINEAR_BE: + case AUDIO_ENCODING_ULINEAR_BE: + break; + default: + printf("%s: unhandled play encoding %d\n", DEVNAME(sc), e); + memset(sc->silence, 0, sc->bps); + return; + } +#endif + if (e == AUDIO_ENCODING_SLINEAR_BE || e == AUDIO_ENCODING_ULINEAR_BE) { + d = -1; + q = sc->silence + sc->bps - 1; + } else { + d = 1; + q = sc->silence; + } + if (e == AUDIO_ENCODING_SLINEAR_LE || e == AUDIO_ENCODING_SLINEAR_BE) { + s = 0; + } else { + s = 0x80000000; + if (sc->msb) + s >>= 32 - 8 * sc->bps; + else + s >>= 32 - sc->bits; + } + for (i = 0; i < sc->bps; i++) { + *q = s; + q += d; + s >>= 8; + } + if (sc->conv_enc) + sc->conv_enc(sc->silence, sc->bps); } void -audio_print_params(char *s, struct audio_params *p) +audio_fill_sil(struct audio_softc *sc, unsigned char *ptr, size_t count) { - printf("audio: %s sr=%ld, enc=%d, chan=%d, prec=%d bps=%d\n", s, - p->sample_rate, p->encoding, p->channels, p->precision, p->bps); -} -#endif + unsigned char *q, *p; + size_t i, j; -int -audio_alloc_ring(struct audio_softc *sc, struct audio_ringbuffer *r, - int direction, int bufsize) -{ - struct audio_hw_if *hw = sc->hw_if; - void *hdl = sc->hw_hdl; - /* - * Alloc DMA play and record buffers - */ - if (bufsize < AUMINBUF) - bufsize = AUMINBUF; - ROUNDSIZE(bufsize); - if (hw->round_buffersize) - bufsize = hw->round_buffersize(hdl, direction, bufsize); - r->bufsize = bufsize; - if (hw->allocm) - r->start = hw->allocm(hdl, direction, r->bufsize, M_DEVBUF, - M_WAITOK); - else - r->start = malloc(bufsize, M_DEVBUF, M_WAITOK); - if (r->start == 0) - return ENOMEM; - return 0; + q = ptr; + for (j = count / sc->bps; j > 0; j--) { + p = sc->silence; + for (i = sc->bps; i > 0; i--) + *q++ = *p++; + } } void -audio_free_ring(struct audio_softc *sc, struct audio_ringbuffer *r) +audio_clear(struct audio_softc *sc) { - if (sc->hw_if->freem) { - sc->hw_if->freem(sc->hw_hdl, r->start, M_DEVBUF); - } else { - free(r->start, M_DEVBUF, 0); + if (sc->mode & AUMODE_PLAY) { + sc->play.used = sc->play.start = 0; + sc->play.pos = sc->play.xrun = 0; + audio_fill_sil(sc, sc->play.data, sc->play.len); + } + if (sc->mode & AUMODE_RECORD) { + sc->rec.used = sc->rec.start = 0; + sc->rec.pos = sc->rec.xrun = 0; + audio_fill_sil(sc, sc->rec.data, sc->rec.len); } } -int -audioopen(dev_t dev, int flags, int ifmt, struct proc *p) +/* + * called whenever a block is consumed by the driver + */ +void +audio_pintr(void *addr) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc; + struct audio_softc *sc = addr; + unsigned char *ptr; + size_t count; int error; - if (unit >= audio_cd.cd_ndevs || - (sc = audio_cd.cd_devs[unit]) == NULL) - return ENXIO; - - if (sc->sc_dying) - return (EIO); - - if (!sc->hw_if) - return (ENXIO); - - sc->sc_refcnt ++; - switch (AUDIODEV(dev)) { - case SOUND_DEVICE: - case AUDIO_DEVICE: - case AUDIOCTL_DEVICE: - error = audio_open(dev, sc, flags, ifmt, p); - break; - case MIXER_DEVICE: - error = mixer_open(dev, sc, flags, ifmt, p); - break; - default: - error = ENXIO; - break; + MUTEX_ASSERT_LOCKED(&audio_lock); + if (!(sc->mode & AUMODE_PLAY) || !sc->active) { + printf("%s: play interrupt but not playing\n", DEVNAME(sc)); + return; + } + if (sc->quiesce) { + DPRINTF("%s: quesced, skipping play intr\n", DEVNAME(sc)); + return; } - if (--sc->sc_refcnt < 0) - wakeup(&sc->sc_refcnt); + sc->play.pos += sc->play.blksz; + audio_fill_sil(sc, sc->play.data + sc->play.start, sc->play.blksz); + audio_buf_rdiscard(&sc->play, sc->play.blksz); + if (sc->play.used < sc->play.blksz) { + DPRINTFN(1, "%s: play underrun\n", DEVNAME(sc)); + sc->play.xrun += sc->play.blksz; + audio_buf_wcommit(&sc->play, sc->play.blksz); + } - return (error); -} + DPRINTFN(1, "%s: play intr, used -> %zu, start -> %zu\n", + DEVNAME(sc), sc->play.used, sc->play.start); -int -audioclose(dev_t dev, int flags, int ifmt, struct proc *p) -{ + if (!sc->ops->trigger_output) { + ptr = audio_buf_rgetblk(&sc->play, &count); + error = sc->ops->start_output(sc->arg, + ptr, sc->play.blksz, audio_pintr, (void *)sc); + if (error) { + printf("%s: play restart failed: %d\n", + DEVNAME(sc), error); + } + } - 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); + if (sc->play.used < sc->play.len) { + DPRINTFN(1, "%s: play wakeup, chan = %d\n", + DEVNAME(sc), sc->play.blocking); + if (sc->play.blocking) { + wakeup(&sc->play.blocking); + sc->play.blocking = 0; + } + selwakeup(&sc->play.sel); } } -int -audioread(dev_t dev, struct uio *uio, int ioflag) +/* + * called whenever a block is produced by the driver + */ +void +audio_rintr(void *addr) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc; + struct audio_softc *sc = addr; + unsigned char *ptr; + size_t count; int error; - if (unit >= audio_cd.cd_ndevs || - (sc = audio_cd.cd_devs[unit]) == NULL) - return ENXIO; + MUTEX_ASSERT_LOCKED(&audio_lock); + if (!(sc->mode & AUMODE_RECORD) || !sc->active) { + printf("%s: rec interrupt but not recording\n", DEVNAME(sc)); + return; + } + if (sc->quiesce) { + DPRINTF("%s: quesced, skipping rec intr\n", DEVNAME(sc)); + return; + } - if (sc->sc_dying) - return (EIO); + sc->rec.pos += sc->rec.blksz; + audio_buf_wcommit(&sc->rec, sc->rec.blksz); + if (sc->rec.used == sc->rec.len) { + DPRINTFN(1, "%s: rec overrun\n", DEVNAME(sc)); + sc->rec.xrun += sc->rec.blksz; + audio_buf_rdiscard(&sc->rec, sc->rec.blksz); + } + DPRINTFN(1, "%s: rec intr, used -> %zu\n", DEVNAME(sc), sc->rec.used); - sc->sc_refcnt ++; - switch (AUDIODEV(dev)) { - case SOUND_DEVICE: - case AUDIO_DEVICE: - error = audio_read(dev, uio, ioflag); - break; - case AUDIOCTL_DEVICE: - case MIXER_DEVICE: - error = ENODEV; - break; - default: - error = ENXIO; - break; + if (!sc->ops->trigger_input) { + ptr = audio_buf_wgetblk(&sc->rec, &count); + error = sc->ops->start_input(sc->arg, + ptr, sc->rec.blksz, audio_rintr, (void *)sc); + if (error) { + printf("%s: rec restart failed: %d\n", + DEVNAME(sc), error); + } } - if (--sc->sc_refcnt < 0) - wakeup(&sc->sc_refcnt); - return (error); + if (sc->rec.used > 0) { + DPRINTFN(1, "%s: rec wakeup, chan = %d\n", + DEVNAME(sc), sc->rec.blocking); + if (sc->rec.blocking) { + wakeup(&sc->rec.blocking); + sc->rec.blocking = 0; + } + selwakeup(&sc->rec.sel); + } } int -audiowrite(dev_t dev, struct uio *uio, int ioflag) +audio_start_do(struct audio_softc *sc) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc; int error; + struct audio_params p; + unsigned char *ptr; + size_t count; + + DPRINTFN(1, "%s: start play: " + "start = %zu, used = %zu, " + "len = %zu, blksz = %zu\n", + DEVNAME(sc), sc->play.start, sc->play.used, + sc->play.len, sc->play.blksz); + DPRINTFN(1, "%s: start rec: " + "start = %zu, used = %zu, " + "len = %zu, blksz = %zu\n", + DEVNAME(sc), sc->rec.start, sc->rec.used, + sc->rec.len, sc->rec.blksz); - if (unit >= audio_cd.cd_ndevs || - (sc = audio_cd.cd_devs[unit]) == NULL) - return ENXIO; - - if (sc->sc_dying) - return (EIO); - - sc->sc_refcnt ++; - switch (AUDIODEV(dev)) { - case SOUND_DEVICE: - case AUDIO_DEVICE: - error = audio_write(dev, uio, ioflag); - break; - case AUDIOCTL_DEVICE: - case MIXER_DEVICE: - error = ENODEV; - break; - default: - error = ENXIO; - break; + error = 0; + if (sc->mode & AUMODE_PLAY) { + if (sc->ops->trigger_output) { + p.encoding = sc->hw_enc; + p.precision = sc->bits; + p.bps = sc->bps; + p.msb = sc->msb; + p.sample_rate = sc->rate; + p.channels = sc->pchan; + error = sc->ops->trigger_output(sc->arg, + sc->play.data, + sc->play.data + sc->play.len, + sc->play.blksz, + audio_pintr, (void *)sc, &p); + } else { + mtx_enter(&audio_lock); + ptr = audio_buf_rgetblk(&sc->play, &count); + error = sc->ops->start_output(sc->arg, + ptr, sc->play.blksz, audio_pintr, (void *)sc); + mtx_leave(&audio_lock); + } + if (error) + printf("%s: failed to start playback\n", DEVNAME(sc)); + } + if (sc->mode & AUMODE_RECORD) { + if (sc->ops->trigger_input) { + p.encoding = sc->hw_enc; + p.precision = sc->bits; + p.bps = sc->bps; + p.msb = sc->msb; + p.sample_rate = sc->rate; + p.channels = sc->rchan; + error = sc->ops->trigger_input(sc->arg, + sc->rec.data, + sc->rec.data + sc->rec.len, + sc->rec.blksz, + audio_rintr, (void *)sc, &p); + } else { + mtx_enter(&audio_lock); + ptr = audio_buf_wgetblk(&sc->rec, &count); + error = sc->ops->start_input(sc->arg, + ptr, sc->rec.blksz, audio_rintr, (void *)sc); + mtx_leave(&audio_lock); + } + if (error) + printf("%s: failed to start recording\n", DEVNAME(sc)); } - - if (--sc->sc_refcnt < 0) - wakeup(&sc->sc_refcnt); - return (error); + return error; } int -audioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +audio_stop_do(struct audio_softc *sc) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc; - int error; - - if (unit >= audio_cd.cd_ndevs || - (sc = audio_cd.cd_devs[unit]) == NULL) - return ENXIO; - - if (sc->sc_dying) - return (EIO); - - sc->sc_refcnt ++; - switch (AUDIODEV(dev)) { - case SOUND_DEVICE: - case AUDIO_DEVICE: - case AUDIOCTL_DEVICE: - error = audio_ioctl(dev, cmd, addr, flag, p); - break; - case MIXER_DEVICE: - error = mixer_ioctl(dev, cmd, addr, flag, p); - break; - default: - error = ENXIO; - break; - } + if (sc->mode & AUMODE_PLAY) + sc->ops->halt_output(sc->arg); + if (sc->mode & AUMODE_RECORD) + sc->ops->halt_input(sc->arg); + return 0; +} - if (--sc->sc_refcnt < 0) - wakeup(&sc->sc_refcnt); - return (error); +int +audio_start(struct audio_softc *sc) +{ + sc->active = 1; + sc->play.xrun = sc->play.pos = sc->rec.xrun = sc->rec.pos = 0; + return audio_start_do(sc); } int -audiopoll(dev_t dev, int events, struct proc *p) +audio_stop(struct audio_softc *sc) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc; int error; - if (unit >= audio_cd.cd_ndevs || - (sc = audio_cd.cd_devs[unit]) == NULL) - return POLLERR; - - if (sc->sc_dying) - return POLLERR; - - sc->sc_refcnt ++; - switch (AUDIODEV(dev)) { - case SOUND_DEVICE: - case AUDIO_DEVICE: - error = audio_poll(dev, events, p); - break; - case AUDIOCTL_DEVICE: - case MIXER_DEVICE: - error = 0; - break; - default: - error = 0; - break; - } - - if (--sc->sc_refcnt < 0) - wakeup(&sc->sc_refcnt); - return (error); + error = audio_stop_do(sc); + if (error) + return error; + audio_clear(sc); + sc->active = 0; + return 0; } -paddr_t -audiommap(dev_t dev, off_t off, int prot) +int +audio_setpar(struct audio_softc *sc) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc; - int ret; + struct audio_params p, r; + unsigned int nr, np, max, min, mult; + int error; - if (unit >= audio_cd.cd_ndevs || - (sc = audio_cd.cd_devs[unit]) == NULL) - return (-1); + DPRINTF("%s: setpar: req enc=%d bits=%d, bps=%d, msb=%d " + "rate=%d, pchan=%d, rchan=%d, round=%u, nblks=%d\n", + DEVNAME(sc), sc->sw_enc, sc->bits, sc->bps, sc->msb, + sc->rate, sc->pchan, sc->rchan, sc->round, sc->nblks); - if (sc->sc_dying) - return (-1); + /* + * AUDIO_ENCODING_SLINEAR and AUDIO_ENCODING_ULINEAR are not + * used anymore, promote them to the _LE and _BE equivalents + */ + if (sc->sw_enc == AUDIO_ENCODING_SLINEAR) { +#if BYTE_ORDER == LITTLE_ENDIAN + sc->sw_enc = AUDIO_ENCODING_SLINEAR_LE; +#else + sc->sw_enc = AUDIO_ENCODING_SLINEAR_BE; +#endif + } + if (sc->sw_enc == AUDIO_ENCODING_ULINEAR) { +#if BYTE_ORDER == LITTLE_ENDIAN + sc->sw_enc = AUDIO_ENCODING_ULINEAR_LE; +#else + sc->sw_enc = AUDIO_ENCODING_ULINEAR_BE; +#endif + } - sc->sc_refcnt ++; - switch (AUDIODEV(dev)) { - case SOUND_DEVICE: - case AUDIO_DEVICE: - ret = audio_mmap(dev, off, prot); - break; - case AUDIOCTL_DEVICE: - case MIXER_DEVICE: - ret = -1; + /* + * check if requested parameters are in the allowed ranges + */ + if (sc->mode & AUMODE_PLAY) { + if (sc->pchan < 1) + sc->pchan = 1; + if (sc->pchan > 64) + sc->pchan = 64; + } + if (sc->mode & AUMODE_RECORD) { + if (sc->rchan < 1) + sc->rchan = 1; + if (sc->rchan > 64) + sc->rchan = 64; + } + switch (sc->sw_enc) { + case AUDIO_ENCODING_ULAW: + case AUDIO_ENCODING_ALAW: + case AUDIO_ENCODING_SLINEAR_LE: + case AUDIO_ENCODING_SLINEAR_BE: + case AUDIO_ENCODING_ULINEAR_LE: + case AUDIO_ENCODING_ULINEAR_BE: break; default: - ret = -1; - break; - } - - if (--sc->sc_refcnt < 0) - wakeup(&sc->sc_refcnt); - return (ret); -} + sc->sw_enc = AUDIO_ENCODING_SLINEAR_LE; + } + if (sc->bits < 8) + sc->bits = 8; + if (sc->bits > 32) + sc->bits = 32; + if (sc->bps < 1) + sc->bps = 1; + if (sc->bps > 4) + sc->bps = 4; + if (sc->rate < 4000) + sc->rate = 4000; + if (sc->rate > 192000) + sc->rate = 192000; -/* - * Audio driver - */ -void -audio_init_ringbuffer(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->stamp_last = 0; - rp->drops = 0; - rp->pdrops = 0; - rp->mmapped = 0; -} - -int -audio_initbufs(struct audio_softc *sc) -{ - struct audio_hw_if *hw = sc->hw_if; - int error; + /* + * copy into struct audio_params, required by drivers + */ + p.encoding = r.encoding = sc->sw_enc; + p.precision = r.precision = sc->bits; + p.bps = r.bps = sc->bps; + p.msb = r.msb = sc->msb; + p.sample_rate = r.sample_rate = sc->rate; + p.channels = sc->pchan; + r.channels = sc->rchan; - 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; + /* + * set parameters + */ + error = sc->ops->set_params(sc->arg, sc->mode, sc->mode, &p, &r); + if (error) + return error; + if (sc->mode == (AUMODE_PLAY | AUMODE_RECORD)) { + if (p.encoding != r.encoding || + p.precision != r.precision || + p.bps != r.bps || + p.msb != r.msb || + p.sample_rate != r.sample_rate) { + printf("%s: different play and record parameters" + "returned by hardware\n", DEVNAME(sc)); + return ENODEV; + } } - - 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 (sc->mode & AUMODE_PLAY) { + sc->hw_enc = p.encoding; + sc->bits = p.precision; + sc->bps = p.bps; + sc->msb = p.msb; + sc->rate = p.sample_rate; + sc->pchan = p.channels; + } + if (sc->mode & AUMODE_RECORD) { + sc->hw_enc = r.encoding; + sc->bits = r.precision; + sc->bps = r.bps; + sc->msb = r.msb; + sc->rate = r.sample_rate; + sc->rchan = r.channels; + } + if (sc->rate == 0 || sc->bps == 0 || sc->bits == 0) { + printf("%s: invalid parameters returned by hardware\n", + DEVNAME(sc)); + return ENODEV; + } + if (sc->ops->commit_settings) { + error = sc->ops->commit_settings(sc->arg); if (error) return error; } -#ifdef AUDIO_INTR_TIME - sc->sc_pnintr = 0; - sc->sc_pblktime = (u_long)( - (u_long)sc->sc_pr.blksize * 100000 / - (u_long)(sc->sc_pparams.bps * - sc->sc_pparams.channels * - sc->sc_pparams.sample_rate)) * 10; - DPRINTF(("audio: play blktime = %lu for %d\n", - sc->sc_pblktime, sc->sc_pr.blksize)); - sc->sc_rnintr = 0; - sc->sc_rblktime = (u_long)( - (u_long)sc->sc_rr.blksize * 100000 / - (u_long)(sc->sc_rparams.bps * - sc->sc_rparams.channels * - sc->sc_rparams.sample_rate)) * 10; - DPRINTF(("audio: record blktime = %lu for %d\n", - sc->sc_rblktime, sc->sc_rr.blksize)); + /* + * conversion from/to exotic/dead encoding, for drivers not supporting + * linear + */ + switch (sc->hw_enc) { + case AUDIO_ENCODING_SLINEAR_LE: + case AUDIO_ENCODING_SLINEAR_BE: + case AUDIO_ENCODING_ULINEAR_LE: + case AUDIO_ENCODING_ULINEAR_BE: + sc->sw_enc = sc->hw_enc; + sc->conv_dec = sc->conv_enc = NULL; + break; + case AUDIO_ENCODING_ULAW: +#if BYTE_ORDER == LITTLE_ENDIAN + sc->sw_enc = AUDIO_ENCODING_SLINEAR_LE; +#else + sc->sw_enc = AUDIO_ENCODING_SLINEAR_BE; #endif + if (sc->bits == 8) { + sc->conv_enc = slinear8_to_mulaw; + sc->conv_dec = mulaw_to_slinear8; + break; + } else if (sc->bits == 24) { + sc->conv_enc = slinear24_to_mulaw24; + sc->conv_dec = mulaw24_to_slinear24; + break; + } + sc->sw_enc = sc->hw_enc; + sc->conv_dec = sc->conv_enc = NULL; + break; + default: + printf("%s: setpar: enc = %d, bits = %d: emulation skipped\n", + DEVNAME(sc), sc->hw_enc, sc->bits); + sc->sw_enc = sc->hw_enc; + sc->conv_dec = sc->conv_enc = NULL; + } + audio_calc_sil(sc); - return 0; -} - -void -audio_calcwater(struct audio_softc *sc) -{ - int hiwat, lowat; - - hiwat = (sc->sc_pr.end - sc->sc_pr.start) / sc->sc_pr.blksize; - lowat = hiwat * 3 / 4; - if (lowat == hiwat) - lowat = hiwat - 1; - sc->sc_pr.usedhigh = hiwat * sc->sc_pr.blksize; - sc->sc_pr.usedlow = lowat * sc->sc_pr.blksize; - sc->sc_rr.usedhigh = sc->sc_rr.end - sc->sc_rr.start; - sc->sc_rr.usedlow = 0; -} - -static __inline int -audio_sleep_timo(int *chan, char *label, int timo) -{ - int st; - - if (!label) - label = "audio"; + /* + * get least multiplier of the number of frames per block + */ + if (sc->ops->round_blocksize) { + mult = sc->ops->round_blocksize(sc->arg, 1); + if (mult == 0) { + printf("%s: 0x%x: bad block size multiplier\n", + DEVNAME(sc), mult); + return ENODEV; + } + } else + mult = 1; + DPRINTF("%s: hw block size multiplier: %u\n", DEVNAME(sc), mult); + if (sc->mode & AUMODE_PLAY) { + np = mult / audio_gcd(sc->pchan * sc->bps, mult); + if (!(sc->mode & AUMODE_RECORD)) + nr = np; + DPRINTF("%s: play number of frames multiplier: %u\n", + DEVNAME(sc), np); + } + if (sc->mode & AUMODE_RECORD) { + nr = mult / audio_gcd(sc->rchan * sc->bps, mult); + if (!(sc->mode & AUMODE_PLAY)) + np = nr; + DPRINTF("%s: record number of frames multiplier: %u\n", + DEVNAME(sc), nr); + } + mult = nr * np / audio_gcd(nr, np); + DPRINTF("%s: least common number of frames multiplier: %u\n", + DEVNAME(sc), mult); - DPRINTFN(3, ("audio_sleep_timo: chan=%p, label=%s, timo=%d\n", - chan, label, timo)); - *chan = 1; - st = msleep(chan, &audio_lock, PWAIT | PCATCH, label, timo); - *chan = 0; -#ifdef AUDIO_DEBUG - if (st != 0) - printf("audio_sleep: woke up st=%d\n", st); -#endif - return (st); -} + /* + * get minumum and maximum frames per block + */ + if (sc->mode & AUMODE_PLAY) { + np = sc->play.datalen / (sc->pchan * sc->bps * 2); + if (!(sc->mode & AUMODE_RECORD)) + nr = np; + } + if (sc->mode & AUMODE_RECORD) { + nr = sc->rec.datalen / (sc->rchan * sc->bps * 2); + if (!(sc->mode & AUMODE_PLAY)) + np = nr; + } + max = np < nr ? np : nr; + max -= max % mult; + min = sc->rate / 1000 + mult - 1; + min -= min % mult; + DPRINTF("%s: frame number range: %u..%u\n", DEVNAME(sc), min, max); + if (max < min) { + printf("%s: %u: bad max frame number\n", DEVNAME(sc), max); + return EIO; + } -static __inline int -audio_sleep(int *chan, char *label) -{ - return audio_sleep_timo(chan, label, 0); -} + /* + * adjust the frame per block to match our constraints + */ + sc->round += mult / 2; + sc->round -= sc->round % mult; + if (sc->round > max) + sc->round = max; + if (sc->round < min) + sc->round = min; + sc->round = sc->round; -/* call with audio_lock */ -static __inline void -audio_wake(int *chan) -{ - DPRINTFN(3, ("audio_wakeup: chan=%p, *chan=%d\n", chan, *chan)); - if (*chan) { - wakeup(chan); - *chan = 0; + /* + * set buffer size (number of blocks) + */ + if (sc->mode & AUMODE_PLAY) { + sc->play.blksz = sc->round * sc->pchan * sc->bps; + max = sc->play.datalen / sc->play.blksz; + if (sc->nblks > max) + sc->nblks = max; + if (sc->nblks < 2) + sc->nblks = 2; + sc->play.len = sc->nblks * sc->play.blksz; + sc->nblks = sc->nblks; + } + if (sc->mode & AUMODE_RECORD) { + /* + * for recording, buffer size is not the latency (it's + * exactly one block), so let's get the maximum buffer + * size of maximum reliability during xruns + */ + sc->rec.blksz = sc->round * sc->rchan * sc->bps; + sc->rec.len = sc->rec.datalen; + sc->rec.len -= sc->rec.datalen % sc->rec.blksz; } + + DPRINTF("%s: setpar: new enc=%d bits=%d, bps=%d, msb=%d " + "rate=%d, pchan=%d, rchan=%d, round=%u, nblks=%d\n", + DEVNAME(sc), sc->sw_enc, sc->bits, sc->bps, sc->msb, + sc->rate, sc->pchan, sc->rchan, sc->round, sc->nblks); + return 0; } int -audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, - struct proc *p) +audio_setinfo(struct audio_softc *sc, struct audio_info *ai) { + struct audio_prinfo *r = &ai->record, *p = &ai->play; int error; - int mode; - struct audio_info ai; - - 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 = sc->hw_if->open(sc->hw_hdl, flags); - if (error) - return (error); - - sc->sc_async_audio = 0; - sc->sc_rchan = 0; - sc->sc_wchan = 0; - 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) && - (sc->hw_if->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; - } + int set; /* - * Multiplex device: /dev/audio (default) and /dev/sound (last) - * The /dev/audio is always (re)set to the default parameters. - * For the other devices, you get what they were last set to. + * stop the device if requested to stop */ - if (ISDEVAUDIO(dev)) { - /* /dev/audio */ - if (sc->hw_if->get_default_params) { - sc->hw_if->get_default_params(sc->hw_hdl, AUMODE_PLAY, - &sc->sc_pparams); - sc->hw_if->get_default_params(sc->hw_hdl, AUMODE_RECORD, - &sc->sc_rparams); - } else { - sc->sc_rparams = audio_default; - sc->sc_pparams = audio_default; + if (sc->mode != 0) { + if (sc->mode & AUMODE_PLAY) { + if (p->pause != (unsigned char)~0) + sc->pause = p->pause; + } + if (sc->mode & AUMODE_RECORD) { + if (r->pause != (unsigned char)~0) + sc->pause = r->pause; + } + if (sc->pause) { + if (sc->active) + audio_stop(sc); } } -#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. + * copy parameters into the softc structure */ - if (sc->sc_rparams.precision == 0 || sc->sc_pparams.precision == 0) { - printf("audio_open: 0 precision\n"); - error = EINVAL; - goto bad; + set = 0; + if (ai->play.encoding != ~0) { + sc->sw_enc = ai->play.encoding; + set = 1; + } + if (ai->play.precision != ~0) { + sc->bits = ai->play.precision; + set = 1; + } + if (ai->play.bps != ~0) { + sc->bps = ai->play.bps; + set = 1; + } + if (ai->play.msb != ~0) { + sc->msb = ai->play.msb; + set = 1; + } + if (ai->play.sample_rate != ~0) { + sc->rate = ai->play.sample_rate; + set = 1; + } + if (ai->play.channels != ~0) { + sc->pchan = ai->play.channels; + set = 1; + } + if (ai->play.block_size != ~0) { + sc->round = ai->play.block_size / + (sc->bps * sc->pchan); + set = 1; + } + if (ai->hiwat != ~0) { + sc->nblks = ai->hiwat; + set = 1; + } + if (ai->record.encoding != ~0) { + sc->sw_enc = ai->record.encoding; + set = 1; + } + if (ai->record.precision != ~0) { + sc->bits = ai->record.precision; + set = 1; + } + if (ai->record.bps != ~0) { + sc->bps = ai->record.bps; + set = 1; + } + if (ai->record.msb != ~0) { + sc->msb = ai->record.msb; + set = 1; + } + if (ai->record.sample_rate != ~0) { + sc->rate = ai->record.sample_rate; + set = 1; + } + if (ai->record.channels != ~0) { + sc->rchan = ai->record.channels; + set = 1; + } + if (ai->record.block_size != ~0) { + sc->round = ai->record.block_size / + (sc->bps * sc->rchan); + set = 1; } -#endif - AUDIO_INITINFO(&ai); - ai.record.sample_rate = sc->sc_rparams.sample_rate; - ai.record.encoding = sc->sc_remu.encoding; - ai.record.channels = sc->sc_rparams.channels; - ai.record.precision = sc->sc_rparams.precision; - ai.record.bps = sc->sc_rparams.bps; - ai.record.msb = sc->sc_rparams.msb; - ai.record.pause = 0; - ai.play.sample_rate = sc->sc_pparams.sample_rate; - ai.play.encoding = sc->sc_pemu.encoding; - ai.play.channels = sc->sc_pparams.channels; - ai.play.precision = sc->sc_pparams.precision; - ai.play.bps = sc->sc_pparams.bps; - ai.play.msb = sc->sc_pparams.msb; - ai.play.pause = 0; - ai.mode = mode; - sc->sc_rr.blkset = sc->sc_pr.blkset = 0; /* Block sizes not set yet */ - 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: - sc->hw_if->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(struct audio_softc *sc) -{ - MUTEX_ASSERT_UNLOCKED(&audio_lock); - 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); -} - -/* - * Must be called from task context. - */ -void -audio_init_play(struct audio_softc *sc) -{ - MUTEX_ASSERT_UNLOCKED(&audio_lock); - sc->sc_wstamp = sc->sc_pr.stamp; - if (sc->hw_if->speaker_ctl) - sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_ON); -} -int -audio_drain(struct audio_softc *sc) -{ - int error, drops; - struct audio_ringbuffer *cb = &sc->sc_pr; + DPRINTF("%s: setinfo: set = %d, mode = %d, pause = %d\n", + DEVNAME(sc), set, sc->mode, sc->pause); - MUTEX_ASSERT_UNLOCKED(&audio_lock); - DPRINTF(("audio_drain: enter busy=%d used=%d\n", - sc->sc_pbus, sc->sc_pr.used)); - if (sc->sc_pr.mmapped || sc->sc_pr.used <= 0) + /* + * if the device not opened, we're done, don't touch the hardware + */ + if (sc->mode == 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; - if (sc->sc_pemu.sw_code) { - audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc); - sc->sc_pemu.sw_code(sc->hw_hdl, inp, cc); - } else - audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc); - inp += cc; - if (inp >= cb->end) - inp = cb->start; - cb->used += cc; - cb->inp = inp; - error = audiostartp(sc); + + /* + * change parameters and recalculate buffer sizes + */ + if (set) { + if (sc->active) { + DPRINTF("%s: can't change params during dma\n", + DEVNAME(sc)); + return EBUSY; + } + error = audio_setpar(sc); if (error) return error; + audio_clear(sc); + if ((sc->mode & AUMODE_PLAY) && sc->ops->init_output) { + error = sc->ops->init_output(sc->arg, + sc->play.data, sc->play.len); + if (error) + return error; + } + if ((sc->mode & AUMODE_RECORD) && sc->ops->init_input) { + error = sc->ops->init_input(sc->arg, + sc->rec.data, sc->rec.len); + 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. + * if unpaused, start */ - mtx_enter(&audio_lock); - drops = cb->drops; - error = 0; - 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); - if (sc->sc_dying) - error = EIO; + if (!sc->pause && !sc->active) { + error = audio_start(sc); + if (error) + return error; } - mtx_leave(&audio_lock); - return error; + return 0; } int -audio_quiesce(struct audio_softc *sc) +audio_getinfo(struct audio_softc *sc, struct audio_info *ai) { - sc->sc_quiesce = AUDIO_QUIESCE_START; + ai->play.sample_rate = ai->record.sample_rate = sc->rate; + ai->play.encoding = ai->record.encoding = sc->sw_enc; + ai->play.precision = ai->record.precision = sc->bits; + ai->play.bps = ai->record.bps = sc->bps; + ai->play.msb = ai->record.msb = sc->msb; + ai->play.channels = sc->pchan; + ai->record.channels = sc->rchan; + /* + * XXX: this is used only to display counters through audioctl + * and the pos counters are more useful + */ mtx_enter(&audio_lock); - while (sc->sc_pbus && !sc->sc_pqui) - audio_sleep(&sc->sc_wchan, "audpqui"); - while (sc->sc_rbus && !sc->sc_rqui) - audio_sleep(&sc->sc_rchan, "audrqui"); + ai->play.samples = sc->play.pos - sc->play.xrun; + ai->record.samples = sc->rec.pos - sc->rec.xrun; mtx_leave(&audio_lock); - sc->sc_quiesce = AUDIO_QUIESCE_SILENT; + ai->play.pause = ai->record.pause = sc->pause; + ai->play.active = ai->record.active = sc->active; - au_get_mute(sc, &sc->sc_outports, &sc->sc_mute); - au_set_mute(sc, &sc->sc_outports, 1); + ai->play.buffer_size = sc->play.datalen; + ai->record.buffer_size = sc->rec.datalen; - if (sc->sc_pbus) - sc->hw_if->halt_output(sc->hw_hdl); - if (sc->sc_rbus) - sc->hw_if->halt_input(sc->hw_hdl); + ai->play.block_size = sc->round * sc->bps * sc->pchan; + ai->record.block_size = sc->round * sc->bps * sc->rchan; + ai->hiwat = sc->nblks; + ai->lowat = sc->nblks; + ai->mode = sc->mode; return 0; } -void -audio_wakeup(struct audio_softc *sc) +int +audio_match(struct device *parent, void *match, void *aux) { - int setmode = 0; + struct audio_attach_args *sa = aux; - sc->sc_pqui = sc->sc_rqui = 0; + return (sa->type == AUDIODEV_TYPE_AUDIO) ? 1 : 0; +} - au_set_mute(sc, &sc->sc_outports, sc->sc_mute); +void +audio_attach(struct device *parent, struct device *self, void *aux) +{ + struct audio_softc *sc = (void *)self; + struct audio_attach_args *sa = aux; + struct audio_hw_if *ops = sa->hwif; + void *arg = sa->hdl; + int error; - if (sc->sc_pbus) - setmode |= AUMODE_PLAY; - if (sc->sc_rbus) - setmode |= AUMODE_RECORD; + printf("\n"); - if (setmode) { - sc->hw_if->set_params(sc->hw_hdl, setmode, - sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), - &sc->sc_pparams, &sc->sc_rparams); +#ifdef DIAGNOSTIC + if (ops == 0 || + ops->open == 0 || + ops->close == 0 || + ops->query_encoding == 0 || + ops->set_params == 0 || + (ops->start_output == 0 && ops->trigger_output == 0) || + (ops->start_input == 0 && ops->trigger_input == 0) || + ops->halt_output == 0 || + ops->halt_input == 0 || + ops->getdev == 0 || + ops->set_port == 0 || + ops->get_port == 0 || + ops->query_devinfo == 0 || + ops->get_props == 0) { + printf("%s: missing method\n", DEVNAME(sc)); + sc->ops = 0; + return; } +#endif + sc->ops = ops; + sc->arg = arg; - if (sc->sc_pbus) { - if (sc->hw_if->trigger_output) - sc->hw_if->trigger_output(sc->hw_hdl, sc->sc_pr.start, - sc->sc_pr.end, sc->sc_pr.blksize, - audio_pint, (void *)sc, &sc->sc_pparams); - else - sc->hw_if->start_output(sc->hw_hdl, sc->sc_pr.outp, - sc->sc_pr.blksize, audio_pint, (void *)sc); - } - if (sc->sc_rbus) { - if (sc->hw_if->trigger_input) - sc->hw_if->trigger_input(sc->hw_hdl, sc->sc_rr.start, - sc->sc_rr.end, sc->sc_rr.blksize, - audio_rint, (void *)sc, &sc->sc_rparams); - else - sc->hw_if->start_input(sc->hw_hdl, sc->sc_rr.inp, - sc->sc_rr.blksize, audio_rint, (void *)sc); +#if NWSKBD > 0 + wskbd_mixer_init(sc); +#endif /* NWSKBD > 0 */ + + error = audio_buf_init(sc, &sc->play, AUMODE_PLAY); + if (error) { + sc->ops = 0; + printf("%s: could not allocate play buffer\n", DEVNAME(sc)); + return; + } + error = audio_buf_init(sc, &sc->rec, AUMODE_RECORD); + if (error) { + audio_buf_done(sc, &sc->play); + sc->ops = 0; + printf("%s: could not allocate record buffer\n", DEVNAME(sc)); + return; } - sc->sc_quiesce = 0; - wakeup(&sc->sc_quiesce); + /* set defaults */ + sc->sw_enc = AUDIO_ENCODING_SLINEAR; + sc->bits = 16; + sc->bps = 2; + sc->msb = 1; + sc->rate = 48000; + sc->pchan = 2; + sc->rchan = 2; + sc->round = 960; + sc->nblks = 2; + sc->play.pos = sc->play.xrun = sc->rec.pos = sc->rec.xrun = 0; } -/* - * Close an audio chip. - */ -/* ARGSUSED */ int -audio_close(dev_t dev, int flags, int ifmt, struct proc *p) +audio_activate(struct device *self, int act) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct audio_hw_if *hw = sc->hw_if; + struct audio_softc *sc = (struct audio_softc *)self; - DPRINTF(("audio_close: unit=%d flags=0x%x\n", unit, flags)); + switch (act) { + case DVACT_QUIESCE: + /* + * good drivers run play and rec handlers in a single + * interrupt. Grab the lock to ensure we expose the same + * sc->quiesce value to both play and rec handlers + */ + mtx_enter(&audio_lock); + sc->quiesce = 1; + mtx_leave(&audio_lock); - - /* Stop recording. */ - if ((flags & FREAD) && sc->sc_rbus) { /* - * XXX Some drivers (e.g. SB) use the same routine - * to halt input and output so don't halt input if - * in full duplex mode. These drivers should be fixed. + * once sc->quiesce is set, interrupts may occur, but + * counters are not advanced and consequently processes + * keep sleeping. + * + * XXX: ensure read/write/ioctl don't start/stop + * DMA at the same time, this needs a "ready" condvar */ - if (!sc->sc_full_duplex || - sc->hw_if->halt_input != sc->hw_if->halt_output) { - sc->hw_if->halt_input(sc->hw_hdl); - } - sc->sc_rbus = 0; - } + if (sc->mode != 0 && sc->active) + audio_stop_do(sc); + DPRINTF("%s: quesce: active = %d\n", DEVNAME(sc), sc->active); + break; + case DVACT_WAKEUP: + DPRINTF("%s: wakeup: active = %d\n", DEVNAME(sc), sc->active); - /* - * If there is pending output, let it drain (unless - * the output is paused). - */ - if ((flags & FWRITE) && sc->sc_pbus) { /* - * Block until output drains, but allow ^C interrupt. - * XXX: drain is never used, remove it! + * keep buffer usage the same, but set start pointer to + * the beginning of the buffer. + * + * No need to grab the audio_lock as DMA is stopped and + * this is the only thread running (caller ensures this) */ - mtx_enter(&audio_lock); - /* avoid excessive wakeups */ - sc->sc_pr.usedlow = sc->sc_pr.blksize; - mtx_leave(&audio_lock); - if (!sc->sc_pr.pause) { - if (!audio_drain(sc) && hw->drain) - (void)hw->drain(sc->hw_hdl); + sc->quiesce = 0; + wakeup(&sc->quiesce); + + if(sc->mode != 0) { + if (audio_setpar(sc) != 0) + break; + if (sc->mode & AUMODE_PLAY) { + sc->play.start = 0; + audio_fill_sil(sc, sc->play.data, sc->play.len); + } + if (sc->mode & AUMODE_RECORD) { + sc->rec.start = sc->rec.len - sc->rec.used; + audio_fill_sil(sc, sc->rec.data, sc->rec.len); + } + if (sc->active) + audio_start_do(sc); } - sc->hw_if->halt_output(sc->hw_hdl); - sc->sc_pbus = 0; + break; } + return 0; +} - hw->close(sc->hw_hdl); +int +audio_detach(struct device *self, int flags) +{ + struct audio_softc *sc = (struct audio_softc *)self; + int maj, mn; + + DPRINTF("%s: audio_detach: flags = %d\n", DEVNAME(sc), flags); + wakeup(&sc->quiesce); + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == audioopen) + break; /* - * If flags has neither read nor write then reset both - * directions. Encountered when someone runs revoke(2). + * Nuke the vnodes for any open instances, calls close but as + * close uses device_lookup, it returns EXIO and does nothing */ + mn = self->dv_unit; + vdevgone(maj, mn | AUDIO_DEV_SOUND, mn | AUDIO_DEV_SOUND, VCHR); + vdevgone(maj, mn | AUDIO_DEV_AUDIO, mn | AUDIO_DEV_AUDIO, VCHR); + vdevgone(maj, mn | AUDIO_DEV_AUDIOCTL, mn | AUDIO_DEV_AUDIOCTL, VCHR); + vdevgone(maj, mn | AUDIO_DEV_MIXER, mn | AUDIO_DEV_MIXER, VCHR); - if ((flags & FREAD) || ((flags & (FREAD|FWRITE)) == 0)) { - sc->sc_open &= ~AUOPEN_READ; - sc->sc_mode &= ~AUMODE_RECORD; - } - if ((flags & FWRITE) || ((flags & (FREAD|FWRITE)) == 0)) { - sc->sc_open &= ~AUOPEN_WRITE; - sc->sc_mode &= ~(AUMODE_PLAY|AUMODE_PLAY_ALL); + /* + * The close() method did nothing, quickly halt DMA (normally + * parent is already gone, and code below is no-op), and wake-up + * user-land blocked in read/write/ioctl, which return EIO. + */ + if (sc->mode != 0) { + if (sc->active) { + wakeup(&sc->play.blocking); + selwakeup(&sc->play.sel); + wakeup(&sc->rec.blocking); + selwakeup(&sc->rec.sel); + audio_stop(sc); + } + sc->ops->close(sc->arg); + sc->mode = 0; } - sc->sc_async_audio = 0; - sc->sc_full_duplex = 0; - DPRINTF(("audio_close: done\n")); - return (0); + /* free resources */ + audio_buf_done(sc, &sc->play); + audio_buf_done(sc, &sc->rec); + return 0; } -int -audio_read(dev_t dev, struct uio *uio, int ioflag) +struct device * +audio_attach_mi(struct audio_hw_if *ops, void *arg, struct device *dev) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct audio_ringbuffer *cb = &sc->sc_rr; - u_char *outp; - int error, cc, n, resid; - - if (cb->mmapped) - return EINVAL; + struct audio_attach_args aa; - DPRINTFN(1,("audio_read: cc=%ld mode=%d\n", - uio->uio_resid, sc->sc_mode)); + aa.type = AUDIODEV_TYPE_AUDIO; + aa.hwif = ops; + aa.hdl = arg; /* - * Block if fully quiesced. Don't block when quiesce - * has started, as the buffer position may still need - * to advance. + * attach this driver to the caller (hardware driver), this + * checks the kernel config and possibly calls audio_attach() */ - while (sc->sc_quiesce == AUDIO_QUIESCE_SILENT) - tsleep(&sc->sc_quiesce, 0, "aud_qrd", 0); + return config_found(dev, &aa, audioprint); +} - 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) { - mtx_enter(&audio_lock); - 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) { - mtx_leave(&audio_lock); - return EWOULDBLOCK; - } - error = audio_sleep(&sc->sc_rchan, "aud_hr"); - if (sc->sc_dying) - error = EIO; - if (error) { - mtx_leave(&audio_lock); - return error; - } - } - mtx_leave(&audio_lock); +int +audioprint(void *aux, const char *pnp) +{ + struct audio_attach_args *arg = aux; + const char *type; - if (uio->uio_resid < cc) - cc = uio->uio_resid; - DPRINTFN(1, ("audio_read: reading in write mode, cc=%d\n", cc)); - error = audio_silence_copyout(sc, cc, uio); - sc->sc_wstamp += cc; - } - return (error); - } - while (uio->uio_resid > 0) { - mtx_enter(&audio_lock); - while (cb->used <= 0) { - if (!sc->sc_rbus && !sc->sc_rr.pause) { - mtx_leave(&audio_lock); - error = audiostartr(sc); - if (error) - return error; - mtx_enter(&audio_lock); - continue; - } - if (ioflag & IO_NDELAY) { - mtx_leave(&audio_lock); - return (EWOULDBLOCK); - } - DPRINTFN(2, ("audio_read: sleep used=%d\n", cb->used)); - error = audio_sleep(&sc->sc_rchan, "aud_rd"); - if (sc->sc_dying) - error = EIO; - if (error) { - mtx_leave(&audio_lock); - return error; - } + if (pnp != NULL) { + switch (arg->type) { + case AUDIODEV_TYPE_AUDIO: + type = "audio"; + break; + case AUDIODEV_TYPE_OPL: + type = "opl"; + break; + case AUDIODEV_TYPE_MPU: + type = "mpu"; + break; + default: + panic("audioprint: unknown type %d", arg->type); } - resid = uio->uio_resid; - outp = cb->outp; - cc = cb->used - cb->usedlow; /* maximum to read */ - n = cb->end - outp; - if (cc > n) - cc = n; /* don't read beyond end of buffer */ - - if (cc > resid) - cc = resid; /* and no more than we want */ - cb->used -= cc; - cb->outp += cc; - if (cb->outp >= cb->end) - cb->outp = cb->start; - mtx_leave(&audio_lock); - DPRINTFN(1,("audio_read: outp=%p, cc=%d\n", outp, cc)); - if (sc->sc_remu.sw_code) - sc->sc_remu.sw_code(sc->hw_hdl, outp, cc); - error = uiomove(outp, cc, uio); - if (error) - return error; + printf("%s at %s", type, pnp); } - return 0; + return UNCONF; } -void -audio_clear(struct audio_softc *sc) +int +audio_open(struct audio_softc *sc, int flags) { - MUTEX_ASSERT_UNLOCKED(&audio_lock); - if (sc->sc_rbus) { - audio_wake(&sc->sc_rchan); - sc->hw_if->halt_input(sc->hw_hdl); - sc->sc_rbus = 0; - } - if (sc->sc_pbus) { - audio_wake(&sc->sc_wchan); - sc->hw_if->halt_output(sc->hw_hdl); - sc->sc_pbus = 0; - } -} + int error; + int props; -void -audio_set_blksize(struct audio_softc *sc, int mode, int fpb) { - struct audio_hw_if *hw = sc->hw_if; - struct audio_params *parm; - struct audio_ringbuffer *rb; - int bs, fs, maxbs; - - if (mode == AUMODE_PLAY) { - parm = &sc->sc_pparams; - rb = &sc->sc_pr; - } else { - parm = &sc->sc_rparams; - rb = &sc->sc_rr; + if (sc->mode) + return EBUSY; + error = sc->ops->open(sc->arg, flags); + if (error) + return error; + sc->active = 0; + sc->pause = 1; + sc->rec.blocking = 0; + sc->play.blocking = 0; + sc->mode = 0; + if (flags & FWRITE) + sc->mode |= AUMODE_PLAY; + if (flags & FREAD) + sc->mode |= AUMODE_RECORD; + props = sc->ops->get_props(sc->arg); + if (sc->mode == (AUMODE_PLAY | AUMODE_RECORD)) { + if (!(props & AUDIO_PROP_FULLDUPLEX)) { + error = ENOTTY; + goto bad; + } + if (sc->ops->setfd) { + error = sc->ops->setfd(sc->arg, 1); + if (error) + goto bad; + } } - fs = parm->channels * parm->bps; - bs = fpb * fs; - maxbs = rb->bufsize / 2; - if (bs > maxbs) - bs = (maxbs / fs) * fs; + if (sc->ops->speaker_ctl) { + /* + * XXX: what is this used for? + */ + sc->ops->speaker_ctl(sc->arg, + (sc->mode & AUMODE_PLAY) ? SPKR_ON : SPKR_OFF); + } - ROUNDSIZE(bs); - if (hw->round_blocksize) - bs = hw->round_blocksize(sc->hw_hdl, bs); - rb->blksize = bs; + error = audio_setpar(sc); + if (error) + goto bad; + audio_clear(sc); - DPRINTF(("audio_set_blksize: %s blksize=%d\n", - mode == AUMODE_PLAY ? "play" : "record", bs)); + /* + * allow read(2)/write(2) to automatically start DMA, without + * the need for ioctl(), to make /dev/audio usable in scripts + */ + sc->pause = 0; + return 0; +bad: + sc->ops->close(sc->arg); + sc->mode = 0; + return error; } -void -audio_calc_blksize(struct audio_softc *sc, int mode) +int +audio_drain(struct audio_softc *sc) { - struct audio_params *param; + int error, xrun; + unsigned char *ptr; + size_t count, bpf; - if (mode == AUMODE_PLAY) { - if (sc->sc_pr.blkset) - return; - param = &sc->sc_pparams; - } else { - if (sc->sc_rr.blkset) - return; - param = &sc->sc_rparams; + DPRINTF("%s: drain: mode = %d, pause = %d, active = %d, used = %zu\n", + DEVNAME(sc), sc->mode, sc->pause, sc->active, sc->play.used); + if (!(sc->mode & AUMODE_PLAY) || sc->pause) + return 0; + + /* discard partial samples, required by audio_fill_sil() */ + mtx_enter(&audio_lock); + bpf = sc->pchan * sc->bps; + sc->play.used -= sc->play.used % bpf; + if (sc->play.used == 0) { + mtx_leave(&audio_lock); + return 0; } - audio_set_blksize(sc, mode, param->sample_rate * audio_blk_ms / 1000); -} -void -audio_fill_silence(struct audio_params *params, u_char *start, u_char *p, int n) -{ - size_t rounderr; - int i, nsamples; - u_char auzero[4] = {0, 0, 0, 0}; + if (!sc->active) { + /* + * dma not started yet because buffer was not full + * enough to start automatically. Pad it and start now. + */ + for (;;) { + ptr = audio_buf_wgetblk(&sc->play, &count); + if (count == 0) + break; + audio_fill_sil(sc, ptr, count); + audio_buf_wcommit(&sc->play, count); + } + mtx_leave(&audio_lock); + error = audio_start(sc); + if (error) + return error; + mtx_enter(&audio_lock); + } - /* - * p may point the middle of a sample; round it to the - * beginning of the sample, so we overwrite partially written - * ones. - */ - rounderr = (p - start) % params->bps; - p -= rounderr; - n += rounderr; - nsamples = n / params->bps; + xrun = sc->play.xrun; + while (sc->play.xrun == xrun) { + DPRINTF("%s: drain: used = %zu, xrun = %ld\n", + DEVNAME(sc), sc->play.used, sc->play.xrun); - switch (params->encoding) { - case AUDIO_ENCODING_SLINEAR_LE: - case AUDIO_ENCODING_SLINEAR_BE: - break; - case AUDIO_ENCODING_ULAW: - auzero[0] = 0x7f; - break; - case AUDIO_ENCODING_ALAW: - auzero[0] = 0x55; - break; - case AUDIO_ENCODING_ULINEAR_LE: - if (params->msb == 1) - auzero[params->bps - 1] = 0x80; - else - auzero[params->bps - 1] = 1 << ((params->precision + 7) % NBBY); - break; - case AUDIO_ENCODING_ULINEAR_BE: - if (params->msb == 1) - auzero[0] = 0x80; - else - auzero[0] = 1 << ((params->precision + 7) % NBBY); - break; - case AUDIO_ENCODING_MPEG_L1_STREAM: - case AUDIO_ENCODING_MPEG_L1_PACKETS: - case AUDIO_ENCODING_MPEG_L1_SYSTEM: - case AUDIO_ENCODING_MPEG_L2_STREAM: - case AUDIO_ENCODING_MPEG_L2_PACKETS: - case AUDIO_ENCODING_MPEG_L2_SYSTEM: - case AUDIO_ENCODING_ADPCM: /* is this right XXX */ - break; - default: - DPRINTF(("audio: bad encoding %d\n", params->encoding)); - break; - } - while (--nsamples >= 0) { - for (i = 0; i < params->bps; i++) - *p++ = auzero[i]; + /* + * set a 5 second timeout, in case interrupts don't + * work, useful only for debugging drivers + */ + sc->play.blocking = 1; + error = msleep(&sc->play.blocking, &audio_lock, + PWAIT | PCATCH, "au_dr", 5 * hz); + if (!(sc->dev.dv_flags & DVF_ACTIVE)) + error = EIO; + if (error) { + DPRINTF("%s: drain, err = %d\n", DEVNAME(sc), error); + break; + } } + mtx_leave(&audio_lock); + return error; } int -audio_silence_copyout(struct audio_softc *sc, int n, struct uio *uio) +audio_close(struct audio_softc *sc) +{ + audio_drain(sc); + if (sc->active) + audio_stop(sc); + sc->ops->close(sc->arg); + sc->mode = 0; + DPRINTF("%s: close: done\n", DEVNAME(sc)); + return 0; +} + +int +audio_read(struct audio_softc *sc, struct uio *uio, int ioflag) { + unsigned char *ptr; + size_t count; int error; - int k; - u_char zerobuf[128]; - audio_fill_silence(&sc->sc_rparams, zerobuf, zerobuf, sizeof zerobuf); + DPRINTFN(1, "%s: read: resid = %zd\n", DEVNAME(sc), uio->uio_resid); - error = 0; - while (n > 0 && uio->uio_resid > 0 && !error) { - k = min(n, min(uio->uio_resid, sizeof zerobuf)); - error = uiomovei(zerobuf, k, uio); - n -= k; + /* block if quiesced */ + while (sc->quiesce) + tsleep(&sc->quiesce, 0, "au_qrd", 0); + + /* start automatically if setinfo() was never called */ + mtx_enter(&audio_lock); + if (!sc->active && !sc->pause && sc->rec.used == 0) { + mtx_leave(&audio_lock); + error = audio_start(sc); + if (error) + return error; + mtx_enter(&audio_lock); + } + + /* if there is no data then sleep */ + while (sc->rec.used == 0) { + if (ioflag & IO_NDELAY) { + mtx_leave(&audio_lock); + return EWOULDBLOCK; + } + DPRINTFN(1, "%s: read sleep\n", DEVNAME(sc)); + sc->rec.blocking = 1; + error = msleep(&sc->rec.blocking, + &audio_lock, PWAIT | PCATCH, "au_rd", 0); + if (!(sc->dev.dv_flags & DVF_ACTIVE)) + error = EIO; +#ifdef AUDIO_DEBUG + if (error) { + DPRINTF("%s: read woke up error = %d\n", + DEVNAME(sc), error); + } +#endif + if (error) { + mtx_leave(&audio_lock); + return error; + } } - return (error); + + /* at this stage, there is data to transfer */ + while (uio->uio_resid > 0 && sc->rec.used > 0) { + ptr = audio_buf_rgetblk(&sc->rec, &count); + if (count > uio->uio_resid) + count = uio->uio_resid; + mtx_leave(&audio_lock); + DPRINTFN(1, "%s: read: start = %zu, count = %zu\n", + DEVNAME(sc), ptr - sc->rec.data, count); + if (sc->conv_dec) + sc->conv_dec(ptr, count); + error = uiomove(ptr, count, uio); + if (error) + return error; + mtx_enter(&audio_lock); + audio_buf_rdiscard(&sc->rec, count); + } + mtx_leave(&audio_lock); + return 0; } int -audio_write(dev_t dev, struct uio *uio, int ioflag) +audio_write(struct audio_softc *sc, struct uio *uio, int ioflag) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct audio_ringbuffer *cb = &sc->sc_pr; - u_char *inp; - int error, n, cc, resid, avail; + unsigned char *ptr; + size_t count; + int error; - DPRINTFN(2, ("audio_write: sc=%p(unit=%d) count=%zd used=%d(hi=%d)\n", sc, unit, - uio->uio_resid, sc->sc_pr.used, sc->sc_pr.usedhigh)); + DPRINTFN(1, "%s: write: resid = %zd\n", DEVNAME(sc), uio->uio_resid); - if (cb->mmapped) - return EINVAL; + /* block if quiesced */ + while (sc->quiesce) + tsleep(&sc->quiesce, 0, "au_qwr", 0); /* - * Block if fully quiesced. Don't block when quiesce - * has started, as the buffer position may still need - * to advance. + * if IO_NDELAY flag is set then check if there is enough room + * in the buffer to store at least one byte. If not then dont + * start the write process. */ - while (sc->sc_quiesce == AUDIO_QUIESCE_SILENT) - tsleep(&sc->sc_quiesce, 0, "aud_qwr", 0); - - 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; + mtx_enter(&audio_lock); + if (uio->uio_resid > 0 && (ioflag & IO_NDELAY)) { + if (sc->play.used == sc->play.len ) { + mtx_leave(&audio_lock); + return EWOULDBLOCK; + } } - DPRINTFN(1, ("audio_write: sr=%ld, enc=%d, prec=%d, chan=%d\n", - sc->sc_pparams.sample_rate, sc->sc_pparams.encoding, - sc->sc_pparams.precision, sc->sc_pparams.channels)); - while (uio->uio_resid > 0) { - mtx_enter(&audio_lock); - while (cb->used >= cb->usedhigh) { - DPRINTFN(2, ("audio_write: sleep used=%d lowat=%d hiwat=%d\n", - cb->used, cb->usedlow, cb->usedhigh)); + while (1) { + ptr = audio_buf_wgetblk(&sc->play, &count); + if (count > 0) + break; if (ioflag & IO_NDELAY) { + /* + * At this stage at least one byte is already + * moved so we do not return EWOULDBLOCK + */ mtx_leave(&audio_lock); - return (EWOULDBLOCK); + return 0; } - error = audio_sleep(&sc->sc_wchan, "aud_wr"); - if (sc->sc_dying) + DPRINTFN(1, "%s: write sleep\n", DEVNAME(sc)); + sc->play.blocking = 1; + error = msleep(&sc->play.blocking, + &audio_lock, PWAIT | PCATCH, "au_wr", 0); + if (!(sc->dev.dv_flags & DVF_ACTIVE)) error = EIO; +#ifdef AUDIO_DEBUG + if (error) { + DPRINTF("%s: write woke up error = %d\n", + DEVNAME(sc), error); + } +#endif if (error) { mtx_leave(&audio_lock); return error; } } - resid = uio->uio_resid; - avail = cb->end - cb->inp; - inp = cb->inp; - cc = cb->usedhigh - cb->used; - if (cc > resid) - cc = resid; - if (cc > avail) - cc = avail; - cb->inp += cc; - if (cb->inp >= cb->end) - cb->inp = cb->start; - cb->used += cc; - /* - * This is a very suboptimal way of keeping track of - * silence in the buffer, but it is simple. - */ - sc->sc_sil_count = 0; - if (!sc->sc_pbus && !cb->pause && cb->used >= cb->blksize) { + if (count > uio->uio_resid) + count = uio->uio_resid; + mtx_leave(&audio_lock); + error = uiomove(ptr, count, uio); + if (error) + return 0; + if (sc->conv_enc) { + sc->conv_enc(ptr, count); + DPRINTFN(1, "audio_write: converted count = %zu\n", + count); + } + mtx_enter(&audio_lock); + audio_buf_wcommit(&sc->play, count); + + /* start automatically if setinfo() was never called */ + if (!sc->active && !sc->pause && + sc->play.used == sc->play.len) { mtx_leave(&audio_lock); - error = audiostartp(sc); + error = audio_start(sc); if (error) return error; - } else - mtx_leave(&audio_lock); - DPRINTFN(1, ("audio_write: uiomove cc=%d inp=%p, left=%zd\n", - cc, inp, uio->uio_resid)); - error = uiomovei(inp, cc, uio); - if (error) - return 0; - if (sc->sc_pemu.sw_code) { - sc->sc_pemu.sw_code(sc->hw_hdl, inp, cc); - DPRINTFN(1, ("audio_write: expanded cc=%d\n", cc)); + mtx_enter(&audio_lock); } } + mtx_leave(&audio_lock); return 0; } int -audio_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +audio_ioctl(struct audio_softc *sc, unsigned long cmd, void *addr) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct audio_hw_if *hw = sc->hw_if; struct audio_offset *ao; - struct audio_info ai; - int error = 0, offs, fd; - int rbus, pbus; + int error = 0, fd; - /* - * Block if fully quiesced. Don't block when quiesce - * has started, as the buffer position may still need - * to advance. An ioctl may be used to determine how - * much to read or write. - */ - while (sc->sc_quiesce == AUDIO_QUIESCE_SILENT) - tsleep(&sc->sc_quiesce, 0, "aud_qio", 0); + /* block if quiesced */ + while (sc->quiesce) + tsleep(&sc->quiesce, 0, "au_qio", 0); - DPRINTF(("audio_ioctl(%ld,'%c',%ld)\n", - IOCPARM_LEN(cmd), (int)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")); - rbus = sc->sc_rbus; - pbus = sc->sc_pbus; - audio_clear(sc); - error = audio_initbufs(sc); - if (error) { - return error; - } - sc->sc_rr.pause = 0; - sc->sc_pr.pause = 0; - if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_pbus && pbus) - error = audiostartp(sc); - if (!error && - (sc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus) - error = audiostartr(sc); - break; - - /* - * Number of read (write) samples dropped. We don't know where or - * when they were dropped. - * - * The audio_ringbuffer->drops count is the number of buffer - * sample size bytes. Convert it to userland sample size bytes, - * then convert to samples. There is no easy way to get the - * buffer sample size, but the userland sample size can be - * calculated with userland channels and userland precision. - * - * original formula: - * sc->sc_rr.drops / - * (sc->sc_rparams.channels * sc->sc_rparams.bps) - */ - case AUDIO_RERROR: - *(int *)addr = sc->sc_rr.drops / - (sc->sc_rparams.channels * sc->sc_rparams.bps); - break; - case AUDIO_PERROR: - *(int *)addr = sc->sc_pr.drops / - (sc->sc_pparams.channels * sc->sc_pparams.bps); + mtx_enter(&audio_lock); + *(int *)addr = sc->play.xrun / (sc->pchan * sc->bps); + mtx_leave(&audio_lock); break; - - /* - * Offsets into buffer. - */ - case AUDIO_GETIOFFS: + case AUDIO_RERROR: mtx_enter(&audio_lock); - /* 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; + *(int *)addr = sc->rec.xrun / (sc->rchan * sc->bps); mtx_leave(&audio_lock); break; - case AUDIO_GETOOFFS: mtx_enter(&audio_lock); - /* 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; + ao->samples = sc->play.pos; mtx_leave(&audio_lock); 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_pr.used; + case AUDIO_GETIOFFS: + mtx_enter(&audio_lock); + ao = (struct audio_offset *)addr; + ao->samples = sc->rec.pos; + mtx_leave(&audio_lock); break; - case AUDIO_SETINFO: - DPRINTF(("AUDIO_SETINFO mode=0x%x\n", sc->sc_mode)); - error = audiosetinfo(sc, (struct audio_info *)addr); + error = audio_setinfo(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); + error = audio_getinfo(sc, (struct audio_info *)addr); break; - case AUDIO_GETDEV: - DPRINTF(("AUDIO_GETDEV\n")); - error = hw->getdev(sc->hw_hdl, (audio_device_t *)addr); + error = sc->ops->getdev(sc->arg, (audio_device_t *)addr); break; - case AUDIO_GETENC: - DPRINTF(("AUDIO_GETENC\n")); - /* Pass read/write info down to query_encoding */ - ((struct audio_encoding *)addr)->flags = sc->sc_open; - error = hw->query_encoding(sc->hw_hdl, (struct audio_encoding *)addr); + error = sc->ops->query_encoding(sc->arg, + (struct audio_encoding *)addr); break; - case AUDIO_GETFD: - DPRINTF(("AUDIO_GETFD\n")); - *(int *)addr = sc->sc_full_duplex; + *(int *)addr = (sc->mode & (AUMODE_PLAY | AUMODE_RECORD)) == + (AUMODE_PLAY | AUMODE_RECORD); break; - 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; - if (fd) { - AUDIO_INITINFO(&ai); - ai.mode = sc->sc_mode | - (AUMODE_PLAY | AUMODE_RECORD); - error = audiosetinfo(sc, &ai); - } - } - } else { - if (fd) - error = ENOTTY; - else - error = 0; - } + if ((sc->mode & (AUMODE_PLAY | AUMODE_RECORD)) != + (AUMODE_PLAY | AUMODE_RECORD) || !fd) + return EINVAL; break; - case AUDIO_GETPROPS: - DPRINTF(("AUDIO_GETPROPS\n")); - *(int *)addr = hw->get_props(sc->hw_hdl); - break; - - case AUDIO_GETPRINFO: - DPRINTF(("AUDIO_GETPRINFO\n")); - error = audiogetbufinfo(sc, (struct audio_bufinfo *)addr, - AUMODE_PLAY); + *(int *)addr = sc->ops->get_props(sc->arg); break; - - case AUDIO_GETRRINFO: - DPRINTF(("AUDIO_GETRRINFO\n")); - error = audiogetbufinfo(sc, (struct audio_bufinfo *)addr, - AUMODE_RECORD); - break; - default: - DPRINTF(("audio_ioctl: unknown ioctl\n")); + DPRINTF("%s: unknown ioctl 0x%lx\n", DEVNAME(sc), cmd); error = ENOTTY; break; } - DPRINTF(("audio_ioctl(%ld,'%c',%ld) result %d\n", - IOCPARM_LEN(cmd), (int)IOCGROUP(cmd), cmd&0xff, error)); - return (error); + return error; } -void -audio_selwakeup(struct audio_softc *sc, int play) +int +audio_ioctl_mixer(struct audio_softc *sc, unsigned long cmd, void *addr) { - struct selinfo *si; + int error; - si = play? &sc->sc_wsel : &sc->sc_rsel; + /* block if quiesced */ + while (sc->quiesce) + tsleep(&sc->quiesce, 0, "mix_qio", 0); - audio_wake(play? &sc->sc_wchan : &sc->sc_rchan); - selwakeup(si); - if (sc->sc_async_audio) - psignal(sc->sc_async_audio, SIGIO); + switch (cmd) { + case FIONBIO: + /* All handled in the upper FS layer. */ + break; + case AUDIO_MIXER_DEVINFO: + ((mixer_devinfo_t *)addr)->un.v.delta = 0; + return sc->ops->query_devinfo(sc->arg, (mixer_devinfo_t *)addr); + case AUDIO_MIXER_READ: + return sc->ops->get_port(sc->arg, (mixer_ctrl_t *)addr); + case AUDIO_MIXER_WRITE: + error = sc->ops->set_port(sc->arg, (mixer_ctrl_t *)addr); + if (error) + return error; + if (sc->ops->commit_settings) + return sc->ops->commit_settings(sc->arg); + break; + default: + return ENOTTY; + } + return 0; } -#define AUDIO_FILTREAD(sc) ( \ - (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) ? \ - sc->sc_pr.stamp > sc->sc_wstamp : sc->sc_rr.used > sc->sc_rr.usedlow) - -#define AUDIO_FILTWRITE(sc) ( \ - (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_RECORD)) || \ - (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) || \ - (sc->sc_pr.used < (sc->sc_pr.usedlow + sc->sc_pr.blksize))) - int -audio_poll(dev_t dev, int events, struct proc *p) +audio_poll(struct audio_softc *sc, int events, struct proc *p) { - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; int revents = 0; mtx_enter(&audio_lock); - - DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, sc->sc_mode)); - - if (events & (POLLIN | POLLRDNORM)) { - if (AUDIO_FILTREAD(sc)) - revents |= events & (POLLIN | POLLRDNORM); - } - if (events & (POLLOUT | POLLWRNORM)) { - if (AUDIO_FILTWRITE(sc)) - revents |= events & (POLLOUT | POLLWRNORM); - } + if ((sc->mode & AUMODE_RECORD) && sc->rec.used > 0) + revents |= events & (POLLIN | POLLRDNORM); + if ((sc->mode & AUMODE_PLAY) && sc->play.used < sc->play.len) + revents |= events & (POLLOUT | POLLWRNORM); if (revents == 0) { if (events & (POLLIN | POLLRDNORM)) - selrecord(p, &sc->sc_rsel); + selrecord(p, &sc->rec.sel); if (events & (POLLOUT | POLLWRNORM)) - selrecord(p, &sc->sc_wsel); + selrecord(p, &sc->play.sel); } mtx_leave(&audio_lock); - return (revents); -} - -paddr_t -audio_mmap(dev_t dev, off_t off, int prot) -{ - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct audio_hw_if *hw = sc->hw_if; - struct audio_ringbuffer *cb; - - DPRINTF(("audio_mmap: off=%lld, 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 PROT_WRITE you SIGSEGV - * when writing to it, so PROT_READ|PROT_WRITE - * has to be used for mmapping the play buffer. - * 2) Even if calling mmap() with PROT_READ|PROT_WRITE - * audio_mmap will get called at some point with PROT_READ - * only. - * So, alas, we always map the play buffer for now. - */ - if (prot == (PROT_READ | PROT_WRITE) || - prot == PROT_WRITE) - cb = &sc->sc_pr; - else if (prot == PROT_READ) - cb = &sc->sc_rr; - else - return -1; -#else - cb = &sc->sc_pr; -#endif - - if ((u_int)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->start, cb->bufsize); - if (!sc->sc_pbus && !sc->sc_pr.pause) - (void)audiostartp(sc); - } else { - if (!sc->sc_rbus && !sc->sc_rr.pause) - (void)audiostartr(sc); - } - } - - return hw->mappage(sc->hw_hdl, cb->start, off, prot); -} - -int -audiostartr(struct audio_softc *sc) -{ - int error; - - MUTEX_ASSERT_UNLOCKED(&audio_lock); - 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)); - - if (sc->hw_if->trigger_input) - error = sc->hw_if->trigger_input(sc->hw_hdl, sc->sc_rr.start, - sc->sc_rr.end, sc->sc_rr.blksize, - audio_rint, (void *)sc, &sc->sc_rparams); - else - 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; + return revents; } int -audiostartp(struct audio_softc *sc) -{ - int error; - - MUTEX_ASSERT_UNLOCKED(&audio_lock); - 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.mmapped && sc->sc_pr.used < sc->sc_pr.blksize) - return 0; - - if (sc->hw_if->trigger_output) - error = sc->hw_if->trigger_output(sc->hw_hdl, sc->sc_pr.start, - sc->sc_pr.end, sc->sc_pr.blksize, - audio_pint, (void *)sc, &sc->sc_pparams); - else - 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 approximate way: it starts at sc_sil_start - * and extends sc_sil_count bytes. If there is already silence in - * the requested area nothing is done; so when the whole buffer is - * silent nothing happens. When the writer starts again sc_sil_count - * is set to 0. - */ -/* XXX - * Putting silence into the output buffer should not really be done - * with audio_lock, but there is no softaudio level to do it at yet. - */ -static __inline void -audio_pint_silence(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); - DPRINTFN(5, ("audio_pint_silence: fill cc=%d inp=%p, count=%d size=%d\n", - cc, inp, sc->sc_sil_count, (int)(cb->end - cb->start))); - - if (sc->sc_pemu.sw_code) { - audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc); - sc->sc_pemu.sw_code(sc->hw_hdl, inp, cc); - } else - audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc); - - } else { - DPRINTFN(5, ("audio_pint_silence: already silent cc=%d inp=%p\n", cc, inp)); - - } - } else { - sc->sc_sil_start = inp; - sc->sc_sil_count = cc; - DPRINTFN(5, ("audio_pint_silence: start fill %p %d\n", - inp, cc)); - - if (sc->sc_pemu.sw_code) { - audio_fill_silence(&sc->sc_pparams, cb->start, inp, cc); - sc->sc_pemu.sw_code(sc->hw_hdl, inp, cc); - } else - audio_fill_silence(&sc->sc_pparams, cb->start, 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(void *v) +audioopen(dev_t dev, int flags, int mode, struct proc *p) { - 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; - int blksize; + struct audio_softc *sc; int error; - MUTEX_ASSERT_LOCKED(&audio_lock); - if (!sc->sc_open) - return; /* ignore interrupt if not open */ - - if (sc->sc_pqui) - return; - - blksize = cb->blksize; - - add_audio_randomness((long)cb); - - cb->outp += blksize; - if (cb->outp >= cb->end) - cb->outp = cb->start; - cb->stamp += blksize; - if (cb->mmapped) { - DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n", - cb->outp, blksize, cb->inp)); - if (!hw->trigger_output) - (void)hw->start_output(sc->hw_hdl, cb->outp, - 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 / 3) { - 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) { - printf("audio: play interrupt(%d) off absolute by %ld us (%lu) (LOST)\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 -= blksize; - if (cb->used < blksize) { - /* we don't have a full block to use */ - inp = cb->inp; - cc = blksize - (inp - cb->start) % blksize; - if (cb->pause) - cb->pdrops += cc; - else { - cb->drops += cc; - sc->sc_playdrop += cc; - } - 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, blksize); - } - - DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->outp, blksize)); - if (!hw->trigger_output) { - error = hw->start_output(sc->hw_hdl, cb->outp, blksize, - audio_pint, (void *)sc); - if (error) { - /* XXX does this really help? */ - DPRINTF(("audio_pint restart failed: %d\n", error)); + sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev)); + if (sc == NULL) + return ENXIO; + if (sc->ops == NULL) + error = ENXIO; + else { + switch (AUDIO_DEV(dev)) { + case AUDIO_DEV_SOUND: + case AUDIO_DEV_AUDIO: + error = audio_open(sc, flags); + break; + case AUDIO_DEV_AUDIOCTL: + case AUDIO_DEV_MIXER: + error = 0; + break; + default: + error = ENXIO; } } - - DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n", - sc->sc_mode, cb->pause, cb->used, cb->usedlow)); - if ((sc->sc_mode & AUMODE_PLAY) && !cb->pause && - cb->used <= cb->usedlow) - audio_selwakeup(sc, 1); - - /* Possible to return one or more "phantom blocks" now. */ - if (!sc->sc_full_duplex && sc->sc_rchan) - audio_selwakeup(sc, 0); - - /* - * If quiesce requested, halt output when the ring buffer position - * is at the beginning, because when the hardware is resumed, it's - * buffer position is reset to the beginning. This will put - * hardware and software positions in sync across a suspend cycle. - */ - if (sc->sc_quiesce == AUDIO_QUIESCE_START && cb->outp == cb->start) { - sc->sc_pqui = 1; - audio_wake(&sc->sc_wchan); - } + device_unref(&sc->dev); + return error; } -/* - * 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(void *v) +int +audioclose(dev_t dev, int flags, int ifmt, struct proc *p) { - struct audio_softc *sc = v; - struct audio_hw_if *hw = sc->hw_if; - struct audio_ringbuffer *cb = &sc->sc_rr; - int blksize; + struct audio_softc *sc; int error; - MUTEX_ASSERT_LOCKED(&audio_lock); - if (!sc->sc_open) - return; /* ignore interrupt if not open */ - - if (sc->sc_rqui) - return; - - add_audio_randomness((long)cb); - - blksize = cb->blksize; - - cb->inp += blksize; - if (cb->inp >= cb->end) - cb->inp = cb->start; - cb->stamp += blksize; - if (cb->mmapped) { - DPRINTFN(2, ("audio_rint: mmapped inp=%p cc=%d\n", - cb->inp, blksize)); - if (!hw->trigger_input) - (void)hw->start_input(sc->hw_hdl, cb->inp, 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 += blksize; - if (cb->pause) { - DPRINTFN(1, ("audio_rint: pdrops %lu\n", cb->pdrops)); - cb->pdrops += blksize; - cb->outp += blksize; - if (cb->outp >= cb->end) - cb->outp = cb->start; - cb->used -= blksize; - } else if (cb->used >= cb->usedhigh) { - DPRINTFN(1, ("audio_rint: drops %lu\n", cb->drops)); - cb->drops += blksize; - cb->outp += blksize; - if (cb->outp >= cb->end) - cb->outp = cb->start; - cb->used -= blksize; - } - - DPRINTFN(2, ("audio_rint: inp=%p cc=%d used=%d\n", - cb->inp, blksize, cb->used)); - if (!hw->trigger_input) { - error = hw->start_input(sc->hw_hdl, cb->inp, blksize, - audio_rint, (void *)sc); - if (error) { - /* XXX does this really help? */ - DPRINTF(("audio_rint: restart failed: %d\n", error)); - } - } - - audio_selwakeup(sc, 0); - - /* - * If quiesce requested, halt input when the ring buffer position - * is at the beginning, because when the hardware is resumed, it's - * buffer position is reset to the beginning. This will put - * hardware and software positions in sync across a suspend cycle. - */ - if (sc->sc_quiesce == AUDIO_QUIESCE_START && cb->inp == cb->start) { - sc->sc_rqui = 1; - audio_wake(&sc->sc_rchan); + sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev)); + if (sc == NULL) + return ENXIO; + switch (AUDIO_DEV(dev)) { + case AUDIO_DEV_SOUND: + case AUDIO_DEV_AUDIO: + error = audio_close(sc); + break; + case AUDIO_DEV_MIXER: + case AUDIO_DEV_AUDIOCTL: + error = 0; + default: + error = ENXIO; } + device_unref(&sc->dev); + return error; } int -audio_check_params(struct audio_params *p) +audioread(dev_t dev, struct uio *uio, int ioflag) { - if (p->channels < 1 || p->channels > 12) - return (EINVAL); - - if (p->precision < 8 || p->precision > 32) - return (EINVAL); - - 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; - } - - 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 + struct audio_softc *sc; + int error; - switch (p->encoding) { - case AUDIO_ENCODING_ADPCM: - if (p->precision != 8) - p->precision = 8; + sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev)); + if (sc == NULL) + return ENXIO; + switch (AUDIO_DEV(dev)) { + case AUDIO_DEV_SOUND: + case AUDIO_DEV_AUDIO: + error = audio_read(sc, uio, ioflag); break; - case AUDIO_ENCODING_ULAW: - case AUDIO_ENCODING_ALAW: - case AUDIO_ENCODING_SLINEAR_LE: - case AUDIO_ENCODING_SLINEAR_BE: - case AUDIO_ENCODING_ULINEAR_LE: - case AUDIO_ENCODING_ULINEAR_BE: - case AUDIO_ENCODING_MPEG_L1_STREAM: - case AUDIO_ENCODING_MPEG_L1_PACKETS: - case AUDIO_ENCODING_MPEG_L1_SYSTEM: - case AUDIO_ENCODING_MPEG_L2_STREAM: - case AUDIO_ENCODING_MPEG_L2_PACKETS: - case AUDIO_ENCODING_MPEG_L2_SYSTEM: + case AUDIO_DEV_AUDIOCTL: + case AUDIO_DEV_MIXER: + error = ENODEV; break; default: - return (EINVAL); - } - - return (0); -} - -void -audio_emu_setup(int mode, struct audio_params *p, struct audio_emu *e) -{ - if (p->encoding == e->encoding) { - e->sw_code = NULL; - return; - } - switch (p->encoding) { - case AUDIO_ENCODING_ULAW: -#if BYTE_ORDER == LITTLE_ENDIAN - e->encoding = AUDIO_ENCODING_SLINEAR_LE; -#else - e->encoding = AUDIO_ENCODING_SLINEAR_BE; -#endif - if (p->precision == 8) { - e->sw_code = (mode == AUMODE_PLAY) ? - slinear8_to_mulaw : mulaw_to_slinear8; - break; - } else if (p->precision == 24) { - e->sw_code = (mode == AUMODE_PLAY) ? - slinear24_to_mulaw24 : mulaw24_to_slinear24; - break; - } - /* FALLTHROUGH */ - default: - e->encoding = p->encoding; - e->sw_code = NULL; + error = ENXIO; } -} - -int -au_set_lr_value(struct audio_softc *sc, mixer_ctrl_t *ct, int l, int r) -{ - ct->type = AUDIO_MIXER_VALUE; - ct->un.value.num_channels = 2; - ct->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; - ct->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; - if (sc->hw_if->set_port(sc->hw_hdl, ct) == 0) - return 0; - ct->un.value.num_channels = 1; - ct->un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; - return sc->hw_if->set_port(sc->hw_hdl, ct); -} - -int -au_get_mute(struct audio_softc *sc, struct au_mixer_ports *ports, u_char *mute) -{ - mixer_devinfo_t mi; - mixer_ctrl_t ct; - int error; - - *mute = 0; - - /* if no master, silently ignore request */ - if (ports->master == -1) - return 0; - - mi.index = ports->master; - error = sc->hw_if->query_devinfo(sc->hw_hdl, &mi); - if (error != 0) - return error; - - /* master mute control should be the next device, if it exists */ - if (mi.next < 0) - return 0; - - ct.dev = mi.next; - ct.type = AUDIO_MIXER_ENUM; - error = sc->hw_if->get_port(sc->hw_hdl, &ct); - if (error != 0) - return error; - - *mute = ct.un.ord; - + device_unref(&sc->dev); return error; } int -au_set_mute(struct audio_softc *sc, struct au_mixer_ports *ports, u_char mute) +audiowrite(dev_t dev, struct uio *uio, int ioflag) { - mixer_devinfo_t mi; - mixer_ctrl_t ct; + struct audio_softc *sc; int error; - /* if no master, silently ignore request */ - if (ports->master == -1) - return 0; - - mi.index = ports->master; - error = sc->hw_if->query_devinfo(sc->hw_hdl, &mi); - if (error != 0) - return error; - - /* master mute control should be the next device, if it exists */ - if (mi.next < 0) - return 0; - - ct.dev = mi.next; - ct.type = AUDIO_MIXER_ENUM; - error = sc->hw_if->get_port(sc->hw_hdl, &ct); - if (error != 0) - return error; - - DPRINTF(("au_set_mute: mute (old): %d, mute (new): %d\n", - ct.un.ord, mute)); - - ct.un.ord = (mute != 0 ? 1 : 0); - error = sc->hw_if->set_port(sc->hw_hdl, &ct); - - if (!error) - mixer_signal(sc); - return error; -} - -int -au_set_gain(struct audio_softc *sc, struct au_mixer_ports *ports, int gain, - int balance) -{ - mixer_ctrl_t ct; - int i, error; - int l, r; - u_int mask; - int nset; - - /* XXX silently adjust to within limits or return EINVAL ? */ - if (gain > AUDIO_MAX_GAIN) - gain = AUDIO_MAX_GAIN; - else if (gain < AUDIO_MIN_GAIN) - gain = AUDIO_MIN_GAIN; - - if (balance == AUDIO_MID_BALANCE) { - l = r = gain; - } else if (balance < AUDIO_MID_BALANCE) { - r = gain; - l = (balance * gain) / AUDIO_MID_BALANCE; - } else { - l = gain; - r = ((AUDIO_RIGHT_BALANCE - balance) * gain) - / AUDIO_MID_BALANCE; - } - DPRINTF(("au_set_gain: gain=%d balance=%d, l=%d r=%d\n", - gain, balance, l, r)); - - if (ports->index == -1) { - usemaster: - if (ports->master == -1) - return 0; /* just ignore it silently */ - ct.dev = ports->master; - error = au_set_lr_value(sc, &ct, l, r); - } else { - ct.dev = ports->index; - if (ports->isenum) { - ct.type = AUDIO_MIXER_ENUM; - error = sc->hw_if->get_port(sc->hw_hdl, &ct); - if (error) - return error; - for(i = 0; i < ports->nports; i++) { - if (ports->misel[i] == ct.un.ord) { - ct.dev = ports->miport[i]; - if (ct.dev == -1 || - au_set_lr_value(sc, &ct, l, r)) - goto usemaster; - else - break; - } - } - } else { - ct.type = AUDIO_MIXER_SET; - error = sc->hw_if->get_port(sc->hw_hdl, &ct); - if (error) - return error; - mask = ct.un.mask; - nset = 0; - for(i = 0; i < ports->nports; i++) { - if (ports->misel[i] & mask) { - ct.dev = ports->miport[i]; - if (ct.dev != -1 && - au_set_lr_value(sc, &ct, l, r) == 0) - nset++; - } - } - if (nset == 0) - goto usemaster; - } + sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev)); + if (sc == NULL) + return ENXIO; + switch (AUDIO_DEV(dev)) { + case AUDIO_DEV_SOUND: + case AUDIO_DEV_AUDIO: + error = audio_write(sc, uio, ioflag); + break; + case AUDIO_DEV_AUDIOCTL: + case AUDIO_DEV_MIXER: + error = ENODEV; + break; + default: + error = ENXIO; } - if (!error) - mixer_signal(sc); + device_unref(&sc->dev); return error; } int -au_get_lr_value(struct audio_softc *sc, mixer_ctrl_t *ct, int *l, int *r) +audioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { + struct audio_softc *sc; int error; - ct->un.value.num_channels = 2; - if (sc->hw_if->get_port(sc->hw_hdl, ct) == 0) { - *l = ct->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; - *r = ct->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; - } else { - ct->un.value.num_channels = 1; - error = sc->hw_if->get_port(sc->hw_hdl, ct); - if (error) - return error; - *r = *l = ct->un.value.level[AUDIO_MIXER_LEVEL_MONO]; - } - return 0; -} - -void -au_get_gain(struct audio_softc *sc, struct au_mixer_ports *ports, u_int *pgain, - u_char *pbalance) -{ - mixer_ctrl_t ct; - int i, l, r, n; - int lgain = AUDIO_MAX_GAIN/2, rgain = AUDIO_MAX_GAIN/2; - - if (ports->index == -1) { - usemaster: - if (ports->master == -1) - goto bad; - ct.dev = ports->master; - ct.type = AUDIO_MIXER_VALUE; - if (au_get_lr_value(sc, &ct, &lgain, &rgain)) - goto bad; - } else { - ct.dev = ports->index; - if (ports->isenum) { - ct.type = AUDIO_MIXER_ENUM; - if (sc->hw_if->get_port(sc->hw_hdl, &ct)) - goto bad; - ct.type = AUDIO_MIXER_VALUE; - for(i = 0; i < ports->nports; i++) { - if (ports->misel[i] == ct.un.ord) { - ct.dev = ports->miport[i]; - if (ct.dev == -1 || - au_get_lr_value(sc, &ct, - &lgain, &rgain)) - goto usemaster; - else - break; - } - } - } else { - ct.type = AUDIO_MIXER_SET; - if (sc->hw_if->get_port(sc->hw_hdl, &ct)) - goto bad; - ct.type = AUDIO_MIXER_VALUE; - lgain = rgain = n = 0; - for(i = 0; i < ports->nports; i++) { - if (ports->misel[i] & ct.un.mask) { - ct.dev = ports->miport[i]; - if (ct.dev == -1 || - au_get_lr_value(sc, &ct, &l, &r)) - goto usemaster; - else { - lgain += l; - rgain += r; - n++; - } - } - } - if (n != 0) { - lgain /= n; - rgain /= n; - } + sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev)); + if (sc == NULL) + return ENXIO; + switch (AUDIO_DEV(dev)) { + case AUDIO_DEV_SOUND: + case AUDIO_DEV_AUDIO: + error = audio_ioctl(sc, cmd, addr); + break; + case AUDIO_DEV_AUDIOCTL: + if (cmd == AUDIO_SETINFO && sc->mode != 0) { + error = EBUSY; + break; } + error = audio_ioctl(sc, cmd, addr); + break; + case AUDIO_DEV_MIXER: + error = audio_ioctl_mixer(sc, cmd, addr); + break; + default: + error = ENXIO; } -bad: - if (lgain == rgain) { /* handles lgain==rgain==0 */ - *pgain = lgain; - *pbalance = AUDIO_MID_BALANCE; - } else if (lgain < rgain) { - *pgain = rgain; - *pbalance = (AUDIO_MID_BALANCE * lgain) / rgain; - } else /* lgain > rgain */ { - *pgain = lgain; - *pbalance = AUDIO_RIGHT_BALANCE - - (AUDIO_MID_BALANCE * rgain) / lgain; - } + device_unref(&sc->dev); + return error; } int -au_set_port(struct audio_softc *sc, struct au_mixer_ports *ports, u_int port) +audiopoll(dev_t dev, int events, struct proc *p) { - mixer_ctrl_t ct; - int i, error; - - if (port == 0) /* allow this special case */ - return 0; + struct audio_softc *sc; + int revents; - if (ports->index == -1) - return EINVAL; - ct.dev = ports->index; - if (ports->isenum) { - if (port & (port-1)) - return EINVAL; /* Only one port allowed */ - ct.type = AUDIO_MIXER_ENUM; - error = EINVAL; - for(i = 0; i < ports->nports; i++) - if (ports->aumask[i] == port) { - ct.un.ord = ports->misel[i]; - error = sc->hw_if->set_port(sc->hw_hdl, &ct); - break; - } - } else { - ct.type = AUDIO_MIXER_SET; - ct.un.mask = 0; - for(i = 0; i < ports->nports; i++) - if (ports->aumask[i] & port) - ct.un.mask |= ports->misel[i]; - if (port != 0 && ct.un.mask == 0) - error = EINVAL; - else - error = sc->hw_if->set_port(sc->hw_hdl, &ct); + sc = (struct audio_softc *)device_lookup(&audio_cd, AUDIO_UNIT(dev)); + if (sc == NULL) + return POLLERR; + switch (AUDIO_DEV(dev)) { + case AUDIO_DEV_SOUND: + case AUDIO_DEV_AUDIO: + revents = audio_poll(sc, events, p); + break; + case AUDIO_DEV_AUDIOCTL: + case AUDIO_DEV_MIXER: + revents = 0; + break; + default: + revents = 0; } - if (!error) - mixer_signal(sc); - return error; + device_unref(&sc->dev); + return revents; } +#if NWSKBD > 0 int -au_get_port(struct audio_softc *sc, struct au_mixer_ports *ports) +wskbd_initmute(struct audio_softc *sc, struct mixer_devinfo *vol) { - mixer_ctrl_t ct; - int i, aumask; + struct mixer_devinfo mi; - if (ports->index == -1) - return 0; - ct.dev = ports->index; - ct.type = ports->isenum ? AUDIO_MIXER_ENUM : AUDIO_MIXER_SET; - if (sc->hw_if->get_port(sc->hw_hdl, &ct)) - return 0; - aumask = 0; - if (ports->isenum) { - for(i = 0; i < ports->nports; i++) - if (ct.un.ord == ports->misel[i]) - aumask = ports->aumask[i]; - } else { - for(i = 0; i < ports->nports; i++) - if (ct.un.mask & ports->misel[i]) - aumask |= ports->aumask[i]; + mi.index = vol->next; + for (mi.index = vol->next; mi.index != -1; mi.index = mi.next) { + if (sc->ops->query_devinfo(sc->arg, &mi) != 0) + break; + if (strcmp(mi.label.name, AudioNmute) == 0) + return mi.index; } - return aumask; + return -1; } int -audiosetinfo(struct audio_softc *sc, struct audio_info *ai) +wskbd_initvol(struct audio_softc *sc, struct wskbd_vol *vol, char *cn, char *dn) { - struct audio_prinfo *r = &ai->record, *p = &ai->play; - int cleared; - int setmode, modechange = 0; - int error; - struct audio_hw_if *hw = sc->hw_if; - struct audio_params pp, rp; - int np, nr; - unsigned int blks; - int oldpblksize, oldrblksize; - int rbus, pbus; - int fpb; - int fs; - u_int gain; - u_char balance; - - 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->bps != ~0) { - pp.bps = p->bps; - np++; - } - if (r->bps != ~0) { - rp.bps = r->bps; - nr++; - } - if (p->msb != ~0) { - pp.msb = p->msb; - np++; - } - if (r->msb != ~0) { - rp.msb = r->msb; - 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); - modechange = cleared = 1; - setmode |= AUMODE_RECORD; - } - if (np) { - if (!cleared) - audio_clear(sc); - modechange = cleared = 1; - setmode |= AUMODE_PLAY; - } - - if (ai->mode != ~0) { - if (!cleared) - audio_clear(sc); - modechange = 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 (modechange) { - int indep = hw->get_props(sc->hw_hdl) & AUDIO_PROP_INDEPENDENT; - if (!indep) { - if (setmode == AUMODE_RECORD) - pp = rp; - else - rp = pp; - } - sc->sc_pemu.encoding = pp.encoding; - sc->sc_remu.encoding = rp.encoding; - error = hw->set_params(sc->hw_hdl, setmode, - sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp); - if (error) - return (error); - if (sc->sc_mode & AUMODE_PLAY) - audio_emu_setup(AUMODE_PLAY, &pp, &sc->sc_pemu); - if (sc->sc_mode & AUMODE_RECORD) - audio_emu_setup(AUMODE_RECORD, &rp, &sc->sc_remu); - if (!indep) { - if (setmode == AUMODE_RECORD) { - pp.sample_rate = rp.sample_rate; - pp.encoding = rp.encoding; - pp.channels = rp.channels; - pp.precision = rp.precision; - pp.bps = rp.bps; - pp.msb = rp.msb; - } else if (setmode == AUMODE_PLAY) { - rp.sample_rate = pp.sample_rate; - rp.encoding = pp.encoding; - rp.channels = pp.channels; - rp.precision = pp.precision; - rp.bps = pp.bps; - rp.msb = pp.msb; - } - } - sc->sc_rparams = rp; - sc->sc_pparams = pp; - } - - oldpblksize = sc->sc_pr.blksize; - oldrblksize = sc->sc_rr.blksize; - - /* - * allow old-style blocksize changes, for compatibility; - * individual play/record block sizes have precedence - */ - if (ai->blocksize != ~0) { - if (r->block_size == ~0) - r->block_size = ai->blocksize; - if (p->block_size == ~0) - p->block_size = ai->blocksize; - } - if (r->block_size != ~0) { - sc->sc_rr.blkset = 0; - if (!cleared) - audio_clear(sc); - cleared = 1; - nr++; - } - if (p->block_size != ~0) { - sc->sc_pr.blkset = 0; - if (!cleared) - audio_clear(sc); - cleared = 1; - np++; - } - if (nr) { - if (r->block_size == ~0 || r->block_size == 0) { - fpb = rp.sample_rate * audio_blk_ms / 1000; - } else { - fs = rp.channels * rp.bps; - fpb = r->block_size / fs; - } - if (sc->sc_rr.blkset == 0) - audio_set_blksize(sc, AUMODE_RECORD, fpb); - } - if (np) { - if (p->block_size == ~0 || p->block_size == 0) { - fpb = pp.sample_rate * audio_blk_ms / 1000; - } else { - fs = pp.channels * pp.bps; - fpb = p->block_size / fs; - } - if (sc->sc_pr.blkset == 0) - audio_set_blksize(sc, AUMODE_PLAY, fpb); - } - if (r->block_size != ~0 && r->block_size != 0) - sc->sc_rr.blkset = 1; - if (p->block_size != ~0 && p->block_size != 0) - sc->sc_pr.blkset = 1; - -#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 = au_set_port(sc, &sc->sc_outports, p->port); - if (error) - return(error); - } - if (r->port != ~0) { - if (!cleared) - audio_clear(sc); - cleared = 1; + struct mixer_devinfo dev, cls; - error = au_set_port(sc, &sc->sc_inports, r->port); - if (error) - return(error); - } - if (p->gain != ~0) { - au_get_gain(sc, &sc->sc_outports, &gain, &balance); - error = au_set_gain(sc, &sc->sc_outports, p->gain, balance); - if (error) - return(error); - } - if ((r->gain != ~0) && (r->port != 0)) { - au_get_gain(sc, &sc->sc_inports, &gain, &balance); - error = au_set_gain(sc, &sc->sc_inports, r->gain, balance); - if (error) - return(error); - } - - if (p->balance != (u_char)~0) { - au_get_gain(sc, &sc->sc_outports, &gain, &balance); - error = au_set_gain(sc, &sc->sc_outports, gain, p->balance); - if (error) - return(error); - } - if ((r->balance != (u_char)~0) && (r->port != 0)) { - au_get_gain(sc, &sc->sc_inports, &gain, &balance); - error = au_set_gain(sc, &sc->sc_inports, gain, r->balance); - if (error) - return(error); - } - - if (ai->output_muted != (u_char)~0) { - error = au_set_mute(sc, &sc->sc_outports, ai->output_muted); - if (error) - return(error); - } - - if (ai->monitor_gain != ~0 && - sc->sc_monitor_port != -1) { - mixer_ctrl_t ct; - - ct.dev = sc->sc_monitor_port; - ct.type = AUDIO_MIXER_VALUE; - ct.un.value.num_channels = 1; - ct.un.value.level[AUDIO_MIXER_LEVEL_MONO] = ai->monitor_gain; - error = sc->hw_if->set_port(sc->hw_hdl, &ct); - if (error) - return(error); - } - - 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 (cleared) { - 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 && !sc->sc_pr.pause) - error = audiostartp(sc); - if (!error && - (sc->sc_mode & AUMODE_RECORD) && - rbus && !sc->sc_rbus && !sc->sc_rr.pause) - error = audiostartr(sc); - err: - 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 < 2) - blks = 2; - 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; - } - if (ai->hiwat != ~0 || ai->lowat != ~0) { - if (sc->sc_pr.usedlow > sc->sc_pr.usedhigh - sc->sc_pr.blksize) - sc->sc_pr.usedlow = sc->sc_pr.usedhigh - sc->sc_pr.blksize; - } - - if (p->pause != (u_char)~0) { - sc->sc_pr.pause = p->pause; - if (!p->pause && !sc->sc_pbus && (sc->sc_mode & AUMODE_PLAY)) { - error = audiostartp(sc); - if (error) - return error; - } - } - if (r->pause != (u_char)~0) { - sc->sc_rr.pause = r->pause; - if (!r->pause && !sc->sc_rbus && (sc->sc_mode & AUMODE_RECORD)) { - error = audiostartr(sc); - if (error) - return error; + for (dev.index = 0; ; dev.index++) { + if (sc->ops->query_devinfo(sc->arg, &dev) != 0) + break; + cls.index = dev.mixer_class; + if (sc->ops->query_devinfo(sc->arg, &cls) != 0) + continue; + if (strcmp(cls.label.name, cn) == 0 && + strcmp(dev.label.name, dn) == 0) { + vol->val = dev.index; + vol->nch = dev.un.v.num_channels; + vol->step = dev.un.v.delta > 8 ? dev.un.v.delta : 8; + vol->mute = wskbd_initmute(sc, &dev); + vol->val_pending = vol->mute_pending = 0; + DPRINTF("%s: wskbd using %s.%s, %s\n", + DEVNAME(sc), cn, dn, vol->mute >= -1 ? "mute control" : ""); + return 1; } } - - return (0); + vol->val = vol->mute = -1; + return 0; } -int -audiogetinfo(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; - - 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->bps = sc->sc_pparams.bps; - r->bps = sc->sc_rparams.bps; - p->msb = sc->sc_pparams.msb; - r->msb = sc->sc_rparams.msb; - p->encoding = sc->sc_pemu.encoding; - r->encoding = sc->sc_remu.encoding; - r->port = au_get_port(sc, &sc->sc_inports); - p->port = au_get_port(sc, &sc->sc_outports); - - r->avail_ports = sc->sc_inports.allports; - p->avail_ports = sc->sc_outports.allports; - - au_get_gain(sc, &sc->sc_inports, &r->gain, &r->balance); - au_get_gain(sc, &sc->sc_outports, &p->gain, &p->balance); - - if (sc->sc_monitor_port != -1) { - mixer_ctrl_t ct; - - ct.dev = sc->sc_monitor_port; - ct.type = AUDIO_MIXER_VALUE; - ct.un.value.num_channels = 1; - if (sc->hw_if->get_port(sc->hw_hdl, &ct)) - ai->monitor_gain = 0; - else - ai->monitor_gain = - ct.un.value.level[AUDIO_MIXER_LEVEL_MONO]; - } else - ai->monitor_gain = 0; - - au_get_mute(sc, &sc->sc_outports, &ai->output_muted); - - 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; - - p->buffer_size = sc->sc_pr.bufsize; - r->buffer_size = sc->sc_rr.bufsize; +void +wskbd_mixer_init(struct audio_softc *sc) +{ + static struct { + char *cn, *dn; + } spkr_names[] = { + {AudioCoutputs, AudioNmaster}, + {AudioCinputs, AudioNdac}, + {AudioCoutputs, AudioNdac}, + {AudioCoutputs, AudioNoutput} + }, mic_names[] = { + {AudioCrecord, AudioNrecord}, + {AudioCrecord, AudioNvolume}, + {AudioCinputs, AudioNrecord}, + {AudioCinputs, AudioNvolume}, + {AudioCinputs, AudioNinput} + }; + int i; - r->block_size = sc->sc_rr.blksize; - p->block_size = sc->sc_pr.blksize; - if (p->block_size != 0) { - ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize; - ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize; - } else { - ai->hiwat = ai->lowat = 0; + if (sc->dev.dv_unit != 0) { + DPRINTF("%s: not configuring wskbd keys\n", DEVNAME(sc)); + return; } - ai->blocksize = p->block_size; /* for compatibility, remove this */ - ai->mode = sc->sc_mode; - - return (0); -} - -int -audiogetbufinfo(struct audio_softc *sc, struct audio_bufinfo *info, int mode) -{ - struct audio_ringbuffer *buf; - - if (mode == AUMODE_PLAY) { - buf = &sc->sc_pr; - } else { - buf = &sc->sc_rr; + for (i = 0; i < sizeof(spkr_names) / sizeof(spkr_names[0]); i++) { + if (wskbd_initvol(sc, &sc->spkr, + spkr_names[i].cn, spkr_names[i].dn)) + break; } - - info->seek = buf->used; - info->blksize = buf->blksize; - if (buf->blksize != 0) { - info->hiwat = buf->usedhigh / buf->blksize; - info->lowat = buf->usedlow / buf->blksize; - } else { - info->hiwat = 0; - info->lowat = 0; + for (i = 0; i < sizeof(mic_names) / sizeof(mic_names[0]); i++) { + if (wskbd_initvol(sc, &sc->mic, + mic_names[i].cn, mic_names[i].dn)) + break; } - - return (0); } - -/* - * Mixer driver - */ -int -mixer_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, - struct proc *p) +void +wskbd_mixer_update(struct audio_softc *sc, struct wskbd_vol *vol) { - DPRINTF(("mixer_open: dev=0x%x flags=0x%x sc=%p\n", dev, flags, sc)); - - return (0); -} + struct mixer_ctrl ctrl; + int val_pending, mute_pending, i, gain, error, s; -/* - * Remove a process from those to be signalled on mixer activity. - */ -static void -mixer_remove(struct audio_softc *sc, struct proc *p) -{ - struct mixer_asyncs **pm, *m; + s = spltty(); + val_pending = vol->val_pending; + vol->val_pending = 0; + mute_pending = vol->mute_pending; + vol->mute_pending = 0; + splx(s); - for(pm = &sc->sc_async_mixer; *pm; pm = &(*pm)->next) { - if ((*pm)->proc == p) { - m = *pm; - *pm = m->next; - free(m, M_DEVBUF, 0); + if (sc->ops == NULL) + return; + if (vol->mute >= 0 && mute_pending) { + ctrl.dev = vol->mute; + ctrl.type = AUDIO_MIXER_ENUM; + error = sc->ops->get_port(sc->arg, &ctrl); + if (error) { + DPRINTF("%s: get mute err = %d\n", DEVNAME(sc), error); return; } - } -} - -/* - * Signal all processes waiting for the mixer. - */ -static void -mixer_signal(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_t dev, int flags, int ifmt, struct proc *p) -{ - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - - DPRINTF(("mixer_close: unit %d\n", AUDIOUNIT(dev))); - - mixer_remove(sc, p); - - return (0); -} - -int -mixer_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) -{ - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct audio_hw_if *hw = sc->hw_if; - int error = EINVAL; - - DPRINTF(("mixer_ioctl(%ld,'%c',%ld)\n", - IOCPARM_LEN(cmd), (int)IOCGROUP(cmd), cmd & 0xff)); - - /* Block when fully quiesced. No need to block earlier. */ - while (sc->sc_quiesce == AUDIO_QUIESCE_SILENT) - tsleep(&sc->sc_quiesce, 0, "aud_qmi", 0); - - 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; + ctrl.un.ord = ctrl.un.ord ^ mute_pending; + DPRINTFN(1, "%s: wskbd mute setting to %d\n", + DEVNAME(sc), ctrl.un.ord); + error = sc->ops->set_port(sc->arg, &ctrl); + if (error) { + DPRINTF("%s: set mute err = %d\n", DEVNAME(sc), error); + return; } - 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")); - ((mixer_devinfo_t *)addr)->un.v.delta = 0; /* default */ - 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: - if (!(flag & FWRITE)) - return (EACCES); - 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 = ENOTTY; - break; } - DPRINTF(("mixer_ioctl(%ld,'%c',%ld) result %d\n", - IOCPARM_LEN(cmd), (int)IOCGROUP(cmd), cmd & 0xff, error)); - return (error); -} - -int -audiokqfilter(dev_t dev, struct knote *kn) -{ - int unit = AUDIOUNIT(dev); - struct audio_softc *sc = audio_cd.cd_devs[unit]; - struct klist *klist; - - switch (kn->kn_filter) { - case EVFILT_READ: - klist = &sc->sc_rsel.si_note; - kn->kn_fop = &audioread_filtops; - break; - case EVFILT_WRITE: - klist = &sc->sc_wsel.si_note; - kn->kn_fop = &audiowrite_filtops; - break; - default: - return (EINVAL); + if (vol->val >= 0 && val_pending) { + ctrl.dev = vol->val; + ctrl.type = AUDIO_MIXER_VALUE; + ctrl.un.value.num_channels = vol->nch; + error = sc->ops->get_port(sc->arg, &ctrl); + if (error) { + DPRINTF("%s: get mute err = %d\n", DEVNAME(sc), error); + return; + } + for (i = 0; i < vol->nch; i++) { + gain = ctrl.un.value.level[i] + vol->step * val_pending; + if (gain > AUDIO_MAX_GAIN) + gain = AUDIO_MAX_GAIN; + if (gain < AUDIO_MIN_GAIN) + gain = AUDIO_MIN_GAIN; + ctrl.un.value.level[i] = gain; + DPRINTFN(1, "%s: wskbd level %d set to %d\n", + DEVNAME(sc), i, gain); + } + error = sc->ops->set_port(sc->arg, &ctrl); + if (error) { + DPRINTF("%s: set vol err = %d\n", DEVNAME(sc), error); + return; + } } - kn->kn_hook = (void *)sc; - - mtx_enter(&audio_lock); - SLIST_INSERT_HEAD(klist, kn, kn_selnext); - mtx_leave(&audio_lock); - - return (0); } void -filt_audiordetach(struct knote *kn) +wskbd_mixer_cb(void *addr) { - struct audio_softc *sc = (struct audio_softc *)kn->kn_hook; + struct audio_softc *sc = addr; + int s; - mtx_enter(&audio_lock); - SLIST_REMOVE(&sc->sc_rsel.si_note, kn, knote, kn_selnext); - mtx_leave(&audio_lock); -} - -int -filt_audioread(struct knote *kn, long hint) -{ - struct audio_softc *sc = (struct audio_softc *)kn->kn_hook; - - return AUDIO_FILTREAD(sc); -} - -void -filt_audiowdetach(struct knote *kn) -{ - struct audio_softc *sc = (struct audio_softc *)kn->kn_hook; - - mtx_enter(&audio_lock); - SLIST_REMOVE(&sc->sc_wsel.si_note, kn, knote, kn_selnext); - mtx_leave(&audio_lock); -} - -int -filt_audiowrite(struct knote *kn, long hint) -{ - struct audio_softc *sc = (struct audio_softc *)kn->kn_hook; - - return AUDIO_FILTWRITE(sc); + wskbd_mixer_update(sc, &sc->spkr); + wskbd_mixer_update(sc, &sc->mic); + s = spltty(); + sc->wskbd_taskset = 0; + splx(s); + device_unref(&sc->dev); } -#if NWSKBD > 0 -struct wskbd_vol_change { - struct task t; - long dir; - long out; -}; - int wskbd_set_mixervolume(long dir, long out) { struct audio_softc *sc; - struct wskbd_vol_change *ch; - - if (audio_cd.cd_ndevs == 0 || (sc = audio_cd.cd_devs[0]) == NULL) { - DPRINTF(("wskbd_set_mixervolume: audio_cd\n")); - return (ENXIO); - } - - ch = malloc(sizeof(*ch), M_TEMP, M_NOWAIT); - if (ch == NULL) - return (ENOMEM); - - task_set(&ch->t, wskbd_set_mixervolume_callback, ch); - ch->dir = dir; - ch->out = out; - task_add(systq, &ch->t); - - return (0); -} - -void -wskbd_set_mixervolume_callback(void *xch) -{ - struct wskbd_vol_change *ch = xch; - struct audio_softc *sc; - struct au_mixer_ports *ports; - mixer_devinfo_t mi; - u_char balance, mute; - long dir, out; - u_int gain; - int error; - - dir = ch->dir; - out = ch->out; - free(ch, M_TEMP, sizeof(*ch)); + struct wskbd_vol *vol; sc = (struct audio_softc *)device_lookup(&audio_cd, 0); if (sc == NULL) - return; - - ports = out ? &sc->sc_outports : &sc->sc_inports; - - if (ports->master == -1) { - DPRINTF(("%s: master == -1\n", __func__)); - goto done; - } - - if (dir == 0) { - /* Mute */ - - error = au_get_mute(sc, ports, &mute); - if (error != 0) { - DPRINTF(("%s: au_get_mute: %d\n", __func__, error)); - goto done; - } - - mute = !mute; - - error = au_set_mute(sc, ports, mute); - if (error != 0) { - DPRINTF(("%s: au_set_mute: %d\n", __func__, error)); - goto done; - } - } else { - /* Raise or lower volume */ - - mi.index = ports->master; - error = sc->hw_if->query_devinfo(sc->hw_hdl, &mi); - if (error != 0) { - DPRINTF(("%s: query_devinfo: %d\n", __func__, error)); - goto done; - } - - au_get_gain(sc, ports, &gain, &balance); - - if (dir > 0) - gain += mi.un.v.delta; - else - gain -= mi.un.v.delta; - - error = au_set_gain(sc, ports, gain, balance); - if (error != 0) { - DPRINTF(("%s: au_set_gain: %d\n", __func__, error)); - goto done; - } - - /* - * Unmute whenever we raise or lower the volume. This - * mimicks the behaviour of the hardware volume - * buttons on Thinkpads making sure that our software - * mute state follows the hardware mute state. - */ - error = au_set_mute(sc, ports, 0); - if (error != 0) { - DPRINTF(("%s: au_set_mute: %d\n", __func__, error)); - goto done; - } + return ENODEV; + vol = out ? &sc->spkr : &sc->mic; + if (dir == 0) + vol->mute_pending ^= 1; + else + vol->val_pending += dir; + if (!sc->wskbd_taskset) { + task_set(&sc->wskbd_task, wskbd_mixer_cb, sc); + task_add(systq, &sc->wskbd_task); + sc->wskbd_taskset = 1; } - -done: - device_unref(&sc->dev); + return 0; } #endif /* NWSKBD > 0 */ diff --git a/sys/dev/audio_if.h b/sys/dev/audio_if.h index 7c2500ce9d5..788ef0b5eba 100644 --- a/sys/dev/audio_if.h +++ b/sys/dev/audio_if.h @@ -1,4 +1,4 @@ -/* $OpenBSD: audio_if.h,v 1.29 2015/05/11 06:46:21 ratchov Exp $ */ +/* $OpenBSD: audio_if.h,v 1.30 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: audio_if.h,v 1.24 1998/01/10 14:07:25 tv Exp $ */ /* @@ -46,7 +46,6 @@ * Generic interface to hardware driver. */ -struct audio_softc; struct audio_device; struct audio_encoding; struct mixer_devinfo; @@ -61,9 +60,6 @@ struct audio_params { u_int channels; /* mono(1), stereo(2) */ }; -/* The default audio mode: 8 kHz mono mu-law */ -extern struct audio_params audio_default; - struct audio_hw_if { int (*open)(void *, int); /* open hardware */ void (*close)(void *); /* close hardware */ @@ -148,27 +144,6 @@ struct audio_attach_args { struct device *audio_attach_mi(struct audio_hw_if *, void *, struct device *); int audioprint(void *, const char *); -/* 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((x)) == SOUND_DEVICE) -#define ISDEVAUDIO(x) (AUDIODEV((x)) == AUDIO_DEVICE) -#define ISDEVAUDIOCTL(x) (AUDIODEV((x)) == AUDIOCTL_DEVICE) -#define ISDEVMIXER(x) (AUDIODEV((x)) == MIXER_DEVICE) - -/* - * USB Audio specification defines 12 channels: - * L R C LFE Ls Rs Lc Rc S Sl Sr T - */ -#define AUDIO_MAX_CHANNELS 12 - extern struct mutex audio_lock; #endif /* _SYS_DEV_AUDIO_IF_H_ */ - diff --git a/sys/dev/isa/ad1848.c b/sys/dev/isa/ad1848.c index ad7fdc01ab7..a1e985c205c 100644 --- a/sys/dev/isa/ad1848.c +++ b/sys/dev/isa/ad1848.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ad1848.c,v 1.43 2015/05/11 06:52:35 ratchov Exp $ */ +/* $OpenBSD: ad1848.c,v 1.44 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: ad1848.c,v 1.45 1998/01/30 02:02:38 augustss Exp $ */ /* @@ -140,6 +140,9 @@ static int ad1848_init_values[] = { 0 /* lower record count */ }; +static struct audio_params ad1848_audio_default = + {48000, AUDIO_ENCODING_SLINEAR_LE, 16, 2, 1, 2}; + void ad1848_reset(struct ad1848_softc *); int ad1848_set_speed(struct ad1848_softc *, u_long *); void ad1848_mute_monitor(void *, int); @@ -542,8 +545,8 @@ ad1848_attach(struct ad1848_softc *sc) } ad1848_reset(sc); - pparams = audio_default; - rparams = audio_default; + pparams = ad1848_audio_default; + rparams = ad1848_audio_default; (void) ad1848_set_params(sc, AUMODE_RECORD|AUMODE_PLAY, 0, &pparams, &rparams); diff --git a/sys/dev/isa/ess.c b/sys/dev/isa/ess.c index 22b322e907c..1e42e6bc9b3 100644 --- a/sys/dev/isa/ess.c +++ b/sys/dev/isa/ess.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ess.c,v 1.21 2015/05/11 06:46:21 ratchov Exp $ */ +/* $OpenBSD: ess.c,v 1.22 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: ess.c,v 1.44.4.1 1999/06/21 01:18:00 thorpej Exp $ */ /* @@ -110,6 +110,9 @@ struct cfdriver ess_cd = { NULL, "ess", DV_DULL }; +struct audio_params ess_audio_default = + {44100, AUDIO_ENCODING_SLINEAR_LE, 16, 2, 1, 2}; + int ess_setup_sc(struct ess_softc *, int); int ess_open(void *, int); @@ -929,8 +932,8 @@ essattach(struct ess_softc *sc) * Set record and play parameters to default values defined in * generic audio driver. */ - pparams = audio_default; - rparams = audio_default; + pparams = ess_audio_default; + rparams = ess_audio_default; ess_set_params(sc, AUMODE_RECORD|AUMODE_PLAY, 0, &pparams, &rparams); /* Do a hardware reset on the mixer. */ diff --git a/sys/dev/isa/sbdsp.c b/sys/dev/isa/sbdsp.c index 9d4ef0a393e..8b5146816d4 100644 --- a/sys/dev/isa/sbdsp.c +++ b/sys/dev/isa/sbdsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sbdsp.c,v 1.35 2015/05/11 06:46:21 ratchov Exp $ */ +/* $OpenBSD: sbdsp.c,v 1.36 2015/06/25 06:43:46 ratchov Exp $ */ /* * Copyright (c) 1991-1993 Regents of the University of California. @@ -170,6 +170,9 @@ static struct sbmode sbrmodes[] = { { -1 } }; +static struct audio_params sbdsp_audio_default = + {44100, AUDIO_ENCODING_SLINEAR_LE, 16, 2, 1, 2}; + void sbversion(struct sbdsp_softc *); void sbdsp_jazz16_probe(struct sbdsp_softc *); void sbdsp_set_mixer_gain(struct sbdsp_softc *sc, int port); @@ -348,8 +351,8 @@ sbdsp_attach(sc) } } - pparams = audio_default; - rparams = audio_default; + pparams = sbdsp_audio_default; + rparams = sbdsp_audio_default; sbdsp_set_params(sc, AUMODE_RECORD|AUMODE_PLAY, 0, &pparams, &rparams); sbdsp_set_in_ports(sc, 1 << SB_MIC_VOL); diff --git a/sys/dev/mulaw.c b/sys/dev/mulaw.c index e34d0028f08..9a86fccf85e 100644 --- a/sys/dev/mulaw.c +++ b/sys/dev/mulaw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mulaw.c,v 1.17 2015/06/04 06:03:11 jsg Exp $ */ +/* $OpenBSD: mulaw.c,v 1.18 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: mulaw.c,v 1.15 2001/01/18 20:28:20 jdolecek Exp $ */ /* @@ -146,7 +146,7 @@ static const u_char lintomulaw[256] = { }; void -mulaw_to_slinear8(void *v, u_char *p, int cc) +mulaw_to_slinear8(u_char *p, int cc) { /* Use the 16 bit table for 8 bits too. */ while (--cc >= 0) { @@ -156,7 +156,7 @@ mulaw_to_slinear8(void *v, u_char *p, int cc) } void -slinear8_to_mulaw(void *v, u_char *p, int cc) +slinear8_to_mulaw(u_char *p, int cc) { while (--cc >= 0) { *p = lintomulaw[*p ^ 0x80]; @@ -165,7 +165,7 @@ slinear8_to_mulaw(void *v, u_char *p, int cc) } void -mulaw24_to_slinear24(void *v, u_char *p, int cc) +mulaw24_to_slinear24(u_char *p, int cc) { int s, *q = (int *)p; @@ -178,7 +178,7 @@ mulaw24_to_slinear24(void *v, u_char *p, int cc) } void -slinear24_to_mulaw24(void *v, u_char *p, int cc) +slinear24_to_mulaw24(u_char *p, int cc) { u_int *q = (u_int *)p; diff --git a/sys/dev/mulaw.h b/sys/dev/mulaw.h index 3626e6a451f..8723f7a1b4c 100644 --- a/sys/dev/mulaw.h +++ b/sys/dev/mulaw.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mulaw.h,v 1.15 2015/05/11 06:46:21 ratchov Exp $ */ +/* $OpenBSD: mulaw.h,v 1.16 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: mulaw.h,v 1.11 1999/11/01 18:12:19 augustss Exp $ */ /*- @@ -30,11 +30,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* *_mts versions convert mono to stereo, in addition */ - /* Convert 8-bit mu-law to/from 8 bit signed linear. */ -extern void mulaw_to_slinear8(void *, u_char *, int); -extern void slinear8_to_mulaw(void *, u_char *, int); +void mulaw_to_slinear8(u_char *, int); +void slinear8_to_mulaw(u_char *, int); /* Convert 24-bit mu-law to/from 24 bit signed linear. */ -void mulaw24_to_slinear24(void *, u_char *, int); -void slinear24_to_mulaw24(void *, u_char *, int); +void mulaw24_to_slinear24(u_char *, int); +void slinear24_to_mulaw24(u_char *, int); diff --git a/sys/dev/pci/envy.c b/sys/dev/pci/envy.c index acf77cea608..a71b7c3f5ee 100644 --- a/sys/dev/pci/envy.c +++ b/sys/dev/pci/envy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envy.c,v 1.59 2015/03/14 03:38:48 jsg Exp $ */ +/* $OpenBSD: envy.c,v 1.60 2015/06/25 06:43:46 ratchov Exp $ */ /* * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> * @@ -1881,27 +1881,7 @@ envy_set_params(void *self, int setmode, int usemode, int envy_round_blocksize(void *self, int blksz) { - struct envy_softc *sc = (struct envy_softc *)self; - int mul, pmult, rmult; - - /* - * XXX: audio(4) layer doesn't round to the sample size - * until it's fixed, roll our own rounding - */ - - pmult = (sc->isht ? sc->card->noch : ENVY_PCHANS); - if (pmult == 0) - pmult = 1; - rmult = (sc->isht ? sc->card->nich : ENVY_RCHANS); - if (rmult == 0) - rmult = 1; - mul = pmult * rmult; - while ((mul & 0x1f) != 0) - mul <<= 1; - blksz -= blksz % mul; - if (blksz == 0) - blksz = mul; - return blksz; + return (blksz + 0x1f) & ~0x1f; } size_t diff --git a/sys/sys/audioio.h b/sys/sys/audioio.h index 1e72c131317..afbdd7a7a19 100644 --- a/sys/sys/audioio.h +++ b/sys/sys/audioio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: audioio.h,v 1.21 2010/07/15 03:43:12 jakemsr Exp $ */ +/* $OpenBSD: audioio.h,v 1.22 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: audioio.h,v 1.24 1998/08/13 06:28:41 mrg Exp $ */ /* @@ -48,21 +48,14 @@ struct audio_prinfo { u_int bps; /* number of bytes/sample */ u_int msb; /* data alignment */ u_int encoding; /* data encoding (AUDIO_ENCODING_* below) */ - u_int gain; /* volume level */ - u_int port; /* selected I/O port */ - u_int seek; /* BSD extension */ - u_int avail_ports; /* available I/O ports */ + u_int ispare2[4]; u_int buffer_size; /* total size audio buffer */ u_int block_size; /* size a block */ /* Current state of device: */ u_int samples; /* number of samples */ - u_int eof; /* End Of File (zero-size writes) counter */ + u_int ispare[1]; u_char pause; /* non-zero if paused, zero to resume */ - u_char error; /* non-zero if underflow/overflow occurred */ - u_char waiting; /* non-zero if another process hangs in open */ - u_char balance; /* stereo channel balance */ - u_char cspare[2]; - u_char open; /* non-zero if currently open */ + u_char cspare2[6]; u_char active; /* non-zero if I/O is currently active */ }; typedef struct audio_prinfo audio_prinfo_t; @@ -70,32 +63,19 @@ 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 monitor_gain; /* input to output mix */ - /* BSD extensions */ - u_int blocksize; /* H/W read/write block size */ + u_int ispare[2]; /* H/W read/write block size */ u_int hiwat; /* output high water mark */ u_int lowat; /* output low water mark */ - u_char output_muted; /* toggle play mute */ - u_char cspare[3]; + u_char cspare[4]; 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; #define AUDIO_INITINFO(p) \ (void)memset((void *)(p), 0xff, sizeof(struct audio_info)) -struct audio_bufinfo { - u_int blksize; /* block size */ - u_int hiwat; /* high water mark */ - u_int lowat; /* low water mark */ - u_int seek; /* current position */ -}; -typedef struct audio_bufinfo audio_bufinfo_t; - /* * Parameter for the AUDIO_GETDEV ioctl to determine current * audio devices. @@ -109,8 +89,7 @@ typedef struct audio_device { 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 */ + u_int unused[2]; } audio_offset_t; /* @@ -120,10 +99,6 @@ typedef struct audio_offset { #define AUDIO_ENCODING_NONE 0 /* no encoding assigned */ #define AUDIO_ENCODING_ULAW 1 /* ITU G.711 mu-law */ #define AUDIO_ENCODING_ALAW 2 /* ITU G.711 A-law */ -#define AUDIO_ENCODING_PCM16 3 /* signed linear PCM, obsolete */ -#define AUDIO_ENCODING_LINEAR AUDIO_ENCODING_PCM16 /* SunOS compat */ -#define AUDIO_ENCODING_PCM8 4 /* unsigned linear PCM, obsolete */ -#define AUDIO_ENCODING_LINEAR8 AUDIO_ENCODING_PCM8 /* SunOS compat */ #define AUDIO_ENCODING_ADPCM 5 /* adaptive differential PCM */ #define AUDIO_ENCODING_SLINEAR_LE 6 #define AUDIO_ENCODING_SLINEAR_BE 7 @@ -131,15 +106,6 @@ typedef struct audio_offset { #define AUDIO_ENCODING_ULINEAR_BE 9 #define AUDIO_ENCODING_SLINEAR 10 #define AUDIO_ENCODING_ULINEAR 11 -#define AUDIO_ENCODING_MPEG_L1_STREAM 12 -#define AUDIO_ENCODING_MPEG_L1_PACKETS 13 -#define AUDIO_ENCODING_MPEG_L1_SYSTEM 14 -#define AUDIO_ENCODING_MPEG_L2_STREAM 15 -#define AUDIO_ENCODING_MPEG_L2_PACKETS 16 -#define AUDIO_ENCODING_MPEG_L2_SYSTEM 17 -#define AUDIO_ENCODING_MPEG_L3_STREAM 18 -#define AUDIO_ENCODING_MPEG_L3_PACKETS 19 -#define AUDIO_ENCODING_MPEG_L3_SYSTEM 20 typedef struct audio_encoding { int index; @@ -161,28 +127,11 @@ typedef struct audio_encoding { #define AUDIO_BALANCE_SHIFT 3 /* - * Output ports - */ -#define AUDIO_SPEAKER 0x01 /* built-in speaker */ -#define AUDIO_HEADPHONE 0x02 /* headphone jack */ -#define AUDIO_LINE_OUT 0x04 /* line out */ - -/* - * Input ports - */ -#define AUDIO_MICROPHONE 0x01 /* microphone */ -#define AUDIO_LINE_IN 0x02 /* line in */ -#define AUDIO_CD 0x04 /* on-board CD inputs */ -#define AUDIO_INTERNAL_CD_IN AUDIO_CD /* internal CDROM */ - -/* * 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) @@ -195,8 +144,6 @@ typedef struct audio_encoding { #define AUDIO_PROP_FULLDUPLEX 0x01 #define AUDIO_PROP_MMAP 0x02 #define AUDIO_PROP_INDEPENDENT 0x04 -#define AUDIO_GETPRINFO _IOR('A', 35, struct audio_bufinfo) -#define AUDIO_GETRRINFO _IOR('A', 36, struct audio_bufinfo) /* * Mixer device diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 2fd92e20979..fddc4bbd1d2 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.h,v 1.132 2015/05/17 16:55:51 deraadt Exp $ */ +/* $OpenBSD: conf.h,v 1.133 2015/06/25 06:43:46 ratchov Exp $ */ /* $NetBSD: conf.h,v 1.33 1996/05/03 20:03:32 christos Exp $ */ /*- @@ -324,7 +324,7 @@ extern struct cdevsw cdevsw[]; dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \ dev_init(c,n,write), dev_init(c,n,ioctl), \ (dev_type_stop((*))) enodev, 0, dev_init(c,n,poll), \ - dev_init(c,n,mmap), 0, 0, dev_init(c,n,kqfilter) } + (dev_type_mmap((*))) enodev } /* open, close, read, write, ioctl, poll, kqfilter */ #define cdev_midi_init(c,n) { \ |