Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 May 2011 07:44:41 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r221803 - head/sys/dev/sound/pcm
Message-ID:  <201105120744.p4C7ifiM025825@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Thu May 12 07:44:41 2011
New Revision: 221803
URL: http://svn.freebsd.org/changeset/base/221803

Log:
  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

Modified:
  head/sys/dev/sound/pcm/dsp.c

Modified: head/sys/dev/sound/pcm/dsp.c
==============================================================================
--- head/sys/dev/sound/pcm/dsp.c	Thu May 12 03:37:03 2011	(r221802)
+++ head/sys/dev/sound/pcm/dsp.c	Thu May 12 07:44:41 2011	(r221803)
@@ -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_
 #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);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201105120744.p4C7ifiM025825>