am_main.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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Mixer Audio Personality Module (mixer)
*
* This module is used by Audio Drivers that wish to have the audio(7I)
* the mixer(7I) semantics are a superset of the audio(7I) semantics. Either
* semantics may be enforced, depending on the mode of the mixer, AM_MIXER_MODE
* or AM_COMPAT_MODE, respectively. The initial mode is set by the Audio
* Driver and may be changed on the fly, even while playing or recording
* audio. When mixing is enabled multiple play and record streams are allowed,
* and new ioctls are also available. Some legacy applications may not behave
* well in this mode, thus the availability of the compatibility mode.
*
* In addition to providing two sets of semantics, this module also supports
* two types of audio Codecs, those that have a single play and record stream
* (traditional Codecs) and those that allow multiple play and record streams
* (multi-stream Codecs). Because multi-streaming Codecs must do sample rate
* conversion in hardware in order to mix, the audio stream is not sample rate
* converted. However, for traditional Codecs the audio streams must be sample
* rate converted.
*
* The mixer supports a number of data formats on all hardware devices. To do
* this all incoming audio is converted to a canonical format, which is 16-bit
* linear PCM. This audio is held in 32-bit integers, which allows it to be
* mixed with other streams without losing data due to overflowing as would
* happen if it was stored as a 16-bit short. It is also converted to mono or
* stereo to match the hardware. When audio is played it is converted to the
* format the hardware supports.
*
* Once the hardware format is set it is never changed again, except for the
* sample rate.
*
* The following formats are supported:
* 16-bit linear PCM, mono and stereo
* 8-bit linear PCM, mono and stereo
* 8-bit u-law, mono and stereo
* 8-bit a-law, mono and stereo
*
* In order to present a uniform view of audio there are three different
* views of the hardware.
* 1. The true configuration of the audio device. This view is not
* visible by applications. However it is used by the mixer to properly
* format audio for the device.
* 2. The COMPAT mode view. The is the view AUDIOCTL channels get when
* there aren't any AUDIO channels open in the application. This is
* called the master view. The reason this is not the true hardware view
* is because some hardware cannot support all formats. For example,
* many devices do not support u-law or a-law. The mixer can do this
* translation. However it would be confusing to show apps that the
* device was really set to linear PCM.
* 2. The MIXER mode view. This is the view AUDIO channels and AUDIOCTL
* channels in apps that have an AUDIO channel open see. It is mostly
* virtual. It always reflects how the channel was programmed. This
* becomes the COMPAT view when put into compatibility mode.
*
* Most ioctl()s are executed from a task queue. This gives them their own
* thread and thus they can block without violating the STREAMS blocking rules.
* The task queue is limited to just one thread. Thus these ioctl()s are
* serialized and thus access to the hardware state structures and hardware
* are protected.
*
* NOTE: statep->as_max_chs is set when the audiosup module loads, so we
* don't need to protect it when we access it.
*
* NOTE: All linear PCM is assumed to be signed. Therefore if the device
* only supports unsigned linear PCM we need to translate either
* before we send it to the device or after we take it from the
* device. This way we save each Audio Driver from having to do
* this.
*
*
* The audio mixer source code is broken up into three components:
* am_main.c: module load and STREAMS entry points
* am_ad.c: Audio Driver entry points
* am_ioctl.c: ioctl() code
*
* These routines are called by the audio support module:
* am_open_audio()
* am_open_audioctl()
* am_close_audio()
* am_close_audioctl()
* am_restore_state()
* am_save_state()
* am_rput() - private
* am_rsvc() - private
* am_wput() - private
* am_wsvc() - private
*
* These routines are provided for use by the other mixer source code files:
* am_apply_gain_balance()
* am_convert_int_mono_stereo()
* am_convert_to_int()
* am_send_signal()
* am_update_conv_buffer()
* am_update_src_buffer()
*/
#include <sys/audiovar.h>
/*
* Private primary routines provided by this file. These are provided to the
* audio support module via am_open_audio() and am_open_audioctl().
*/
/*
* Local routine prototypes used only by this file.
*/
size_t *);
/*
* Module Linkage Structures
*/
/* Linkage structure for loadable drivers */
static struct modlmisc mixer_modlmisc = {
&mod_miscops, /* drv_modops */
};
static struct modlinkage mixer_modlinkage =
{
MODREV_1, /* ml_rev */
(void*)&mixer_modlmisc, /* ml_linkage */
NULL /* NULL terminates the list */
};
/*
* Loadable Module Configuration Entry Points
*
*
* _init()
*
* Description:
* Driver initialization, called when driver is first loaded.
*
* Arguments:
* None
*
* Returns:
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int error;
ATRACE("in mixer _init()", 0);
/* standard linkage call */
return (error);
}
ATRACE("mixer _init() successful", 0);
return (error);
} /* _init() */
/*
* _fini()
*
* Description
* Module de-initialization, called when driver is to be unloaded.
*
* Arguments:
* None
*
* Returns:
* mod_remove() status, see mod_remove(9f)
*/
int
_fini(void)
{
int error;
ATRACE("in mixer _fini()", 0);
return (error);
}
return (error);
} /* _fini() */
/*
* _info()
*
* Description:
* Module information, returns information about the driver.
*
* Arguments:
* modinfo *modinfop Pointer to an opaque modinfo structure
*
* Returns:
* mod_info() status, see mod_info(9f)
*/
int
{
int rc;
return (rc);
} /* _info() */
/*
* The public main routines for this file.
*/
/*
* am_open_audio()
*
* Description:
* AUDIO channel specific open() routine. There are lots of rules here,
* depending on audio vs. audioctl and backward compatibility vs. mixer
* mode. Thus mode switching must be frozen, which we do by freezing the
* taskq.
*
* NOTE: In user context so it is okay for memory allocation to sleep.
*
* NOTE: audio(7I) redefines the O_NONBLOCK/O_NDELAY open() flags to also
*
* Arguments:
* queue_t *q Pointer to the read queue
* dev_t *devp Pointer to the device
* int oflag Open flags
* int sflag STREAMS flag
* cred_t *credp Ptr to the user's credential structure
*
* Returns:
* AUDIO_SUCCESS Successfully opened the device
* errno Error number for failed open()
*/
/*ARGSUSED*/
int
{
int ch_flags = 0;
int error;
int i;
int max_chs;
int multi_open = 0;
int mode;
int rc;
/* get the state structure */
"am_open_audio() audio_sup_devt_to_state() failed", 0);
return (EIO);
}
/* this driver does only a conventional open(), i.e., no clone opens */
if (sflag) {
"only conventional open()s are supported");
return (EIO);
}
/*
* that at least one of FREAD or FWRITE is set.
*/
ch_flags |= AUDIO_RECORD;
ATRACE_32("am_open_audio() allocate channel with read limits",
ch_flags);
} else {
}
ch_flags |= AUDIO_PLAY;
ATRACE_32("am_open_audio() allocate channel with write limits",
ch_flags);
} else {
}
return (EINVAL);
}
/* figure out if allocates should sleep or not */
/* get the PID for the process opening the channel */
pid = ddi_get_pid();
/* get pointers to various data structures */
ATRACE("am_open_audio() audio_sup_get_apm_info() failed", 0);
return (EIO);
}
/* see if we've been offlined */
return (EIO);
}
/*
* The hardware may support only play or record and not the other.
* If this is the case and the application asked for the direction
* that isn't supported there's no way it can ever succeed. So we
* fail right away.
*/
if ((wantwrite &&
(wantread &&
ATRACE("am_open_audio() trying to do illegal direction",
return (EINVAL);
}
/*
* The hardware may be limited to simplex operation, i.e., it may
* only play or record at any one time. We make sure we haven't
* asked for something the hardware can't do before we continue.
*/
/* we can never, ever succeed, so fail now */
return (EBUSY);
}
/* we have to freeze the channels while we look at them */
/* make sure we are asking for something we can have */
/* is it okay to block and wait for the hw? */
if (ch_flags & AUDIO_NO_SLEEP) {
return (EBUSY);
}
/*
* Mark all AUDIO ch waiting flags. We may be
* re-marking some, but there may be new chs since
* the last loop through the channels. We also
* mark both directions so that one direction knows
* that the other is waiting.
*/
/* send a signal so other procs will wake up */
/* wait for a channel to be freed */
ATRACE("am_open_audio() simplex signal wakeup",
statep);
/*
* This channel may have had a signal, but
* that doesn't mean any of the other channels
* may proceed. So make sure every channel
* gets another go. We clear the waiting flags
* and then any others loop back and reset if
* needed. That's why we do the cv_broadcast().
*/
return (EINTR);
}
/*
* Normal wakeup, clear the waiting flags. If the
* channels need to wait they'll set the flags. That's
* why we wake all the channels up, so they'll go
* through their loop.
*/
}
}
/*
* Before we go any further we allocate all the memory we'll need.
* We do it now so that we can sleep while not holding any locks
* or freezing the taskq, which would be bad if we have to wait
* a long time.
*/
NULL) {
return (error);
}
/*
* Before we get the rest of the memory we need we make sure that we
* can allocate a channel in the audio driver, if needed. This may
* block for a while, which is why we do it now.
*/
ch_flags) == AUDIO_FAILURE) {
/*
* This should always succeed because we have the
* configuration information. So if we don't there's
* something wrong and we fail.
*/
ATRACE("am_open_audio() ad_setup() failed", 0);
(void) audio_sup_free_ch(chptr);
return (EIO);
}
ATRACE("am_open_audio() ad_setup() succeeded", 0);
/*
* CAUTION: From here on we have to call ad_teardown() to
* free the stream resources in the Audio Driver if
* there is an error.
*
* Now allocate the rest of memory.
*/
/* get an audio structure to use */
/*
* CAUTION: From here on we must free the channel and memory if we
* need to return on an error.
*
* Getting to here means there aren't any conflicts with the hardware,
* so now the semantics of MIXER and COMPAT modes come to play. Because
* these semantics depend on the mixer mode we have to block it from
* changing. We do this by taking control of the taskq. Once we have
* the taskq we know there's no way for the mode to change.
*/
again: /* we loop back to here when cv_wait_sig returns due to a wakeup */
/* check again to see if we've been offlined */
goto error;
}
/*
* CAUTION: We must keep the taskq blocked until we have everything
* done. Otherwise a mode switch will make the state inconsistent.
* Every time we jump to again: the taskq has been released so it
* is possible for ioctl()s to make progress. This also means we
* have to do a complete check every time since the hardware state
* could have changed.
*/
/* freeze the channels after freezing the taskq */
/*
* If in MIXER mode then we look for multiple open()s with the same
* PID. If no other AUDIO channels for the PID then we always succeed.
* If in COMPAT mode we look for any open()s in the same direction.
*/
if (mode == AM_MIXER_MODE) {
/* skip non-audio channels and those not allocated */
continue;
}
/*
* The same process can have separate read and write
* open()s. But it can't have two reads or writes,
* unless multiple open()s per process are allowed.
*/
/* don't need to look any further */
break;
} else if (
/*
* Multiple open()s not supported. So let the
* taskq continue.
*/
/* if O_NDELAY then we just return */
if (ch_flags & AUDIO_NO_SLEEP) {
goto error;
}
/* set waiting flags and wait */
/* send a signal so other procs will wake up */
/*
* Wait for channels to be freed so we can
* try again.
*/
/*
* Signal wakeup, clear waiting flags
* for the PID. We wakeup all waiting
* channels, thus they go through their
* loops and remark waiting.
*/
wantread);
goto error;
}
/*
* Normal wakeup, clear the waiting flags. If
* the channels need to wait they'll set the
* flags. That's why we wake all the channels
* up, so they'll go through their loop.
*/
goto again;
}
}
} else {
/* we can't have two reads or writes at one once */
/*
* Multiple open()s not supported. So let the
* taskq continue.
*/
/* if O_NDELAY then we just return */
if (ch_flags & AUDIO_NO_SLEEP) {
goto error;
}
/* set waiting flags and wait */
wantread);
/* send a signal so other procs will wake up */
/* wait for channels to be freed so we can try again */
/*
* Signal wakeup, clear waiting flags for the
* PID. We wakeup all waiting channels, thus
* they go through their loops and remark
* waiting.
*/
goto error;
}
/*
* Normal wakeup, clear the waiting flags. If the
* channels need to wait they'll set the flags. That's
* why we wake all the channels up, so they'll go
* through their loop.
*/
goto again;
}
}
/*
* If we get here there are no conflicting open()s. However, if in
* MIXER mode then we may be limited by the max number of channels,
* max number of read channels, and the max number of write channels.
* It wasn't easy to check above, so we do it now.
*/
if (mode == AM_MIXER_MODE &&
/* let the taskq continue working */
/* if O_NDELAY then we just return */
if (ch_flags & AUDIO_NO_SLEEP) {
goto error;
}
/* set waiting flags and wait */
wantread);
/* send a signal so other procs will wake up */
/*
* Signal wakeup, clear waiting flags for the PID.
* We wakeup all waiting channels, thus they go
* through their loops and remark waiting.
*/
goto error;
}
/*
* Normal wakeup, clear the waiting flags. If the channels
* need to wait they'll set the flags. That's why we wake all
* the channels up, so they'll go through their loop.
*/
wantread);
goto again;
}
/*
* We have a good channel, all open() semantics pass, so init the ch.
*
* CAUTION: pid isn't filled in until the very end. Otherwise
* other routines that look for AUDIO or AUDIOCTL channels may
* think that the channel is fully allocated and available for
* use.
*/
/* get the minor device for the new channel */
/*
* Setup the channel. We use the audio_info structure allocated above.
* If we are in MIXER mode then we keep this structure and it becomes
* the virtual state. If in COMPAT mode we free it after we set the
* state.
*
* We init both play and record, that way it is always filled in.
* Not all members are filled in, that happens in the ioctl() code.
* Thus we have to init the structure so am_audio_set_info() will
* ignore them.
*/
iptr->output_muted = 0;
} else { /* AM_COMPAT_MODE, physical channel */
}
/* before we setup the hardware we need to init src */
if (wantread) {
}
if (wantwrite) {
}
}
/* set the open flags and increment the counts */
if (wantread) {
}
if (wantwrite) {
stpptr->am_out_chs++;
}
stpptr->am_channels++;
/* setting the hardware can take a long time, so let channels go */
/* setup the hardware */
if (wantread) {
}
if (wantwrite) {
stpptr->am_out_chs--;
}
stpptr->am_channels--;
/* let the taskq continue */
/* free up any sample rate conv. memory we allocated earlier */
if (wantread) {
}
if (wantwrite) {
}
goto error;
}
/*
* From here on we can't fail. If in COMPAT mode we need to clean up
* the hardware reference counts.
*/
if (mode == AM_COMPAT_MODE) {
}
if (wantread) {
}
if (wantwrite) {
}
/*
* For mixer mode we see if there are any AUDIOCTL channels with
* this process. If there are then we need to re-associate them
* to this channel.
*
* NOTE: We must still have the taskq frozen.
*/
if (mode == AM_MIXER_MODE) {
/* we have to keep the channels stable */
/* skip myself, unallocated, and closing channels */
(tchptr->ch_private &&
acp_flags & AM_CHNL_CLOSING)) {
continue;
}
/*
* Skip if different PIDs, including 0. pid is set
* to 0 when am_close_audio*() is entered, so we aren't
* associated with a closing channel.
*/
continue;
}
/* same PID, make sure it's AUDIOCTL */
continue;
}
/*
* Yes! It's possible that the AUDIOCTL ch is
* already attached to an AUDIO ch. If so we
* don't muck with it. From this point it is
* indeterminate as to what happens with
* AUDIOCTL channels. If it isn't we associate
* it with this AUDIO channel.
*/
((am_ch_private_t *)
}
/*
* we don't break because there can be more
* than one AUDIOCTL for any one AUDIO channel.
*/
}
/*
* Above we set Codec info, but we need to set the
* rest of the info in the iptr structure, except for
* hardware related members, which we fill in when
* needed.
*/
}
/* we start out open and empty */
/* we've made it through all the checks, so it's safe to make the dev */
/*
* WARNING: Do this after we can no longer fail or we will have a
* potential memory leak. Also, do it before qprocson().
*/
/* schedule the queue */
qprocson(q);
/*
* Now we can set the pid. We don't need to lock the structure because
* the worst thing that will happen is the channel will be skipped and
* then picked up on the next sweep through the channels. Once this is
* set the other mixer routines will see the channel.
*
* CAUTION: This must be after qprocson(), otherwise other threads will
* try to process STREAMS messages on a partially started stream.
* This will cause a panic. This shows up in am_send_signal().
*/
/* thaw the taskq AFTER setting the pid */
/* MUST be after inc. above - not locked any more, start recording */
if (wantread) {
ATRACE("am_open() record STREAMS water marks adjusted",
0);
#ifdef DEBUG
} else {
ATRACE("am_open() record STREAMS water marks not "
"adjusted", 0);
#endif
}
/* start recording, regardless of mode or Codec type */
/* set before start for mode switch */
/* we don't change pause flag if failed to start */
}
}
return (AUDIO_SUCCESS);
}
(void) audio_sup_free_ch(chptr);
/* tell the Audio Driver to free up ch resources */
(ch_flags & AUDIO_BOTH));
ATRACE("am_open_audio() ad_teardown() returned", 0);
return (rc);
} /* am_open_audio() */
/*
* am_open_audioctl()
*
* Description:
* AUDIOCTL channel specific open() routine. There are lots of rules here,
* but not as bad audio am_open_audio(). We don't worry about whether the
* channel is opened for read or writing as we don't read() or write()
* the device.
*
* How AUDIOCTL channels are allocated:
* COMPAT MODE
* always points to the hardware
* MIXER MODE
* if no AUDIO channels open() for the process then points to
* the hardware
* if one AUDIO channel open() for the process then point to that
* channel, thus it is virtual
* if more than one AUDIO channel open() for the process then
* point to the first AUDIO channel, this includes multiple open()s
* as well as one for read and one for write, which isn't the same
* as a multiple open()
*
* NOTE: In user context so it is okay for memory allocation to sleep.
*
* Arguments:
* queue_t *q Pointer to the read queue
* dev_t *devp Pointer to the device
* int oflag Open flags
* int sflag STREAMS flag
* cred_t *credp Ptr to the user's credential structure
*
* Returns:
* AUDIO_SUCCESS Successfully opened the device
* errno Error number for failed open()
*/
/*ARGSUSED*/
int
{
am_ch_private_t *chpptr = 0;
int ch_flags;
int error;
int i;
int max_chs;
int mode;
/* get the state structure */
"am_open_audioctl() audio_sup_devt_to_state() failed", 0);
return (EIO);
}
/* this driver does only a conventional open(), i.e., no clone opens */
if (sflag) {
"only conventional open()s are supported");
return (EIO);
}
/* figure out if allocates should sleep or not */
/* get the PID for the process opening the channel */
pid = ddi_get_pid();
/* get pointers to various data structures */
ATRACE("am_open_audioctl() audio_sup_get_apm_info() failed", 0);
return (EIO);
}
/* see if we've been offlined */
return (EIO);
}
/*
* Before we go any further we allocate the channel and all memory
* that we'll need. That way we don't sleep holding any locks or
* the taskq.
*/
NULL) {
return (error);
}
/*
* CAUTION: We must keep the taskq blocked until we have everything
* done. Otherwise a mode switch will make the state inconsistent.
*/
/* freeze channel allocation */
/* mode may have changed, so re-get */
/*
* We have a good channel, all open() semantics pass, so init the ch.
*
* CAUTION: pid isn't filled in until the very end. Otherwise
* other routines that look for AUDIO or AUDIOCTL channels may
* think that the channel is fully allocated and available for
* use.
*/
/* get the minor device for the new channel */
ATRACE_32("am_open_audioctl() channel number",
/*
* Update the number of AUDIOCTL chs open for the mixer. We
* don't bother updating the read or write counts because
* they don't make sense for AUDIOCTL channels.
*/
stpptr->am_channels++;
/* figure out the audio_info structure, and initialize */
if (mode == AM_MIXER_MODE) {
/* is this AUDIOCTL channel related to an AUDIO ch? */
/* skip myself, unallocated & diff. PID channels */
continue;
}
/* yes, so link the info structs */
/* we lock the channel, not the state */
break;
}
if (i == max_chs) { /* no, so link to HW */
ATRACE("am_open_audioctl() AUDIOCTL not related",
chptr);
}
} else {
/* in COMPAT mode there is only one state structure */
ATRACE("am_open_audioctl() AUDIOCTL: mode == AM_COMPAT_MODE",
chptr);
}
/* we have all the state info, so we can let the state go */
/* we start out open and empty */
/* we've made it through all the checks, so it's safe to make the dev */
/*
* WARNING: Do this after we can no longer fail or we will have a
* potential memory leak. Also, do it before qprocson().
*/
/* schedule the queue */
qprocson(q);
/*
* Now we can set the pid. We don't need to lock the structure because
* the worst thing that will happen is the channel will be skipped and
* then picked up on the next sweep through the channels. Once this is
* set the other mixer routines will see the channel.
*
* CAUTION: This must be after qprocson(), otherwise other threads will
* try to process STREAMS messages on a partially started stream.
* This will cause a panic. This shows up in am_send_signal().
*/
/* thaw the taskq AFTER setting the pid */
return (AUDIO_SUCCESS);
} /* am_open_audioctl() */
/*
* am_close_audio()
*
* Description:
* Close a minor device, returning the minor number to the pool so that
* open(2) may use it again.
*
* chpptr->acp_flags is used to coordinate draining the write queue.
* am_close_audio() sets flags to AM_CHNL_CLOSING. It then waits for the
* flags to have AM_CHNL_EMPTY set by am_get_audio() when all available
* data has been drained and played. If a signal interrupts the draining
* the queue is flushed and the AM_CHNL_OPEN flag is cleared.
* am_get_audio() then ignores this channel until it is open()ed again.
*
* There are a number of rules that have to be followed when closing
* an audio channel. Some of them depend on the state of the mixer.
* Thus we must stop any mode switching. This is done by freezing the
* taskq.
*
* NOTE: When ch_info.info is set and ref_cnt is changed statep->as_lock
* is used and the taskq MUST be frozen. Otherwise a mode switch
* will mess things up.
*
* NOTE: We need to behave differently for a normal close() vs. the user
* app calling exit(). Unfortunately there isn't a DDI compliant
* method for doing this, so we take a look at the current thread
* and see if it's exiting or not. This is how the old diaudio
* module did it.
*
* Arguments:
* queue_t *q Pointer to the read queue
* int flag File status flag
* cred_t *credp Pointer to the user's credential structure
*
* Returns:
* AUDIO_SUCCESS Successfully closed the device
* errno Error number for failed close()
*/
/*ARGSUSED*/
int
{
int dir = 0;
int i;
int max_chs;
int mode;
int tmp_stream;
int was_reading;
int was_writing;
/* mark the channel as in the process of being closed */
/* set the direction for ad_teardown() */
if (was_reading) {
dir |= AUDIO_RECORD;
}
if (was_writing) {
dir |= AUDIO_PLAY;
}
/*
* Wait for queue to drain, unless we were signaled in AUDIO_DRAIN
* or the process is exiting (in which case we use the hack).
*/
if (was_writing) {
/* we now need the info, so protect it by freezing the taskq */
/* release the taskq so it can continue */
ATRACE_32("am_close_audio() not empty",
/* wait for drain to complete */
ATRACE("am_close_audio() signal wakeup",
chptr);
break;
}
ATRACE_32("am_close_audio() normal wakeup",
}
/* clear the writing flag, for mode switching */
chpptr->acp_writing = 0;
} else {
/* release the taskq so it can continue */
}
}
/*
* It is possible an old AUDIO_DRAIN, which was interrupted, is
* still outstanding. So just in case, we see of there's an mblk_t
* hanging around. If so then we send it back. The STREAMS head will
* ignore it if appropriate.
*
* The channel is marked as empty. Thus if the process is killed or
* the cv_wait_sig() returns we force the AUDIO_DRAIN ioctl() to
* return. This is okay since we will be flushing any queued up audio
* and closing the STREAMS q.
*/
/* wait for queued tasks to clear */
/* the mode & info struct mean something ONLY when the mode is frozen */
/*
* Shutdown play and record. We shut down play first because it could
* take a long time to shut down and we don't want to stop recording
* too soon.
*/
/* mark the channel closed so we can shut down the STREAMS queue */
/*
* We shutdown the device if in COMPAT mode or for multi-stream
*/
if (was_writing) {
/* make sure a mode switch wakes up */
}
/* clear the writing flag, for mode switching */
chpptr->acp_writing = 0;
}
if (was_reading) {
}
/* send any recorded data that may still be hanging around */
if (chpptr->acp_rec_mp) {
}
/* clear the reading flag, for mode switching */
chpptr->acp_reading = 0;
}
/* save the gain and balance for the next open() */
if (was_writing) {
}
if (was_reading) {
}
/*
* Clear the pid field - keeps this channel from being used while the
* contents are being freed. But save the pid because we need it later.
*
* CAUTION: The taskq must be blocked before the PID is set to 0.
* Otherwise switching modes can happen in the middle of closing,
* which results in a bad reference count.
*/
/* we are modifying mixer global data, so lock the mixer */
stpptr->am_channels--;
if (was_reading) {
/* turn off capture */
}
}
if (was_writing) {
stpptr->am_out_chs--;
}
/*
* If in MIXER mode the next step for closing the AUDIO channel is to
* fix any AUDIOCTL channels that were pointing to this channel. Even
* if multiple open()s aren't allowed we can still have one channel for
* read and one for write. If there are multiple open()s then there
* could be many channels. Thus we just look for the first AUDIO
* channel with the same PID. If there isn't then we point to the
* hardware.
*
* If in COMPAT mode then we are already pointing to hardware so
* we skip this step.
*/
if (mode == AM_MIXER_MODE) {
/* first find the first AUDIO channel */
/* skip the same and unallocated channels */
continue;
}
/* skip if not AUDIOCTL or different PIDs */
continue;
}
break;
}
/* no match found, so set to hardware */
}
/* next update the AUDIOCTL chs to point to the correct ch */
/* skip the same and unallocated channels */
continue;
}
/* skip if not AUDIOCTL or different PIDs */
continue;
}
ATRACE("am_close_audio() setting AUDIOCTL info",
alt_info);
ATRACE_32("am_close_audio() setting AUDIOCTL info, ch",
i);
/*
* This is the same PID, so fix info pointers. We check
* to see if the AUDIOCTL channel is already pointing
* to alt_info. If so then we don't have to do a thing.
* But if it doesn't then we have to decrement the
* count for where it points and then reset and
* increment the new reference count.
*/
/* the same, so continue */
continue;
}
/* different, so update counters and pointer */
}
}
/* make sure we aren't closing while someone is busy */
while (chpptr->acp_busy_cnt) {
ATRACE_32("am_close_audio() in putnext(), calling cv_wait()",
/* wait for the count to go to 0 */
}
/*
* Mark that qprocsoff() has been called, even though technically it
* hasn't. However, it will eliminate the race condition between
* releasing the lock and the qprocsoff() below.
*/
/* unschedule the queue */
qprocsoff(q);
/*
* Remove the private data from the q, AFTER turning the q off.
* If this is done before qprocsoff() then any STREAMS call
* between these two would find bad qptr data and panic.
*/
/* we have to reget this because it could have changed */
/* take care of references to the audio state structure */
/*
* Need to free the buffer. We don't need to lock because
* only this thread can now be using this channel.
*/
} else {
}
/* release the taskq AFTER we don't care about the mode */
/* free the sample rate conversion routine buffers */
if (codec_type == AM_TRAD_CODEC) {
if (was_writing) {
}
if (was_reading) {
}
}
/*
* Tell the Audio Driver to free up any channel config info.
* This may block, which is why we do it without any locks held
* or the taskq frozen.
*/
ATRACE("am_close_audio() ad_teardown() returned", 0);
/* free all the buffers */
if (chpptr->acp_play_samp_buf) {
chpptr->acp_psb_size = 0;
}
if (chpptr->acp_ch_psrc1) {
}
if (chpptr->acp_ch_psrc2) {
}
chpptr->acp_ch_psrc_siz = 0;
if (chpptr->acp_ch_pconv1) {
}
if (chpptr->acp_ch_pconv2) {
}
chpptr->acp_ch_pconv_siz = 0;
if (chpptr->acp_ch_rsrc1) {
}
if (chpptr->acp_ch_rsrc2) {
}
chpptr->acp_ch_rsrc_siz = 0;
if (chpptr->acp_ch_rconv1) {
}
if (chpptr->acp_ch_rconv2) {
}
chpptr->acp_ch_rconv_siz = 0;
/* send the close signal */
/* wait until the very end to flush */
/* audio_sup_free_ch() requires the info ptr to be NULLed */
/* not much we can do if this fails */
"close_audio() audio_sup_free_ch() error");
}
ATRACE("am_close_audio() successful", 0);
return (AUDIO_SUCCESS);
} /* am_close_audio() */
/*
* am_close_audioctl()
*
* Description:
* Close a minor device, returning the minor number to the pool so that
* open(2) may use it again.
*
* NOTE: When ch_info.info is set and ref_cnt is changed statep->as_lock
* is used and the taskq MUST be frozen. Otherwise a mode switch
* will mess things up.
*
* Arguments:
* queue_t *q Pointer to the read queue
* int flag File status flag
* cred_t *credp Pointer to the user's credential structure
*
* Returns:
* AUDIO_SUCCESS Successfully closed the device
* errno Error number for failed close()
*/
/*ARGSUSED*/
int
{
ATRACE_32("am_close_audioctl() channel number",
/* mark the channel as in the process of being closed */
/* wait for queued tasks to clear */
/* we now need everything stable, so freeze the taskq */
/* mark the channel as being closed */
/*
* Clear the pid field - keeps this channel from being used while the
* contents are being freed.
*
* CAUTION: The taskq must be blocked before the PID is set to 0.
* Otherwise switching modes can happen in the middle of closing,
* which results in a bad reference count.
*/
/* make sure we aren't closing while someone is busy */
while (chpptr->acp_busy_cnt) {
ATRACE_32("am_close_audioctl() in putnext(), calling cv_wait()",
/* wait for the count to go to 0 */
}
/*
* Mark that qprocsoff() has been called, even though technically it
* hasn't. However, it will eliminate the race condition between
* releasing the lock and the qprocsoff() below.
*/
/* unschedule the queue */
qprocsoff(q);
/*
* Remove the private data from the q, AFTER turning the q off.
* If this is done before qprocsoff() then any STREAMS call
* between these two would find bad qptr data and panic.
*/
/* we are modifying mixer global data, so lock the mixer */
stpptr->am_channels--;
/*
* Unlike AUDIO channels the reference count can never be 1. That's
* because AUDIOCTL channels in COMPAT mode always increment the
* count, as they do in MIXER mode when the process doesn't have an
* AUDIO channel open as well. If in MIXER mode with an AUDIO channel
* in the same process then the AUDIO channels reference count is
* used, not the hardware.
*/
/* release the taskq AFTER we don't care about the mode */
#ifdef DEBUG
#endif
/* send the close signal */
/* audio_sup_free_ch() requires the info ptr to be NULLed */
/* not much we can do if this fails */
"close_audioctl() audio_sup_free_ch() error");
}
ATRACE("am_close_audioctl() successful", 0);
return (AUDIO_SUCCESS);
} /* am_close_audioctl() */
/*
* am_restore_state()
*
* Description:
* Restore the device's hardware state and restart playing and recording.
* If am_save_state() was called then we have the taskq frozen. Otherwise
* it isn't and we have to freeze it. That way hot unplug and hot plug
* events won't mess with open(), close(), or ioctl().
*
* Arguments:
* audio_state_t *statep Ptr to the dev instance's state
* audio_apm_info_t *apm_infop Ptr to the APM's state info
* int dir Direction to restore
*
* Returns:
* AUDIO_SUCCESS State restored and restarted
* AUDIO_FAILURE State not restored or restart failed
*/
int
{
int doread; /* device supports record */
int dowrite; /* device supports play */
int psr;
int rsr;
int rc = AUDIO_FAILURE;
/* figure out the direction, am_attach() already did the sanity ck */
dowrite =
(dir & AUDIO_PLAY);
doread =
(dir & AUDIO_RECORD);
return (AUDIO_FAILURE);
}
/* freeze the taskq */
/*
* Set state saved in preparation for restore. We set this because it
* is possible that am_save_state() was not called first, so this
* simulates this call. Don't both with a check if it is set, this
* wastes more time than just setting.
*/
/*
* CAUTION: Keep the calls the same as in am_attach(). There may be
* order dependencies and once the audio driver works we don't
* want to break it if we change the order.
*
* Set the play format and gain, if present.
*/
if (dowrite) {
"couldn't set play data format: %d %d %d %d",
goto error;
}
"am_restore_state() couldn't set play gain");
goto error;
}
}
/* set the record format and gain, if present */
if (doread) {
"am_restore_state() "
"couldn't set record data format: %d %d %d %d",
goto error;
}
/* set the gains */
"am_restore_state() couldn't set record gain");
goto error;
}
}
/* now set the ports, monitor gain, etc. */
if (dowrite) {
"am_restore_state() couldn't set play port: 0x%x",
goto error;
}
}
if (doread) {
"am_restore_state() couldn't set record port: 0x%x",
goto error;
}
}
AM_NO_SERIALIZE) == AUDIO_FAILURE) {
"am_restore_state() couldn't set monitor gain: 0x%x",
goto error;
}
"am_restore_state() couldn't set output muted: 0x%x",
goto error;
}
AM_NO_SERIALIZE) == AUDIO_FAILURE) {
"am_restore_state() couldn't set mic boost: 0x%x",
goto error;
}
/*
* Restart play and record, if there are any apps that have the
* device open for the direction. If not then we don't waste time
* restarting.
*
* It is legal for a play restart calls to fail. This can happen
* when there's no audio to play. If it does fail due to no audio
* then we are already stopped and the active flags should be cleared.
* If play is restarted then the active flags should be set. We don't
* check because there is a race condition and with this routine
* and am_wsvc(). The flags will get set correctly very quickly, so
* there's no reason to try to work around this race, which would not
* be easy.
*
* The same is not true for record. If we start record for no recording
* apps then the mixer will turn off the record later after the 1st
* interrupt.
*/
int i;
int restore_dir = 0;
ATRACE("am_restore_state() restarting MS", 0);
i++, chptr++, restore_dir = 0) {
/* lock the channel before we check it out */
/* skip non-AUDIO and unallocated channels */
continue;
}
/* make sure this channel is writing */
/*
* Figure which to restart for this channel. We do it
* this way so we can free the mutex fore calling the
* device's start routines.
*/
}
}
if (restore_dir & AUDIO_PLAY) {
ATRACE("am_restore_state() MS restart play",
chptr);
/*
* Don't set active flags because they
* should already be set.
*/
ad_infop, i, AM_NO_SERIALIZE);
}
if (restore_dir & AUDIO_RECORD) {
ATRACE("am_restore_state() MS restart record",
chptr);
/*
* Don't set active flags because they
* should already be set.
*/
ad_infop, i, AM_NO_SERIALIZE);
}
}
} else {
if (dowrite) {
/* do restart */
ATRACE_32("am_restore_state() restarting play TRAD",
stpptr->am_out_chs);
/*
* We don't bother with the individual channel or
* hardware active flags because they should already
* be set.
*/
}
if (doread) {
/* do restart */
ATRACE_32("am_restore_state() restarting record TRAD",
/*
* We don't bother with the individual channel or
* hardware active flags because they should already
* be set.
*/
} else {
}
}
rc = AUDIO_SUCCESS;
/* restore error, thaw that taskq and free flags */
/* wake up a blocked driver access, just in case */
return (rc);
} /* am_restore_state() */
/*
* am_save_state()
*
* Description:
* Flag that we are frozen. This stops calls into the audio driver,
* except for ioctl()s.
*
* We don't care about which direction. Both act the same.
*
* There is a very tiny window of opportunity between taskq_wait()
* and the mutex_enter() where in theory we could get another task added
* to the queue, would execute, and potentially cause problems.
*
* Arguments:
* audio_state_t *statep Ptr to the dev instance's state
* audio_apm_info_t *apm_infop Ptr to the APM's state info
* int dir Direction to save
*
* Returns:
* AUDIO_SUCCESS State restored and restarted
* AUDIO_FAILURE State not restored or restart failed
*/
/*ARGSUSED*/
int
{
/* flag that we're frozen */
return (AUDIO_SUCCESS);
} /* am_save_state() */
/*
* The private main routines for this file.
*/
/*
* am_rput()
*
* Description:
* below this, so this should never be called. But just in case, we
* return.
*
* Arguments:
* queue_t *q Pointer to a queue
* mblk_t *mp Ptr to the msg block being passed to the queue
*
* Returns:
* 0 Always returns 0
*/
/*ARGSUSED*/
static int
{
ATRACE("in am_rput()", q);
ATRACE("am_rput() returning 0", q);
return (0);
} /* am_rput() */
/*
* am_rsvc()
*
* Description:
* below this, so this should never be called. But just in case, we
* return
*
* Arguments:
* queue_t *q Pointer to a queue
*
* Returns:
* 0 Always returns 0
*/
/*ARGSUSED*/
static int
{
ATRACE("in am_rsvc()", q);
/* we always have to drain the queue */
}
ATRACE("am_rsvc() returning 0", q);
return (0);
} /* am_rsvc() */
/*
* am_wput()
*
* Description:
* All messages to the mixer arrive here. We don't support very many
* messages.
* M_DATA Passed on to the write svc() routine
* M_IOCTL Calls am_wioctl() for further processing
* M_IOCDATA Calls am_wiocdata() for further processing
*
* Arguments:
* queue_t *q Pointer to a queue
* mblk_t *mp Ptr to the msg block being passed to the queue
*
* Returns:
* 0 Always returns 0
*/
static int
{
ATRACE("in am_wput()", q);
/* figure out what kind of message we've got */
case M_FLUSH:
return (0);
case M_IOCTL:
case M_IOCDATA:
case M_DATA:
/* make sure the write is on an AUDIO channel */
/* NOT an AUDIO channel, we don't allow write */
ATRACE_32("am_wput() not AUDIO",
goto done;
}
/*
* First, concatenate the message. If in mixer mode
* with a traditional Codec we do sample rate conversion
* on the concatenated buffer before we save the data
* for later use.
*/
} else {
"wput() pullupmsg() failed, sound lost");
}
ATRACE_32("am_wput() putting msg on q done", 0);
/* don't break because then we free a msg we've already freed */
ATRACE_32("am_wput() returning", 0);
return (0);
default:
break;
}
/* if we get here there was some kind of error, so send an M_ERROR */
done:
if (error) {
}
} else {
if (mp) {
}
}
return (0);
} /* am_wput() */
/*
* am_wsvc()
*
* Description:
* Write service routine. By definition, this service routine grabs
* all messages from the queue before it returns.
*
* The only message that we ever get is an M_DATA message, which is
* audio data. The audio data is converted to the canonical data format.
* If we need to sample rate convert then the data is converted.
*
* We also make sure the play DMA engine is running.
*
* Arguments:
* queue_t *q Pointer to a queue
*
* Returns:
* 0 Always returns 0
*/
static int
{
int EOF_count;
int *orig_data;
int *proc_data;
ATRACE("in am_wsvc()", q);
/* we always have to drain the queue */
/* this is an AUDIO channel */
/*
* If this is an EOF marker, 0 size write(), we place an
* empty audio data structure on the data list.
*/
if (size == 0) {
0) == AUDIO_FAILURE) {
"am_wsvc() EOF marker lost");
}
#ifdef FLOW_CONTROL
goto flow;
#else
continue;
#endif
}
/* not an EOF message, so process it */
/* don't let mode switch happen while we sample rate convert */
/* if we are switching modes then return */
ATRACE_32("am_wsvc() switching modes",
/* put the message back on the queue */
/* make sure the queue is off */
/* for AUDIO_DRAIN */
return (0);
}
#ifdef FLOW_CONTROL
goto flow;
#else
continue;
#endif
}
/* save the audio data */
"am_wsvc() data save failed, audio lost");
#ifdef FLOW_CONTROL
goto flow;
#else
continue;
#endif
}
/*
* Mark the channel as busy and not empty, but only if we
* actually processed the message. If that failed then we
* give up on this message and try again on the next one.
*/
#ifdef FLOW_CONTROL
flow:
/* do we need to do flow control? */
/* yes, do flow control */
ATRACE("am_wsvc() flow control enabled, q off", q);
noenable(q); /* keep putq() from enabling */
break;
}
#endif
}
/*
* If !paused make sure the play engine is on.
*
* It is possible during a mode switch that the channel info
* structure can change on us. So we use the channel lock to
* make sure it remains stable.
*/
/* for AUDIO_DRAIN */
/*
* Set the active bits before starting play so a switch mode
* will sleep on the CV.
*/
/* we don't change pause if failed to start */
/*
* If we turn off the hardware then make sure any
* queued up EOF messages go out. This is done in
* am_get_samples(). Hopefully most of the EOFs will
* be caught there. However, especially when in mixer
* mode, it can be difficult to catch them all when
* only EOFs are being written. Doing it again here
* costs very little and we know nothing is lost.
*/
EOF_count = 0;
EOF_count +=
}
EOF_count +=
}
}
/*
* It is possible that we tried to start playing
* while in the middle of the audio driver calling
* am_play_shutdown(). Thus the start would reload
* the transfer engine but it would be shut down by
* the ISR after am_play_shutdown() returns. The
* appropriate flags in the audio driver will keep
* the transfer engine from being shut back down.
* However, if a mode switch happens after
* am_play_shutdown() checks the mode switch flag
* then when the ISR calls am_get_audio() the switch
* mode flag will be set and no audio will ever be
* transferred. Thus playing will be deadlocked with
* the mode switch.
*
* The easiest way to expose this is to run showmetv
* with 1894.mov and do insane mode switching. The
* switch modes CV will never wake up, so we go ahead
* and wake it up. The switch mode code will deal
* with a bogus cv_signal().
*/
}
} else {
}
return (0);
} /* am_wsvc() */
/*
* am_apply_gain_balance()
*
* Description:
* Apply gain and balance to the canonical audio data buffer.
*
* Arguments:
* int *buf Pointer to the canonical audio data
* int samples Number of samples
* int channels MONO or STEREO
* int gain Gain, 0 - 255
* int balance Balance, 0 - 64
*
* Returns:
* void
*/
void
int balance)
{
int l_gain;
int r_gain;
if (channels == AUDIO_CHANNELS_MONO) {
}
} else {
if (balance < AUDIO_MID_BALANCE) {
/* leave l gain alone and scale down r gain */
} else if (balance > AUDIO_MID_BALANCE) {
/* leave r gain alone and scale down l gain */
}
}
}
} /* am_apply_gain_balance() */
/*
* am_convert_int_mono_stereo()
*
* Description:
* Convert a buffer between mono and stereo. Both the source and
* destination buffers are 32-bit integers. The number of samples
* is updated to match the new number of samples.
*
* CAUTION: The calling routine must ensure that the dest is large
* enough for the data, or we'll panic.
*
* Arguments:
* int *src Input data buffer
* int *dest Output data buffer
* int *samples Ptr to he number of samples to convert
* int src_chs Input channels
* int dest_chs Output channels
*
* Returns:
* void
*/
void
int dest_chs)
{
int i;
int val;
/*
* The same size so no translation needed, just copy.
* size = samples * sizeof (int)
*/
/* convert from stereo to mono */
*samples >>= AM_TIMES_2_SHIFT;
for (i = *samples; i--; ) {
/* average the left and right channels */
}
} else {
/* convert from mono to stereo */
for (i = *samples; i--; ) {
}
*samples <<= AM_TIMES_2_SHIFT;
}
} /* am_convert_int_mono_stereo() */
/*
* am_convert_to_int()
*
* Description:
* Convert a buffer of various precisions and encodings into 16-bit
* linear PCM stored in a 32-bit int. If the input is unsigned PCM
* we convert it to signed PCM while converting it.
*
* CAUTION: The calling routine must ensure that the outbuf is large
* enough for the data, or we'll panic.
*
* Arguments:
* void *inbuf Input data buffer
* int *outbuf Output data buffer
* int samples The number of samples to convert
* int precision The precision of the input buffer.
* int encoding The encoding of the input buffer.
* int flags Flags, including AM_PRIV_8/16_TRANS
*
* Returns:
* void
*/
void
{
int i;
if (flags & AM_PRIV_16_TRANS) {
for (i = samples; i--; ) {
}
} else {
for (i = samples; i--; ) {
}
}
} else { /* now the hard case, 8-bit */
if (encoding == AUDIO_ENCODING_ULAW) {
/*
* Copy the data into the buf. acp_ch_pptr1,
* char -> int.
*/
for (i = samples; i--; ) {
/* the conv. array does the scaling */
}
} else if (encoding == AUDIO_ENCODING_ALAW) {
/*
* Copy the data into the buf. acp_ch_pptr1,
* char -> int.
*/
for (i = samples; i--; ) {
/* the conv. array does the scaling */
}
} else if (encoding == AUDIO_ENCODING_LINEAR8 ||
(flags & AM_PRIV_8_TRANS)) {
/*
* Copy the data into the buffer with a shift to
* make signed.
*/
for (i = samples; i--; ) {
INT8_MAX) << AM_256_SHIFT;
}
} else {
/*
* Copy the data into the buf. acp_ch_pptr1,
* char -> int.
*/
for (i = samples; i--; ) {
}
}
}
} /* am_convert_to_int() */
/*
* am_reprocess()
*
* Description:
* Process the original data, which am_p_process() created, into sample
* rate converted audio.
*
* Unlike am_p_process() we don't have the opportunity to minimize the
* sample rate conversion processing because we can't take advantage of
* converting between mono and stereo.
*
* It is possible we need to convert original audio that has been
* partially played. So see if we may need to fix the processed
* data pointers.
*
* CAUTION: This routine can be called from interrupt context, so memory
* allocation cannot sleep.
*
* CAUTION: It is not possible to update the sample rate converter in
* this routine because it may be called when switching modes
* and thus the configuration information may be in transition
* and not accurate. Thus the calling routines must ensure the
* the converter is ready.
*
* Arguments:
* audio_ch_t *chptr Pointer to this channel's state info
* audio_data_t *data Original and new processed data struct
*
* Returns:
* AUDIO_SUCCESS Data converted and saved
* AUDIO_FAILURE Data conversion and save failed, audio lost
*/
int
{
int *tmp;
int tmp_size;
int orig_samples;
int src_samples;
int *data_start;
int *conv;
/* don't let mode switch happen while we sample rate convert */
/* make sure we need to process the data */
/* we don't SRC this data */
return (AUDIO_FAILURE);
}
/* figure out the number of samples */
/*
* Make sure we've got good sample rate converter buffers. If we fail
* the calling routine will throw away the audio. There aren't more
* chances to process the audio.
*/
AUDIO_PLAY) == AUDIO_SUCCESS) {
ATRACE("am_reprocess() update_src_buffer() okay", 0);
} else {
ATRACE_32("am_reprocess() calling update_src_buffer() failed",
mode);
return (AUDIO_FAILURE);
}
/* Only do extra work if necessary */
if (chpptr->acp_ch_pbuf_size > 0) {
/* Make sure we have good conversion buffers */
AUDIO_PLAY) == AUDIO_FAILURE) {
"!process(1) couldn't allocate memory, "
"audio lost");
return (AUDIO_FAILURE);
}
/* Set conv pointing to pconv2 */
/* Set data_start pointing to where incoming data begins */
/* We have to copy orig data to data_start */
} else {
/* Set conv pointing to orig data */
}
/* if successful then copy to proc */
if (tmp) {
/* sizeof (int) = 4 */
if (data->adata_proc) {
} else {
ATRACE_32("am_reprocess() couldn't allocate new buffer",
0);
return (AUDIO_FAILURE);
}
} else {
ATRACE_32("am_reprocess() SRC failed", 0);
return (AUDIO_FAILURE);
}
/* see if we need to fix the processed data pointers */
long tmp;
/*
* 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 a sample.
*
* The equation:
* (offset of orig data from start)*(length of proc data)
* --------------------------------------------------------
* (length of orig data)
*/
/*
* tmp is an offset, which must be added to adata_proc to
* get adata_pptr. We mask off adata_pptr so that regardless
* of the format of the data we always are on a sample frame
* boundary.
*/
(tmp & ~AM_MISC_MASK);
} else {
/* on a boundary, so the pointers are easy */
}
/* we don't need to block mode switching now */
return (AUDIO_SUCCESS);
} /* am_reprocess() */
/*
* am_send_signal()
*
* Description:
* This routine is used to send signals back to user land processes.
*
* We always create a prototype signal message, but we use dupb() to
* actually send up the queue.
*
* NOTE: We don't lock the tchptr because the state is frozen thus
* channels can't be allocated or freed. However they can
* have their state updated. The worst that can happen is
* we miss a channel to send a signal on, which isn't that
* bad. And this is only when open()ing or close()ing. Since
* those operations send a signal we're covered.
*
* NOTE: This routine must be called with as_lock held.
*
* Arguments:
* audio_state_t *statep Pointer to the device instance's state
* am_apm_private_t *stpptr Pointer to APM private data
*
* Returns:
* void
*/
void
{
int i;
int max_chs;
/* get the number of chs for this instance */
/* look for AUDIOCTL channels */
/* skip unallocated, non-AUDIOCTL and closing channels */
AM_CHNL_CLOSING)) {
continue;
}
ATRACE("am_send_signal() AUDIOCTL "
"couldn't allocate duplicate message", tchptr);
"signal() couldn't allocate duplicate "
"message to send signal, signal lost");
continue;
}
ATRACE_32("am_send_signal() AM_SIGNAL_ALL_CTL putnext()",
AM_CHNL_CLOSING) == 0);
ATRACE("am_send_signal() "
"AM_SIGNAL_ALL_CTL putnext() done", dup_mp);
}
} /* am_send_signal() */
/*
* am_update_conv_buffer()
*
* Description:
* Make sure the conversion to 32-bit linear PCM buffers are large
* enough. If not then the ones are allocated. Both buffers are always
* set to the same size.
*
* CAUTION: This routine is called from interrupt context, so memory
* allocation cannot sleep.
*
* Arguments:
* audio_ch_t *chptr Pointer to this channel's state info
* size_t size The size we need
* int dir AUDIO_PLAY or AUDIO_RECORD, not both
*
* Returns:
* AUDIO_SUCCESS Buffers okay
* AUDIO_FAILURE Buffer allocation failed
*/
int
{
void *tmp1;
void *tmp2;
if (dir == AUDIO_PLAY) {
if (chpptr->acp_ch_pconv_siz &&
/* buffers large enough */
return (AUDIO_SUCCESS);
}
} else {
if (chpptr->acp_ch_rconv_siz &&
/* buffers large enough */
return (AUDIO_SUCCESS);
}
}
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
if (dir == AUDIO_PLAY) {
if (chpptr->acp_ch_pconv1) {
}
if (chpptr->acp_ch_pconv2) {
}
} else {
if (chpptr->acp_ch_rconv1) {
}
if (chpptr->acp_ch_rconv2) {
}
}
return (AUDIO_SUCCESS);
} /* am_update_conv_buffer() */
/*
* am_update_src_buffer()
*
* Description:
* Make sure the sample rate conversion buffers are large enough. If
* not then the ones are allocated. Both buffers are always set to
* the same size.
*
* CAUTION: This routine is called from interrupt context, so memory
* allocation cannot sleep.
*
* Arguments:
* audio_ch_t *chptr Pointer to this channel's state info
* int samples The number of samples to convert
* uint_t hw_channels Number of hardware channels
* int dir AUDIO_PLAY or AUDIO_RECORD, not both
*
* Returns:
* AUDIO_SUCCESS Buffers okay
* AUDIO_FAILURE Buffer allocation failed, buffers 0ed
*/
int
int dir)
{
void *tmp1;
void *tmp2;
if (dir == AUDIO_PLAY) {
if (chpptr->acp_ch_psrc_siz &&
/* buffers large enough */
return (AUDIO_SUCCESS);
}
} else {
if (chpptr->acp_ch_rsrc_siz &&
/* buffers large enough */
return (AUDIO_SUCCESS);
}
}
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
if (dir == AUDIO_PLAY) {
if (chpptr->acp_ch_psrc1) {
}
if (chpptr->acp_ch_psrc2) {
}
} else {
if (chpptr->acp_ch_rsrc1) {
}
if (chpptr->acp_ch_rsrc2) {
}
}
return (AUDIO_SUCCESS);
} /* am_update_src_buffer() */
/*
* Private routines used only by this file.
*/
/*
* am_flush()
*
* Description:
* Flush the data stream. We handle both play and record. In order
* to flush we have to clear out any play buffers so we have to stop
* and then restart. This applies to both play and record.
*
* Arguments:
* queue_t *q Pointer to a queue
* mblk_t *mp Ptr to the msg block being passed to the queue
*
* Returns:
* void
*/
static void
{
ATRACE("in am_flush()", q);
/* are we flushing the play side? */
ATRACE("am_flush() flushing play side", 0);
/* flush accumulated data */
/*
* Flush the DMA engine and Codec, but only if this
* channel points to the hardware and is an AUDIO
* channel.
*/
/* before we can flush the DMA engine we must stop it */
/* we don't change pause flag, it can be set */
}
/* update the played sample count */
} else {
}
chpptr->acp_psamples_c = 0;
chpptr->acp_psamples_f = 0;
chpptr->acp_psamples_p = 0;
/* by definition we are empty */
ATRACE("am_flush() flushing play done", q);
}
/* now for the record side */
ATRACE("am_flush() flushing record side", 0);
/*
* Flush the DMA engine and Codec, but only if this channel
* points to the hardware and is an AUDIO channel.
*/
/*
* We only flush AUDIO channels, there's nothing on
* AUDIOCTL channels to flush.
*/
/*
* Before we can flush the DMA engine we
* must stop it.
*/
/*
* Flush any partially captured data. This
* needs to be done before we restart recording.
*/
if (chpptr->acp_rec_mp) {
} else {
}
/* restart the record */
if (am_ad_start_record(statep,
AM_SERIALIZE) == AUDIO_FAILURE) {
CE_WARN, "couldn't restart record "
"after flush");
}
} else {
/*
* Not currently recording but we still
* need to flush any partial buffers.
*/
if (chpptr->acp_rec_mp) {
} else {
}
}
}
/* send the flush back up to the STREAMS head */
ATRACE("am_flush() flushing read done", q);
}
if (mp) {
}
ATRACE("am_flush() returning", q);
} /* am_flush() */
/*
* am_p_process()
*
* Description:
* Process the message block into canonical form. If in MIXER mode
* then we also sample rate convert it to match the hardware. When
* done the original data and the converted data, if present, are
* saved in an audio_data_t structure. If sample rate conversion
* fails we don't worry about it. There is a second chance when
* the samples are mixed.
*
* CAUTION: This routine is called from interrupt context, so memory
* allocation cannot sleep.
*
* Arguments:
* audio_ch_t *chptr Pointer to this channel's state info
* void *buf Ptr to the original msg block being
* processed
* size_t size The size of the data buffer in bytes
* int **orig_data Ptr to the original canonical audio ptr
* size_t *orig_size Ptr to the original audio size
* int **proc_data Ptr to the processed canonical audio ptr
* size_t *proc_size Ptr to the processed audio size
*
* Returns:
* AUDIO_SUCCESS Data converted and saved
* AUDIO_FAILURE Data conversion and save failed, audio lost
*/
static int
{
int *dest;
int *src;
int *tmp;
int tmp_size;
int i;
int mode;
int orig_samples;
int src_samples;
int val;
int *conv;
int *data_start;
/* set after we are frozen */
/* figure out the number of samples */
/*
* It is possible that an odd sample write has happened. This can
* happen when the system is very busy and partial STREAMS messages
* are created. Or when an application just plain makes a mistake.
* We chop off the partial sample and press on. This does mean it is
* possible that the next buffer will be out of sync. If it was due to
* the STREAMS message being chopped off then hopefully this just
* finish off the messed up buffer and all will be back in sync. If it
* was because the app is in error, then there's not much hope.
*
* This applies only for stereo, mono can be odd.
*/
if (channels == AUDIO_CHANNELS_STEREO) {
if ((orig_samples % AUDIO_CHANNELS_STEREO) != 0) {
ATRACE("am_p_process() stereo samples chopped",
/* fix */
ATRACE_32("am_p_process() new BUF orig_size",
*orig_size);
ATRACE_32("am_p_process() new BUF orig_samples",
}
}
/*
* If we are going to need to do sample rate conversion then make sure
* we've got good buffers.
*/
/*
* Make sure we have the sample rate converter set up
* properly. If we cannot we try again later.
*/
ATRACE_32("am_p_process() need to update src",
ATRACE_32("am_p_process() update src failed",
} else {
ATRACE_32("am_p_process() update src good",
}
}
/*
* Make sure we've got good sample rate converter buffers.
* However, even if we fail we still proceed. We get a second
* chance later on to do the sample rate conversion.
*/
AUDIO_PLAY) == AUDIO_SUCCESS) {
ATRACE("am_p_process() update_src_buffer() okay", 0);
} else {
/* mark the failure */
"am_p_process() calling update_src_buffer() failed",
mode);
src_samples = 0;
}
} else {
/* setting to 0 also means don't do src */
src_samples = 0;
}
/*
* We convert to 16-bit linear in a 32-bit integer only if buf != NULL.
*
* For converting to canonical format or sample rate conversion
* we need several buffers. We use acp_ch_pconv* for conversion and
* acp_ch_pptr* for sample rate conversion. There are two buffers
* for each, which gives us the space to toggle. We need different
* buffers because we could be converting to 32-bit linear PCM the
* same time we are sample rate converting.
*/
AUDIO_PLAY) == AUDIO_FAILURE) {
"!process(1) couldn't allocate memory, audio lost");
return (AUDIO_FAILURE);
}
/* prepare for failure */
*proc_size = 0;
/*
* Do the conversions. We try to minimize data copies as much as
* possible. There are three possible situations, and how we deal
* with the conversions. We make sure we do the sample rate conversion
* on the minimum amount of audio data
*
* same (mono --> mono or stereo --> stereo)
* am_convert_to_int(buf) --> orig data
* SRC(orig data) --> proc data
*
* mono --> stereo
* am_convert_to_int(buf) --> tmp data
* inline mono2stereo(tmp data) --> orig data
* SRC(tmp data) --> tmp data
* inline mono2stereo(tmp data) --> proc data
*
* stereo --> mono
* am_convert_to_int(buf) --> tmp data
* inline stereo2mono(tmp data) --> orig data
* SRC(orig data) --> proc data
*/
if (channels == hw_channels) {
/* either mono --> mono or stereo --> stereo */
ATRACE("am_p_process() channels == hw_channels", 0);
"!process(2) couldn't allocate"
" memory, audio lost");
return (AUDIO_FAILURE);
}
/* convert to int and place in orig data */
encoding, 0);
/* SRC the data if we need to */
if (src_samples) {
/* only do extra work if necessary */
if (chpptr->acp_ch_pbuf_size > 0) {
/* set conv pointing to pconv1 */
/* set data_start pointing to incoming data */
>> AM_TIMES_4_SHIFT);
/* we have to copy orig data to data_start */
} else {
/* set conv pointing to orig data */
}
/* if successful then update info */
if (tmp) {
/* sizeof (int) = 4 */
if (*proc_data) {
}
}
}
} else if (channels < hw_channels) {
/* mono --> stereo */
/* set data_start pointing to incoming data */
/*
* Convert to int and save. We need this mono data twice,
* as the source for SRC and to be converted to stereo. If
* we converted directly to stereo then we would have to
* do SRC on stereo instead of mono.
*/
encoding, 0);
/* double in size and allocate orig data to save */
*orig_size <<= AM_TIMES_2_SHIFT;
"!process(2) couldn't allocate"
" memory, audio lost");
return (AUDIO_FAILURE);
}
/* convert to stereo */
src = data_start;
for (i = orig_samples; i--; ) {
/* dup to left and right channels */
}
/* SRC the data and then convert to stereo */
if (src_samples) {
/* SRC the old mono data, not the new stereo data */
/* if successful then convert to stereo and save */
if (tmp) {
/* sizeof (int) * 2 = 8 */
if (*proc_data) {
for (i = src_samples; i--; ) {
/* dup to left & right chs */
}
}
}
}
} else {
/* stereo --> mono */
/* set data_start pointing to incoming data */
/* convert to int and then from stereo --> mono */
encoding, 0);
/* divide in half the size and allocate orig data to save */
*orig_size >>= AM_TIMES_2_SHIFT;
"!process(3) couldn't allocate"
" memory, audio lost");
return (AUDIO_FAILURE);
}
/* convert to mono */
src = data_start;
for (i = (orig_samples >> AM_TIMES_2_SHIFT); i--; ) {
/* average the left and right channels */
}
/* SRC the data, which is already mono */
if (src_samples) {
/* if successful then update info */
if (tmp) {
/* sizeof (int) = 4 */
if (*proc_data) {
}
}
}
}
return (AUDIO_SUCCESS);
} /* am_p_process() */
/*
* am_set_waiting()
*
* Description:
* Go through all of the channels. If PID is set then we set the read
* and write waiting flags for that PID only. If PID is 0 then we set
* all of the AUDIO channels. We set the flag to the argument value.
* This lets this routine to be used to both set and clear the flag.
*
* Arguments:
* audio_state_t *statep Pointer to the device instance's state
* pid_t pid The PID to match
* int value Value to set the waiting flag
* boolean_t wantwrite If not 0 then set play.waiting
* boolean_t wantread If not 0 then set record.waiting
*
* Returns:
* void
*/
static void
{
int i;
i++, tchptr++) {
/* skip non-audio channels */
continue;
}
/* if pid then look for that PID, otherwise all others */
continue;
}
/* pid == 0 or pid's match, so set flags */
if (wantwrite) {
}
if (wantread) {
}
}
} /* am_set_waiting() */