PERFORCE change 104169 for review

Ryan Beasley ryanb at FreeBSD.org
Wed Aug 16 01:36:27 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=104169

Change 104169 by ryanb at ryanb_yuki on 2006/08/16 01:35:27

	Integrate from user branch.

Affected files ...

.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/buffer.c#3 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/buffer.h#3 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel.c#2 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel.h#2 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel_if.m#2 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/dsp.c#11 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/dsp.h#4 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/mixer.c#7 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/mixer.h#6 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/sound.c#11 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/sound.h#5 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/sys/soundcard.h#7 integrate

Differences ...

==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/buffer.c#3 (text+ko) ====

@@ -117,6 +117,11 @@
 		free(b->tmpbuf, M_DEVBUF);
 	b->tmpbuf = NULL;
 
+	if (b->shadbuf)
+		free(b->shadbuf, M_DEVBUF);
+	b->shadbuf = NULL;
+	b->sl = 0;
+
 	if (b->dmamap)
 		bus_dmamap_unload(b->dmatag, b->dmamap);
 
@@ -168,6 +173,7 @@
 sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
 {
         u_int8_t *buf, *tmpbuf, *f1, *f2;
+	u_int8_t *shadbuf, *f3;
         unsigned int bufsize;
 	int ret;
 
@@ -189,6 +195,15 @@
 		ret = ENOMEM;
 		goto out;
 	}
+
+	shadbuf = malloc(bufsize, M_DEVBUF, M_WAITOK);
+	if (shadbuf == NULL) {
+		free(buf, M_DEVBUF);
+		free(tmpbuf, M_DEVBUF);
+		ret = ENOMEM;
+		goto out;
+	}
+
 	chn_lock(b->channel);
 
 	b->blkcnt = blkcnt;
@@ -199,6 +214,9 @@
 	f2 = b->tmpbuf;
 	b->buf = buf;
 	b->tmpbuf = tmpbuf;
+	f3 = b->shadbuf;
+	b->shadbuf = shadbuf;
+	b->sl = bufsize;
 
 	sndbuf_reset(b);
 
@@ -207,6 +225,8 @@
 		free(f1, M_DEVBUF);
       	if (f2)
 		free(f2, M_DEVBUF);
+	if (f3)
+		free(f3, M_DEVBUF);
 
 	ret = 0;
 out:
@@ -214,6 +234,15 @@
 	return ret;
 }
 
+/**
+ * @brief Zero out space in buffer free area
+ *
+ * This function clears a chunk of @c length bytes in the buffer free area
+ * (i.e., where the next write will be placed).
+ *
+ * @param b		buffer context
+ * @param length	number of bytes to blank
+ */
 void
 sndbuf_clear(struct snd_dbuf *b, unsigned int length)
 {
@@ -241,6 +270,11 @@
 	}
 }
 
+/**
+ * @brief Zap buffer contents, resetting "ready area" fields
+ *
+ * @param b	buffer context
+ */
 void
 sndbuf_fillsilence(struct snd_dbuf *b)
 {
@@ -260,7 +294,24 @@
 	b->rl = b->bufsize;
 }
 
+/**
+ * @brief Reset buffer w/o flushing statistics
+ *
+ * This function just zeroes out buffer contents and sets the "ready length"
+ * to zero.  This was originally to facilitate minimal playback interruption
+ * (i.e., dropped samples) in SNDCTL_DSP_SILENCE/SKIP ioctls.
+ *
+ * @param b	buffer context
+ */
 void
+sndbuf_softreset(struct snd_dbuf *b)
+{
+	b->rl = 0;
+	if (b->buf && b->bufsize > 0)
+		sndbuf_clear(b, b->bufsize);
+}
+
+void
 sndbuf_reset(struct snd_dbuf *b)
 {
 	b->hp = 0;
@@ -272,6 +323,7 @@
 	b->xrun = 0;
 	if (b->buf && b->bufsize > 0)
 		sndbuf_clear(b, b->bufsize);
+	sndbuf_clearshadow(b);
 }
 
 u_int32_t
@@ -493,6 +545,19 @@
 
 /************************************************************/
 
+/**
+ * @brief Acquire buffer space to extend ready area
+ *
+ * This function extends the ready area length by @c count bytes, and may
+ * optionally copy samples from another location stored in @c from.  The
+ * counter @c snd_dbuf::total is also incremented by @c count bytes.
+ *
+ * @param b	audio buffer
+ * @param from	sample source (optional)
+ * @param count	number of bytes to acquire
+ *
+ * @retval 0	Unconditional
+ */
 int
 sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count)
 {
@@ -516,6 +581,20 @@
 	return 0;
 }
 
+/**
+ * @brief Dispose samples from channel buffer, increasing size of ready area
+ *
+ * This function discards samples from the supplied buffer by advancing the
+ * ready area start pointer and decrementing the ready area length.  If 
+ * @c to is not NULL, then the discard samples will be copied to the location
+ * it points to.
+ *
+ * @param b	PCM channel sound buffer
+ * @param to	destination buffer (optional)
+ * @param count	number of bytes to discard
+ *
+ * @returns 0 unconditionally
+ */
 int
 sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
 {
@@ -592,3 +671,49 @@
 		b->flags |= flags;
 }
 
+/**
+ * @brief Clear the shadow buffer by filling with samples equal to zero.
+ *
+ * @param b buffer to clear
+ */
+void
+sndbuf_clearshadow(struct snd_dbuf *b)
+{
+	KASSERT(b != NULL, ("b is a null pointer"));
+	KASSERT(b->sl >= 0, ("illegal shadow length"));
+
+	if ((b->shadbuf != NULL) && (b->sl > 0)) {
+		if (b->fmt & AFMT_SIGNED)
+			memset(b->shadbuf, 0x00, b->sl);
+		else
+			memset(b->shadbuf, 0x80, b->sl);
+	}
+}
+
+#ifdef OSSV4_EXPERIMENT
+/**
+ * @brief Return peak value from samples in buffer ready area.
+ *
+ * Peak ranges from 0-32767.  If channel is monaural, most significant 16
+ * bits will be zero.  For now, only expects to work with 1-2 channel
+ * buffers.
+ *
+ * @note  Currently only operates with linear PCM formats.
+ *
+ * @param b buffer to analyze
+ * @param lpeak pointer to store left peak value
+ * @param rpeak pointer to store right peak value
+ */
+void
+sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp)
+{
+	u_int32_t lpeak, rpeak;
+
+	lpeak = 0;
+	rpeak = 0;
+
+	/**
+	 * @todo fill this in later
+	 */
+}
+#endif

==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/buffer.h#3 (text+ko) ====

@@ -38,6 +38,8 @@
 struct snd_dbuf {
 	device_t dev;
 	u_int8_t *buf, *tmpbuf;
+	u_int8_t *shadbuf; /**< shadow buffer used w/ S_D_SILENCE/SKIP */
+	volatile int sl; /**< shadbuf ready length in # of bytes */
 	unsigned int bufsize, maxsize;
 	volatile int dl; /* transfer size */
 	volatile int rp; /* pointers to the ready area */
@@ -70,6 +72,8 @@
 void sndbuf_reset(struct snd_dbuf *b);
 void sndbuf_clear(struct snd_dbuf *b, unsigned int length);
 void sndbuf_fillsilence(struct snd_dbuf *b);
+void sndbuf_softreset(struct snd_dbuf *b);
+void sndbuf_clearshadow(struct snd_dbuf *b);
 
 u_int32_t sndbuf_getfmt(struct snd_dbuf *b);
 int sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt);
@@ -117,3 +121,7 @@
 void sndbuf_dma(struct snd_dbuf *b, int go);
 int sndbuf_dmaptr(struct snd_dbuf *b);
 void sndbuf_dmabounce(struct snd_dbuf *b);
+
+#ifdef OSSV4_EXPERIMENT
+void sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp);
+#endif

==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel.c#2 (text+ko) ====

@@ -68,6 +68,18 @@
 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
 	&report_soft_formats, 1, "report software-emulated formats");
 
+/**
+ * @brief Channel sync group lock
+ *
+ * Clients should acquire this lock @b without holding any channel locks
+ * before touching syncgroups or the main syncgroup list.
+ *
+ * @todo rename?
+ */
+struct mtx snd_pcm_syncgroups_mtx;
+MTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock", MTX_DEF);
+struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(head);
+
 static int chn_buildfeeder(struct pcm_channel *c);
 
 static void
@@ -87,14 +99,23 @@
 		c->lock = snd_mtxcreate(c->name, "pcm fake channel");
 		break;
 	}
+
+	cv_init(&c->cv, c->name);
 }
 
 static void
 chn_lockdestroy(struct pcm_channel *c)
 {
 	snd_mtxfree(c->lock);
+	cv_destroy(&c->cv);
 }
 
+/**
+ * @brief Determine channel is ready for I/O
+ *
+ * @retval 1 = ready for I/O
+ * @retval 0 = not ready for I/O
+ */
 static int
 chn_polltrigger(struct pcm_channel *c)
 {
@@ -112,8 +133,8 @@
 #if 0
 		lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
 #endif
-		lim = 1;
-		return (amt >= lim)? 1 : 0;
+		lim = c->lw;
+		return (amt >= lim) ? 1 : 0;
 	}
 	return 0;
 }
@@ -310,12 +331,25 @@
 
 	ret = 0;
 	count = hz;
+
 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
 		sz = sndbuf_getfree(bs);
 		if (sz == 0) {
 			if (c->flags & CHN_F_NBIO)
 				ret = EWOULDBLOCK;
-			else {
+			else if (c->flags & CHN_F_NOTRIGGER) {
+				/**
+				 * @todo Evaluate whether EAGAIN is truly desirable.
+				 * 	 4Front drivers behave like this, but I'm
+				 * 	 not sure if it at all violates the "write
+				 * 	 should be allowed to block" model.
+				 *
+				 * 	 The idea is that, while set with CHN_F_NOTRIGGER,
+				 * 	 a channel isn't playing, *but* without this we
+				 * 	 end up with "interrupt timeout / channel dead".
+				 */
+				ret = EAGAIN;
+			} else {
 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
 				if (timeout < 1)
 					timeout = 1;
@@ -783,6 +817,7 @@
 		chn_resetbuf(c);
 		r = CHANNEL_RESETDONE(c->methods, c->devinfo);
 	}
+	chn_syncdestroy(c);
 	return r;
 }
 
@@ -830,6 +865,17 @@
 	c->flags = 0;
 	c->feederflags = 0;
 
+	/*
+	 * OSSv4 docs: "By default OSS will set the low water level equal
+	 * to the fragment size which is optimal in most cases."
+	 *
+	 * @todo So what happens if user requests a new block size?  Should
+	 * 	 this value be updated?  (Will mail 4Front with a list of
+	 * 	 questions as project continues.)
+	 */
+	c->lw = sndbuf_getblksz(bs);
+	c->sm = NULL;
+
 	ret = ENODEV;
 	CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
 	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
@@ -853,6 +899,19 @@
 	if (ret)
 		goto out;
 
+	/**
+	 * @todo Should this be moved somewhere else?  The primary buffer
+	 * 	 is allocated by the driver or via DMA map setup, and tmpbuf
+	 * 	 seems to only come into existence in sndbuf_resize().
+	 */
+	if (c->direction == PCMDIR_PLAY) {
+		bs->sl = sndbuf_getmaxsize(bs);
+		bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT);
+		if (bs->shadbuf == NULL) {
+			ret = ENOMEM;
+			goto out;
+		}
+	}
 
 out:
 	CHN_UNLOCK(c);
@@ -888,6 +947,7 @@
 	c->flags |= CHN_F_DEAD;
 	sndbuf_destroy(bs);
 	sndbuf_destroy(b);
+	chn_syncdestroy(c);
 	chn_lockdestroy(c);
 	return 0;
 }
@@ -1243,6 +1303,17 @@
 	return ret;
 }
 
+/**
+ * @brief Queries sound driver for sample-aligned hardware buffer pointer index
+ *
+ * This function obtains the hardware pointer location, then aligns it to
+ * the current bytes-per-sample value before returning.  (E.g., a channel
+ * running in 16 bit stereo mode would require 4 bytes per sample, so a
+ * hwptr value ranging from 32-35 would be returned as 32.)
+ *
+ * @param c	PCM channel context	
+ * @returns 	sample-aligned hardware buffer pointer index
+ */
 int
 chn_getptr(struct pcm_channel *c)
 {
@@ -1501,6 +1572,60 @@
 	return 0;
 }
 
+/**
+ * @brief Fetch array of supported discrete sample rates
+ *
+ * Wrapper for CHANNEL_GETRATES.  Please see channel_if.m:getrates() for
+ * detailed information.
+ *
+ * @note If the operation isn't supported, this function will just return 0
+ *       (no rates in the array), and *rates will be set to NULL.  Callers
+ *       should examine rates @b only if this function returns non-zero.
+ *
+ * @param c	pcm channel to examine
+ * @param rates	pointer to array of integers; rate table will be recorded here
+ *
+ * @return number of rates in the array pointed to be @c rates
+ */
+int
+chn_getrates(struct pcm_channel *c, int **rates)
+{
+	KASSERT(rates != NULL, ("rates is null"));
+	CHN_LOCKASSERT(c);
+	return CHANNEL_GETRATES(c->methods, c->devinfo, rates);
+}
+
+/**
+ * @brief Remove channel from a sync group, if there is one.
+ *
+ * This is meant to be called before destroying the channel.
+ *
+ * @param c channel facing imminent destruction
+ */
+void
+chn_syncdestroy(struct pcm_channel *c)
+{
+	struct pcmchan_syncmember *sm;
+	struct pcmchan_syncgroup *sg;
+
+	if (c->sm == NULL)
+		return;
+
+	sm = c->sm;
+	sg = sm->parent;
+	c->sm = NULL;
+
+	KASSERT(sg != NULL, ("syncmember has null parent"));
+
+	SLIST_REMOVE(&sg->members, sm, pcmchan_syncmember, link);
+	free(sm, M_DEVBUF);
+
+	if (SLIST_EMPTY(&sg->members)) {
+		SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
+		free(sg, M_DEVBUF);
+	}
+}
+
 void
 chn_lock(struct pcm_channel *c)
 {
@@ -1512,3 +1637,12 @@
 {
 	CHN_UNLOCK(c);
 }
+
+#ifdef OSSV4_EXPERIMENT
+int
+chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak)
+{
+	CHN_LOCKASSERT(c);
+	return CHANNEL_GETPEAKS(c->methods, c->devinfo, lpeak, rpeak);
+}
+#endif

==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel.h#2 (text+ko) ====

@@ -37,6 +37,36 @@
 	u_int32_t caps;
 };
 
+/* Forward declarations */
+struct pcm_channel;
+struct pcmchan_syncgroup;
+struct pcmchan_syncmember;
+
+extern struct mtx snd_pcm_syncgroups_mtx;
+extern SLIST_HEAD(pcm_synclist, pcmchan_syncgroup) snd_pcm_syncgroups;
+
+#define PCM_SG_LOCK()	mtx_lock(&snd_pcm_syncgroups_mtx)
+#define PCM_SG_TRYLOCK() mtx_trylock(&snd_pcm_syncgroups_mtx)
+#define PCM_SG_UNLOCK()	mtx_unlock(&snd_pcm_syncgroups_mtx)
+
+/**
+ * @brief Specifies an audio device sync group
+ */
+struct pcmchan_syncgroup {
+	SLIST_ENTRY(pcmchan_syncgroup) link;
+	SLIST_HEAD(, pcmchan_syncmember) members;
+	int id; /**< Group identifier; set to address of group. */
+};
+
+/**
+ * @brief Specifies a container for members of a sync group
+ */
+struct pcmchan_syncmember {
+	SLIST_ENTRY(pcmchan_syncmember) link;
+	struct pcmchan_syncgroup *parent; /**< group head */
+	struct pcm_channel *ch;
+};
+
 #define	CHN_NAMELEN	32
 struct pcm_channel {
 	kobj_t methods;
@@ -63,6 +93,26 @@
 	device_t dev;
 	char name[CHN_NAMELEN];
 	struct mtx *lock;
+	/**
+	 * Increment,decrement this around operations that temporarily yield
+	 * lock.
+	 */
+	unsigned int inprog;
+	/**
+	 * Special channel operations should examine @c inprog after acquiring
+	 * lock.  If zero, operations may continue.  Else, thread should
+	 * wait on this cv for previous operation to finish.
+	 */
+	struct cv cv;
+	unsigned int lw; /**< select()/poll() low water mark */
+	/**
+	 * If part of a sync group, this will point to the syncmember
+	 * container.
+	 */
+	struct pcmchan_syncmember *sm;
+#ifdef OSSV4_EXPERIMENT
+	u_int16_t lpeak, rpeak;	/**< Peak value from 0-32767. */
+#endif
 	SLIST_HEAD(, pcmchan_children) children;
 };
 
@@ -102,13 +152,22 @@
 void chn_lock(struct pcm_channel *c);
 void chn_unlock(struct pcm_channel *c);
 
+int chn_getrates(struct pcm_channel *c, int **rates);
+void chn_syncdestroy(struct pcm_channel *c);
+
+#ifdef OSSV4_EXPERIMENT
+int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
+#endif
+
 #ifdef	USING_MUTEX
 #define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock))
 #define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock))
+#define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock))
 #define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED)
 #else
 #define CHN_LOCK(c)
 #define CHN_UNLOCK(c)
+#define CHN_TRYLOCK(c)
 #define CHN_LOCKASSERT(c)
 #endif
 

==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel_if.m#2 (text+ko) ====

@@ -70,6 +70,19 @@
 		return 0;
 	}
 
+	static int
+	channel_nogetpeaks(kobj_t obj, void *data, int *lpeak, int *rpeak)
+	{
+		return -1;
+	}
+
+	static int
+	channel_nogetrates(kobj_t obj, void *data, int **rates)
+	{
+		*rates = NULL;
+		return 0;
+	}
+
 };
 
 METHOD void* init {
@@ -140,3 +153,55 @@
 	void *data;
 	u_int32_t changed;
 } DEFAULT channel_nonotify;
+
+/**
+ * @brief Retrieve channel peak values
+ *
+ * This function is intended to obtain peak volume values for samples
+ * played/recorded on a channel.  Values are on a linear scale from 0 to
+ * 32767.  If the channel is monaural, a single value should be recorded
+ * in @c lpeak.
+ *
+ * If hardware support isn't available, the SNDCTL_DSP_GET[IO]PEAKS
+ * operation should return EINVAL.  However, we may opt to provide
+ * software support that the user may toggle via sysctl/mixext.
+ *
+ * @param obj	standard kobj object (usually @c channel->methods)
+ * @param data	driver-specific data (usually @c channel->devinfo)
+ * @param lpeak	pointer to store left peak level
+ * @param rpeak	pointer to store right peak level
+ *
+ * @retval -1	Error; usually operation isn't supported.
+ * @retval 0	success
+ */
+METHOD int getpeaks {
+	kobj_t obj;
+	void *data;
+	int *lpeak;
+	int *rpeak;
+} DEFAULT channel_nogetpeaks;
+
+/**
+ * @brief Retrieve discrete supported sample rates
+ *
+ * Some cards operate at fixed rates, and this call is intended to retrieve
+ * those rates primarily for when in-kernel rate adjustment is undesirable
+ * (e.g., application wants direct DMA access after setting a channel to run
+ * "uncooked").
+ *
+ * @todo Once 4Front docs for SNDCTL_AUDIOINFO are back, double check to
+ * 	 make sure that such tables are truly static.  I'm wondering if it's
+ * 	 possible for a driver's supported rates to change depending on the
+ * 	 card's operating mode.
+ *
+ * @param obj	standard kobj object (usually @c channel->methods)
+ * @param data	driver-specific data (usually @c channel->devinfo)
+ * @param rates	if successful, will point to static array of supported rates
+ *
+ * @return Number of rates in the array
+ */
+METHOD int getrates {
+	kobj_t obj;
+	void *data;
+	int **rates;
+} DEFAULT channel_nogetrates;

==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/dsp.c#11 (text+ko) ====

@@ -58,6 +58,20 @@
 static eventhandler_tag dsp_ehtag;
 #endif
 
+static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group);
+static int dsp_oss_syncstart(int sg_id);
+static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy);
+#ifdef OSSV4_EXPERIMENT
+static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled);
+static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
+static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map);
+static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
+static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label);
+static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
+static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song);
+static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
+#endif
+
 static struct snddev_info *
 dsp_get_info(struct cdev *dev)
 {
@@ -394,7 +408,17 @@
 	}
 	if (!(wrch->flags & CHN_F_RUNNING))
 		wrch->flags |= CHN_F_RUNNING;
+
+	/*
+	 * Chn_write() must give up channel lock in order to copy bytes from
+	 * userland, so up the "in progress" counter to make sure someone
+	 * else doesn't come along and muss up the buffer.
+	 */
+	++wrch->inprog;
 	ret = chn_write(wrch, buf);
+	--wrch->inprog;
+	cv_signal(&wrch->cv);
+
 	relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
 
 	return ret;
@@ -407,12 +431,9 @@
 	struct snddev_info *d;
 	int kill;
     	int ret = 0, *arg_i = (int *)arg, tmp;
-#ifdef OSSV4_EXPERIMENT
-	oss_mixer_enuminfo *ei;
 	int xcmd;
 
 	xcmd = 0;
-#endif
 
 	/*
 	 * this is an evil hack to allow broken apps to perform mixer ioctls
@@ -1038,7 +1059,6 @@
 			dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
 		break;
 
-#ifdef	OSSV4_EXPERIMENT
 	case SNDCTL_SYSINFO:
 		sound_oss_sysinfo((oss_sysinfo *)arg);
 		break;
@@ -1091,18 +1111,22 @@
 	 * the wrch bound to this cdev).
 	 */
 	case SNDCTL_DSP_GET_PLAYTGT_NAMES:
-		ei = (oss_mixer_enuminfo *)arg;
-		ei->dev = 0;
-		ei->ctrl = 0;
-		ei->version = 0; /* static for now */
-		ei->strindex[0] = 0;
+		{
+			oss_mixer_enuminfo *ei;
+			ei = (oss_mixer_enuminfo *)arg;
+			ei->dev = 0;
+			ei->ctrl = 0;
+			ei->version = 0; /* static for now */
+			ei->strindex[0] = 0;
 
-		if (wrch != NULL) {
-			ei->nvalues = 1;
-			strlcpy(ei->strings, wrch->name, sizeof(ei->strings));
-		} else {
-			ei->nvalues = 0;
-			ei->strings[0] = '\0';
+			if (wrch != NULL) {
+				ei->nvalues = 1;
+				strlcpy(ei->strings, wrch->name,
+					sizeof(ei->strings));
+			} else {
+				ei->nvalues = 0;
+				ei->strings[0] = '\0';
+			}
 		}
 		break;
 	case SNDCTL_DSP_GET_PLAYTGT:
@@ -1119,6 +1143,235 @@
 			ret = EINVAL;
 		break;
 
+	case SNDCTL_DSP_SILENCE:
+	/*
+	 * Flush the software (pre-feed) buffer, but try to minimize playback
+	 * interruption.  (I.e., record unplayed samples with intent to
+	 * restore by SNDCTL_DSP_SKIP.) Intended for application "pause"
+	 * functionality.
+	 */
+		if (wrch == NULL)
+			ret = EINVAL;
+		else {
+			struct snd_dbuf *bs;
+			CHN_LOCK(wrch);
+			while (wrch->inprog != 0)
+				cv_wait(&wrch->cv, wrch->lock);
+			bs = wrch->bufsoft;
+			if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) {
+				bs->sl = sndbuf_getready(bs);
+				sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs));
+				sndbuf_fillsilence(bs);
+				chn_start(wrch, 0);
+			}
+			CHN_UNLOCK(wrch);
+		}
+		break;
+
+	case SNDCTL_DSP_SKIP:
+	/*
+	 * OSSv4 docs: "This ioctl call discards all unplayed samples in the
+	 * playback buffer by moving the current write position immediately
+	 * before the point where the device is currently reading the samples."
+	 */
+		if (wrch == NULL)
+			ret = EINVAL;
+		else {
+			struct snd_dbuf *bs;
+			CHN_LOCK(wrch);
+			while (wrch->inprog != 0)
+				cv_wait(&wrch->cv, wrch->lock);
+			bs = wrch->bufsoft;
+			if ((bs->shadbuf != NULL) && (bs->sl > 0)) {
+				sndbuf_softreset(bs);
+				sndbuf_acquire(bs, bs->shadbuf, bs->sl);
+				bs->sl = 0;
+				chn_start(wrch, 0);
+			}
+			CHN_UNLOCK(wrch);
+		}
+		break;
+
+	case SNDCTL_DSP_CURRENT_OPTR:
+	case SNDCTL_DSP_CURRENT_IPTR:
+	/**
+	 * @note Changing formats resets the buffer counters, which differs
+	 * 	 from the 4Front drivers.  However, I don't expect this to be
+	 * 	 much of a problem.
+	 *
+	 * @todo In a test where this ioctl is called immediately after write
+	 * 	 returns, this driver is about 32K samples behind whereas
+	 * 	 4Front's is about 8K samples behind.  Must determine source
+	 * 	 of discrepancy.
+	 *
+	 * @todo Actually test SNDCTL_DSP_CURRENT_IPTR.
+	 */
+		chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch;
+		if (chn == NULL) 
+			ret = EINVAL;
+		else {
+			struct snd_dbuf *bs;
+			/* int tmp; */
+
+			oss_count_t *oc = (oss_count_t *)arg;
+
+			CHN_LOCK(chn);
+			bs = chn->bufsoft;
+#if 0
+			tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b);
+			oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getbps(b);
+			oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getbps(b);
+#else
+			oc->samples = sndbuf_gettotal(bs) / sndbuf_getbps(bs);
+			oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getbps(bs);
+#endif
+			CHN_UNLOCK(chn);
+		}
+		break;
+
+	case SNDCTL_DSP_HALT_OUTPUT:
+	case SNDCTL_DSP_HALT_INPUT:
+		chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch;
+		if (chn == NULL)
+			ret = EINVAL;
+		else {
+			CHN_LOCK(chn);
+			chn_abort(chn);
+			CHN_UNLOCK(chn);
+		}
+		break;
+
+	case SNDCTL_DSP_LOW_WATER:
+	/*
+	 * Set the number of bytes required to attract attention by
+	 * select/poll.
+	 */
+		if (wrch != NULL) {
+			CHN_LOCK(wrch);
+			wrch->lw = (*arg_i > 1) ? *arg_i : 1;
+			CHN_UNLOCK(wrch);
+		}
+		if (rdch != NULL) {
+			CHN_LOCK(rdch);
+			rdch->lw = (*arg_i > 1) ? *arg_i : 1;
+			CHN_UNLOCK(rdch);
+		}
+		break;
+
+	case SNDCTL_DSP_GETERROR:
+	/*
+	 * OSSv4 docs:  "All errors and counters will automatically be
+	 * cleared to zeroes after the call so each call will return only
+	 * the errors that occurred after the previous invocation. ... The
+	 * play_underruns and rec_overrun fields are the only usefull fields
+	 * returned by OSS 4.0."
+	 */
+		{
+			audio_errinfo *ei = (audio_errinfo *)arg;
+
+			bzero((void *)ei, sizeof(*ei));
+
+			if (wrch != NULL) {
+				CHN_LOCK(wrch);
+				ei->play_underruns = wrch->xruns;
+				wrch->xruns = 0;
+				CHN_UNLOCK(wrch);
+			}
+			if (rdch != NULL) {
+				CHN_LOCK(rdch);
+				ei->rec_overruns = rdch->xruns;
+				rdch->xruns = 0;
+				CHN_UNLOCK(rdch);
+			}
+		}
+		break;
+
+	case SNDCTL_DSP_SYNCGROUP:
+		ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg);
+		break;
+
+	case SNDCTL_DSP_SYNCSTART:
+		ret = dsp_oss_syncstart(*arg_i);
+		break;
+
+	case SNDCTL_DSP_POLICY:
+		ret = dsp_oss_policy(wrch, rdch, *arg_i);
+		break;
+
+#ifdef	OSSV4_EXPERIMENT
+	/*
+	 * XXX The following ioctls are not yet supported and just return
+	 * EINVAL.
+	 */
+	case SNDCTL_DSP_GETOPEAKS:
+	case SNDCTL_DSP_GETIPEAKS:
+		chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch;
+		if (chn == NULL)
+			ret = EINVAL;
+		else {
+			oss_peaks_t *op = (oss_peaks_t *)arg;
+			int lpeak, rpeak;
+
+			CHN_LOCK(chn);
+			ret = chn_getpeaks(chn, &lpeak, &rpeak);
+			if (ret == -1)
+				ret = EINVAL;
+			else {
+				(*op)[0] = lpeak;
+				(*op)[1] = rpeak;
+			}
+			CHN_UNLOCK(chn);
+		}
+		break;
+
+	case SNDCTL_DSP_COOKEDMODE:
+		ret = dsp_oss_cookedmode(wrch, rdch, *arg_i);
+		break;
+	case SNDCTL_DSP_GET_CHNORDER:
+		ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg);
+		break;
+	case SNDCTL_DSP_SET_CHNORDER:
+		ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg);
+		break;
+	case SNDCTL_GETLABEL:
+		ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg);
+		break;
+	case SNDCTL_SETLABEL:
+		ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg);
+		break;
+	case SNDCTL_GETSONG:
+		ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg);
+		break;
+	case SNDCTL_SETSONG:
+		ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg);
+		break;
+	case SNDCTL_SETNAME:
+		ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg);
+		break;
+#if 0
+	/**
+	 * @note The SNDCTL_CARDINFO ioctl was omitted per 4Front developer
+	 * documentation.  "The usability of this call is very limited. It's
+	 * provided only for completeness of the API. OSS API doesn't have
+	 * any concept of card. Any information returned by this ioctl calld
+	 * is reserved exclusively for the utility programs included in the
+	 * OSS package. Applications should not try to use for this
+	 * information in any ways."
+	 */
+	case SNDCTL_CARDINFO:
+		ret = EINVAL;
+		break;
+	/**
+	 * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and
+	 * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of
+	 * 4Front Technologies.
+	 */
+	case SNDCTL_DSP_READCTL:
+	case SNDCTL_DSP_WRITECTL:
+		ret = EINVAL;
+		break;
+#endif	/* !0 (explicitly omitted ioctls) */
+
 #endif	/* !OSSV4_EXPERIMENT */
     	case SNDCTL_DSP_MAPINBUF:
     	case SNDCTL_DSP_MAPOUTBUF:
@@ -1294,7 +1547,6 @@
 SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
 #endif
 
-#ifdef OSSV4_EXPERIMENT
 /**
  * @brief Handler for SNDCTL_AUDIOINFO.
  *
@@ -1339,7 +1591,7 @@

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list