Tue Sep 29 15:58:54 2009 UTC ()
Add support for playback- or capture-only devices.

Fixes PR 42050


(sborrill)
diff -r1.69 -r1.70 src/share/man/man4/audio.4
diff -r1.247 -r1.248 src/sys/dev/audio.c
diff -r1.65 -r1.66 src/sys/dev/audio_if.h
diff -r1.15 -r1.16 src/sys/dev/pci/hdaudio/hdaudio_afg.c
diff -r1.32 -r1.33 src/sys/sys/audioio.h

cvs diff -r1.69 -r1.70 src/share/man/man4/audio.4 (expand / switch to context diff)
--- src/share/man/man4/audio.4 2009/01/03 17:44:20 1.69
+++ src/share/man/man4/audio.4 2009/09/29 15:58:54 1.70
@@ -1,4 +1,4 @@
-.\"	$NetBSD: audio.4,v 1.69 2009/01/03 17:44:20 christos Exp $
+.\"	$NetBSD: audio.4,v 1.70 2009/09/29 15:58:54 sborrill Exp $
 .\"
 .\" Copyright (c) 1996 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd January 3, 2009
+.Dd September 29, 2009
 .Dt AUDIO 4
 .Os
 .Sh NAME
@@ -243,6 +243,10 @@
 .It Dv AUDIO_PROP_INDEPENDENT
 the device can set the playing and recording encoding parameters
 independently.
+.It Dv AUDIO_PROP_PLAYBACK
+the device is capable of audio playback.
+.It Dv AUDIO_PROP_CAPTURE
+the device is capable of audio capture.
 .El
 .It Dv AUDIO_GETIOFFS (audio_offset_t)
 .It Dv AUDIO_GETOOFFS (audio_offset_t)

cvs diff -r1.247 -r1.248 src/sys/dev/Attic/audio.c (expand / switch to context diff)
--- src/sys/dev/Attic/audio.c 2009/09/24 16:03:11 1.247
+++ src/sys/dev/Attic/audio.c 2009/09/29 15:58:54 1.248
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio.c,v 1.247 2009/09/24 16:03:11 sborrill Exp $	*/
+/*	$NetBSD: audio.c,v 1.248 2009/09/29 15:58:54 sborrill Exp $	*/
 
 /*
  * Copyright (c) 1991-1993 Regents of the University of California.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.247 2009/09/24 16:03:11 sborrill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.248 2009/09/29 15:58:54 sborrill Exp $");
 
 #include "audio.h"
 #if NAUDIO > 0
@@ -192,6 +192,10 @@
 static void	audio_mixer_capture(struct audio_softc *);
 static void	audio_mixer_restore(struct audio_softc *);
 
+static int	audio_get_props(struct audio_softc *);
+static bool	audio_can_playback(struct audio_softc *);
+static bool	audio_can_capture(struct audio_softc *);
+
 static void	audio_softintr_rd(void *);
 static void	audio_softintr_wr(void *);
 
@@ -315,41 +319,51 @@
 	}
 #endif
 
-	props = hwp->get_props(hdlp);
+	sc->hw_if = hwp;
+	sc->hw_hdl = hdlp;
+	sc->sc_dev = parent;
+	sc->sc_opencnt = 0;
+	sc->sc_writing = sc->sc_waitcomp = 0;
+	sc->sc_lastinfovalid = false;
 
-	aprint_naive("\n");
+	props = audio_get_props(sc);
 
 	if (props & AUDIO_PROP_FULLDUPLEX)
 		aprint_normal(": full duplex");
 	else
 		aprint_normal(": half duplex");
 
+	if (props & AUDIO_PROP_PLAYBACK)
+		aprint_normal(", playback");
+	if (props & AUDIO_PROP_CAPTURE)
+		aprint_normal(", capture");
 	if (props & AUDIO_PROP_MMAP)
 		aprint_normal(", mmap");
 	if (props & AUDIO_PROP_INDEPENDENT)
 		aprint_normal(", independent");
 
+	aprint_naive("\n");
 	aprint_normal("\n");
 
-	sc->hw_if = hwp;
-	sc->hw_hdl = hdlp;
-	sc->sc_dev = parent;
-	sc->sc_opencnt = 0;
-	sc->sc_writing = sc->sc_waitcomp = 0;
-	sc->sc_lastinfovalid = false;
-
-	error = audio_alloc_ring(sc, &sc->sc_pr, AUMODE_PLAY, AU_RING_SIZE);
-	if (error) {
-		sc->hw_if = NULL;
-		aprint_error("audio: could not allocate play buffer\n");
-		return;
+	if (audio_can_playback(sc)) {
+		error = audio_alloc_ring(sc, &sc->sc_pr,
+		    AUMODE_PLAY, AU_RING_SIZE);
+		if (error) {
+			sc->hw_if = NULL;
+			aprint_error("audio: could not allocate play buffer\n");
+			return;
+		}
 	}
-	error = audio_alloc_ring(sc, &sc->sc_rr, AUMODE_RECORD, AU_RING_SIZE);
-	if (error) {
-		audio_free_ring(sc, &sc->sc_pr);
-		sc->hw_if = NULL;
-		aprint_error("audio: could not allocate record buffer\n");
-		return;
+	if (audio_can_capture(sc)) {
+		error = audio_alloc_ring(sc, &sc->sc_rr,
+		    AUMODE_RECORD, AU_RING_SIZE);
+		if (error) {
+			if (sc->sc_pr.s.start != 0)
+				audio_free_ring(sc, &sc->sc_pr);
+			sc->hw_if = NULL;
+			aprint_error("audio: could not allocate record buffer\n");
+			return;
+		}
 	}
 
 	sc->sc_lastgain = 128;
@@ -742,6 +756,8 @@
 void
 audio_free_ring(struct audio_softc *sc, struct audio_ringbuffer *r)
 {
+	if (r->s.start == 0)
+		return;
 
 	if (sc->hw_if->freem)
 		sc->hw_if->freem(sc->hw_hdl, r->s.start, M_DEVBUF);
@@ -1301,41 +1317,49 @@
 
 	DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_mode));
 	hw = sc->hw_if;
-	audio_init_ringbuffer(sc, &sc->sc_rr, AUMODE_RECORD);
-	if (hw->init_input && (sc->sc_mode & AUMODE_RECORD)) {
-		error = hw->init_input(sc->hw_hdl, sc->sc_rr.s.start,
+	if (audio_can_capture(sc)) {
+		audio_init_ringbuffer(sc, &sc->sc_rr, AUMODE_RECORD);
+		if (hw->init_input && (sc->sc_mode & AUMODE_RECORD)) {
+			error = hw->init_input(sc->hw_hdl, sc->sc_rr.s.start,
 				       sc->sc_rr.s.end - sc->sc_rr.s.start);
-		if (error)
-			return error;
+			if (error)
+				return error;
+		}
 	}
 
-	audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
-	sc->sc_sil_count = 0;
-	if (hw->init_output && (sc->sc_mode & AUMODE_PLAY)) {
-		error = hw->init_output(sc->hw_hdl, sc->sc_pr.s.start,
+	if (audio_can_playback(sc)) {
+		audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
+		sc->sc_sil_count = 0;
+		if (hw->init_output && (sc->sc_mode & AUMODE_PLAY)) {
+			error = hw->init_output(sc->hw_hdl, sc->sc_pr.s.start,
 					sc->sc_pr.s.end - sc->sc_pr.s.start);
-		if (error)
-			return error;
+			if (error)
+				return error;
+		}
 	}
 
 #ifdef AUDIO_INTR_TIME
 #define double u_long
-	sc->sc_pnintr = 0;
-	sc->sc_pblktime = (u_long)(
-	    (double)sc->sc_pr.blksize * 100000 /
-	    (double)(sc->sc_pparams.precision / NBBY *
-		     sc->sc_pparams.channels *
-		     sc->sc_pparams.sample_rate)) * 10;
-	DPRINTF(("audio: play blktime = %lu for %d\n",
-		 sc->sc_pblktime, sc->sc_pr.blksize));
-	sc->sc_rnintr = 0;
-	sc->sc_rblktime = (u_long)(
-	    (double)sc->sc_rr.blksize * 100000 /
-	    (double)(sc->sc_rparams.precision / NBBY *
-		     sc->sc_rparams.channels *
-		     sc->sc_rparams.sample_rate)) * 10;
-	DPRINTF(("audio: record blktime = %lu for %d\n",
-		 sc->sc_rblktime, sc->sc_rr.blksize));
+	if (audio_can_playback(sc)) {
+		sc->sc_pnintr = 0;
+		sc->sc_pblktime = (u_long)(
+		    (double)sc->sc_pr.blksize * 100000 /
+		    (double)(sc->sc_pparams.precision / NBBY *
+			     sc->sc_pparams.channels *
+			     sc->sc_pparams.sample_rate)) * 10;
+		DPRINTF(("audio: play blktime = %lu for %d\n",
+			 sc->sc_pblktime, sc->sc_pr.blksize));
+	}
+	if (audio_can_capture(sc)) {
+		sc->sc_rnintr = 0;
+		sc->sc_rblktime = (u_long)(
+		    (double)sc->sc_rr.blksize * 100000 /
+		    (double)(sc->sc_rparams.precision / NBBY *
+			     sc->sc_rparams.channels *
+			     sc->sc_rparams.sample_rate)) * 10;
+		DPRINTF(("audio: record blktime = %lu for %d\n",
+			 sc->sc_rblktime, sc->sc_rr.blksize));
+	}
 #undef double
 #endif
 
@@ -1347,18 +1371,24 @@
 {
 
 	/* set high at 100% */
-	sc->sc_pr.usedhigh = sc->sc_pustream->end - sc->sc_pustream->start;
-	/* set low at 75% of usedhigh */
-	sc->sc_pr.usedlow = sc->sc_pr.usedhigh * 3 / 4;
-	if (sc->sc_pr.usedlow == sc->sc_pr.usedhigh)
-		sc->sc_pr.usedlow -= sc->sc_pr.blksize;
+	if (audio_can_playback(sc)) {
+		sc->sc_pr.usedhigh =
+		    sc->sc_pustream->end - sc->sc_pustream->start;
+		/* set low at 75% of usedhigh */
+		sc->sc_pr.usedlow = sc->sc_pr.usedhigh * 3 / 4;
+		if (sc->sc_pr.usedlow == sc->sc_pr.usedhigh)
+			sc->sc_pr.usedlow -= sc->sc_pr.blksize;
+	}
 
-	sc->sc_rr.usedhigh = sc->sc_rustream->end - sc->sc_rustream->start
-		- sc->sc_rr.blksize;
-	sc->sc_rr.usedlow = 0;
-	DPRINTF(("%s: plow=%d phigh=%d rlow=%d rhigh=%d\n", __func__,
-		 sc->sc_pr.usedlow, sc->sc_pr.usedhigh,
-		 sc->sc_rr.usedlow, sc->sc_rr.usedhigh));
+	if (audio_can_capture(sc)) {
+		sc->sc_rr.usedhigh =
+		    sc->sc_rustream->end - sc->sc_rustream->start -
+		    sc->sc_rr.blksize;
+		sc->sc_rr.usedlow = 0;
+		DPRINTF(("%s: plow=%d phigh=%d rlow=%d rhigh=%d\n", __func__,
+			 sc->sc_pr.usedlow, sc->sc_pr.usedhigh,
+			 sc->sc_rr.usedlow, sc->sc_rr.usedhigh));
+	}
 }
 
 static inline int
@@ -1436,7 +1466,7 @@
 
 	sc->sc_full_duplex = 
 		(flags & (FWRITE|FREAD)) == (FWRITE|FREAD) &&
-		(hw->get_props(sc->hw_hdl) & AUDIO_PROP_FULLDUPLEX);
+		(audio_get_props(sc) & AUDIO_PROP_FULLDUPLEX);
 
 	mode = 0;
 	if (flags & FREAD) {
@@ -2251,7 +2281,7 @@
 	case AUDIO_SETFD:
 		DPRINTF(("AUDIO_SETFD\n"));
 		fd = *(int *)addr;
-		if (hw->get_props(sc->hw_hdl) & AUDIO_PROP_FULLDUPLEX) {
+		if (audio_get_props(sc) & AUDIO_PROP_FULLDUPLEX) {
 			if (hw->setfd)
 				error = hw->setfd(sc->hw_hdl, fd);
 			else
@@ -2268,7 +2298,7 @@
 
 	case AUDIO_GETPROPS:
 		DPRINTF(("AUDIO_GETPROPS\n"));
-		*(int *)addr = hw->get_props(sc->hw_hdl);
+		*(int *)addr = audio_get_props(sc);
 		break;
 
 	default:
@@ -2439,7 +2469,7 @@
 
 	DPRINTF(("audio_mmap: off=%lld, prot=%d\n", (long long)off, prot));
 	hw = sc->hw_if;
-	if (!(hw->get_props(sc->hw_hdl) & AUDIO_PROP_MMAP) || !hw->mappage)
+	if (!(audio_get_props(sc) & AUDIO_PROP_MMAP) || !hw->mappage)
 		return -1;
 #if 0
 /* XXX
@@ -2498,6 +2528,9 @@
 		 sc->sc_rr.s.start, audio_stream_get_used(&sc->sc_rr.s),
 		 sc->sc_rr.usedhigh, sc->sc_rr.mmapped));
 
+	if (!audio_can_capture(sc))
+		return EINVAL;
+
 	if (sc->hw_if->trigger_input)
 		error = sc->hw_if->trigger_input(sc->hw_hdl, sc->sc_rr.s.start,
 		    sc->sc_rr.s.end, sc->sc_rr.blksize,
@@ -2524,6 +2557,9 @@
 		 sc->sc_pr.s.start, used, sc->sc_pr.usedhigh,
 		 sc->sc_pr.blksize, sc->sc_pr.mmapped));
 
+	if (!audio_can_playback(sc))
+		return EINVAL;
+
 	if (!sc->sc_pr.mmapped && used < sc->sc_pr.blksize) {
 		wakeup(&sc->sc_wchan);
 		DPRINTF(("%s: wakeup and return\n", __func__));
@@ -3331,6 +3367,12 @@
 		rp.channels = r->channels;
 		nr++;
 	}
+
+	if (!audio_can_capture(sc))
+		nr = 0;
+	if (!audio_can_playback(sc))
+		np = 0;
+
 #ifdef AUDIO_DEBUG
 	if (audiodebug && nr > 0)
 	    audio_print_params("audiosetinfo() Setting record params:", &rp);
@@ -3382,7 +3424,7 @@
 	if (modechange) {
 		int indep;
 
-		indep = hw->get_props(sc->hw_hdl) & AUDIO_PROP_INDEPENDENT;
+		indep = audio_get_props(sc) & AUDIO_PROP_INDEPENDENT;
 		if (!indep) {
 			if (setmode == AUMODE_RECORD)
 				pp = rp;
@@ -3716,12 +3758,15 @@
 	p->active = sc->sc_pbus;
 	r->active = sc->sc_rbus;
 
-	p->buffer_size = sc->sc_pustream->bufsize;
-	r->buffer_size = sc->sc_rustream->bufsize;
+	p->buffer_size = sc->sc_pustream ? sc->sc_pustream->bufsize : 0;
+	r->buffer_size = sc->sc_rustream ? sc->sc_rustream->bufsize : 0;
 
 	ai->blocksize = sc->sc_pr.blksize;
-	ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize;
-	ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize;
+	if (sc->sc_pr.blksize > 0) {
+		ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize;
+		ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize;
+	} else
+		ai->hiwat = ai->lowat = 0;
 	ai->mode = sc->sc_mode;
 
 	return 0;
@@ -4135,6 +4180,37 @@
 		newgain = sc->sc_lastgain;
 	au_set_gain(sc, &sc->sc_outports, newgain, balance);
 	splx(s);
+}
+
+static int
+audio_get_props(struct audio_softc *sc)
+{
+	const struct audio_hw_if *hw;
+	int props;
+
+	hw = sc->hw_if;
+	props = hw->get_props(sc->hw_hdl);
+
+	/*
+	 * if neither playback nor capture properties are reported,
+	 * assume both are supported by the device driver
+	 */
+	if ((props & (AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE)) == 0)
+		props |= (AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE);
+
+	return props;
+}
+
+static bool
+audio_can_playback(struct audio_softc *sc)
+{
+	return audio_get_props(sc) & AUDIO_PROP_PLAYBACK ? true : false;
+}
+
+static bool
+audio_can_capture(struct audio_softc *sc)
+{
+	return audio_get_props(sc) & AUDIO_PROP_CAPTURE ? true : false;
 }
 
 #endif /* NAUDIO > 0 */

cvs diff -r1.65 -r1.66 src/sys/dev/Attic/audio_if.h (expand / switch to context diff)
--- src/sys/dev/Attic/audio_if.h 2008/03/04 18:23:44 1.65
+++ src/sys/dev/Attic/audio_if.h 2009/09/29 15:58:54 1.66
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio_if.h,v 1.65 2008/03/04 18:23:44 cube Exp $	*/
+/*	$NetBSD: audio_if.h,v 1.66 2009/09/29 15:58:54 sborrill Exp $	*/
 
 /*
  * Copyright (c) 1994 Havard Eidnes.
@@ -86,13 +86,15 @@
 static __inline int
 audio_stream_get_space(const audio_stream_t *s)
 {
-	return (s->end - s->start) - s->used;
+	if (s)
+		return (s->end - s->start) - s->used;
+	return 0;
 }
 
 static __inline int
 audio_stream_get_used(const audio_stream_t *s)
 {
-	return s->used;
+	return s ? s->used : 0;
 }
 
 static __inline uint8_t *

cvs diff -r1.15 -r1.16 src/sys/dev/pci/hdaudio/Attic/hdaudio_afg.c (expand / switch to context diff)
--- src/sys/dev/pci/hdaudio/Attic/hdaudio_afg.c 2009/09/27 02:36:38 1.15
+++ src/sys/dev/pci/hdaudio/Attic/hdaudio_afg.c 2009/09/29 15:58:54 1.16
@@ -1,4 +1,4 @@
-/* $NetBSD: hdaudio_afg.c,v 1.15 2009/09/27 02:36:38 jmcneill Exp $ */
+/* $NetBSD: hdaudio_afg.c,v 1.16 2009/09/29 15:58:54 sborrill Exp $ */
 
 /*
  * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk>
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hdaudio_afg.c,v 1.15 2009/09/27 02:36:38 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hdaudio_afg.c,v 1.16 2009/09/29 15:58:54 sborrill Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -3323,13 +3323,11 @@
 	int bufsize;
 
 	st = (mode == AUMODE_PLAY) ? ad->ad_playback : ad->ad_capture;
-#ifdef DIAGNOSTIC
 	if (st == NULL) {
-		hda_error(ad->ad_sc,
+		hda_trace(ad->ad_sc,
 		    "round_blocksize called for invalid stream\n");
-		return 256;
+		return 128;
 	}
-#endif
 
 	/* Multiple of 128 */
 	blksize &= ~128;
@@ -3583,8 +3581,21 @@
 static int
 hdaudio_afg_get_props(void *opaque)
 {
+	struct hdaudio_audiodev *ad = opaque;
+	int props = 0;
+
+	if (ad->ad_playback)
+		props |= AUDIO_PROP_PLAYBACK;
+	if (ad->ad_capture)
+		props |= AUDIO_PROP_CAPTURE;
+	if (ad->ad_playback && ad->ad_capture) {
+		props |= AUDIO_PROP_FULLDUPLEX;
+		props |= AUDIO_PROP_INDEPENDENT;
+	}
+
 	/* TODO: AUDIO_PROP_MMAP */
-	return AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
+
+	return props;
 }
 
 static int

cvs diff -r1.32 -r1.33 src/sys/sys/audioio.h (expand / switch to context diff)
--- src/sys/sys/audioio.h 2007/06/11 13:05:47 1.32
+++ src/sys/sys/audioio.h 2009/09/29 15:58:54 1.33
@@ -1,4 +1,4 @@
-/*	$NetBSD: audioio.h,v 1.32 2007/06/11 13:05:47 joerg Exp $	*/
+/*	$NetBSD: audioio.h,v 1.33 2009/09/29 15:58:54 sborrill Exp $	*/
 
 /*
  * Copyright (c) 1991-1993 Regents of the University of California.
@@ -185,6 +185,8 @@
 #define  AUDIO_PROP_FULLDUPLEX	0x01
 #define  AUDIO_PROP_MMAP	0x02
 #define  AUDIO_PROP_INDEPENDENT	0x04
+#define  AUDIO_PROP_PLAYBACK	0x10
+#define  AUDIO_PROP_CAPTURE	0x20
 #define AUDIO_GETBUFINFO	_IOR('A', 35, struct audio_info)
 
 /*