summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound
diff options
context:
space:
mode:
authoravg <avg@FreeBSD.org>2011-05-12 07:44:41 +0000
committeravg <avg@FreeBSD.org>2011-05-12 07:44:41 +0000
commit4e7ece616d2d0e02278282154e76de6402c32578 (patch)
tree646c0fb8a95c4b91a6e64136fec6b12aa1fd2c21 /sys/dev/sound
parent3fe0beec0e865dcf8092b7331e21b8690f9e3567 (diff)
downloadFreeBSD-src-4e7ece616d2d0e02278282154e76de6402c32578.zip
FreeBSD-src-4e7ece616d2d0e02278282154e76de6402c32578.tar.gz
dsp/pcm: allow to mmap both read and write buffers using the same fd
This brings our implementation in line with OSS specification for systems that support mmap. The change should also improve compatibility with OSS software not specifically written for FreeBSD, e.g. PulseAudio OSS plugin. Reviewed by: kib, jhb MFC after: 1 week
Diffstat (limited to 'sys/dev/sound')
-rw-r--r--sys/dev/sound/pcm/dsp.c56
1 files changed, 35 insertions, 21 deletions
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 66d0c8c..8e93931 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -34,6 +34,11 @@
#include <sys/ctype.h>
#include <sys/sysent.h>
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
SND_DECLARE_FILE("$FreeBSD$");
static int dsp_mmap_allow_prot_exec = 0;
@@ -67,6 +72,7 @@ static d_write_t dsp_write;
static d_ioctl_t dsp_ioctl;
static d_poll_t dsp_poll;
static d_mmap_t dsp_mmap;
+static d_mmap_single_t dsp_mmap_single;
struct cdevsw dsp_cdevsw = {
.d_version = D_VERSION,
@@ -77,6 +83,7 @@ struct cdevsw dsp_cdevsw = {
.d_ioctl = dsp_ioctl,
.d_poll = dsp_poll,
.d_mmap = dsp_mmap,
+ .d_mmap_single = dsp_mmap_single,
.d_name = "dsp",
};
@@ -2187,6 +2194,16 @@ static int
dsp_mmap(struct cdev *i_dev, vm_ooffset_t offset, vm_paddr_t *paddr,
int nprot, vm_memattr_t *memattr)
{
+
+ /* XXX memattr is not honored */
+ *paddr = vtophys(offset);
+ return (0);
+}
+
+static int
+dsp_mmap_single(struct cdev *i_dev, vm_ooffset_t *offset,
+ vm_size_t size, struct vm_object **object, int nprot)
+{
struct snddev_info *d;
struct pcm_channel *wrch, *rdch, *c;
@@ -2205,51 +2222,48 @@ dsp_mmap(struct cdev *i_dev, vm_ooffset_t offset, vm_paddr_t *paddr,
#else
if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1)
#endif
- return (-1);
+ return (EINVAL);
+
+ /*
+ * PROT_READ (alone) selects the input buffer.
+ * PROT_WRITE (alone) selects the output buffer.
+ * PROT_WRITE|PROT_READ together select the output buffer.
+ */
+ if ((nprot & (PROT_READ | PROT_WRITE)) == 0)
+ return (EINVAL);
d = dsp_get_info(i_dev);
if (!DSP_REGISTERED(d, i_dev))
- return (-1);
+ return (EINVAL);
PCM_GIANT_ENTER(d);
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
- /*
- * XXX The linux api uses the nprot to select read/write buffer
- * our vm system doesn't allow this, so force write buffer.
- *
- * This is just a quack to fool full-duplex mmap, so that at
- * least playback _or_ recording works. If you really got the
- * urge to make _both_ work at the same time, avoid O_RDWR.
- * Just open each direction separately and mmap() it.
- *
- * Failure is not an option due to INVARIANTS check within
- * device_pager.c, which means, we have to give up one over
- * another.
- */
- c = (wrch != NULL) ? wrch : rdch;
-
+ c = ((nprot & PROT_WRITE) != 0) ? wrch : rdch;
if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) ||
- offset >= sndbuf_getsize(c->bufsoft) ||
+ (*offset + size) > sndbuf_getsize(c->bufsoft) ||
(wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) ||
(rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) {
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
PCM_GIANT_EXIT(d);
- return (-1);
+ return (EINVAL);
}
- /* XXX full-duplex quack. */
if (wrch != NULL)
wrch->flags |= CHN_F_MMAP;
if (rdch != NULL)
rdch->flags |= CHN_F_MMAP;
- *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
+ *offset = (vm_ooffset_t)sndbuf_getbufofs(c->bufsoft, *offset);
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
+ *object = vm_pager_allocate(OBJT_DEVICE, i_dev,
+ size, nprot, *offset, curthread->td_ucred);
PCM_GIANT_LEAVE(d);
+ if (*object == NULL)
+ return (EINVAL);
return (0);
}
OpenPOWER on IntegriCloud