Module Name:    src
Committed By:   nat
Date:           Sun May  7 08:19:39 UTC 2017

Modified Files:
        src/share/man/man4: audio.4
        src/sys/dev: audio.c audiovar.h

Log Message:
Audio mmap now works with all devices as hw->mappage is no longer used.
Audio ring buffers are now uvm objects.

This was made possibile with help from christos@ and much help and mmap
functions written by riastradh@.


To generate a diff of this commit:
cvs rdiff -u -r1.78 -r1.79 src/share/man/man4/audio.4
cvs rdiff -u -r1.335 -r1.336 src/sys/dev/audio.c
cvs rdiff -u -r1.54 -r1.55 src/sys/dev/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/share/man/man4/audio.4
diff -u src/share/man/man4/audio.4:1.78 src/share/man/man4/audio.4:1.79
--- src/share/man/man4/audio.4:1.78	Mon Apr 17 22:40:06 2017
+++ src/share/man/man4/audio.4	Sun May  7 08:19:39 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: audio.4,v 1.78 2017/04/17 22:40:06 nat Exp $
+.\"	$NetBSD: audio.4,v 1.79 2017/05/07 08:19:39 nat Exp $
 .\"
 .\" Copyright (c) 1996 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -190,7 +190,6 @@ or
 .Xr write 2
 calls, but it can also be mapped into user memory with
 .Xr mmap 2
-(when supported by the device).
 Once the device has been mapped it can no longer be accessed
 by read or write; all access is by reading and writing to
 the mapped memory.
@@ -776,6 +775,3 @@ string values.
 .Sh HISTORY
 Support for virtual channels and mixing first appeared in
 .Nx 8.0 .
-.Sh BUGS
-.Xr mmap 2
-currently does not work and should be avoided.

Index: src/sys/dev/audio.c
diff -u src/sys/dev/audio.c:1.335 src/sys/dev/audio.c:1.336
--- src/sys/dev/audio.c:1.335	Sat May  6 00:13:25 2017
+++ src/sys/dev/audio.c	Sun May  7 08:19:39 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: audio.c,v 1.335 2017/05/06 00:13:25 nat Exp $	*/
+/*	$NetBSD: audio.c,v 1.336 2017/05/07 08:19:39 nat Exp $	*/
 
 /*-
  * Copyright (c) 2016 Nathanial Sloss <nathanialsl...@yahoo.com.au>
@@ -148,7 +148,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.335 2017/05/06 00:13:25 nat Exp $");
+__KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.336 2017/05/07 08:19:39 nat Exp $");
 
 #include "audio.h"
 #if NAUDIO > 0
@@ -179,6 +179,7 @@ __KERNEL_RCSID(0, "$NetBSD: audio.c,v 1.
 #include <sys/intr.h>
 #include <sys/kthread.h>
 #include <sys/cpu.h>
+#include <sys/mman.h>
 
 #include <dev/audio_if.h>
 #include <dev/audiovar.h>
@@ -231,8 +232,8 @@ int	audio_ioctl(dev_t, struct audio_soft
 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_mmap(struct audio_softc *, off_t *, size_t, int, int *, int *,
+		   struct uvm_object **, int *, struct virtual_channel *);
 static	int audio_fop_mmap(struct file *, off_t *, size_t, int, int *, int *,
 			   struct uvm_object **, int *);
 
@@ -1175,6 +1176,9 @@ audio_alloc_ring(struct audio_softc *sc,
 	const struct audio_hw_if *hw;
 	struct audio_chan *chan;
 	void *hdl;
+	vaddr_t vstart;
+	vsize_t vsize;
+	int error;
 
 	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
 	hw = sc->hw_if;
@@ -1188,12 +1192,35 @@ audio_alloc_ring(struct audio_softc *sc,
 	if (hw->round_buffersize) {
 		bufsize = hw->round_buffersize(hdl, direction, bufsize);
 	}
-	if (hw->allocm && (r == &chan->vc->sc_mpr || r == &chan->vc->sc_mrr))
+
+	if (hw->allocm && (r == &chan->vc->sc_mpr || r == &chan->vc->sc_mrr)) {
+		/* Hardware ringbuffer.	 No dedicated uvm object.*/
+		r->uobj = NULL;
 		r->s.start = hw->allocm(hdl, direction, bufsize);
-	else
-		r->s.start = kmem_zalloc(bufsize, KM_SLEEP);
-	if (r->s.start == NULL)
-		return ENOMEM;
+		if (r->s.start == NULL)
+			return ENOMEM;
+	} else {
+		/* Software ringbuffer.	 */
+		vstart = 0;
+
+		/* Get a nonzero multiple of PAGE_SIZE.	 */
+		vsize = roundup2(MAX(bufsize, PAGE_SIZE), PAGE_SIZE);
+
+		/* Create a uvm anonymous object.  */
+		r->uobj = uao_create(vsize, 0);
+
+		/* Map it into the kernel virtual address space.  */
+		error = uvm_map(kernel_map, &vstart, vsize, r->uobj, 0, 0,
+		    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_INH_NONE,
+			UVM_ADV_NORMAL, 0));
+		if (error) {
+			uao_detach(r->uobj);	/* release reference */
+			r->uobj = NULL;		/* paranoia */
+			return error;
+		}
+		r->s.start = (void *)vstart;
+	}
+
 	r->s.bufsize = bufsize;
 
 	return 0;
@@ -1203,6 +1230,8 @@ void
 audio_free_ring(struct audio_softc *sc, struct audio_ringbuffer *r)
 {
 	struct audio_chan *chan;
+	vaddr_t vstart;
+	vsize_t vsize;
 
 	if (r->s.start == NULL)
 		return;
@@ -1210,10 +1239,25 @@ audio_free_ring(struct audio_softc *sc, 
 	chan = SIMPLEQ_FIRST(&sc->sc_audiochan);
 
 	if (sc->hw_if->freem && (r == &chan->vc->sc_mpr ||
-						r == &chan->vc->sc_mrr))
+					r == &chan->vc->sc_mrr)) {
+		 /* Hardware ringbuffer.  */
+		KASSERT(r->uobj == NULL);
 		sc->hw_if->freem(sc->hw_hdl, r->s.start, r->s.bufsize);
-	else
-		kmem_free(r->s.start, r->s.bufsize);
+	} else {
+				/* Software ringbuffer.	 */
+		vstart = (vaddr_t)r->s.start;
+		vsize = roundup2(MAX(r->s.bufsize, PAGE_SIZE), PAGE_SIZE);
+
+		/*
+		 * Unmap the kernel mapping.  uvm_unmap releases the
+		 * reference to the uvm object, and this should be the
+		 * last virtual mapping of the uvm object, so no need
+		 * to explicitly release (`detach') the object.
+		 */
+		uvm_unmap(kernel_map, vstart, vsize);
+		r->uobj = NULL;		/* paranoia */
+	}
+
 	r->s.start = NULL;
 }
 
@@ -1646,17 +1690,18 @@ audioclose(struct file *fp)
 
 static int
 audioread(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
-	  int ioflag)
+	  int flags)
 {
 	struct audio_softc *sc;
 	struct virtual_channel *vc;
-	int error;
+	int error, ioflag;
 	dev_t dev;
 
 	if (fp->f_audioctx == NULL)
 		return EIO;
 
 	dev = fp->f_audioctx->dev;
+	ioflag = 0;
 
 	if ((error = audio_enter(dev, RW_READER, &sc)) != 0)
 		return error;
@@ -1685,17 +1730,18 @@ audioread(struct file *fp, off_t *offp, 
 
 static int
 audiowrite(struct file *fp, off_t *offp, struct uio *uio, kauth_cred_t cred,
-	   int ioflag)
+	   int flags)
 {
 	struct audio_softc *sc;
 	struct virtual_channel *vc;
-	int error;
+	int error, ioflag;
 	dev_t dev;
 
 	if (fp->f_audioctx == NULL)
 		return EIO;
 
 	dev = fp->f_audioctx->dev;
+	ioflag = 0;
 
 	if ((error = audio_enter(dev, RW_READER, &sc)) != 0)
 		return error;
@@ -1874,41 +1920,21 @@ audiokqfilter(struct file *fp, struct kn
 	return rv;
 }
 
-/* XXX:NS mmap is disabled. */
 static 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_softc *sc;
 	struct audio_chan *chan;
 	struct virtual_channel *vc;
 	dev_t dev;
-
-	return -1;
+	int error;
 
 	chan = fp->f_audioctx;
 	dev = chan->dev;
 	vc = chan->vc;
+	error = 0;
 
-	*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, 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.
-	 * Although not yet possible, we want to prevent memory
-	 * from being allocated or freed out from under us.
-	 */
 	if ((error = audio_enter(dev, RW_READER, &sc)) != 0)
 		return 1;
 	device_active(sc->dev, DVA_SYSTEM); /* XXXJDM */
@@ -1916,17 +1942,17 @@ audiommap(dev_t dev, off_t off, int prot
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		error = audio_mmap(sc, off, prot, vc);
+		error = audio_mmap(sc, offp, len, prot, flagsp, advicep,
+		    uobjp, maxprotp, vc);	
 		break;
 	case AUDIOCTL_DEVICE:
 	case MIXER_DEVICE:
-		error = -1;
-		break;
 	default:
-		error = -1;
+		error = ENOTSUP;
 		break;
 	}
 	audio_exit(sc);
+
 	return error;
 }
 
@@ -2446,7 +2472,8 @@ audio_close(struct audio_softc *sc, int 
 		vc->sc_pbus = false;
 	}
 	if (sc->sc_opens == 1) {
-		audio_drain(sc, SIMPLEQ_FIRST(&sc->sc_audiochan));
+		if (vc->sc_mpr.mmapped == false)
+			audio_drain(sc, SIMPLEQ_FIRST(&sc->sc_audiochan));
 		if (hw->drain)
 			(void)hw->drain(sc->hw_hdl);
 		hw->halt_output(sc->hw_hdl);
@@ -3364,13 +3391,12 @@ audio_kqfilter(struct audio_chan *chan, 
 	return 0;
 }
 
-/* XXX:NS mmap to be fixed. */
-paddr_t
-audio_mmap(struct audio_softc *sc, off_t off, int prot,
-	   struct virtual_channel *vc)
+int
+audio_mmap(struct audio_softc *sc, off_t *offp, size_t len, int prot,
+    int *flagsp, int *advicep, struct uvm_object **uobjp, int *maxprotp,
+    struct virtual_channel *vc)
 {
 	struct audio_ringbuffer *cb;
-	paddr_t rv;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
@@ -3379,7 +3405,14 @@ audio_mmap(struct audio_softc *sc, off_t
 
 	DPRINTF(("audio_mmap: off=%lld, prot=%d\n", (long long)off, prot));
 	if (!(audio_get_props(sc) & AUDIO_PROP_MMAP))
-		return -1;
+		return ENOTSUP;
+
+	if (*offp < 0)
+		return EINVAL;
+	if ((off_t)(*offp + len) < *offp) {
+		/* no offset wrapping */
+		return EOVERFLOW;
+	}
 #if 0
 /* XXX
  * The idea here was to use the protection to determine if
@@ -3399,31 +3432,38 @@ audio_mmap(struct audio_softc *sc, off_t
 	else if (prot == VM_PROT_READ)
 		cb = &vc->sc_mrr;
 	else
-		return -1;
+		return EINVAL;
 #else
 	cb = &vc->sc_mpr;
 #endif
 
-	if ((u_int)off >= cb->s.bufsize)
-		return -1;
+	if ((u_int)*offp >= cb->s.bufsize)
+		return EOVERFLOW;
+
 	if (!cb->mmapped) {
 		cb->mmapped = true;
-		if (cb != &sc->sc_rr) {
+		if (cb == &vc->sc_mpr) {
 			audio_fill_silence(&cb->s.param, cb->s.start,
 					   cb->s.bufsize);
 			vc->sc_pustream = &cb->s;
 			if (!vc->sc_pbus && !vc->sc_mpr.pause)
 				(void)audiostartp(sc, vc);
-		} else {
+		} else if (cb == &vc->sc_mrr) {
 			vc->sc_rustream = &cb->s;
 			if (!vc->sc_rbus && !sc->sc_rr.pause)
 				(void)audiostartr(sc, vc);
 		}
 	}
 
-	rv = (paddr_t)(uintptr_t)(cb->s.start + off);
+	/* get ringbuffer */
+	*uobjp = cb->uobj;
 
-	return rv;
+	/* Acquire a reference for the mmap.  munmap will release.*/
+	uao_reference(*uobjp);
+	*maxprotp = prot;
+	*advicep = UVM_ADV_NORMAL;
+	*flagsp = MAP_SHARED;
+	return 0;
 }
 
 int
@@ -3693,6 +3733,8 @@ audio_mix(void *v)
 				     cb->s.outp, blksize, cb->s.inp));
 			mutex_enter(sc->sc_intr_lock);
 			mix_func(sc, cb, vc);
+			cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp,
+			    blksize);
 			mutex_exit(sc->sc_intr_lock);
 			continue;
 		}
@@ -3811,21 +3853,22 @@ audio_mix(void *v)
 	cc = blksize - (inp - cb->s.start) % blksize;
 	if (sc->sc_writeme == false) {
 		DPRINTFN(3, ("MIX RING EMPTY - INSERT SILENCE\n"));
+		mutex_exit(sc->sc_intr_lock);
 		audio_fill_silence(&vc->sc_mpr.s.param, inp, cc);
+		mutex_enter(sc->sc_intr_lock);
 		sc->sc_pr.drops += cc;
 	} else
 		cc = blksize;
 	cb->s.inp = audio_stream_add_inp(&cb->s, cb->s.inp, cc);
 	cc = blksize;
 	cc1 = sc->sc_pr.s.end - sc->sc_pr.s.inp;
+	mutex_exit(sc->sc_intr_lock);
 	if (cc1 < cc) {
-		memset(sc->sc_pr.s.inp, 0, cc1);
+		audio_fill_silence(&vc->sc_mpr.s.param, sc->sc_pr.s.inp, cc1);
 		cc -= cc1;
-		memset(sc->sc_pr.s.start, 0, cc);
+		audio_fill_silence(&vc->sc_mpr.s.param, sc->sc_pr.s.start, cc);
 	} else
-		memset(sc->sc_pr.s.inp, 0, cc);
-
-	mutex_exit(sc->sc_intr_lock);
+		audio_fill_silence(&vc->sc_mpr.s.param, sc->sc_pr.s.inp, cc);
 
 	kpreempt_disable();
 	if (sc->schedule_wih == true)
@@ -3909,14 +3952,12 @@ audio_upmix(void *v)
 		cc = blksize;
 		if (cb->s.inp + blksize > cb->s.end)
 			cc = cb->s.end - cb->s.inp;
-		mutex_enter(sc->sc_intr_lock);
 		memcpy(cb->s.inp, sc->sc_rr.s.start, cc);
 		if (cc < blksize && cc != 0) {
 			cc1 = cc;
 			cc = blksize - cc;
 			memcpy(cb->s.start, sc->sc_rr.s.start + cc1, cc);
 		}
-		mutex_exit(sc->sc_intr_lock);
 
 		cc = blksize;
 		recswvol_func(sc, cb, blksize, vc);
@@ -5383,6 +5424,8 @@ audio_get_props(struct audio_softc *sc)
 	if ((props & (AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE)) == 0)
 		props |= (AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE);
 
+	props |= AUDIO_PROP_MMAP;
+
 	return props;
 }
 

Index: src/sys/dev/audiovar.h
diff -u src/sys/dev/audiovar.h:1.54 src/sys/dev/audiovar.h:1.55
--- src/sys/dev/audiovar.h:1.54	Sat May  6 00:13:25 2017
+++ src/sys/dev/audiovar.h	Sun May  7 08:19:39 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: audiovar.h,v 1.54 2017/05/06 00:13:25 nat Exp $	*/
+/*	$NetBSD: audiovar.h,v 1.55 2017/05/07 08:19:39 nat Exp $	*/
 
 /*-
  * Copyright (c) 2002 The NetBSD Foundation, Inc.
@@ -94,6 +94,7 @@ int audiobellioctl(struct file *, u_long
 #define AUMINBLK	32
 #define AUMINNOBLK	3
 struct audio_ringbuffer {
+	struct uvm_object	*uobj;
 	audio_stream_t s;
 	int	blksize;	/* I/O block size (bytes) */
 	int	maxblks;	/* no of blocks in ring */

Reply via email to