changeset 10632:704f6d4f41c9

6885017 audio output tries to queue up too many fragments 6885023 audio framework can lose taskqs 6885091 audio1575 gets offset wrong by about 1 fragment
author Garrett D'Amore <gdamore@opensolaris.org>
date Wed, 23 Sep 2009 20:55:51 -0700
parents abfe11cf0ce1
children 62ff2de09191
files usr/src/uts/common/io/audio/drv/audio1575/audio1575.c usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c usr/src/uts/common/io/audio/impl/audio_client.c usr/src/uts/common/io/audio/impl/audio_client.h usr/src/uts/common/io/audio/impl/audio_ctrl.c usr/src/uts/common/io/audio/impl/audio_ddi.c usr/src/uts/common/io/audio/impl/audio_impl.h usr/src/uts/common/io/audio/impl/audio_input.c usr/src/uts/common/io/audio/impl/audio_oss.c usr/src/uts/common/io/audio/impl/audio_output.c usr/src/uts/common/io/audio/impl/audio_sun.c
diffstat 11 files changed, 93 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/audio/drv/audio1575/audio1575.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/drv/audio1575/audio1575.c	Wed Sep 23 20:55:51 2009 -0700
@@ -680,7 +680,7 @@
 
 	mutex_enter(&statep->lock);
 	audio1575_update_port(port);
-	val = port->count + port->picb;
+	val = port->count + (port->picb / port->nchan);
 	mutex_exit(&statep->lock);
 
 	return (val);
--- a/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c	Wed Sep 23 20:55:51 2009 -0700
@@ -564,21 +564,9 @@
 	}
 
 	/*
-	 * We choose 6 fragments for a specific reason.	 Since the
-	 * device only has full and half interrupts, and since the
-	 * framework will try to queue up 4 frags automatically, this
-	 * ensures that we will be able to queue all 4 fragments, and
-	 * it avoids a potential underrun that you would get with 8
-	 * fragments.  (More than 8 fragments is guaranteed to cause
-	 * underruns in Boomer.)
-	 *
-	 * Boomer needs to get smarter about dealing with devices with
-	 * fewer fragment counts.  This device, for instance, should
-	 * really be represented with just two fragments.  That wll
-	 * cause an infinite loop in Boomer, when Boomer tries to
-	 * queue up 4 fragments.
+	 * This needs the very latest Boomer changes.
 	 */
-	port->nfrags = 6;
+	port->nfrags = 2;
 	port->fragfr = 48000 / port->intrs;
 	/*
 	 * The device operates in pairs of dwords at a time, for
--- a/usr/src/uts/common/io/audio/impl/audio_client.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_client.c	Wed Sep 23 20:55:51 2009 -0700
@@ -674,10 +674,6 @@
 		sp->s_gain_eff = sp->s_gain_scaled;
 	}
 	mutex_exit(&sp->s_lock);
-
-	/*
-	 * No need to notify clients, since they can't see this update.
-	 */
 }
 
 int
@@ -743,7 +739,7 @@
 	}
 	mutex_exit(&sp->s_lock);
 
-	auclnt_notify_dev(sp->s_client->c_dev);
+	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
 }
 
 uint8_t
@@ -771,7 +767,7 @@
 	}
 	mutex_exit(&sp->s_lock);
 
-	auclnt_notify_dev(sp->s_client->c_dev);
+	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
 }
 
 boolean_t
@@ -832,7 +828,7 @@
 
 	auclnt_stop(sp);
 
-	auclnt_notify_dev(sp->s_client->c_dev);
+	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
 }
 
 void
@@ -973,64 +969,6 @@
 	}
 }
 
-void
-auimpl_client_task(void *arg)
-{
-	audio_client_t		*c = arg;
-
-	mutex_enter(&c->c_lock);
-
-	for (;;) {
-		if (c->c_closing) {
-			break;
-		}
-
-		if (c->c_do_output) {
-			c->c_do_output = B_FALSE;
-
-			mutex_exit(&c->c_lock);
-			if (c->c_output != NULL)
-				c->c_output(c);
-			mutex_enter(&c->c_lock);
-			continue;
-		}
-
-		if (c->c_do_input) {
-			c->c_do_input = B_FALSE;
-
-			mutex_exit(&c->c_lock);
-			if (c->c_input != NULL)
-				c->c_input(c);
-			mutex_enter(&c->c_lock);
-			continue;
-		}
-
-		if (c->c_do_notify) {
-			c->c_do_notify = B_FALSE;
-
-			mutex_exit(&c->c_lock);
-			if (c->c_notify != NULL)
-				c->c_notify(c);
-			mutex_enter(&c->c_lock);
-			continue;
-		}
-
-		if (c->c_do_drain) {
-			c->c_do_drain = B_FALSE;
-
-			mutex_exit(&c->c_lock);
-			if (c->c_drain != NULL)
-				c->c_drain(c);
-			mutex_enter(&c->c_lock);
-			continue;
-		}
-
-		/* if we got here, we had no work to do */
-		cv_wait(&c->c_cv, &c->c_lock);
-	}
-	mutex_exit(&c->c_lock);
-}
-
 int
 auclnt_start_drain(audio_client_t *c)
 {
@@ -1084,8 +1022,6 @@
 	list_t			*list = &auimpl_clients;
 	minor_t			minor;
 	audio_dev_t		*d;
-	char			scratch[80];
-	static uint64_t		unique = 0;
 
 	/* validate minor number */
 	minor = getminor(dev) & AUDIO_MN_TYPE_MASK;
@@ -1119,14 +1055,6 @@
 	c->c_origminor =	getminor(dev);
 	c->c_ops =		*ops;
 
-	(void) snprintf(scratch, sizeof (scratch), "auclnt%" PRIx64,
-	    atomic_inc_64_nv(&unique));
-	c->c_tq = ddi_taskq_create(NULL, scratch, 1, TASKQ_DEFAULTPRI, 0);
-	if (c->c_tq == NULL) {
-		audio_dev_warn(d, "client taskq_create failed");
-		goto failed;
-	}
-
 	/*
 	 * We hold the client lock here.
 	 */
@@ -1153,9 +1081,6 @@
 
 failed:
 	auimpl_dev_release(d);
-	if (c->c_tq != NULL) {
-		ddi_taskq_destroy(c->c_tq);
-	}
 	audio_stream_fini(&c->c_ostream);
 	audio_stream_fini(&c->c_istream);
 	mutex_destroy(&c->c_lock);
@@ -1179,8 +1104,6 @@
 	auimpl_dev_release(c->c_dev);
 	c->c_dev = NULL;
 
-	ddi_taskq_destroy(c->c_tq);
-
 	mutex_destroy(&c->c_lock);
 	cv_destroy(&c->c_cv);
 
@@ -1223,13 +1146,8 @@
 	while (c->c_refcnt) {
 		cv_wait(&c->c_cv, &c->c_lock);
 	}
-	c->c_closing = B_TRUE;
-	cv_broadcast(&c->c_cv);
 	mutex_exit(&c->c_lock);
 
-	/* make sure taskq has drained */
-	ddi_taskq_wait(c->c_tq);
-
 	/* release any engines that we were holding */
 	auimpl_engine_close(&c->c_ostream);
 	auimpl_engine_close(&c->c_istream);
@@ -1286,6 +1204,12 @@
 	mutex_exit(&c->c_lock);
 }
 
+unsigned
+auclnt_dev_get_serial(audio_dev_t *d)
+{
+	return (d->d_serial);
+}
+
 void
 auclnt_dev_walk_clients(audio_dev_t *d,
     int (*walker)(audio_client_t *, void *),
@@ -1356,12 +1280,6 @@
 		}
 	}
 
-	if (ddi_taskq_dispatch(c->c_tq, auimpl_client_task, c, DDI_NOSLEEP) !=
-	    DDI_SUCCESS) {
-		audio_dev_warn(d, "unable to start client taskq");
-		rv = ENOMEM;
-	}
-
 done:
 	if (rv != 0) {
 		/* close any engines that we opened */
@@ -1510,24 +1428,6 @@
 	return (caps);
 }
 
-void
-auclnt_notify_dev(audio_dev_t *dev)
-{
-	list_t *l = &dev->d_clients;
-	audio_client_t *c;
-
-	rw_enter(&auimpl_client_lock, RW_READER);
-	for (c = list_head(l); c != NULL; c = list_next(l, c)) {
-		if (!c->c_is_active)
-			continue;
-		mutex_enter(&c->c_lock);
-		c->c_do_notify = B_TRUE;
-		cv_broadcast(&c->c_cv);
-		mutex_exit(&c->c_lock);
-	}
-	rw_exit(&auimpl_client_lock);
-}
-
 uint64_t
 auclnt_get_samples(audio_stream_t *sp)
 {
--- a/usr/src/uts/common/io/audio/impl/audio_client.h	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_client.h	Wed Sep 23 20:55:51 2009 -0700
@@ -47,11 +47,11 @@
 	int		(*aco_mmap)(audio_client_t *, ...);
 	void		(*aco_input)(audio_client_t *);
 	void		(*aco_output)(audio_client_t *);
-	void		(*aco_notify)(audio_client_t *);
 	void		(*aco_drain)(audio_client_t *);
 
 	void		(*aco_wput)(audio_client_t *, mblk_t *);
 	void		(*aco_wsrv)(audio_client_t *);
+	void		(*aco_rsrv)(audio_client_t *);
 } audio_client_ops_t;
 
 void *auclnt_get_private(audio_client_t *);
@@ -149,7 +149,6 @@
 audio_dev_t *auclnt_get_dev(audio_client_t *);
 audio_dev_t *auclnt_hold_dev_by_index(int);
 void auclnt_release_dev(audio_dev_t *);
-void auclnt_notify_dev(audio_dev_t *);
 int auclnt_get_dev_index(audio_dev_t *);
 int auclnt_get_dev_number(audio_dev_t *);
 void auclnt_set_dev_number(audio_dev_t *, int);
@@ -177,6 +176,14 @@
     int (*)(audio_client_t *, void *), void *);
 
 /*
+ * This is used to check for updates to volume and control status.
+ * Its a polling-based interface because that's what our clients (OSS)
+ * need, and its far lighter weight than forcing an asynchronous
+ * callback on everything.
+ */
+unsigned auclnt_dev_get_serial(audio_dev_t *);
+
+/*
  * Audio control functions for use by clients.
  */
 
--- a/usr/src/uts/common/io/audio/impl/audio_ctrl.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_ctrl.c	Wed Sep 23 20:55:51 2009 -0700
@@ -28,6 +28,7 @@
 #include <sys/sysmacros.h>
 #include <sys/ddi.h>
 #include <sys/sunddi.h>
+#include <sys/atomic.h>
 
 #include "audio_impl.h"
 
@@ -264,15 +265,14 @@
  * values since they have changed.
  *
  * There will be a routine that allows a client to register
- * a callback. All callbacks registered to this device should
- * get called from this routine.
+ * a callback.   For now we just update the serial number.
  *
  * d                - The device that needs updates.
  */
 void
 audio_dev_update_controls(audio_dev_t *d)
 {
-	auclnt_notify_dev(d);
+	atomic_inc_uint(&d->d_serial);
 }
 
 
--- a/usr/src/uts/common/io/audio/impl/audio_ddi.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_ddi.c	Wed Sep 23 20:55:51 2009 -0700
@@ -33,6 +33,7 @@
 #include <sys/mkdev.h>
 #include <sys/conf.h>
 #include <sys/note.h>
+#include <sys/atomic.h>
 #include <sys/ddi.h>
 #include <sys/sunddi.h>
 
@@ -183,7 +184,7 @@
 	/* now we can receive upcalls */
 	auimpl_client_activate(c);
 
-	auclnt_notify_dev(c->c_dev);
+	atomic_inc_uint(&c->c_dev->d_serial);
 
 	return (0);
 }
@@ -244,7 +245,7 @@
 	/* now we can receive upcalls */
 	auimpl_client_activate(c);
 
-	auclnt_notify_dev(c->c_dev);
+	atomic_inc_uint(&c->c_dev->d_serial);
 
 	return (0);
 }
@@ -289,7 +290,7 @@
 	auimpl_client_destroy(c);
 
 	/* notify peers that a change has occurred */
-	auclnt_notify_dev(d);
+	atomic_inc_uint(&d->d_serial);
 
 	/* now we can drop the release we had on the device */
 	auimpl_dev_release(d);
@@ -340,7 +341,7 @@
 	auimpl_client_destroy(c);
 
 	/* notify peers that a change has occurred */
-	auclnt_notify_dev(d);
+	atomic_inc_uint(&d->d_serial);
 
 	/* now we can drop the release we had on the device */
 	auimpl_dev_release(d);
@@ -441,6 +442,21 @@
 	return (0);
 }
 
+static int
+audio_rsrv(queue_t *rq)
+{
+	audio_client_t	*c;
+
+	c = rq->q_ptr;
+	if (c->c_rsrv) {
+		c->c_rsrv(c);
+	} else {
+		flushq(rq, FLUSHALL);
+	}
+	return (0);
+}
+
+
 static struct dev_ops audio_dev_ops = {
 	DEVO_REV,		/* rev */
 	0,			/* refcnt */
@@ -488,7 +504,7 @@
 	helper->minfo.mi_idnum = 0;	/* only for strlog(1M) */
 	helper->minfo.mi_idname = helper->name;
 	helper->minfo.mi_minpsz = 0;
-	helper->minfo.mi_maxpsz = 2048;
+	helper->minfo.mi_maxpsz = 8192;
 	helper->minfo.mi_hiwat = 65536;
 	helper->minfo.mi_lowat = 32768;
 
@@ -500,8 +516,8 @@
 	helper->wqinit.qi_minfo = &helper->minfo;
 	helper->wqinit.qi_mstat = NULL;
 
-	helper->rqinit.qi_putp = NULL;
-	helper->rqinit.qi_srvp = NULL;
+	helper->rqinit.qi_putp = putq;
+	helper->rqinit.qi_srvp = audio_rsrv;
 	helper->rqinit.qi_qopen = audio_stropen;
 	helper->rqinit.qi_qclose = audio_strclose;
 	helper->rqinit.qi_qadmin = NULL;
--- a/usr/src/uts/common/io/audio/impl/audio_impl.h	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_impl.h	Wed Sep 23 20:55:51 2009 -0700
@@ -183,12 +183,6 @@
 
 	kmutex_t		c_lock;
 	kcondvar_t		c_cv;
-	ddi_taskq_t		*c_tq;
-	boolean_t		c_do_output;
-	boolean_t		c_do_input;
-	boolean_t		c_do_notify;
-	boolean_t		c_do_drain;
-	boolean_t		c_closing;
 	boolean_t		c_is_active;
 
 	/*
@@ -211,6 +205,7 @@
 #define	c_drain			c_ops.aco_drain
 #define	c_wput			c_ops.aco_wput
 #define	c_wsrv			c_ops.aco_wsrv
+#define	c_rsrv			c_ops.aco_rsrv
 
 	struct pollhead		c_pollhead;
 
@@ -359,6 +354,8 @@
 	audio_ctrl_t		*d_pcmvol_ctrl;
 	uint64_t		d_pcmvol;
 
+	volatile unsigned	d_serial;
+
 	/*
 	 * Linkage onto global list of devices.
 	 */
--- a/usr/src/uts/common/io/audio/impl/audio_input.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_input.c	Wed Sep 23 20:55:51 2009 -0700
@@ -141,7 +141,6 @@
 auimpl_input_callback(audio_engine_t *eng)
 {
 	int		fragfr = eng->e_fragfr;
-	boolean_t	overrun;
 	audio_client_t	*c;
 
 	/* consume all fragments in the buffer */
@@ -187,9 +186,6 @@
 				eng->e_errors++;
 				sp->s_errors += count - space;
 				count = space;
-				overrun = B_TRUE;
-			} else {
-				overrun = B_FALSE;
 			}
 
 			auimpl_produce_fragment(sp, count);
@@ -199,13 +195,9 @@
 
 			mutex_exit(&sp->s_lock);
 
-			mutex_enter(&c->c_lock);
-			if (overrun) {
-				c->c_do_notify = B_TRUE;
+			if (c->c_input != NULL) {
+				c->c_input(c);
 			}
-			c->c_do_input = B_TRUE;
-			cv_broadcast(&c->c_cv);
-			mutex_exit(&c->c_lock);
 		}
 
 		/*
--- a/usr/src/uts/common/io/audio/impl/audio_oss.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_oss.c	Wed Sep 23 20:55:51 2009 -0700
@@ -78,7 +78,6 @@
 struct ossdev {
 	audio_dev_t		*d_dev;
 
-	uint_t			d_modify_cnt;	/* flag apps of ctrl changes */
 	uint_t			d_nctrl;	/* num actual controls */
 	uint_t			d_nalloc;	/* num allocated controls */
 	audio_ctrl_t		**d_ctrls;	/* array of control handles */
@@ -1345,20 +1344,15 @@
 sound_mixer_info(audio_client_t *c, mixer_info *mi)
 {
 	audio_dev_t	*d;
-	ossdev_t	*odev;
-	ossclient_t	*sc;
 	const char	*name;
 
-	sc = auclnt_get_private(c);
-	odev = sc->o_ossdev;
-
 	d = auclnt_get_dev(c);
 
 	name = auclnt_get_dev_name(d);
 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
-	mi->modify_counter = odev->d_modify_cnt;
+	mi->modify_counter = (int)auclnt_dev_get_serial(d);
 	mi->card_number = auclnt_get_dev_index(d);
 	mi->port_number = 0;
 	return (0);
@@ -1438,7 +1432,7 @@
 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
-	mi->modify_counter = odev->d_modify_cnt;
+	mi->modify_counter = (int)auclnt_dev_get_serial(d);
 	mi->card_number = auclnt_get_dev_index(d);
 	mi->legacy_device = auclnt_get_dev_number(d);
 	if (mi->legacy_device >= 0) {
@@ -1924,19 +1918,6 @@
 	auclnt_pollwakeup(c, POLLIN | POLLRDNORM);
 }
 
-static void
-oss_notify(audio_client_t *c)
-{
-	audio_dev_t	*d;
-	ossdev_t	*odev;
-
-	d = auclnt_get_dev(c);
-	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
-		return;
-	}
-	odev->d_modify_cnt++;
-}
-
 static int
 ossmix_open(audio_client_t *c, int oflag)
 {
@@ -2578,7 +2559,6 @@
 	NULL,		/* mmap */
 	oss_input,
 	oss_output,
-	NULL,		/* notify */
 	NULL,		/* drain */
 };
 
@@ -2595,7 +2575,6 @@
 	NULL,   /* mmap */
 	NULL,	/* input */
 	NULL,   /* output */
-	oss_notify,
 	NULL,	/* drain */
 	NULL,	/* wput */
 	NULL,	/* wsrv */
@@ -2615,7 +2594,6 @@
 	NULL,	/* mmap */
 	NULL,	/* input */
 	NULL,	/* output */
-	NULL,	/* notify */
 	NULL,	/* drain */
 	NULL,	/* wput */
 	NULL,	/* wsrv */
--- a/usr/src/uts/common/io/audio/impl/audio_output.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_output.c	Wed Sep 23 20:55:51 2009 -0700
@@ -288,7 +288,6 @@
 		int		avail;
 		int		used;
 		int		offset;
-		boolean_t	underrun = B_FALSE;
 		boolean_t	drained = B_FALSE;
 		audio_client_t	*c = sp->s_client;
 
@@ -378,7 +377,6 @@
 					drained = B_TRUE;
 				}
 			} else {
-				underrun = B_TRUE;
 				sp->s_errors += need;
 				eng->e_stream_underruns++;
 			}
@@ -397,17 +395,20 @@
 		 * chance of deadlock.
 		 */
 
-		mutex_enter(&c->c_lock);
-		c->c_do_output = B_TRUE;
-
-		if (underrun)
-			c->c_do_notify = B_TRUE;
+		/*
+		 * NB: The only lock we are holding now is the engine
+		 * lock.  But the client can't go away because the
+		 * closer would have to get the engine lock to remove
+		 * the client's stream from engine.  So we're safe.
+		 */
 
-		if (drained)
-			c->c_do_drain = B_TRUE;
+		if (c->c_output != NULL) {
+			c->c_output(c);
+		}
 
-		cv_broadcast(&c->c_cv);
-		mutex_exit(&c->c_lock);
+		if (drained && (c->c_drain != NULL)) {
+			c->c_drain(c);
+		}
 	}
 
 	/*
@@ -450,7 +451,7 @@
 	cnt = eng->e_head - eng->e_tail;
 
 	/* stay a bit ahead */
-	while (cnt < (fragfr * 4)) {
+	while (cnt < ((fragfr * 3) / 2)) {
 		auimpl_output_callback_impl(eng);
 		cnt = eng->e_head - eng->e_tail;
 	}
--- a/usr/src/uts/common/io/audio/impl/audio_sun.c	Wed Sep 23 22:00:34 2009 -0400
+++ b/usr/src/uts/common/io/audio/impl/audio_sun.c	Wed Sep 23 20:55:51 2009 -0700
@@ -539,7 +539,7 @@
 
 	while (auclnt_get_count(sp) >= count) {
 
-		if ((!canputnext(rq)) ||
+		if ((!canput(rq)) ||
 		    ((mp = allocb(nbytes, BPRI_MED)) == NULL)) {
 			/*
 			 * This will apply back pressure to the
@@ -560,7 +560,7 @@
 
 		(void) auclnt_consume_data(sp, (caddr_t)mp->b_wptr, count);
 		mp->b_wptr += nbytes;
-		putnext(rq, mp);
+		(void) putq(rq, mp);
 	}
 }
 
@@ -968,14 +968,12 @@
 static int
 devaudio_sigpoll(audio_client_t *c, void *arg)
 {
-	daproc_t	*proc = arg;
-	daclient_t	*dc;
+	pid_t		pid = (pid_t)(uintptr_t)arg;
 
 	if (auclnt_get_minor_type(c) == AUDIO_MINOR_DEVAUDIOCTL) {
-		dc = auclnt_get_private(c);
 		/* we only need to notify peers in our own process */
-		if ((dc != NULL) && (dc->dc_proc == proc)) {
-			(void) putnextctl1(auclnt_get_rq(c), M_PCSIG, SIGPOLL);
+		if (auclnt_get_pid(c) == pid) {
+			(void) putctl1(auclnt_get_rq(c), M_PCSIG, SIGPOLL);
 		}
 	}
 	return (AUDIO_WALK_CONTINUE);
@@ -995,7 +993,8 @@
 	while ((mp = mplist) != NULL) {
 		mplist = mp->b_next;
 		mp->b_next = NULL;
-		miocack(auclnt_get_wq(c), mp, 0, 0);
+		mioc2ack(mp, NULL, 0, 0);
+		(void) putq(auclnt_get_rq(c), mp);
 	}
 }
 
@@ -1025,7 +1024,7 @@
 
 	if (eofs) {
 		auclnt_dev_walk_clients(auclnt_get_dev(c),
-		    devaudio_sigpoll, proc);
+		    devaudio_sigpoll, (void *)(uintptr_t)auclnt_get_pid(c));
 	}
 }
 
@@ -1337,6 +1336,21 @@
 }
 
 static void
+devaudio_rsrv(audio_client_t *c)
+{
+	queue_t		*rq = auclnt_get_rq(c);
+	mblk_t		*mp;
+
+	while ((mp = getq(rq)) != NULL) {
+
+		if ((queclass(mp) != QPCTL) && (!canputnext(rq))) {
+			return;
+		}
+		putnext(rq, mp);
+	}
+}
+
+static void
 devaudio_wsrv(audio_client_t *c)
 {
 	queue_t		*wq = auclnt_get_wq(c);
@@ -1421,10 +1435,10 @@
 	NULL,	/* mmap */
 	devaudio_input,
 	devaudio_output,
-	NULL,	/* notify */
 	devaudio_drain,
 	devaudio_wput,
-	devaudio_wsrv
+	devaudio_wsrv,
+	devaudio_rsrv
 };
 
 static struct audio_client_ops devaudioctl_ops = {
@@ -1440,10 +1454,10 @@
 	NULL,	/* mmap */
 	NULL,	/* output */
 	NULL,	/* input */
-	NULL,	/* notify */
 	NULL,	/* drain */
 	devaudioctl_wput,
 	NULL,
+	NULL,
 };
 
 void