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 <sys/conf.h>
#include <sys/device.h>
#include <sys/fcntl.h>
+#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/null.h>
#include <sys/systm.h>
@@ -46,6 +48,7 @@
#include <dev/audiobellvar.h>

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 <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/kthread.h>
+
+struct vbell_args {
+       device_t *cookie;
+       u_int pitch;
+       u_int period;
+       u_int volume;
+       bool dying;
+};

-#include <sys/bus.h>
+void bell_thread(void *);

-#include <dev/isa/pcppivar.h>
+#include <dev/audiobellvar.h>

+#include <dev/spkrvar.h>
#include <dev/isa/spkrio.h>

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 <dev/wscons/wsmuxvar.h>

+#ifdef VAUDIO
+#include <dev/spkrvar.h>
+#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 <sys/condvar.h>
+#include <sys/proc.h>

#include <dev/audio_if.h>

@@ -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]    <list_t::filters[1].param>
        *      |  sc_pfilters[n-1]
        *    sc_pr             <list_t::filters[0].param>
+        * (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 <[email protected]>
+ * 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 <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
@@ -177,10 +194,17 @@
#include <sys/audioio.h>
#include <sys/device.h>
#include <sys/intr.h>
+#include <sys/kthread.h>
#include <sys/cpu.h>

#include <dev/audio_if.h>
#include <dev/audiovar.h>
+#include <dev/auconv.h>
+#include <dev/auvolconv.h>
+
+#ifdef VAUDIOSPEAKER
+#include <dev/spkrvar.h>
+#endif

#include <machine/endian.h>

@@ -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);