Date: Wed, 16 Aug 2006 01:36:25 GMT From: Ryan Beasley <ryanb@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 104169 for review Message-ID: <200608160136.k7G1aPtI094886@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=104169 Change 104169 by ryanb@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) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200608160136.k7G1aPtI094886>