am_ioctl.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains the code for implementing the ioctl()s that the audio.7I
* and mixer.7I man pages define. This file also contains private routines to
* support these calls.
*
* am_wput() in am_main.c just grabs the M_IOCTL and M_IOCDATA messages and
* sends them to either am_wioctl() or am_wiocdata(), where all processing
* takes place. am_svc() doesn't do any ioctl() processing at all.
*
* The following ioctl()s are supported:
* AUDIO_DIAG_LOOPBACK
* AUDIO_DRAIN
* AUDIO_GETINFO
* AUDIO_SETINFO
* AUDIO_GETDEV
* AUDIO_MIXER_MULTIPLE_OPEN
* AUDIO_MIXER_SINGLE_OPEN
* AUDIO_MIXER_GET_SAMPLE_RATES
* AUDIO_MIXERCTL_GETINFO
* AUDIO_MIXERCTL_SETINFO
* AUDIO_MIXERCTL_GET_CHINFO
* AUDIO_MIXERCTL_SET_CHINFO
* AUDIO_MIXERCTL_GET_MODE
* AUDIO_MIXERCTL_SET_MODE
*
* Most of the ioctl()s copy in a data structure for use by the ioctl().
* am_wioctl() or am_wiocdata() will request the data. Based on the ioctl()
* it then creates a data structure and enqueues a task request to execute
* that ioctl() in a separate thread. This allows am_wput() and am_wsvc() to
* continue working while the ioctl() is processed. When the ioctl() in it's
* own thread is complete it creates the appropriate message and sends it
* am_wiocdata().
*
* A task queue is used to serialize all access to the hardware state
* structures and the hardware. This greatly simplifies the locking model.
* When closing we wait for all of the tasks to complete. This may introduce
* a delay in closing, but tests with 40 playing channels shows no noticable
* delays.
*
* Two ioctl()s are not placed on the task queue. They are:
*
* AUDIO_GETINFO - Returns static data and thus the hardware state is
* irrelevant.
* AUDIO_DRAIN - This ioctl() doesn't change or get the device state.
* It is also a very long lived ioctl() and is dependent on how
* much audio is queued up. We don't want to block other channels
* from being open()ed or their ioctl()s. Because this is a long
* lived ioctl() it is handled in am_wsvc() instead of am_wput().
* Otherwise it would block am_wput() and this is against the
* rules.
*
* Signals are generated when the hardware is modified. The signal is sent
* after the ioctl()'s ack or nack has been sent. That way we don't get an
* interrupted system call.
*
* These routines are provided for use by the other mixer source code files:
* am_wiocdata()
* am_wioctl()
* am_audio_drained()
* am_audio_set_info()
* am_set_format()
* am_set_gain()
*/
#include <sys/audiovar.h>
/*
* Local routine prototypes used only by this file.
*/
static int am_ck_bits_set32(uint_t);
static void am_exit_task(audio_ch_t *);
void (*func)(void *));
audio_ch_t *);
audio_ch_t *, audio_ch_t *);
audio_i_state_t *);
audio_i_state_t *);
audio_i_state_t *, int);
struct copyreq *);
audio_i_state_t *);
/*
* Taskq callbacks.
*/
static void am_diag_loopback_task(void *);
static void am_get_chinfo_task(void *);
static void am_get_mode_task(void *);
static void am_getinfo_task(void *);
static void am_mixerctl_getinfo_task(void *);
static void am_mixerctl_setinfo_task(void *);
static void am_multiple_open_task(void *);
static void am_sample_rate_task(void *);
static void am_set_chinfo_task(void *);
static void am_set_mode_task(void *);
static void am_setinfo_task(void *);
static void am_single_open_task(void *);
/* this simulates the rw lock handling by taskq framework */
#ifdef __lock_lint
static void
{
}
static void
{
}
#else
#define am_enter_rwlock()
#define am_release_rwlock()
#endif
/*
* The main routines for this file.
*/
/*
* am_wiocdata()
*
* Description:
* This routine is called by am_wput() to process all M_IOCDATA
* messages.
*
* We only support transparent ioctls.
*
* This routine also is used to return a IOCNAK if the state pointer
* or the channel pointer, setup in am_wsvc(), are invalid.
*
* CAUTION: This routine is called from interrupt context, so memory
* allocation cannot sleep.
*
* WARNING: Don't forget to free the mblk_t struct used to hold private
* data. The ack: and nack: jump points take care of this.
*
* WARNING: Don't free the private mblk_t structure if the command is
* going to call qreply(). This frees the private data that will
* be needed for the next M_IOCDATA message.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
*
* Returns:
* 0 Always returns a 0, becomes a return for
* am_wsvc()
*/
int
{
int error = 0;
int send_sig = 0;
/* make sure we've got a good return value */
goto done;
}
/*
* Work through the iocdata messages. These are arranged so that
* the messages that need to do further work are ordered first and
* then the ACKs.
*/
switch (cmd->ais_command) {
case AM_COPY_IN_AUDIOINFO:
/* AUDIO_SETINFO */
if (error) {
goto done;
}
return (0);
case AM_COPY_IN_DIAG_LOOPB:
/* AUDIO_DIAG_LOOPBACK */
if (error) {
goto done;
}
return (0);
case AM_COPY_IN_SAMP_RATES:
/* AUDIO_MIXER_GET_SAMPLE_RATES */
if (error) {
goto done;
}
return (0);
case AM_COPY_IN_SAMP_RATES2:
/* AUDIO_MIXER_GET_SAMPLE_RATES */
if (error) {
goto done;
}
return (0);
case AM_COPY_IN_MIXCTLINFO:
/* AUDIO_MIXERCTL_SETINFO */
if (error) {
goto done;
}
return (0);
/* AUDIO_MIXERCTL_GET_CHINFO */
if (error) {
goto done;
}
return (0);
/* AUDIO_MIXERCTL_GET/SET_CHINFO */
ATRACE("am_wiocdata() AM_COPY_OUT_MIXCTL_GET_CHINFO",
chptr);
if (error) {
goto done;
}
return (0);
/* AUDIO_MIXERCTL_SET_CHINFO */
if (error) {
goto done;
}
return (0);
/* AUDIO_MIXERCTL_SET_CHINFO */
if (error) {
goto done;
}
return (0);
case AM_COPY_IN_MIXCTL_MODE:
/* AUDIO_MIXERCTL_SET_MODE */
if (error) {
goto done;
}
return (0);
case AM_COPY_OUT_AUDIOINFO:
/* AUDIO_GETINFO */
goto done;
case AM_COPY_OUT_AUDIOINFO2:
/* AUDIO_SETINFO */
send_sig++;
goto done;
case AM_COPY_OUT_GETDEV:
/* AUDIO_GETDEV */
goto done;
case AM_COPY_OUT_SAMP_RATES:
/* AUDIO_MIXER_GET_SAMPLE_RATES */
goto done;
case AM_COPY_OUT_MIXCTLINFO:
/* AUDIO_MIXERCTL_GET/SETINFO */
/* generate a signal ONLY when we set the info */
send_sig++;
}
goto done;
/* AUDIO_MIXERCTL_GET/SET_CHINFO */
ATRACE("am_wiocdata() AM_COPY_OUT_MIXCTL_GET_CHINFO2",
chptr);
/* generate a signal ONLY when we set the info */
send_sig++;
}
goto done;
case AM_COPY_OUT_MIXCTL_MODE:
/* AUDIO_MIXERCTL_GET_MODE */
goto done;
default:
ATRACE("am_wiocdata() No framework cmds found, "
"check driver entry points next", chptr);
break;
}
}
/* see if we have an entry pt in the Audio Driver */
/* we do, so call it */
ATRACE("am_wiocdata(): "
"calling Audio Driver iocdata() routine",
case AM_WIOCDATA:
return (0);
case AM_ACK:
goto done;
case AM_NACK:
goto done;
default:
break;
}
}
/* no driver entry, so we nack unrecognized iocdata cmds */
/* Done checking */
done:
if (csp->cp_private) {
}
if (cqp->cq_private) {
}
if (error) {
} else {
}
if (send_sig) {
}
return (0);
} /* am_wiocdata() */
/*
* am_wioctl()
*
* Description:
* This routine is called by am_wput() to process all M_IOCTL
* messages.
*
* We only support transparent ioctls. Since this is a driver we
* nack unrecognized ioctls.
*
* This routine also is used to return a IOCNAK if the state pointer
* or the channel pointer, setup in am_wsvc(), are invalid.
*
* The following ioctls are supported:
* AUDIO_DIAG_LOOPBACK special diagnostics mode
* AUDIO_DRAIN
* AUDIO_GETDEV
* AUDIO_GETINFO
* AUDIO_SETINFO
* AUDIO_MIXER_MULTIPLE_OPEN
* AUDIO_MIXER_SINGLE_OPEN
* AUDIO_MIXER_GET_SAMPLE_RATES
* AUDIO_MIXERCTL_GETINFO
* AUDIO_MIXERCTL_SETINFO
* AUDIO_MIXERCTL_GET_CHINFO
* AUDIO_MIXERCTL_SET_CHINFO
* AUDIO_MIXERCTL_GET_MODE
* AUDIO_MIXERCTL_SET_MODE
* unknown call Audio Driver ioctl() routine
*
* WARNING: There cannot be any locks owned by calling routines.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
*
* Returns:
* 0 Always returns a 0, becomes a return for
* am_wsvc()
*/
int
{
int command;
int error = 0;
/* make sure this is a transparent ioctl */
goto done;
}
/* get a buffer for priv. data, but only if this isn't an AUDIO_DRAIN */
if (command != AUDIO_DRAIN) {
NULL) {
ATRACE("am_wioctl() state kmem_zalloc() failed", 0);
goto done;
}
}
switch (command) {
case AUDIO_DIAG_LOOPBACK:
if (error) {
goto done;
}
return (0);
case AUDIO_DRAIN:
return (0);
case AUDIO_GETDEV:
if (error) {
goto done;
}
return (0);
case AUDIO_GETINFO:
if (error) {
goto done;
}
return (0);
case AUDIO_SETINFO:
if (error) {
goto done;
}
return (0);
goto done;
}
if (error) {
goto done;
}
return (0);
case AUDIO_MIXER_SINGLE_OPEN:
goto done;
}
if (error) {
goto done;
}
return (0);
if (error) {
goto done;
}
return (0);
case AUDIO_MIXERCTL_GETINFO:
if (error) {
goto done;
}
return (0);
case AUDIO_MIXERCTL_SETINFO:
if (error) {
goto done;
}
return (0);
if (error) {
goto done;
}
return (0);
if (error) {
goto done;
}
return (0);
case AUDIO_MIXERCTL_GET_MODE:
/* allowed only on AUDIOCTL channels */
goto done;
}
if (error) {
goto done;
}
return (0);
case AUDIO_MIXERCTL_SET_MODE:
if (error) {
goto done;
}
return (0);
default: /* see if we have an entry pt in the Audio Driver */
/* we do, so call it */
ATRACE("am_wioctl(): "
"calling Audio Driver ioctl() routine",
case AM_WIOCDATA:
return (0);
case AM_ACK:
goto done;
case AM_NACK:
goto done;
default:
break;
}
}
/* no - we're a driver, so we nack unrecognized ioctls */
CE_NOTE, "wioctl() unrecognized ioc_cmd: 0x%x",
break;
}
/* we always either ack or nack depending on if error is set or not */
done:
/* free allocated state memory */
if (state) {
}
if (error) {
} else {
}
return (0);
} /* am_wioctl() */
/*
* Private utilities used by this and other audio mixer files.
*/
/*
* am_audio_drained()
*
* Description:
* There's an AUDIO_DRAIN ioctl() that is waiting. When the channel
* goes empty this routine is called to send the ack back to the
* STREAMS head, which lets the ioctl() return.
*
* We also generate a CV signal so that waiting close()s will wakeup.
*
* Arguments:
* audio_ch_t *chptr Pointer to this channel's state info
*
* Returns:
* void
*/
void
{
/* make sure the channel is empty, while locked */
return;
}
/*
* By definition we don't have any audio to play thus if we need to
* switch modes we can go ahead and do it now.
*/
/* ack only if we have an mblk_t */
if (mp) {
}
} /* am_audio_drained() */
/*
* am_audio_set_info()
*
* Description:
* This routine double checks the passed in audio_info_t structure to
* make sure the values are legal. If they are then they are used to
* update the audio hardware. In COMPAT mode all the hardware is updated,
* as it is for a multi-stream Codec. However traditional Codecs in MIXER
* mode don't update the data format or gain. Everything else can be
* updated.
*
* After the checks are completed and the hardware has been updated
* the reti pointer is checked. If NULL we are done. Otherwise the
* structure pointed to by reti is filled in with the new hardware
* configuration.
*
* The mixer only supports a few formats, 16-bit linear and 8-bit
* u-law, A-law and linear. Any other format will cause the check to
* fail.
*
* We don't bother checking the read only members, silently ignoring any
* modifications.
*
* XXX Need to set hardware to original state if error, especially
* if src_update() fails. Also, maybe move src_update() up higher so it
* can fail before we change hardware. Plus, it's easier to undo
* src_update().
*
* NOTE: The Codec's lock must NOT be held when calling this routine.
*
* NOTE: reti will be NULL only when this routine is being called by
* am_open().
*
* NOTE: The calling routine is responsible for sending the hardware
* change signal.
*
* Arguments:
* audio_ch_t *chptr Pointer to this channel's state info
* audio_info_t *newi Pointer to the struct with new values
* audio_info_t *reti Pointer to the updated struct that is
* returned
*
* Returns:
* AUDIO_SUCCESS Successful
* AUDIO_FAILURE Failed
*/
int
{
int doread;
int dowrite;
int stream;
/*
* force play and record, thus checking to see if it is changing
* the format. Since AUDIOCTL channels can't change the format
* we fail if the format isn't the same.
*/
}
}
/*
* If hardware supports both play and record then we need to do the
* play vs. record checks.
*/
/*
* The AUDIO_MIXERCTL_SETINFO ioctl() creates a pseudo channel that
* has it's ch_info.info set to hw_info. When in mixer mode this is
* the only time this happens. For this ioctl() we set only a small
* number of the h/w specific entries. So we fake out compat mode
* so that the h/w does get set.
*/
ATRACE("am_audio_set_info() AUDIO_MIXERCTL_SETINFO ioctl()",
NULL);
}
/* we use curi to get info to check against */
if (mode == AM_COMPAT_MODE) {
#ifdef DEBUG
} else {
/* this was set above, just be a bit paranoid */
#endif
}
/* first make sure the new data format is legal */
goto error;
}
} else {
}
goto error;
}
} else {
}
if (mode == AM_COMPAT_MODE &&
/* if only play or record we can fix this */
/* set play to capture sample rate */
stpptr->am_out_chs) {
/* set capture to play sample rate */
} else {
goto error;
}
} else {
/*
* There's a bug in audiotool which after doing an
* AUDIO_SETINFO it updates the state in AudioDevice.cc
* SetState() it uses the record side to get the new
* sample rate! So work around if write only. Who knows,
* perhaps other apps are as stupid!
*/
/* set to the same sample rate, gads! */
}
}
}
ATRACE_32("am_audio_set_info() PLAY sample rate set",
ATRACE_32("am_audio_set_info() RECORD sample rate set",
goto error;
}
} else {
}
goto error;
}
} else {
}
if (mode == AM_COMPAT_MODE &&
/* if only play or record we can fix this */
/* set play to capture sample rate */
stpptr->am_out_chs) {
/* set capture to play sample rate */
} else {
goto error;
}
} else {
/* see audiotool bug description above */
/* set to the same channels, gads! */
}
}
}
ATRACE_32("am_audio_set_info() PLAY channels set",
ATRACE_32("am_audio_set_info() RECORD channels set",
goto error;
}
} else {
}
goto error;
}
} else {
}
if (mode == AM_COMPAT_MODE &&
/* if only play or record we can fix this */
/* set play to capture sample rate */
stpptr->am_out_chs) {
/* set capture to play sample rate */
} else {
goto error;
}
} else {
/* see audiotool bug description above */
/* set to the same precision, gads! */
}
}
}
ATRACE_32("am_audio_set_info() PLAY precision set",
ATRACE_32("am_audio_set_info() RECORD precision set",
goto error;
}
} else {
}
goto error;
}
} else {
}
if (mode == AM_COMPAT_MODE &&
/* if only play or record we can fix this */
/* set play to capture sample rate */
stpptr->am_out_chs) {
/* set capture to play sample rate */
} else {
goto error;
}
} else {
/* see audiotool bug description above */
/* set to the same encoding, gads! */
}
}
}
ATRACE_32("am_audio_set_info() PLAY encoding set",
ATRACE_32("am_audio_set_info() RECORD encoding set",
/*
* In COMPAT mode or with multi-channel Codecs we check against
* what the hardware allows. Otherwise, we check against what the
* mixer can deal with. But only if an AUDIO channel.
*/
if (dowrite && am_ck_combinations(
goto error;
}
B_FALSE) == AUDIO_FAILURE) {
goto error;
}
} else { /* AM_MIXER_MODE */
/* make sure the mixer can deal with the combinations */
case -1: /* no change to channel */
case AUDIO_CHANNELS_MONO:
case AUDIO_CHANNELS_STEREO:
break;
default:
goto error;
}
case -1: /* no change to channel */
case AUDIO_CHANNELS_MONO:
case AUDIO_CHANNELS_STEREO:
break;
default:
goto error;
}
case -1: /* no change to encoding */
break;
case AUDIO_ENCODING_LINEAR: /* signed */
/* we support 8 & 16-bit linear */
goto error;
}
break;
case AUDIO_ENCODING_LINEAR8: /* unsigned */
case AUDIO_ENCODING_ULAW:
case AUDIO_ENCODING_ALAW:
/* support 8-bit unsigned linear, u-law & A-law */
goto error;
}
break;
default:
goto error;
}
case -1: /* no change to encoding */
break;
case AUDIO_ENCODING_LINEAR: /* signed */
/* we support 8 & 16-bit linear */
goto error;
}
break;
case AUDIO_ENCODING_LINEAR8: /* unsigned */
case AUDIO_ENCODING_ULAW:
case AUDIO_ENCODING_ALAW:
/* support 8-bit unsigned linear, u-law & A-law */
goto error;
}
break;
default:
goto error;
}
}
}
goto error;
}
} else {
}
goto error;
}
} else {
}
} else {
}
goto error;
}
/* always turn on un-modifiable ports */
goto error;
}
}
} else {
}
goto error;
}
/* always turn on un-modifiable ports */
/* check exclusivity */
goto error;
}
}
goto error;
}
} else {
}
goto error;
}
} else {
}
goto error;
}
} else {
ATRACE("am_audio_set_info() "
"monitor gain cannot be set", 0);
goto error;
}
}
} else {
}
} else {
}
/*
* Now that we've got the new values verified we need to update the
* hardware. The following is updated:
* COMPAT Mode, All Devices
* play.minordev (H/W) record.minordev (H/W)
* COMPAT Mode, AUDIO Device
* play.precision (H/W) record.precision (H/W)
* play.encoding (H/W) record.encoding (H/W)
* play.gain (H/W) record.gain (H/W)
* play.balance (H/W) record.balance (H/W)
* output_muted (H/W)
* COMPAT Mode, AUDIOCTL Device
* play.gain (H/W) record.gain (H/W)
* play.balance (H/W) record.balance (H/W)
* output_muted (H/W)
* MIXER Mode, All Devices
* play.minordev (CH) record.minordev (CH)
* MIXER Mode, AUDIO Device, Traditional Codec
* play.precision (CH) record.precision (CH)
* play.encoding (CH) record.encoding (CH)
* play.gain (CH) record.gain (CH)
* play.balance (CH) record.balance (CH)
* output_muted (CH)
* MIXER Mode, AUDIOCTL Device, Traditional Codec, Same Process As
* An AUDIO Channel, ch_ctl == TRUE
* play.gain (CH) record.gain (CH)
* play.balance (CH) record.balance (CH)
* output_muted (CH)
* MIXER Mode, AUDIOCTL Device, Traditional Codec, Different Proc.
* From An AUDIO Channel, ch_ctl == FALSE
* play.gain (H/W) record.gain (H/W)
* play.balance (H/W) record.balance (H/W)
* output_muted (H/W)
* MIXER Mode, AUDIO Device, Multi-Channel Codec
* play.precision (CH H/W) record.precision (CH H/W)
* play.encoding (CH H/W) record.encoding (CH H/W)
* play.gain (CH H/W) record.gain (CH H/W)
* play.balance (CH H/W) record.balance (CH H/W)
* output_muted (CH H/W)
* MIXER Mode, AUDIOCTL Device, Multi-Channel Codec, Same Proc. As
* An AUDIO Channel, ch_ctl == TRUE
* play.gain (CH H/W) record.gain (CH H/W)
* play.balance (CH H/W) record.balance (CH H/W)
* output_muted (CH H/W)
* MIXER Mode, AUDIOCTL Device, Multi-Channel Codec, Different
* Process From An AUDIO, ch_ctl == FALSE
* play.gain (H/W) record.gain (H/W)
* play.balance (H/W) record.balance (H/W)
* output_muted (H/W)
* All May Modify These Fields
* play.port (H/W) record.port (H/W)
* monitor_gain (H/W)
*
* If we are in AM_COMPAT_MODE then output_muted controls the hardware,
* otherwise it just affects the channel, if it is a ch_ctl.
*/
/* only AUDIO channels can affect the data format */
/* figure out our "stream number" */
if (codec_type == AM_MS_CODEC) {
} else {
}
/*
* We only set the format if there's been a change.
* Otherwise we risk introducing noise, pops, etc.,
* for little good reason.
*/
AM_SERIALIZE) == AUDIO_FAILURE) {
goto error;
}
}
AM_SERIALIZE) == AUDIO_FAILURE) {
goto error;
}
}
}
/* lock state while updating so ISR calls will be okay */
if (mode == AM_MIXER_MODE) {
} else {
}
/* see if we need to update the sample rate conv. routines */
if (chpptr->acp_writing) {
ATRACE("am_audio_set_info() PLAY, "
"calling src update", chpptr);
play,
play,
AUDIO_PLAY) == AUDIO_FAILURE) {
ATRACE("am_audio_set_info() "
"play src_update() failed", 0);
goto error;
}
}
if (chpptr->acp_reading) {
ATRACE("am_audio_set_info() RECORD, "
"calling src update", chpptr);
AUDIO_RECORD) == AUDIO_FAILURE) {
ATRACE("am_audio_set_info() "
"record src_update() failed", 0);
goto error;
}
}
}
}
/* is this an AUDIOCTL ch with the PID as another AUDIO ch? */
/* re-figure out our "stream number" */
} else {
}
/*
* AUDIO and AUDIOCTL can affect gains, ports, etc. If in COMPAT
* mode or a MS Codec we affect hardware. Otherwise this is a
* virtual ch. and only that channel's parameters are affected,
* i.e., no hardware update. Also, if a MS Codec and an AUDIOCTL
* channel isn't associated with a particular stream then we don't
* muck with hardware either.
*/
AM_SERIALIZE) == AUDIO_FAILURE) {
goto error;
}
AM_SERIALIZE) == AUDIO_FAILURE) {
goto error;
}
/* only if output_muted actually changed */
goto error;
}
}
}
if (mode == AM_MIXER_MODE) {
} else {
}
/* now we can set the ports and monitor gain, since all can set them */
/* only if the play port actually changed */
AM_SERIALIZE) == AUDIO_FAILURE) {
goto error;
}
}
}
/* only if the record port actually changed */
goto error;
}
}
}
/* only if the monitor gain actually changed */
goto error;
}
}
}
/* we need to update the virtual channel, if we have one */
if (mode == AM_MIXER_MODE) {
}
/* now fix virtual channel parameters */
} else {
}
} else {
}
} else {
}
} else {
}
} else {
}
} else {
}
} else {
}
/* if we unpaused we need to make sure we start up again */
start_play = B_TRUE;
}
}
} else {
}
} else {
}
} else {
}
} else {
}
/*
* For MIXER mode we must update virtual channel parameters, because
* as soon as we restart the DMA engine(s) it's going to ask for audio.
* If the pause is still in effect then no data is going to be
* transferred.
*/
if (mode == AM_MIXER_MODE) {
}
/* before we leave, we need to restart the DMA engines, or ... */
if (start_play == B_TRUE) {
/* make sure the play DMA engine is running */
AM_SERIALIZE) == AUDIO_FAILURE) {
/* we don't change pause flag if failed to start */
/*
* Since we are serialized we don't worry about the
* mode switch CV like am_wsvc() has to.
*/
} else {
}
/* make sure the play DMA engine is paused */
}
if (start_record == B_TRUE) {
/* make sure the record DMA engine is running */
AM_SERIALIZE) == AUDIO_FAILURE) {
/* we don't change pause flag if failed to start */
} else {
}
} else if (stop_record == B_TRUE) {
/* make sure the record DMA engine is stopped */
}
/*
* For COMPAT mode we are dealing with the hardware, not a virtual
* channel. So the true state of the hardware can't be modified before
* starting or stopping the DMA engine(s).
*/
if (mode == AM_COMPAT_MODE) {
}
/* everything passed so we can update the samples count */
if (new_play_samples) {
chpptr->acp_psamples_c = 0;
chpptr->acp_psamples_f = 0;
chpptr->acp_psamples_p = 0;
}
if (new_record_samples) {
}
/*
* If we don't have a reti pointer we ignore the R/O members. If we
* need them we get them directly from the channel that is active.
* So if reti == NULL we are done. Otherwise copy tempi into the
* memory pointed to by reti and then copy over the R/O members.
*
* We pass the reserved members, just in case.
* play._xxx[1] record._xxx[1]
* _xxx[1]
* _xxx[2]
*/
/* do a quick copy, and then fill in the special fields */
}
return (AUDIO_SUCCESS);
return (AUDIO_FAILURE);
} /* am_audio_set_info() */
/*
* am_set_format()
*
* Description:
* Set the hardware to the desired format. If the format is not
* supported we set it to the next best thing. Then the audio is
* translated to the desired format during playback or record.
*
* NOTE: All setting of the hardware format MUST be done via this
* routine. Thus this is the only place the am_hw_* members
* are updated.
*
* NOTE: We don't worry about checking the sample rate. This routine
* won't be called with a sample rate that isn't legal according
* to the audio driver's configuration tables.
*
* Arguments:
* audio_state_t *statep Ptr to the dev instance's state
* am_apm_private_t *stpptr Ptr to APM private data
* am_ad_info_t *ad_infop Ptr to the AD's config info
* int stream Audio stream
* int dir AUDIO_PLAY or AUDIO_RECORD
* int sample_rate Sample rate to set
* int channels The number of channels to set
* int precision The sample precision
* int encoding The encoding method
* int force Force the format to be set
* int serialize Serialize calls into driver
*
* Returns:
* AUDIO_SUCCESS The format was successfully set
* AUDIO_FAILURE The format wasn't set
*/
int
{
int flags;
if (dir == AUDIO_PLAY) {
} else if (dir == AUDIO_RECORD) {
} else {
return (AUDIO_FAILURE);
}
/* start with channels */
switch (channels) {
case AUDIO_CHANNELS_MONO:
/* try to set the same 1st */
if (flags & AM_PRIV_CH_MONO) {
} else {
/* we have to use stereo, so will need to translate */
}
break;
case AUDIO_CHANNELS_STEREO:
/* try to set the same 1st */
if (flags & AM_PRIV_CH_STEREO) {
} else {
/* we have to use mono, so will need to translate */
}
break;
default:
CE_NOTE, "set_format() bad channels: %d",
channels);
return (AUDIO_FAILURE);
}
/* check the precision */
if (precision == AUDIO_PRECISION_16) {
/* see if the hardware supports what we want */
if (flags & AM_PRIV_16_PCM) {
} else {
/* the h/w doesn't, so pick an alternative */
if (flags & AM_PRIV_8_ULAW) {
} else if (flags & AM_PRIV_8_ALAW) {
} else {
}
}
} else {
/* just like above go through the list to see what we can use */
if (encoding == AUDIO_ENCODING_LINEAR) {
if (flags & AM_PRIV_8_PCM) {
} else {
if (flags & AM_PRIV_8_ULAW) {
} else if (flags & AM_PRIV_8_ALAW) {
} else {
}
}
} else if (encoding == AUDIO_ENCODING_ULAW) {
if (flags & AM_PRIV_8_ULAW) {
} else {
if (flags & AM_PRIV_8_ALAW) {
} else if (flags & AM_PRIV_8_PCM) {
} else {
}
}
} else {
if (flags & AM_PRIV_8_ALAW) {
} else {
if (flags & AM_PRIV_8_ULAW) {
} else if (flags & AM_PRIV_8_PCM) {
} else {
}
}
}
}
/*
* We now have the best possible h/w configuration. We see if
* it matches what we already have. If so then there's nothing
* to do. Otherwise the driver is called to set the hardware.
* If we do call the h/w we use the derived format, not the
* format passed to this routine.
*/
ATRACE("am_set_format() calling am_ad_set_format()", 0);
ATRACE("am_set_format() am_ad_set_format() failed", 0);
return (AUDIO_FAILURE);
}
/* update the true hardware image */
/* XXX this is probably not right! */
if (dir == AUDIO_PLAY) {
} else {
}
}
/* there's no hardware change so just return */
ATRACE("am_set_format() just returning", 0);
return (AUDIO_SUCCESS);
} /* am_set_format() */
/*
* am_set_gain()
*
* Description:
* This routine is used to set the gain of all channels in the Codec.
* The gain is modified by balance. We try two different methods, the
* first with the gain and balance, and the second with gain and balance
* mixed down into left and right gain. This lets the Audio Driver accept
* whichever format it prefers. If the Audio Driver doesn't like the
* first method it returns AUDIO_FAILURE and the second is tried.
*
* Some Codecs, like the Crystal 4231, will copy a mono input signal
* over to the 2nd channel. If this is the case then we apply balance
* to the left and right channels. Otherwise we adjust the only left
* gain.
*
* NOTE: We change the gain only if it actually did change.
*
* Arguments:
* audio_state_t *statep Pointer to the device instance's state
* audio_apm_info_t *apm_infop Ptr to driver's audio_apm_info structure
* uint_t channels The number of h/w channels
* uint_t gain The gain to set
* uint_t balance The balance to set
* int dir AUDIO_PLAY or AUDIO_RECORD
* int stream The hardware stream to set gain on
* int force Force the gain to be set
* int serialize Serialize access to the audio driver
*
* Returns:
* AUDIO_SUCCESS The gain was successfully set
* AUDIO_FAILURE The gain was not successfully set
*/
int
int serialize)
{
uint_t g;
if (dir == AUDIO_PLAY) {
} else {
}
/* 1st try the gain and balance method since it's the easiest for us */
ATRACE("am_set_gain() AM_SET_GAIN_BAL successful", 0);
return (AUDIO_SUCCESS);
}
/* make sure there was a change */
return (AUDIO_SUCCESS);
}
/* we always set left gain */
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} else {
/* make sure there was a change */
if (!force &&
ATRACE_32("am_set_gain() stereo, the same balance",
balance);
return (AUDIO_SUCCESS);
}
/*
* Balance adjusts gain. If balance < 32 then left is
* enhanced by attenuating right. If balance > 32 then
* right is enhanced by attenuating left.
*/
/* left channel */
return (AUDIO_FAILURE);
}
/* right channel */
} else if (balance < AUDIO_MID_BALANCE) {
/*
* l = gain
* r = (gain * balance) / 32
*/
/* left channel */
return (AUDIO_FAILURE);
}
/* right channel */
ATRACE_32("am_set_gain() R2 gain", g);
} else {
/*
* l = (gain * (64 - balance)) / 32
* r = gain
*/
/* left channel */
ATRACE_32("am_set_gain() L3 gain", g);
return (AUDIO_FAILURE);
}
/* right channel */
}
}
} /* am_set_gain() */
/*
* Private utilities used only by this file.
*/
/*
* am_ck_bits_set32()
*
* Description:
* This routine figures out how many bits are set in the passed in val.
*
* Arguments:
* uint val The argument to test
*
* Returns:
* 0 - 32 The number of bits set
*/
int
{
int count;
int i;
for (i = 0, count = 0; i < 32; i++) {
count++;
}
mask <<= 1;
}
return (count);
} /* am_ck_bits_set32() */
/*
* am_exit_task()
*
* Description:
* Exit from a task. This means decrementing the task counter so a
* blocked close() may continue.
*
* Arguments:
* audio_ch_t *chptr Ptr to the channel's struct
*
* Returns:
* void
*/
static void
{
/* clear the taskq flag */
} /* am_exit_task() */
/*
* am_fix_info()
*
* Description:
* When in mixer mode we usually play at a different sample rate
* than the data stream from the application. Therefore the sample
* count from the Codec is meaningless. This routine adjusts for the
* difference in sample rates.
*
* We only adjust the play sample count because when recording you send
* x samples so you always know how many samples you sent so you don't
* have to adjust.
*
* If this is an AUDIOCTL channel and it is associated with the H/W
* we don't do anything.
*
* We also fix port and pause info, as well as other H/W related info,
* depending on the mixer mode.
*
* Arguments:
* audio_ch_t *chptr Ptr to the channel's state structure
* audio_info_t *info Ptr to the info structure to update
*
* Returns:
* void
*/
static void
{
/* first, update the features */
/* now fix various other things */
if (mode == AM_MIXER_MODE) {
ATRACE("am_fix_info() fixing other things", 0);
}
}
}
/*
* CAUTION: Don't place anything related to record below this
* point. Otherwise it may not execute.
*
* Finally, fix play samples, if we need to.
*/
ATRACE_32("am_fix_info() not writing, returning",
return;
} else {
if (mode == AM_MIXER_MODE &&
ATRACE("am_fix_info() sample conversion", 0);
} else {
ATRACE("am_fix_info() NO sample conversion",
}
}
} /* am_fix_info() */
/*
* am_fix_play_pause()
*
* Description:
* Convenience routine to clean up the code for switching to
* mixer mode while paused.
*
* Arguments:
* audio_ch_t *chptr Ptr to the channel
* audio_apm_info_t *apm_infop Ptr to mixer's APM info struct
*
* Returns:
* AUDIO_SUCCESS Fix successful
* AUDIO_FAILURE Fix failed
*/
static int
{
/* first we update the sample rate converter */
return (AUDIO_FAILURE);
}
/*
* It is possible that the channel was paused and then the mode
* was switched. Thus we are most likely changing modes not on
* a message boundary. Thus we need to make a best guess as to
* where to start playing.
*/
/* it's remotely possible that we happen to be at the end */
/* don't let it be played again! */
return (AUDIO_SUCCESS);
/* or that we are just about to use a new msg */
/* put it back to use next */
return (AUDIO_SUCCESS);
}
/* see if we need to process the data */
/*
* For some reason we can't convert the message and
* there isn't much we can do, so just blow it away
* and live with the gap in audio. We don't fail
* changing modes because that went okay.
*/
ATRACE("am_fix_play_pause() am_reprocess() failed",
statep);
return (AUDIO_SUCCESS);
}
/* put it back to use next */
}
return (AUDIO_SUCCESS);
} /* am_fix_play_pause() */
/*
* am_mixer_task_acknack()
*
* Description:
* Sometimes when a taskq thread is done the ioctl() is also done.
* To finish off an ioctl() an M_IOCACK or M_IOCNAK message must be sent
* up to the STREAMS head. This routine performs that task.
*
* Arguments:
* audio_i_state_t *state Pointer to the ioctl() state structure
* audio_ch_t *chptr Ptr 2 the channel's state structure
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the STREAMS message to use
* am_ioctl_args_t *arg Argument structure
* int error 0 if no error, errno otherwise
*
* Returns:
* void
*/
static void
{
/* no memory leaks allowed */
if (state->ais_address2) {
}
if (cqp->cq_private) {
}
if (error) {
} else {
}
/* let am_close() proceed */
} /* am_mixer_task_acknack() */
/*
* am_restart()
*
* Description:
* This routine is used to restart playing and recording audio when
* they have been stopped to switch mixer modes.
*
* NOTE: This can only be for traditional Codecs, multi-stream Codecs
* aren't stopped to changed modes.
*
* Arguments:
* audio_state_t *statep Pointer to the device instance's state
* audio_info_t *hw_info Pointer to the hardware state
*
* Returns:
* void
*/
static void
{
int i;
"am_restart() failed");
return;
}
i++, tchptr++) {
/* skip non-AUDIO and unallocated channels */
continue;
}
if (chpptr->acp_writing) {
/* turn the Q back on */
/* make sure we'll be flow controlled in am_wsvc() */
/*
* It is possible we switched modes right when the last
* message was played and there's no way we're going
* to get the three calls to am_get_samples() we need
* to call cv_signal() for AUDIO_DRAIN. Therefore we
* set AM_CHNL_EMPTY, which means the next time
* am_get_samples() is called, which will happen when
* we start playing below, it does the cv_signal().
* We can subvert the number of times am_get_samples()
* needs to be called because we know the DMA buffers
* have been drained.
*/
if (audio_sup_get_audio_data_cnt(tchptr) == 0 &&
/* signal any DRAIN situation */
}
/* set before start for mode switch */
}
/*
* We don't change pause if failed to
* start.
*/
/*
* Since we are part of the mode switch
* we don't have to worry about the
* mode switch CV like am_wsvc() has to.
*/
}
}
}
if (chpptr->acp_reading) {
/* set before start for mode switch */
}
/*
* We don't change pause if failed to
* start.
*/
}
} else {
}
} else {
}
}
} /* am_restart() */
/*
* am_sched_task()
*
* Description:
* Common routine called to place a task on the taskq.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
* void (*func)(void *) Pointer to the task to schedule
*
* Returns:
* 0 No error
* errno Error number for the error
*/
static int
void (*func)(void *))
{
ATRACE("in am_sched_task()", q);
/* get the arg structure and fill it in */
return (ENOMEM);
}
/* schedule the task */
/* let am_close() proceed and free the arg structure */
return (EIO);
}
return (0);
} /* am_sched_task() */
/*
* am_set_compat_mode()
*
* Description:
* This routine is used to convert the mixer from MIXER mode to COMPAT
* mode. Any playing and recording channels should have been stopped
* before this routine is called.
*
* When this routine is called there may be one playing and one recording
* channel.
*
* We don't have to worry about resetting psamples_f after calling
* am_audio_set_info() because am_get_samples() has been called twice
* while we wait to shutdown. Thus it has already been added into the
* sample count.
*
* NOTE: Only traditional Codecs will use this code.
*
* Arguments:
* audio_ch_t *chptr Ptr to the channel changing the mode
* am_ad_info_t *ad_infop Ptr to the Audio Driver's config info
* audio_ch_t *pchptr Ptr to the play channel
* audio_ch_t *pchptr Ptr to the record channel
*
* Returns:
* AUDIO_SUCCESS Mode change completed successfully.
* AUDIO_FAILURE Mode change failed.
*/
static int
{
long tmp;
int tmp_pgain;
int tmp_pbal;
int tmp_rgain;
int tmp_rbal;
ATRACE("am_set_compat_mode() audio_sup_get_apm_info() failed",
statep);
return (AUDIO_FAILURE);
}
/* copy the original channel structure to the temp, just in case */
/* we only reset the hardware if we are playing or recording */
if (pchptr) {
}
if (rchptr) {
}
ATRACE("am_set_compat_mode() am_audio_set_info() failed",
&nchptr);
"set_compat() "
"couldn't reconfigure hardware");
return (AUDIO_FAILURE);
}
}
/* update the hardware open flags */
/* update persistent memory */
/*
* It is possible that the channel was paused and then the mode
* was switched. Thus we are most likely changing modes not on
* a message boundary. Thus we need to make a best guess as to
* where to start playing.
*/
if (pchptr &&
/* it's remotely possible that we happen to be at the end */
/* don't let it be played again! */
return (AUDIO_SUCCESS);
/* or that we are just about to use new data */
/* put it back to use next */
return (AUDIO_SUCCESS);
}
/*
* Make a guess as to where to point to. We make sure we are
* on a 4 byte boundary. That way we don't have to worry
* about being in the middle of sample.
*
* The equation:
* (offset of proc data from start)*(length of orig data)
* --------------------------------------------------------
* (length of proc data)
*/
/*
* tmp is an offset, which must be added to adata_orig to
* get adata_optr. We mask off adata_optr so that regardless
* of the format of the data we always are on a sample frame
* boundary.
*/
(tmp & ~AM_MISC_MASK);
/* put it back to use next */
}
ATRACE("am_set_compat_mode() done", 0);
return (AUDIO_SUCCESS);
} /* am_set_compat_mode() */
/*
* am_set_mixer_mode()
*
* Description:
* This routine is used to convert the mixer from COMPAT mode to MIXER
* mode. Any playing and recording channels should have been stopped
* before this routine is called.
*
* When this routine is called there may be one playing and one recording
* channel.
*
* Just like am_set_compat_mode(), psamples_f has already been added into
* the played sample count. So we don't need to do anything with it here.
*
* NOTE: Only traditional Codecs will use this code.
*
* Arguments:
* audio_ch_t *chptr Ptr to the channel changing the mode
* am_ad_info_t *ad_infop Ptr to the Audio Driver's config info
* am_apm_private_t **stpptr Ptr to the mixer's private state data
* audio_apm_info_t *apm_infop Ptr to the mixer's APM info structure
* audio_ch_t *pchptr Ptr to the play channel
* audio_ch_t *rchptr Ptr to the record channel
*
* Returns:
* AUDIO_SUCCESS Mode change completed successfully.
* AUDIO_FAILURE Mode change failed.
*/
static int
{
ATRACE("am_set_mixer_mode() audio_sup_get_apm_info() failed",
statep);
return (AUDIO_FAILURE);
}
/* copy the original channel structure to the temp, just in case */
if (pchptr) {
ATRACE("am_set_mixer_mode() mixer can't play using "
"unsupported sample rate",
return (AUDIO_FAILURE);
}
}
if (rchptr) {
ATRACE("am_set_mixer_mode() mixer can't record using "
"unsupported sample rate",
return (AUDIO_FAILURE);
}
}
}
}
ATRACE("am_set_mixer_mode() am_audio_set_info() failed",
&nchptr);
"set_mixer() "
"couldn't reconfigure hardware");
return (AUDIO_FAILURE);
}
/*
* We need to look like we've changed modes AFTER we try to set hw.
* Otherwise am_audio_set_info() won't update the hardware. It'll
* try to update the virtual channel.
*/
/* clear the open flags in the hardware */
/* clear the hardware's waiting flags */
/* clear misc flags */
/*
* Also update the hardware info for the number of channels,
* precision, and encoding.
*/
return (AUDIO_SUCCESS);
} /* am_set_mixer_mode() */
/*
* am_wiocdata_mixerctl_chinfo()
*
* Description:
* We have the audio_channel_t data structure so we know how big the info
* structure is. We make sure the size of the info structure is correct,
* that there is a buffer, and that the channel number is reasonable.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_i_state_t *state Pointer to ioctl() state structure
*
* Returns:
* 0 No error
* errno Error number for the error
*/
static int
{
int ch_number;
ATRACE("in am_wiocdata_mixerctl_chinfo()", q);
/* get ready to do the model conversion */
/*
* Make sure the size is good, fortunately audio_info_t doesn't have
* any pointers in it, so it's the same size, regardless of _ILP32
* or _LP64.
*/
ATRACE_32("am_wiocdata_mixerctl_chinfo() bad size",
return (EINVAL);
}
/* make sure the app has a buffer to place the data into */
ATRACE("am_wiocdata_mixerctl_chinfo() no buffer",
return (EINVAL);
}
/* get the channel number and make sure it's good */
ATRACE_32("am_wiocdata_mixerctl_chinfo() bad ch number",
return (EINVAL);
}
return (EINVAL);
}
/*
* We have a good audio_channel_t structure so we can do the next
* step in the process, which is to ask for the audio_info structure.
* But first we save the audio_channel structure for later use.
* The STREAMS head will give us a new mp->b_cont message block
* when it gives us the audio_info structure.
*/
/* Set mp->b_cont = NULL so mcopyin() does not free the saved message */
/* Setup for copyin */
/* send the copy in request */
ATRACE("am_wiocdata_mixerctl_chinfo() returning", q);
return (0);
} /* am_wiocdata_mixerctl_chinfo() */
/*
* am_wiocdata_mixerctl_get_chinfo()
*
* Description:
* The audio_info_t data structure has been copied out, so now we copy
* out the updated audio_channel_t structure.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_i_state_t *state Pointer to ioctl() state structure
*
* Returns:
* 0 No error
* errno Error number for the error
*/
static int
{
ATRACE("in am_wiocdata_mixerctl_get_chinfo()", q);
/* Setup for copyout */
ATRACE("am_wiocdata_mixerctl_get_chinfo() done", q);
return (0);
} /* am_wiocdata_mixerctl_get_chinfo() */
/*
* am_wiocdata_sr()
*
* Description:
* The next step in getting the sample rates. We've got the
* am_sample_rates_t structure so we can now get the number of sample
* rates to ask for, which we do. Thus we copy in the structure a
* second time, but this time it'll be larger.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* struct copyreq *cqp Pointer to copy request register
* audio_i_state_t *state Pointer to ioctl() state structure
*
* Returns:
* 0 No error
* errno Error number for the error
*/
static int
{
ATRACE("in am_wiocdata_sr()", q);
/*
* We copy in just the am_sample_rates_t structure to get the
* number of sample rates the samp_rates array can support.
* Once we know this we make another call to get the whole
* thing.
*/
/* get a pointer to the user supplied structure */
/* make sure the number of array elements is sane */
if (new->num_samp_rates <= 0) {
ATRACE_32("am_wiocdata_sr() AM_COPY_IN_SAMP_RATES "
return (EINVAL);
}
/*
* Now that we know the number of array elements, we can ask
* for the right number of bytes.
*
* Reuse the cq_private buffer, saving data for M_IOCDATA processing.
*/
/* Setup for copyin */
/* send the copy in request */
ATRACE("am_wiocdata_sr() returning success", 0);
return (0);
} /* am_wiocdata_sr() */
/*
* am_wioctl_copyin()
*
* Description:
* Common routine used to start the copy in process for many ioctl()s.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
* audio_i_state_t *state Pointer to ioctl() state structure
* int cmd IOCTL command
*
* Returns:
* 0 No error
* errno Error number for the error
*/
static int
{
int new_cmd;
ATRACE("in am_wioctl_copyin()", q);
/* set copyin based on the ioctl() command */
switch (cmd) {
case AUDIO_DIAG_LOOPBACK: {
/* not all Audio Drivers and their hardware support loopbacks */
ATRACE_32("am_wioctl_diag_loopback() no loopbacks",
return (ENOTTY);
}
size = sizeof (int);
break;
}
case AUDIO_SETINFO:
size = sizeof (audio_info_t);
break;
size = sizeof (am_sample_rates_t);
break;
case AUDIO_MIXERCTL_SETINFO:
/* allowed only on AUDIOCTL channels */
return (EINVAL);
}
/*
* We only need the dev_info part of am_control_t. Fortunately
* this is at the front of the structure. So set the size to
* copy in only the dev_info part. Otherwise we'll blow an
* assert in am_mixerctl_setinfo_task().
*/
size = sizeof (audio_info_t);
break;
/* allowed only on AUDIOCTL channels */
return (EINVAL);
}
/* size can be different, depending on _ILP32 and _LP64 */
break;
/* allowed only on AUDIOCTL channels */
return (EINVAL);
}
/* size can be different, depending on _ILP32 and _LP64 */
break;
case AUDIO_MIXERCTL_SET_MODE:
/* allowed only on AUDIOCTL channels */
return (EINVAL);
}
size = sizeof (int);
break;
default:
return (EIO);
}
/* set up the message */
/* Setup for copyin */
/* send the copy in request */
return (0);
} /* am_wioctl_copyin() */
/*
* am_wioctl_drain()
*
* Description:
* First make sure this is an AUDIO channel. Then see if there is
* any queued up audio. If there is then just return after setting
* a flag. Otherwise we've got an AUDIO_DRAIN before there's any
* played audio or after it has all played, thus we ACK.
*
* Once all audio has been played am_audio_drained() is called and
* it generates the ACK.
*
* This ioctl() doesn't use the taskq. Thus other channels won't be
* blocked by this ioctl().
*
* Like all ioctl()s, AUDIO_DRAIN may be interrupted by a signal.
* The STREAMS head will let the ioctl() return to the app. We have
* no way of knowing this has happened, unless we use cv_wait_sig().
* This would block am_wput() so we aren't going to use it. However
* the fact we don't know about the signal is okay. The STREAMS head
* keeps a sequence number and as long as we save the original mp it
* can tell if this is an old ioctl() or not. If it is it just ignores
* the ACK. Further, am_close() will call am_audio_drained() which
* will send the ACK, which the STREAMS head will ignore if it wishes.
* Or we'll get another AUDIO_DRAIN and we still have the old mp, and
* thus we'll know the old AUDIO_DRAIN was interrupted. So we just
* free the mp and we're still okay.
*
* XXX - I don't understand what this means if there's a mux pushed
* on top of the mixer. If this happens it is possible to have multiple
* AUDIO_DRAINs at once, but then you can also have multiple audio
* streams on one channel. So audio isn't going to work very well in
* that case anyway.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
* struct copyreq *cqp Pointer to copy request register
*
* Returns:
* void
*/
/*ARGSUSED*/
static void
{
int error = 0;
/* must be on a play audio channel */
goto done;
}
/*
* See if we've got a new AUDIO_DRAIN, which means the last one
* was interrupted.
*/
if (chpptr->acp_drain_mp) {
/* free it, it's lost and the STREAM head knows this */
}
/* see if we are empty, the easy case */
audio_sup_get_audio_data_cnt(chptr) == 0) {
goto done;
}
/* we need to wait for empty */
return;
done:
if (error) {
} else {
}
} /* am_wioctl_drain() */
/*
* am_wioctl_getdev()
*
* Description:
* The first half of the AUDIO_GETDEV ioctl(). Ask to copy out
* the audio driver's device information structure.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
* audio_i_state_t *state Pointer to ioctl() state structure
*
* Returns:
* 0 No error
* errno Error number for the error
*/
static int
{
ATRACE("in am_wioctl_getdev()", q);
/* set STREAMS for copy out of the audio_device structure */
/* Setup for copyout */
/* put the data in the buffer, but try to reuse it first */
return (ENOMEM);
}
/*
* We don't bother to lock the state structure because this
* is static data.
*/
sizeof (*ad_infop->ad_dev_info));
/* send the copy out request */
return (0);
} /* am_wioctl_getdev() */
/*
* Task queue callbacks.
*/
/*
* am_diag_loopback_task()
*
* Description:
* Called by the task queue to set the loopback mode in the audio
* driver.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_diag_loopback_task(void *arg)
{
int error = 0;
ATRACE_32("am_diag_loopback_task() enable",
AM_SERIALIZE) == AUDIO_FAILURE) {
ATRACE("am_diag_loopback_task() "
"AM_COPY_IN_DIAG_LOOPB enable failed", 0);
}
} else {
ATRACE_32("am_diag_loopback_task() disable",
AM_SERIALIZE) == AUDIO_FAILURE) {
ATRACE("am_diag_loopback_task() "
"AM_COPY_IN_DIAG_LOOPB disable failed", 0);
}
}
} /* am_diag_loopback_task() */
/*
* am_get_chinfo_task()
*
* Description:
* Called by the task queue to get the channel's info. If the size of
* the info structure doesn't match then we return an EINVAL. This is
* better than trying to copy out to much data and cause a core dump
* in the application.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_get_chinfo_task(void *arg)
{
int ch_number;
int error = 0;
/* we have to check the mode when it's stable */
ATRACE_32("am_get_chinfo_task() bad mode",
goto nack;
}
/* get ready to do the model conversion */
/*
* Make sure the size is good, fortunately audio_info_t doesn't have
* any pointers in it, so it's the same size, regardless of _ILP32
* or _LP64.
*/
ATRACE_32("am_get_chinfo_task() bad size",
goto nack;
}
/* make sure the app has a buffer to place the data into */
ATRACE("am_get_chinfo_task() no buffer",
goto nack;
}
/* get the channel number and make sure it's good */
ATRACE_32("am_get_chinfo_task() bad ch number",
goto nack;
}
goto nack;
}
/* okay to update the channel's info */
/*
* We have consistent data so now we can start the copy out,
* beginning with the audio_info_t structure. That's because
* we've got the _IPL32/_LP64 environment. Also, we can't use
* the macros here because of the special address. So we save
* the audio_channel_t data structure for later. And then get
* a new mblk to put the audio_info structure into.
*/
goto nack;
}
/*
* Set mp->b_cont = NULL so the mcopyout() below does not free the
* saved message in state->ais_address2 above when it sets
* mp->b_cont = tmp
*/
/* Setup for copyout */
/* send the copy in request */
return;
nack:
} /* am_get_chinfo_task() */
/*
* am_get_mode_task()
*
* Description:
* This task gets the current mixer mode while the state is stable.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_get_mode_task(void *arg)
{
/* set STREAMS for copy out of the mode */
/* Setup for copyout */
/* put the data in the buffer, but try to reuse it first */
if (cqp->cq_private) {
}
} else {
}
} /* am_get_mode_task() */
/*
* am_getinfo_task()
*
* Description:
* This is the task that gets serial access to the info data structure
* and copies out the audio_info data structure.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_getinfo_task(void *arg)
{
/* set STREAMS for copy out of the audio_info structure */
/* Setup for copyout */
NULL);
/* put the data in the buffer, but try to reuse it first */
return;
}
/* update the played sample count */
} /* am_getinfo_task() */
/*
* am_mixerctl_getinfo_task()
*
* Description:
* This task gets serial access to the state to copy out the am_control
* data structure.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_mixerctl_getinfo_task(void *arg)
{
int i;
int error = 0;
/* we have to check the mode when it's stable, we also check the type */
goto nack;
}
/* set STREAMS for copy out of the audio_info structure */
/* Setup for copyout */
NULL);
/* put the data in the buffer, but try to reuse it first */
ATRACE("am_mixerctl_getinfo_task() can't get memory", 0);
goto nack;
}
/*
* We have to assemble this one by pieces. First we take
* care of the hardware state, then extended hardware state.
*/
/* update the played sample count */
/* now get the channel information */
i++, tchptr++) {
} else {
}
}
/* send the copy out request */
return;
nack:
} /* am_mixerctl_getinfo_task() */
/*
* am_mixerctl_setinfo_task()
*
* Description:
* This task gets serial access to the state to set it based on the
* am_control data structure. Can only set a few global things. The
* ch_open argument of the am_control data structure since this is
* read only.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_mixerctl_setinfo_task(void *arg)
{
int error = 0;
/* we have to check the mode when it's stable */
ATRACE_32("am_mixerctl_setinfo_task() bad mode",
goto nack;
}
/* get a pointer to the user supplied structure */
/* we can only modify a few things so make sure that's all we touch */
/* we always create a pseudo channel that points to the h/w */
/* too ugly to check here, so send to a utility routine */
ATRACE("am_mixerctl_setinfo_task() am_audio_set_info() failed",
chptr);
goto nack;
}
/* since there wasn't an error we succeeded, so return struct */
/*
* Since there wasn't an error we were successful, now return
* the updated structure.
*/
/* Setup for copyout */
NULL);
return;
nack:
} /* am_mixerctl_setinfo_task() */
/*
* am_multiple_open_task()
*
* Description:
* The second part of the AUDIO_MIXER_MULTIPLE_OPEN ioctl(). We check
* the state here so we can rely on the state.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_multiple_open_task(void *arg)
{
int error = 0;
/*
* Don't allow this ioctl() if not in MIXER mode. We have to do this
* check in the task because that's the only way we can rely on the
* state.
*/
ATRACE_32("am_multiple_open_task() bad mode",
goto done;
}
/* just set the mode without checking what it is */
/* wake up any channels waiting on multiple open()s */
done:
} /* am_multiple_open_task() */
/*
* am_sample_rate_task()
*
* Description:
* Now that we will know how many sample rates to return we can get
* them. We do this in a task because we need to know the mixer mode.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_sample_rate_task(void *arg)
{
int error = 0;
int i;
int num;
/* get a pointer to the user supplied structure */
/* new->flags is random data, so clear */
/* make sure the number of array elements is sane */
if (new->num_samp_rates <= 0) {
ATRACE_32("am_sample_rate_task() AM_COPY_IN_SAMP_RATES2 "
goto nack;
}
ATRACE_32("am_sample_rate_task() AM_COPY_IN_SAMP_RATES2 type",
if (mode == AM_MIXER_MODE) {
} else {
}
if (mode == AM_MIXER_MODE) {
} else {
}
} else {
goto nack;
}
/* figure out how many sample rates we have */
/* we don't copy more sample rates than we have */
}
/* we reuse the buffer we got from user space */
for (i = 0; i < new->num_samp_rates; i++) {
/* get sample rate for array elements */
/* at the end of sample rates */
break;
}
}
/* let the app know there are more */
}
/* type remains the same, but update others */
}
/* ready to send the filled in structure back */
/* Setup for copyout */
return;
nack:
} /* am_sample_rate_task() */
/*
* am_set_chinfo_task()
*
* Description:
* Called by the task queue to set the channel's state. We've already
* verified the size and channel are okay, as well as there is a buffer.
* We have the audio_info structure, in mp->b_cont, so new we can set
* the state.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_set_chinfo_task(void *arg)
{
int ch_number;
int error = 0;
/* we have to check the mode when it's stable */
ATRACE_32("am_set_chinfo_task() bad mode",
goto nack;
}
/* get ready to do the model conversion */
/*
* We check things again because since the last check it could have
* changed on us.
*/
ATRACE_32("am_set_chinfo_task() bad size",
goto nack;
}
ATRACE("am_set_chinfo_task() no buffer",
goto nack;
}
ATRACE_32("am_set_chinfo_task() bad ch number",
goto nack;
}
goto nack;
}
/* because the channels are stable we update the audio_channel struct */
/* too ugly to check here, so send to a utility routine */
goto nack;
}
/* update the played sample count */
/*
* Since there wasn't an error we were successful, now return
* the updated structure.
*/
/* Setup for copyout */
NULL);
return;
nack:
} /* am_set_chinfo_task() */
/*
* am_set_mode_task()
*
* Description:
* Switch modes. We have to shut down play and record first, then change
* modes, and finally restart. Since we are a low priority thread we
* can sleep!
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_set_mode_task(void *arg)
{
int doread;
int dowrite;
int error = 0;
int i;
int new_mode;
int ppid = 0;
int rpid = 0;
#ifdef DEBUG
int pcount = 0;
int rcount = 0;
#endif
/* get the new_mode and make sure it's good */
goto done;
}
/* make sure we aren't going into the same mode */
goto done;
}
/* figure out the direction */
/* we allocate this memory while it's easy to back out */
if (new_mode == AM_MIXER_MODE &&
}
/* get the AUDIO apm_info pointer, we've got one for AUDIOCTL */
if (new_pinfo) {
}
if (new_rinfo) {
}
goto done;
}
/*
* Make sure we can go to COMPAT mode while we're locked. By
* definition if we are going to MIXER mode we can't have more
* than one in and one out channel allocated.
*/
goto done;
} else {
}
/* how we do the switch is different based on the device */
/* find the reading and writing channels */
/* skip non-AUDIO and unallocated channels */
continue;
}
/* is this channel playing?, recording? */
ATRACE("am_set_mode_task() found ch flags",
if (tchpptr->acp_writing) {
ATRACE_32("am_set_mode_task() MS found play ch",
#ifdef DEBUG
pcount++;
#endif
}
if (tchpptr->acp_reading) {
"am_set_mode_task() MS found record ch",
#ifdef DEBUG
rcount++;
#endif
}
/* are we done finding active channels? */
break;
}
}
/* pause playing & recording, so the ISR isn't called */
if (dowrite) {
}
if (doread) {
}
/*
* Multi-stream Codecs already use the virtual channel
* configuration to set the hardware, so this is a trivial
* change to make. Everything is halted so we don't need
* to lock this.
*/
if (new_mode == AM_COMPAT_MODE) {
} else {
}
} else {
/* wait for playing to end */
ATRACE_32("am_set_mode_task() wait to stop playing",
&stpptr->am_mode_lock) <= 0) {
ATRACE("am_set_mode_task() signal interrupt",
/* we aren't switching modes any longer */
/* we are bailing, so we need to restart */
if (new_mode == AM_MIXER_MODE) {
if (new_pinfo) {
sizeof (*new_pinfo));
}
if (new_rinfo) {
sizeof (*new_rinfo));
}
}
goto done;
}
"am_set_mode_task() signal returned normally", 0);
}
/*
* Now we shutdown the record channel, if active.
* We have to lock to make this call.
*/
}
/* wait as long as possible to save the old state for later */
/* find the play and record channels */
/* skip non-AUDIO and unallocated channels */
continue;
}
/* is this channel playing?, recording? */
ATRACE("am_set_mode_task() found ch flags",
ATRACE_32("am_set_mode_task() T found play ch",
/*
* Disable the queue so that am_wsvc() won't
* process any more data messages.
*/
#ifdef DEBUG
pcount++;
#endif
}
"am_set_mode_task() T found record ch",
#ifdef DEBUG
rcount++;
#endif
}
/* are we done finding active channels? */
break;
}
}
/*
* We stop playing because we have to force it to restart from
* scratch, which means flushing the DMA engine. Otherwise
* there's old data with the wrong format. This a problem only
* when we are paused.
*/
if (pchptr &&
}
if (new_mode == AM_MIXER_MODE) {
/* we aren't switching modes any longer */
/* we are bailing, so we need to restart */
if (new_pinfo) {
sizeof (*new_pinfo));
}
if (new_rinfo) {
sizeof (*new_rinfo));
}
goto done;
}
} else {
/* we aren't switching modes any longer */
/* we are bailing, so we need to restart */
goto done;
}
}
}
/* save the mode in persistent memory */
/*
* CAUTION: From here on out we cannot fail. We've changed modes
* and to fail would require setting it back. It is better
* to have a gap in audio then to go back at this point.
*/
if (new_mode == AM_MIXER_MODE) {
int tpid;
/* rebuild info structures and get pids */
if (new_rinfo) {
}
/* copy in the old play state */
/* copy the current dev state */
} else {
/* play or record channels */
if (pchptr) {
/* copy in the old play state */
/* copy the current dev state */
} else {
if (new_pinfo) {
sizeof (*new_pinfo));
}
}
if (rchptr) {
/* copy in the old record state */
sizeof (old_hw_info.record));
/* copy the current dev state */
} else {
if (new_rinfo) {
sizeof (*new_rinfo));
}
}
}
/* start over with the reference count */
/* find AUDIOCTL channels to assoc. with AUDIO chs */
/* skip if not AUDIOCTL or allocated chs */
continue;
}
} else {
}
}
/* now we need to re-initialize the src structures */
if (pchptr) {
ATRACE("am_set_mode_task() calling play src update",
pchptr->ch_private);
/* flag that we need to update the SRC */
ATRACE("am_set_mode_task() need to update PLAY",
}
}
if (rchptr) {
ATRACE("am_set_mode_task() calling rec src update",
rchptr->ch_private);
AUDIO_RECORD) == AUDIO_FAILURE) {
/* flag that we need to update the SRC */
ATRACE("am_set_mode_task() need to update REC",
}
}
} else {
/* start over with the reference count */
/*
* Skip if ! AUDIO, AUDIOCTL or allocated channels.
*
* It is possible that new audio will arrive in
* am_wsvc(), thus we need to make sure we have this
* lock and hold it if we have to manipulate the
* channel's info structure.
*/
continue;
}
/* see if we need to free the info structure */
/* not set to hw */
/* not set to hw, and only ref so clr */
} else {
/* someone else has a link to it */
}
}
/* now set to hardware */
}
}
/* we don't need this flag anymore, re-enabling ioctls */
/* we're in the new mode, so restart I/O */
/* if we have blocked processes they should unblock */
if (new_mode == AM_MIXER_MODE) {
}
done:
} /* am_set_mode_task() */
/*
* am_setinfo_task()
*
* Description:
* The third part of the AUDIO_SETINFO ioctl(). This is the task that
* gets serial access to the info data structure and hardware.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_setinfo_task(void *arg)
{
int error = 0;
goto nack;
}
/*
* Don't allow this ioctl() when on an audioctl channel when multiple
* audio channel open()s are allowed. We have to do this in the task
* because that's the only way we can rely on the state.
*/
ATRACE_32("am_wioctl() AUDIO_SETINFO bad type",
goto nack;
}
/* too ugly to check here, so send to a utility routine */
ATRACE_32("am_wioctl() AUDIO_SETINFO setinfo failed command",
goto nack;
}
/* update the played sample count */
/*
* Since there wasn't an error we were successful, now return
* the updated structure.
*/
/* Setup for copyout */
NULL);
return;
nack:
} /* am_setinfo_task() */
/*
* am_single_open_task()
*
* Description:
* The second part of the AUDIO_MIXER_SINGLE_OPEN ioctl(). We check
* the state here so we can rely on the state.
*
* Arguments:
* void *arg Argument structure
*
* Returns:
* void
*/
static void
am_single_open_task(void *arg)
{
int error = 0;
int i;
int num_rd = 0;
int num_wr = 0;
/*
* Don't allow this ioctl() if not in MIXER mode. We have to do this
* check in the task because that's the only way we can rely on the
* state.
*/
ATRACE_32("am_single_open_task() bad mode",
goto done;
}
/* see if there are multiple open()s already */
/* we need to freeze channel allocation */
i++, tchptr++) {
/* ignore different processes */
continue;
}
if (tchpptr->acp_reading) {
num_rd++;
}
if (tchpptr->acp_writing) {
num_wr++;
}
}
/* we change back only if at most 1 read or write ch. open */
/* too many channels open, so we have to fail */
} else {
/* if we get here we know there is only 1 ch, ours */
}
done:
} /* am_single_open_task() */