audio.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 (c) 1995, 1997, 2000 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* SunOS STREAMS Device-Independent Audio driver
*/
#include <sys/machtypes.h>
#include <sys/audiovar.h>
#include <sys/audiodebug.h>
/*
* Generic AUDIO driver
*
* This file contains the generic routines for handling a STREAMS-based
* audio device. The SPARCstation 1 audio chips, the SPARCstation 3 DBRI
* chips, and the Crystal Semiconductor CS4231 are examples of such devices.
*/
/*
* Local Function Prototypes
*/
/*
* Loadable module support
*/
extern struct mod_ops mod_miscops;
&mod_miscops, "Generic Audio"
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
/*
* Start of audio routines...
*/
/*
* define macros to add and remove aud_cmd_t structures from the free list
*/
#define audio_alloc_cmd(c, d) { \
(d) = (c)->free; \
if ((d) != NULL) \
}
#define audio_free_cmds(v, f, l) { \
(v)->free = (f); \
}
/*
* For debugging, allocate space for the trace buffer
*/
#if defined(AUDIOTRACE) || defined(DBRITRACE)
struct audiotrace *audiotrace_ptr;
int audiotrace_count;
#endif
/*
* Append a command block to a list of chained commands
*/
static void
{
else
}
/*
* audio_delete_cmds - Remove one or more cmds from anywhere on a command
* list. Deletes the commands from headp to lastp inclusive.
*/
static void
{
return;
/*
* Find element directly preceeding headp of list to delete
*/
break;
}
}
} /* audio_delete_cmds */
/*
* Flush a command list.
* Audio i/o must be stopped on the list before flushing it.
*/
void
{
/* Release STREAMS command block */
}
cmdp->lastfragment = 0;
}
/*
* Remove entire command list
*/
} /* audio_flush_cmdlist */
/*
* The ordinary audio device may only have a single reader and single
* writer. However, a special minor device is defined for which multiple
* opens are permitted. Reads and writes are prohibited for this
* 'control' device, but certain ioctls, such as AUDIO_SETINFO, are
* allowed.
*
* Note that this is NOT a generic STREAMS open routine. It must be
* called from a device-dependent open routine that sets 'as'
* appropriately.
*
* NB: as must point to the correct "as"
*/
/*ARGSUSED*/
int
{
int wantwrite;
int wantread;
int newminor;
ATRACEINIT(); /* initialize tracing */
return (EINVAL);
}
/*
* If this is a data device: allow only one reader and one writer.
* If the requested access is busy, return EBUSY or hang until
* close().
*
* Due to a STREAMS bug, exclusive access is tricky to implement.
* A CLONE device is used, but, since a clone open cannot specify
* the minor number, we have to determine what it should be.
*/
/*
* If this device does not carry data, then it is not an
* exclusive open device. Open is very simple for such devices.
*/
if (!ISDATASTREAM(as)) {
/* If this is the first open, init the Streams queues */
qprocson(q);
}
return (0);
}
int notify;
/*
* We can't sleep if this open is not a clone open or
* if the user specified a non-blocking open
*/
return (EBUSY);
}
/*
* Otherwise, hang until device is closed or a signal
* interrupts the sleep.
*
* If this is the first process to request access, signal
* the control device so that it can detect the status
* change. Unfortunately, if the current process has
* opened and enabled signals on the control device, this
* signal will break the sleep we're about to do.
* However, the process should already be prepared for
* this by retrying the open() when EINTR is returned.
*/
} else {
}
}
/*
* NB: ASLOCK is held across this audio_sendsig
*/
if (notify)
return (EINTR);
}
}
if (wantwrite)
else
if (newminor == 0)
return (ENODEV);
/*
* Set up the streams pointers for the requested access modes.
* mark the stream accordingly.
*/
if (wantread) {
}
if (wantwrite) {
}
/*
* Enable queueing on the stream
*/
qprocson(q);
/*
* Signal a state change
*/
/*
* NB: It is the caller's responsibility to return a unique
* minor number if it was a clone open. The generic routine
* cannot know enough to properly construct the minor number.
*/
return (0);
}
/*
* Close the device. Be careful to only dismantle the open parts.
*
* If the device is open for both play and record, q_ptr will have been
* set to as->output_as and as->openflag to (FREAD|FWRITE).
*/
/*ARGSUSED*/
int
{
#if 1 /* XXX */
/* What about draining? */
#endif
/*
* Stop recording
*/
if (reading) {
/* Call the device-dependent close routine */
/* Clear the record-side stream info */
}
if (writing)
}
/*
* Stop playing
*/
if (writing) {
/*
* If there is any data waiting to be written, then sleep
* until it is all gone. Since any process may
* legitimately use the control device to pause output,
* this could take a very long time to complete.
*
* However, if the process was killed we don't want to
* continue draining audio output... we want to stop
* audio output immediately. Since we can't distinguish
* a normal exit from an abnormal termination, we must
* settle for distinguishing exit() from close(),
* flushing the output buffer if the process is exiting.
*
* XXX - Using curthread is promiscuous and
* non-DDI-compliant, but it is the only way to implement
* this at present.
*/
#if defined(TP_LWPEXIT) /* XXX - ON493 and beyond */
#else /* XXX - Jupiter and Mars */
#endif
if (!lwpexit) {
/*
* cv_wait_sig releases and re-acquires
* the audio_stream lock.
*
* XXX - There seems to be cases in which
* the streamhead caught the signal and
* cv_wait_sig does not detect this.
*
* XXX - The driver should be able to
* disable the streamhead 15-sec delay on
* slow closes.
*/
}
}
/* Call the device-dependent close routine */
if (reading)
}
/*
* This is so control streams can be cleaned up
* XXXXXX - check for side effects!
*/
}
/*
* Clear out stream info
*/
#if 0 /* XXX */
#endif
/*
* If closing play or record, signal the control stream
*/
/*
* if either record or play is now closed
* ensure waiting flags get cleared
*/
}
/*
* If this stream is only a control stream, then cleanup is needed.
* Otherwise, the following is redundant.
*/
return (0);
}
/*
* Acknowledge an ioctl, given a reusable message block. If error == 0,
* ACK; else NAK. No data can be returned using this interface (use
* mcopyout()).
*/
void
{
if (q == NULL) {
return;
}
/*
* We wanted to delay the ack of this ioctl, but
* we haven't entered the final stages of negotiation
* yet, so we'll just let the ack happen automatically
* at a later time.
*/
return;
}
return;
}
/* Safety net */
}
}
/*
* Send it off
*/
}
/*
* This routine is provided only for compatibility with `audioens'; new
* code should use mcopyout() instead.
*/
void
{
}
/*
* In addition to the streamio(4) and filio(4) ioctls, the driver accepts:
* AUDIO_DRAIN - hang until output is drained
* AUDIO_GETINFO - get state information
* AUDIO_SETINFO - set state information
*
* Other ioctls may be processed by the device-specific ioctl handler.
*
* If the IOCTL is done on the control stream and channel is not
* return status of all streams (need to use streams data xfer
* stream.
*
* XXX - fix up this definition.
*/
static void
{
long state;
int cmd;
int error;
case M_IOCTL:
/* I_STR ioctls are invalid */
return;
}
state = 0; /* initial state */
break;
case M_IOCDATA: {
/*
* If copy request failed, quit now
*/
/*
* XXX - This does not appear to "wakeup"
* the ioctl.
*/
return;
}
/*
* If the state is -1, then all we need is an ACK
*/
if (state == -1) {
case AUDIOCACTION_WAIT:
return;
case AUDIOCACTION_WAITING:
"audio: ioctl out of sequence");
return;
default:
/* copyout completed */
return;
}
}
break;
} /* case M_IOCDATA */
default:
cmd = 0;
state = (long)0;
} /* switch message type */
switch (cmd) {
case AUDIO_SETINFO: { /* Set state information */
switch (state) {
case 0: /* initial state */
sizeof (audio_info_t), NULL);
return;
default: /* copyin completed state */
error = 0;
/*
* Error indicators and play eof count are updated
* atomically so that processes may reset them safely.
* Sample counts are also updated like this, but are
* handled in the device-specific setinfo routine.
*/
if (error != 0) {
return;
} else if (change == AUDRETURN_CHANGE) {
}
/*
* Restore old values
*/
return;
} /* switch state */
} /* case AUDIO_SETINFO */
case AUDIO_GETINFO: { /* Get state information */
switch (state) {
case 0: /* initial state */
/* Get the user buffer address */
/* Allocate a buffer for the return info structure */
return;
}
/* Set pointer to buffer to receive the info struct */
sizeof (audio_info_t);
/* Update values not stored in the state struct */
/* Copy current state */
NULL);
return;
} /* switch state */
} /* case AUDIO_GETINFO */
break;
case AUDIO_DRAIN: /* Drain output */
/*
* AUDIO_DRAIN must be queued to the service procedure,
* since there is no user context in which to sleep. If
* the request is not for a play device, return an error.
*/
if (!ISPLAYSTREAM(as))
else
return; /* don't acknowledge now */
/* Other ioctls may be handled by the device-specific module */
default:
if (change == AUDRETURN_CHANGE)
} /* switch on command */
} /* audio_ioctl */
/*
* Set all modified fields in the AUDIO_SETINFO structure. Return B_TRUE
* if no error, with 'as' updated to reflect new values. Otherwise,
* returns B_FALSE.
*/
/*ARGSUSED*/
static aud_return_t
{
/*
* Make sure user structure is reasonable.
* Unsigned fields don't need bounds check for < 0
*/
return (AUDRETURN_NOCHANGE); /* if error, return ignored */
}
return (AUDRETURN_NOCHANGE);
}
return (AUDRETURN_NOCHANGE);
}
return (AUDRETURN_NOCHANGE);
}
/*
* Validate and set device-specific values
*/
if (*error != 0)
return (change);
/*
* The following parameters are zeroed on close() of the i/o
* device. Attempts to change them are silently ignored if it is
* closed. Applications should check the info struct returned by
* AUDIO_SETINFO to determine whether they succeeded.
*/
else
}
/* The waiting flags may only be set. close() clears them. */
/*
* changed them. If we called the getinfo routine here,
* then the sample count would get overwritten as well.
*/
}
else
}
/* Get active flag again */
}
return (change);
}
/*
* audio_wput - Stream write queue put procedure. All messages from
* above arrive first in this routine. All control device messages
* should be handled or dismissed here.
*/
int
{
case M_PROTO: /* inline control messages */
/*
* If the M_PROTO message came down a control stream,
* process it now.
*/
if (change == AUDRETURN_CHANGE)
break;
}
if (!ISDATASTREAM(as)) {
break;
}
/*
* An incoming M_PROTO message will only be delivered
* on a RW or RO STREAM.
*/
/*FALLTHROUGH*/
case M_DATA: /* regular data */
/*
* Only queue data on output stream
*/
if (ISPLAYSTREAM(as)) {
/*
* If audio_process_output() has previously
* executed, as it would have during open(), then
* it may have found an empty queue (getq()). If
* the queue was previously found empty, then
* getq() will have set QWANTR in the queue_t and
* this call to putq() will schedule the write
* service procedure, audio_wsrv(). Therefore,
* there is no need for this routine to directly
* call audio_process_output().
*
* XXX - Correct?
*/
qenable(q); /* XXX - Need this? */
} else {
}
break;
case M_IOCTL: /* ioctl */
case M_IOCDATA:
/*
* Most ioctls take effect immediately. audio_ioctl()
* queues AUDIO_DRAIN to the service procedure.
*/
break;
case M_FLUSH: /* flush queues */
/*
* Any stream can flush its queues. We must be careful
* to flush the device command list only when flushing
* the relevant queue.
*/
if (ISPLAYSTREAM(as)) {
qenable(q); /* schedule audio_wsrv() */
}
}
/*
* Don't bother flushing the record buffers if
* this is not the record device or recording is
* already paused (buffers are flushed when
* pausing record).
*/
}
} else {
}
break;
default:
break;
}
return (0);
}
/*
* Write service procedure can find the following on its queue:
* data messages queued for writing
* AUDIO_DRAIN ioctl messages
* Only messages for the audio i/o stream should be found on the queue.
*/
int
audio_wsrv(queue_t *q)
{
return (0);
}
/*
* audio_gc_output_internal - Garbage collect used output buffers.
*
* Returns B_TRUE if application needs to be signaled.
*/
static int
{
int notify;
/*
* Insure that beyond first processed cmd lies a valid done
* command.
*
* NB - Remember error case where entire list is NULLED out
* cmdptr == NULL and so is cmdlast. (pas)
*/
/* Don't look at cmd's still owned by the device */
break;
/*
* Headp points to the first cmd on the list that can
* be deleted.
*
* It may be that the head of the list has been previously
* processed and commands completed since then allow for the
* head to be removed (ouch!).
*
* Everything from headp to lastp will be removed from the
* list.
*/
/*
* If the command has been processed here already,
* it still cannot be reclaimed if it is the last
* done command in the transmit chain.
*/
break;
/*
* Headp is left pointing at the "processed"
* command while cmdp has advanced to the next command.
*/
}
/*
* Check if the last command of the packet is done and do
* nothing if it is still uncompleted.
*/
break;
case M_IOCTL:
/*
* ACK the AUDIO_DRAIN ioctl
*/
/* ignore error after drain */
/*
* Do not delete device's "continuation" command.
*/
/* Delete everything from headp to lastp */
break;
}
/* ignore error after eof */
}
break;
case M_PROTO:
break;
case M_DATA:
/*
* The current aud_cmd has been completely transmitted
* or otherwise processed. Therefore, it is ok to free
* the mblk.
*/
}
/*
* XXX - We will probably have to
* differentiate between "skip" which is
* never seen by the device, and some new
* flag, "error", which is on the device
* IO list.
*/
/*
* There was a transmission error, and
* the packet was marked as "skip" as
* part of discarding it. Transmission
* errors include trying to transmit on
* an inactive channel.
*
* XXX - check for other types of errors,
* does this code still work?
*/
/* Delete this entire aud_cmd */
break;
}
/*
* aud_cmd contained real data.
*/
/*
* If the packet was owned by the device, then it
* may be important for the device specific code
* to retain partial "ownership" of the last
* command so that it can pick up the forward
* pointer if it is told simply to "continue IO".
*
* If the packet following the current packet is
* also marked as "done", then the current packet
* can be completely garbage collected,
* otherwise, the last fragment of the current
* packet must remain on the list.
*/
/*EMPTY*/
/*
* This packet, and the possible
* preceeding "processed" command, can be
* completely gc'ed, which is what headp
* and lastp currently indicate.
*
* XXX - Packets marked skip MAY be on
* the device IO list!
*/
/*
* This packet consists of one fragment
* and there is no completed packet after
* it. It must remain on the chain.
*/
/*
* If there was a previously "processed"
* packet at the head of the list, it can
* now be removed.
*/
} else {
'nada', cmdp);
}
} else {
aud_cmd_t *p;
/*
* This is a multi-fragment packet where
* all but the last fragment can be
* collected.
*
* Set lastp to the penultimate fragment.
*/
for (p = cmdp; p != p->lastfragment;
p = p->next) {
lastp = p;
}
cmdp->lastfragment);
}
break;
default:
}
break;
} /* switch on packet type */
/*
* Delete cmd struct from play list and add to free list
*/
aud_cmd_t *p;
/*
* Be tidy...
*/
p->lastfragment = NULL;
p->iotype = 0;
if (p == lastp)
break;
}
}
} /* for each packet on the command list */
return (notify);
}
/*
* audio_gc_output - perform only the garbage collection phase
* of output processing
*/
void
{
if (audio_gc_output_internal(as))
}
/*
* audio_process_output - Deliver new play buffers to the interrupt routine
* and clean up used buffers.
*/
void
{
int notify;
/* If no write access, don't even bother trying */
if (!ISPLAYSTREAM(as))
return;
/*
* Garbage collect recently emptied output buffers.
*/
/*
* Dequeue messages as long as there are command blocks available.
*/
#if defined(AUDIOTRACE)
}
#endif
/*
* Attach each element of a mblk chain to a command structure.
*/
do {
/*
* Allocate and initialize a command block
*/
/*
* It is assumed that an mblk_t and all of its
* continuation blocks are of the same type.
* The processing of M_DATA messages depends on
* this assumption.
*/
/*
* Do not allocate command blocks for null fragments
* in M_DATA messages.
*/
continue;
}
/*
* If any data block is larger than what the device
* says it can handle, drop it.
*/
}
goto restart;
}
/*
* cmdp gets the next free aud_cmd_t from the
* free list
*/
/*
* Initialize aud_cmd defaults
*/
/*
* AUDIO_DRAIN M_IOCTL, 0 length M_DATA messages
* (EOF), and M_CTL messages go through the
* command path for synchronization but do not
* get played.
*/
} else {
/*
* Non-null M_DATA fragment.
*
* Empty M_DATA fragments have already
* been filtered out.
*/
}
/*
* NB: although the "driver" list is being appended
* here, the "device" list is not being affected.
* Even if the device is currently running, it is not
* allowed to "notice" these new aud_cmd's until
* the AUD_QUEUE() primitive is executed.
*/
/*
* Since the device routine doesn't
* really process non-M_DATA messages,
* it is not important to allocate a
* separate aud_cmd for each one.
*/
/* Exit loop successfully */
} else {
/* Advance to next mblk on chain */
}
/*
* Stop when there are no more mblk fragments
* or when there are no free aud_cmd_t's left.
*/
/*
* If non-zero, head_cmdp points to the start of the first
* aud_cmd representing the first M_DATA fragment that has
* some data in it, or, if not an M_DATA message, the first
* fragment of the mblk.
*
* If head_cmdp is zero, it is because the message was a zero
* length M_DATA message. If we are here, there was at least
* one aud_cmd structure on the free list.
*
* If non-zero, cmdp points to the last aud_cmd used to
* represent the mblk.
*/
/*
* Zero length M_DATA is used as an "Audio Marker".
* It is queued the same as a non-data message.
*/
/*
* The encompassing while loop condition ensures that
* there is at least one aud_cmd structure available.
*/
/*
* XXX - It would be nice to freemsg(mp) now, but
* other code uses the db_type field.
*/
/*
* If mp is not null, it is because we ran out of
* aud_cmd structures. Release any aud_cmd's that
* may have been used and put the mblk back on
* the queue.
*/
/*
* Release mblk and command chains at the tail of
* the list.
*/
/*
* Unless we do something, this packet will block
* the output stream forever. If there is
* nothing else to do, concatenate the parts of
* this message.
*/
/*
* Ensure that there will not be a single
* fragment larger than the max fragment
* size.
*/
#if 1 /* XXX - Broken pullupmsg in Mars */
/*
* For now, assume that we have an MTU of
* maxfrag_size and drop anything larger.
*/
}
#else
'pull', nmp);
} else {
'!pll', nmp);
}
}
#endif
#if 1 /* XXX - Broken pullupmsg in Mars */
/*
* The remaining fragments are smaller
* than the max fragment size, so pull
* them all together.
*/
'pull', nmp);
head_mp);
} else {
/*EMPTY*/
'!pll', nmp);
}
}
#else
/*
* The remaining fragments are smaller
* than the max fragment size, so pull
* them all together.
*/
'pull', nmp);
head_mp);
} else {
'!pll', nmp);
}
}
#endif
/*
* If we were able to massage it, then try
* to process it again.
*/
goto restart;
/*
* Looks like we have to drop it.
*/
break;
} else {
}
/*
* We won't get desperate until there is
* positively no may to get more resources. If
* we're here, then there are still resources
* available that will be freed when more output
* is processed.
*/
break; /* out of while loop queuing commands */
}
/*
* Cmdp still points to the last fragment in the chain.
* Set the lastfragment pointer in each aud_cmd to point
* to the last fragment to simplify future processing.
*/
{
aud_cmd_t *p;
p->lastfragment = cmdp;
}
/*
* The last fragment gets the pointer to the mblk.
*/
/*
* Make the device aware of the new output tasks
*/
} /* while there are audio commands and more STREAMS messages */
/*
* The device-dependent portion of the driver is responsible for
* completing pseudo-IO. It must mark the pseudo-IO as "done"
* and then call audio_gc_output_internal().
*/
/*
* If no messages left, and no data in write buffers, wake up
* audio_close() if necessary. Ignore errors if draining.
*
* XXX - The test for "empty list" is ugly due to the "processed"
* fragment that may be at the end of the list.
*/
/* Only signal when this flag is set for the first time */
}
}
/*
* If a state change occurred, send a signal to the control device
*/
if (notify)
} /* audio_process_output */
/*
* audio_rput- Since putnext() is a macro, it is convenient to have this
* simple read put procedure to keep from having to dequeue packets in
* the service procedure.
*/
/*ARGSUSED*/
int
{
return (0); /*NOTREACHED*/
}
/*
* audio_rsrc - The read service procedure is scheduled when the upstream
* read queue is flushed, to make sure that further record buffers are
* processed.
*
* It can also be scheduled if the driver is flow controlled by
* (canput() == 0)
*/
int
audio_rsrv(queue_t *q)
{
return (0);
}
/*
* audio_gc_input - Collect completed input buffers. Also garbage collect
* unused input buffers when IO has been stopped. Return 1 if read side
* was flow controlled.
*/
static int
{
struct {
} packet;
do {
/*
* Empty fragments should not hurt anyone.
* Packet.head gets set to 1st non-empty
* fragment.
*/
/* XXX - TIDY this up */
/*
* XXX - If this is going to remain a
* subroutine, it should return both
* "notify" and "flow_control".
*/
} else {
/* Set STREAMS end of data */
/*
* If start of packet not set yet, this
* must be it. Don't chain the 1st
* fragment to the "previous".
*/
} else {
/* Chain up current mblk to list */
}
}
break;
/*
* Collect new received packets on driver's readq
* for this aud_stream.
*/
}
/*
* XXX - As soon as we start using DBRI's CDP command for
* the receive side, we will need logic similar to that
* in audio_gc_output_internal() in order to maintain
* an end-of-list command structure for the benefit of
* the device.
*/
} /* for each completed audio command */
break;
}
/*
* Flow control is ok. Send received packet to upper
* module. Don't send zero-length messages to the stream
* head.
*/
} else {
}
}
return (flow_control);
}
/*
* audio_process_input - Send record buffers upstream, if ready. If
* recording is not paused, make sure record buffers are allocated.
*/
void
{
/* If no read access, don't bother even trying */
if (!ISRECORDSTREAM(as))
return;
/*
* Collect finished record buffers and send upstream. If
* recording was paused, all buffers were marked done, even if
* they were unused. The same goes for error condition.
*
* Note: We need to chain up potentially multiple mblks for
* datacomm.
*/
#if defined(AUDIOTRACE)
if (flow_control) {
} else {
}
#endif
/*
* If paused or upstream flow control hit high water, don't
* allocate new record buffers.
*/
/*
* As long as there are free command blocks, allocate new
* buffers for recording.
*/
/*
* Allocate and initialize a command block
*/
/* iotype not used for receive */
/*
* Add it to the cmd chain
*/
}
#if defined(AUDIOTRACE)
/*
* We'll want to know if we ran out of STREAMS buffers
*/
}
#endif
/*
* Queue up dbri cmd after available free cmds are
* chained up and send to device if we have allocated new
* command blocks.
*/
}
}
/*
* If record overflow occurred, send a signal to the control
* device. Only signal when this flag is set for the first time.
*/
}
} /* audio_process_input */
/*
* Send a SIGPOLL up the specified stream.
*
* NB: as always points to the write-side aud_stream
* NB: sometimes called with XXX lock help
*/
void
{
int i;
return;
switch (which) {
case AUDIO_SENDSIG_ALL:
break;
case AUDIO_SENDSIG_EXPLICIT:
break;
default:
return;
}
/*
* Filter out duplicate queues
*/
/*
* Initialize a message to send a SIGPOLL upstream
* Just allocate one and deallocate it before we
* leave this routine. For each message we send
* upstream we simply duplicate our prototype message.
*/
return;
}
for (i = 0; i < 3; i++) {
continue;
/*
* If stream is not open, simply return
*/
continue;
if (!all[i]->signals_okay)
continue;
continue;
}
/*
* Signal the specified stream
*/
}
} /* audio_sendsig */
/*
* The next two routines are used to pause reads or writes. Pause is
* used to temporarily suspend i/o without losing the contents of the
* buffer.
*/
void
{
return;
/*
* When recording is paused, partially filled buffers are sent
* upstream and unused buffers are released. Mark all command
* buffers done and let audio_process_input() handle them.
*
* XXX - There could be a problem here as packets have multiple
* cmds.
*/
/*
* Flush the device's chained command list
*/
/*
* Process partially filled buffer and release the rest
*/
}
void
{
return;
}
}
/*
* The next two routines are called from ioctls to resume paused
*/
void
{
return;
/*
* Must clear pause flag before calling audio_process_input
*/
/*
* audio_process_input() will call the AUD_START routine
*/
}
void
{
return;
/*
* Must clear pause flag before calling audio_process_output
*/
/*
* Queue up output buffers and enable output conversion
*/
}
/*
* audio_trace - for a particular STREAM, copy the STREAMS message
* pointed to by the audio command to the trace queue (if any). the
* dihandle of the command will contain a pointer to the data message and
* the aud_stream will have a pointer to the trace queue. The audio
* command also contains a structure of information pertaining to the
* data message (status, etc).
*/
void
{
/* If trace stream is not open, simply return */
return;
/*
* If STREAM is full, then we just don't send anything. No
* promises are made that this is reliable...
*/
return;
return;
return;
}
/*
* Send the messages upstream
*/
}
/*
* audio_trace_hdr - send only the header part up the trace stream
*/
void
{
/* If trace stream is not open, simply return */
return;
/*
* If STREAM is full, then we just don't send anything. No
* promises are made that this is reliable...
*/
return;
return;
}
/*
* Send the messages upstream
*/
}