Index: src/sys/miscfs/specfs/spec_vnops.c =================================================================== RCS file: /cvsroot/src/sys/miscfs/specfs/spec_vnops.c,v retrieving revision 1.162 diff -u -r1.162 spec_vnops.c --- src/sys/miscfs/specfs/spec_vnops.c 4 Apr 2016 08:03:53 -0000 1.162 +++ src/sys/miscfs/specfs/spec_vnops.c 15 Apr 2016 13:01:13 -0000 @@ -1228,7 +1228,7 @@ sd->sd_bdevvp = NULL; mutex_exit(&device_lock); - if (count != 0) + if (count != 0 && (vp->v_type != VCHR || cdev_type(dev) != D_MCLOSE)) return 0; /* Index: src/sys/sys/conf.h =================================================================== RCS file: /cvsroot/src/sys/sys/conf.h,v retrieving revision 1.146 diff -u -r1.146 conf.h --- src/sys/sys/conf.h 17 Jan 2016 23:16:46 -0000 1.146 +++ src/sys/sys/conf.h 15 Apr 2016 13:01:45 -0000 @@ -60,6 +60,7 @@ #define D_TAPE 0x0001 #define D_DISK 0x0002 #define D_TTY 0x0003 +#define D_MCLOSE 0x0004 #define D_TYPEMASK 0x00ff #define D_MPSAFE 0x0100 #define D_NEGOFFSAFE 0x0200 Index: src/sys/dev/audiobell.c =================================================================== RCS file: /cvsroot/src/sys/dev/audiobell.c,v retrieving revision 1.8 diff -u -r1.8 audiobell.c --- src/sys/dev/audiobell.c 12 May 2009 10:22:31 -0000 1.8 +++ src/sys/dev/audiobell.c 25 May 2016 12:14:24 -0000 @@ -1,5 +1,6 @@ /* $NetBSD: audiobell.c,v 1.8 2009/05/12 10:22:31 cegger Exp $ */ + /* * Copyright (c) 1999 Richard Earnshaw * Copyright (c) 2004 Ben Harris @@ -37,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +48,7 @@ #include extern dev_type_open(audioopen); +extern dev_type_ioctl(audioioctl); extern dev_type_write(audiowrite); extern dev_type_close(audioclose); @@ -138,8 +141,10 @@ { device_t audio = arg; uint8_t *buf; + struct audio_info ai; struct uio auio; struct iovec aiov; + int size, len, offset; /* The audio system isn't built for polling. */ if (poll) return; @@ -148,21 +153,32 @@ if (audioopen(AUDIO_DEVICE | device_unit(audio), FWRITE, 0, NULL) != 0) return; - buf = malloc(period * 8, M_TEMP, M_WAITOK); - if (buf == NULL) goto out; - if (audiobell_synthesize(buf, pitch, period, volume) != 0) goto out; - - aiov.iov_base = (void *)buf; - aiov.iov_len = period * 8; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_offset = 0; - auio.uio_resid = period * 8; - auio.uio_rw = UIO_WRITE; - UIO_SETUP_SYSSPACE(&auio); + if (audioioctl((dev_t)(AUDIO_DEVICE | device_unit(audio)), + AUDIO_GETINFO, &ai, 0, NULL) != 0) + return; - audiowrite(AUDIO_DEVICE | device_unit(audio), &auio, 0); + buf = malloc(ai.blocksize, M_TEMP, M_WAITOK); + if (buf == NULL) goto out; + len = period * 8; + offset = 0; + while (len > 0) { + size = min(len, ai.blocksize); + if (audiobell_synthesize(buf, pitch, size / 8, volume) != 0) + goto out; + aiov.iov_base = (void *)buf; + aiov.iov_len = size; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + auio.uio_resid = size; + auio.uio_rw = UIO_WRITE; + UIO_SETUP_SYSSPACE(&auio); + + audiowrite(AUDIO_DEVICE | device_unit(audio), &auio, 0); + len -= size; + offset += size; + } out: if (buf != NULL) free(buf, M_TEMP); audioclose(AUDIO_DEVICE | device_unit(audio), FWRITE, 0, NULL); Index: src/sys/dev/files.audio =================================================================== RCS file: /cvsroot/src/sys/dev/files.audio,v retrieving revision 1.3 diff -u -r1.3 files.audio --- src/sys/dev/files.audio 18 Nov 2014 01:53:17 -0000 1.3 +++ src/sys/dev/files.audio 25 May 2016 12:14:53 -0000 @@ -13,10 +13,12 @@ # audio and midi devices, attaches to audio hardware driver # -device audio: audiodev +device audio {}: audiodev attach audio at audiobus device midi: audio attach midi at midibus +device spkr: audiobell +attach spkr at audio # console bell via audio device # @@ -31,3 +33,4 @@ file dev/midictl.c midisyn file dev/midisyn.c midisyn file dev/mulaw.c mulaw needs-flag +file dev/isa/spkr.c spkr Index: src/sys/conf/majors =================================================================== RCS file: /cvsroot/src/sys/conf/majors,v retrieving revision 1.73 diff -u -r1.73 majors --- src/sys/conf/majors 13 May 2016 07:41:47 -0000 1.73 +++ src/sys/conf/majors 25 May 2016 12:15:21 -0000 @@ -9,6 +9,7 @@ # # Majors 160-511 are used for the MI drivers. +device-major spkr char 156 spkr device-major crypto char 160 crypto single device-major pf char 161 pf single #obsolete vinum char 162 vinum Index: src/sys/arch/amd64/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/amd64/conf/GENERIC,v retrieving revision 1.433 diff -u -r1.433 GENERIC --- src/sys/arch/amd64/conf/GENERIC 14 May 2016 17:11:30 -0000 1.433 +++ src/sys/arch/amd64/conf/GENERIC 25 May 2016 12:16:40 -0000 @@ -1099,6 +1103,12 @@ # Audio support audio* at audiobus? +options VAUDIO +#vaudio* at audio? + +# The spkr driver provides a simple tone interface to the built in speaker. +options VAUDIOSPEAKER +spkr0 at audio0 # PC speaker # MPU 401 UARTs #mpu* at isa? port 0x330 irq 9 # MPU401 or compatible card @@ -1110,10 +1120,6 @@ midi* at midibus? midi* at pcppi? # MIDI interface to the PC speaker -# The spkr driver provides a simple tone interface to the built in speaker. -#spkr0 at pcppi? # PC speaker - - # FM-Radio devices # PCI radio devices #gtp* at pci? dev ? function ? # Guillemot Maxi Radio FM 2000 Radio Card Index: src/sys/dev/isa/spkr.c =================================================================== RCS file: /cvsroot/src/sys/dev/isa/spkr.c,v retrieving revision 1.36 diff -u -r1.36 spkr.c --- src/sys/dev/isa/spkr.c 17 May 2015 05:20:37 -0000 1.36 +++ src/sys/dev/isa/spkr.c 21 Apr 2016 12:20:39 -0000 @@ -56,34 +56,51 @@ #include #include #include +#include +#include +#include + +struct vbell_args { + device_t *cookie; + u_int pitch; + u_int period; + u_int volume; + bool dying; +}; -#include +void bell_thread(void *); -#include +#include +#include #include int spkrprobe(device_t, cfdata_t, void *); void spkrattach(device_t, device_t, void *); int spkrdetach(device_t, int); +device_t speakerattach_mi(device_t); #include "ioconf.h" -MODULE(MODULE_CLASS_DRIVER, spkr, NULL /* "pcppi" */); +MODULE(MODULE_CLASS_DRIVER, spkr, NULL /* "audio" */); #ifdef _MODULE #include "ioconf.c" #endif -CFATTACH_DECL_NEW(spkr, 0, - spkrprobe, spkrattach, spkrdetach, NULL); +CFATTACH_DECL3_NEW(spkr, 0, + spkrprobe, spkrattach, spkrdetach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN); dev_type_open(spkropen); dev_type_close(spkrclose); dev_type_write(spkrwrite); dev_type_ioctl(spkrioctl); +struct spkr_attach_args { + device_t dev; +}; + const struct cdevsw spkr_cdevsw = { .d_open = spkropen, .d_close = spkrclose, @@ -99,7 +116,12 @@ .d_flag = D_OTHER }; -static pcppi_tag_t ppicookie; +device_t vacookie; +device_t *vaudiocookie; +struct vbell_args sc_bell_args; +lwp_t *sc_bellthread; +kmutex_t sc_bellock; +kcondvar_t sc_bellcv; #define SPKRPRI (PZERO - 1) @@ -113,7 +135,7 @@ tone(u_int xhz, u_int ticks) /* emit tone of frequency hz for given number of ticks */ { - pcppi_bell(ppicookie, xhz, ticks, PCPPI_BELL_SLEEP); + audiobell(vaudiocookie, xhz, ticks * (1000 / hz), 80, 0); } static void @@ -129,7 +151,7 @@ printf("rest: %d\n", ticks); #endif /* SPKRDEBUG */ if (ticks > 0) - tsleep(rest, SPKRPRI | PCATCH, "rest", ticks); + audiobell(vaudiocookie, 0, ticks * (1000 / hz), 80, 0); } /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** @@ -424,14 +446,30 @@ return (!spkr_attached); } +device_t +speakerattach_mi(device_t dev) +{ + struct spkr_attach_args sa; + sa.dev = dev; + return config_found(dev, &sa, NULL); +} + void spkrattach(device_t parent, device_t self, void *aux) { + struct spkr_attach_args *sa; + sa = aux; + printf("\n"); - ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie; + vacookie = sa->dev; + vaudiocookie = &vacookie; spkr_attached = 1; if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); + mutex_init(&sc_bellock, MUTEX_DEFAULT, IPL_SCHED); + cv_init(&sc_bellcv, "bellcv"); + kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, bell_thread, &sc_bell_args, + &sc_bellthread, "vbell"); } int @@ -439,8 +477,19 @@ { pmf_device_deregister(self); + + mutex_enter(&sc_bellock); + sc_bell_args.dying = true; + + cv_broadcast(&sc_bellcv); + mutex_exit(&sc_bellock); + + kthread_join(sc_bellthread); + cv_destroy(&sc_bellcv); + mutex_destroy(&sc_bellock); + spkr_attached = 0; - ppicookie = NULL; + vaudiocookie = NULL; return 0; } @@ -546,6 +595,44 @@ return(0); } +void +bell_thread(void *arg) +{ + struct vbell_args *vb = arg; + u_int bpitch; + u_int bperiod; + u_int bvolume; + + for (;;) { + mutex_enter(&sc_bellock); + cv_wait_sig(&sc_bellcv, &sc_bellock); + + if (vb->dying == true) { + mutex_exit(&sc_bellock); + kthread_exit(0); + } + + bpitch = vb->pitch; + bperiod = vb->period; + bvolume = vb->volume; + mutex_exit(&sc_bellock); + audiobell(vaudiocookie, bpitch, bperiod, bvolume, 0); + } +} + +void +speaker_play(u_int pitch, u_int period, u_int volume) +{ + mutex_enter(&sc_bellock); + sc_bell_args.dying = false; + sc_bell_args.pitch = pitch; + sc_bell_args.period = period; + sc_bell_args.volume = volume; + + cv_broadcast(&sc_bellcv); + mutex_exit(&sc_bellock); +} + static int spkr_modcmd(modcmd_t cmd, void *arg) { Index: src/sys/dev/wscons/wskbd.c =================================================================== RCS file: /cvsroot/src/sys/dev/wscons/wskbd.c,v retrieving revision 1.136 diff -u -r1.136 wskbd.c --- src/sys/dev/wscons/wskbd.c 24 Aug 2015 22:50:33 -0000 1.136 +++ src/sys/dev/wscons/wskbd.c 21 Apr 2016 12:21:30 -0000 @@ -156,6 +156,10 @@ #include +#ifdef VAUDIO +#include +#endif + struct wskbd_internal { const struct wskbd_mapdata *t_keymap; @@ -1089,16 +1093,27 @@ case WSKBDIO_BELL: if ((flag & FWRITE) == 0) return (EACCES); +#ifndef VAUDIO return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, WSKBDIO_COMPLEXBELL, (void *)&sc->sc_bell_data, flag, l)); +#else + wskbd_cnbell(0, sc->sc_bell_data.pitch, sc->sc_bell_data.period, + sc->sc_bell_data.volume); + return 0; +#endif case WSKBDIO_COMPLEXBELL: if ((flag & FWRITE) == 0) return (EACCES); ubdp = (struct wskbd_bell_data *)data; SETBELL(ubdp, ubdp, &sc->sc_bell_data); +#ifndef VAUDIO return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, WSKBDIO_COMPLEXBELL, (void *)ubdp, flag, l)); +#else + wskbd_cnbell(0, ubdp->pitch, ubdp->period, ubdp->volume); + return 0; +#endif case WSKBDIO_SETBELL: if ((flag & FWRITE) == 0) @@ -1466,10 +1481,14 @@ if (!wskbd_console_initted) return; +#ifndef VAUDIO if (wskbd_console_data.t_consops->bell != NULL) (*wskbd_console_data.t_consops->bell) (wskbd_console_data.t_consaccesscookie, pitch, period, volume); +#else + speaker_play(pitch, period, volume); +#endif } Index: src/sys/arch/amd64/conf/majors.amd64 =================================================================== RCS file: /cvsroot/src/sys/arch/amd64/conf/majors.amd64,v retrieving revision 1.25 diff -u -r1.25 majors.amd64 --- src/sys/arch/amd64/conf/majors.amd64 23 Apr 2015 23:22:51 -0000 1.25 +++ src/sys/arch/amd64/conf/majors.amd64 21 Apr 2016 12:24:11 -0000 @@ -29,8 +29,6 @@ device-major bpf char 23 bpfilter device-major md char 24 block 17 md -device-major spkr char 27 spkr - device-major cy char 38 cy device-major mcd char 39 block 7 mcd device-major tun char 40 tun Index: src/sys/dev/isa/files.isa =================================================================== RCS file: /cvsroot/src/sys/dev/isa/files.isa,v retrieving revision 1.163 diff -u -r1.163 files.isa --- src/sys/dev/isa/files.isa 10 Jun 2013 07:14:02 -0000 1.163 +++ src/sys/dev/isa/files.isa 25 May 2016 12:26:30 -0000 @@ -432,9 +432,6 @@ device pcppi {} attach pcppi at isa file dev/isa/pcppi.c pcppi needs-flag -device spkr -attach spkr at pcppi -file dev/isa/spkr.c spkr needs-flag attach midi at pcppi with midi_pcppi: midisyn file dev/isa/midi_pcppi.c midi_pcppi Index: audiovar.h =================================================================== RCS file: /cvsroot/src/sys/dev/audiovar.h,v retrieving revision 1.46 diff -u -r1.46 audiovar.h --- src/sys/dev/audiovar.h 23 Nov 2011 23:07:31 -0000 1.46 +++ src/sys/dev/audiovar.h 3 Jun 2016 22:46:23 -0000 @@ -67,6 +67,7 @@ #define _SYS_DEV_AUDIOVAR_H_ #include +#include #include @@ -101,6 +102,35 @@ bool mmapped; /* device is mmap()-ed */ }; +#ifndef VAUDIOCHANS +#define VAUDIOCHANS 4096 +#endif + +struct virtual_channel { + u_char sc_open; /* multiple use device */ + u_char sc_mode; /* bitmask for RECORD/PLAY */ + + bool sc_blkset; /* Blocksize has been set */ + + uint8_t *sc_sil_start; /* start of silence in buffer */ + int sc_sil_count; /* # of silence bytes */ + bool sc_pbus; /* output DMA in progress */ + audio_params_t sc_pparams; /* play encoding parameters */ + audio_stream_t *sc_pustream; /* the first buffer */ + int sc_npfilters; /* number of filters */ + audio_stream_t sc_pstreams[AUDIO_MAX_FILTERS]; + stream_filter_t *sc_pfilters[AUDIO_MAX_FILTERS]; + struct audio_ringbuffer sc_mpr; /* Play ring to mix*/ + u_long sc_wstamp; /* # of bytes read with read(2) */ + u_long sc_playdrop; + + int sc_full_duplex; /* device in full duplex mode */ + + struct audio_info sc_lastinfo; + bool sc_lastinfovalid; + uint8_t sc_swvol; +}; + #define AUDIO_N_PORTS 4 struct au_mixer_ports { @@ -126,16 +156,17 @@ void *hw_hdl; /* Hardware driver handle */ const struct audio_hw_if *hw_if; /* Hardware interface */ device_t sc_dev; /* Hardware device struct */ - u_char sc_open; /* single use device */ + pid_t sc_audiopid[VAUDIOCHANS]; /* audio caller */ #define AUOPEN_READ 0x01 #define AUOPEN_WRITE 0x02 - u_char sc_mode; /* bitmask for RECORD/PLAY */ + struct audio_encoding_set *sc_encodings; struct selinfo sc_wsel; /* write selector */ struct selinfo sc_rsel; /* read selector */ pid_t sc_async_audio; /* process who wants audio SIGIO */ void *sc_sih_rd; void *sc_sih_wr; + struct virtual_channel *sc_vchan[VAUDIOCHANS]; struct mixer_asyncs { struct mixer_asyncs *next; pid_t pid; @@ -147,16 +178,12 @@ kcondvar_t sc_rchan; kcondvar_t sc_wchan; kcondvar_t sc_lchan; - int sc_dvlock; + bool sc_trigger_started; + bool sc_writeme; + int sc_opens; bool sc_dying; - bool sc_blkset; /* Blocksize has been set */ - - uint8_t *sc_sil_start; /* start of silence in buffer */ - int sc_sil_count; /* # of silence bytes */ - bool sc_rbus; /* input DMA in progress */ - bool sc_pbus; /* output DMA in progress */ /** * userland @@ -168,15 +195,19 @@ * sc_pstreams[n-1] * | sc_pfilters[n-1] * sc_pr + * (vchans mixed into sc_pr) + * + * play_thread + * sc_pr * | + * vchan[0]->sc_pustream + * | + * vchan[0]->sc_mpr + * | * hardware */ - audio_params_t sc_pparams; /* play encoding parameters */ - audio_stream_t *sc_pustream; /* the first buffer */ - int sc_npfilters; /* number of filters */ - audio_stream_t sc_pstreams[AUDIO_MAX_FILTERS]; - stream_filter_t *sc_pfilters[AUDIO_MAX_FILTERS]; - struct audio_ringbuffer sc_pr; /* Play ring */ + + struct audio_ringbuffer sc_pr; /* Play ring to mix into */ /** * hardware @@ -200,11 +231,6 @@ audio_params_t sc_rparams; /* record encoding parameters */ int sc_eof; /* EOF, i.e. zero sized write, counter */ - u_long sc_wstamp; /* # of bytes read with read(2) */ - 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; @@ -220,11 +246,16 @@ #endif u_int sc_lastgain; - struct audio_info sc_lastinfo; - bool sc_lastinfovalid; mixer_ctrl_t *sc_mixer_state; int sc_nmixer_states; + int sc_static_nmixer_states; + + bool schedule_wih; + bool schedule_rih; + + lwp_t *sc_playthread; + kcondvar_t sc_condvar; }; #endif /* _SYS_DEV_AUDIOVAR_H_ */ Index: audio.c =================================================================== RCS file: /cvsroot/src/sys/dev/audio.c,v retrieving revision 1.267 diff -u -r1.267 audio.c --- src/sys/dev/audio.c 23 Apr 2016 10:15:31 -0000 1.267 +++ src/sys/dev/audio.c 3 Jun 2016 22:46:47 -0000 @@ -1,6 +1,32 @@ /* $NetBSD: audio.c,v 1.267 2016/04/23 10:15:31 skrll Exp $ */ /*- + * Copyright (c) 2016 Nathanial Sloss + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- * Copyright (c) 2008 The NetBSD Foundation, Inc. * All rights reserved. * @@ -109,16 +135,6 @@ * sc_intr_lock. This is to ensure that groups of hardware operations are * made atomically. SLEEPS CANNOT OCCUR WITH THIS LOCK HELD. * - * - sc_dvlock, private to this module. This is a custom reader/writer lock - * built on sc_lock and a condition variable. Some operations release - * sc_lock in order to allocate memory, to wait for in-flight I/O to - * complete, to copy to/from user context, etc. sc_dvlock serializes - * changes to filters and audio device settings while a read/write to the - * hardware is in progress. A write lock is taken only under exceptional - * circumstances, for example when opening /dev/audio or changing audio - * parameters. Long term sleeps and copy to/from user space may be done - * with this lock held. - * * List of hardware interface methods, and which locks are held when each * is called by this module: * @@ -160,6 +176,7 @@ #include "audio.h" #if NAUDIO > 0 +#include #include #include #include @@ -177,10 +194,17 @@ #include #include #include +#include #include #include #include +#include +#include + +#ifdef VAUDIOSPEAKER +#include +#endif #include @@ -198,6 +222,12 @@ #define SPECIFIED(x) (x != ~0) #define SPECIFIED_CH(x) (x != (u_char)~0) +#define VAUDIO_NFORMATS 1 +static const struct audio_format vaudio_formats[VAUDIO_NFORMATS] = { + { NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, + 2, AUFMT_STEREO, 1, { 44100 } }, +}; + /* #define AUDIO_PM_IDLE */ #ifdef AUDIO_PM_IDLE int audio_idle_timeout = 30; @@ -205,11 +235,11 @@ int audio_blk_ms = AUDIO_BLK_MS; -int audiosetinfo(struct audio_softc *, struct audio_info *); -int audiogetinfo(struct audio_softc *, struct audio_info *, int); +int audiosetinfo(struct audio_softc *, struct audio_info *, bool, int); +int audiogetinfo(struct audio_softc *, struct audio_info *, int, int); int audio_open(dev_t, struct audio_softc *, int, int, struct lwp *); -int audio_close(struct audio_softc *, int, int, struct lwp *); +int audio_close(struct audio_softc *, int, int, struct lwp *, int); int audio_read(struct audio_softc *, struct uio *, int); int audio_write(struct audio_softc *, struct uio *, int); int audio_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *); @@ -223,33 +253,37 @@ static void mixer_remove(struct audio_softc *); static void mixer_signal(struct audio_softc *); -void audio_init_record(struct audio_softc *); -void audio_init_play(struct audio_softc *); +void audio_init_record(struct audio_softc *, int); +void audio_init_play(struct audio_softc *, int); int audiostartr(struct audio_softc *); -int audiostartp(struct audio_softc *); +int audiostartp(struct audio_softc *, int); void audio_rint(void *); void audio_pint(void *); +void audio_mix(void *); +void audio_play_thread(void *); +void mix_func(struct audio_softc *, struct audio_ringbuffer *, int); +void mix_write(void *); int audio_check_params(struct audio_params *); -void audio_calc_blksize(struct audio_softc *, int); +void audio_calc_blksize(struct audio_softc *, int, int); void audio_fill_silence(struct audio_params *, uint8_t *, int); int audio_silence_copyout(struct audio_softc *, int, struct uio *); void audio_init_ringbuffer(struct audio_softc *, struct audio_ringbuffer *, int); -int audio_initbufs(struct audio_softc *); -void audio_calcwater(struct audio_softc *); -int audio_drain(struct audio_softc *); -void audio_clear(struct audio_softc *); -void audio_clear_intr_unlocked(struct audio_softc *sc); +int audio_initbufs(struct audio_softc *, int); +void audio_calcwater(struct audio_softc *, int); +int audio_drain(struct audio_softc *, int); +void audio_clear(struct audio_softc *, int); +void audio_clear_intr_unlocked(struct audio_softc *sc, int); static inline void audio_pint_silence - (struct audio_softc *, struct audio_ringbuffer *, uint8_t *, int); + (struct audio_softc *, struct audio_ringbuffer *, uint8_t *, int, int); int audio_alloc_ring (struct audio_softc *, struct audio_ringbuffer *, int, size_t); void audio_free_ring(struct audio_softc *, struct audio_ringbuffer *); static int audio_setup_pfilters(struct audio_softc *, const audio_params_t *, - stream_filter_list_t *); + stream_filter_list_t *, int); static int audio_setup_rfilters(struct audio_softc *, const audio_params_t *, stream_filter_list_t *); static void audio_stream_dtor(audio_stream_t *); @@ -263,7 +297,7 @@ static void stream_filter_list_set (stream_filter_list_t *, int, stream_filter_factory_t, const audio_params_t *); -int audio_set_defaults(struct audio_softc *, u_int); +int audio_set_defaults(struct audio_softc *, u_int, int); int audioprobe(device_t, cfdata_t, void *); void audioattach(device_t, device_t, void *); @@ -295,6 +329,8 @@ static void audio_exit(struct audio_softc *); static int audio_waitio(struct audio_softc *, kcondvar_t *); +#define AUDIO_OUTPUT_CLASS 0 + struct portname { const char *name; int mask; @@ -320,6 +356,20 @@ int au_set_port(struct audio_softc *, struct au_mixer_ports *, u_int); int au_get_port(struct audio_softc *, struct au_mixer_ports *); +static int + audio_get_port(struct audio_softc *, mixer_ctrl_t *); +static int + audio_set_port(struct audio_softc *, mixer_ctrl_t *); +static int + audio_query_devinfo(struct audio_softc *, mixer_devinfo_t *); +static int +audio_set_params(struct audio_softc *, int, int, audio_params_t *, + audio_params_t *, stream_filter_list_t *, stream_filter_list_t *, int); +static int +audio_query_encoding(struct audio_softc *, struct audio_encoding *); +static int +audio_set_vchan_defaults(struct audio_softc *, u_int, + const struct audio_format *, int); int au_get_lr_value(struct audio_softc *, mixer_ctrl_t *, int *, int *); int au_set_lr_value(struct audio_softc *, mixer_ctrl_t *, @@ -360,7 +410,16 @@ .d_mmap = audiommap, .d_kqfilter = audiokqfilter, .d_discard = nodiscard, - .d_flag = D_OTHER | D_MPSAFE + .d_flag = D_MCLOSE | D_MPSAFE +}; + +/* The default vchan mode: 48 kHz stereo signed linear */ +struct audio_params vchan_default = { + .sample_rate = 44100, + .encoding = AUDIO_ENCODING_SLINEAR_LE, + .precision = 16, + .validbits = 16, + .channels = 2, }; /* The default audio mode: 8 kHz mono mu-law */ @@ -394,6 +453,7 @@ { struct audio_softc *sc; struct audio_attach_args *sa; + struct virtual_channel *vc; const struct audio_hw_if *hwp; void *hdlp; int error; @@ -407,10 +467,30 @@ sa = aux; hwp = sa->hwif; hdlp = sa->hdl; + sc->sc_opens = 0; + + sc->sc_trigger_started = false; + sc->sc_vchan[0] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP); + vc = sc->sc_vchan[0]; + memset(sc->sc_audiopid, -1, sizeof(sc->sc_audiopid)); + vc->sc_open = 0; + vc->sc_mode = 0; + vc->sc_npfilters = 0; + memset(vc->sc_pfilters, 0, + sizeof(vc->sc_pfilters)); + vc->sc_lastinfovalid = false; + vc->sc_swvol = 255; + + if (auconv_create_encodings(vaudio_formats, VAUDIO_NFORMATS, + &sc->sc_encodings) != 0) { + aprint_error_dev(self, "couldn't create encodings\n"); + return; + } cv_init(&sc->sc_rchan, "audiord"); cv_init(&sc->sc_wchan, "audiowr"); cv_init(&sc->sc_lchan, "audiolk"); + cv_init(&sc->sc_condvar,"play"); if (hwp == 0 || hwp->get_locks == 0) { printf(": missing method\n"); @@ -440,7 +520,6 @@ sc->hw_if = hwp; sc->hw_hdl = hdlp; sc->sc_dev = parent; - sc->sc_lastinfovalid = false; mutex_enter(sc->sc_lock); props = audio_get_props(sc); @@ -466,11 +545,12 @@ mutex_enter(sc->sc_lock); can_playback = audio_can_playback(sc); can_capture = audio_can_capture(sc); - mutex_exit(sc->sc_lock); if (can_playback) { error = audio_alloc_ring(sc, &sc->sc_pr, - AUMODE_PLAY, AU_RING_SIZE); + AUMODE_PLAY, AU_RING_SIZE); + error = audio_alloc_ring(sc, &sc->sc_vchan[0]->sc_mpr, + AUMODE_PLAY, AU_RING_SIZE); if (error) { sc->hw_if = NULL; aprint_error("audio: could not allocate play buffer\n"); @@ -483,6 +563,8 @@ if (error) { if (sc->sc_pr.s.start != 0) audio_free_ring(sc, &sc->sc_pr); + if (sc->sc_vchan[0]->sc_mpr.s.start != 0) + audio_free_ring(sc, &sc->sc_vchan[0]->sc_mpr); sc->hw_if = NULL; aprint_error("audio: could not allocate record buffer\n"); return; @@ -491,8 +573,8 @@ sc->sc_lastgain = 128; - mutex_enter(sc->sc_lock); - error = audio_set_defaults(sc, 0); + audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL | AUMODE_RECORD, + &vaudio_formats[0], 0); mutex_exit(sc->sc_lock); if (error != 0) { aprint_error("audioattach: audio_set_defaults() failed\n"); @@ -500,6 +582,7 @@ return; } + sc->sc_pr.blksize = sc->sc_vchan[0]->sc_mpr.blksize; sc->sc_sih_rd = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE, audio_softintr_rd, sc); sc->sc_sih_wr = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE, @@ -530,7 +613,7 @@ */ mutex_enter(sc->sc_lock); for(mi.index = 0; ; mi.index++) { - if (hwp->query_devinfo(hdlp, &mi) != 0) + if (audio_query_devinfo(sc, &mi) != 0) break; /* * The type of AUDIO_MIXER_CLASS merely introduces a class. @@ -551,8 +634,9 @@ /* Allocate save area. Ensure non-zero allocation. */ sc->sc_nmixer_states = mi.index; - sc->sc_mixer_state = kmem_alloc(sizeof(mixer_ctrl_t) * - sc->sc_nmixer_states + 1, KM_SLEEP); + sc->sc_static_nmixer_states = mi.index; + sc->sc_mixer_state = kmem_zalloc(sizeof(mixer_ctrl_t) * + (sc->sc_nmixer_states + VAUDIOCHANS + 1), KM_SLEEP); /* * This is where we assign each control in the "audio" model, to the @@ -563,7 +647,7 @@ record_source_found = 0; mutex_enter(sc->sc_lock); for(mi.index = 0; ; mi.index++) { - if (hwp->query_devinfo(hdlp, &mi) != 0) + if (audio_query_devinfo(sc, &mi) != 0) break; KASSERT(mi.index < sc->sc_nmixer_states); if (mi.type == AUDIO_MIXER_CLASS) @@ -670,6 +754,11 @@ #ifdef AUDIO_PM_IDLE callout_schedule(&sc->sc_idle_counter, audio_idle_timeout * hz); #endif + kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, + audio_play_thread, sc, &sc->sc_playthread, "audiomix"); +#ifdef VAUDIOSPEAKER + speakerattach_mi(self); +#endif } int @@ -692,16 +781,23 @@ audiodetach(device_t self, int flags) { struct audio_softc *sc; - int maj, mn, i; + int maj, mn, i, n, rc; sc = device_private(self); DPRINTF(("audio_detach: sc=%p flags=%d\n", sc, flags)); /* Start draining existing accessors of the device. */ + if ((rc = config_detach_children(self, flags)) != 0) + return rc; mutex_enter(sc->sc_lock); sc->sc_dying = true; cv_broadcast(&sc->sc_wchan); cv_broadcast(&sc->sc_rchan); + cv_broadcast(&sc->sc_condvar); + mutex_exit(sc->sc_lock); + kthread_join(sc->sc_playthread); + mutex_enter(sc->sc_lock); + cv_destroy(&sc->sc_condvar); mutex_exit(sc->sc_lock); /* locate the major number */ @@ -737,6 +833,11 @@ pmf_device_deregister(self); /* free resources */ + for (n = 0; n < VAUDIOCHANS; n++) { + if (n != 0 && sc->sc_audiopid[n] == -1) + continue; + audio_free_ring(sc, &sc->sc_vchan[n]->sc_mpr); + } audio_free_ring(sc, &sc->sc_pr); audio_free_ring(sc, &sc->sc_rr); for (i = 0; i < sc->sc_nrfilters; i++) { @@ -745,12 +846,18 @@ audio_stream_dtor(&sc->sc_rstreams[i]); } sc->sc_nrfilters = 0; - for (i = 0; i < sc->sc_npfilters; i++) { - sc->sc_pfilters[i]->dtor(sc->sc_pfilters[i]); - sc->sc_pfilters[i] = NULL; - audio_stream_dtor(&sc->sc_pstreams[i]); + for (n = 0; n < VAUDIOCHANS; n++) { + if (n != 0 && sc->sc_audiopid[n] == -1) + continue; + for (i = 0; i < sc->sc_vchan[n]->sc_npfilters; i++) { + sc->sc_vchan[n]->sc_pfilters[i]->dtor(sc->sc_vchan[n]->sc_pfilters[i]); + sc->sc_vchan[n]->sc_pfilters[i] = NULL; + audio_stream_dtor(&sc->sc_vchan[n]->sc_pstreams[i]); + } + sc->sc_vchan[n]->sc_npfilters = 0; } - sc->sc_npfilters = 0; + + auconv_delete_encodings(sc->sc_encodings); if (sc->sc_sih_rd) { softint_disestablish(sc->sc_sih_rd); @@ -761,6 +868,8 @@ sc->sc_sih_wr = NULL; } + kmem_free(sc->sc_vchan[0], sizeof(struct virtual_channel)); + #ifdef AUDIO_PM_IDLE callout_destroy(&sc->sc_idle_counter); #endif @@ -780,7 +889,7 @@ mixer_devinfo_t mi; for(mi.index = 0; - sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0; + audio_query_devinfo(sc, &mi) == 0; mi.index++) if (mi.mixer_class == class && strcmp(mi.label.name, name) == 0) return mi.index; @@ -857,16 +966,28 @@ void audio_printsc(struct audio_softc *sc) { + int n; + + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + + if (n == VAUDIOCHANS) + return; + 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("open 0x%x mode 0x%x\n", sc->sc_vchan[n]->sc_open, + sc->sc_vchan[n]->sc_mode); printf("rchan 0x%x wchan 0x%x ", cv_has_waiters(&sc->sc_rchan), cv_has_waiters(&sc->sc_wchan)); printf("rring used 0x%x pring used=%d\n", audio_stream_get_used(&sc->sc_rr.s), - audio_stream_get_used(&sc->sc_pr.s)); - printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_pbus); - printf("blksize %d", sc->sc_pr.blksize); - printf("hiwat %d lowat %d\n", sc->sc_pr.usedhigh, sc->sc_pr.usedlow); + audio_stream_get_used(&sc->sc_vhan[n].sc_mpr.s)); + printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_vchan[n]->sc_pbus); + printf("blksize %d", sc->sc_vchan[n]->sc_mpr.blksize); + printf("hiwat %d lowat %d\n", sc->sc_vchan[n]->sc_mpr.usedhigh, + sc->sc_vchan[n]->sc_mpr.usedlow); } void @@ -893,17 +1014,16 @@ bufsize = AUMINBUF; ROUNDSIZE(bufsize); if (hw->round_buffersize) { - mutex_enter(sc->sc_lock); bufsize = hw->round_buffersize(hdl, direction, bufsize); - mutex_exit(sc->sc_lock); } - if (hw->allocm) + if (hw->allocm && (r == &sc->sc_vchan[0]->sc_mpr || r == &sc->sc_rr)) r->s.start = hw->allocm(hdl, direction, bufsize); else - r->s.start = kmem_alloc(bufsize, KM_SLEEP); + r->s.start = kmem_zalloc(bufsize, KM_SLEEP); if (r->s.start == 0) return ENOMEM; r->s.bufsize = bufsize; + return 0; } @@ -913,7 +1033,7 @@ if (r->s.start == 0) return; - if (sc->hw_if->freem) + if (sc->hw_if->freem && (r == &sc->sc_vchan[0]->sc_mpr || r == &sc->sc_rr)) sc->hw_if->freem(sc->hw_hdl, r->s.start, r->s.bufsize); else kmem_free(r->s.start, r->s.bufsize); @@ -922,7 +1042,7 @@ static int audio_setup_pfilters(struct audio_softc *sc, const audio_params_t *pp, - stream_filter_list_t *pfilters) + stream_filter_list_t *pfilters, int m) { stream_filter_t *pf[AUDIO_MAX_FILTERS], *of[AUDIO_MAX_FILTERS]; audio_stream_t ps[AUDIO_MAX_FILTERS], os[AUDIO_MAX_FILTERS]; @@ -964,22 +1084,22 @@ /* Swap in new filters. */ mutex_enter(sc->sc_intr_lock); - memcpy(of, sc->sc_pfilters, sizeof(of)); - memcpy(os, sc->sc_pstreams, sizeof(os)); - onfilters = sc->sc_npfilters; - memcpy(sc->sc_pfilters, pf, sizeof(pf)); - memcpy(sc->sc_pstreams, ps, sizeof(ps)); - sc->sc_npfilters = pfilters->req_size; + memcpy(of, sc->sc_vchan[m]->sc_pfilters, sizeof(of)); + memcpy(os, sc->sc_vchan[m]->sc_pstreams, sizeof(os)); + onfilters = sc->sc_vchan[m]->sc_npfilters; + memcpy(sc->sc_vchan[m]->sc_pfilters, pf, sizeof(pf)); + memcpy(sc->sc_vchan[m]->sc_pstreams, ps, sizeof(ps)); + sc->sc_vchan[m]->sc_npfilters = pfilters->req_size; for (i = 0; i < pfilters->req_size; i++) { - pf[i]->set_inputbuffer(pf[i], &sc->sc_pstreams[i]); + pf[i]->set_inputbuffer(pf[i], &sc->sc_vchan[m]->sc_pstreams[i]); } /* hardware format and the buffer near to userland */ if (pfilters->req_size <= 0) { - sc->sc_pr.s.param = *pp; - sc->sc_pustream = &sc->sc_pr.s; + sc->sc_vchan[m]->sc_mpr.s.param = *pp; + sc->sc_vchan[m]->sc_pustream = &sc->sc_vchan[m]->sc_mpr.s; } else { - sc->sc_pr.s.param = pfilters->filters[0].param; - sc->sc_pustream = &sc->sc_pstreams[0]; + sc->sc_vchan[m]->sc_mpr.s.param = pfilters->filters[0].param; + sc->sc_vchan[m]->sc_pustream = &sc->sc_vchan[m]->sc_pstreams[0]; } mutex_exit(sc->sc_intr_lock); @@ -993,13 +1113,13 @@ #ifdef AUDIO_DEBUG printf("%s: HW-buffer=%p pustream=%p\n", - __func__, &sc->sc_pr.s, sc->sc_pustream); + __func__, &sc->sc_vchan[m]->sc_mpr.s, sc->sc_vchan[m]->sc_pustream); for (i = 0; i < pfilters->req_size; i++) { char num[100]; snprintf(num, 100, "[%d]", i); - audio_print_params(num, &sc->sc_pstreams[i].param); + audio_print_params(num, &sc->sc_vchan[m]->sc_pstreams[i].param); } - audio_print_params("[HW]", &sc->sc_pr.s.param); + audio_print_params("[HW]", &sc->sc_vchan[m]->sc_mpr.s.param); #endif /* AUDIO_DEBUG */ return 0; @@ -1111,7 +1231,7 @@ size = min(size, AU_RING_SIZE); stream->bufsize = size; - stream->start = kmem_alloc(size, KM_SLEEP); + stream->start = kmem_zalloc(size, KM_SLEEP); if (stream->start == NULL) return ENOMEM; frame_size = (param->precision + 7) / 8 * param->channels; @@ -1182,6 +1302,7 @@ static int audio_enter(dev_t dev, krw_t rw, struct audio_softc **scp) { + struct audio_softc *sc; /* First, find the device and take sc_lock. */ @@ -1194,24 +1315,6 @@ return EIO; } - /* Acquire device access lock. */ - switch (rw) { - case RW_WRITER: - while (__predict_false(sc->sc_dvlock != 0)) { - cv_wait(&sc->sc_lchan, sc->sc_lock); - } - sc->sc_dvlock = -1; - break; - case RW_READER: - while (__predict_false(sc->sc_dvlock < 0)) { - cv_wait(&sc->sc_lchan, sc->sc_lock); - } - sc->sc_dvlock++; - break; - default: - panic("audio_enter"); - } - *scp = sc; return 0; } @@ -1222,16 +1325,6 @@ static void audio_exit(struct audio_softc *sc) { - - KASSERT(mutex_owned(sc->sc_lock)); - KASSERT(sc->sc_dvlock != 0); - - /* Release device level lock. */ - if (__predict_false(sc->sc_dvlock < 0)) { - sc->sc_dvlock = 0; - } else { - sc->sc_dvlock--; - } cv_broadcast(&sc->sc_lchan); mutex_exit(sc->sc_lock); } @@ -1243,37 +1336,13 @@ audio_waitio(struct audio_softc *sc, kcondvar_t *chan) { int error; - krw_t rw; KASSERT(mutex_owned(sc->sc_lock)); - - /* Release device level lock while sleeping. */ - if (__predict_false(sc->sc_dvlock < 0)) { - sc->sc_dvlock = 0; - rw = RW_WRITER; - } else { - KASSERT(sc->sc_dvlock > 0); - sc->sc_dvlock--; - rw = RW_READER; - } cv_broadcast(&sc->sc_lchan); /* Wait for pending I/O to complete. */ error = cv_wait_sig(chan, sc->sc_lock); - /* Re-acquire device level lock. */ - if (__predict_false(rw == RW_WRITER)) { - while (__predict_false(sc->sc_dvlock != 0)) { - cv_wait(&sc->sc_lchan, sc->sc_lock); - } - sc->sc_dvlock = -1; - } else { - while (__predict_false(sc->sc_dvlock < 0)) { - cv_wait(&sc->sc_lchan, sc->sc_lock); - } - sc->sc_dvlock++; - } - return error; } @@ -1310,7 +1379,7 @@ audioclose(dev_t dev, int flags, int ifmt, struct lwp *l) { struct audio_softc *sc; - int error; + int error, n; if ((error = audio_enter(dev, RW_WRITER, &sc)) != 0) return error; @@ -1318,7 +1387,15 @@ switch (AUDIODEV(dev)) { case SOUND_DEVICE: case AUDIO_DEVICE: - error = audio_close(sc, flags, ifmt, l); + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) { + error = EIO; + break; + } + error = audio_close(sc, flags, ifmt, l, n); break; case MIXER_DEVICE: error = mixer_close(sc, flags, ifmt, l); @@ -1565,19 +1642,23 @@ rp->copying = false; rp->needfill = false; rp->mmapped = false; + memset(rp->s.start, 0, blksize * 2); } int -audio_initbufs(struct audio_softc *sc) +audio_initbufs(struct audio_softc *sc, int n) { const struct audio_hw_if *hw; + struct virtual_channel *vc; int error; - DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_mode)); + DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_vchan[n]->sc_mode)); + vc = sc->sc_vchan[0]; hw = sc->hw_if; if (audio_can_capture(sc)) { audio_init_ringbuffer(sc, &sc->sc_rr, AUMODE_RECORD); - if (hw->init_input && (sc->sc_mode & AUMODE_RECORD)) { + if (sc->sc_opens == 1 && hw->init_input && + (sc->sc_vchan[n]->sc_mode & AUMODE_RECORD)) { error = hw->init_input(sc->hw_hdl, sc->sc_rr.s.start, sc->sc_rr.s.end - sc->sc_rr.s.start); if (error) @@ -1586,11 +1667,12 @@ } if (audio_can_playback(sc)) { - audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY); - sc->sc_sil_count = 0; - if (hw->init_output && (sc->sc_mode & AUMODE_PLAY)) { - error = hw->init_output(sc->hw_hdl, sc->sc_pr.s.start, - sc->sc_pr.s.end - sc->sc_pr.s.start); + audio_init_ringbuffer(sc, &sc->sc_vchan[n]->sc_mpr, AUMODE_PLAY); + sc->sc_vchan[n]->sc_sil_count = 0; + if (sc->sc_opens == 1 && hw->init_output && + (sc->sc_vchan[n]->sc_mode & AUMODE_PLAY)) { + error = hw->init_output(sc->hw_hdl, vc->sc_mpr.s.start, + vc->sc_mpr.s.end - vc->sc_mpr.s.start); if (error) return error; } @@ -1601,12 +1683,12 @@ if (audio_can_playback(sc)) { sc->sc_pnintr = 0; sc->sc_pblktime = (u_long)( - (double)sc->sc_pr.blksize * 100000 / - (double)(sc->sc_pparams.precision / NBBY * - sc->sc_pparams.channels * - sc->sc_pparams.sample_rate)) * 10; + (double)sc->sc_vchan[n]->sc_mpr.blksize * 100000 / + (double)(sc->sc_vchan[n]->sc_pparams.precision / NBBY * + sc->sc_vhan[n].sc_pparams.channels * + sc->sc_vhan[n].sc_pparams.sample_rate)) * 10; DPRINTF(("audio: play blktime = %lu for %d\n", - sc->sc_pblktime, sc->sc_pr.blksize)); + sc->sc_pblktime, sc->sc_vhan[n].sc_mpr.blksize)); } if (audio_can_capture(sc)) { sc->sc_rnintr = 0; @@ -1625,17 +1707,17 @@ } void -audio_calcwater(struct audio_softc *sc) +audio_calcwater(struct audio_softc *sc, int n) { + struct virtual_channel *vc = sc->sc_vchan[n]; /* set high at 100% */ if (audio_can_playback(sc)) { - sc->sc_pr.usedhigh = - sc->sc_pustream->end - sc->sc_pustream->start; + vc->sc_mpr.usedhigh = vc->sc_pustream->end - vc->sc_pustream->start; /* set low at 75% of usedhigh */ - sc->sc_pr.usedlow = sc->sc_pr.usedhigh * 3 / 4; - if (sc->sc_pr.usedlow == sc->sc_pr.usedhigh) - sc->sc_pr.usedlow -= sc->sc_pr.blksize; + vc->sc_mpr.usedlow = vc->sc_mpr.usedhigh * 3 / 4; + if (vc->sc_mpr.usedlow == vc->sc_mpr.usedhigh) + vc->sc_mpr.usedlow -= vc->sc_mpr.blksize; } if (audio_can_capture(sc)) { @@ -1644,7 +1726,7 @@ sc->sc_rr.blksize; sc->sc_rr.usedlow = 0; DPRINTF(("%s: plow=%d phigh=%d rlow=%d rhigh=%d\n", __func__, - sc->sc_pr.usedlow, sc->sc_pr.usedhigh, + vc->sc_mpr.usedlow, vc->sc_mpr.usedhigh, sc->sc_rr.usedlow, sc->sc_rr.usedhigh)); } } @@ -1653,51 +1735,106 @@ audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, struct lwp *l) { - int error; + int error, i, n; u_int mode; const struct audio_hw_if *hw; + struct virtual_channel *vc; + mixer_ctrl_t *mc; KASSERT(mutex_owned(sc->sc_lock)); + if (sc->sc_opens >= VAUDIOCHANS) + return ENXIO; + + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n < VAUDIOCHANS) + return ENXIO; + + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == -1) + break; + } + if (n == VAUDIOCHANS) + return ENXIO; + hw = sc->hw_if; if (hw == NULL) return ENXIO; + sc->sc_vchan[n] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP); + if (sc->sc_vchan[n] == NULL) + return ENOMEM; + vc = sc->sc_vchan[n]; + + vc->sc_open = 0; + vc->sc_mode = 0; + vc->sc_sil_count = 0; + vc->sc_npfilters = 0; + memset(vc->sc_pfilters, 0, + sizeof(vc->sc_pfilters)); + vc->sc_pbus = false; + vc->sc_blkset = false; + vc->sc_lastinfovalid = false; + vc->sc_swvol = 255; + DPRINTF(("audio_open: flags=0x%x sc=%p hdl=%p\n", flags, sc, sc->hw_hdl)); - if (((flags & FREAD) && (sc->sc_open & AUOPEN_READ)) || - ((flags & FWRITE) && (sc->sc_open & AUOPEN_WRITE))) + if (((flags & FREAD) && (vc->sc_open & AUOPEN_READ)) || + ((flags & FWRITE) && (vc->sc_open & AUOPEN_WRITE))) { + kmem_free(vc, sizeof(struct virtual_channel)); return EBUSY; + } - if (hw->open != NULL) { - mutex_enter(sc->sc_intr_lock); - error = hw->open(sc->hw_hdl, flags); - mutex_exit(sc->sc_intr_lock); - if (error) - return error; + sc->sc_opens++; + sc->sc_audiopid[n] = curproc->p_pid; + error = audio_alloc_ring(sc, &vc->sc_mpr, + AUMODE_PLAY, AU_RING_SIZE); + if (error) { + sc->sc_opens--; + sc->sc_audiopid[n] = -1; + kmem_free(vc, sizeof(struct virtual_channel)); + return error; } - sc->sc_async_audio = 0; - sc->sc_sil_count = 0; - sc->sc_rbus = false; - sc->sc_pbus = false; - sc->sc_eof = 0; - sc->sc_playdrop = 0; + if (sc->sc_opens == 1) { + if (hw->open != NULL) { + mutex_enter(sc->sc_intr_lock); + error = hw->open(sc->hw_hdl, flags); + mutex_exit(sc->sc_intr_lock); + if (error) { + sc->sc_opens--; + sc->sc_audiopid[n] = -1; + kmem_free(vc, + sizeof(struct virtual_channel)); + return error; + } + } + audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY); + audio_initbufs(sc, 0); + sc->schedule_wih = false; + sc->schedule_rih = false; + sc->sc_eof = 0; + sc->sc_rbus = false; + sc->sc_async_audio = 0; + } mutex_enter(sc->sc_intr_lock); - sc->sc_full_duplex = + vc->sc_full_duplex = (flags & (FWRITE|FREAD)) == (FWRITE|FREAD) && (audio_get_props(sc) & AUDIO_PROP_FULLDUPLEX); mutex_exit(sc->sc_intr_lock); mode = 0; if (flags & FREAD) { - sc->sc_open |= AUOPEN_READ; + vc->sc_open |= AUOPEN_READ; mode |= AUMODE_RECORD; } if (flags & FWRITE) { - sc->sc_open |= AUOPEN_WRITE; + vc->sc_open |= AUOPEN_WRITE; mode |= AUMODE_PLAY | AUMODE_PLAY_ALL; } @@ -1706,14 +1843,13 @@ * The /dev/audio is always (re)set to 8-bit MU-Law mono * For the other devices, you get what they were last set to. */ - if (ISDEVAUDIO(dev)) { - error = audio_set_defaults(sc, mode); - } else { + error = audio_set_defaults(sc, mode, n); + if (ISDEVSOUND(dev)) { struct audio_info ai; AUDIO_INITINFO(&ai); ai.mode = mode; - error = audiosetinfo(sc, &ai); + error = audiosetinfo(sc, &ai, true, n); } if (error) goto bad; @@ -1724,27 +1860,44 @@ * default values by the hardware driver, so that it may give * us these values. */ - if (sc->sc_rparams.precision == 0 || sc->sc_pparams.precision == 0) { + if (sc->sc_rparams.precision == 0 || vc->sc_pparams.precision == 0) { printf("audio_open: 0 precision\n"); - return EINVAL; + goto bad; } #endif - /* audio_close() decreases sc_pr.usedlow, recalculate here */ - audio_calcwater(sc); + /* audio_close() decreases sc_mpr[n].usedlow, recalculate here */ + audio_calcwater(sc, n); + + DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode[n])); + + mutex_enter(sc->sc_intr_lock); + sc->sc_nmixer_states++; + mc = &sc->sc_mixer_state[sc->sc_nmixer_states]; + mc->dev = sc->sc_nmixer_states; + mc->type = AUDIO_MIXER_VALUE; + mc->un.value.num_channels = 1; + (void)audio_get_port(sc, mc); - DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode)); + mutex_exit(sc->sc_intr_lock); return 0; bad: - mutex_enter(sc->sc_intr_lock); - if (hw->close != NULL) + for (i = 0; i < vc->sc_npfilters; i++) { + vc->sc_pfilters[i]->dtor(vc->sc_pfilters[i]); + vc->sc_pfilters[i] = NULL; + audio_stream_dtor(&vc->sc_pstreams[i]); + } + vc->sc_npfilters = 0; + if (hw->close != NULL && sc->sc_opens == 0) hw->close(sc->hw_hdl); - sc->sc_open = 0; - sc->sc_mode = 0; - mutex_exit(sc->sc_intr_lock); - sc->sc_full_duplex = 0; + sc->sc_opens--; + sc->sc_audiopid[n] = -1; + mutex_exit(sc->sc_lock); + audio_free_ring(sc, &vc->sc_mpr); + mutex_enter(sc->sc_lock); + kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel)); return error; } @@ -1752,14 +1905,19 @@ * Must be called from task context. */ void -audio_init_record(struct audio_softc *sc) +audio_init_record(struct audio_softc *sc, int n) { KASSERT(mutex_owned(sc->sc_lock)); + struct virtual_channel *vc = sc->sc_vchan[n]; + + if (sc->sc_opens != 0) + return; + mutex_enter(sc->sc_intr_lock); if (sc->hw_if->speaker_ctl && - (!sc->sc_full_duplex || (sc->sc_mode & AUMODE_PLAY) == 0)) + (!vc->sc_full_duplex || (vc->sc_mode & AUMODE_PLAY) == 0)) sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_OFF); mutex_exit(sc->sc_intr_lock); } @@ -1768,20 +1926,23 @@ * Must be called from task context. */ void -audio_init_play(struct audio_softc *sc) +audio_init_play(struct audio_softc *sc, int n) { KASSERT(mutex_owned(sc->sc_lock)); + + if (sc->sc_opens != 0) + return; mutex_enter(sc->sc_intr_lock); - sc->sc_wstamp = sc->sc_pr.stamp; + sc->sc_vchan[n]->sc_wstamp = sc->sc_vchan[n]->sc_mpr.stamp; if (sc->hw_if->speaker_ctl) sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_ON); mutex_exit(sc->sc_intr_lock); } int -audio_drain(struct audio_softc *sc) +audio_drain(struct audio_softc *sc, int n) { struct audio_ringbuffer *cb; int error, drops; @@ -1789,19 +1950,19 @@ KASSERT(mutex_owned(sc->sc_lock)); KASSERT(mutex_owned(sc->sc_intr_lock)); - - DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_pbus)); - cb = &sc->sc_pr; + + DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_pbus[n])); + cb = &sc->sc_vchan[n]->sc_mpr; if (cb->mmapped) return 0; - used = audio_stream_get_used(&sc->sc_pr.s); - for (i = 0; i < sc->sc_npfilters; i++) - used += audio_stream_get_used(&sc->sc_pstreams[i]); + used = audio_stream_get_used(&cb->s); + for (i = 0; i < sc->sc_vchan[n]->sc_npfilters; i++) + used += audio_stream_get_used(&sc->sc_vchan[n]->sc_pstreams[i]); if (used <= 0) return 0; - if (!sc->sc_pbus) { + if (n != 0 && !sc->sc_vchan[n]->sc_pbus) { /* We've never started playing, probably because the * block was too short. Pad it and start now. */ @@ -1811,7 +1972,7 @@ cc = cb->blksize - (inp - cb->s.start) % cb->blksize; audio_fill_silence(&cb->s.param, inp, cc); cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc); - error = audiostartp(sc); + error = audiostartp(sc, n); if (error) return error; } @@ -1823,15 +1984,15 @@ */ #ifdef DIAGNOSTIC if (cb->copying) { - printf("audio_drain: copying in progress!?!\n"); + DPRINTF(("audio_drain: copying in progress!?!\n")); cb->copying = false; } #endif drops = cb->drops; error = 0; while (cb->drops == drops && !error) { - DPRINTF(("audio_drain: used=%d, drops=%ld\n", - audio_stream_get_used(&sc->sc_pr.s), cb->drops)); + DPRINTF(("audio_drain: n=%d, used=%d, drops=%ld\n", n, + audio_stream_get_used(&sc->sc_vchan[n]->sc_mpr.s), cb->drops)); mutex_exit(sc->sc_intr_lock); error = audio_waitio(sc, &sc->sc_wchan); mutex_enter(sc->sc_intr_lock); @@ -1847,47 +2008,77 @@ /* ARGSUSED */ int audio_close(struct audio_softc *sc, int flags, int ifmt, - struct lwp *l) + struct lwp *l, int n) { + struct virtual_channel *vc; const struct audio_hw_if *hw; + int o; KASSERT(mutex_owned(sc->sc_lock)); + + if (sc->sc_opens == 0 || n == 0) + return ENXIO; + + vc = sc->sc_vchan[n]; - DPRINTF(("audio_close: sc=%p\n", sc)); hw = sc->hw_if; mutex_enter(sc->sc_intr_lock); + DPRINTF(("audio_close: sc=%p\n", sc)); /* Stop recording. */ - if ((flags & FREAD) && sc->sc_rbus) { + if (sc->sc_opens == 1 && (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. */ - if (!sc->sc_full_duplex || hw->halt_input != hw->halt_output) + if (!vc->sc_full_duplex || hw->halt_input != hw->halt_output) hw->halt_input(sc->hw_hdl); sc->sc_rbus = false; } /* * Block until output drains, but allow ^C interrupt. */ - sc->sc_pr.usedlow = sc->sc_pr.blksize; /* avoid excessive wakeups */ + vc->sc_mpr.usedlow = vc->sc_mpr.blksize; /* avoid excessive wakeups */ /* * If there is pending output, let it drain (unless * the output is paused). */ - if ((flags & FWRITE) && sc->sc_pbus) { - if (!sc->sc_pr.pause && !audio_drain(sc) && hw->drain) + if ((flags & FWRITE) && vc->sc_pbus) { + if (!vc->sc_mpr.pause) + audio_drain(sc, n); + vc->sc_pbus = false; + } + if (sc->sc_opens == 1) { + audio_drain(sc, 0); + if (hw->drain) (void)hw->drain(sc->hw_hdl); hw->halt_output(sc->hw_hdl); - sc->sc_pbus = false; + sc->sc_trigger_started = false; } - if (hw->close != NULL) + if (sc->sc_opens == 1 && hw->close != NULL) hw->close(sc->hw_hdl); - sc->sc_open = 0; - sc->sc_mode = 0; - sc->sc_full_duplex = 0; + if (sc->sc_opens == 1) + sc->sc_async_audio = 0; + + vc->sc_open = 0; + vc->sc_mode = 0; + vc->sc_full_duplex = 0; + + for (o = 0; o < vc->sc_npfilters; o++) { + vc->sc_pfilters[o]->dtor(vc->sc_pfilters[o]); + vc->sc_pfilters[o] = NULL; + audio_stream_dtor(&vc->sc_pstreams[o]); + } + vc->sc_npfilters = 0; + + sc->sc_opens--; + sc->sc_nmixer_states--; + sc->sc_audiopid[n] = -1; mutex_exit(sc->sc_intr_lock); - sc->sc_async_audio = 0; + mutex_exit(sc->sc_lock); + audio_free_ring(sc, &vc->sc_mpr); + mutex_enter(sc->sc_lock); + kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel)); return 0; } @@ -1896,18 +2087,27 @@ audio_read(struct audio_softc *sc, struct uio *uio, int ioflag) { struct audio_ringbuffer *cb; + struct virtual_channel *vc; const uint8_t *outp; uint8_t *inp; - int error, used, cc, n; + int error, used, cc, n, m; KASSERT(mutex_owned(sc->sc_lock)); + for (m = 1; m < VAUDIOCHANS; m++) { + if (sc->sc_audiopid[m] == curproc->p_pid) + break; + } + if (m == VAUDIOCHANS) + return ENXIO; + vc = sc->sc_vchan[m]; + cb = &sc->sc_rr; if (cb->mmapped) return EINVAL; DPRINTFN(1,("audio_read: cc=%zu mode=%d\n", - uio->uio_resid, sc->sc_mode)); + uio->uio_resid, vc->sc_mode)); #ifdef AUDIO_PM_IDLE if (device_is_active(&sc->dev) || sc->sc_idle) @@ -1919,18 +2119,18 @@ * 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)) { + if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) { while (uio->uio_resid > 0 && !error) { for(;;) { /* * No need to lock, as any wakeup will be * held for us while holding sc_lock. */ - cc = sc->sc_pr.stamp - sc->sc_wstamp; + cc = vc->sc_mpr.stamp - vc->sc_wstamp; if (cc > 0) break; DPRINTF(("audio_read: stamp=%lu, wstamp=%lu\n", - sc->sc_pr.stamp, sc->sc_wstamp)); + vc->sc_mpr.stamp, vc->sc_wstamp)); if (ioflag & IO_NDELAY) return EWOULDBLOCK; error = audio_waitio(sc, &sc->sc_rchan); @@ -1945,7 +2145,7 @@ DPRINTFN(1,("audio_read: reading in write mode, " "cc=%d\n", cc)); error = audio_silence_copyout(sc, cc, uio); - sc->sc_wstamp += cc; + vc->sc_wstamp += cc; } return error; } @@ -1998,45 +2198,47 @@ } void -audio_clear(struct audio_softc *sc) +audio_clear(struct audio_softc *sc, int n) { + struct virtual_channel *vc = sc->sc_vchan[n]; + KASSERT(mutex_owned(sc->sc_intr_lock)); if (sc->sc_rbus) { cv_broadcast(&sc->sc_rchan); - sc->hw_if->halt_input(sc->hw_hdl); + if (sc->sc_opens == 1) + sc->hw_if->halt_input(sc->hw_hdl); sc->sc_rbus = false; sc->sc_rr.pause = false; } - if (sc->sc_pbus) { + if (vc->sc_pbus) { cv_broadcast(&sc->sc_wchan); - sc->hw_if->halt_output(sc->hw_hdl); - sc->sc_pbus = false; - sc->sc_pr.pause = false; + vc->sc_pbus = false; + vc->sc_mpr.pause = false; } } void -audio_clear_intr_unlocked(struct audio_softc *sc) +audio_clear_intr_unlocked(struct audio_softc *sc, int n) { mutex_enter(sc->sc_intr_lock); - audio_clear(sc); + audio_clear(sc, n); mutex_exit(sc->sc_intr_lock); } void -audio_calc_blksize(struct audio_softc *sc, int mode) +audio_calc_blksize(struct audio_softc *sc, int mode, int n) { const audio_params_t *parm; struct audio_ringbuffer *rb; - if (sc->sc_blkset) + if (sc->sc_vchan[n]->sc_blkset) return; if (mode == AUMODE_PLAY) { - rb = &sc->sc_pr; + rb = &sc->sc_vchan[n]->sc_mpr ; parm = &rb->s.param; } else { rb = &sc->sc_rr; @@ -2210,18 +2412,28 @@ uio_fetcher_t ufetcher; audio_stream_t stream; struct audio_ringbuffer *cb; + struct virtual_channel *vc; stream_fetcher_t *fetcher; stream_filter_t *filter; uint8_t *inp, *einp; - int saveerror, error, n, cc, used; + int saveerror, error, n, m, cc, used; KASSERT(mutex_owned(sc->sc_lock)); + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) + return EINVAL; + + vc = sc->sc_vchan[n]; + cb = &vc->sc_mpr; + DPRINTFN(2,("audio_write: sc=%p count=%zu used=%d(hi=%d)\n", - sc, uio->uio_resid, audio_stream_get_used(sc->sc_pustream), - sc->sc_pr.usedhigh)); - cb = &sc->sc_pr; - if (cb->mmapped) + sc, uio->uio_resid, audio_stream_get_used(vc->sc_pustream), + vc->sc_mpr.usedhigh)); + if (vc->sc_mpr.mmapped) return EINVAL; if (uio->uio_resid == 0) { @@ -2237,20 +2449,20 @@ /* * If half-duplex and currently recording, throw away data. */ - if (!sc->sc_full_duplex && - (sc->sc_mode & AUMODE_RECORD)) { + if (!vc->sc_full_duplex && + (vc->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 (!(vc->sc_mode & AUMODE_PLAY_ALL) && vc->sc_playdrop > 0) { + m = min(vc->sc_playdrop, uio->uio_resid); + DPRINTF(("audio_write: playdrop %d\n", m)); + uio->uio_offset += m; + uio->uio_resid -= m; + vc->sc_playdrop -= m; if (uio->uio_resid == 0) return 0; } @@ -2258,9 +2470,9 @@ /** * setup filter pipeline */ - uio_fetcher_ctor(&ufetcher, uio, cb->usedhigh); - if (sc->sc_npfilters > 0) { - fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base; + uio_fetcher_ctor(&ufetcher, uio, vc->sc_mpr.usedhigh); + if (vc->sc_npfilters > 0) { + fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base; } else { fetcher = &ufetcher.base; } @@ -2269,7 +2481,7 @@ mutex_enter(sc->sc_intr_lock); while (uio->uio_resid > 0 && !error) { /* wait if the first buffer is occupied */ - while ((used = audio_stream_get_used(sc->sc_pustream)) + while ((used = audio_stream_get_used(vc->sc_pustream)) >= cb->usedhigh) { DPRINTFN(2, ("audio_write: sleep used=%d lowat=%d " "hiwat=%d\n", used, @@ -2291,17 +2503,17 @@ /* Write to the sc_pustream as much as possible. */ mutex_exit(sc->sc_intr_lock); - if (sc->sc_npfilters > 0) { - filter = sc->sc_pfilters[0]; + if (vc->sc_npfilters > 0) { + filter = vc->sc_pfilters[0]; filter->set_fetcher(filter, &ufetcher.base); - fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base; + fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base; cc = cb->blksize * 2; error = fetcher->fetch_to(sc, fetcher, &stream, cc); if (error != 0) { fetcher = &ufetcher.base; - cc = sc->sc_pustream->end - sc->sc_pustream->start; + cc = vc->sc_pustream->end - vc->sc_pustream->start; error = fetcher->fetch_to(sc, fetcher, - sc->sc_pustream, cc); + vc->sc_pustream, cc); } } else { fetcher = &ufetcher.base; @@ -2309,9 +2521,9 @@ error = fetcher->fetch_to(sc, fetcher, &stream, cc); } mutex_enter(sc->sc_intr_lock); - if (sc->sc_npfilters > 0) { + if (vc->sc_npfilters > 0) { cb->fstamp += ufetcher.last_used - - audio_stream_get_used(sc->sc_pustream); + - audio_stream_get_used(vc->sc_pustream); } cb->s.used += stream.used - used; cb->s.inp = stream.inp; @@ -2321,7 +2533,7 @@ * This is a very suboptimal way of keeping track of * silence in the buffer, but it is simple. */ - sc->sc_sil_count = 0; + vc->sc_sil_count = 0; /* * If the interrupt routine wants the last block filled AND @@ -2338,9 +2550,9 @@ cc = 0; cb->needfill = false; cb->copying = false; - if (!sc->sc_pbus && !cb->pause) { + if (!vc->sc_pbus && !cb->pause) { saveerror = error; - error = audiostartp(sc); + error = audiostartp(sc, n); if (saveerror != 0) { /* Report the first error that occurred. */ error = saveerror; @@ -2361,12 +2573,21 @@ struct lwp *l) { const struct audio_hw_if *hw; + struct virtual_channel *vc; struct audio_offset *ao; u_long stamp; - int error, offs, fd; + int error, offs, fd, n; bool rbus, pbus; KASSERT(mutex_owned(sc->sc_lock)); + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n >= VAUDIOCHANS) + n = 1; + + vc = sc->sc_vchan[n]; DPRINTF(("audio_ioctl(%lu,'%c',%lu)\n", IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd&0xff)); @@ -2396,18 +2617,18 @@ case AUDIO_FLUSH: DPRINTF(("AUDIO_FLUSH\n")); rbus = sc->sc_rbus; - pbus = sc->sc_pbus; + pbus = vc->sc_pbus; mutex_enter(sc->sc_intr_lock); - audio_clear(sc); - error = audio_initbufs(sc); + audio_clear(sc, n); + error = audio_initbufs(sc, n); if (error) { mutex_exit(sc->sc_intr_lock); return error; } - if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_pbus && pbus) - error = audiostartp(sc); + if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_pbus && pbus) + error = audiostartp(sc, n); if (!error && - (sc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus) + (vc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus) error = audiostartr(sc); mutex_exit(sc->sc_intr_lock); break; @@ -2421,7 +2642,7 @@ break; case AUDIO_PERROR: - *(int *)addr = sc->sc_pr.drops; + *(int *)addr = vc->sc_mpr.drops; break; /* @@ -2447,17 +2668,17 @@ ao = (struct audio_offset *)addr; mutex_enter(sc->sc_intr_lock); /* figure out where next DMA will start */ - stamp = sc->sc_pustream == &sc->sc_pr.s - ? sc->sc_pr.stamp : sc->sc_pr.fstamp; - offs = sc->sc_pustream->outp - sc->sc_pustream->start - + sc->sc_pr.blksize; + stamp = vc->sc_pustream == &vc->sc_mpr.s + ? vc->sc_mpr.stamp : vc->sc_mpr.fstamp; + offs = vc->sc_pustream->outp - vc->sc_pustream->start + + vc->sc_mpr.blksize; mutex_exit(sc->sc_intr_lock); ao->samples = stamp; ao->deltablks = - (stamp / sc->sc_pr.blksize) - - (sc->sc_pr.stamp_last / sc->sc_pr.blksize); - sc->sc_pr.stamp_last = stamp; - if (sc->sc_pustream->start + offs >= sc->sc_pustream->end) + (stamp / vc->sc_mpr.blksize) - + (vc->sc_mpr.stamp_last / vc->sc_mpr.blksize); + vc->sc_mpr.stamp_last = stamp; + if (vc->sc_pustream->start + offs >= vc->sc_pustream->end) offs = 0; ao->offset = offs; break; @@ -2467,29 +2688,29 @@ * sample of what we write next? */ case AUDIO_WSEEK: - *(u_long *)addr = audio_stream_get_used(sc->sc_pustream); + *(u_long *)addr = audio_stream_get_used(vc->sc_pustream); break; case AUDIO_SETINFO: - DPRINTF(("AUDIO_SETINFO mode=0x%x\n", sc->sc_mode)); - error = audiosetinfo(sc, (struct audio_info *)addr); + DPRINTF(("AUDIO_SETINFO mode=0x%x\n", vc->sc_mode)); + error = audiosetinfo(sc, (struct audio_info *)addr, false, n); break; case AUDIO_GETINFO: DPRINTF(("AUDIO_GETINFO\n")); - error = audiogetinfo(sc, (struct audio_info *)addr, 0); + error = audiogetinfo(sc, (struct audio_info *)addr, 0, n); break; case AUDIO_GETBUFINFO: DPRINTF(("AUDIO_GETBUFINFO\n")); - error = audiogetinfo(sc, (struct audio_info *)addr, 1); + error = audiogetinfo(sc, (struct audio_info *)addr, 1, n); break; case AUDIO_DRAIN: DPRINTF(("AUDIO_DRAIN\n")); mutex_enter(sc->sc_intr_lock); - error = audio_drain(sc); - if (!error && hw->drain) + error = audio_drain(sc, n); + if (!error && sc->sc_opens == 1 && hw->drain) error = hw->drain(sc->hw_hdl); mutex_exit(sc->sc_intr_lock); break; @@ -2501,13 +2722,13 @@ case AUDIO_GETENC: DPRINTF(("AUDIO_GETENC\n")); - error = hw->query_encoding(sc->hw_hdl, + error = audio_query_encoding(sc, (struct audio_encoding *)addr); break; case AUDIO_GETFD: DPRINTF(("AUDIO_GETFD\n")); - *(int *)addr = sc->sc_full_duplex; + *(int *)addr = vc->sc_full_duplex; break; case AUDIO_SETFD: @@ -2519,7 +2740,7 @@ else error = 0; if (!error) - sc->sc_full_duplex = fd; + vc->sc_full_duplex = fd; } else { if (fd) error = ENOTTY; @@ -2550,12 +2771,20 @@ int audio_poll(struct audio_softc *sc, int events, struct lwp *l) { + struct virtual_channel *vc; int revents; - int used; + int used, n; KASSERT(mutex_owned(sc->sc_lock)); + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) + return ENXIO; + vc = sc->sc_vchan[n]; - DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, sc->sc_mode)); + DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, vc->sc_mode)); revents = 0; mutex_enter(sc->sc_intr_lock); @@ -2566,23 +2795,23 @@ * silence at the play rate; poll for silence being * available. Otherwise, poll for recorded sound. */ - if ((!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) ? - sc->sc_pr.stamp > sc->sc_wstamp : + if ((!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) ? + vc->sc_mpr.stamp > vc->sc_wstamp : used > sc->sc_rr.usedlow) revents |= events & (POLLIN | POLLRDNORM); } if (events & (POLLOUT | POLLWRNORM)) { - used = audio_stream_get_used(sc->sc_pustream); + used = audio_stream_get_used(vc->sc_pustream); /* * If half duplex and recording, audio_write() will throw * away play data, which means we are always ready to write. * Otherwise, poll for play buffer being below its low water * mark. */ - if ((!sc->sc_full_duplex && (sc->sc_mode & AUMODE_RECORD)) || - (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) || - (used <= sc->sc_pr.usedlow)) + if ((!vc->sc_full_duplex && (vc->sc_mode & AUMODE_RECORD)) || + (!(vc->sc_mode & AUMODE_PLAY_ALL) && vc->sc_playdrop > 0) || + (used <= vc->sc_mpr.usedlow)) revents |= events & (POLLOUT | POLLWRNORM); } mutex_exit(sc->sc_intr_lock); @@ -2613,11 +2842,21 @@ filt_audioread(struct knote *kn, long hint) { struct audio_softc *sc; + struct virtual_channel *vc; + int n; sc = kn->kn_hook; + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) + return ENXIO; + + vc = sc->sc_vchan[n]; mutex_enter(sc->sc_intr_lock); - if (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) - kn->kn_data = sc->sc_pr.stamp - sc->sc_wstamp; + if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) + kn->kn_data = vc->sc_mpr.stamp - vc->sc_wstamp; else kn->kn_data = audio_stream_get_used(sc->sc_rustream) - sc->sc_rr.usedlow; @@ -2645,10 +2884,18 @@ { struct audio_softc *sc; audio_stream_t *stream; + int n; sc = kn->kn_hook; mutex_enter(sc->sc_intr_lock); - stream = sc->sc_pustream; + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) + return ENXIO; + + stream = sc->sc_vchan[n]->sc_pustream; kn->kn_data = (stream->end - stream->start) - audio_stream_get_used(stream); mutex_exit(sc->sc_intr_lock); @@ -2694,9 +2941,16 @@ const struct audio_hw_if *hw; struct audio_ringbuffer *cb; paddr_t rv; + int n; KASSERT(mutex_owned(sc->sc_lock)); - KASSERT(sc->sc_dvlock > 0); + + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) + return -1; DPRINTF(("audio_mmap: off=%lld, prot=%d\n", (long long)off, prot)); hw = sc->hw_if; @@ -2717,26 +2971,26 @@ */ if (prot == (VM_PROT_READ|VM_PROT_WRITE) || prot == VM_PROT_WRITE) - cb = &sc->sc_pr; + cb = &sc->sc_vchan[n]->sc_mpr; else if (prot == VM_PROT_READ) cb = &sc->sc_rr; else return -1; #else - cb = &sc->sc_pr; + cb = &sc->sc_vchan[n]->sc_mpr; #endif if ((u_int)off >= cb->s.bufsize) return -1; if (!cb->mmapped) { cb->mmapped = true; - if (cb == &sc->sc_pr) { + if (cb != &sc->sc_rr) { audio_fill_silence(&cb->s.param, cb->s.start, cb->s.bufsize); mutex_enter(sc->sc_intr_lock); - sc->sc_pustream = &cb->s; - if (!sc->sc_pbus && !sc->sc_pr.pause) - (void)audiostartp(sc); + sc->sc_vchan[n]->sc_pustream = &cb->s; + if (!sc->sc_vchan[n]->sc_pbus && !sc->sc_vchan[n]->sc_mpr.pause) + (void)audiostartp(sc, n); mutex_exit(sc->sc_intr_lock); } else { mutex_enter(sc->sc_intr_lock); @@ -2785,45 +3039,38 @@ } int -audiostartp(struct audio_softc *sc) +audiostartp(struct audio_softc *sc, int n) { - int error; - int used; + struct virtual_channel *vc = sc->sc_vchan[n]; + int used, error; KASSERT(mutex_owned(sc->sc_lock)); KASSERT(mutex_owned(sc->sc_intr_lock)); - used = audio_stream_get_used(&sc->sc_pr.s); + error = 0; + used = audio_stream_get_used(&vc->sc_mpr.s); DPRINTF(("audiostartp: start=%p used=%d(hi=%d blk=%d) mmapped=%d\n", - sc->sc_pr.s.start, used, sc->sc_pr.usedhigh, - sc->sc_pr.blksize, sc->sc_pr.mmapped)); + vc->sc_mpr.s.start, used, vc->sc_mpr.usedhigh, + vc->sc_mpr.blksize, vc->sc_mpr.mmapped)); if (!audio_can_playback(sc)) return EINVAL; - if (!sc->sc_pr.mmapped && used < sc->sc_pr.blksize) { + if (!vc->sc_mpr.mmapped && used < vc->sc_mpr.blksize) { cv_broadcast(&sc->sc_wchan); DPRINTF(("%s: wakeup and return\n", __func__)); return 0; } - - if (sc->hw_if->trigger_output) { - DPRINTF(("%s: call trigger_output\n", __func__)); - error = sc->hw_if->trigger_output(sc->hw_hdl, sc->sc_pr.s.start, - sc->sc_pr.s.end, sc->sc_pr.blksize, - audio_pint, (void *)sc, &sc->sc_pr.s.param); - } else { - DPRINTF(("%s: call start_output\n", __func__)); - error = sc->hw_if->start_output(sc->hw_hdl, - __UNCONST(sc->sc_pr.s.outp), sc->sc_pr.blksize, - audio_pint, (void *)sc); - } - if (error) { - DPRINTF(("audiostartp failed: %d\n", error)); - return error; + + vc->sc_pbus = true; + mix_func(sc, &vc->sc_mpr, n); + if (sc->sc_trigger_started == false) { + mix_write(sc); + cv_broadcast(&sc->sc_condvar); + } - sc->sc_pbus = true; - return 0; + + return error; } /* @@ -2843,15 +3090,16 @@ */ static inline void audio_pint_silence(struct audio_softc *sc, struct audio_ringbuffer *cb, - uint8_t *inp, int cc) + uint8_t *inp, int cc, int n) { + struct virtual_channel *vc = sc->sc_vchan[n]; uint8_t *s, *e, *p, *q; - KASSERT(mutex_owned(sc->sc_intr_lock)); + KASSERT(mutex_owned(sc->sc_lock)); - if (sc->sc_sil_count > 0) { - s = sc->sc_sil_start; /* start of silence */ - e = s + sc->sc_sil_count; /* end of sil., may be beyond end */ + if (vc->sc_sil_count > 0) { + s = vc->sc_sil_start; /* start of silence */ + e = s + vc->sc_sil_count; /* end of sil., may be beyond end */ p = inp; /* adjusted pointer to area to fill */ if (p < s) p += cb->s.end - cb->s.start; @@ -2860,10 +3108,10 @@ if (!(s <= p && p < e && s <= q && q <= e)) { if (s <= p) - sc->sc_sil_count = max(sc->sc_sil_count, q-s); + vc->sc_sil_count = max(vc->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, + cc, inp, vc->sc_sil_count, (int)(cb->s.end - cb->s.start))); audio_fill_silence(&cb->s.param, inp, cc); } else { @@ -2872,8 +3120,8 @@ } } else { - sc->sc_sil_start = inp; - sc->sc_sil_count = cc; + vc->sc_sil_start = inp; + vc->sc_sil_count = cc; DPRINTFN(5, ("audio_pint_silence: start fill %p %d\n", inp, cc)); audio_fill_silence(&cb->s.param, inp, cc); @@ -2929,136 +3177,180 @@ void audio_pint(void *v) { + struct audio_softc *sc; + struct virtual_channel *vc; + + sc = v; + vc = sc->sc_vchan[0]; + + if (sc->sc_dying == true) + return; + + vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s, + vc->sc_mpr.s.outp, vc->sc_mpr.blksize); + mix_write(sc); + + cv_broadcast(&sc->sc_condvar); +} + +void +audio_mix(void *v) +{ stream_fetcher_t null_fetcher; struct audio_softc *sc; - const struct audio_hw_if *hw; + struct virtual_channel *vc; struct audio_ringbuffer *cb; stream_fetcher_t *fetcher; uint8_t *inp; int cc, used; int blksize; - int error; - + int n, i; + sc = v; - KASSERT(mutex_owned(sc->sc_intr_lock)); - - if (!sc->sc_open) - return; /* ignore interrupt if not open */ + DPRINTF(("PINT\n")); + sc->schedule_rih = false; + sc->schedule_wih = false; + sc->sc_writeme = false; - hw = sc->hw_if; - cb = &sc->sc_pr; - blksize = cb->blksize; - cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize); - cb->stamp += blksize; - if (cb->mmapped) { - DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n", - cb->s.outp, blksize, cb->s.inp)); - if (hw->trigger_output == NULL) - (void)hw->start_output(sc->hw_hdl, __UNCONST(cb->s.outp), - blksize, audio_pint, (void *)sc); + if (sc->sc_dying == true) return; - } + + i = sc->sc_opens; + for (n = 1; n < VAUDIOCHANS; n++) { + if (!sc->sc_opens || i <= 0) + break; /* ignore interrupt if not open */ + + if (sc->sc_audiopid[n] == -1) + continue; + i--; + vc = sc->sc_vchan[n]; + if (!vc->sc_open) + continue; + if (!vc->sc_pbus) + continue; + + sc->sc_writeme = true; + + cb = &vc->sc_mpr; + blksize = cb->blksize; + + cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize); + cb->stamp += blksize; + if (cb->mmapped) { + DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n", + cb->s.outp, blksize, cb->s.inp)); + mix_func(sc, cb, n); + continue; + } #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 " + { + 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++; - } + 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 - used = audio_stream_get_used(&cb->s); - /* - * "used <= cb->usedlow" should be "used < blksize" ideally. - * Some HW drivers such as uaudio(4) does not call audio_pint() - * at accurate timing. If used < blksize, uaudio(4) already - * request transfer of garbage data. - */ - if (used <= cb->usedlow && !cb->copying && sc->sc_npfilters > 0) { - /* we might have data in filter pipeline */ - null_fetcher.fetch_to = null_fetcher_fetch_to; - fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base; - sc->sc_pfilters[0]->set_fetcher(sc->sc_pfilters[0], - &null_fetcher); - used = audio_stream_get_used(sc->sc_pustream); - cc = cb->s.end - cb->s.start; - if (blksize * 2 < cc) - cc = blksize * 2; - fetcher->fetch_to(sc, fetcher, &cb->s, cc); - cb->fstamp += used - audio_stream_get_used(sc->sc_pustream); used = audio_stream_get_used(&cb->s); - } - if (used < blksize) { - /* we don't have a full block to use */ - if (cb->copying) { - /* writer is in progress, don't disturb */ - cb->needfill = true; - DPRINTFN(1, ("audio_pint: copying in progress\n")); - } else { - inp = cb->s.inp; - cc = blksize - (inp - cb->s.start) % blksize; - if (cb->pause) - cb->pdrops += cc; - else { - cb->drops += cc; - sc->sc_playdrop += cc; - } - audio_pint_silence(sc, cb, inp, cc); - cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc); - - /* Clear next block so we keep ahead of the DMA. */ + /* + * "used <= cb->usedlow" should be "used < blksize" ideally. + * Some HW drivers such as uaudio(4) does not call audio_pint() + * at accurate timing. If used < blksize, uaudio(4) already + * request transfer of garbage data. + */ + if (used <= cb->usedlow && !cb->copying && vc->sc_npfilters > 0) { + /* we might have data in filter pipeline */ + null_fetcher.fetch_to = null_fetcher_fetch_to; + fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base; + vc->sc_pfilters[0]->set_fetcher(vc->sc_pfilters[0], + &null_fetcher); + used = audio_stream_get_used(vc->sc_pustream); + cc = cb->s.end - cb->s.start; + if (blksize * 2 < cc) + cc = blksize * 2; + fetcher->fetch_to(sc, fetcher, &cb->s, cc); + cb->fstamp += used - audio_stream_get_used(vc->sc_pustream); used = audio_stream_get_used(&cb->s); - if (used + blksize < cb->s.end - cb->s.start) - audio_pint_silence(sc, cb, cb->s.inp, blksize); } - } + if (used < blksize) { + /* we don't have a full block to use */ + if (cb->copying) { + /* writer is in progress, don't disturb */ + cb->needfill = true; + DPRINTFN(1, ("audio_pint: copying in progress\n")); + } else { + inp = cb->s.inp; + cc = blksize - (inp - cb->s.start) % blksize; + if (cb->pause) + cb->pdrops += cc; + else { + cb->drops += cc; + vc->sc_playdrop += cc; + } - DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp, blksize)); - if (hw->trigger_output == NULL) { - error = hw->start_output(sc->hw_hdl, __UNCONST(cb->s.outp), - blksize, audio_pint, (void *)sc); - if (error) { - /* XXX does this really help? */ - DPRINTF(("audio_pint restart failed: %d\n", error)); - audio_clear(sc); + audio_pint_silence(sc, cb, inp, cc, n); + cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc); + + /* Clear next block so we keep ahead of the DMA. */ + used = audio_stream_get_used(&cb->s); + if (used + blksize < cb->s.end - cb->s.start) + audio_pint_silence(sc, cb, cb->s.inp, blksize, n); + } } - } - DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n", - sc->sc_mode, cb->pause, - audio_stream_get_used(sc->sc_pustream), cb->usedlow)); - if ((sc->sc_mode & AUMODE_PLAY) && !cb->pause) { - if (audio_stream_get_used(sc->sc_pustream) <= cb->usedlow) - softint_schedule(sc->sc_sih_wr); + DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp, blksize)); + mix_func(sc, cb, n); + + DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n", + sc->sc_mode[n], cb->pause, + audio_stream_get_used(vc->sc_pustream), cb->usedlow)); + + if ((vc->sc_mode & AUMODE_PLAY) && !cb->pause) { + if (audio_stream_get_used(&cb->s) <= cb->usedlow) + sc->schedule_wih = true; + } + /* Possible to return one or more "phantom blocks" now. */ + if (!vc->sc_full_duplex && vc->sc_mode & AUMODE_RECORD) + sc->schedule_rih = true; } - /* Possible to return one or more "phantom blocks" now. */ - if (!sc->sc_full_duplex) + kpreempt_disable(); + if (sc->schedule_wih == true) + softint_schedule(sc->sc_sih_wr); + + if (sc->schedule_rih == true) softint_schedule(sc->sc_sih_rd); + kpreempt_enable(); + + cc = sc->sc_vchan[0]->sc_mpr.blksize; + if (sc->sc_writeme == false) { + sc->sc_vchan[0]->sc_mpr.drops += cc; + cv_broadcast(&sc->sc_wchan); + } } /* @@ -3083,8 +3375,7 @@ cb = &sc->sc_rr; KASSERT(mutex_owned(sc->sc_intr_lock)); - - if (!sc->sc_open) + if (!sc->sc_opens) return; /* ignore interrupt if not open */ hw = sc->hw_if; @@ -3162,7 +3453,7 @@ if (error) { /* XXX does this really help? */ DPRINTF(("audio_rint: restart failed: %d\n", error)); - audio_clear(sc); + audio_clear(sc, 0); } } @@ -3242,17 +3533,66 @@ return 0; } +static int +audio_set_vchan_defaults(struct audio_softc *sc, u_int mode, + const struct audio_format *format, int n) +{ + struct virtual_channel *vc = sc->sc_vchan[n]; + struct audio_info ai; + int i, error; + + KASSERT(mutex_owned(sc->sc_lock)); + + /* default parameters */ + sc->sc_rparams = vchan_default; + vc->sc_pparams = vchan_default; + vc->sc_blkset = false; + + AUDIO_INITINFO(&ai); + ai.record.sample_rate = vc->sc_pparams.sample_rate; + ai.record.encoding = vc->sc_pparams.encoding; + ai.record.channels = vc->sc_pparams.channels; + ai.record.precision = vc->sc_pparams.precision; + ai.record.pause = false; + ai.play.sample_rate = format->frequency[0]; + ai.play.encoding = format->encoding; + ai.play.channels = format->channels; + ai.play.precision = format->precision; + ai.play.pause = false; + ai.mode = mode; + + error = audiosetinfo(sc, &ai, true, n); + if (vc->sc_npfilters > 0) { + ai.play.sample_rate = vc->sc_pstreams[vc->sc_npfilters -1].param.sample_rate; + ai.record.sample_rate = + vc->sc_pstreams[vc->sc_npfilters -1].param.sample_rate; + vc->sc_pparams.sample_rate = ai.play.sample_rate; + sc->sc_rparams.sample_rate = ai.record.sample_rate; + + for (i = 0; i < vc->sc_npfilters; i++) { + vc->sc_pfilters[i]->dtor(vc->sc_pfilters[i]); + vc->sc_pfilters[i] = NULL; + audio_stream_dtor(&vc->sc_pstreams[i]); + } + vc->sc_npfilters = 0; + + return audiosetinfo(sc, &ai, true, n); + } else + return error; +} + int -audio_set_defaults(struct audio_softc *sc, u_int mode) +audio_set_defaults(struct audio_softc *sc, u_int mode, int n) { + struct virtual_channel *vc = sc->sc_vchan[n]; struct audio_info ai; KASSERT(mutex_owned(sc->sc_lock)); /* default parameters */ sc->sc_rparams = audio_default; - sc->sc_pparams = audio_default; - sc->sc_blkset = false; + vc->sc_pparams = audio_default; + vc->sc_blkset = false; AUDIO_INITINFO(&ai); ai.record.sample_rate = sc->sc_rparams.sample_rate; @@ -3260,14 +3600,14 @@ ai.record.channels = sc->sc_rparams.channels; ai.record.precision = sc->sc_rparams.precision; ai.record.pause = false; - ai.play.sample_rate = sc->sc_pparams.sample_rate; - ai.play.encoding = sc->sc_pparams.encoding; - ai.play.channels = sc->sc_pparams.channels; - ai.play.precision = sc->sc_pparams.precision; + ai.play.sample_rate = vc->sc_pparams.sample_rate; + ai.play.encoding = vc->sc_pparams.encoding; + ai.play.channels = vc->sc_pparams.channels; + ai.play.precision = vc->sc_pparams.precision; ai.play.pause = false; ai.mode = mode; - return audiosetinfo(sc, &ai); + return audiosetinfo(sc, &ai, true, n); } int @@ -3280,11 +3620,11 @@ 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) + if (audio_set_port(sc, 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); + return audio_set_port(sc, ct); } int @@ -3322,7 +3662,7 @@ ct.dev = ports->index; if (ports->isenum) { ct.type = AUDIO_MIXER_ENUM; - error = sc->hw_if->get_port(sc->hw_hdl, &ct); + error = audio_get_port(sc, &ct); if (error) return error; if (ports->isdual) { @@ -3344,7 +3684,7 @@ } } else { ct.type = AUDIO_MIXER_SET; - error = sc->hw_if->get_port(sc->hw_hdl, &ct); + error = audio_get_port(sc, &ct); if (error) return error; mask = ct.un.mask; @@ -3374,12 +3714,12 @@ KASSERT(mutex_owned(sc->sc_lock)); ct->un.value.num_channels = 2; - if (sc->hw_if->get_port(sc->hw_hdl, ct) == 0) { + if (audio_get_port(sc, 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); + error = audio_get_port(sc, ct); if (error) return error; *r = *l = ct->un.value.level[AUDIO_MIXER_LEVEL_MONO]; @@ -3411,7 +3751,7 @@ ct.dev = ports->index; if (ports->isenum) { ct.type = AUDIO_MIXER_ENUM; - if (sc->hw_if->get_port(sc->hw_hdl, &ct)) + if (audio_get_port(sc, &ct)) goto bad; ct.type = AUDIO_MIXER_VALUE; if (ports->isdual) { @@ -3434,7 +3774,7 @@ } } else { ct.type = AUDIO_MIXER_SET; - if (sc->hw_if->get_port(sc->hw_hdl, &ct)) + if (audio_get_port(sc, &ct)) goto bad; ct.type = AUDIO_MIXER_VALUE; lgain = rgain = n = 0; @@ -3511,7 +3851,7 @@ } else { ct.un.ord = ports->misel[i]; } - error = sc->hw_if->set_port(sc->hw_hdl, &ct); + error = audio_set_port(sc, &ct); break; } } else { @@ -3523,7 +3863,7 @@ if (port != 0 && ct.un.mask == 0) error = EINVAL; else - error = sc->hw_if->set_port(sc->hw_hdl, &ct); + error = audio_set_port(sc, &ct); } if (!error) mixer_signal(sc); @@ -3542,7 +3882,7 @@ 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)) + if (audio_get_port(sc, &ct)) return 0; aumask = 0; if (ports->isenum) { @@ -3565,12 +3905,13 @@ } int -audiosetinfo(struct audio_softc *sc, struct audio_info *ai) +audiosetinfo(struct audio_softc *sc, struct audio_info *ai, bool reset, int n) { stream_filter_list_t pfilters, rfilters; audio_params_t pp, rp; struct audio_prinfo *r, *p; const struct audio_hw_if *hw; + struct virtual_channel *vc; audio_stream_t *oldpus, *oldrus; int setmode; int error; @@ -3584,6 +3925,7 @@ KASSERT(mutex_owned(sc->sc_lock)); + vc = sc->sc_vchan[n]; hw = sc->hw_if; if (hw == NULL) /* HW has not attached */ return ENXIO; @@ -3592,13 +3934,13 @@ r = &ai->record; p = &ai->play; rbus = sc->sc_rbus; - pbus = sc->sc_pbus; + pbus = vc->sc_pbus; error = 0; cleared = false; modechange = false; pausechange = false; - pp = sc->sc_pparams; /* Temporary encoding storage in */ + pp = vc->sc_pparams; /* Temporary encoding storage in */ rp = sc->sc_rparams; /* case setting the modes fails. */ nr = np = 0; @@ -3655,13 +3997,13 @@ if (np > 0 && (error = audio_check_params(&pp))) return error; - oldpblksize = sc->sc_pr.blksize; + oldpblksize = vc->sc_mpr.blksize; oldrblksize = sc->sc_rr.blksize; setmode = 0; if (nr > 0) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } modechange = true; @@ -3669,7 +4011,7 @@ } if (np > 0) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } modechange = true; @@ -3678,19 +4020,19 @@ if (SPECIFIED(ai->mode)) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } modechange = true; - 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) + vc->sc_mode = ai->mode; + if (vc->sc_mode & AUMODE_PLAY_ALL) + vc->sc_mode |= AUMODE_PLAY; + if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_full_duplex) /* Play takes precedence */ - sc->sc_mode &= ~AUMODE_RECORD; + vc->sc_mode &= ~AUMODE_RECORD; } - oldpus = sc->sc_pustream; + oldpus = vc->sc_pustream; oldrus = sc->sc_rustream; if (modechange) { int indep; @@ -3712,12 +4054,12 @@ rfilters.set = stream_filter_list_set; /* Some device drivers change channels/sample_rate and change * no channels/sample_rate. */ - error = hw->set_params(sc->hw_hdl, setmode, - sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp, - &pfilters, &rfilters); + error = audio_set_params(sc, setmode, + vc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp, + &pfilters, &rfilters, n); if (error) { - DPRINTF(("%s: hw->set_params() failed with %d\n", - __func__, error)); + DPRINTF(("%s: audio_set_params() failed with %d\n", + __func__, error)); goto cleanup; } @@ -3733,7 +4075,7 @@ } } - if (sc->sc_pr.mmapped && pfilters.req_size > 0) { + if (vc->sc_mpr.mmapped && pfilters.req_size > 0) { DPRINTF(("%s: mmapped, and filters are requested.\n", __func__)); error = EINVAL; @@ -3742,7 +4084,7 @@ /* construct new filter chain */ if (setmode & AUMODE_PLAY) { - error = audio_setup_pfilters(sc, &pp, &pfilters); + error = audio_setup_pfilters(sc, &pp, &pfilters, n); if (error) goto cleanup; } @@ -3754,25 +4096,29 @@ DPRINTF(("%s: filter setup is completed.\n", __func__)); /* userland formats */ - sc->sc_pparams = pp; + vc->sc_pparams = pp; sc->sc_rparams = rp; } /* Play params can affect the record params, so recalculate blksize. */ if (nr > 0 || np > 0) { - audio_calc_blksize(sc, AUMODE_RECORD); - audio_calc_blksize(sc, AUMODE_PLAY); + audio_calc_blksize(sc, AUMODE_RECORD, n); + audio_calc_blksize(sc, AUMODE_PLAY, n); } #ifdef AUDIO_DEBUG - if (audiodebug > 1 && nr > 0) - audio_print_params("audiosetinfo() After setting record params:", &sc->sc_rparams); - if (audiodebug > 1 && np > 0) - audio_print_params("audiosetinfo() After setting play params:", &sc->sc_pparams); + if (audiodebug > 1 && nr > 0) { + audio_print_params("audiosetinfo() After setting record params:", + &sc->sc_rparams); + } + if (audiodebug > 1 && np > 0) { + audio_print_params("audiosetinfo() After setting play params:", + &vc->sc_pparams); + } #endif if (SPECIFIED(p->port)) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } error = au_set_port(sc, &sc->sc_outports, p->port); @@ -3781,7 +4127,7 @@ } if (SPECIFIED(r->port)) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } error = au_set_port(sc, &sc->sc_inports, r->port); @@ -3821,13 +4167,13 @@ 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); + error = audio_set_port(sc, &ct); if (error) goto cleanup; } if (SPECIFIED_CH(p->pause)) { - sc->sc_pr.pause = p->pause; + vc->sc_mpr.pause = p->pause; pbus = !p->pause; pausechange = true; } @@ -3843,34 +4189,34 @@ /* Block size specified explicitly. */ if (ai->blocksize == 0) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } - sc->sc_blkset = false; - audio_calc_blksize(sc, AUMODE_RECORD); - audio_calc_blksize(sc, AUMODE_PLAY); + vc->sc_blkset = false; + audio_calc_blksize(sc, AUMODE_RECORD, n); + audio_calc_blksize(sc, AUMODE_PLAY, n); } else { - sc->sc_blkset = true; + vc->sc_blkset = true; /* check whether new blocksize changes actually */ if (hw->round_blocksize == NULL) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } - sc->sc_pr.blksize = ai->blocksize; + vc->sc_mpr.blksize = ai->blocksize; sc->sc_rr.blksize = ai->blocksize; } else { pblksize = hw->round_blocksize(sc->hw_hdl, - ai->blocksize, AUMODE_PLAY, &sc->sc_pr.s.param); + ai->blocksize, AUMODE_PLAY, &vc->sc_mpr.s.param); rblksize = hw->round_blocksize(sc->hw_hdl, ai->blocksize, AUMODE_RECORD, &sc->sc_rr.s.param); - if (pblksize != sc->sc_pr.blksize || + if (pblksize != vc->sc_mpr.blksize || rblksize != sc->sc_rr.blksize) { if (!cleared) { - audio_clear_intr_unlocked(sc); + audio_clear_intr_unlocked(sc, n); cleared = true; } - sc->sc_pr.blksize = ai->blocksize; + vc->sc_mpr.blksize = ai->blocksize; sc->sc_rr.blksize = ai->blocksize; } } @@ -3878,38 +4224,38 @@ } if (SPECIFIED(ai->mode)) { - if (sc->sc_mode & AUMODE_PLAY) - audio_init_play(sc); - if (sc->sc_mode & AUMODE_RECORD) - audio_init_record(sc); + if (vc->sc_mode & AUMODE_PLAY) + audio_init_play(sc, n); + if (vc->sc_mode & AUMODE_RECORD) + audio_init_record(sc, n); } - if (hw->commit_settings) { + if (hw->commit_settings && sc->sc_opens == 0) { error = hw->commit_settings(sc->hw_hdl); if (error) goto cleanup; } - sc->sc_lastinfo = *ai; - sc->sc_lastinfovalid = true; + vc->sc_lastinfo = *ai; + vc->sc_lastinfovalid = true; cleanup: - if (cleared || pausechange) { + if (cleared || pausechange|| reset) { int init_error; mutex_enter(sc->sc_intr_lock); - init_error = audio_initbufs(sc); + init_error = audio_initbufs(sc, n); if (init_error) goto err; - if (sc->sc_pr.blksize != oldpblksize || + if (vc->sc_mpr.blksize != oldpblksize || sc->sc_rr.blksize != oldrblksize || - sc->sc_pustream != oldpus || + vc->sc_pustream != oldpus || sc->sc_rustream != oldrus) - audio_calcwater(sc); - if ((sc->sc_mode & AUMODE_PLAY) && - pbus && !sc->sc_pbus) - init_error = audiostartp(sc); + audio_calcwater(sc, n); + if ((vc->sc_mode & AUMODE_PLAY) && + pbus && !vc->sc_pbus) + init_error = audiostartp(sc, n); if (!init_error && - (sc->sc_mode & AUMODE_RECORD) && + (vc->sc_mode & AUMODE_RECORD) && rbus && !sc->sc_rbus) init_error = audiostartr(sc); err: @@ -3921,48 +4267,51 @@ /* Change water marks after initializing the buffers. */ if (SPECIFIED(ai->hiwat)) { blks = ai->hiwat; - if (blks > sc->sc_pr.maxblks) - blks = sc->sc_pr.maxblks; + if (blks > vc->sc_mpr.maxblks) + blks = vc->sc_mpr.maxblks; if (blks < 2) blks = 2; - sc->sc_pr.usedhigh = blks * sc->sc_pr.blksize; + vc->sc_mpr.usedhigh = blks * vc->sc_mpr.blksize; } if (SPECIFIED(ai->lowat)) { 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 (blks > vc->sc_mpr.maxblks - 1) + blks = vc->sc_mpr.maxblks - 1; + vc->sc_mpr.usedlow = blks * vc->sc_mpr.blksize; } if (SPECIFIED(ai->hiwat) || SPECIFIED(ai->lowat)) { - 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 (vc->sc_mpr.usedlow > vc->sc_mpr.usedhigh - vc->sc_mpr.blksize) + vc->sc_mpr.usedlow = + vc->sc_mpr.usedhigh - vc->sc_mpr.blksize; } return error; } int -audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode) +audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode, + int n) { struct audio_prinfo *r, *p; const struct audio_hw_if *hw; + struct virtual_channel *vc; KASSERT(mutex_owned(sc->sc_lock)); r = &ai->record; p = &ai->play; + vc = sc->sc_vchan[n]; hw = sc->hw_if; if (hw == NULL) /* HW has not attached */ return ENXIO; - p->sample_rate = sc->sc_pparams.sample_rate; + p->sample_rate = vc->sc_pparams.sample_rate; r->sample_rate = sc->sc_rparams.sample_rate; - p->channels = sc->sc_pparams.channels; + p->channels = vc->sc_pparams.channels; r->channels = sc->sc_rparams.channels; - p->precision = sc->sc_pparams.precision; + p->precision = vc->sc_pparams.precision; r->precision = sc->sc_rparams.precision; - p->encoding = sc->sc_pparams.encoding; + p->encoding = vc->sc_pparams.encoding; r->encoding = sc->sc_rparams.encoding; if (buf_only_mode) { @@ -3984,7 +4333,7 @@ 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_inports, &r->gain, &r->balance); au_get_gain(sc, &sc->sc_outports, &p->gain, &p->balance); } @@ -3994,7 +4343,7 @@ 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)) + if (audio_get_port(sc, &ct)) ai->monitor_gain = 0; else ai->monitor_gain = @@ -4002,45 +4351,45 @@ } else ai->monitor_gain = 0; - p->seek = audio_stream_get_used(sc->sc_pustream); + p->seek = audio_stream_get_used(vc->sc_pustream); r->seek = audio_stream_get_used(sc->sc_rustream); /* * XXX samples should be a value for userland data. * But drops is a value for HW data. */ - p->samples = (sc->sc_pustream == &sc->sc_pr.s - ? sc->sc_pr.stamp : sc->sc_pr.fstamp) - sc->sc_pr.drops; + p->samples = (vc->sc_pustream == &vc->sc_mpr.s + ? vc->sc_mpr.stamp : vc->sc_mpr.fstamp) - vc->sc_mpr.drops; r->samples = (sc->sc_rustream == &sc->sc_rr.s ? sc->sc_rr.stamp : sc->sc_rr.fstamp) - sc->sc_rr.drops; p->eof = sc->sc_eof; r->eof = 0; - p->pause = sc->sc_pr.pause; + p->pause = vc->sc_mpr.pause; r->pause = sc->sc_rr.pause; - p->error = sc->sc_pr.drops != 0; + p->error = vc->sc_mpr.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->open = (vc->sc_open & AUOPEN_WRITE) != 0; + r->open = (vc->sc_open & AUOPEN_READ) != 0; - p->active = sc->sc_pbus; + p->active = vc->sc_pbus; r->active = sc->sc_rbus; - p->buffer_size = sc->sc_pustream ? sc->sc_pustream->bufsize : 0; + p->buffer_size = vc->sc_pustream ? vc->sc_pustream->bufsize : 0; r->buffer_size = sc->sc_rustream ? sc->sc_rustream->bufsize : 0; - ai->blocksize = sc->sc_pr.blksize; - if (sc->sc_pr.blksize > 0) { - ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize; - ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize; + ai->blocksize = vc->sc_mpr.blksize; + if (vc->sc_mpr.blksize > 0) { + ai->hiwat = vc->sc_mpr.usedhigh / vc->sc_mpr.blksize; + ai->lowat = vc->sc_mpr.usedlow / vc->sc_mpr.blksize; } else ai->hiwat = ai->lowat = 0; - ai->mode = sc->sc_mode; + ai->mode = vc->sc_mode; return 0; } @@ -4162,7 +4511,7 @@ 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); + error = audio_query_devinfo(sc, (mixer_devinfo_t *)addr); break; case AUDIO_MIXER_READ: @@ -4170,7 +4519,7 @@ mc = (mixer_ctrl_t *)addr; if (device_is_active(sc->sc_dev)) - error = hw->get_port(sc->hw_hdl, mc); + error = audio_get_port(sc, mc); else if (mc->dev >= sc->sc_nmixer_states) error = ENXIO; else { @@ -4183,7 +4532,7 @@ case AUDIO_MIXER_WRITE: DPRINTF(("AUDIO_MIXER_WRITE\n")); - error = hw->set_port(sc->hw_hdl, (mixer_ctrl_t *)addr); + error = audio_set_port(sc, (mixer_ctrl_t *)addr); if (!error && hw->commit_settings) error = hw->commit_settings(sc->hw_hdl); if (!error) @@ -4263,7 +4612,7 @@ KASSERT(mutex_owned(sc->sc_lock)); for (mi.index = 0;; mi.index++) { - if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) != 0) + if (audio_query_devinfo(sc, &mi) != 0) break; KASSERT(mi.index < sc->sc_nmixer_states); if (mi.type == AUDIO_MIXER_CLASS) @@ -4272,7 +4621,7 @@ mc->dev = mi.index; mc->type = mi.type; mc->un.value.num_channels = mi.un.v.num_channels; - (void)sc->hw_if->get_port(sc->hw_hdl, mc); + (void)audio_get_port(sc, mc); } return; @@ -4287,12 +4636,12 @@ KASSERT(mutex_owned(sc->sc_lock)); for (mi.index = 0; ; mi.index++) { - if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) != 0) + if (audio_query_devinfo(sc, &mi) != 0) break; if (mi.type == AUDIO_MIXER_CLASS) continue; mc = &sc->sc_mixer_state[mi.index]; - (void)sc->hw_if->set_port(sc->hw_hdl, mc); + (void)audio_set_port(sc, mc); } if (sc->hw_if->commit_settings) sc->hw_if->commit_settings(sc->hw_hdl); @@ -4347,11 +4696,19 @@ { struct audio_softc *sc = device_private(dv); const struct audio_hw_if *hwp = sc->hw_if; + int n; mutex_enter(sc->sc_lock); + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + if (n == VAUDIOCHANS) + return false; + audio_mixer_capture(sc); mutex_enter(sc->sc_intr_lock); - if (sc->sc_pbus == true) + if (sc->sc_vchan[n]->sc_pbus == true) hwp->halt_output(sc->hw_hdl); if (sc->sc_rbus == true) hwp->halt_input(sc->hw_hdl); @@ -4368,14 +4725,25 @@ audio_resume(device_t dv, const pmf_qual_t *qual) { struct audio_softc *sc = device_private(dv); + struct virtual_channel *vc; + int n; + + for (n = 1; n < VAUDIOCHANS; n++) { + if (sc->sc_audiopid[n] == curproc->p_pid) + break; + } + + if (n == VAUDIOCHANS) + return false; + vc = sc->sc_vchan[n]; mutex_enter(sc->sc_lock); - if (sc->sc_lastinfovalid) - audiosetinfo(sc, &sc->sc_lastinfo); + if (vc->sc_lastinfovalid) + audiosetinfo(sc, &vc->sc_lastinfo, true, n); audio_mixer_restore(sc); mutex_enter(sc->sc_intr_lock); - if ((sc->sc_pbus == true) && !sc->sc_pr.pause) - audiostartp(sc); + if ((vc->sc_pbus == true) && !vc->sc_mpr.pause) + audiostartp(sc, n); if ((sc->sc_rbus == true) && !sc->sc_rr.pause) audiostartr(sc); mutex_exit(sc->sc_intr_lock); @@ -4397,7 +4765,7 @@ if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) { mi.index = sc->sc_outports.master; mi.un.v.delta = 0; - if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0) { + if (audio_query_devinfo(sc, &mi) == 0) { au_get_gain(sc, &sc->sc_outports, &gain, &balance); newgain = gain - mi.un.v.delta; if (newgain < AUDIO_MIN_GAIN) @@ -4420,7 +4788,7 @@ if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) { mi.index = sc->sc_outports.master; mi.un.v.delta = 0; - if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0) { + if (audio_query_devinfo(sc, &mi) == 0) { au_get_gain(sc, &sc->sc_outports, &gain, &balance); newgain = gain + mi.un.v.delta; if (newgain > AUDIO_MAX_GAIN) @@ -4482,4 +4850,219 @@ return audio_get_props(sc) & AUDIO_PROP_CAPTURE ? true : false; } +void +mix_write(void *arg) +{ + struct audio_softc *sc = arg; + struct virtual_channel *vc; + stream_filter_t *filter; + stream_fetcher_t *fetcher; + stream_fetcher_t null_fetcher; + int cc, cc1, blksize, error; + uint8_t *inp; + + vc = sc->sc_vchan[0]; + blksize = vc->sc_mpr.blksize; + cc = blksize; + + if (sc->sc_trigger_started == false) + cc *= 2; + + cc1 = cc; + if (vc->sc_pustream->inp + cc > vc->sc_pustream->end) + cc1 = vc->sc_pustream->end - vc->sc_pustream->inp; + memcpy(vc->sc_pustream->inp, sc->sc_pr.s.start, cc1); + if (cc1 < cc) + memcpy(vc->sc_pustream->start, sc->sc_pr.s.start + cc1, cc - cc1); + memset(sc->sc_pr.s.start, 0, cc); + + inp = vc->sc_pustream->inp; + vc->sc_pustream->inp = audio_stream_add_inp(vc->sc_pustream, inp, cc); + + if (vc->sc_npfilters > 0) { + null_fetcher.fetch_to = null_fetcher_fetch_to; + filter = vc->sc_pfilters[0]; + filter->set_fetcher(filter, &null_fetcher); + fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base; + fetcher->fetch_to(sc, fetcher, &vc->sc_mpr.s, cc); + } + + if (sc->hw_if->trigger_output && sc->sc_trigger_started == false) { + DPRINTF(("%s: call trigger_output\n", __func__)); + error = sc->hw_if->trigger_output(sc->hw_hdl, vc->sc_mpr.s.start, + vc->sc_mpr.s.end, blksize, + audio_pint, (void *)sc, &vc->sc_mpr.s.param); + } else if (sc->hw_if->start_output) { + DPRINTF(("%s: call start_output\n", __func__)); + error = sc->hw_if->start_output(sc->hw_hdl, + __UNCONST(vc->sc_mpr.s.outp), blksize, + audio_pint, (void *)sc); + if (error) { + /* XXX does this really help? */ + DPRINTF(("audio_mix restart failed: %d\n", error)); + audio_clear(sc, 0); + } + } + if (sc->sc_trigger_started == false) { + vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s, vc->sc_mpr.s.outp, + blksize); + } + sc->sc_trigger_started = true; +} + +void +mix_func(struct audio_softc *sc, struct audio_ringbuffer *cb, int n) +{ + int blksize, cc, cc1, cc2, m, resid; + int16_t *orig, *tomix; + + blksize = cb->blksize; + resid = blksize; + if (sc->sc_trigger_started == false) + resid *= 2; + + tomix = __UNCONST(cb->s.outp); + orig = (int16_t *)(sc->sc_pr.s.start); + + while (resid > 0) { + cc = resid; + cc1 = sc->sc_pr.s.end - (uint8_t *)orig; + cc2 = cb->s.end - (uint8_t *)tomix; + if (cc > cc1) + cc = cc1; + if (cc > cc2) + cc = cc2; + + for (m = 0; m < cc / 2; m++) { + orig[m] += (int16_t)((int32_t)(tomix[m] * + ((sc->sc_vchan[n]->sc_swvol + 1) * 16)) / + (sc->sc_opens * VAUDIOCHANS)); + } + + if (&orig[m] >= (int16_t *)sc->sc_pr.s.end) + orig = (int16_t *)sc->sc_pr.s.start; + if (&tomix[m] >= (int16_t *)cb->s.end) + tomix = (int16_t *)cb->s.start; + + resid -= cc; + } +} + +static int +audio_set_port(struct audio_softc *sc, mixer_ctrl_t *mc) +{ + int n; + + KASSERT(mutex_owned(sc->sc_lock)); + + n = (mc->dev - sc->sc_static_nmixer_states) + 1; + + if (mc->dev >= sc->sc_static_nmixer_states && mc->dev < sc->sc_nmixer_states) { + sc->sc_vchan[n]->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + return 0; + } + + return sc->hw_if->set_port(sc->hw_hdl, mc); +} + +static int +audio_get_port(struct audio_softc *sc, mixer_ctrl_t *mc) +{ + int n; + + KASSERT(mutex_owned(sc->sc_lock)); + + n = (mc->dev - sc->sc_static_nmixer_states) + 1; + + if (mc->dev >= sc->sc_static_nmixer_states && mc->dev < sc->sc_nmixer_states) { + mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vchan[n]->sc_swvol; + return 0; + } + + return sc->hw_if->get_port(sc->hw_hdl, mc); +} + +static int +audio_query_devinfo(struct audio_softc *sc, mixer_devinfo_t *di) +{ + char mixLabel[255]; + + KASSERT(mutex_owned(sc->sc_lock)); + + if (di->index >= sc->sc_static_nmixer_states && di->index < + sc->sc_nmixer_states) { + di->mixer_class = AUDIO_OUTPUT_CLASS; + snprintf(mixLabel, sizeof(mixLabel), AudioNdac"%d", di->index + - sc->sc_static_nmixer_states); + strcpy(di->label.name, mixLabel); + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 1; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + } + + return sc->hw_if->query_devinfo(sc->hw_hdl, di); +} + +static int +audio_set_params(struct audio_softc *sc, int setmode, int usemode, + audio_params_t *play, audio_params_t *rec, + stream_filter_list_t *pfil, stream_filter_list_t *rfil, int n) +{ + + struct audio_format norm_format[VAUDIO_NFORMATS] = { + { NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, + 2, AUFMT_STEREO, 1, { 44100 } }, + }; + KASSERT(mutex_owned(sc->sc_lock)); + + norm_format[0].frequency[0] = sc->sc_vchan[0]->sc_pparams.sample_rate; + + if (n == 0 && sc->hw_if->set_params != NULL) { + return sc->hw_if->set_params(sc->hw_hdl, setmode, usemode, + play, rec, pfil, rfil); + } + + if (auconv_set_converter(norm_format, VAUDIO_NFORMATS, AUMODE_PLAY, + play, true, pfil) < 0) + return EINVAL; + if (auconv_set_converter(norm_format, VAUDIO_NFORMATS, AUMODE_RECORD, + rec, true, rfil) < 0) + return EINVAL; + + if (pfil->req_size > 0) + play = &pfil->filters[0].param; + + return 0; +} + +static int +audio_query_encoding(struct audio_softc *sc, struct audio_encoding *ae) +{ + KASSERT(mutex_owned(sc->sc_lock)); + + return auconv_query_encoding(sc->sc_encodings, ae); +} + +void +audio_play_thread(void *v) +{ + struct audio_softc *sc; + + sc = (struct audio_softc *)v; + + mutex_enter(sc->sc_lock); + for (;;) { + cv_wait_sig(&sc->sc_condvar, sc->sc_lock); + if (sc->sc_dying) { + mutex_exit(sc->sc_lock); + kthread_exit(0); + } + + audio_mix(sc); + } + +} + #endif /* NAUDIO > 0 */ Index: src/sys/arch/arm/broadcom/bcm2835_vcaudio.c =================================================================== RCS file: /cvsroot/src/sys/arch/arm/broadcom/bcm2835_vcaudio.c,v retrieving revision 1.10 diff -u -r1.10 bcm2835_vcaudio.c --- src/sys/arch/arm/broadcom/bcm2835_vcaudio.c 28 Jul 2015 21:24:43 -0000 1.10 +++ src/sys/arch/arm/broadcom/bcm2835_vcaudio.c 4 Jun 2016 15:42:49 -0000 @@ -436,7 +436,7 @@ sched = true; } - if (sched) { + if (sched && sc->sc_pint) { intr(intrarg); sc->sc_abytes += sc->sc_pblksize; cv_signal(&sc->sc_datacv);