audio_client.c revision 2c30fa4582c5d6c659e059e719c5f6163f7ef1e3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (C) 4Front Technologies 1996-2008.
*
*/
#include <sys/sysmacros.h>
#include "audio_impl.h"
/*
* Audio Client implementation.
*/
/*
* Attenuation table for dB->linear conversion. Indexed in steps of
* 0.5 dB. Table size is 25 dB (first entry is handled as mute).
*
* Notably, the last item in table is taken as 0 dB (i.e. maximum volume).
*
* Table contents can be calculated as follows (requires sunmath library):
*
* scale = AUDIO_VOL_SCALE;
* for (i = -50; i <= 0; i++) {
* x = exp10(0.05 * i);
* printf("%d: %f %.0f\n", i, x, trunc(x * scale));
* }
*
*/
0, 0, 1, 1, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 4, 4, 5, 5, 6, 7,
8, 9, 10, 11, 12, 14, 16, 18, 20, 22,
25, 28, 32, 36, 40, 45, 51, 57, 64, 72,
80, 90, 101, 114, 128, 143, 161, 181, 203, 228,
256
};
static list_t auimpl_clients;
static krwlock_t auimpl_client_lock;
void *
{
return (c->c_private);
}
void
{
}
int
{
int rv = 0;
/* basic sanity checks! */
return (EINVAL);
}
}
return (rv);
}
int
{
}
{
return (sp->s_fragbytes);
}
{
}
{
}
{
}
void
{
}
{
}
{
}
{
}
{
}
{
return (&c->c_istream);
}
{
return (&c->c_ostream);
}
{
return (count);
}
{
}
return (n);
}
{
do {
n -= nf;
}
} while (n);
return (cnt);
}
{
}
return (n);
}
{
do {
n -= nf;
}
} while (n);
return (cnt);
}
int
{
int rv = 0;
int eagain;
}
return (eagain);
}
return (EINTR);
}
}
eagain = 0;
if (rv != 0) {
return (rv);
}
}
}
/* round off any remaining partial bits */
return (rv);
}
int
{
int rv = 0;
int eagain;
return (eagain);
}
return (EINTR);
}
}
/*
* We have to drop the stream lock, because the
* uiomove might require doing a page in, which could
* get blocked behind the PIL of the audio processing
* thread which also grabs the s_lock. (Hence, there
* is a risk of deadlock due to priority inversion.)
*/
eagain = 0;
if (rv != 0) {
return (rv);
}
}
}
}
/* round off any remaining partial bits */
return (rv);
}
int
{
short nev = 0;
}
}
}
}
if (nev) {
} else {
*reventsp = 0;
if (!anyyet) {
*phpp = &c->c_pollhead;
}
}
return (0);
}
void
{
}
void
{
if (e == NULL) {
/* if no output engine, can't do it! */
*slen = 0;
*flen = 0;
return;
}
mutex_enter(&e->e_lock);
} else {
}
mutex_exit(&e->e_lock);
/* engine frames converted to stream rate, plus stream frames */
}
int
{
int rv = 0;
/*
* AC3: If we select an AC3 format, then we have to allocate
* another engine. Normally this will be an output only
* engine. However, for now we aren't supporting AC3
* passthru.
*/
switch (fmt) {
case AUDIO_FORMAT_U8:
case AUDIO_FORMAT_ULAW:
case AUDIO_FORMAT_ALAW:
case AUDIO_FORMAT_S8:
case AUDIO_FORMAT_S16_LE:
case AUDIO_FORMAT_S16_BE:
case AUDIO_FORMAT_U16_LE:
case AUDIO_FORMAT_U16_BE:
case AUDIO_FORMAT_S24_LE:
case AUDIO_FORMAT_S24_BE:
case AUDIO_FORMAT_S32_LE:
case AUDIO_FORMAT_S32_BE:
case AUDIO_FORMAT_S24_PACKED:
break;
case AUDIO_FORMAT_AC3: /* AC3: PASSTHRU */
default:
return (ENOTSUP);
}
/*
* Optimization. Some personalities send us the same format
* over and over again. (Sun personality does this
* repeatedly.) setup_src is potentially expensive, so we
* avoid doing it unless we really need to.
*/
/*
* Note that setting the format doesn't check that the
* audio streams have been paused. As a result, any
* data still playing or recording will probably get
* misinterpreted. It would be smart if the client
* formats.
*/
}
return (rv);
}
int
{
}
int
{
}
int
{
}
int
{
int rv = 0;
/* Validate setting */
return (EINVAL);
}
}
return (rv);
}
int
{
}
static void
{
if (gain > 100) {
gain = 0;
}
return;
}
/*
* calculate the scaled values. Done now to avoid calculations
* later.
*/
}
}
int
{
audio_dev_t *d = arg;
audio_client_t *c;
if (val > 100) {
return (EINVAL);
}
/* don't need to check is_active here, its safe */
}
return (0);
}
int
{
audio_dev_t *d = arg;
return (0);
}
void
{
if (gain > 100) {
gain = 0;
}
/* if no change, don't bother doing updates */
return;
}
/*
* calculate the scaled values. Done now to avoid calculations
* later.
*/
}
}
{
return (sp->s_gain_pct);
}
void
{
/* if no work change, don't bother doing updates */
return;
}
if (muted) {
sp->s_gain_eff = 0;
} else {
}
}
{
}
{
}
void
{
}
void
{
/* if running, then stop it */
/*
* if we stopped the engine, we might need to wake up
* a thread that is waiting for drain to complete.
*/
}
}
/*
* When pausing, no new data will be played after the most recently
* mixed samples have played. However, the audio engine will continue
* to play (possibly just silence).
*
* engine here. Once fired up, the engine continues running unil it
* is closed.
*/
void
{
return;
}
}
void
{
return;
}
}
{
}
void
{
} else {
}
}
int
{
return (c->c_omode);
}
/*
* These routines should not be accessed by client "personality"
* implementations, but are for private framework use only.
*/
void
auimpl_client_init(void)
{
}
void
auimpl_client_fini(void)
{
}
static int
{
} else {
}
/* for now initialize conversion parameters */
sp->s_gain_pct = 0;
/*
* We have to start off with a reasonable buffer and
* interrupt configuration.
*/
return (ENOMEM);
}
/* make sure no stale data left in stream */
/*
* Allocate SRC and data conversion state.
*/
if (auimpl_format_alloc(sp) != 0) {
return (ENOMEM);
}
return (0);
}
static void
{
if (sp->s_cnv_buf0)
if (sp->s_cnv_buf1)
}
}
int
{
int rv;
/* start an asynchronous drain operation. */
} else {
rv = 0;
}
return (rv);
}
int
{
/*
* Note: Drain logic will automatically "stop" the stream when
* the drain threshold has been reached. So all we have to do
* is wait for the stream to stop.
*/
return (EINTR);
}
}
return (0);
}
{
audio_client_t *c;
audio_dev_t *d;
/* validate minor number */
return (NULL);
}
/* lookup device instance */
return (NULL);
}
audio_dev_warn(d, "unable to allocate client structure");
return (NULL);
}
c->c_dev = d;
if ((auimpl_stream_init(&c->c_ostream, c) != 0) ||
(auimpl_stream_init(&c->c_istream, c) != 0)) {
goto failed;
}
/*
* We hold the client lock here.
*/
break;
}
minor++;
}
goto failed;
}
return (c);
audio_stream_fini(&c->c_ostream);
audio_stream_fini(&c->c_istream);
mutex_destroy(&c->c_lock);
cv_destroy(&c->c_cv);
kmem_free(c, sizeof (*c));
return (NULL);
}
void
{
/* remove us from the global list */
list_remove(&auimpl_clients, c);
/* release the device reference count */
auimpl_dev_release(c->c_dev);
mutex_destroy(&c->c_lock);
cv_destroy(&c->c_cv);
audio_stream_fini(&c->c_istream);
audio_stream_fini(&c->c_ostream);
kmem_free(c, sizeof (*c));
}
void
{
c->c_is_active = B_TRUE;
}
void
{
c->c_is_active = B_FALSE;
}
void
{
audio_dev_t *d = c->c_dev;
/* stop the engines if they are running */
auclnt_stop(&c->c_istream);
auclnt_stop(&c->c_ostream);
list_remove(&d->d_clients, c);
mutex_enter(&c->c_lock);
/* if in transition need to wait for other thread to release */
while (c->c_refcnt) {
}
mutex_exit(&c->c_lock);
/* release any engines that we were holding */
auimpl_engine_close(&c->c_ostream);
auimpl_engine_close(&c->c_istream);
}
{
return (auimpl_dev_hold_by_index(index));
}
void
{
}
{
audio_client_t *c;
list = &auimpl_clients;
/* linked list search is kind of inefficient, but it works */
mutex_enter(&c->c_lock);
if (c->c_is_active) {
c->c_refcnt++;
mutex_exit(&c->c_lock);
} else {
mutex_exit(&c->c_lock);
c = NULL;
}
break;
}
}
return (c);
}
int
{
mutex_enter(&c->c_lock);
while (c->c_serialize) {
mutex_exit(&c->c_lock);
return (EINTR);
}
}
c->c_serialize = B_TRUE;
mutex_exit(&c->c_lock);
return (0);
}
void
{
mutex_enter(&c->c_lock);
ASSERT(c->c_serialize);
c->c_serialize = B_FALSE;
cv_broadcast(&c->c_cv);
mutex_exit(&c->c_lock);
}
void
{
mutex_enter(&c->c_lock);
c->c_refcnt++;
mutex_exit(&c->c_lock);
}
void
{
mutex_enter(&c->c_lock);
c->c_refcnt--;
if (c->c_refcnt == 0)
cv_broadcast(&c->c_cv);
mutex_exit(&c->c_lock);
}
{
return (d->d_serial);
}
void
int (*walker)(audio_client_t *, void *),
void *arg)
{
audio_client_t *c;
int rv;
if (!c->c_is_active)
continue;
if (rv == AUDIO_WALK_STOP) {
break;
} else if (rv == AUDIO_WALK_RESTART) {
goto restart;
}
}
}
int
{
audio_dev_t *d = c->c_dev;
int rv = 0;
int flags;
flags = 0;
flags |= ENGINE_NDELAY;
goto done;
}
goto done;
}
done:
if (rv != 0) {
/* close any engines that we opened */
auimpl_engine_close(&c->c_ostream);
auimpl_engine_close(&c->c_istream);
} else {
list_insert_tail(&d->d_clients, c);
}
return (rv);
}
{
return (c->c_minor);
}
{
return (c->c_origminor);
}
{
return (c->c_origminor & AUDIO_MN_TYPE_MASK);
}
queue_t *
{
return (c->c_rq);
}
queue_t *
{
return (c->c_wq);
}
{
return (c->c_pid);
}
cred_t *
{
return (c->c_cred);
}
{
return (c->c_dev);
}
int
{
}
int
{
}
const char *
{
}
const char *
{
}
{
}
const char *
{
} else {
}
}
int
{
return (dev->d_instance);
}
const char *
{
}
const char *
{
}
{
if (flags & DEV_OUTPUT_CAP)
if (flags & DEV_INPUT_CAP)
if (flags & DEV_DUPLEX_CAP)
/* AC3: deal with formats that don't support mixing */
return (caps);
}
{
uint64_t n;
return (n);
}
void
{
}
{
uint64_t n;
return (n);
}
void
{
}
void
{
/* we control minor number allocations, no need for runtime checks */
}
int
{
char path[MAXPATHLEN];
int rv = 0;
char *nt;
for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
continue;
switch (i) {
case AUDIO_MINOR_SNDSTAT:
if (!(d->d_flags & DEV_SNDSTAT_CAP)) {
continue;
}
nt = DDI_PSEUDO;
break;
default:
continue;
}
nt = DDI_NT_AUDIO;
break;
}
if (rv != 0)
break;
}
}
return (rv);
}
void
{
char path[MAXPATHLEN];
for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
continue;
}
}
}
void *
{
return (d->d_minor_data[mn]);
}
void *
{
}
/*
* This will walk all controls registered to a clients device and callback
* to walker for each one with its audio_ctrl. Note this data
* must be considered read only by walker.
*
* Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE)
* or stop walk (AUDIO_WALK_STOP).
*
*/
void
int (*walker)(audio_ctrl_t *, void *),
void *arg)
{
mutex_enter(&d->d_ctrl_lock);
break;
}
mutex_exit(&d->d_ctrl_lock);
}
/*
* This will search all controls attached to an
* audio device for a control with the desired name.
*
* d - the audio device to look on
* name - name of the control being looked for.
*
* On successful return a ctrl handle will be returned. On
* failure NULL is returned.
*/
{
/* Verify argument */
ASSERT(d);
mutex_enter(&d->d_ctrl_lock);
mutex_exit(&d->d_ctrl_lock);
return (ctrl);
}
}
mutex_exit(&d->d_ctrl_lock);
return (NULL);
}
/*
* Given a known control, get its attributes.
*
* The caller must supply a audio_ctrl_desc_t structure. Also the
* values in the structure are ignored when making the call and filled
* in by this function. All data pointed to by elements of desc should
* be assumed read only.
*
* If an error occurs then a non-zero is returned.
*
*/
int
{
return (0);
}
int
{
}
int
{
}
void
{
}