Module Name:    src
Committed By:   nat
Date:           Fri Feb 10 19:31:42 UTC 2017

Modified Files:
        src/share/man/man4: audio.4
        src/sys/dev: audio.c audiobell.c audiovar.h
        src/sys/sys: audioio.h file.h
        src/usr.bin/audio/ctl: audioctl.1 ctl.c

Log Message:
Audio now uses fdclone, there is no longer a limitation of one audio
instance per process.  Virtual channels are placed in a queue, so there is
no longer a compile time limit of VAUDIOCHANS.

A new sysctl has been added to control multiple user access.
Mixer labels on virtual channels are now prefixed by vchan.

audiobell.c and audioctl have been updated to reflect these changes.

Use of fdclone was posted to tech-kern@ and improvements made.
Multiuser access control changes and the use of a queue were suggessted by
pgoyette@


To generate a diff of this commit:
cvs rdiff -u -r1.73 -r1.74 src/share/man/man4/audio.4
cvs rdiff -u -r1.301 -r1.302 src/sys/dev/audio.c
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/audiobell.c
cvs rdiff -u -r1.49 -r1.50 src/sys/dev/audiovar.h
cvs rdiff -u -r1.35 -r1.36 src/sys/sys/audioio.h
cvs rdiff -u -r1.80 -r1.81 src/sys/sys/file.h
cvs rdiff -u -r1.20 -r1.21 src/usr.bin/audio/ctl/audioctl.1
cvs rdiff -u -r1.41 -r1.42 src/usr.bin/audio/ctl/ctl.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/share/man/man4/audio.4
diff -u src/share/man/man4/audio.4:1.73 src/share/man/man4/audio.4:1.74
--- src/share/man/man4/audio.4:1.73	Mon Dec 12 11:49:27 2016
+++ src/share/man/man4/audio.4	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: audio.4,v 1.73 2016/12/12 11:49:27 nat Exp $
+.\"	$NetBSD: audio.4,v 1.74 2017/02/10 19:31:42 nat Exp $
 .\"
 .\" Copyright (c) 1996 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -99,11 +99,8 @@ and
 are identical.
 .Sh VIRTUAL CHANNELS
 Any process may open a sampling device at a given time.
-There is a constraint of
-.Em one
-device per process and file descriptors may
-.Em not
-be shared between processes.
+Any number of devices per process and file descriptors may be shared between
+processes.
 .Pp
 Virtual channels are converted to a common format, signed linear encoding,
 frequency channels and precision.
@@ -116,6 +113,7 @@ variables.
 .It hw.driverN.frequency
 .It hw.driverN.channels
 .It hw.driverN.saturate
+.It hw.driverN.multiuser
 .El
 .Pp
 Where
@@ -141,14 +139,28 @@ If set to false the channels are
 .Em divided
 evenly in volume with respect to the master volume.
 .Pp
+An additional
+.Xr sysctl 8
+variable determines if multiple users are allowed to access the sampling
+device, hw.driverN.multiuser.
+.Pp
+By default it is set to false.
+This means that the sampling device may be only used by
+.Em one
+user at a time.
+Other users (except root) attempting to open the sampling device will be
+denied.
+.Pp
+If set to true, all users may access the sampling device at any time.
+.Pp
 Each virtual channel has a corresponding mixer:
 .Bl -tag -width -compact
-.It outputs.dacN  Output volume
-.It inputs.micN   Recording volume
+.It vchan.dacN  Output volume
+.It vchan.micN  Recording volume
 .El
 .Pp
 Where N is the virtual channel number.
-e.g ouputs.dac0 controlling playback volume and outputs.mic0 controlling
+e.g vchan.dac0 controlling playback volume and vchan.mic0 controlling
 recording volume for the first virtual channel.
 .Pp
 On a half-duplex device, writes while recording is in progress will be
@@ -233,17 +245,8 @@ The following
 commands are supported on the sample devices:
 .Pp
 .Bl -tag -width indent
-.It Dv AUDIO_SETPROC (struct audio_pid)
-This command will select the audio device opened by pid.
-.Bd -literal
-struct audio_pid {
-        pid_t   pid;
-        lwpid_t lwpid;
-};
-.Ed
-.Pp
-Currently the lpwid value is
-.Em ignored .
+.It Dv AUDIO_SETPROC (int)
+This command will select the audio channel for subsequent ioctl calls.
 .It Dv AUDIO_FLUSH
 This command stops all playback and recording, clears all queued
 buffers, resets error counters, and restarts recording and playback as

Index: src/sys/dev/audio.c
diff -u src/sys/dev/audio.c:1.301 src/sys/dev/audio.c:1.302
--- src/sys/dev/audio.c:1.301	Tue Feb  7 02:05:26 2017
+++ src/sys/dev/audio.c	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio.c,v 1.301 2017/02/07 02:05:26 nat Exp $	*/
+/*	$NetBSD: audio.c,v 1.302 2017/02/10 19:31:42 nat Exp $	*/
 
 /*-
  * Copyright (c) 2016 Nathanial Sloss <[email protected]>
@@ -148,7 +148,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.301 2017/02/07 02:05:26 nat Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.302 2017/02/10 19:31:42 nat Exp $");
 
 #include "audio.h"
 #if NAUDIO > 0
@@ -157,12 +157,17 @@ __KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
 #include <sys/vnode.h>
 #include <sys/select.h>
 #include <sys/poll.h>
+#include <sys/kauth.h>
 #include <sys/kmem.h>
 #include <sys/malloc.h>
 #include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
 #include <sys/systm.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
@@ -203,88 +208,103 @@ int	audio_idle_timeout = 30;
 
 int	audio_blk_ms = AUDIO_BLK_MS;
 
-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);
-int	audio_read(struct audio_softc *, struct uio *, int);
-int	audio_write(struct audio_softc *, struct uio *, int);
+int	audiosetinfo(struct audio_softc *, struct audio_info *, bool,
+		     struct virtual_channel *);
+int	audiogetinfo(struct audio_softc *, struct audio_info *, int,
+		     struct virtual_channel *);
+
+int	audio_open(dev_t, struct audio_softc *, int, int, struct lwp *,
+		   struct file **);
+int	audio_close(struct audio_softc *, int, struct audio_chan *);
+int	audio_read(struct audio_softc *, struct uio *, int,
+		   struct virtual_channel *);
+int	audio_write(struct audio_softc *, struct uio *, int,
+		    struct virtual_channel *);
 int	audio_ioctl(dev_t, struct audio_softc *, u_long, void *, int,
-		    struct lwp *);
-int	audio_poll(struct audio_softc *, int, struct lwp *);
-int	audio_kqfilter(struct audio_softc *, struct knote *);
-paddr_t	audio_mmap(struct audio_softc *, off_t, int);
-
-int	mixer_open(dev_t, struct audio_softc *, int, int, struct lwp *);
-int	mixer_close(struct audio_softc *, int, int, struct lwp *);
+		    struct lwp *, struct audio_chan *);
+int	audio_poll(struct audio_softc *, int, struct lwp *,
+		   struct virtual_channel *);
+int	audio_kqfilter(struct audio_chan *, struct knote *);
+paddr_t audiommap(dev_t, off_t, int, struct virtual_channel *);
+paddr_t audio_mmap(struct audio_softc *, off_t, int, struct virtual_channel *);
+int	audio_fop_mmap(struct file *, off_t *, size_t, int, int *, int *,
+			     struct uvm_object **, int *);
+
+int	mixer_open(dev_t, struct audio_softc *, int, int, struct lwp *,
+		   struct file **);
+int	mixer_close(struct audio_softc *, int, struct audio_chan *);
 int	mixer_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *);
 static	void mixer_remove(struct audio_softc *);
 static	void mixer_signal(struct audio_softc *);
+static	void grow_mixer_states(struct audio_softc *, int);
+static	void shrink_mixer_states(struct audio_softc *, int);
 
-void	audio_init_record(struct audio_softc *, int);
-void	audio_init_play(struct audio_softc *, int);
-int	audiostartr(struct audio_softc *, int);
-int	audiostartp(struct audio_softc *, int);
+void	audio_init_record(struct audio_softc *, struct virtual_channel *);
+void	audio_init_play(struct audio_softc *, struct virtual_channel *);
+int	audiostartr(struct audio_softc *, struct virtual_channel *);
+int	audiostartp(struct audio_softc *, struct virtual_channel *);
 void	audio_rint(void *);
 void	audio_pint(void *);
 void	audio_mix(void *);
 void	audio_upmix(void *);
 void	audio_play_thread(void *);
 void	audio_rec_thread(void *);
-void	recswvol_func(struct audio_softc *, struct audio_ringbuffer *, int,
-		      size_t);
-void	recswvol_func8(struct audio_softc *, struct audio_ringbuffer *, int,
-		       size_t);
-void	recswvol_func16(struct audio_softc *, struct audio_ringbuffer *, int,
-			size_t);
-void	recswvol_func32(struct audio_softc *, struct audio_ringbuffer *, int,
-			size_t);
+void	recswvol_func(struct audio_softc *, struct audio_ringbuffer *,
+		      size_t, struct virtual_channel *);
+void	recswvol_func8(struct audio_softc *, struct audio_ringbuffer *,
+		       size_t, struct virtual_channel *);
+void	recswvol_func16(struct audio_softc *, struct audio_ringbuffer *,
+			size_t, struct virtual_channel *);
+void	recswvol_func32(struct audio_softc *, struct audio_ringbuffer *,
+			size_t, struct virtual_channel *);
 void	saturate_func(struct audio_softc *);
 void	saturate_func8(struct audio_softc *);
 void	saturate_func16(struct audio_softc *);
 void	saturate_func32(struct audio_softc *);
-void	mix_func(struct audio_softc *, struct audio_ringbuffer *, int);
-void	mix_func8(struct audio_softc *, struct audio_ringbuffer *, int);
-void	mix_func16(struct audio_softc *, struct audio_ringbuffer *, int);
-void	mix_func32(struct audio_softc *, struct audio_ringbuffer *, int);
+void	mix_func(struct audio_softc *, struct audio_ringbuffer *,
+		 struct virtual_channel *);
+void	mix_func8(struct audio_softc *, struct audio_ringbuffer *,
+		 struct virtual_channel *);
+void	mix_func16(struct audio_softc *, struct audio_ringbuffer *,
+		 struct virtual_channel *);
+void	mix_func32(struct audio_softc *, struct audio_ringbuffer *,
+		 struct virtual_channel *);
 void	mix_write(void *);
 void	mix_read(void *);
 int	audio_check_params(struct audio_params *);
 
-void	audio_calc_blksize(struct audio_softc *, int, int);
+void	audio_calc_blksize(struct audio_softc *, int, struct virtual_channel *);
 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 *, 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);
+int	audio_initbufs(struct audio_softc *, struct virtual_channel *);
+void	audio_calcwater(struct audio_softc *, struct virtual_channel *);
+int	audio_drain(struct audio_softc *, struct audio_chan *);
+void	audio_clear(struct audio_softc *, struct virtual_channel *);
+void	audio_clear_intr_unlocked(struct audio_softc *sc,
+				  struct virtual_channel *);
 static inline void
 	audio_pint_silence(struct audio_softc *, struct audio_ringbuffer *,
-			   uint8_t *, int, int);
+			   uint8_t *, int, struct virtual_channel *);
 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 *, int);
+			      stream_filter_list_t *, struct virtual_channel *);
 static int audio_setup_rfilters(struct audio_softc *, const audio_params_t *,
-				stream_filter_list_t *, int);
+			      stream_filter_list_t *, struct virtual_channel *);
 static void audio_stream_dtor(audio_stream_t *);
 static int audio_stream_ctor(audio_stream_t *, const audio_params_t *, int);
-static void stream_filter_list_append
-	(stream_filter_list_t *, stream_filter_factory_t,
-	 const audio_params_t *);
-static void stream_filter_list_prepend
-	(stream_filter_list_t *, stream_filter_factory_t,
-	 const audio_params_t *);
-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);
+static void stream_filter_list_append(stream_filter_list_t *,
+		stream_filter_factory_t, const audio_params_t *);
+static void stream_filter_list_prepend(stream_filter_list_t *,
+	    	stream_filter_factory_t, const audio_params_t *);
+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,
+						struct virtual_channel *);
 static int audio_sysctl_frequency(SYSCTLFN_PROTO);
 static int audio_sysctl_precision(SYSCTLFN_PROTO);
 static int audio_sysctl_channels(SYSCTLFN_PROTO);
@@ -321,8 +341,14 @@ static int	audio_enter(dev_t, krw_t, str
 static void	audio_exit(struct audio_softc *);
 static int	audio_waitio(struct audio_softc *, kcondvar_t *);
 
-#define AUDIO_OUTPUT_CLASS 0
-#define AUDIO_INPUT_CLASS 1
+int audioclose(struct file *);
+int audioread(struct file *, off_t *, struct uio *, kauth_cred_t, int);
+int audiowrite(struct file *, off_t *, struct uio *, kauth_cred_t, int);
+int audioioctl(struct file *, u_long, void *);
+int audiopoll(struct file *, int);
+int audiofcntl(struct file *, u_int, void *);
+int audiokqfilter(struct file *, struct knote *);
+int audiostat(struct file *, struct stat *);
 
 struct portname {
 	const char *name;
@@ -355,13 +381,14 @@ 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_set_params (struct audio_softc *, int, int,
+		 audio_params_t *, audio_params_t *,
+		 stream_filter_list_t *, stream_filter_list_t *,
+		 struct virtual_channel *);
 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);
+	(struct audio_softc *, u_int, const struct audio_format *);
 static int vchan_autoconfig(struct audio_softc *);
 int	au_get_lr_value(struct audio_softc *, mixer_ctrl_t *, int *, int *);
 int	au_set_lr_value(struct audio_softc *, mixer_ctrl_t *, int, int);
@@ -380,28 +407,36 @@ static int	uio_fetcher_fetch_to(struct a
 static int	null_fetcher_fetch_to(struct audio_softc *, stream_fetcher_t *,
 				      audio_stream_t *, int);
 
+int audiobellopen(dev_t, int, int, struct lwp *, struct file **);
+
 dev_type_open(audioopen);
-dev_type_close(audioclose);
-dev_type_read(audioread);
-dev_type_write(audiowrite);
-dev_type_ioctl(audioioctl);
-dev_type_poll(audiopoll);
-dev_type_mmap(audiommap);
-dev_type_kqfilter(audiokqfilter);
 
 const struct cdevsw audio_cdevsw = {
 	.d_open = audioopen,
-	.d_close = audioclose,
-	.d_read = audioread,
-	.d_write = audiowrite,
-	.d_ioctl = audioioctl,
+	.d_close = noclose,
+	.d_read = noread,
+	.d_write = nowrite,
+	.d_ioctl = noioctl,
 	.d_stop = nostop,
 	.d_tty = notty,
-	.d_poll = audiopoll,
-	.d_mmap = audiommap,
-	.d_kqfilter = audiokqfilter,
+	.d_poll = nopoll,
+	.d_mmap = nommap,
+	.d_kqfilter = nokqfilter,
 	.d_discard = nodiscard,
-	.d_flag = D_MCLOSE | D_MPSAFE
+	.d_flag = D_OTHER | D_MPSAFE
+};
+
+const struct fileops audio_fileops = {
+	.fo_read = audioread,
+	.fo_write = audiowrite,
+	.fo_ioctl = audioioctl,
+	.fo_fcntl = audiofcntl,
+	.fo_stat = audiostat,
+	.fo_poll = audiopoll,
+	.fo_close = audioclose,
+	.fo_mmap = audio_fop_mmap,
+	.fo_kqfilter = audiokqfilter,
+	.fo_restart = fnullop_restart
 };
 
 /* The default audio mode: 8 kHz mono mu-law */
@@ -441,6 +476,7 @@ audioattach(device_t parent, device_t se
 	struct audio_softc *sc;
 	struct audio_attach_args *sa;
 	struct virtual_channel *vc;
+	struct audio_chan *chan;
 	const struct audio_hw_if *hwp;
 	const struct sysctlnode *node;
 	void *hdlp;
@@ -487,10 +523,11 @@ audioattach(device_t parent, device_t se
 	sc->sc_trigger_started = false;
 	sc->sc_rec_started = false;
 	sc->sc_dying = 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));
-	memset(sc->sc_despid, -1, sizeof(sc->sc_despid));
+	chan = kmem_zalloc(sizeof(struct audio_chan), KM_SLEEP);
+	vc = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
+	chan->vc = vc;
+	SIMPLEQ_INIT(&sc->sc_audiochan);
+	SIMPLEQ_INSERT_HEAD(&sc->sc_audiochan, chan, entries);
 	vc->sc_open = 0;
 	vc->sc_mode = 0;
 	vc->sc_npfilters = 0;
@@ -575,13 +612,16 @@ audioattach(device_t parent, device_t se
 		if (error)
 			goto bad_play;
 
-		error = audio_alloc_ring(sc, &sc->sc_vchan[0]->sc_mpr,
+		error = audio_alloc_ring(sc, &vc->sc_mpr,
 	    	    AUMODE_PLAY, AU_RING_SIZE);
 bad_play:
 		if (error) {
 			if (sc->sc_pr.s.start != NULL)
 				audio_free_ring(sc, &sc->sc_pr);
 			sc->hw_if = NULL;
+			if (vc->sc_mpr.s.start != 0)
+				audio_free_ring(sc, &vc->sc_mpr);
+			sc->hw_if = NULL;
 			aprint_error_dev(sc->sc_dev, "could not allocate play "
 			    "buffer\n");
 			return;
@@ -593,16 +633,16 @@ bad_play:
 		if (error)
 			goto bad_rec;
 
-		error = audio_alloc_ring(sc, &sc->sc_vchan[0]->sc_mrr,
+		error = audio_alloc_ring(sc, &vc->sc_mrr,
 		    AUMODE_RECORD, AU_RING_SIZE);
 bad_rec:
 		if (error) {
-			if (sc->sc_vchan[0]->sc_mrr.s.start != NULL)
-				audio_free_ring(sc, &sc->sc_vchan[0]->sc_mrr);
+			if (vc->sc_mrr.s.start != NULL)
+				audio_free_ring(sc, &vc->sc_mrr);
 			if (sc->sc_pr.s.start != NULL)
 				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);
+			if (vc->sc_mpr.s.start != 0)
+				audio_free_ring(sc, &vc->sc_mpr);
 			sc->hw_if = NULL;
 			aprint_error_dev(sc->sc_dev, "could not allocate record"
 			   " buffer\n");
@@ -612,6 +652,7 @@ bad_rec:
 
 	sc->sc_lastgain = 128;
 	sc->sc_saturate = true;
+	sc->sc_multiuser = false;
 	mutex_exit(sc->sc_lock);
 
 	error = vchan_autoconfig(sc);
@@ -620,8 +661,8 @@ bad_rec:
 		    "failed\n", __func__);
 	}
 
-	sc->sc_pr.blksize = sc->sc_vchan[0]->sc_mpr.blksize;
-	sc->sc_rr.blksize = sc->sc_vchan[0]->sc_mrr.blksize;
+	sc->sc_pr.blksize = vc->sc_mpr.blksize;
+	sc->sc_rr.blksize = vc->sc_mrr.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,
@@ -672,10 +713,11 @@ bad_rec:
 	mutex_exit(sc->sc_lock);
 
 	/* Allocate save area.  Ensure non-zero allocation. */
-	sc->sc_nmixer_states = mi.index;
 	sc->sc_static_nmixer_states = mi.index;
+	sc->sc_static_nmixer_states++;
+	sc->sc_nmixer_states = sc->sc_static_nmixer_states;
 	sc->sc_mixer_state = kmem_zalloc(sizeof(mixer_ctrl_t) *
-	    (sc->sc_nmixer_states + (VAUDIOCHANS + 1) * 2), KM_SLEEP);
+	    (sc->sc_nmixer_states + 1), KM_SLEEP);
 
 	/*
 	 * This is where we assign each control in the "audio" model, to the
@@ -811,6 +853,15 @@ bad_rec:
 			&sc->sc_saturate, 0,
 			CTL_HW, node->sysctl_num,
 			CTL_CREATE, CTL_EOL);
+
+		sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+			CTLFLAG_READWRITE,
+			CTLTYPE_BOOL, "multiuser",
+			SYSCTL_DESCR("allow multiple user acess"),
+			NULL, 0,
+			&sc->sc_multiuser, 0,
+			CTL_HW, node->sysctl_num,
+			CTL_CREATE, CTL_EOL);
 	}
 
 	selinit(&sc->sc_rsel);
@@ -869,7 +920,8 @@ int
 audiodetach(device_t self, int flags)
 {
 	struct audio_softc *sc;
-	int maj, mn, i, n, rc;
+	struct audio_chan *chan;
+	int maj, mn, i, rc;
 
 	sc = device_private(self);
 	DPRINTF(("audio_detach: sc=%p flags=%d\n", sc, flags));
@@ -927,32 +979,38 @@ audiodetach(device_t self, int flags)
 	pmf_device_deregister(self);
 
 	/* free resources */
-	for (n = 0; n < VAUDIOCHANS; n++) {
-		if (n != 0 && sc->sc_audiopid[n].pid == -1)
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (chan == NULL)
+			break;
+
+		if (chan->chan == MIXER_INUSE)
 			continue;
-		audio_free_ring(sc, &sc->sc_vchan[n]->sc_mpr);
-		audio_free_ring(sc, &sc->sc_vchan[n]->sc_mrr);
+		audio_free_ring(sc, &chan->vc->sc_mpr);
+		audio_free_ring(sc, &chan->vc->sc_mrr);
 	}
 	audio_free_ring(sc, &sc->sc_pr);
 	audio_free_ring(sc, &sc->sc_rr);
-	for (n = 0; n < VAUDIOCHANS; n++) {
-		if (n != 0 && sc->sc_audiopid[n].pid == -1)
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (chan == NULL)
+			break;
+
+		if (chan->chan == MIXER_INUSE)
 			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;
-
-		for (i = 0; i < sc->sc_vchan[n]->sc_nrfilters; i++) {
-			sc->sc_vchan[n]->sc_rfilters[i]->dtor
-			    (sc->sc_vchan[n]->sc_rfilters[i]);
-			sc->sc_vchan[n]->sc_rfilters[i] = NULL;
-			audio_stream_dtor(&sc->sc_vchan[n]->sc_rstreams[i]);
+		for (i = 0; i < chan->vc->sc_npfilters; i++) {
+			chan->vc->sc_pfilters[i]->dtor
+			    (chan->vc->sc_pfilters[i]);
+			chan->vc->sc_pfilters[i] = NULL;
+			audio_stream_dtor(&chan->vc->sc_pstreams[i]);
+		}
+		chan->vc->sc_npfilters = 0;
+
+		for (i = 0; i < chan->vc->sc_nrfilters; i++) {
+			chan->vc->sc_rfilters[i]->dtor
+			    (chan->vc->sc_rfilters[i]);
+			chan->vc->sc_rfilters[i] = NULL;
+			audio_stream_dtor(&chan->vc->sc_rstreams[i]);
 		}
-		sc->sc_vchan[n]->sc_nrfilters = 0;
+		chan->vc->sc_nrfilters = 0;
 	}
 
 	auconv_delete_encodings(sc->sc_encodings);
@@ -966,7 +1024,12 @@ audiodetach(device_t self, int flags)
 		sc->sc_sih_wr = NULL;
 	}
 
-	kmem_free(sc->sc_vchan[0], sizeof(struct virtual_channel));
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	kmem_free(chan->vc, sizeof(struct virtual_channel));
+	SIMPLEQ_REMOVE(&sc->sc_audiochan, chan, audio_chan, entries);
+	kmem_free(chan, sizeof(struct audio_chan));
+	kmem_free(sc->sc_mixer_state, sizeof(mixer_ctrl_t) *
+	    (sc->sc_nmixer_states + 1));
 
 #ifdef AUDIO_PM_IDLE
 	callout_destroy(&sc->sc_idle_counter);
@@ -1094,29 +1157,26 @@ void	audio_print_params(const char *, st
 void
 audio_printsc(struct audio_softc *sc)
 {
-	int n;
-
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-
-	if (n == VAUDIOCHANS)
+	struct audio_chan *chan;
+	
+	chan = SIMPLEQ_FIRST(sc->sc_audiochan);
+	
+	if (chan == NULL)
 		return;
 
 	printf("hwhandle %p hw_if %p ", sc->hw_hdl, sc->hw_if);
-	printf("open 0x%x mode 0x%x\n", sc->sc_vchan[n]->sc_open,
-	    sc->sc_vchan[n]->sc_mode);
+	printf("open 0x%x mode 0x%x\n", chan->vc->sc_open,
+	    chan->vc->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_vchan[n]->sc_mrr.s),
-	       audio_stream_get_used(&sc->sc_vchan[n]->sc_mpr.s));
-	printf("rbus 0x%x pbus 0x%x ", sc->sc_vchan[n]->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);
+	       audio_stream_get_used(&chan->vc->sc_mrr.s),
+	       audio_stream_get_used(&chan->vc->sc_mpr.s));
+	printf("rbus 0x%x pbus 0x%x ", chan->vc->sc_rbus,
+	    chan->vc->sc_pbus);
+	printf("blksize %d", chan->vc->sc_mpr.blksize);
+	printf("hiwat %d lowat %d\n", chan->vc->sc_mpr.usedhigh,
+	    chan->vc->sc_mpr.usedlow);
 }
 
 void
@@ -1132,8 +1192,10 @@ audio_alloc_ring(struct audio_softc *sc,
 		 int direction, size_t bufsize)
 {
 	const struct audio_hw_if *hw;
+	struct audio_chan *chan;
 	void *hdl;
 
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
 	hw = sc->hw_if;
 	hdl = sc->hw_hdl;
 	/*
@@ -1145,8 +1207,7 @@ audio_alloc_ring(struct audio_softc *sc,
 	if (hw->round_buffersize) {
 		bufsize = hw->round_buffersize(hdl, direction, bufsize);
 	}
-	if (hw->allocm && (r == &sc->sc_vchan[0]->sc_mpr ||
-	    r == &sc->sc_vchan[0]->sc_mrr))
+	if (hw->allocm && (r == &chan->vc->sc_mpr || r == &chan->vc->sc_mrr))
 		r->s.start = hw->allocm(hdl, direction, bufsize);
 	else
 		r->s.start = kmem_zalloc(bufsize, KM_SLEEP);
@@ -1160,11 +1221,15 @@ audio_alloc_ring(struct audio_softc *sc,
 void
 audio_free_ring(struct audio_softc *sc, struct audio_ringbuffer *r)
 {
+	struct audio_chan *chan;
+
 	if (r->s.start == NULL)
 		return;
 
-	if (sc->hw_if->freem && (r == &sc->sc_vchan[0]->sc_mpr ||
-	    r == &sc->sc_vchan[0]->sc_mrr))
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+
+	if (sc->hw_if->freem && (r == &chan->vc->sc_mpr ||
+						r == &chan->vc->sc_mrr))
 		sc->hw_if->freem(sc->hw_hdl, r->s.start, r->s.bufsize);
 	else
 		kmem_free(r->s.start, r->s.bufsize);
@@ -1173,17 +1238,15 @@ audio_free_ring(struct audio_softc *sc, 
 
 static int
 audio_setup_pfilters(struct audio_softc *sc, const audio_params_t *pp,
-		     stream_filter_list_t *pfilters, int m)
+		     stream_filter_list_t *pfilters, struct virtual_channel *vc)
 {
 	stream_filter_t *pf[AUDIO_MAX_FILTERS], *of[AUDIO_MAX_FILTERS];
 	audio_stream_t ps[AUDIO_MAX_FILTERS], os[AUDIO_MAX_FILTERS];
-	struct virtual_channel *vc;
 	const audio_params_t *from_param;
 	audio_params_t *to_param;
 	int i, n, onfilters;
 
 	KASSERT(mutex_owned(sc->sc_lock));
-	vc = sc->sc_vchan[m];
 
 	/* Construct new filters. */
 	memset(pf, 0, sizeof(pf));
@@ -1255,17 +1318,15 @@ audio_setup_pfilters(struct audio_softc 
 
 static int
 audio_setup_rfilters(struct audio_softc *sc, const audio_params_t *rp,
-		     stream_filter_list_t *rfilters, int m)
+		     stream_filter_list_t *rfilters, struct virtual_channel *vc)
 {
 	stream_filter_t *rf[AUDIO_MAX_FILTERS], *of[AUDIO_MAX_FILTERS];
 	audio_stream_t rs[AUDIO_MAX_FILTERS], os[AUDIO_MAX_FILTERS];
-	struct virtual_channel *vc;
 	const audio_params_t *to_param;
 	audio_params_t *from_param;
 	int i, onfilters;
 
 	KASSERT(mutex_owned(sc->sc_lock));
-	vc = sc->sc_vchan[m];
 
 	/* Construct new filters. */
 	memset(rf, 0, sizeof(rf));
@@ -1470,9 +1531,32 @@ audio_waitio(struct audio_softc *sc, kco
 }
 
 int
+audiobellopen(dev_t dev, int flags, int ifmt, struct lwp *l,
+	      struct file **fp)
+{
+	struct audio_softc *sc;
+	int error;
+
+	if ((error = audio_enter(dev, RW_WRITER, &sc)) != 0)
+		return error;
+	device_active(sc->dev, DVA_SYSTEM);
+	switch (AUDIODEV(dev)) {
+	case AUDIO_DEVICE:
+		error = audio_open(dev, sc, flags, ifmt, l, fp);
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	audio_exit(sc);
+
+	return error;
+}
+int
 audioopen(dev_t dev, int flags, int ifmt, struct lwp *l)
 {
 	struct audio_softc *sc;
+	struct file *fp;
 	int error;
 
 	if ((error = audio_enter(dev, RW_WRITER, &sc)) != 0)
@@ -1482,10 +1566,10 @@ audioopen(dev_t dev, int flags, int ifmt
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
 	case AUDIOCTL_DEVICE:
-		error = audio_open(dev, sc, flags, ifmt, l);
+		error = audio_open(dev, sc, flags, ifmt, l, &fp);
 		break;
 	case MIXER_DEVICE:
-		error = mixer_open(dev, sc, flags, ifmt, l);
+		error = mixer_open(dev, sc, flags, ifmt, l, &fp);
 		break;
 	default:
 		error = ENXIO;
@@ -1497,52 +1581,65 @@ audioopen(dev_t dev, int flags, int ifmt
 }
 
 int
-audioclose(dev_t dev, int flags, int ifmt, struct lwp *l)
+audioclose(struct file *fp)
 {
 	struct audio_softc *sc;
-	int error, n;
+	struct audio_chan *chan;
+	int error;
+	dev_t dev;
+
+	chan = fp->f_audioctx;
+	if (chan == NULL)	/* XXX:NS Why is this needed. */
+		return EIO;
+
+	dev = chan->dev;
 
 	if ((error = audio_enter(dev, RW_WRITER, &sc)) != 0)
 		return error;
+
 	device_active(sc->dev, DVA_SYSTEM);
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
 	case AUDIOCTL_DEVICE:
-		for (n = 1; n < VAUDIOCHANS; n++) {
-			if (sc->sc_audiopid[n].pid == curproc->p_pid)
-				break;
-		}
-		if (n == VAUDIOCHANS) {
-			error = EIO;
-			break;	
-		}
-		error = audio_close(sc, flags, ifmt, l, n);
+		error = audio_close(sc, fp->f_flag, chan);
 		break;
 	case MIXER_DEVICE:
-		error = mixer_close(sc, flags, ifmt, l);
+		error = mixer_close(sc, fp->f_flag, chan);
 		break;
 	default:
 		error = ENXIO;
 		break;
 	}
+	if (error == 0) {
+		kmem_free(fp->f_audioctx, sizeof(struct audio_chan));
+		fp->f_audioctx = NULL;
+	}
+
 	audio_exit(sc);
 
 	return error;
 }
 
 int
-audioread(dev_t dev, struct uio *uio, int ioflag)
+audioread(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
+	  int ioflag)
 {
 	struct audio_softc *sc;
+	struct virtual_channel *vc;
 	int error;
+	dev_t dev;
+
+	dev = fp->f_audioctx->dev;
 
 	if ((error = audio_enter(dev, RW_READER, &sc)) != 0)
 		return error;
+
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		error = audio_read(sc, uio, ioflag);
+		vc = fp->f_audioctx->vc;
+		error = audio_read(sc, uio, ioflag, vc);
 		break;
 	case AUDIOCTL_DEVICE:
 	case MIXER_DEVICE:
@@ -1558,17 +1655,24 @@ audioread(dev_t dev, struct uio *uio, in
 }
 
 int
-audiowrite(dev_t dev, struct uio *uio, int ioflag)
+audiowrite(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
+	   int ioflag)
 {
 	struct audio_softc *sc;
+	struct virtual_channel *vc;
 	int error;
+	dev_t dev;
+
+	dev = fp->f_audioctx->dev;
 
 	if ((error = audio_enter(dev, RW_READER, &sc)) != 0)
 		return error;
+
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		error = audio_write(sc, uio, ioflag);
+		vc = fp->f_audioctx->vc;
+		error = audio_write(sc, uio, ioflag, vc);
 		break;
 	case AUDIOCTL_DEVICE:
 	case MIXER_DEVICE:
@@ -1584,11 +1688,17 @@ audiowrite(dev_t dev, struct uio *uio, i
 }
 
 int
-audioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
+audioioctl(struct file *fp, u_long cmd, void *addr)
 {
 	struct audio_softc *sc;
+	struct audio_chan *chan;
+	struct lwp *l = curlwp;
 	int error;
 	krw_t rw;
+	dev_t dev;
+
+	chan = fp->f_audioctx;
+	dev = chan->dev;
 
 	/* Figure out which lock type we need. */
 	switch (cmd) {
@@ -1605,18 +1715,21 @@ audioioctl(dev_t dev, u_long cmd, void *
 
 	if ((error = audio_enter(dev, rw, &sc)) != 0)
 		return error;
+	chan = fp->f_audioctx;
+
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
 	case AUDIOCTL_DEVICE:
 		device_active(sc->dev, DVA_SYSTEM);
 		if (IOCGROUP(cmd) == IOCGROUP(AUDIO_MIXER_READ))
-			error = mixer_ioctl(sc, cmd, addr, flag, l);
+			error = mixer_ioctl(sc, cmd, addr, fp->f_flag, l);
 		else
-			error = audio_ioctl(dev, sc, cmd, addr, flag, l);
+			error = audio_ioctl(dev, sc, cmd, addr, fp->f_flag, l,
+			    chan);
 		break;
 	case MIXER_DEVICE:
-		error = mixer_ioctl(sc, cmd, addr, flag, l);
+		error = mixer_ioctl(sc, cmd, addr, fp->f_flag, l);
 		break;
 	default:
 		error = ENXIO;
@@ -1628,10 +1741,35 @@ audioioctl(dev_t dev, u_long cmd, void *
 }
 
 int
-audiopoll(dev_t dev, int events, struct lwp *l)
+audiofcntl(struct file *fp, u_int cmd, void *data)
+{
+
+	return EOPNOTSUPP;
+}
+
+int
+audiostat(struct file *fp, struct stat *st)
+{
+	memset(st, 0, sizeof(*st));
+
+	st->st_dev = fp->f_audioctx->dev;
+
+	st->st_uid = kauth_cred_geteuid(fp->f_cred);
+	st->st_gid = kauth_cred_getegid(fp->f_cred);
+	st->st_mode = S_IFCHR;
+	return 0;
+}
+
+int
+audiopoll(struct file *fp, int events)
 {
 	struct audio_softc *sc;
+	struct virtual_channel *vc;
+	struct lwp *l = curlwp;
 	int revents;
+	dev_t dev;
+
+	dev = fp->f_audioctx->dev;
 
 	/* Don't bother with device level lock here. */
 	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
@@ -1642,10 +1780,12 @@ audiopoll(dev_t dev, int events, struct 
 		mutex_exit(sc->sc_lock);
 		return EIO;
 	}
+
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		revents = audio_poll(sc, events, l);
+		vc = fp->f_audioctx->vc;
+		revents = audio_poll(sc, events, l, vc);
 		break;
 	case AUDIOCTL_DEVICE:
 	case MIXER_DEVICE:
@@ -1661,10 +1801,15 @@ audiopoll(dev_t dev, int events, struct 
 }
 
 int
-audiokqfilter(dev_t dev, struct knote *kn)
+audiokqfilter(struct file *fp, struct knote *kn)
 {
 	struct audio_softc *sc;
 	int rv;
+	struct audio_chan *chan;
+	dev_t dev;
+
+	chan = fp->f_audioctx;
+	dev = chan->dev;
 
 	/* Don't bother with device level lock here. */
 	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
@@ -1678,7 +1823,7 @@ audiokqfilter(dev_t dev, struct knote *k
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		rv = audio_kqfilter(sc, kn);
+		rv = audio_kqfilter(chan, kn);
 		break;
 	case AUDIOCTL_DEVICE:
 	case MIXER_DEVICE:
@@ -1692,12 +1837,35 @@ audiokqfilter(dev_t dev, struct knote *k
 	return rv;
 }
 
+/* XXX:NS mmap is disabled. */
+int
+audio_fop_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp,
+	     int *advicep, struct uvm_object **uobjp, int *maxprotp)
+{
+	struct audio_chan *chan;
+	struct virtual_channel *vc;
+	dev_t dev;
+
+	return -1;
+
+	chan = fp->f_audioctx;
+	dev = chan->dev;
+	vc = chan->vc;
+
+	*offp = audiommap(dev, *offp, prot, vc);
+	*maxprotp = prot;
+	*advicep = UVM_ADV_RANDOM;
+	return -1;
+}
+
 paddr_t
-audiommap(dev_t dev, off_t off, int prot)
+audiommap(dev_t dev, off_t off, int prot, struct virtual_channel *vc)
 {
 	struct audio_softc *sc;
 	paddr_t error;
 
+	return -1;
+
 	/*
 	 * Acquire a reader lock.  audio_mmap() will drop sc_lock
 	 * in order to allow the device's mmap routine to sleep.
@@ -1707,10 +1875,11 @@ audiommap(dev_t dev, off_t off, int prot
 	if ((error = audio_enter(dev, RW_READER, &sc)) != 0)
 		return 1;
 	device_active(sc->dev, DVA_SYSTEM); /* XXXJDM */
+
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		error = audio_mmap(sc, off, prot);
+		error = audio_mmap(sc, off, prot, vc);
 		break;
 	case AUDIOCTL_DEVICE:
 	case MIXER_DEVICE:
@@ -1765,20 +1934,24 @@ audio_init_ringbuffer(struct audio_softc
 }
 
 int
-audio_initbufs(struct audio_softc *sc, int n)
+audio_initbufs(struct audio_softc *sc, struct virtual_channel *vc)
 {
 	const struct audio_hw_if *hw;
-	struct virtual_channel *vc;
+	struct audio_chan *chan;
 	int error;
 
-	DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_vchan[n]->sc_mode));
-	vc = sc->sc_vchan[0];
+	if (vc == NULL) {
+		chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+		vc = chan->vc;
+	}
+
+	DPRINTF(("audio_initbufs: mode=0x%x\n", vc->sc_mode));
 	hw = sc->hw_if;
-	if (audio_can_capture(sc) || (sc->sc_vchan[n]->sc_open & AUOPEN_READ)) {
-		audio_init_ringbuffer(sc, &sc->sc_vchan[n]->sc_mrr,
+	if (audio_can_capture(sc) || (vc->sc_open & AUOPEN_READ)) {
+		audio_init_ringbuffer(sc, &vc->sc_mrr,
 		    AUMODE_RECORD);
 		if (sc->sc_opens == 0 && hw->init_input &&
-		    (sc->sc_vchan[n]->sc_mode & AUMODE_RECORD)) {
+		    (vc->sc_mode & AUMODE_RECORD)) {
 			error = hw->init_input(sc->hw_hdl, vc->sc_mrr.s.start,
 				       vc->sc_mrr.s.end - vc->sc_mrr.s.start);
 			if (error)
@@ -1786,13 +1959,12 @@ audio_initbufs(struct audio_softc *sc, i
 		}
 	}
 
-	if (audio_can_playback(sc) ||
-				 (sc->sc_vchan[n]->sc_open & AUOPEN_WRITE)) {
-		audio_init_ringbuffer(sc, &sc->sc_vchan[n]->sc_mpr,
+	if (audio_can_playback(sc) || (vc->sc_open & AUOPEN_WRITE)) {
+		audio_init_ringbuffer(sc, &vc->sc_mpr,
 		    AUMODE_PLAY);
-		sc->sc_vchan[n]->sc_sil_count = 0;
+		vc->sc_sil_count = 0;
 		if (sc->sc_opens == 0 && hw->init_output &&
-		    (sc->sc_vchan[n]->sc_mode & AUMODE_PLAY)) {
+		    (vc->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)
@@ -1805,22 +1977,22 @@ audio_initbufs(struct audio_softc *sc, i
 	if (audio_can_playback(sc)) {
 		sc->sc_pnintr = 0;
 		sc->sc_pblktime = (u_long)(
-		    (double)sc->sc_vchan[n]->sc_mpr.blksize * 100000 /
-		    (double)(sc->sc_vchan[n]->sc_pparams.precision / NBBY *
-			     sc->sc_vchan[n].sc_pparams.channels *
-			     sc->sc_vchan[n].sc_pparams.sample_rate)) * 10;
+		    (double)vc->sc_mpr.blksize * 100000 /
+		    (double)(vc->sc_pparams.precision / NBBY *
+			     vc->sc_pparams.channels *
+			     vc->sc_pparams.sample_rate)) * 10;
 		DPRINTF(("audio: play blktime = %lu for %d\n",
-			 sc->sc_pblktime, sc->sc_vchan[n].sc_mpr.blksize));
+			 sc->sc_pblktime, vc->sc_mpr.blksize));
 	}
 	if (audio_can_capture(sc)) {
 		sc->sc_rnintr = 0;
 		sc->sc_rblktime = (u_long)(
-		    (double)sc->sc_vchan[n]->sc_mrr.blksize * 100000 /
-		    (double)(sc->sc_vchan[n]->sc_rparams.precision / NBBY *
-			     sc->sc_vchan[n]->sc_rparams.channels *
-			     sc->sc_vchan[n]->sc_rparams.sample_rate)) * 10;
+		    (double)vc->sc_mrr.blksize * 100000 /
+		    (double)(vc->sc_rparams.precision / NBBY *
+			     vc->sc_rparams.channels *
+			     vc->sc_rparams.sample_rate)) * 10;
 		DPRINTF(("audio: record blktime = %lu for %d\n",
-			 sc->sc_rblktime, sc->sc_vchan[n]->sc_mrr.blksize));
+			 sc->sc_rblktime, vc->sc_mrr.blksize));
 	}
 #undef double
 #endif
@@ -1829,10 +2001,8 @@ audio_initbufs(struct audio_softc *sc, i
 }
 
 void
-audio_calcwater(struct audio_softc *sc, int n)
+audio_calcwater(struct audio_softc *sc, struct virtual_channel *vc)
 {
-	struct virtual_channel *vc = sc->sc_vchan[n];
-
 	/* set high at 100% */
 	if (audio_can_playback(sc) && vc && vc->sc_pustream) {
 		vc->sc_mpr.usedhigh =
@@ -1856,38 +2026,30 @@ audio_calcwater(struct audio_softc *sc, 
 
 int
 audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
-    struct lwp *l)
+    struct lwp *l, struct file **nfp)
 {
-	int error, i, n;
+	struct file *fp;
+	int error, fd, i, n;
 	u_int mode;
 	const struct audio_hw_if *hw;
 	struct virtual_channel *vc;
+	struct audio_chan *chan;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	if (sc->sc_ready == false || sc->sc_opens >= VAUDIOCHANS)
-		return ENXIO;
-
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-	if (n < VAUDIOCHANS)
-		return ENXIO;
-
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == -1)
-			break;
-	}
-	if (n == VAUDIOCHANS)
+	if (sc->sc_ready == false)
 		return ENXIO;
 
 	hw = sc->hw_if;
 	if (hw == NULL)
 		return ENXIO;
-
-	sc->sc_vchan[n] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
-	vc = sc->sc_vchan[n];
+	n = 0;
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries)
+		n++;
+
+	chan = kmem_zalloc(sizeof(struct audio_chan), KM_SLEEP);
+	vc = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
+	chan->vc = vc;
 
 	vc->sc_open = 0;
 	vc->sc_mode = 0;
@@ -1912,6 +2074,7 @@ audio_open(dev_t dev, struct audio_softc
 	if (((flags & FREAD) && (vc->sc_open & AUOPEN_READ)) ||
 	    ((flags & FWRITE) && (vc->sc_open & AUOPEN_WRITE))) {
 		kmem_free(vc, sizeof(struct virtual_channel));
+		kmem_free(chan, sizeof(struct audio_chan));
 		return EBUSY;
 	}
 
@@ -1923,10 +2086,13 @@ audio_open(dev_t dev, struct audio_softc
 	}
 	if (error) {
 		kmem_free(vc, sizeof(struct virtual_channel));
+		kmem_free(chan, sizeof(struct audio_chan));
 		return error;
 	}
 
 	if (sc->sc_opens == 0) {
+		sc->sc_credentials = kauth_cred_get();
+		kauth_cred_hold(sc->sc_credentials);
 		if (hw->open != NULL) {
 			mutex_enter(sc->sc_intr_lock);
 			error = hw->open(sc->hw_hdl, flags);
@@ -1934,17 +2100,28 @@ audio_open(dev_t dev, struct audio_softc
 			if (error) {
 				kmem_free(vc,
 				    sizeof(struct virtual_channel));
+				kmem_free(chan,
+				    sizeof(struct audio_chan));
 				return error;
 			}
 		}
 		audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
 		audio_init_ringbuffer(sc, &sc->sc_rr, AUMODE_RECORD);
-		audio_initbufs(sc, 0);
+		audio_initbufs(sc, NULL);
 		sc->schedule_wih = false;
 		sc->schedule_rih = false;
 		sc->sc_eof = 0;
 		vc->sc_rbus = false;
 		sc->sc_async_audio = 0;
+	} else if (sc->sc_multiuser == false) {
+		/* XXX:NS Should be handled correctly. */
+		/* Do we allow multi user access */
+		if (kauth_cred_geteuid(sc->sc_credentials) !=
+		    kauth_cred_geteuid(kauth_cred_get()) &&
+		    kauth_cred_geteuid(kauth_cred_get()) != 0) {
+			error = EPERM;
+			goto bad;
+		}
 	}
 
 	mutex_enter(sc->sc_intr_lock);
@@ -1963,18 +2140,18 @@ audio_open(dev_t dev, struct audio_softc
 		mode |= AUMODE_PLAY | AUMODE_PLAY_ALL;
 	}
 
-	vc->sc_mrr.blksize = sc->sc_vchan[0]->sc_mrr.blksize;
-	vc->sc_mpr.blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	vc->sc_mrr.blksize = sc->sc_rr.blksize;
+	vc->sc_mpr.blksize = sc->sc_pr.blksize;
 
 	/*
 	 * Multiplex device: /dev/audio (MU-Law) and /dev/sound (linear)
 	 * The /dev/audio is always (re)set to 8-bit MU-Law mono
 	 * For the other devices, you get what they were last set to.
 	 */
-	error = audio_set_defaults(sc, mode, n);
+	error = audio_set_defaults(sc, mode, vc);
 	if (!error && ISDEVSOUND(dev) && sc->sc_aivalid == true) {
 		sc->sc_ai.mode = mode;
-		error = audiosetinfo(sc, &sc->sc_ai, true, n);
+		error = audiosetinfo(sc, &sc->sc_ai, true, vc);
 	}
 	if (error)
 		goto bad;
@@ -1992,7 +2169,11 @@ audio_open(dev_t dev, struct audio_softc
 #endif
 
 	/* audio_close() decreases sc_mpr[n].usedlow, recalculate here */
-	audio_calcwater(sc, n);
+	audio_calcwater(sc, vc);
+
+	error = fd_allocfile(&fp, &fd);
+	if (error)
+		return error;
 
 	DPRINTF(("audio_open: done sc_mode = 0x%x\n", vc->sc_mode));
 
@@ -2000,12 +2181,18 @@ audio_open(dev_t dev, struct audio_softc
 	if (flags & FREAD)
 		sc->sc_recopens++;
 	sc->sc_opens++;
-	sc->sc_audiopid[n].pid = curproc->p_pid;
-	sc->sc_despid[n].pid = curproc->p_pid;
-	sc->sc_nmixer_states += 2;
+	chan->dev = dev;
+	chan->chan = n;
+	chan->deschan = n;
+	grow_mixer_states(sc, 2);
+	SIMPLEQ_INSERT_TAIL(&sc->sc_audiochan, chan, entries);
 	mutex_exit(sc->sc_intr_lock);
 
-	return 0;
+	error = fd_clone(fp, fd, flags, &audio_fileops, chan);
+	KASSERT(error == EMOVEFD);
+
+	*nfp = fp;
+	return error;
 
 bad:
 	for (i = 0; i < vc->sc_npfilters; i++) {
@@ -2026,7 +2213,8 @@ bad:
 	audio_free_ring(sc, &vc->sc_mpr);
 	audio_free_ring(sc, &vc->sc_mrr);
 	mutex_enter(sc->sc_lock);
-	kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel));
+	kmem_free(vc, sizeof(struct virtual_channel));
+	kmem_free(chan, sizeof(struct audio_chan));
 	return error;
 }
 
@@ -2034,13 +2222,11 @@ bad:
  * Must be called from task context.
  */
 void
-audio_init_record(struct audio_softc *sc, int n)
+audio_init_record(struct audio_softc *sc, struct virtual_channel *vc)
 {
 
 	KASSERT(mutex_owned(sc->sc_lock));
-
-	struct virtual_channel *vc = sc->sc_vchan[n];
- 
+	
 	if (sc->sc_opens != 0)
 		return;
 
@@ -2055,7 +2241,7 @@ audio_init_record(struct audio_softc *sc
  * Must be called from task context.
  */
 void
-audio_init_play(struct audio_softc *sc, int n)
+audio_init_play(struct audio_softc *sc, struct virtual_channel *vc)
 {
 
 	KASSERT(mutex_owned(sc->sc_lock));
@@ -2064,37 +2250,42 @@ audio_init_play(struct audio_softc *sc, 
 		return;
 
 	mutex_enter(sc->sc_intr_lock);
-	sc->sc_vchan[n]->sc_wstamp = sc->sc_vchan[n]->sc_mpr.stamp;
+	vc->sc_wstamp = vc->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, int n)
+audio_drain(struct audio_softc *sc, struct audio_chan *chan)
 {
 	struct audio_ringbuffer *cb;
+	struct virtual_channel *vc;
 	int error, drops;
 	int cc, i, used;
+	bool hw = false;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(mutex_owned(sc->sc_intr_lock));
 	
 	error = 0;
+	vc = chan->vc;
 	DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_vchan[n]->sc_pbus));
-	cb = &sc->sc_vchan[n]->sc_mpr;
+	cb = &chan->vc->sc_mpr;
 	if (cb->mmapped)
 		return 0;
 
 	used = audio_stream_get_used(&cb->s);
-	if (n == 0)
+	if (chan == SIMPLEQ_FIRST(&sc->sc_audiochan)) {
+		hw = true;
 		used += audio_stream_get_used(&sc->sc_pr.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 || (n == 0 && sc->hw_if->trigger_output == NULL))
+	}
+	for (i = 0; i < vc->sc_npfilters; i++)
+		used += audio_stream_get_used(&vc->sc_pstreams[i]);
+	if (used <= 0 || (hw == true && sc->hw_if->trigger_output == NULL))
 		return 0;
 
-	if (n != 0 && !sc->sc_vchan[n]->sc_pbus) {
+	if (hw == false && !vc->sc_pbus) {
 		/* We've never started playing, probably because the
 		 * block was too short.  Pad it and start now.
 		 */
@@ -2103,11 +2294,12 @@ audio_drain(struct audio_softc *sc, int 
 		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, n);
+		error = audiostartp(sc, vc);
 		if (error)
 			return error;
-	} else if (n == 0) {
-		used = cb->blksize * 2;
+	} else if (hw == true) {
+		used = cb->blksize - (sc->sc_pr.s.inp - sc->sc_pr.s.start)
+		    % cb->blksize;
 		while (used > 0) {
 			cc = sc->sc_pr.s.end - sc->sc_pr.s.inp;
 			if (cc > used)
@@ -2131,15 +2323,13 @@ audio_drain(struct audio_softc *sc, int 
 		cb->copying = false;
 	}
 #endif
-	sc->sc_vchan[n]->sc_draining = true;
+	vc->sc_draining = true;
 
-	i = 0;
-drain_again:
 	drops = cb->drops;
 	error = 0;
 	while (cb->drops == drops && !error) {
 		DPRINTF(("audio_drain: n=%d, used=%d, drops=%ld\n", n,
-			audio_stream_get_used(&sc->sc_vchan[n]->sc_mpr.s),
+			audio_stream_get_used(&vc->sc_mpr.s),
 			cb->drops));
 		mutex_exit(sc->sc_intr_lock);
 		error = audio_waitio(sc, &sc->sc_wchan);
@@ -2147,11 +2337,7 @@ drain_again:
 		if (sc->sc_dying)
 			error = EIO;
 	}
-	i++;
-	if (n == 0 && i < 2)
-		goto drain_again;
-
-	sc->sc_vchan[n]->sc_draining = false;
+	vc->sc_draining = false;
 
 	return error;
 }
@@ -2161,8 +2347,7 @@ drain_again:
  */
 /* ARGSUSED */
 int
-audio_close(struct audio_softc *sc, int flags, int ifmt,
-    struct lwp *l, int n)
+audio_close(struct audio_softc *sc, int flags, struct audio_chan *chan)
 {
 	struct virtual_channel *vc;
 	const struct audio_hw_if *hw;
@@ -2170,10 +2355,10 @@ audio_close(struct audio_softc *sc, int 
 
 	KASSERT(mutex_owned(sc->sc_lock));
 	
-	if (sc->sc_opens == 0 || n == 0)
+	if (sc->sc_opens == 0)
 		return ENXIO;
 
-	vc = sc->sc_vchan[n];
+	vc = chan->vc;
 
 	hw = sc->hw_if;
 	if (hw == NULL)
@@ -2201,11 +2386,11 @@ audio_close(struct audio_softc *sc, int 
 	 */
 	if ((flags & FWRITE) && vc->sc_pbus) {
 		if (!vc->sc_mpr.pause)
-			audio_drain(sc, n);
+			audio_drain(sc, chan);
 		vc->sc_pbus = false;
 	}
 	if (sc->sc_opens == 1) {
-		audio_drain(sc, 0);
+		audio_drain(sc, SIMPLEQ_FIRST(&sc->sc_audiochan));
 		if (hw->drain)
 			(void)hw->drain(sc->hw_hdl);
 		hw->halt_output(sc->hw_hdl);
@@ -2216,8 +2401,10 @@ audio_close(struct audio_softc *sc, int 
 
 	if (sc->sc_opens == 1 && hw->close != NULL)
 		hw->close(sc->hw_hdl);
-	if (sc->sc_opens == 1)
+	if (sc->sc_opens == 1) {
 		sc->sc_async_audio = 0;
+		kauth_cred_free(sc->sc_credentials);
+	}
 
 	vc->sc_open = 0;
 	vc->sc_mode = 0;
@@ -2239,40 +2426,31 @@ audio_close(struct audio_softc *sc, int 
 	if (flags & FREAD)
 		sc->sc_recopens--;
 	sc->sc_opens--;
-	sc->sc_nmixer_states -= 2;
-	sc->sc_audiopid[n].pid = -1;
-	sc->sc_despid[n].pid = -1;
+	shrink_mixer_states(sc, 2);
+	SIMPLEQ_REMOVE(&sc->sc_audiochan, chan, audio_chan, entries);
 	mutex_exit(sc->sc_intr_lock);
 	mutex_exit(sc->sc_lock);
 	audio_free_ring(sc, &vc->sc_mpr);
 	audio_free_ring(sc, &vc->sc_mrr);
 	mutex_enter(sc->sc_lock);
-	kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel));
+	kmem_free(vc, sizeof(struct virtual_channel));
 
 	return 0;
 }
 
 int
-audio_read(struct audio_softc *sc, struct uio *uio, int ioflag)
+audio_read(struct audio_softc *sc, struct uio *uio, int ioflag,
+	   struct virtual_channel *vc)
 {
 	struct audio_ringbuffer *cb;
-	struct virtual_channel *vc;
 	const uint8_t *outp;
 	uint8_t *inp;
-	int error, used, cc, n, m;
+	int error, used, cc, n;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	for (m = 1; m < VAUDIOCHANS; m++) {
-		if (sc->sc_audiopid[m].pid == curproc->p_pid)
-			break;
-	}
-	if (m == VAUDIOCHANS)
-		return EINVAL;
-
 	if (sc->hw_if == NULL)
 		return ENXIO;
-	vc = sc->sc_vchan[m];
 
 	cb = &vc->sc_mrr;
 	if (cb->mmapped)
@@ -2326,7 +2504,7 @@ audio_read(struct audio_softc *sc, struc
 	while (uio->uio_resid > 0 && !error) {
 		while ((used = audio_stream_get_used(vc->sc_rustream)) <= 0) {
 			if (!vc->sc_rbus && !vc->sc_mrr.pause)
-				error = audiostartr(sc, m);
+				error = audiostartr(sc, vc);
 			mutex_exit(sc->sc_intr_lock);
 			if (error)
 				return error;
@@ -2370,11 +2548,9 @@ audio_read(struct audio_softc *sc, struc
 }
 
 void
-audio_clear(struct audio_softc *sc, int n)
+audio_clear(struct audio_softc *sc, struct virtual_channel *vc)
 {
 
-	struct virtual_channel *vc = sc->sc_vchan[n];
- 
 	KASSERT(mutex_owned(sc->sc_intr_lock));
 
 	if (vc->sc_rbus) {
@@ -2394,28 +2570,29 @@ audio_clear(struct audio_softc *sc, int 
 }
 
 void
-audio_clear_intr_unlocked(struct audio_softc *sc, int n)
+audio_clear_intr_unlocked(struct audio_softc *sc, struct virtual_channel *vc)
 {
 
 	mutex_enter(sc->sc_intr_lock);
-	audio_clear(sc, n);
+	audio_clear(sc, vc);
 	mutex_exit(sc->sc_intr_lock);
 }
 
 void
-audio_calc_blksize(struct audio_softc *sc, int mode, int n)
+audio_calc_blksize(struct audio_softc *sc, int mode,
+		   struct virtual_channel *vc)
 {
 	const audio_params_t *parm;
 	struct audio_ringbuffer *rb;
 
-	if (sc->sc_vchan[n]->sc_blkset)
+	if (vc->sc_blkset)
 		return;
 
 	if (mode == AUMODE_PLAY) {
-		rb = &sc->sc_vchan[n]->sc_mpr;
+		rb = &vc->sc_mpr;
 		parm = &rb->s.param;
 	} else {
-		rb = &sc->sc_vchan[n]->sc_mrr;
+		rb = &vc->sc_mrr;
 		parm = &rb->s.param;
 	}
 
@@ -2493,12 +2670,14 @@ audio_fill_silence(struct audio_params *
 int
 audio_silence_copyout(struct audio_softc *sc, int n, struct uio *uio)
 {
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
 	uint8_t zerobuf[128];
 	int error;
 	int k;
 
-	vc = sc->sc_vchan[0];
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	vc = chan->vc;
 	audio_fill_silence(&vc->sc_rparams, zerobuf, sizeof zerobuf);
 
 	error = 0;
@@ -2583,30 +2762,22 @@ uio_fetcher_ctor(uio_fetcher_t *this, st
 }
 
 int
-audio_write(struct audio_softc *sc, struct uio *uio, int ioflag)
+audio_write(struct audio_softc *sc, struct uio *uio, int ioflag,
+	    struct virtual_channel *vc)
 {
 	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, m, cc, used;
+	int saveerror, error, m, cc, used;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-	if (n == VAUDIOCHANS)
-		return EINVAL;
-
 	if (sc->hw_if == NULL)
 		return ENXIO;
 
-	vc = sc->sc_vchan[n];
 	cb = &vc->sc_mpr;
 
 	DPRINTFN(2,("audio_write: sc=%p count=%zu used=%d(hi=%d)\n",
@@ -2732,7 +2903,7 @@ audio_write(struct audio_softc *sc, stru
 		cb->copying = false;
 		if (!vc->sc_pbus && !cb->pause) {
 			saveerror = error;
-			error = audiostartp(sc, n);
+			error = audiostartp(sc, vc);
 			if (saveerror != 0) {
 				/* Report the first error that occurred. */
 				error = saveerror;
@@ -2750,31 +2921,26 @@ audio_write(struct audio_softc *sc, stru
 
 int
 audio_ioctl(dev_t dev, struct audio_softc *sc, u_long cmd, void *addr, int flag,
-	    struct lwp *l)
+	    struct lwp *l, struct audio_chan *chan)
 {
 	const struct audio_hw_if *hw;
+	struct audio_chan *pchan;
 	struct virtual_channel *vc;
 	struct audio_offset *ao;
 	u_long stamp;
-	int error, offs, fd, m, n;
+	int error, offs, fd;
 	bool rbus, pbus;
 
 	KASSERT(mutex_owned(sc->sc_lock));
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
+
+	SIMPLEQ_FOREACH(pchan, &sc->sc_audiochan, entries) {
+		if (pchan->chan == chan->deschan)
 			break;
 	}
-	m = n;
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_despid[m].pid >= 0 && sc->sc_audiopid[n].pid ==
-		    sc->sc_despid[m].pid)
-				break;
-	}
-
-	if (m >= VAUDIOCHANS || n >= VAUDIOCHANS)
+	if (pchan == NULL)
 		return ENXIO;
 
-	vc = sc->sc_vchan[n];
+	vc = pchan->vc;
 
 	DPRINTF(("audio_ioctl(%lu,'%c',%lu)\n",
 		 IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd&0xff));
@@ -2784,8 +2950,8 @@ audio_ioctl(dev_t dev, struct audio_soft
 	error = 0;
 	switch (cmd) {
 	case AUDIO_SETPROC:
-		if ((struct audio_pid *)addr != NULL)
-			sc->sc_despid[m] = *(struct audio_pid *)addr;
+		if ((int *)addr != NULL && *(int*)addr > 0)
+			chan->deschan = *(int*)addr;
 		break;
 	case FIONBIO:
 		/* All handled in the upper FS layer. */
@@ -2800,9 +2966,9 @@ audio_ioctl(dev_t dev, struct audio_soft
 			if (sc->sc_async_audio != 0)
 				error = EBUSY;
 			else
-				sc->sc_async_audio = sc->sc_audiopid[n].pid;
-			DPRINTF(("audio_ioctl: FIOASYNC pid %d\n",
-			    sc->sc_audiopid[n].pid));
+				sc->sc_async_audio = pchan->chan;
+			DPRINTF(("audio_ioctl: FIOASYNC chan %d\n",
+			    sc->sc_audiochan[n].chan));
 		} else
 			sc->sc_async_audio = 0;
 		break;
@@ -2812,17 +2978,17 @@ audio_ioctl(dev_t dev, struct audio_soft
 		rbus = vc->sc_rbus;
 		pbus = vc->sc_pbus;
 		mutex_enter(sc->sc_intr_lock);
-		audio_clear(sc, n);
-		error = audio_initbufs(sc, n);
+		audio_clear(sc, vc);
+		error = audio_initbufs(sc, vc);
 		if (error) {
 			mutex_exit(sc->sc_intr_lock);
 			return error;
 		}
 		if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_pbus && pbus)
-			error = audiostartp(sc, n);
+			error = audiostartp(sc, vc);
 		if (!error &&
 		    (vc->sc_mode & AUMODE_RECORD) && !vc->sc_rbus && rbus)
-			error = audiostartr(sc, n);
+			error = audiostartr(sc, vc);
 		mutex_exit(sc->sc_intr_lock);
 		break;
 
@@ -2886,27 +3052,27 @@ audio_ioctl(dev_t dev, struct audio_soft
 
 	case AUDIO_SETINFO:
 		DPRINTF(("AUDIO_SETINFO mode=0x%x\n", vc->sc_mode));
-		error = audiosetinfo(sc, (struct audio_info *)addr, false, n);
+		error = audiosetinfo(sc, (struct audio_info *)addr, false, vc);
 		if (!error && ISDEVSOUND(dev)) {
-			error = audiogetinfo(sc, &sc->sc_ai, 0, n);
+			error = audiogetinfo(sc, &sc->sc_ai, 0, vc);
 			sc->sc_aivalid = true;
 		}
 		break;
 
 	case AUDIO_GETINFO:
 		DPRINTF(("AUDIO_GETINFO\n"));
-		error = audiogetinfo(sc, (struct audio_info *)addr, 0, n);
+		error = audiogetinfo(sc, (struct audio_info *)addr, 0, vc);
 		break;
 
 	case AUDIO_GETBUFINFO:
 		DPRINTF(("AUDIO_GETBUFINFO\n"));
-		error = audiogetinfo(sc, (struct audio_info *)addr, 1, n);
+		error = audiogetinfo(sc, (struct audio_info *)addr, 1, vc);
 		break;
 
 	case AUDIO_DRAIN:
 		DPRINTF(("AUDIO_DRAIN\n"));
 		mutex_enter(sc->sc_intr_lock);
-		error = audio_drain(sc, n);
+		error = audio_drain(sc, pchan);
 		if (!error && sc->sc_opens == 1 && hw->drain)
 		    error = hw->drain(sc->hw_hdl);
 		mutex_exit(sc->sc_intr_lock);
@@ -2966,20 +3132,13 @@ audio_ioctl(dev_t dev, struct audio_soft
 }
 
 int
-audio_poll(struct audio_softc *sc, int events, struct lwp *l)
+audio_poll(struct audio_softc *sc, int events, struct lwp *l,
+	   struct virtual_channel *vc)
 {
-	struct virtual_channel *vc;
 	int revents;
-	int used, n;
+	int used;
 
 	KASSERT(mutex_owned(sc->sc_lock));
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-	if (n == VAUDIOCHANS)
-		return ENXIO;
-	vc = sc->sc_vchan[n];
 
 	DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, vc->sc_mode));
 
@@ -3028,8 +3187,16 @@ static void
 filt_audiordetach(struct knote *kn)
 {
 	struct audio_softc *sc;
+	struct audio_chan *chan;
+	dev_t dev;
+
+	chan = kn->kn_hook;
+	dev = chan->dev;
+	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
+	if (sc == NULL)
+		return;
+
 
-	sc = kn->kn_hook;
 	mutex_enter(sc->sc_intr_lock);
 	SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
 	mutex_exit(sc->sc_intr_lock);
@@ -3039,18 +3206,17 @@ static int
 filt_audioread(struct knote *kn, long hint)
 {
 	struct audio_softc *sc;
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
-	int n;
+	dev_t dev;
 
-	sc = kn->kn_hook;
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-	if (n == VAUDIOCHANS)
+	chan = kn->kn_hook;
+	dev = chan->dev;
+	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
+	if (sc == NULL)
 		return ENXIO;
 
-	vc = sc->sc_vchan[n];
+	vc = chan->vc;
 	mutex_enter(sc->sc_intr_lock);
 	if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY))
 		kn->kn_data = vc->sc_mpr.stamp - vc->sc_wstamp;
@@ -3069,8 +3235,15 @@ static void
 filt_audiowdetach(struct knote *kn)
 {
 	struct audio_softc *sc;
+	struct audio_chan *chan;
+	dev_t dev;
+
+	chan = kn->kn_hook;
+	dev = chan->dev;
+	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
+	if (sc == NULL)
+		return;
 
-	sc = kn->kn_hook;
 	mutex_enter(sc->sc_intr_lock);
 	SLIST_REMOVE(&sc->sc_wsel.sel_klist, kn, knote, kn_selnext);
 	mutex_exit(sc->sc_intr_lock);
@@ -3080,19 +3253,19 @@ static int
 filt_audiowrite(struct knote *kn, long hint)
 {
 	struct audio_softc *sc;
+	struct audio_chan *chan;
 	audio_stream_t *stream;
-	int n;
+	dev_t dev;
 
-	sc = kn->kn_hook;
-	mutex_enter(sc->sc_intr_lock);
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-	if (n == VAUDIOCHANS)
+	chan = kn->kn_hook;
+	dev = chan->dev;
+	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
+	if (sc == NULL)
 		return ENXIO;
 
-	stream = sc->sc_vchan[n]->sc_pustream;
+	mutex_enter(sc->sc_intr_lock);
+
+	stream = chan->vc->sc_pustream;
 	kn->kn_data = (stream->end - stream->start)
 		- audio_stream_get_used(stream);
 	mutex_exit(sc->sc_intr_lock);
@@ -3104,9 +3277,17 @@ static const struct filterops audiowrite
 	{ 1, NULL, filt_audiowdetach, filt_audiowrite };
 
 int
-audio_kqfilter(struct audio_softc *sc, struct knote *kn)
+audio_kqfilter(struct audio_chan *chan, struct knote *kn)
 {
+	struct audio_softc *sc;
 	struct klist *klist;
+	dev_t dev;
+
+	dev = chan->dev;
+
+	sc = device_lookup_private(&audio_cd, AUDIOUNIT(dev));
+	if (sc == NULL)
+		return ENXIO;
 
 	switch (kn->kn_filter) {
 	case EVFILT_READ:
@@ -3123,7 +3304,7 @@ audio_kqfilter(struct audio_softc *sc, s
 		return EINVAL;
 	}
 
-	kn->kn_hook = sc;
+	kn->kn_hook = chan;
 
 	mutex_enter(sc->sc_intr_lock);
 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
@@ -3132,28 +3313,20 @@ audio_kqfilter(struct audio_softc *sc, s
 	return 0;
 }
 
+/* XXX:NS mmap to be fixed. */
 paddr_t
-audio_mmap(struct audio_softc *sc, off_t off, int prot)
+audio_mmap(struct audio_softc *sc, off_t off, int prot,
+	   struct virtual_channel *vc)
 {
 	struct audio_ringbuffer *cb;
-	struct virtual_channel *vc;
 	paddr_t rv;
-	int n;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == curproc->p_pid)
-			break;
-	}
-	if (n == VAUDIOCHANS)
-		return -1;
-
 	if (sc->hw_if == NULL)
 		return ENXIO;
 
 	DPRINTF(("audio_mmap: off=%lld, prot=%d\n", (long long)off, prot));
-	vc = sc->sc_vchan[n];
 	if (!(audio_get_props(sc) & AUDIO_PROP_MMAP))
 		return -1;
 #if 0
@@ -3171,13 +3344,13 @@ audio_mmap(struct audio_softc *sc, off_t
  */
 	if (prot == (VM_PROT_READ|VM_PROT_WRITE) ||
 	    prot == VM_PROT_WRITE)
-		cb = &sc->sc_vchan[n]->sc_mpr;
+		cb = &vc->sc_mpr;
 	else if (prot == VM_PROT_READ)
-		cb = &sc->sc_vchan[n]->sc_mrr;
+		cb = &vc->sc_mrr;
 	else
 		return -1;
 #else
-	cb = &sc->sc_vchan[n]->sc_mpr;
+	cb = &vc->sc_mpr;
 #endif
 
 	if ((u_int)off >= cb->s.bufsize)
@@ -3190,13 +3363,13 @@ audio_mmap(struct audio_softc *sc, off_t
 			mutex_enter(sc->sc_intr_lock);
 			vc->sc_pustream = &cb->s;
 			if (!vc->sc_pbus && !vc->sc_mpr.pause)
-				(void)audiostartp(sc, n);
+				(void)audiostartp(sc, vc);
 			mutex_exit(sc->sc_intr_lock);
 		} else {
 			mutex_enter(sc->sc_intr_lock);
 			vc->sc_rustream = &cb->s;
 			if (!vc->sc_rbus && !sc->sc_rr.pause)
-				(void)audiostartr(sc, n);
+				(void)audiostartr(sc, vc);
 			mutex_exit(sc->sc_intr_lock);
 		}
 	}
@@ -3207,14 +3380,12 @@ audio_mmap(struct audio_softc *sc, off_t
 }
 
 int
-audiostartr(struct audio_softc *sc, int n)
+audiostartr(struct audio_softc *sc, struct virtual_channel *vc)
 {
-	struct virtual_channel *vc;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(mutex_owned(sc->sc_intr_lock));
 
-	vc = sc->sc_vchan[n];
 	DPRINTF(("audiostartr: start=%p used=%d(hi=%d) mmapped=%d\n",
 		 vc->sc_mrr.s.start, audio_stream_get_used(&vc->sc_mrr.s),
 		 vc->sc_mrr.usedhigh, vc->sc_mrr.mmapped));
@@ -3232,14 +3403,15 @@ audiostartr(struct audio_softc *sc, int 
 }
 
 int
-audiostartp(struct audio_softc *sc, int n)
+audiostartp(struct audio_softc *sc, struct virtual_channel *vc)
 {
-	struct virtual_channel *vc = sc->sc_vchan[n];
+	struct audio_chan *chan;
 	int error, used;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(mutex_owned(sc->sc_intr_lock));
 
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
 	error = 0;
 	used = audio_stream_get_used(&vc->sc_mpr.s);
 	DPRINTF(("audiostartp: start=%p used=%d(hi=%d blk=%d) mmapped=%d\n",
@@ -3260,7 +3432,7 @@ audiostartp(struct audio_softc *sc, int 
 		audio_mix(sc);
 		mix_write(sc);
 
-		vc = sc->sc_vchan[0];
+		vc = chan->vc;
 		vc->sc_mpr.s.outp =
 		    audio_stream_add_outp(&vc->sc_mpr.s,
 		      vc->sc_mpr.s.outp, vc->sc_mpr.blksize);
@@ -3289,9 +3461,8 @@ audiostartp(struct audio_softc *sc, int 
  */
 static inline void
 audio_pint_silence(struct audio_softc *sc, struct audio_ringbuffer *cb,
-		   uint8_t *inp, int cc, int n)
+		   uint8_t *inp, int cc, struct virtual_channel *vc)
 {
-	struct virtual_channel *vc = sc->sc_vchan[n];
 	uint8_t *s, *e, *p, *q;
 
 	KASSERT(mutex_owned(sc->sc_lock));
@@ -3377,28 +3548,26 @@ void
 audio_pint(void *v)
 {
 	struct audio_softc *sc;
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
-	int blksize, i, used;
+	int blksize;
 
 	sc = v;
-	vc = sc->sc_vchan[0];
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	vc = chan->vc;
 	blksize = vc->sc_mpr.blksize;
 
 	if (sc->sc_dying == true)
 		return;
 
-	used = audio_stream_get_used(&vc->sc_mpr.s);
-	for (i = 0; i < vc->sc_npfilters; i++)
-		used += audio_stream_get_used(&vc->sc_pstreams[i]);
-
-	if (audio_stream_get_used(&sc->sc_pr.s) <blksize)
-		goto wake_mix;
-
-	if (vc->sc_draining == true && used <= 0) {
+	if (vc->sc_draining == true) {
 		vc->sc_mpr.drops += blksize;
 		cv_broadcast(&sc->sc_wchan);
 	}
 
+	if (audio_stream_get_used(&sc->sc_pr.s) < blksize)
+		goto wake_mix;
+
 	vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s,
 	    vc->sc_mpr.s.outp, blksize);
 
@@ -3413,13 +3582,12 @@ audio_mix(void *v)
 {
 	stream_fetcher_t null_fetcher;
 	struct audio_softc *sc;
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
 	struct audio_ringbuffer *cb;
 	stream_fetcher_t *fetcher;
 	uint8_t *inp;
-	int cc, used;
-	int blksize;
-	int n, i;
+	int cc, used, blksize;
 	
 	sc = v;
 
@@ -3431,16 +3599,22 @@ audio_mix(void *v)
 	if (sc->sc_dying == true)
 		return;
 
-	i = sc->sc_opens;
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (!sc->sc_opens || i <= 0)
+	blksize = sc->sc_pr.blksize;
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (!sc->sc_opens)
 			break;		/* ignore interrupt if not open */
 
-		if (sc->sc_audiopid[n].pid == -1)
+		if (chan == NULL)
+			break;
+
+		if (chan == SIMPLEQ_FIRST(&sc->sc_audiochan))
+			continue;
+
+		if (chan->chan == MIXER_INUSE)
 			continue;
-		i--;
-		vc = sc->sc_vchan[n];
+
+		vc = chan->vc;
+
 		if (!vc->sc_open)
 			continue;
 		if (!vc->sc_pbus)
@@ -3455,7 +3629,7 @@ audio_mix(void *v)
 		if (cb->mmapped) {
 			DPRINTF(("audio_pint: mmapped outp=%p cc=%d inp=%p\n",
 				     cb->s.outp, blksize, cb->s.inp));
-			mix_func(sc, cb, n);
+			mix_func(sc, cb, vc);
 			continue;
 		}
 
@@ -3532,7 +3706,7 @@ audio_mix(void *v)
 					vc->sc_playdrop += cc;
 				}
 
-				audio_pint_silence(sc, cb, inp, cc, n);
+				audio_pint_silence(sc, cb, inp, cc, vc);
 				cb->s.inp = audio_stream_add_inp(&cb->s, inp,
 				    cc);
 
@@ -3540,14 +3714,14 @@ audio_mix(void *v)
 				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);
+					    blksize, vc);
 				}
 			}
 		}
 
 		DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp,
 			 blksize));
-		mix_func(sc, cb, n);
+		mix_func(sc, cb, vc);
 		cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize);
 
 		DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n",
@@ -3576,12 +3750,6 @@ audio_mix(void *v)
 	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);
-	}
 }
 
 /*
@@ -3613,28 +3781,30 @@ audio_upmix(void *v)
 {
 	stream_fetcher_t null_fetcher;
 	struct audio_softc *sc;
+	struct audio_chan *chan;
 	struct audio_ringbuffer *cb;
 	stream_fetcher_t *last_fetcher;
 	struct virtual_channel *vc;
-	int cc;
-	int used;
-	int blksize;
-	int i;
-	int n;
-	int cc1;
+	int cc, used, blksize, cc1;
 
 	sc = v;
-	i = sc->sc_opens;
-	blksize = sc->sc_vchan[0]->sc_mrr.blksize;
+	blksize = sc->sc_rr.blksize;
 
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (!sc->sc_opens || i <= 0)
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (!sc->sc_opens)
 			break;		/* ignore interrupt if not open */
 
-		if (sc->sc_audiopid[n].pid == -1)
+		if (chan == NULL)
+			break;
+
+		if (chan == SIMPLEQ_FIRST(&sc->sc_audiochan))
+			continue;
+
+		if (chan->chan == MIXER_INUSE)
 			continue;
-		i--;
-		vc = sc->sc_vchan[n];
+
+		vc = chan->vc;
+
 		if (!(vc->sc_open & AUOPEN_READ))
 			continue;
 		if (!vc->sc_rbus)
@@ -3661,7 +3831,7 @@ audio_upmix(void *v)
 		}
 
 		cc = blksize;
-		recswvol_func(sc, cb, n, blksize);
+		recswvol_func(sc, cb, blksize, vc);
 
 		cb->s.inp = audio_stream_add_inp(&cb->s, cb->s.inp, blksize);
 		cb->stamp += blksize;
@@ -3815,14 +3985,20 @@ audio_check_params(struct audio_params *
 
 static int
 audio_set_vchan_defaults(struct audio_softc *sc, u_int mode,
-     const struct audio_format *format, int n)
+     const struct audio_format *format)
 {
-	struct virtual_channel *vc = sc->sc_vchan[n];
+	struct audio_chan *chan;
+	struct virtual_channel *vc;
 	struct audio_info ai;
 	int error;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	if (chan == NULL)
+		return EINVAL;
+	vc = chan->vc;
+
 	sc->sc_vchan_params.sample_rate = sc->sc_iffreq;
 #if BYTE_ORDER == LITTLE_ENDIAN
 	sc->sc_vchan_params.encoding = AUDIO_ENCODING_SLINEAR_LE;
@@ -3861,15 +4037,18 @@ audio_set_vchan_defaults(struct audio_so
 	    &sc->sc_encodings);
 
 	if (error == 0)
-		error = audiosetinfo(sc, &ai, true, n);
+		error = audiosetinfo(sc, &ai, true, vc);
+
+	sc->sc_pr.blksize = vc->sc_mpr.blksize;
+	sc->sc_rr.blksize = vc->sc_mrr.blksize;
 
 	return error;
 }
 
 int
-audio_set_defaults(struct audio_softc *sc, u_int mode, int n)
+audio_set_defaults(struct audio_softc *sc, u_int mode,
+		   struct virtual_channel *vc)
 {
-	struct virtual_channel *vc = sc->sc_vchan[n];
 	struct audio_info ai;
 
 	KASSERT(mutex_owned(sc->sc_lock));
@@ -3892,7 +4071,7 @@ audio_set_defaults(struct audio_softc *s
 	ai.play.pause         = false;
 	ai.mode		      = mode;
 
-	return audiosetinfo(sc, &ai, true, n);
+	return audiosetinfo(sc, &ai, true, vc);
 }
 
 int
@@ -4190,13 +4369,13 @@ au_get_port(struct audio_softc *sc, stru
 }
 
 int
-audiosetinfo(struct audio_softc *sc, struct audio_info *ai, bool reset, int n)
+audiosetinfo(struct audio_softc *sc, struct audio_info *ai, bool reset,
+	     struct virtual_channel *vc)
 {
 	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;
@@ -4210,7 +4389,6 @@ audiosetinfo(struct audio_softc *sc, str
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	vc = sc->sc_vchan[n];
 	hw = sc->hw_if;
 	if (hw == NULL)		/* HW has not attached */
 		return ENXIO;
@@ -4288,7 +4466,7 @@ audiosetinfo(struct audio_softc *sc, str
 	setmode = 0;
 	if (nr > 0) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc, n);
+			audio_clear_intr_unlocked(sc, vc);
 			cleared = true;
 		}
 		modechange = true;
@@ -4296,7 +4474,7 @@ audiosetinfo(struct audio_softc *sc, str
 	}
 	if (np > 0) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc, n);
+			audio_clear_intr_unlocked(sc, vc);
 			cleared = true;
 		}
 		modechange = true;
@@ -4305,7 +4483,7 @@ audiosetinfo(struct audio_softc *sc, str
 
 	if (SPECIFIED(ai->mode)) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc, n);
+			audio_clear_intr_unlocked(sc, vc);
 			cleared = true;
 		}
 		modechange = true;
@@ -4341,7 +4519,7 @@ audiosetinfo(struct audio_softc *sc, str
 		 * no channels/sample_rate. */
 		error = audio_set_params(sc, setmode,
 		    vc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp,
-		    &pfilters, &rfilters, n);
+		    &pfilters, &rfilters, vc);
 		if (error) {
 			DPRINTF(("%s: audio_set_params() failed with %d\n",
 			    __func__, error));
@@ -4369,12 +4547,12 @@ audiosetinfo(struct audio_softc *sc, str
 
 		/* construct new filter chain */
 		if (setmode & AUMODE_PLAY) {
-			error = audio_setup_pfilters(sc, &pp, &pfilters, n);
+			error = audio_setup_pfilters(sc, &pp, &pfilters, vc);
 			if (error)
 				goto cleanup;
 		}
 		if (setmode & AUMODE_RECORD) {
-			error = audio_setup_rfilters(sc, &rp, &rfilters, n);
+			error = audio_setup_rfilters(sc, &rp, &rfilters, vc);
 			if (error)
 				goto cleanup;
 		}
@@ -4388,8 +4566,8 @@ audiosetinfo(struct audio_softc *sc, str
 	/* Play params can affect the record params, so recalculate blksize. */
 	if (nr > 0 || np > 0 || reset) {
 		vc->sc_blkset = false;
-		audio_calc_blksize(sc, AUMODE_RECORD, n);
-		audio_calc_blksize(sc, AUMODE_PLAY, n);
+		audio_calc_blksize(sc, AUMODE_RECORD, vc);
+		audio_calc_blksize(sc, AUMODE_PLAY, vc);
 	}
 #ifdef AUDIO_DEBUG
 	if (audiodebug > 1 && nr > 0) {
@@ -4404,7 +4582,7 @@ audiosetinfo(struct audio_softc *sc, str
 
 	if (SPECIFIED(p->port)) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc, n);
+			audio_clear_intr_unlocked(sc, vc);
 			cleared = true;
 		}
 		error = au_set_port(sc, &sc->sc_outports, p->port);
@@ -4413,7 +4591,7 @@ audiosetinfo(struct audio_softc *sc, str
 	}
 	if (SPECIFIED(r->port)) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc, n);
+			audio_clear_intr_unlocked(sc, vc);
 			cleared = true;
 		}
 		error = au_set_port(sc, &sc->sc_inports, r->port);
@@ -4475,18 +4653,18 @@ audiosetinfo(struct audio_softc *sc, str
 		/* Block size specified explicitly. */
 		if (ai->blocksize == 0) {
 			if (!cleared) {
-				audio_clear_intr_unlocked(sc, n);
+				audio_clear_intr_unlocked(sc, vc);
 				cleared = true;
 			}
 			vc->sc_blkset = false;
-			audio_calc_blksize(sc, AUMODE_RECORD, n);
-			audio_calc_blksize(sc, AUMODE_PLAY, n);
+			audio_calc_blksize(sc, AUMODE_RECORD, vc);
+			audio_calc_blksize(sc, AUMODE_PLAY, vc);
 		} else {
 			vc->sc_blkset = true;
 			/* check whether new blocksize changes actually */
 			if (hw->round_blocksize == NULL) {
 				if (!cleared) {
-					audio_clear_intr_unlocked(sc, n);
+					audio_clear_intr_unlocked(sc, vc);
 					cleared = true;
 				}
 				vc->sc_mpr.blksize = ai->blocksize;
@@ -4499,12 +4677,11 @@ audiosetinfo(struct audio_softc *sc, str
 				    ai->blocksize, AUMODE_RECORD,
 				    &vc->sc_mrr.s.param);
 				if ((pblksize != vc->sc_mpr.blksize &&
-				    pblksize > sc->sc_vchan[0]->sc_mpr.blksize)
+				    pblksize > sc->sc_pr.blksize)
 				    || (rblksize != vc->sc_mrr.blksize &&
-				    rblksize >
-					sc->sc_vchan[0]->sc_mrr.blksize)) {
+				    rblksize > sc->sc_rr.blksize)) {
 					if (!cleared) {
-					    audio_clear_intr_unlocked(sc, n);
+					    audio_clear_intr_unlocked(sc, vc);
 					    cleared = true;
 					}
 					vc->sc_mpr.blksize = pblksize;
@@ -4516,9 +4693,9 @@ audiosetinfo(struct audio_softc *sc, str
 
 	if (SPECIFIED(ai->mode)) {
 		if (vc->sc_mode & AUMODE_PLAY)
-			audio_init_play(sc, n);
+			audio_init_play(sc, vc);
 		if (vc->sc_mode & AUMODE_RECORD)
-			audio_init_record(sc, n);
+			audio_init_record(sc, vc);
 	}
 
 	if (hw->commit_settings && sc->sc_opens == 0) {
@@ -4536,20 +4713,20 @@ cleanup:
 
 		mutex_enter(sc->sc_intr_lock);
 		init_error = (pausechange == 1 && reset == 0) ? 0 :
-		    audio_initbufs(sc, n);
+		    audio_initbufs(sc, vc);
 		if (init_error) goto err;
 		if (vc->sc_mpr.blksize != oldpblksize ||
 		    vc->sc_mrr.blksize != oldrblksize ||
 		    vc->sc_pustream != oldpus ||
 		    vc->sc_rustream != oldrus)
-			audio_calcwater(sc, n);
+			audio_calcwater(sc, vc);
 		if ((vc->sc_mode & AUMODE_PLAY) &&
 		    pbus && !vc->sc_pbus)
-			init_error = audiostartp(sc, n);
+			init_error = audiostartp(sc, vc);
 		if (!init_error &&
 		    (vc->sc_mode & AUMODE_RECORD) &&
 		    rbus && !vc->sc_rbus)
-			init_error = audiostartr(sc, n);
+			init_error = audiostartr(sc, vc);
 	err:
 		mutex_exit(sc->sc_intr_lock);
 		if (init_error)
@@ -4584,17 +4761,15 @@ cleanup:
 
 int
 audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode,
-    int n)
+	     struct virtual_channel *vc)
 {
 	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;
@@ -4693,8 +4868,11 @@ audiogetinfo(struct audio_softc *sc, str
  */
 int
 mixer_open(dev_t dev, struct audio_softc *sc, int flags,
-    int ifmt, struct lwp *l)
+    int ifmt, struct lwp *l, struct file **nfp)
 {
+	struct file *fp;
+	struct audio_chan *chan;
+	int error, fd;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
@@ -4703,7 +4881,22 @@ mixer_open(dev_t dev, struct audio_softc
 
 	DPRINTF(("mixer_open: flags=0x%x sc=%p\n", flags, sc));
 
-	return 0;
+	chan = kmem_zalloc(sizeof(struct audio_chan), KM_SLEEP);
+
+	error = fd_allocfile(&fp, &fd);
+	if (error)
+		return error;
+
+	chan->dev = dev;
+	chan->chan = MIXER_INUSE;
+
+	SIMPLEQ_INSERT_TAIL(&sc->sc_audiochan, chan, entries);
+
+	error = fd_clone(fp, fd, flags, &audio_fileops, chan);
+	KASSERT(error == EMOVEFD);
+
+	*nfp = fp;
+	return error;
 }
 
 /*
@@ -4750,7 +4943,7 @@ mixer_signal(struct audio_softc *sc)
  */
 /* ARGSUSED */
 int
-mixer_close(struct audio_softc *sc, int flags, int ifmt, struct lwp *l)
+mixer_close(struct audio_softc *sc, int flags, struct audio_chan *chan)
 {
 
 	KASSERT(mutex_owned(sc->sc_lock));
@@ -4759,6 +4952,8 @@ mixer_close(struct audio_softc *sc, int 
 
 	DPRINTF(("mixer_close: sc %p\n", sc));
 	mixer_remove(sc);
+	SIMPLEQ_REMOVE(&sc->sc_audiochan, chan, audio_chan, entries);
+
 	return 0;
 }
 
@@ -4785,9 +4980,7 @@ mixer_ioctl(struct audio_softc *sc, u_lo
 	switch (cmd) {
 	case FIOASYNC:
 		if (*(int *)addr) {
-			mutex_exit(sc->sc_lock);
 			ma = kmem_alloc(sizeof(struct mixer_asyncs), KM_SLEEP);
-			mutex_enter(sc->sc_lock);
 		} else {
 			ma = NULL;
 		}
@@ -4837,9 +5030,9 @@ mixer_ioctl(struct audio_softc *sc, u_lo
 		break;
 
 	default:
-		if (hw->dev_ioctl)
+		if (hw->dev_ioctl) {
 			error = hw->dev_ioctl(sc->hw_hdl, cmd, addr, flag, l);
-		else
+		} else
 			error = EINVAL;
 		break;
 	}
@@ -4992,19 +5185,20 @@ static bool
 audio_suspend(device_t dv, const pmf_qual_t *qual)
 {
 	struct audio_softc *sc = device_private(dv);
+	struct audio_chan *chan;
 	const struct audio_hw_if *hwp = sc->hw_if;
 	struct virtual_channel *vc;
-	int n;
 	bool pbus, rbus;
 
 	pbus = rbus = false;
 	mutex_enter(sc->sc_lock);
 	audio_mixer_capture(sc);
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == -1)
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (chan == SIMPLEQ_FIRST(&sc->sc_audiochan) ||
+			chan->chan == MIXER_INUSE)
 			continue;
 
-		vc = sc->sc_vchan[n];
+		vc = chan->vc;
 		if (vc->sc_pbus && !pbus)
 			pbus = true;
 		if (vc->sc_rbus && !rbus)
@@ -5028,29 +5222,30 @@ static bool
 audio_resume(device_t dv, const pmf_qual_t *qual)
 {
 	struct audio_softc *sc = device_private(dv);
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
-	int n;
 	
 	mutex_enter(sc->sc_lock);
 	sc->sc_trigger_started = false;
 	sc->sc_rec_started = false;
 
 	audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL |
-	    AUMODE_RECORD, &sc->sc_format[0], 0);
+	    AUMODE_RECORD, &sc->sc_format[0]);
 
 	audio_mixer_restore(sc);
-	for (n = 1; n < VAUDIOCHANS; n++) {
-		if (sc->sc_audiopid[n].pid == -1)
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (chan == SIMPLEQ_FIRST(&sc->sc_audiochan) ||
+				chan->chan == MIXER_INUSE)
 			continue;
-		vc = sc->sc_vchan[n];
+		vc = chan->vc;
 
 		if (vc->sc_lastinfovalid == true)
-			audiosetinfo(sc, &vc->sc_lastinfo, true, n);
+			audiosetinfo(sc, &vc->sc_lastinfo, true, vc);
 		mutex_enter(sc->sc_intr_lock);
 		if (vc->sc_pbus == true && !vc->sc_mpr.pause)
-			audiostartp(sc, n);
+			audiostartp(sc, vc);
 		if (vc->sc_rbus == true && !vc->sc_mrr.pause)
-			audiostartr(sc, n);
+			audiostartr(sc, vc);
 		mutex_exit(sc->sc_intr_lock);
 	}
 	mutex_exit(sc->sc_lock);
@@ -5160,6 +5355,7 @@ void
 mix_read(void *arg)
 {
 	struct audio_softc *sc = arg;
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
 	stream_filter_t *filter;
 	stream_fetcher_t *fetcher;
@@ -5167,7 +5363,8 @@ mix_read(void *arg)
 	int cc, cc1, blksize, error;
 	uint8_t *inp;
 
-	vc = sc->sc_vchan[0];
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	vc = chan->vc;
 	blksize = vc->sc_mrr.blksize;
 	cc = blksize;
 
@@ -5220,6 +5417,7 @@ void
 mix_write(void *arg)
 {
 	struct audio_softc *sc = arg;
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
 	stream_filter_t *filter;
 	stream_fetcher_t *fetcher;
@@ -5227,7 +5425,8 @@ mix_write(void *arg)
 	int cc, cc1, cc2, blksize, error, used;
 	uint8_t *inp, *orig, *tocopy;
 
-	vc = sc->sc_vchan[0];
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	vc = chan->vc;
 	blksize = vc->sc_mpr.blksize;
 	cc = blksize;
 	error = 0;
@@ -5303,12 +5502,13 @@ mix_write(void *arg)
 }
 
 void
-mix_func8(struct audio_softc *sc, struct audio_ringbuffer *cb, int n)
+mix_func8(struct audio_softc *sc, struct audio_ringbuffer *cb,
+	  struct virtual_channel *vc)
 {
 	int blksize, cc, cc1, cc2, m, resid;
 	int8_t *orig, *tomix;
 
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	blksize = sc->sc_pr.blksize;
 	resid = blksize;
 
 	tomix = __UNCONST(cb->s.outp);
@@ -5325,8 +5525,8 @@ mix_func8(struct audio_softc *sc, struct
 
 		for (m = 0; m < cc;  m++) {
 			orig[m] += (int8_t)((int32_t)(tomix[m] *
-			    ((sc->sc_vchan[n]->sc_swvol + 1) * 16)) /
-			    (sc->sc_opens * VAUDIOCHANS));
+			    (vc->sc_swvol + 1)) / (sc->sc_opens *
+			    255));
 		}
 
 		if (&orig[m] >= (int8_t *)sc->sc_pr.s.end)
@@ -5339,12 +5539,13 @@ mix_func8(struct audio_softc *sc, struct
 }
 
 void
-mix_func16(struct audio_softc *sc, struct audio_ringbuffer *cb, int n)
+mix_func16(struct audio_softc *sc, struct audio_ringbuffer *cb,
+	  struct virtual_channel *vc)
 {
 	int blksize, cc, cc1, cc2, m, resid;
 	int16_t *orig, *tomix;
 
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	blksize = sc->sc_pr.blksize;
 	resid = blksize;
 
 	tomix = __UNCONST(cb->s.outp);
@@ -5361,8 +5562,8 @@ mix_func16(struct audio_softc *sc, struc
 
 		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));
+			    (vc->sc_swvol + 1)) / (sc->sc_opens *
+			    255));
 		}
 
 		if (&orig[m] >= (int16_t *)sc->sc_pr.s.end)
@@ -5375,12 +5576,13 @@ mix_func16(struct audio_softc *sc, struc
 }
 
 void
-mix_func32(struct audio_softc *sc, struct audio_ringbuffer *cb, int n)
+mix_func32(struct audio_softc *sc, struct audio_ringbuffer *cb,
+	  struct virtual_channel *vc)
 {
 	int blksize, cc, cc1, cc2, m, resid;
 	int32_t *orig, *tomix;
 
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	blksize = sc->sc_pr.blksize;
 	resid = blksize;
 
 	tomix = __UNCONST(cb->s.outp);
@@ -5397,8 +5599,8 @@ mix_func32(struct audio_softc *sc, struc
 
 		for (m = 0; m < cc / 4; m++) {
 			orig[m] += (int32_t)((int32_t)(tomix[m] *
-			    ((sc->sc_vchan[n]->sc_swvol + 1) * 16)) /
-			    (sc->sc_opens * VAUDIOCHANS));
+			    (vc->sc_swvol + 1)) / (sc->sc_opens *
+			    255));
 		}
 
 		if (&orig[m] >= (int32_t *)sc->sc_pr.s.end)
@@ -5411,18 +5613,19 @@ mix_func32(struct audio_softc *sc, struc
 }
 
 void
-mix_func(struct audio_softc *sc, struct audio_ringbuffer *cb, int n)
+mix_func(struct audio_softc *sc, struct audio_ringbuffer *cb,
+	 struct virtual_channel *vc)
 {
 	switch (sc->sc_precision) {
 	case 8:
-		mix_func8(sc, cb, n);
+		mix_func8(sc, cb, vc);
 		break;
 	case 16:
-		mix_func16(sc, cb, n);
+		mix_func16(sc, cb, vc);
 		break;
 	case 24:
 	case 32:
-		mix_func32(sc, cb, n);
+		mix_func32(sc, cb, vc);
 		break;
 	default:
 		break;
@@ -5435,7 +5638,7 @@ saturate_func8(struct audio_softc *sc)
 	int blksize, m, i, resid;
 	int8_t *orig;
 
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	blksize = sc->sc_pr.blksize;
 	resid = blksize;
 	if (sc->sc_trigger_started == false)
 		resid *= 2;
@@ -5467,7 +5670,7 @@ saturate_func16(struct audio_softc *sc)
 	int blksize, m, i, resid;
 	int16_t *orig;
 
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	blksize = sc->sc_pr.blksize;
 	resid = blksize;
 	if (sc->sc_trigger_started == false)
 		resid *= 2;
@@ -5499,7 +5702,7 @@ saturate_func32(struct audio_softc *sc)
 	int blksize, m, i, resid;
 	int32_t *orig;
 
-	blksize = sc->sc_vchan[0]->sc_mpr.blksize;
+	blksize = sc->sc_pr.blksize;
 	resid = blksize;
 	if (sc->sc_trigger_started == false)
 		resid *= 2;
@@ -5545,8 +5748,8 @@ saturate_func(struct audio_softc *sc)
 }
 
 void
-recswvol_func8(struct audio_softc *sc, struct audio_ringbuffer *cb, int n,
-    size_t blksize)
+recswvol_func8(struct audio_softc *sc, struct audio_ringbuffer *cb,
+    size_t blksize, struct virtual_channel *vc)
 {
 	int cc, cc1, m, resid;
 	int8_t *orig;
@@ -5562,7 +5765,7 @@ recswvol_func8(struct audio_softc *sc, s
 
 		for (m = 0; m < cc; m++) {
 			orig[m] = (int16_t)(orig[m] *
-			    (int16_t)(sc->sc_vchan[n]->sc_recswvol) / 256);
+			    (int16_t)(vc->sc_recswvol) / 256);
 		}
 		orig = cb->s.start;
 
@@ -5571,8 +5774,8 @@ recswvol_func8(struct audio_softc *sc, s
 }
 
 void
-recswvol_func16(struct audio_softc *sc, struct audio_ringbuffer *cb, int n,
-    size_t blksize)
+recswvol_func16(struct audio_softc *sc, struct audio_ringbuffer *cb,
+    size_t blksize, struct virtual_channel *vc)
 {
 	int cc, cc1, m, resid;
 	int16_t *orig;
@@ -5588,7 +5791,7 @@ recswvol_func16(struct audio_softc *sc, 
 
 		for (m = 0; m < cc / 2; m++) {
 			orig[m] = (int32_t)(orig[m] *
-			    (int32_t)(sc->sc_vchan[n]->sc_recswvol) / 256);
+			    (int32_t)(vc->sc_recswvol) / 256);
 		}
 		orig = (uint16_t *)cb->s.start;
 
@@ -5597,8 +5800,8 @@ recswvol_func16(struct audio_softc *sc, 
 }
 
 void
-recswvol_func32(struct audio_softc *sc, struct audio_ringbuffer *cb, int n,
-    size_t blksize)
+recswvol_func32(struct audio_softc *sc, struct audio_ringbuffer *cb,
+    size_t blksize, struct virtual_channel *vc)
 {
 	int cc, cc1, m, resid;
 	int32_t *orig;
@@ -5614,7 +5817,7 @@ recswvol_func32(struct audio_softc *sc, 
 
 		for (m = 0; m < cc / 2; m++) {
 			orig[m] = (int64_t)(orig[m] *
-			    (int64_t)(sc->sc_vchan[n]->sc_recswvol) / 256);
+			    (int64_t)(vc->sc_recswvol) / 256);
 		}
 		orig = (uint32_t *)cb->s.start;
 
@@ -5623,19 +5826,19 @@ recswvol_func32(struct audio_softc *sc, 
 }
 
 void
-recswvol_func(struct audio_softc *sc, struct audio_ringbuffer *cb, int n,
-    size_t blksize)
+recswvol_func(struct audio_softc *sc, struct audio_ringbuffer *cb,
+    size_t blksize, struct virtual_channel *vc)
 {
 	switch (sc->sc_precision) {
 	case 8:
-		recswvol_func8(sc, cb, n, blksize);
+		recswvol_func8(sc, cb, blksize, vc);
 		break;
 	case 16:
-		recswvol_func16(sc, cb, n, blksize);
+		recswvol_func16(sc, cb, blksize, vc);
 		break;
 	case 24:
 	case 32:
-		recswvol_func32(sc, cb, n, blksize);
+		recswvol_func32(sc, cb, blksize, vc);
 		break;
 	default:
 		break;
@@ -5645,14 +5848,20 @@ recswvol_func(struct audio_softc *sc, st
 static uint8_t *
 find_vchan_vol(struct audio_softc *sc, int d)
 {
+	struct audio_chan *chan;
 	size_t j, n = (size_t)d / 2;
 	
-	for (size_t i = j = 0; i <= n; i++)
-		if (sc->sc_audiopid[i].pid != -1)
-			j++;
-
+	j = 0;
+	SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) {
+		if (chan == SIMPLEQ_FIRST(&sc->sc_audiochan) ||
+		    chan->chan == MIXER_INUSE)
+			continue;
+		if (j == n)
+			break;
+		j++;
+	}
 	return (d & 1) == 0 ?
-	    &sc->sc_vchan[j]->sc_swvol : &sc->sc_vchan[j]->sc_recswvol;
+	    &chan->vc->sc_swvol : &chan->vc->sc_recswvol;
 }
 
 static int
@@ -5662,6 +5871,8 @@ audio_set_port(struct audio_softc *sc, m
 
 	int d = mc->dev - sc->sc_static_nmixer_states;
 
+	if (d == -1)
+		return 0;
 	if (d < 0)
 		return sc->hw_if->set_port(sc->hw_hdl, mc);
 
@@ -5680,6 +5891,8 @@ audio_get_port(struct audio_softc *sc, m
 
 	int d = mc->dev - sc->sc_static_nmixer_states;
 
+	if (d == -1)
+		return 0;
 	if (d < 0)
 		return sc->hw_if->get_port(sc->hw_hdl, mc);
 
@@ -5697,10 +5910,15 @@ audio_query_devinfo(struct audio_softc *
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	if (di->index >= sc->sc_static_nmixer_states && di->index <
-	    sc->sc_nmixer_states) {
-		if ((di->index - sc->sc_static_nmixer_states) % 2 == 0) {
-			di->mixer_class = AUDIO_OUTPUT_CLASS;
+	if (di->index >= sc->sc_static_nmixer_states - 1 &&
+	    di->index < sc->sc_nmixer_states) {
+		if (di->index == sc->sc_static_nmixer_states - 1) {
+			di->mixer_class = sc->sc_static_nmixer_states -1;
+			di->next = di->prev = AUDIO_MIXER_LAST;
+			strcpy(di->label.name, AudioCvirtchan);
+			di->type = AUDIO_MIXER_CLASS;
+		} else if ((di->index - sc->sc_static_nmixer_states) % 2 == 0) {
+			di->mixer_class = sc->sc_static_nmixer_states -1;
 			snprintf(mixLabel, sizeof(mixLabel), AudioNdac"%d",
 			    (di->index - sc->sc_static_nmixer_states) / 2);
 			strcpy(di->label.name, mixLabel);
@@ -5709,10 +5927,10 @@ audio_query_devinfo(struct audio_softc *
 			di->un.v.num_channels = 1;
 			strcpy(di->un.v.units.name, AudioNvolume);
 		} else {
-			di->mixer_class = AUDIO_INPUT_CLASS;
-			snprintf(mixLabel, sizeof(mixLabel), AudioNmicrophone
-			    "%d", (di->index - sc->sc_static_nmixer_states) /
-			     2);
+			di->mixer_class = sc->sc_static_nmixer_states -1;
+			snprintf(mixLabel, sizeof(mixLabel),
+			    AudioNmicrophone "%d",
+			    (di->index - sc->sc_static_nmixer_states) / 2);
 			strcpy(di->label.name, mixLabel);
 			di->type = AUDIO_MIXER_VALUE;
 			di->next = di->prev = AUDIO_MIXER_LAST;
@@ -5729,13 +5947,17 @@ audio_query_devinfo(struct audio_softc *
 
 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)
+		 audio_params_t *play, audio_params_t *rec,
+		 stream_filter_list_t *pfil, stream_filter_list_t *rfil,
+		 struct virtual_channel *vc)
 {
+	struct audio_chan *chan;
+	
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
 	int error = 0;
 	KASSERT(mutex_owned(sc->sc_lock));
 
-	if (n == 0 && sc->hw_if->set_params != NULL) {
+	if (vc == chan->vc && sc->hw_if->set_params != NULL) {
 		sc->sc_ready = true;
 		if (sc->sc_precision == 8)
 			play->encoding = rec->encoding = AUDIO_ENCODING_SLINEAR;
@@ -5843,7 +6065,7 @@ audio_sysctl_frequency(SYSCTLFN_ARGS)
 
 	sc->sc_iffreq = t;
 	error = audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL
-	    | AUMODE_RECORD, &sc->sc_format[0], 0);
+	    | AUMODE_RECORD, &sc->sc_format[0]);
 	if (error)
 		aprint_error_dev(sc->sc_dev, "Error setting frequency, "
 				 "please check hardware capabilities\n");
@@ -5895,7 +6117,7 @@ audio_sysctl_precision(SYSCTLFN_ARGS)
  		sc->sc_format[0].encoding = AUDIO_ENCODING_SLINEAR_LE;
 
 	error = audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL
-	    | AUMODE_RECORD, &sc->sc_format[0], 0);
+	    | AUMODE_RECORD, &sc->sc_format[0]);
 	if (error)
 		aprint_error_dev(sc->sc_dev, "Error setting precision, "
 				 "please check hardware capabilities\n");
@@ -5936,7 +6158,7 @@ audio_sysctl_channels(SYSCTLFN_ARGS)
 
 	sc->sc_channels = t;
 	error = audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL
-	    | AUMODE_RECORD, &sc->sc_format[0], 0);
+	    | AUMODE_RECORD, &sc->sc_format[0]);
 	if (error)
 		aprint_error_dev(sc->sc_dev, "Error setting channels, "
 				 "please check hardware capabilities\n");
@@ -5948,10 +6170,12 @@ audio_sysctl_channels(SYSCTLFN_ARGS)
 static int
 vchan_autoconfig(struct audio_softc *sc)
 {
+	struct audio_chan *chan;
 	struct virtual_channel *vc;
 	int error, i, j, k;
 
-	vc = sc->sc_vchan[0];
+	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
+	vc = chan->vc;
 	error = 0;
 
 	mutex_enter(sc->sc_lock);
@@ -5964,7 +6188,7 @@ vchan_autoconfig(struct audio_softc *sc)
 				sc->sc_iffreq = auto_config_freq[k];
 				error = audio_set_vchan_defaults(sc,
 				    AUMODE_PLAY | AUMODE_PLAY_ALL |
-				    AUMODE_RECORD, &sc->sc_format[0], 0);
+				    AUMODE_RECORD, &sc->sc_format[0]);
 				if (vc->sc_npfilters > 0 &&
 				    (vc->sc_mpr.s.param.
 					sample_rate != sc->sc_iffreq ||
@@ -5994,4 +6218,34 @@ vchan_autoconfig(struct audio_softc *sc)
 	return EINVAL;
 }
 
+static void
+grow_mixer_states(struct audio_softc *sc, int count)
+{
+	mixer_ctrl_t *tmp_mixer_state;
+	size_t origlen = sizeof(mixer_ctrl_t) * (sc->sc_nmixer_states + 1);
+	size_t newlen = sizeof(mixer_ctrl_t) * count + origlen;
+
+	tmp_mixer_state = kmem_zalloc(newlen, KM_SLEEP);
+	memcpy(tmp_mixer_state, sc->sc_mixer_state, origlen);
+
+	sc->sc_nmixer_states += count;
+	kmem_free(sc->sc_mixer_state, origlen);
+	sc->sc_mixer_state = tmp_mixer_state;
+}	
+
+static void
+shrink_mixer_states(struct audio_softc *sc, int count)
+{
+	mixer_ctrl_t *tmp_mixer_state;
+	size_t origlen = sizeof(mixer_ctrl_t) * (sc->sc_nmixer_states + 1);
+	size_t newlen = origlen - sizeof(mixer_ctrl_t) * count;
+
+	tmp_mixer_state = kmem_zalloc(newlen, KM_SLEEP);
+	memcpy(tmp_mixer_state, sc->sc_mixer_state, newlen);
+
+	sc->sc_nmixer_states -= count;
+	kmem_free(sc->sc_mixer_state, origlen);
+	sc->sc_mixer_state = tmp_mixer_state;
+}	
+
 #endif /* NAUDIO > 0 */

Index: src/sys/dev/audiobell.c
diff -u src/sys/dev/audiobell.c:1.12 src/sys/dev/audiobell.c:1.13
--- src/sys/dev/audiobell.c:1.12	Tue Dec 13 20:18:32 2016
+++ src/sys/dev/audiobell.c	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiobell.c,v 1.12 2016/12/13 20:18:32 christos Exp $	*/
+/*	$NetBSD: audiobell.c,v 1.13 2017/02/10 19:31:42 nat Exp $	*/
 
 
 /*
@@ -32,25 +32,29 @@
  */
 
 #include <sys/types.h>
-__KERNEL_RCSID(0, "$NetBSD: audiobell.c,v 1.12 2016/12/13 20:18:32 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audiobell.c,v 1.13 2017/02/10 19:31:42 nat Exp $");
 
 #include <sys/audioio.h>
 #include <sys/conf.h>
 #include <sys/device.h>
 #include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
 #include <sys/ioctl.h>
 #include <sys/malloc.h>
+#include <sys/mutex.h>
 #include <sys/null.h>
 #include <sys/systm.h>
 #include <sys/uio.h>
+#include <sys/unistd.h>
 
 #include <dev/audio_if.h>
 #include <dev/audiobellvar.h>
 
-extern dev_type_open(audioopen);
-extern dev_type_ioctl(audioioctl);
-extern dev_type_write(audiowrite);
-extern dev_type_close(audioclose);
+extern int audiobellopen(dev_t, int, int, struct lwp *, struct file **);
+extern int audioclose(struct file *);
+extern int audiowrite(struct file *, off_t *, struct uio *, kauth_cred_t, int);
+extern int audioioctl(struct file *, u_long, void *);
 
 /* Convert a %age volume to an amount to add to u-law values */
 /* XXX Probably highly inaccurate -- should be regenerated */
@@ -143,21 +147,32 @@ audiobell(void *v, u_int pitch, u_int pe
 	struct audio_info ai;
 	struct uio auio;
 	struct iovec aiov;
+	struct file *fp;
 	int size, len, offset;
+
+	fp = NULL;
 	dev_t audio = AUDIO_DEVICE | device_unit((device_t)v);
 
 	/* The audio system isn't built for polling. */
 	if (poll) return;
 
 	/* If not configured, we can't beep. */
-	if (audioopen(audio, FWRITE, 0, NULL) != 0)
+	if (audiobellopen(audio, FWRITE, 0, NULL, &fp) != EMOVEFD || fp == NULL)
 		return;
+	mutex_enter(&fp->f_lock);
+	fp->f_count++;
+	mutex_exit(&fp->f_lock);
 
-	if (audioioctl(audio, AUDIO_GETINFO, &ai, 0, NULL) != 0)
+	if (audioioctl(fp, AUDIO_GETINFO, &ai) != 0) {
+		audioclose(fp);
 		return;
+	}
 
 	buf = NULL;
 
+	if (ai.blocksize < 8192)
+		ai.blocksize = 8192;
+
 	len = period * 8;
 	size = min(len, ai.blocksize);
 	if (size == 0) goto out;
@@ -179,11 +194,12 @@ audiobell(void *v, u_int pitch, u_int pe
 		auio.uio_rw = UIO_WRITE;
 		UIO_SETUP_SYSSPACE(&auio);
 
-		audiowrite(audio, &auio, 0);
+		audiowrite(fp, NULL, &auio, NULL, 0);
 		len -= size;
 		offset += size;
 	}
 out:
 	if (buf != NULL) free(buf, M_TEMP);
-	audioclose(audio, FWRITE, 0, NULL);
+	audioclose(fp);
+	closef(fp);
 }

Index: src/sys/dev/audiovar.h
diff -u src/sys/dev/audiovar.h:1.49 src/sys/dev/audiovar.h:1.50
--- src/sys/dev/audiovar.h:1.49	Sun Jan 15 07:46:57 2017
+++ src/sys/dev/audiovar.h	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiovar.h,v 1.49 2017/01/15 07:46:57 isaki Exp $	*/
+/*	$NetBSD: audiovar.h,v 1.50 2017/02/10 19:31:42 nat Exp $	*/
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -68,6 +68,7 @@
 
 #include <sys/condvar.h>
 #include <sys/proc.h>
+#include <sys/queue.h>
 
 #include <dev/audio_if.h>
 #include <dev/auconv.h>
@@ -103,9 +104,14 @@ struct audio_ringbuffer {
 	bool mmapped;		/* device is mmap()-ed */
 };
 
-#ifndef VAUDIOCHANS
-#define VAUDIOCHANS 4096
-#endif
+struct audio_chan {
+	dev_t	dev;
+#define MIXER_INUSE	-2
+	struct virtual_channel	*vc;
+	int	chan;			/* virtual channel */
+	int	deschan;		/* desired channel for ioctls*/
+	SIMPLEQ_ENTRY(audio_chan) entries;
+};
 
 struct virtual_channel {
 	u_char			sc_open;	/* multiple use device */
@@ -159,6 +165,7 @@ struct au_mixer_ports {
 				   mixerout is selected, for dual case */
 };
 
+SIMPLEQ_HEAD(chan_queue, audio_chan);
 /*
  * Software state, per audio device.
  */
@@ -167,10 +174,7 @@ struct audio_softc {
 	void		*hw_hdl;	/* Hardware driver handle */
 	const struct audio_hw_if *hw_if; /* Hardware interface */
 	device_t	sc_dev;		/* Hardware device struct */
-	struct audio_pid
-			sc_audiopid[VAUDIOCHANS]; /* audio caller */
-	struct audio_pid
-			sc_despid[VAUDIOCHANS]; /* process to work with ioctls */
+	struct chan_queue sc_audiochan; /* queue of open chans */
 #define AUOPEN_READ	0x01
 #define AUOPEN_WRITE	0x02
 
@@ -180,7 +184,6 @@ struct audio_softc {
 	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;
@@ -215,7 +218,7 @@ struct audio_softc {
 	 * play_thread
 	 *    sc_pr
 	 *      |
-	 *  vchan[0]->sc_pustream
+	 *  vchan[0]->sc_pustream 	(First elemendt in sc_audiochan)
 	 *      |
 	 *  vchan[0]->sc_mpr
 	 *      |
@@ -287,6 +290,9 @@ struct audio_softc {
 #define VAUDIO_NFORMATS	1
 	struct audio_format sc_format[VAUDIO_NFORMATS];
 	struct audio_params sc_vchan_params;
+
+	bool		sc_multiuser;
+	kauth_cred_t	sc_credentials;		/* audio user's credentials */
 };
 
 #endif /* _SYS_DEV_AUDIOVAR_H_ */

Index: src/sys/sys/audioio.h
diff -u src/sys/sys/audioio.h:1.35 src/sys/sys/audioio.h:1.36
--- src/sys/sys/audioio.h:1.35	Thu Dec  8 10:28:44 2016
+++ src/sys/sys/audioio.h	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: audioio.h,v 1.35 2016/12/08 10:28:44 nat Exp $	*/
+/*	$NetBSD: audioio.h,v 1.36 2017/02/10 19:31:42 nat Exp $	*/
 
 /*
  * Copyright (c) 1991-1993 Regents of the University of California.
@@ -143,11 +143,6 @@ typedef struct audio_encoding {
 #define AUDIO_ENCODINGFLAG_EMULATED 1 /* software emulation mode */
 } audio_encoding_t;
 
-struct audio_pid {
-	pid_t	pid;			/* for audio device belonging to pid */
-	lwpid_t	lwpid;			/* unused */
-};
-
 /*
  * Balance settings.
  */
@@ -162,6 +157,7 @@ struct audio_pid {
 #define	AUDIO_SPEAKER		0x01	/* built-in speaker */
 #define	AUDIO_HEADPHONE		0x02	/* headphone jack */
 #define	AUDIO_LINE_OUT		0x04	/* line out	 */
+#define	VC_OUT			0x08	/* virt chan out */
 
 /*
  * Input ports
@@ -170,6 +166,7 @@ struct audio_pid {
 #define	AUDIO_LINE_IN		0x02	/* line in	 */
 #define	AUDIO_CD		0x04	/* on-board CD inputs */
 #define	AUDIO_INTERNAL_CD_IN	AUDIO_CD	/* internal CDROM */
+#define	VC_IN			0x08	/* virt chan in */
 
 /*
  * Audio device operations
@@ -194,7 +191,7 @@ struct audio_pid {
 #define  AUDIO_PROP_PLAYBACK	0x10
 #define  AUDIO_PROP_CAPTURE	0x20
 #define AUDIO_GETBUFINFO	_IOR('A', 35, struct audio_info)
-#define AUDIO_SETPROC	_IOW('A', 36, struct audio_pid)
+#define AUDIO_SETPROC	_IOW('A', 36, int)
 
 /*
  * Mixer device
@@ -340,5 +337,6 @@ typedef struct mixer_ctrl {
 #define AudioCmonitor	"monitor"
 #define AudioCequalization	"equalization"
 #define AudioCmodem	"modem"
+#define AudioCvirtchan	"vchan"
 
 #endif /* !_SYS_AUDIOIO_H_ */

Index: src/sys/sys/file.h
diff -u src/sys/sys/file.h:1.80 src/sys/sys/file.h:1.81
--- src/sys/sys/file.h:1.80	Thu Jan 12 18:50:17 2017
+++ src/sys/sys/file.h	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: file.h,v 1.80 2017/01/12 18:50:17 christos Exp $	*/
+/*	$NetBSD: file.h,v 1.81 2017/02/10 19:31:42 nat Exp $	*/
 
 /*-
  * Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -103,6 +103,7 @@ union file_data {
 	struct kqueue *fd_kq;		// DTYPE_KQUEUE
 	void *fd_data;			// DTYPE_MISC
 	struct rnd_ctx *fd_rndctx;	// DTYPE_MISC (rnd)
+	struct audio_chan *fd_audioctx;	// DTYPE_MISC (audio)
 	int fd_devunit;			// DTYPE_MISC (tap)
 	struct bpf_d *fd_bpf;		// DTYPE_MISC (bpf)
 	struct fcrypt *fd_fcrypt;	// DTYPE_CRYPTO is not used
@@ -144,6 +145,7 @@ struct file {
 #define f_ksem		f_undata.fd_ks
 
 #define f_rndctx	f_undata.fd_rndctx
+#define f_audioctx	f_undata.fd_audioctx
 #define f_devunit	f_undata.fd_devunit
 #define f_bpf		f_undata.fd_bpf
 #define f_fcrypt	f_undata.fd_fcrypt

Index: src/usr.bin/audio/ctl/audioctl.1
diff -u src/usr.bin/audio/ctl/audioctl.1:1.20 src/usr.bin/audio/ctl/audioctl.1:1.21
--- src/usr.bin/audio/ctl/audioctl.1:1.20	Thu Dec  8 10:28:44 2016
+++ src/usr.bin/audio/ctl/audioctl.1	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-.\" $NetBSD: audioctl.1,v 1.20 2016/12/08 10:28:44 nat Exp $
+.\" $NetBSD: audioctl.1,v 1.21 2017/02/10 19:31:42 nat Exp $
 .\"
 .\" Copyright (c) 1997, 1999 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -35,17 +35,17 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar device
-.Op Fl p Ar pid
+.Op Fl p Ar channel
 .Op Fl n
 .Fl a
 .Nm
 .Op Fl d Ar device
-.Op Fl p Ar pid
+.Op Fl p Ar channel
 .Op Fl n
 .Ar name ...
 .Nm
 .Op Fl d Ar device
-.Op Fl p Ar pid
+.Op Fl p Ar channel
 .Op Fl n
 .Fl w
 .Ar name=value ...
@@ -72,8 +72,8 @@ flag can be used to give an alternative 
 .Pp
 The
 .Fl p
-flag can be used to give a virtual channel pid to control, the default is
-current process id.
+flag can be used to give a virtual channel to control, the default is a new
+channel.
 .Pp
 The
 .Fl n
@@ -93,8 +93,7 @@ audio control device
 audio I/O device (does not reset on open)
 .El
 .Sh EXAMPLES
-To set the playing sampling rate to 11025, for the channel opened by pid 3
-you can use
+To set the playing sampling rate to 11025, for the channel 3 you can use
 .Dl audioctl -p 3 -w play.sample_rate=11025
 To set all of the play parameters for CD-quality audio, you can use
 .Dl audioctl -p 3 -w play=44100,2,16,slinear_le

Index: src/usr.bin/audio/ctl/ctl.c
diff -u src/usr.bin/audio/ctl/ctl.c:1.41 src/usr.bin/audio/ctl/ctl.c:1.42
--- src/usr.bin/audio/ctl/ctl.c:1.41	Thu Dec  8 10:28:44 2016
+++ src/usr.bin/audio/ctl/ctl.c	Fri Feb 10 19:31:42 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ctl.c,v 1.41 2016/12/08 10:28:44 nat Exp $	*/
+/*	$NetBSD: ctl.c,v 1.42 2017/02/10 19:31:42 nat Exp $	*/
 
 /*
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: ctl.c,v 1.41 2016/12/08 10:28:44 nat Exp $");
+__RCSID("$NetBSD: ctl.c,v 1.42 2017/02/10 19:31:42 nat Exp $");
 #endif
 
 
@@ -66,8 +66,7 @@ static char encbuf[1000];
 
 static int properties, fullduplex, rerror;
 
-static struct audio_pid vchan_pid;
-
+int channel;
 int verbose;
 
 static struct field {
@@ -292,7 +291,7 @@ getinfo(int fd)
 {
 	int pos, i;
 
-	if (vchan_pid.pid >= 0 && ioctl(fd, AUDIO_SETPROC, &vchan_pid) < 0)
+	if (channel >= 0 && ioctl(fd, AUDIO_SETPROC, &channel) < 0)
 		err(1, "AUDIO_SETPROC");
 
 	if (ioctl(fd, AUDIO_GETDEV, &adev) < 0)
@@ -327,9 +326,11 @@ usage(void)
 {
 	const char *prog = getprogname();
 
-	fprintf(stderr, "Usage: %s [-d file] [-p] pid [-n] name ...\n", prog);
-	fprintf(stderr, "Usage: %s [-d file] [-p] pid [-n] -w name=value ...\n", prog);
-	fprintf(stderr, "Usage: %s [-d file] [-p] pid [-n] -a\n", prog);
+	fprintf(stderr, "Usage: %s [-d file] [-p] channel "
+			"[-n] name ...\n", prog);
+	fprintf(stderr, "Usage: %s [-d file] [-p] channel [-n] "
+			"-w name=value ...\n", prog);
+	fprintf(stderr, "Usage: %s [-d file] [-p] channel [-n] -a\n", prog);
 	exit(1);
 }
 
@@ -342,7 +343,7 @@ main(int argc, char *argv[])
 	const char *file;
 	const char *sep = "=";
 
-	vchan_pid.pid = -1;
+	channel = -1;
 	file = getenv("AUDIOCTLDEVICE");
 	if (file == NULL)
 		file = deffile;
@@ -359,7 +360,7 @@ main(int argc, char *argv[])
 			sep = 0;
 			break;
 		case 'p':
-			vchan_pid.pid = atoi(optarg);
+			channel = atoi(optarg);
 			break;
 		case 'f': /* compatibility */
 		case 'd':
@@ -403,7 +404,8 @@ main(int argc, char *argv[])
 				getinfo(fd);
 				for (i = 0; fields[i].name; i++) {
 					if (fields[i].flags & SET) {
-						printf("%s: -> ", fields[i].name);
+						printf("%s: -> ",
+						    fields[i].name);
 						prfield(&fields[i], 0);
 						printf("\n");
 					}
@@ -415,9 +417,12 @@ main(int argc, char *argv[])
 				p = findfield(*argv);
 				if (p == 0) {
 					if (strchr(*argv, '='))
-						warnx("field %s does not exist (use -w to set a variable)", *argv);
+						warnx("field %s does not exist "
+						      "(use -w to set a "
+						      "variable)", *argv);
 					else
-						warnx("field %s does not exist", *argv);
+						warnx("field %s does not exist",
+						      *argv);
 				} else {
 					prfield(p, sep);
 					printf("\n");
@@ -435,7 +440,7 @@ audioctl_write(int fd, int argc, char *a
 {
 	struct field *p;
 
-	if (vchan_pid.pid >= 0 && ioctl(fd, AUDIO_SETPROC, &vchan_pid) < 0)
+	if (channel >= 0 && ioctl(fd, AUDIO_SETPROC, &channel) < 0)
 		err(1, "AUDIO_SETPROC");
 
 	AUDIO_INITINFO(&info);
@@ -454,7 +459,8 @@ audioctl_write(int fd, int argc, char *a
 				else {
 					rdfield(p, q);
 					if (p->valp == &fullduplex)
-						if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0)
+						if (ioctl(fd, AUDIO_SETFD,
+							   &fullduplex) < 0)
 							err(1, "set failed");
 				}
 			}

Reply via email to