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 <[email protected]>
@@ -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 */