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 */