Module Name:    src
Committed By:   martin
Date:           Thu Apr 30 16:05:18 UTC 2020

Modified Files:
        src/sys/dev/audio [netbsd-9]: audio.c audiovar.h

Log Message:
Pull up following revision(s) (requested by isaki in ticket #877):

        sys/dev/audio/audio.c: revision 1.55
        sys/dev/audio/audio.c: revision 1.41
        sys/dev/audio/audio.c: revision 1.48
        sys/dev/audio/audiovar.h: revision 1.7
        sys/dev/audio/audio.c: revision 1.63 (via patch)
        sys/dev/audio/audiovar.h: revision 1.11
        sys/dev/audio/audio.c: revision 1.64

Simplify async_mixer handling.
- It makes FIOASYNC code in mixer_ioctl() symmetric.
- For readability, mixer_async_{add,remove}() should take pid argument
  though pid is always curproc.

hw_if->query_format is already mandatory method.  Drop null checks.

Improve error handling around audio_hw_probe().
It was difficult to return multiple errors.

Split sc_lock and sc_exlock.

Most (probably all) malloc/free (or routines which may sleep) now can be
called without holding mutex.
Pointed out by riastradh@.

Fix/Update comments about allocm/freem.


To generate a diff of this commit:
cvs rdiff -u -r1.28.2.11 -r1.28.2.12 src/sys/dev/audio/audio.c
cvs rdiff -u -r1.4.2.2 -r1.4.2.3 src/sys/dev/audio/audiovar.h

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

Modified files:

Index: src/sys/dev/audio/audio.c
diff -u src/sys/dev/audio/audio.c:1.28.2.11 src/sys/dev/audio/audio.c:1.28.2.12
--- src/sys/dev/audio/audio.c:1.28.2.11	Thu Apr 30 15:43:30 2020
+++ src/sys/dev/audio/audio.c	Thu Apr 30 16:05:18 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio.c,v 1.28.2.11 2020/04/30 15:43:30 martin Exp $	*/
+/*	$NetBSD: audio.c,v 1.28.2.12 2020/04/30 16:05:18 martin Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -118,8 +118,8 @@
  *	set_port 		-	x +
  *	get_port 		-	x +
  *	query_devinfo 		-	x
- *	allocm 			-	- +	(*1)
- *	freem 			-	- +	(*1)
+ *	allocm 			-	- +
+ *	freem 			-	- +
  *	round_buffersize 	-	x
  *	get_props 		-	x	Called at attach time
  *	trigger_output 		x	x +
@@ -127,10 +127,6 @@
  *	dev_ioctl 		-	x
  *	get_locks 		-	-	Called at attach time
  *
- * *1 Note: Before 8.0, since these have been called only at attach time,
- *   neither lock were necessary.  Currently, on the other hand, since
- *   these may be also called after attach, the thread lock is required.
- *
  * In addition, there is an additional lock.
  *
  * - track->lock.  This is an atomic variable and is similar to the
@@ -142,7 +138,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.28.2.11 2020/04/30 15:43:30 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.28.2.12 2020/04/30 16:05:18 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "audio.h"
@@ -466,6 +462,9 @@ audio_track_bufstat(audio_track_t *track
 int audio_idle_timeout = 30;
 #endif
 
+/* Number of elements of async mixer's pid */
+#define AM_CAPACITY	(4)
+
 struct portname {
 	const char *name;
 	int mask;
@@ -497,8 +496,10 @@ static void audio_mixer_restore(struct a
 static void audio_softintr_rd(void *);
 static void audio_softintr_wr(void *);
 
-static int  audio_enter_exclusive(struct audio_softc *);
-static void audio_exit_exclusive(struct audio_softc *);
+static int audio_exlock_mutex_enter(struct audio_softc *);
+static void audio_exlock_mutex_exit(struct audio_softc *);
+static int audio_exlock_enter(struct audio_softc *);
+static void audio_exlock_exit(struct audio_softc *);
 static struct audio_softc *audio_file_enter(audio_file_t *, struct psref *);
 static void audio_file_exit(struct audio_softc *, struct psref *);
 static int audio_track_waitio(struct audio_softc *, audio_track_t *);
@@ -560,9 +561,7 @@ static int audio_mixers_init(struct audi
 	const audio_format2_t *, const audio_format2_t *,
 	const audio_filter_reg_t *, const audio_filter_reg_t *);
 static int audio_select_freq(const struct audio_format *);
-static int audio_hw_probe(struct audio_softc *, int, int *,
-	audio_format2_t *, audio_format2_t *);
-static int audio_hw_probe_fmt(struct audio_softc *, audio_format2_t *, int);
+static int audio_hw_probe(struct audio_softc *, audio_format2_t *, int);
 static int audio_hw_validate_format(struct audio_softc *, int,
 	const audio_format2_t *);
 static int audio_mixers_set_format(struct audio_softc *,
@@ -609,7 +608,8 @@ static void mixer_init(struct audio_soft
 static int mixer_open(dev_t, struct audio_softc *, int, int, struct lwp *);
 static int mixer_close(struct audio_softc *, audio_file_t *);
 static int mixer_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *);
-static void mixer_remove(struct audio_softc *);
+static void mixer_async_add(struct audio_softc *, pid_t);
+static void mixer_async_remove(struct audio_softc *, pid_t);
 static void mixer_signal(struct audio_softc *);
 
 static int au_portof(struct audio_softc *, char *, int);
@@ -882,9 +882,13 @@ audioattach(device_t parent, device_t se
 	sc->hw_hdl = hdlp;
 	sc->hw_dev = parent;
 
+	sc->sc_exlock = 1;
 	sc->sc_blk_ms = AUDIO_BLK_MS;
 	SLIST_INIT(&sc->sc_files);
 	cv_init(&sc->sc_exlockcv, "audiolk");
+	sc->sc_am_capacity = 0;
+	sc->sc_am_used = 0;
+	sc->sc_am = NULL;
 
 	mutex_enter(sc->sc_lock);
 	sc->sc_props = hw_if->get_props(sc->hw_hdl);
@@ -932,24 +936,47 @@ audioattach(device_t parent, device_t se
 	memset(&rhwfmt, 0, sizeof(rhwfmt));
 	memset(&pfil, 0, sizeof(pfil));
 	memset(&rfil, 0, sizeof(rfil));
-	mutex_enter(sc->sc_lock);
-	error = audio_hw_probe(sc, has_indep, &mode, &phwfmt, &rhwfmt);
-	if (error) {
-		mutex_exit(sc->sc_lock);
-		aprint_error_dev(self, "audio_hw_probe failed, "
-		    "error = %d\n", error);
-		goto bad;
-	}
-	if (mode == 0) {
-		mutex_exit(sc->sc_lock);
-		aprint_error_dev(self, "audio_hw_probe failed, no mode\n");
-		goto bad;
+	if (has_indep) {
+		int perror, rerror;
+
+		/* On independent devices, probe separately. */
+		perror = audio_hw_probe(sc, &phwfmt, AUMODE_PLAY);
+		rerror = audio_hw_probe(sc, &rhwfmt, AUMODE_RECORD);
+		if (perror && rerror) {
+			aprint_error_dev(self, "audio_hw_probe failed, "
+			    "perror = %d, rerror = %d\n", perror, rerror);
+			goto bad;
+		}
+		if (perror) {
+			mode &= ~AUMODE_PLAY;
+			aprint_error_dev(self, "audio_hw_probe failed with "
+			    "%d, playback disabled\n", perror);
+		}
+		if (rerror) {
+			mode &= ~AUMODE_RECORD;
+			aprint_error_dev(self, "audio_hw_probe failed with "
+			    "%d, capture disabled\n", rerror);
+		}
+	} else {
+		/*
+		 * On non independent devices or uni-directional devices,
+		 * probe once (simultaneously).
+		 */
+		audio_format2_t *fmt = has_playback ? &phwfmt : &rhwfmt;
+		error = audio_hw_probe(sc, fmt, mode);
+		if (error) {
+			aprint_error_dev(self, "audio_hw_probe failed, "
+			    "error = %d\n", error);
+			goto bad;
+		}
+		if (has_playback && has_capture)
+			rhwfmt = phwfmt;
 	}
+
 	/* Init hardware. */
 	/* hw_probe() also validates [pr]hwfmt.  */
 	error = audio_hw_set_format(sc, mode, &phwfmt, &rhwfmt, &pfil, &rfil);
 	if (error) {
-		mutex_exit(sc->sc_lock);
 		aprint_error_dev(self, "audio_hw_set_format failed, "
 		    "error = %d\n", error);
 		goto bad;
@@ -960,7 +987,6 @@ audioattach(device_t parent, device_t se
 	 * attach time, we assume a success.
 	 */
 	error = audio_mixers_init(sc, mode, &phwfmt, &rhwfmt, &pfil, &rfil);
-	mutex_exit(sc->sc_lock);
 	if (sc->sc_pmixer == NULL && sc->sc_rmixer == NULL) {
 		aprint_error_dev(self, "audio_mixers_init failed, "
 		    "error = %d\n", error);
@@ -1052,11 +1078,13 @@ audioattach(device_t parent, device_t se
 #endif
 
 	audiorescan(self, "audio", NULL);
+	sc->sc_exlock = 0;
 	return;
 
 bad:
 	/* Clearing hw_if means that device is attached but disabled. */
 	sc->hw_if = NULL;
+	sc->sc_exlock = 0;
 	aprint_error_dev(sc->sc_dev, "disabled\n");
 	return;
 }
@@ -1274,6 +1302,7 @@ audiodetach(device_t self, int flags)
 	 * that hold sc, and any new calls with files that were for sc will
 	 * fail.  Thus, we now have exclusive access to the softc.
 	 */
+	sc->sc_exlock = 1;
 
 	/*
 	 * Nuke all open instances.
@@ -1299,7 +1328,6 @@ audiodetach(device_t self, int flags)
 	pmf_device_deregister(self);
 
 	/* Free resources */
-	mutex_enter(sc->sc_lock);
 	if (sc->sc_pmixer) {
 		audio_mixer_destroy(sc, sc->sc_pmixer);
 		kmem_free(sc->sc_pmixer, sizeof(*sc->sc_pmixer));
@@ -1308,7 +1336,8 @@ audiodetach(device_t self, int flags)
 		audio_mixer_destroy(sc, sc->sc_rmixer);
 		kmem_free(sc->sc_rmixer, sizeof(*sc->sc_rmixer));
 	}
-	mutex_exit(sc->sc_lock);
+	if (sc->sc_am)
+		kern_free(sc->sc_am);
 
 	seldestroy(&sc->sc_wsel);
 	seldestroy(&sc->sc_rsel);
@@ -1378,12 +1407,12 @@ audio_attach_mi(const struct audio_hw_if
 }
 
 /*
- * Acquire sc_lock and enter exlock critical section.
- * If successful, it returns 0.  Otherwise returns errno.
+ * Enter critical section and also keep sc_lock.
+ * If successful, returns 0 with sc_lock held.  Otherwise returns errno.
  * Must be called without sc_lock held.
  */
 static int
-audio_enter_exclusive(struct audio_softc *sc)
+audio_exlock_mutex_enter(struct audio_softc *sc)
 {
 	int error;
 
@@ -1409,23 +1438,51 @@ audio_enter_exclusive(struct audio_softc
 }
 
 /*
- * Leave exlock critical section and release sc_lock.
+ * Exit critical section and exit sc_lock.
  * Must be called with sc_lock held.
  */
 static void
-audio_exit_exclusive(struct audio_softc *sc)
+audio_exlock_mutex_exit(struct audio_softc *sc)
 {
 
 	KASSERT(mutex_owned(sc->sc_lock));
-	KASSERT(sc->sc_exlock);
 
-	/* Leave critical section */
 	sc->sc_exlock = 0;
 	cv_broadcast(&sc->sc_exlockcv);
 	mutex_exit(sc->sc_lock);
 }
 
 /*
+ * Enter critical section.
+ * If successful, it returns 0.  Otherwise returns errno.
+ * Must be called without sc_lock held.
+ * This function returns without sc_lock held.
+ */
+static int
+audio_exlock_enter(struct audio_softc *sc)
+{
+	int error;
+
+	error = audio_exlock_mutex_enter(sc);
+	if (error)
+		return error;
+	mutex_exit(sc->sc_lock);
+	return 0;
+}
+
+/*
+ * Exit critical section.
+ * Must be called without sc_lock held.
+ */
+static void
+audio_exlock_exit(struct audio_softc *sc)
+{
+
+	mutex_enter(sc->sc_lock);
+	audio_exlock_mutex_exit(sc);
+}
+
+/*
  * Acquire sc from file, and increment the psref count.
  * If successful, returns sc.  Otherwise returns NULL.
  */
@@ -1539,7 +1596,7 @@ audioopen(dev_t dev, int flags, int ifmt
 	if (sc == NULL || sc->hw_if == NULL)
 		return ENXIO;
 
-	error = audio_enter_exclusive(sc);
+	error = audio_exlock_enter(sc);
 	if (error)
 		return error;
 
@@ -1559,7 +1616,7 @@ audioopen(dev_t dev, int flags, int ifmt
 		error = ENXIO;
 		break;
 	}
-	audio_exit_exclusive(sc);
+	audio_exlock_exit(sc);
 
 	return error;
 }
@@ -1893,14 +1950,14 @@ audiobellopen(dev_t dev, audio_file_t **
 	if (sc == NULL || sc->hw_if == NULL)
 		return ENXIO;
 
-	error = audio_enter_exclusive(sc);
+	error = audio_exlock_enter(sc);
 	if (error)
 		return error;
 
 	device_active(sc->sc_dev, DVA_SYSTEM);
 	error = audio_open(dev, sc, FWRITE, 0, curlwp, filep);
 
-	audio_exit_exclusive(sc);
+	audio_exlock_exit(sc);
 	return error;
 }
 
@@ -1943,11 +2000,11 @@ audiobellsetrate(audio_file_t *file, u_i
 	AUDIO_INITINFO(&ai);
 	ai.play.sample_rate = sample_rate;
 
-	error = audio_enter_exclusive(sc);
+	error = audio_exlock_enter(sc);
 	if (error)
 		goto done;
 	error = audio_file_setinfo(sc, file, &ai);
-	audio_exit_exclusive(sc);
+	audio_exlock_exit(sc);
 
 done:
 	audio_file_exit(sc, &sc_ref);
@@ -1976,6 +2033,10 @@ audiobellwrite(audio_file_t *file, struc
 /*
  * Audio driver
  */
+
+/*
+ * Must be called with sc_exlock held and without sc_lock held.
+ */
 int
 audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
 	struct lwp *l, audio_file_t **bellfile)
@@ -1988,7 +2049,6 @@ audio_open(dev_t dev, struct audio_softc
 	int fd;
 	int error;
 
-	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(sc->sc_exlock);
 
 	TRACE(1, "%sdev=%s flags=0x%x po=%d ro=%d",
@@ -2110,9 +2170,11 @@ audio_open(dev_t dev, struct audio_softc
 					hwflags |= FREAD;
 			}
 
+			mutex_enter(sc->sc_lock);
 			mutex_enter(sc->sc_intr_lock);
 			error = sc->hw_if->open(sc->hw_hdl, hwflags);
 			mutex_exit(sc->sc_intr_lock);
+			mutex_exit(sc->sc_lock);
 			if (error)
 				goto bad2;
 		}
@@ -2129,9 +2191,11 @@ audio_open(dev_t dev, struct audio_softc
 				} else {
 					on = 0;
 				}
+				mutex_enter(sc->sc_lock);
 				mutex_enter(sc->sc_intr_lock);
 				error = sc->hw_if->speaker_ctl(sc->hw_hdl, on);
 				mutex_exit(sc->sc_intr_lock);
+				mutex_exit(sc->sc_lock);
 				if (error)
 					goto bad3;
 			}
@@ -2148,12 +2212,14 @@ audio_open(dev_t dev, struct audio_softc
 	if (af->ptrack && sc->sc_popens == 0) {
 		if (sc->hw_if->init_output) {
 			hwbuf = &sc->sc_pmixer->hwbuf;
+			mutex_enter(sc->sc_lock);
 			mutex_enter(sc->sc_intr_lock);
 			error = sc->hw_if->init_output(sc->hw_hdl,
 			    hwbuf->mem,
 			    hwbuf->capacity *
 			    hwbuf->fmt.channels * hwbuf->fmt.stride / NBBY);
 			mutex_exit(sc->sc_intr_lock);
+			mutex_exit(sc->sc_lock);
 			if (error)
 				goto bad3;
 		}
@@ -2162,12 +2228,14 @@ audio_open(dev_t dev, struct audio_softc
 	if (af->rtrack && sc->sc_ropens == 0) {
 		if (sc->hw_if->init_input) {
 			hwbuf = &sc->sc_rmixer->hwbuf;
+			mutex_enter(sc->sc_lock);
 			mutex_enter(sc->sc_intr_lock);
 			error = sc->hw_if->init_input(sc->hw_hdl,
 			    hwbuf->mem,
 			    hwbuf->capacity *
 			    hwbuf->fmt.channels * hwbuf->fmt.stride / NBBY);
 			mutex_exit(sc->sc_intr_lock);
+			mutex_exit(sc->sc_lock);
 			if (error)
 				goto bad3;
 		}
@@ -2183,6 +2251,7 @@ audio_open(dev_t dev, struct audio_softc
 	 * Count up finally.
 	 * Don't fail from here.
 	 */
+	mutex_enter(sc->sc_lock);
 	if (af->ptrack)
 		sc->sc_popens++;
 	if (af->rtrack)
@@ -2190,6 +2259,7 @@ audio_open(dev_t dev, struct audio_softc
 	mutex_enter(sc->sc_intr_lock);
 	SLIST_INSERT_HEAD(&sc->sc_files, af, entry);
 	mutex_exit(sc->sc_intr_lock);
+	mutex_exit(sc->sc_lock);
 
 	if (bellfile) {
 		*bellfile = af;
@@ -2208,9 +2278,11 @@ audio_open(dev_t dev, struct audio_softc
 bad3:
 	if (sc->sc_popens + sc->sc_ropens == 0) {
 		if (sc->hw_if->close) {
+			mutex_enter(sc->sc_lock);
 			mutex_enter(sc->sc_intr_lock);
 			sc->hw_if->close(sc->hw_hdl);
 			mutex_exit(sc->sc_intr_lock);
+			mutex_exit(sc->sc_lock);
 		}
 	}
 bad2:
@@ -2239,7 +2311,7 @@ audio_close(struct audio_softc *sc, audi
 
 	/*
 	 * Drain first.
-	 * It must be done before unlinking(acquiring exclusive lock).
+	 * It must be done before unlinking(acquiring exlock).
 	 */
 	if (file->ptrack) {
 		mutex_enter(sc->sc_lock);
@@ -2259,6 +2331,8 @@ audio_unlink(struct audio_softc *sc, aud
 {
 	int error;
 
+	mutex_enter(sc->sc_lock);
+
 	TRACEF(1, file, "%spid=%d.%d po=%d ro=%d",
 	    (audiodebug >= 3) ? "start " : "",
 	    (int)curproc->p_pid, (int)curlwp->l_lid,
@@ -2267,10 +2341,9 @@ audio_unlink(struct audio_softc *sc, aud
 	    "sc->sc_popens=%d, sc->sc_ropens=%d",
 	    sc->sc_popens, sc->sc_ropens);
 
-	mutex_enter(sc->sc_lock);
 	/*
-	 * Acquire exclusive lock to protect counters.
-	 * Does not use audio_enter_exclusive() due to sc_dying.
+	 * Acquire exlock to protect counters.
+	 * Does not use audio_exlock_enter() due to sc_dying.
 	 */
 	while (__predict_false(sc->sc_exlock != 0)) {
 		error = cv_timedwait_sig(&sc->sc_exlockcv, sc->sc_lock,
@@ -2344,12 +2417,14 @@ audio_unlink(struct audio_softc *sc, aud
 			sc->hw_if->close(sc->hw_hdl);
 			mutex_exit(sc->sc_intr_lock);
 		}
+	}
 
+	mutex_exit(sc->sc_lock);
+	if (sc->sc_popens + sc->sc_ropens == 0)
 		kauth_cred_free(sc->sc_cred);
-	}
 
 	TRACE(3, "done");
-	audio_exit_exclusive(sc);
+	audio_exlock_exit(sc);
 
 	return 0;
 }
@@ -2382,27 +2457,26 @@ audio_read(struct audio_softc *sc, struc
 
 	TRACET(2, track, "resid=%zd", uio->uio_resid);
 
+	error = audio_exlock_mutex_enter(sc);
+	if (error)
+		return error;
+
 #ifdef AUDIO_PM_IDLE
-	mutex_enter(sc->sc_lock);
 	if (device_is_active(&sc->sc_dev) || sc->sc_idle)
 		device_active(&sc->sc_dev, DVA_SYSTEM);
-	mutex_exit(sc->sc_lock);
 #endif
 
-	usrbuf = &track->usrbuf;
-	input = track->input;
-
 	/*
 	 * The first read starts rmixer.
 	 */
-	error = audio_enter_exclusive(sc);
-	if (error)
-		return error;
 	if (sc->sc_rbusy == false)
 		audio_rmixer_start(sc);
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 
+	usrbuf = &track->usrbuf;
+	input = track->input;
 	error = 0;
+
 	while (uio->uio_resid > 0 && error == 0) {
 		int bytes;
 
@@ -2510,28 +2584,27 @@ audio_write(struct audio_softc *sc, stru
 		return 0;
 	}
 
+	error = audio_exlock_mutex_enter(sc);
+	if (error)
+		return error;
+
 #ifdef AUDIO_PM_IDLE
-	mutex_enter(sc->sc_lock);
 	if (device_is_active(&sc->sc_dev) || sc->sc_idle)
 		device_active(&sc->sc_dev, DVA_SYSTEM);
-	mutex_exit(sc->sc_lock);
 #endif
 
-	usrbuf = &track->usrbuf;
-	outbuf = &track->outbuf;
-
 	/*
 	 * The first write starts pmixer.
 	 */
-	error = audio_enter_exclusive(sc);
-	if (error)
-		return error;
 	if (sc->sc_pbusy == false)
 		audio_pmixer_start(sc, false);
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 
+	usrbuf = &track->usrbuf;
+	outbuf = &track->outbuf;
 	track->pstate = AUDIO_STATE_RUNNING;
 	error = 0;
+
 	while (uio->uio_resid > 0 && error == 0) {
 		int bytes;
 
@@ -2760,32 +2833,34 @@ audio_ioctl(dev_t dev, struct audio_soft
 		break;
 
 	case AUDIO_SETINFO:
-		error = audio_enter_exclusive(sc);
+		error = audio_exlock_enter(sc);
 		if (error)
 			break;
 		error = audio_file_setinfo(sc, file, (struct audio_info *)addr);
 		if (error) {
-			audio_exit_exclusive(sc);
+			audio_exlock_exit(sc);
 			break;
 		}
 		/* XXX TODO: update last_ai if /dev/sound ? */
 		if (ISDEVSOUND(dev))
 			error = audiogetinfo(sc, &sc->sc_ai, 0, file);
-		audio_exit_exclusive(sc);
+		audio_exlock_exit(sc);
 		break;
 
 	case AUDIO_GETINFO:
-		error = audio_enter_exclusive(sc);
+		error = audio_exlock_enter(sc);
 		if (error)
 			break;
 		error = audiogetinfo(sc, (struct audio_info *)addr, 1, file);
-		audio_exit_exclusive(sc);
+		audio_exlock_exit(sc);
 		break;
 
 	case AUDIO_GETBUFINFO:
-		mutex_enter(sc->sc_lock);
+		error = audio_exlock_enter(sc);
+		if (error)
+			break;
 		error = audiogetinfo(sc, (struct audio_info *)addr, 0, file);
-		mutex_exit(sc->sc_lock);
+		audio_exlock_exit(sc);
 		break;
 
 	case AUDIO_DRAIN:
@@ -2828,10 +2903,12 @@ audio_ioctl(dev_t dev, struct audio_soft
 		 * If HW has full duplex mode and there are two mixers,
 		 * it is full duplex.  Otherwise half duplex.
 		 */
-		mutex_enter(sc->sc_lock);
+		error = audio_exlock_enter(sc);
+		if (error)
+			break;
 		fd = (sc->sc_props & AUDIO_PROP_FULLDUPLEX)
 		    && (sc->sc_pmixer && sc->sc_rmixer);
-		mutex_exit(sc->sc_lock);
+		audio_exlock_exit(sc);
 		*(int *)addr = fd;
 		break;
 
@@ -2841,30 +2918,30 @@ audio_ioctl(dev_t dev, struct audio_soft
 
 	case AUDIO_QUERYFORMAT:
 		query = (audio_format_query_t *)addr;
-		if (sc->hw_if->query_format) {
-			mutex_enter(sc->sc_lock);
-			error = sc->hw_if->query_format(sc->hw_hdl, query);
-			mutex_exit(sc->sc_lock);
-			/* Hide internal infomations */
-			query->fmt.driver_data = NULL;
-		} else {
-			error = ENODEV;
-		}
+		mutex_enter(sc->sc_lock);
+		error = sc->hw_if->query_format(sc->hw_hdl, query);
+		mutex_exit(sc->sc_lock);
+		/* Hide internal infomations */
+		query->fmt.driver_data = NULL;
 		break;
 
 	case AUDIO_GETFORMAT:
+		error = audio_exlock_enter(sc);
+		if (error)
+			break;
 		audio_mixers_get_format(sc, (struct audio_info *)addr);
+		audio_exlock_exit(sc);
 		break;
 
 	case AUDIO_SETFORMAT:
-		mutex_enter(sc->sc_lock);
+		error = audio_exlock_enter(sc);
 		audio_mixers_get_format(sc, &ai);
 		error = audio_mixers_set_format(sc, (struct audio_info *)addr);
 		if (error) {
 			/* Rollback */
 			audio_mixers_set_format(sc, &ai);
 		}
-		mutex_exit(sc->sc_lock);
+		audio_exlock_exit(sc);
 		break;
 
 	case AUDIO_SETFD:
@@ -2875,12 +2952,10 @@ audio_ioctl(dev_t dev, struct audio_soft
 
 	default:
 		if (sc->hw_if->dev_ioctl) {
-			error = audio_enter_exclusive(sc);
-			if (error)
-				break;
+			mutex_enter(sc->sc_lock);
 			error = sc->hw_if->dev_ioctl(sc->hw_hdl,
 			    cmd, addr, flag, l);
-			audio_exit_exclusive(sc);
+			mutex_exit(sc->sc_lock);
 		} else {
 			TRACEF(2, file, "unknown ioctl");
 			error = EINVAL;
@@ -3083,6 +3158,7 @@ audio_kqfilter(struct audio_softc *sc, a
 
 	TRACEF(3, file, "kn=%p kn_filter=%x", kn, (int)kn->kn_filter);
 
+	mutex_enter(sc->sc_lock);
 	switch (kn->kn_filter) {
 	case EVFILT_READ:
 		klist = &sc->sc_rsel.sel_klist;
@@ -3095,12 +3171,12 @@ audio_kqfilter(struct audio_softc *sc, a
 		break;
 
 	default:
+		mutex_exit(sc->sc_lock);
 		return EINVAL;
 	}
 
 	kn->kn_hook = file;
 
-	mutex_enter(sc->sc_lock);
 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
 	mutex_exit(sc->sc_lock);
 
@@ -3161,12 +3237,12 @@ audio_mmap(struct audio_softc *sc, off_t
 		track->mmapped = true;
 
 		if (!track->is_pause) {
-			error = audio_enter_exclusive(sc);
+			error = audio_exlock_mutex_enter(sc);
 			if (error)
 				return error;
 			if (sc->sc_pbusy == false)
 				audio_pmixer_start(sc, true);
-			audio_exit_exclusive(sc);
+			audio_exlock_mutex_exit(sc);
 		}
 		/* XXX mmapping record buffer is not supported */
 	}
@@ -3185,6 +3261,7 @@ audio_mmap(struct audio_softc *sc, off_t
 /*
  * /dev/audioctl has to be able to open at any time without interference
  * with any /dev/audio or /dev/sound.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static int
 audioctl_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
@@ -3195,7 +3272,6 @@ audioctl_open(dev_t dev, struct audio_so
 	int fd;
 	int error;
 
-	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(sc->sc_exlock);
 
 	TRACE(1, "");
@@ -3686,6 +3762,7 @@ audio_track_freq_down(audio_filter_arg_t
 
 /*
  * Creates track and returns it.
+ * Must be called without sc_lock held.
  */
 audio_track_t *
 audio_track_create(struct audio_softc *sc, audio_trackmixer_t *mixer)
@@ -4658,7 +4735,7 @@ audio_track_record(audio_track_t *track)
 
 /*
  * Calcurate blktime [msec] from mixer(.hwbuf.fmt).
- * Must be called with sc_lock held.
+ * Must be called with sc_exlock held.
  */
 static u_int
 audio_mixer_calc_blktime(struct audio_softc *sc, audio_trackmixer_t *mixer)
@@ -4667,7 +4744,7 @@ audio_mixer_calc_blktime(struct audio_so
 	u_int blktime;
 	u_int frames_per_block;
 
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock);
 
 	fmt = &mixer->hwbuf.fmt;
 	blktime = sc->sc_blk_ms;
@@ -4695,7 +4772,7 @@ audio_mixer_calc_blktime(struct audio_so
  * Set AUMODE_PLAY to the 'mode' for playback or AUMODE_RECORD for recording.
  * sc->sc_[pr]mixer (corresponding to the 'mode') must be zero-filled.
  * This function returns 0 on sucessful.  Otherwise returns errno.
- * Must be called with sc_lock held.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static int
 audio_mixer_init(struct audio_softc *sc, int mode,
@@ -4714,7 +4791,7 @@ audio_mixer_init(struct audio_softc *sc,
 
 	KASSERT(hwfmt != NULL);
 	KASSERT(reg != NULL);
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock);
 
 	error = 0;
 	if (mode == AUMODE_PLAY)
@@ -4737,8 +4814,10 @@ audio_mixer_init(struct audio_softc *sc,
 	if (sc->hw_if->round_blocksize) {
 		int rounded;
 		audio_params_t p = format2_to_params(&mixer->hwbuf.fmt);
+		mutex_enter(sc->sc_lock);
 		rounded = sc->hw_if->round_blocksize(sc->hw_hdl, blksize,
 		    mode, &p);
+		mutex_exit(sc->sc_lock);
 		TRACE(1, "round_blocksize %d -> %d", blksize, rounded);
 		if (rounded != blksize) {
 			if ((rounded * NBBY) % (mixer->hwbuf.fmt.stride *
@@ -4762,8 +4841,10 @@ audio_mixer_init(struct audio_softc *sc,
 	bufsize = frametobyte(&mixer->hwbuf.fmt, capacity);
 	if (sc->hw_if->round_buffersize) {
 		size_t rounded;
+		mutex_enter(sc->sc_lock);
 		rounded = sc->hw_if->round_buffersize(sc->hw_hdl, mode,
 		    bufsize);
+		mutex_exit(sc->sc_lock);
 		TRACE(1, "round_buffersize %zd -> %zd", bufsize, rounded);
 		if (rounded < bufsize) {
 			/* buffersize needs NBLKHW blocks at least. */
@@ -4792,10 +4873,8 @@ audio_mixer_init(struct audio_softc *sc,
 	    bufsize);
 	mixer->hwbuf.capacity = capacity;
 
-	/*
-	 * XXX need to release sc_lock for compatibility?
-	 */
 	if (sc->hw_if->allocm) {
+		/* sc_lock is not necessary for allocm */
 		mixer->hwbuf.mem = sc->hw_if->allocm(sc->hw_hdl, mode, bufsize);
 		if (mixer->hwbuf.mem == NULL) {
 			device_printf(sc->sc_dev, "%s: allocm(%zu) failed\n",
@@ -4901,19 +4980,20 @@ abort:
 /*
  * Releases all resources of 'mixer'.
  * Note that it does not release the memory area of 'mixer' itself.
- * Must be called with sc_lock held.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static void
 audio_mixer_destroy(struct audio_softc *sc, audio_trackmixer_t *mixer)
 {
 	int bufsize;
 
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock == 1);
 
 	bufsize = frametobyte(&mixer->hwbuf.fmt, mixer->hwbuf.capacity);
 
 	if (mixer->hwbuf.mem != NULL) {
 		if (sc->hw_if->freem) {
+			/* sc_lock is not necessary for freem */
 			sc->hw_if->freem(sc->hw_hdl, mixer->hwbuf.mem, bufsize);
 		} else {
 			kmem_free(mixer->hwbuf.mem, bufsize);
@@ -5849,31 +5929,24 @@ audio_track_drain(struct audio_softc *sc
 /*
  * Send signal to process.
  * This is intended to be called only from audio_softintr_{rd,wr}.
- * Must be called with sc_lock && sc_intr_lock held.
+ * Must be called without sc_intr_lock held.
  */
 static inline void
 audio_psignal(struct audio_softc *sc, pid_t pid, int signum)
 {
 	proc_t *p;
 
-	KASSERT(mutex_owned(sc->sc_lock));
-	KASSERT(mutex_owned(sc->sc_intr_lock));
 	KASSERT(pid != 0);
 
 	/*
 	 * psignal() must be called without spin lock held.
-	 * So leave intr_lock temporarily here.
 	 */
-	mutex_exit(sc->sc_intr_lock);
 
 	mutex_enter(proc_lock);
 	p = proc_find(pid);
 	if (p)
 		psignal(p, signum);
 	mutex_exit(proc_lock);
-
-	/* Enter intr_lock again */
-	mutex_enter(sc->sc_intr_lock);
 }
 
 /*
@@ -5897,7 +5970,6 @@ audio_softintr_rd(void *cookie)
 	pid_t pid;
 
 	mutex_enter(sc->sc_lock);
-	mutex_enter(sc->sc_intr_lock);
 
 	SLIST_FOREACH(f, &sc->sc_files, entry) {
 		audio_track_t *track = f->rtrack;
@@ -5916,7 +5988,6 @@ audio_softintr_rd(void *cookie)
 			audio_psignal(sc, pid, SIGIO);
 		}
 	}
-	mutex_exit(sc->sc_intr_lock);
 
 	/* Notify that data has arrived. */
 	selnotify(&sc->sc_rsel, 0, NOTE_SUBMIT);
@@ -5948,7 +6019,6 @@ audio_softintr_wr(void *cookie)
 	found = false;
 
 	mutex_enter(sc->sc_lock);
-	mutex_enter(sc->sc_intr_lock);
 
 	SLIST_FOREACH(f, &sc->sc_files, entry) {
 		audio_track_t *track = f->ptrack;
@@ -5978,7 +6048,6 @@ audio_softintr_wr(void *cookie)
 			}
 		}
 	}
-	mutex_exit(sc->sc_intr_lock);
 
 	/*
 	 * Notify for select/poll when someone become writable.
@@ -6082,7 +6151,7 @@ audio_check_params(audio_format2_t *p)
  * phwfmt and rhwfmt indicate the hardware format.  pfil and rfil indicate
  * the filter registration information.  These four must not be NULL.
  * If successful returns 0.  Otherwise returns errno.
- * Must be called with sc_lock held.
+ * Must be called with sc_exlock held and without sc_lock held.
  * Must not be called if there are any tracks.
  * Caller should check that the initialization succeed by whether
  * sc_[pr]mixer is not NULL.
@@ -6098,7 +6167,7 @@ audio_mixers_init(struct audio_softc *sc
 	KASSERT(rhwfmt != NULL);
 	KASSERT(pfil != NULL);
 	KASSERT(rfil != NULL);
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock);
 
 	if ((mode & AUMODE_PLAY)) {
 		if (sc->sc_pmixer == NULL) {
@@ -6185,68 +6254,13 @@ audio_select_freq(const struct audio_for
 }
 
 /*
- * Probe playback and/or recording format (depending on *modep).
- * *modep is an in-out parameter.  It indicates the direction to configure
- * as an argument, and the direction configured is written back as out
- * parameter.
- * If successful, probed hardware format is stored into *phwfmt, *rhwfmt
- * depending on *modep, and return 0.  Otherwise it returns errno.
- * Must be called with sc_lock held.
- */
-static int
-audio_hw_probe(struct audio_softc *sc, int is_indep, int *modep,
-	audio_format2_t *phwfmt, audio_format2_t *rhwfmt)
-{
-	audio_format2_t fmt;
-	int mode;
-	int error = 0;
-
-	KASSERT(mutex_owned(sc->sc_lock));
-
-	mode = *modep;
-	KASSERTMSG((mode & (AUMODE_PLAY | AUMODE_RECORD)) != 0, "mode=0x%x", mode);
-
-	if (is_indep) {
-		int errorp = 0, errorr = 0;
-
-		/* On independent devices, probe separately. */
-		if ((mode & AUMODE_PLAY) != 0) {
-			errorp = audio_hw_probe_fmt(sc, phwfmt, AUMODE_PLAY);
-			if (errorp)
-				mode &= ~AUMODE_PLAY;
-		}
-		if ((mode & AUMODE_RECORD) != 0) {
-			errorr = audio_hw_probe_fmt(sc, rhwfmt, AUMODE_RECORD);
-			if (errorr)
-				mode &= ~AUMODE_RECORD;
-		}
-
-		/* Return error if both play and record probes failed. */
-		if (errorp && errorr)
-			error = errorp;
-	} else {
-		/* On non independent devices, probe simultaneously. */
-		error = audio_hw_probe_fmt(sc, &fmt, mode);
-		if (error) {
-			mode = 0;
-		} else {
-			*phwfmt = fmt;
-			*rhwfmt = fmt;
-		}
-	}
-
-	*modep = mode;
-	return error;
-}
-
-/*
  * Choose the most preferred hardware format.
  * If successful, it will store the chosen format into *cand and return 0.
  * Otherwise, return errno.
- * Must be called with sc_lock held.
+ * Must be called without sc_lock held.
  */
 static int
-audio_hw_probe_fmt(struct audio_softc *sc, audio_format2_t *cand, int mode)
+audio_hw_probe(struct audio_softc *sc, audio_format2_t *cand, int mode)
 {
 	audio_format_query_t query;
 	int cand_score;
@@ -6254,8 +6268,6 @@ audio_hw_probe_fmt(struct audio_softc *s
 	int i;
 	int error;
 
-	KASSERT(mutex_owned(sc->sc_lock));
-
 	/*
 	 * Score each formats and choose the highest one.
 	 *
@@ -6270,7 +6282,9 @@ audio_hw_probe_fmt(struct audio_softc *s
 		memset(&query, 0, sizeof(query));
 		query.index = i;
 
+		mutex_enter(sc->sc_lock);
 		error = sc->hw_if->query_format(sc->hw_hdl, &query);
+		mutex_exit(sc->sc_lock);
 		if (error == EINVAL)
 			break;
 		if (error)
@@ -6358,7 +6372,7 @@ audio_hw_probe_fmt(struct audio_softc *s
  * Validate fmt with query_format.
  * If fmt is included in the result of query_format, returns 0.
  * Otherwise returns EINVAL.
- * Must be called with sc_lock held.
+ * Must be called without sc_lock held.
  */ 
 static int
 audio_hw_validate_format(struct audio_softc *sc, int mode,
@@ -6370,26 +6384,11 @@ audio_hw_validate_format(struct audio_so
 	int error;
 	int j;
 
-	KASSERT(mutex_owned(sc->sc_lock));
-
-	/*
-	 * If query_format is not supported by hardware driver,
-	 * a rough check instead will be performed.
-	 * XXX This will gone in the future.
-	 */
-	if (sc->hw_if->query_format == NULL) {
-		if (fmt->encoding != AUDIO_ENCODING_SLINEAR_NE)
-			return EINVAL;
-		if (fmt->precision != AUDIO_INTERNAL_BITS)
-			return EINVAL;
-		if (fmt->stride != AUDIO_INTERNAL_BITS)
-			return EINVAL;
-		return 0;
-	}
-
 	for (index = 0; ; index++) {
 		query.index = index;
+		mutex_enter(sc->sc_lock);
 		error = sc->hw_if->query_format(sc->hw_hdl, &query);
+		mutex_exit(sc->sc_lock);
 		if (error == EINVAL)
 			break;
 		if (error)
@@ -6446,7 +6445,7 @@ audio_hw_validate_format(struct audio_so
  * All other fields in ai are ignored.
  * If successful returns 0.  Otherwise returns errno.
  * This function does not roll back even if it fails.
- * Must be called with sc_lock held.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static int
 audio_mixers_set_format(struct audio_softc *sc, const struct audio_info *ai)
@@ -6458,7 +6457,7 @@ audio_mixers_set_format(struct audio_sof
 	int mode;
 	int error;
 
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock);
 
 	/*
 	 * Even when setting either one of playback and recording,
@@ -6563,10 +6562,14 @@ audio_mixers_set_format(struct audio_sof
 
 /*
  * Store current mixers format into *ai.
+ * Must be called with sc_exlock held.
  */
 static void
 audio_mixers_get_format(struct audio_softc *sc, struct audio_info *ai)
 {
+
+	KASSERT(sc->sc_exlock);
+
 	/*
 	 * There is no stride information in audio_info but it doesn't matter.
 	 * trackmixer always treats stride and precision as the same.
@@ -6678,7 +6681,7 @@ audio_mixers_get_format(struct audio_sof
 /*
  * Set both track's parameters within a file depending on ai.
  * Update sc_sound_[pr]* if set.
- * Must be called with sc_lock and sc_exlock held.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static int
 audio_file_setinfo(struct audio_softc *sc, audio_file_t *file,
@@ -6698,7 +6701,6 @@ audio_file_setinfo(struct audio_softc *s
 	audio_format2_t saved_rfmt;
 	int error;
 
-	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(sc->sc_exlock);
 
 	pi = &ai->play;
@@ -6885,7 +6887,9 @@ audio_file_setinfo(struct audio_softc *s
 	}
 
 	/* Set mixer parameters */
+	mutex_enter(sc->sc_lock);
 	error = audio_hw_setinfo(sc, ai, &saved_ai);
+	mutex_exit(sc->sc_lock);
 	if (error)
 		goto abort1;
 
@@ -6959,7 +6963,9 @@ abort2:
 	sc->sc_sound_pparams = saved_pfmt;
 	file->mode = saved_ai.mode;
 abort1:
+	mutex_enter(sc->sc_lock);
 	audio_hw_setinfo(sc, &saved_ai, NULL);
+	mutex_exit(sc->sc_lock);
 
 	return error;
 }
@@ -7070,7 +7076,7 @@ audio_track_setinfo_water(audio_track_t 
  * The parameters handled here are *.port, *.gain, *.balance and monitor_gain.
  * If oldai is specified, previous parameters are stored.
  * This function itself does not roll back if error occurred.
- * Must be called with sc_lock and sc_exlock held.
+ * Must be called with sc_lock && sc_exlock held.
  */
 static int
 audio_hw_setinfo(struct audio_softc *sc, const struct audio_info *newai,
@@ -7220,7 +7226,7 @@ abort:
  * - pfil, rfil will be filled with filter information specified by the
  *   hardware driver.
  * and then returns 0.  Otherwise returns errno.
- * Must be called with sc_lock held.
+ * Must be called without sc_lock held.
  */
 static int
 audio_hw_set_format(struct audio_softc *sc, int setmode,
@@ -7230,16 +7236,17 @@ audio_hw_set_format(struct audio_softc *
 	audio_params_t pp, rp;
 	int error;
 
-	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(phwfmt != NULL);
 	KASSERT(rhwfmt != NULL);
 
 	pp = format2_to_params(phwfmt);
 	rp = format2_to_params(rhwfmt);
 
+	mutex_enter(sc->sc_lock);
 	error = sc->hw_if->set_format(sc->hw_hdl, setmode,
 	    &pp, &rp, pfil, rfil);
 	if (error) {
+		mutex_exit(sc->sc_lock);
 		device_printf(sc->sc_dev,
 		    "set_format failed with %d\n", error);
 		return error;
@@ -7248,11 +7255,13 @@ audio_hw_set_format(struct audio_softc *
 	if (sc->hw_if->commit_settings) {
 		error = sc->hw_if->commit_settings(sc->hw_hdl);
 		if (error) {
+			mutex_exit(sc->sc_lock);
 			device_printf(sc->sc_dev,
 			    "commit_settings failed with %d\n", error);
 			return error;
 		}
 	}
+	mutex_exit(sc->sc_lock);
 
 	return 0;
 }
@@ -7260,9 +7269,7 @@ audio_hw_set_format(struct audio_softc *
 /*
  * Fill audio_info structure.  If need_mixerinfo is true, it will also
  * fill the hardware mixer information.
- * Must be called with sc_lock held.
- * Must be called with sc_exlock held, in addition, if need_mixerinfo is
- * true.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static int
 audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int need_mixerinfo,
@@ -7274,7 +7281,7 @@ audiogetinfo(struct audio_softc *sc, str
 	audio_track_t *rtrack;
 	int gain;
 
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock);
 
 	ri = &ai->record;
 	pi = &ai->play;
@@ -7367,7 +7374,7 @@ audiogetinfo(struct audio_softc *sc, str
 	}
 
 	if (need_mixerinfo) {
-		KASSERT(sc->sc_exlock);
+		mutex_enter(sc->sc_lock);
 
 		pi->port = au_get_port(sc, &sc->sc_outports);
 		ri->port = au_get_port(sc, &sc->sc_inports);
@@ -7383,6 +7390,7 @@ audiogetinfo(struct audio_softc *sc, str
 			if (gain != -1)
 				ai->monitor_gain = gain;
 		}
+		mutex_exit(sc->sc_lock);
 	}
 
 	return 0;
@@ -7521,7 +7529,9 @@ audio_sysctl_blk_ms(SYSCTLFN_ARGS)
 	node = *rnode;
 	sc = node.sysctl_data;
 
-	mutex_enter(sc->sc_lock);
+	error = audio_exlock_enter(sc);
+	if (error)
+		return error;
 
 	old_blk_ms = sc->sc_blk_ms;
 	t = old_blk_ms;
@@ -7568,7 +7578,7 @@ audio_sysctl_blk_ms(SYSCTLFN_ARGS)
 	}
 	error = 0;
 abort:
-	mutex_exit(sc->sc_lock);
+	audio_exlock_exit(sc);
 	return error;
 }
 
@@ -7586,7 +7596,9 @@ audio_sysctl_multiuser(SYSCTLFN_ARGS)
 	node = *rnode;
 	sc = node.sysctl_data;
 
-	mutex_enter(sc->sc_lock);
+	error = audio_exlock_enter(sc);
+	if (error)
+		return error;
 
 	t = sc->sc_multiuser;
 	node.sysctl_data = &t;
@@ -7597,7 +7609,7 @@ audio_sysctl_multiuser(SYSCTLFN_ARGS)
 	sc->sc_multiuser = t;
 	error = 0;
 abort:
-	mutex_exit(sc->sc_lock);
+	audio_exlock_exit(sc);
 	return error;
 }
 
@@ -7677,7 +7689,7 @@ audio_suspend(device_t dv, const pmf_qua
 	struct audio_softc *sc = device_private(dv);
 	int error;
 
-	error = audio_enter_exclusive(sc);
+	error = audio_exlock_mutex_enter(sc);
 	if (error)
 		return error;
 	audio_mixer_capture(sc);
@@ -7695,7 +7707,7 @@ audio_suspend(device_t dv, const pmf_qua
 #ifdef AUDIO_PM_IDLE
 	callout_halt(&sc->sc_idle_counter, sc->sc_lock);
 #endif
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 
 	return true;
 }
@@ -7707,7 +7719,7 @@ audio_resume(device_t dv, const pmf_qual
 	struct audio_info ai;
 	int error;
 
-	error = audio_enter_exclusive(sc);
+	error = audio_exlock_mutex_enter(sc);
 	if (error)
 		return error;
 
@@ -7721,7 +7733,7 @@ audio_resume(device_t dv, const pmf_qual
 	if (sc->sc_rbusy)
 		audio_rmixer_start(sc);
 
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 
 	return true;
 }
@@ -7822,6 +7834,10 @@ audio_diagnostic_ring(const char *where,
 /*
  * Mixer driver
  */
+
+/*
+ * Must be called without sc_lock held.
+ */
 int
 mixer_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
 	struct lwp *l)
@@ -7830,8 +7846,6 @@ mixer_open(dev_t dev, struct audio_softc
 	audio_file_t *af;
 	int error, fd;
 
-	KASSERT(mutex_owned(sc->sc_lock));
-
 	TRACE(1, "flags=0x%x", flags);
 
 	error = fd_allocfile(&fp, &fd);
@@ -7849,23 +7863,60 @@ mixer_open(dev_t dev, struct audio_softc
 }
 
 /*
+ * Add a process to those to be signalled on mixer activity.
+ * If the process has already been added, do nothing.
+ * Must be called with sc_exlock held and without sc_lock held.
+ */
+static void
+mixer_async_add(struct audio_softc *sc, pid_t pid)
+{
+	int i;
+
+	KASSERT(sc->sc_exlock);
+
+	/* If already exists, returns without doing anything. */
+	for (i = 0; i < sc->sc_am_used; i++) {
+		if (sc->sc_am[i] == pid)
+			return;
+	}
+
+	/* Extend array if necessary. */
+	if (sc->sc_am_used >= sc->sc_am_capacity) {
+		sc->sc_am_capacity += AM_CAPACITY;
+		sc->sc_am = kern_realloc(sc->sc_am,
+		    sc->sc_am_capacity * sizeof(pid_t), M_WAITOK);
+		TRACE(2, "realloc am_capacity=%d", sc->sc_am_capacity);
+	}
+
+	TRACE(2, "am[%d]=%d", sc->sc_am_used, (int)pid);
+	sc->sc_am[sc->sc_am_used++] = pid;
+}
+
+/*
  * Remove a process from those to be signalled on mixer activity.
- * Must be called with sc_lock held.
+ * If the process has not been added, do nothing.
+ * Must be called with sc_exlock held and without sc_lock held.
  */
 static void
-mixer_remove(struct audio_softc *sc)
+mixer_async_remove(struct audio_softc *sc, pid_t pid)
 {
-	struct mixer_asyncs **pm, *m;
-	pid_t pid;
+	int i;
 
-	KASSERT(mutex_owned(sc->sc_lock));
+	KASSERT(sc->sc_exlock);
 
-	pid = curproc->p_pid;
-	for (pm = &sc->sc_async_mixer; *pm; pm = &(*pm)->next) {
-		if ((*pm)->pid == pid) {
-			m = *pm;
-			*pm = m->next;
-			kmem_free(m, sizeof(*m));
+	for (i = 0; i < sc->sc_am_used; i++) {
+		if (sc->sc_am[i] == pid) {
+			sc->sc_am[i] = sc->sc_am[--sc->sc_am_used];
+			TRACE(2, "am[%d](%d) removed, used=%d",
+			    i, (int)pid, sc->sc_am_used);
+
+			/* Empty array if no longer necessary. */
+			if (sc->sc_am_used == 0) {
+				kern_free(sc->sc_am);
+				sc->sc_am = NULL;
+				sc->sc_am_capacity = 0;
+				TRACE(2, "released");
+			}
 			return;
 		}
 	}
@@ -7873,17 +7924,20 @@ mixer_remove(struct audio_softc *sc)
 
 /*
  * Signal all processes waiting for the mixer.
- * Must be called with sc_lock held.
+ * Must be called with sc_exlock held.
  */
 static void
 mixer_signal(struct audio_softc *sc)
 {
-	struct mixer_asyncs *m;
 	proc_t *p;
+	int i;
 
-	for (m = sc->sc_async_mixer; m; m = m->next) {
+	KASSERT(sc->sc_exlock);
+
+	for (i = 0; i < sc->sc_am_used; i++) {
 		mutex_enter(proc_lock);
-		if ((p = proc_find(m->pid)) != NULL)
+		p = proc_find(sc->sc_am[i]);
+		if (p)
 			psignal(p, SIGIO);
 		mutex_exit(proc_lock);
 	}
@@ -7895,11 +7949,14 @@ mixer_signal(struct audio_softc *sc)
 int
 mixer_close(struct audio_softc *sc, audio_file_t *file)
 {
+	int error;
 
-	mutex_enter(sc->sc_lock);
+	error = audio_exlock_enter(sc);
+	if (error)
+		return error;
 	TRACE(1, "");
-	mixer_remove(sc);
-	mutex_exit(sc->sc_lock);
+	mixer_async_remove(sc, curproc->p_pid);
+	audio_exlock_exit(sc);
 
 	return 0;
 }
@@ -7911,7 +7968,6 @@ int
 mixer_ioctl(struct audio_softc *sc, u_long cmd, void *addr, int flag,
 	struct lwp *l)
 {
-	struct mixer_asyncs *ma;
 	mixer_devinfo_t *mi;
 	mixer_ctrl_t *mc;
 	int error;
@@ -7929,29 +7985,22 @@ mixer_ioctl(struct audio_softc *sc, u_lo
 
 	switch (cmd) {
 	case FIOASYNC:
+		error = audio_exlock_enter(sc);
+		if (error)
+			break;
 		if (*(int *)addr) {
-			ma = kmem_alloc(sizeof(struct mixer_asyncs), KM_SLEEP);
+			mixer_async_add(sc, curproc->p_pid);
 		} else {
-			ma = NULL;
+			mixer_async_remove(sc, curproc->p_pid);
 		}
-		mutex_enter(sc->sc_lock);
-		mixer_remove(sc);	/* remove old entry */
-		if (ma != NULL) {
-			ma->next = sc->sc_async_mixer;
-			ma->pid = curproc->p_pid;
-			sc->sc_async_mixer = ma;
-		}
-		mutex_exit(sc->sc_lock);
-		error = 0;
+		audio_exlock_exit(sc);
 		break;
 
 	case AUDIO_GETDEV:
 		TRACE(2, "AUDIO_GETDEV");
-		error = audio_enter_exclusive(sc);
-		if (error)
-			break;
+		mutex_enter(sc->sc_lock);
 		error = sc->hw_if->getdev(sc->hw_hdl, (audio_device_t *)addr);
-		audio_exit_exclusive(sc);
+		mutex_exit(sc->sc_lock);
 		break;
 
 	case AUDIO_MIXER_DEVINFO:
@@ -7968,7 +8017,7 @@ mixer_ioctl(struct audio_softc *sc, u_lo
 		TRACE(2, "AUDIO_MIXER_READ");
 		mc = (mixer_ctrl_t *)addr;
 
-		error = audio_enter_exclusive(sc);
+		error = audio_exlock_mutex_enter(sc);
 		if (error)
 			break;
 		if (device_is_active(sc->hw_dev))
@@ -7981,39 +8030,38 @@ mixer_ioctl(struct audio_softc *sc, u_lo
 			    sizeof(mixer_ctrl_t));
 			error = 0;
 		}
-		audio_exit_exclusive(sc);
+		audio_exlock_mutex_exit(sc);
 		break;
 
 	case AUDIO_MIXER_WRITE:
 		TRACE(2, "AUDIO_MIXER_WRITE");
-		error = audio_enter_exclusive(sc);
+		error = audio_exlock_mutex_enter(sc);
 		if (error)
 			break;
 		error = audio_set_port(sc, (mixer_ctrl_t *)addr);
 		if (error) {
-			audio_exit_exclusive(sc);
+			audio_exlock_mutex_exit(sc);
 			break;
 		}
 
 		if (sc->hw_if->commit_settings) {
 			error = sc->hw_if->commit_settings(sc->hw_hdl);
 			if (error) {
-				audio_exit_exclusive(sc);
+				audio_exlock_mutex_exit(sc);
 				break;
 			}
 		}
+		mutex_exit(sc->sc_lock);
 		mixer_signal(sc);
-		audio_exit_exclusive(sc);
+		audio_exlock_exit(sc);
 		break;
 
 	default:
 		if (sc->hw_if->dev_ioctl) {
-			error = audio_enter_exclusive(sc);
-			if (error)
-				break;
+			mutex_enter(sc->sc_lock);
 			error = sc->hw_if->dev_ioctl(sc->hw_hdl,
 			    cmd, addr, flag, l);
-			audio_exit_exclusive(sc);
+			mutex_exit(sc->sc_lock);
 		} else
 			error = EINVAL;
 		break;
@@ -8534,7 +8582,7 @@ audio_volume_down(device_t dv)
 	u_int gain;
 	u_char balance;
 
-	if (audio_enter_exclusive(sc) != 0)
+	if (audio_exlock_mutex_enter(sc) != 0)
 		return;
 	if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) {
 		mi.index = sc->sc_outports.master;
@@ -8547,7 +8595,7 @@ audio_volume_down(device_t dv)
 			au_set_gain(sc, &sc->sc_outports, newgain, balance);
 		}
 	}
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 }
 
 static void
@@ -8558,7 +8606,7 @@ audio_volume_up(device_t dv)
 	u_int gain, newgain;
 	u_char balance;
 
-	if (audio_enter_exclusive(sc) != 0)
+	if (audio_exlock_mutex_enter(sc) != 0)
 		return;
 	if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) {
 		mi.index = sc->sc_outports.master;
@@ -8571,7 +8619,7 @@ audio_volume_up(device_t dv)
 			au_set_gain(sc, &sc->sc_outports, newgain, balance);
 		}
 	}
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 }
 
 static void
@@ -8581,7 +8629,7 @@ audio_volume_toggle(device_t dv)
 	u_int gain, newgain;
 	u_char balance;
 
-	if (audio_enter_exclusive(sc) != 0)
+	if (audio_exlock_mutex_enter(sc) != 0)
 		return;
 	au_get_gain(sc, &sc->sc_outports, &gain, &balance);
 	if (gain != 0) {
@@ -8590,9 +8638,12 @@ audio_volume_toggle(device_t dv)
 	} else
 		newgain = sc->sc_lastgain;
 	au_set_gain(sc, &sc->sc_outports, newgain, balance);
-	audio_exit_exclusive(sc);
+	audio_exlock_mutex_exit(sc);
 }
 
+/*
+ * Must be called with sc_lock held.
+ */
 static int
 audio_query_devinfo(struct audio_softc *sc, mixer_devinfo_t *di)
 {

Index: src/sys/dev/audio/audiovar.h
diff -u src/sys/dev/audio/audiovar.h:1.4.2.2 src/sys/dev/audio/audiovar.h:1.4.2.3
--- src/sys/dev/audio/audiovar.h:1.4.2.2	Sat Mar 21 15:47:01 2020
+++ src/sys/dev/audio/audiovar.h	Thu Apr 30 16:05:18 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiovar.h,v 1.4.2.2 2020/03/21 15:47:01 martin Exp $	*/
+/*	$NetBSD: audiovar.h,v 1.4.2.3 2020/04/30 16:05:18 martin Exp $	*/
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -158,34 +158,37 @@ struct audio_softc {
 
 	/*
 	 * Blocksize in msec.
-	 * Must be protected by sc_lock.
+	 * Must be protected by sc_exlock.
 	 */
 	int sc_blk_ms;
 
 	/*
 	 * Track mixer for playback and recording.
 	 * If null, the mixer is disabled.
+	 * Must be protected by sc_exlock.
 	 */
 	audio_trackmixer_t *sc_pmixer;
 	audio_trackmixer_t *sc_rmixer;
 
 	/*
 	 * Opening track counter.
-	 * Must be protected by sc_lock.
+	 * Must be protected by sc_lock && sc_exlock for modifying.
+	 * Must be protected by sc_lock || sc_exlock for reference.
 	 */
 	int sc_popens;
 	int sc_ropens;
 
 	/*
 	 * true if the track mixer is running.
-	 * Must be protected by sc_lock.
+	 * Must be protected by sc_exlock && sc_intr_lock for modifying.
+	 * Must be protected by sc_exlock || sc_intr_lock for reference.
 	 */
 	bool sc_pbusy;
 	bool sc_rbusy;
 
 	/*
 	 * These four are the parameters sustained with /dev/sound.
-	 * Must be protected by sc_lock.
+	 * Must be protected by sc_exlock.
 	 */
 	audio_format2_t sc_sound_pparams;
 	audio_format2_t sc_sound_rparams;
@@ -204,13 +207,15 @@ struct audio_softc {
 	struct selinfo sc_rsel;
 
 	/*
-	 * processes who want mixer SIGIO.
-	 * Must be protected by sc_lock.
-	 */
-	struct	mixer_asyncs {
-		struct mixer_asyncs *next;
-		pid_t	pid;
-	} *sc_async_mixer;
+	 * Processes who want mixer SIGIO.
+	 * sc_am is an array of pids, or NULL if empty.
+	 * sc_am_capacity is the number of allocated elements.
+	 * sc_am_used is the number of elements actually used.
+	 * Must be protected by sc_exlock.
+	 */
+	pid_t *sc_am;
+	int sc_am_capacity;
+	int sc_am_used;
 
 	/*
 	 * Thread lock and interrupt lock obtained by get_locks().
@@ -240,7 +245,7 @@ struct audio_softc {
 	/*
 	 * If multiuser is false, other users who have different euid
 	 * than the first user cannot open this device.
-	 * Must be protected by sc_lock.
+	 * Must be protected by sc_exlock.
 	 */
 	bool sc_multiuser;
 	kauth_cred_t sc_cred;

Reply via email to