/*
 * Copyright 1993 Network Computing Devices, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name Network Computing Devices, Inc. not be
 * used in advertising or publicity pertaining to distribution of this 
 * software without specific, written prior permission.
 * 
 * THIS SOFTWARE IS PROVIDED `AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
 * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
 * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * $Id: audispatch.c,v 1.3 1999/09/05 02:43:11 jon Exp $
 * $NCDId: @(#)audispatch.c,v 1.13 1996/05/07 20:12:46 greg Exp $
 */

#include	"release.h"
#include	"misc.h"
#include	"dixstruct.h"
#include	"servermd.h"			/* VENDOR release & strings */
#include	<audio/audio.h>
#include	<audio/Aproto.h>
#include	"au.h"

extern char     isItTimeToYield;
extern int      (*InitialVector[3]) ();
extern int      (*AuReplySwapVector[256]) ();
extern void     Swap32Write(), AuSwapDeviceAttributesWrite(),
                AuSwapBucketAttributesWrite(), AuSwapSetupPrefix(),
                AuSwapSetup(), AuCopySwapElementWrite(),
                AuSwapElementStateWrite(), AuFreeComponent(),
                AuFreeFlowElements(), AuProcessClockedFlows(),
                AuProcessStateChanges(), AuProcessUnclockedFlows(),
                AuSetInputGainAndLineMode(), AuSetOutputGainAndMode(),
                AuSetFeedbackGain(),
                CloseDownRetainedResources(), WriteEventsToClient(),
                WriteToClient();
extern AuBool   AuChangeElementState(), AuInitDevice(), AuMatchAttributes();
extern int      AuCompileFlow(), AuSetComponentAttributes();
extern void    AuGetOutputGainAndMode(), AuGetFeedbackGain(),
               AuGetInputGain(), AuGetInputMode();

extern RESTYPE  auFlowType,
                auComponentType;
extern FlowPtr  auClockedFlows,
                auUnclockedFlows;
extern ComponentPtr auBuckets,
                auDevices;

static auConnSetupPrefix auSetupPrefix;
auConnSetup auSetup;

static char    *auVendorString;

#define WriteAuSwappedDataToClient(client, size, pbuf, swapper)		       \
{									       \
    (client)->pSwapReplyFunc = (swapper);				       \
    WriteSwappedDataToClient(client, size, pbuf);			       \
}

#define xferCommonAttributes(c, a)					       \
{									       \
    (a).common.value_mask = (c)->valueMask;				       \
    (a).common.changable_mask = (c)->changableMask;			       \
    (a).common.id = (c)->id;						       \
    (a).common.kind = (c)->kind;					       \
    (a).common.use = (c)->use;						       \
    (a).common.format = (c)->format;					       \
    (a).common.num_tracks = (c)->numTracks;				       \
    (a).common.access = (c)->access;					       \
    (a).common.description.type = (c)->description.type;		       \
    (a).common.description.len = (c)->description.len;			       \
}


#define xferDeviceAttributes(d, da)					       \
{									       \
    xferCommonAttributes(d, da);					       \
    (da).device.min_sample_rate = (d)->minSampleRate;			       \
    (da).device.max_sample_rate = (d)->maxSampleRate;			       \
    (da).device.location = (d)->location;				       \
    (da).device.gain = (d)->gain;					       \
    (da).device.line_mode = (d)->lineMode;				       \
    (da).device.num_children = (d)->numChildren;			       \
}

#define xferBucketAttributes(b, ba)					       \
{									       \
    xferCommonAttributes(b, ba);					       \
    (ba).bucket.sample_rate = (b)->sampleRate;				       \
    (ba).bucket.num_samples = (b)->numSamples;				       \
}

/* REQUESTS */

int
ProcAuNotImplemented(client)
ClientPtr       client;
{
    AU_ERROR(AuBadImplementation, 0);
}

int
ProcAuListDevices(client)
ClientPtr       client;
{
    REQUEST(auReq);
    auListDevicesReply rep;
    auDeviceAttributes a,
                   *match;
    ComponentPtr    c;
    int             ndevices,
                    len;

    REQUEST_AT_LEAST_SIZE(auReq);
    len = (stuff->length << 2) - sizeof(auReq) - sizeof(auDeviceAttributes);

    if (len < 0)
	AU_ERROR(AuBadLength, 0);

    match = (auDeviceAttributes *) & stuff[1];

    if (match->common.value_mask & AuCompCommonDescriptionMask)
	if (len != PAD4(match->common.description.len))
	    AU_ERROR(AuBadLength, 0);

    c = auDevices;
    ndevices = len = 0;

    /*
     * first get the number of devices and the length of the variable length
     * data
     */
    while (c)
    {
	c->list = AuMatchAttributes(match, c, &match[1]);

	if (c->list)
	{
	    ndevices++;
	    len += PAD4(c->description.len);

	    if (c->numChildren)
		len += c->numChildren * sizeof(AuDeviceID);
	}

	c = c->next;
    }

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = (sizeof(auDeviceAttributes) * ndevices + len) >> 2;
    rep.num_devices = ndevices;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    c = auDevices;

    while (c)
    {
	if (c->list)
	{
           if (c->kind == AuComponentKindPhysicalInput)
           {
               AuGetInputGain(&c->gain);
               AuGetInputMode(&c->lineMode);
           }
           else if (c->kind == AuComponentKindPhysicalOutput)
		AuGetOutputGainAndMode(&c->gain, &c->lineMode);
           else if (c->kind == AuComponentKindPhysicalFeedback)
               AuGetFeedbackGain(&c->gain);

	    xferDeviceAttributes(c, a);

	    WriteAuSwappedDataToClient(client, sizeof(a), &a,
				       AuSwapDeviceAttributesWrite);
	    (void) WriteToClient(client, c->description.len,
				 c->description.string);

	    if (c->numChildren)
	    {
		bcopy(c->children, c->childSwap,
		      c->numChildren * sizeof(AuDeviceID));
		WriteAuSwappedDataToClient(client,
					c->numChildren * sizeof(AuDeviceID),
					   c->childSwap, Swap32Write);
	    }

	}

	c = c->next;
    }

    return client->noClientException;
}

int
ProcAuGetDeviceAttributes(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    auGetDeviceAttributesReply rep;
    auDeviceAttributes a;
    ComponentPtr    c;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (!(c = (ComponentPtr) LookupIDByType(stuff->id, auComponentType)))
	AU_ERROR(AuBadDevice, stuff->id);

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = (sizeof(auDeviceAttributes) + PAD4(c->description.len) +
		  PAD4(c->numChildren * sizeof(AuDeviceID))) >> 2;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    if (c->kind == AuComponentKindPhysicalInput)
    {
       AuGetInputGain(&c->gain);
       AuGetInputMode(&c->lineMode);
    }
    else if (c->kind == AuComponentKindPhysicalOutput)
	AuGetOutputGainAndMode(&c->gain, &c->lineMode);
    else if (c->kind == AuComponentKindPhysicalFeedback)
       AuGetFeedbackGain(&c->gain);

    xferDeviceAttributes(c, a);

    WriteAuSwappedDataToClient(client, sizeof(a), &a,
			       AuSwapDeviceAttributesWrite);
    (void) WriteToClient(client, c->description.len, c->description.string);

    if (c->numChildren)
    {
	bcopy(c->children, c->childSwap, c->numChildren * sizeof(AuDeviceID));
	WriteAuSwappedDataToClient(client, c->numChildren * sizeof(AuDeviceID),
				   c->childSwap, Swap32Write);
    }

    return client->noClientException;
}

int
ProcAuSetDeviceAttributes(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    ComponentPtr    c;
    auDeviceAttributes *a;
    int             len,
                    status;

    REQUEST_AT_LEAST_SIZE(auResourceReq);

    if (!(c = (ComponentPtr) LookupIDByType(stuff->id, auComponentType)))
	AU_ERROR(AuBadDevice, stuff->id);

    len = (stuff->length << 2) - sizeof(auResourceReq) -
	sizeof(auDeviceAttributes);

    if (len < 0)
	AU_ERROR(AuBadLength, 0);

    a = (auDeviceAttributes *) & stuff[1];

    /* see if any illegal changes are being attempted */
    if (a->common.value_mask & ~c->changableMask)
	AU_ERROR(AuBadValue, a->common.value_mask);

    if (a->common.value_mask & AuCompCommonDescriptionMask)
	if (len != PAD4(a->common.description.len))
	    AU_ERROR(AuBadLength, 0);

    if ((status = AuSetComponentAttributes(a, c, NULL)) != AuSuccess)
	return status;

    if (c->kind == AuComponentKindPhysicalInput)
	AuSetInputGainAndLineMode(c->gain, c->lineMode);
    else if (c->kind == AuComponentKindPhysicalOutput)
	AuSetOutputGainAndMode(c->gain, c->lineMode);
    else if (c->kind == AuComponentKindPhysicalFeedback)
       AuSetFeedbackGain(c->gain);

    return AuSuccess;
}

int
ProcAuCreateBucket(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    ComponentPtr    c;
    auBucketAttributes *a;
    int             len,
                    status;
    AuUint32           dataSize;
    extern int      auMinibufSamples;

    REQUEST_AT_LEAST_SIZE(auResourceReq);
    len = (stuff->length << 2) - sizeof(auResourceReq) -
	sizeof(auBucketAttributes);

    if (len < 0)
	AU_ERROR(AuBadLength, 0);

    a = (auBucketAttributes *) & stuff[1];

    /* see if we've got all the values we want */
    if (a->common.value_mask !=
     (AuCompBucketAllMasks & ~(AuCompCommonKindMask | AuCompCommonUseMask)))
	AU_ERROR(AuBadValue, a->common.value_mask);

    if (a->common.value_mask & AuCompCommonDescriptionMask)
	if (len != PAD4(a->common.description.len))
	    AU_ERROR(AuBadLength, 0);

    dataSize = a->bucket.num_samples * sizeofFormat(a->common.format) *
	a->common.num_tracks;

    if (!(c = (ComponentPtr) aucalloc(1, PAD4(sizeof(ComponentRec)) +
				      PAD4(dataSize) +
				      PAD4(a->common.description.len))))
	AU_ERROR(AuBadAlloc, 0);

    c->kind = AuComponentKindBucket;
    c->use = AuComponentUseImportMask | AuComponentUseExportMask;
    c->valueMask = AuCompBucketAllMasks;
    c->data = (AuUint8 *) c + PAD4(sizeof(ComponentRec));
    c->dataSize = dataSize;
    c->dataEnd = c->data + c->dataSize;
    c->read = c->write = c->data;
    c->physicalDeviceMask = NotAPhysicalDevice;
    c->description.string = (char *) c->data + PAD4(dataSize);
    c->minibufSize = auNativeBytesPerSample * auMinibufSamples *
	a->common.num_tracks;
    c->destroyed = AuFalse;
    c->bytesPerSample = a->common.num_tracks * sizeofFormat(a->common.format);

    AddToLinkedList(auBuckets, c);

    if ((status = AuSetComponentAttributes(a, c, &a[1])) != AuSuccess)
    {
	AuFreeComponent(c);
	return status;
    }

    if (!AddResource(stuff->id, auComponentType, c))
	AU_ERROR(AuBadAlloc, 0);

    return AuSuccess;
}

int
ProcAuDestroyBucket(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    ComponentPtr    c;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (!(c = (ComponentPtr) LookupIDByType(stuff->id, auComponentType)))
	AU_ERROR(AuBadBucket, stuff->id);

    if (!(c->access & AuAccessDestroyMask) &&
	CLIENT_ID(stuff->id) != client->index)
	AU_ERROR(AuBadAccess, stuff->id);

    c->destroyed = AuTrue;
    FreeResource(stuff->id, AuFalse);

    return AuSuccess;
}

int
ProcAuListBuckets(client)
ClientPtr       client;
{
    REQUEST(auReq);
    auListBucketsReply rep;
    auBucketAttributes a,
                   *match;
    ComponentPtr    c;
    int             nbuckets,
                    len;

    REQUEST_AT_LEAST_SIZE(auReq);
    len = (stuff->length << 2) - sizeof(auReq) - sizeof(auBucketAttributes);

    if (len < 0)
	AU_ERROR(AuBadLength, 0);

    match = (auBucketAttributes *) & stuff[1];

    if (match->common.value_mask & AuCompCommonDescriptionMask)
	if (len != PAD4(match->common.description.len))
	    AU_ERROR(AuBadLength, 0);

    c = auBuckets;
    nbuckets = len = 0;

    /*
     * first get the number of buckets and the length of the variable length
     * data
     */
    while (c)
    {
	c->list = !c->destroyed &&
	    (CLIENT_ID(c->id) == client->index ||
	     c->access & AuAccessListMask) &&
	    AuMatchAttributes(match, c, &match[1]);

	if (c->list)
	{
	    nbuckets++;
	    len += PAD4(c->description.len);
	}

	c = c->next;
    }

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = (sizeof(auBucketAttributes) * nbuckets + len) >> 2;
    rep.num_buckets = nbuckets;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    c = auBuckets;

    while (c)
    {
	if (c->list)
	{
	    xferBucketAttributes(c, a);

	    WriteAuSwappedDataToClient(client, sizeof(a), &a,
				       AuSwapBucketAttributesWrite);
	    (void) WriteToClient(client, c->description.len,
				 c->description.string);
	}

	c = c->next;
    }

    return client->noClientException;
}

int
ProcAuGetBucketAttributes(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    auGetBucketAttributesReply rep;
    auBucketAttributes a;
    ComponentPtr    c;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (!(c = (ComponentPtr) LookupIDByType(stuff->id, auComponentType)))
	AU_ERROR(AuBadBucket, stuff->id);

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = (sizeof(auBucketAttributes) + PAD4(c->description.len)) >> 2;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    xferBucketAttributes(c, a);

    WriteAuSwappedDataToClient(client, sizeof(a), &a, AuSwapBucketAttributesWrite);
    (void) WriteToClient(client, c->description.len, c->description.string);

    return client->noClientException;
}

int
ProcAuCreateFlow(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    FlowPtr         flow;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (!(flow = (FlowPtr) aucalloc(1, sizeof(FlowRec))))
	AU_ERROR(AuBadAlloc, 0);

    flow->flowId = stuff->id;
    flow->state = flow->pendingState = AuStateStop;
    flow->compiled.freeComponents = AuTrue;

    if (!AddResource(stuff->id, auFlowType, flow))
	AU_ERROR(AuBadAlloc, 0);

    return AuSuccess;
}

int
ProcAuDestroyFlow(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    FlowPtr         flow;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (!(flow = (FlowPtr) LookupIDByType(stuff->id, auFlowType)))
	AU_ERROR(AuBadFlow, stuff->id);

    FreeResource(stuff->id, AuFalse);

    return AuSuccess;
}

int
ProcAuGetElements(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);
    auGetElementsReply rep;
    FlowPtr         flow;
    int             i;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (!(flow = (FlowPtr) LookupIDByType(stuff->id, auFlowType)))
	AU_ERROR(AuBadFlow, stuff->id);

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = (sizeof(auElement) * flow->numElements + flow->varLen) >> 2;
    rep.flow = flow->flowId;
    rep.clocked = flow->clocked;
    rep.num_elements = flow->numElements;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    for (i = 0; i < flow->numElements; i++)
	WriteAuSwappedDataToClient(client,
			       flow->elements[i].varLen + sizeof(auElement),
				   flow->elements[i].raw,
				   AuCopySwapElementWrite);

    return AuSuccess;
}

static int      numDefaultActions[] =
{
    5,						/* AuElementTypeImportClient */
    0,						/* AuElementTypeImportDevice */
    3,						/* AuElementTypeImportBucket */
    3,						/* AuElementTypeImportWaveForm
						 * */
    0,						/* AuElementTypeImportRadio */
    0,						/* AuElementTypeBundle */
    0,						/* AuElementTypeMultiplyConsta
						 * nt */
    0,						/* AuElementTypeAddConstant */
    0,						/* AuElementTypeSum */
    5,						/* AuElementTypeExportClient */
    0,						/* AuElementTypeExportDevice */
    3,						/* AuElementTypeExportBucket */
    0,						/* AuElementTypeExportRadio */
    0,						/* AuElementTypeExportMonitor */
};

#define ADD_VAR(n)							      \
{									      \
    AuUint8 *_t = (AuUint8 *) el;					      \
									      \
    varLen += (n);							      \
    _t += (n);								      \
    el = (auElement *) _t;						      \
}

#define COMP_ACTIONS(num)						       \
{									       \
    numActions += (num) ? (num) : numDefaultActions[el->type];		       \
    ADD_VAR((num) * sizeof(auElementAction));				       \
}

#define FREE_FLOW_ERROR(e, v)						       \
{									       \
    AuFreeFlowElements(flow);						       \
    AU_ERROR(e, v);							       \
}

int
ProcAuSetElements(client)
ClientPtr       client;
{
    REQUEST(auSetElementsReq);
    FlowPtr         flow;
    int             len,
                    i,
                    varLen,
                    numActions,
                    status;
    auElement      *el;
    FlowElementPtr  e;
    AuUint8          *p;
    ActionPtr       a;

    REQUEST_AT_LEAST_SIZE(auSetElementsReq);

    if (!(flow = (FlowPtr) LookupIDByType(stuff->flow, auFlowType)))
	AU_ERROR(AuBadFlow, stuff->flow);

    el = (auElement *) & stuff[1];

    /* compute length of variable data and do some error checking */
    for (i = varLen = numActions = 0; i < stuff->numElements; i++, el++)
	switch (el->type)
	{
	    case AuElementTypeImportClient:
		COMP_ACTIONS(el->importclient.actions.num_actions);
		break;
	    case AuElementTypeImportDevice:
		COMP_ACTIONS(el->importdevice.actions.num_actions);
		break;
	    case AuElementTypeImportBucket:
		COMP_ACTIONS(el->importbucket.actions.num_actions);
		break;
	    case AuElementTypeImportWaveForm:
		COMP_ACTIONS(el->importwaveform.actions.num_actions);
		break;
	    case AuElementTypeBundle:
		ADD_VAR(el->bundle.num_inputs * sizeof(auInputTrack));
		break;
	    case AuElementTypeMultiplyConstant:
		break;
	    case AuElementTypeAddConstant:
		break;
	    case AuElementTypeSum:
		ADD_VAR(PAD4(el->sum.num_inputs * sizeof(CARD16)));
		break;
	    case AuElementTypeExportClient:
		COMP_ACTIONS(el->exportclient.actions.num_actions);
		break;
	    case AuElementTypeExportDevice:
		COMP_ACTIONS(el->exportdevice.actions.num_actions);
		break;
	    case AuElementTypeExportBucket:
		COMP_ACTIONS(el->exportbucket.actions.num_actions);
		break;
	    case AuElementTypeExportMonitor:
		break;
	    default:
		AU_ERROR(AuBadElement, el->type);
	}

    /* size of element list */
    len = (stuff->length << 2) - sizeof(auSetElementsReq);

    /* do a length check */
    if (varLen != len - stuff->numElements * sizeof(auElement))
	AU_ERROR(AuBadLength, 0);

    /* get rid of the existing elements */
    if (flow->elements)
	AuFreeFlowElements(flow);

    /* alloc some space */
    if (!(p = (AuUint8 *)
	  aualloc(PAD4(stuff->numElements * sizeof(FlowElementRec)) +
		  PAD4(len) +
		  PAD4(numActions * sizeof(ActionRec)))))
	AU_ERROR(AuBadAlloc, 0);

    flow->elements = (FlowElementPtr) p;
    p += PAD4(stuff->numElements * sizeof(FlowElementRec));

    flow->raw = (auElement *) p;
    p += PAD4(len);

    a = (ActionPtr) p;

    /* copy the raw data */
    bcopy(&stuff[1], flow->raw, len);

    flow->clocked = stuff->clocked;
    flow->numElements = stuff->numElements;
    flow->state = AuStateStop;
    flow->varLen = varLen;

    el = flow->raw;
    e = flow->elements;

    for (i = 0; i < stuff->numElements; i++, e++, a += numActions)
    {
	int             copyActions;
	AuUint8 	*tmp;

	varLen = numActions = copyActions = 0;

	e->client = (AuPointer) client;
	e->raw = el;
	e->elementNum = i;
	e->compiled = AuFalse;
	e->setup = AuFalse;
	e->flow = flow;
	e->isComponent = AuTrue;
	e->stateChange = AuFalse;
	e->state = e->prevState = AuStateStop;
	e->refCnt = 0;
	e->parmsChanged = AuFalse;

	switch (el->type)
	{
	    case AuElementTypeImportClient:
		if (!(copyActions = el->importclient.actions.num_actions))
		{
		    numActions = numDefaultActions[el->type];

		    a[0].triggerState = AuStatePause;
		    a[0].triggerPrevState = AuStateAny;
		    a[0].triggerReason = AuReasonAny;
		    a[0].action = AuElementActionSendNotify;

		    a[1].triggerState = AuStateStop;
		    a[1].triggerPrevState = AuStateAny;
		    a[1].triggerReason = AuReasonAny;
		    a[1].action = AuElementActionSendNotify;

		    a[2].triggerState = AuStatePause;
		    a[2].triggerPrevState = AuStateAny;
		    a[2].triggerReason = AuReasonAny;
		    a[2].action = AuElementActionChangeState;
		    a[2].flow = flow;
		    a[2].elementNum = AuElementAll;
		    a[2].newState = AuStatePause;

		    a[3].triggerState = AuStateStart;
		    a[3].triggerPrevState = AuStatePause;
		    a[3].triggerReason = AuReasonAny;
		    a[3].action = AuElementActionChangeState;
		    a[3].flow = flow;
		    a[3].elementNum = AuElementAll;
		    a[3].newState = AuStateStart;

		    a[4].triggerState = AuStateStop;
		    a[4].triggerPrevState = AuStateAny;
		    a[4].triggerReason = AuReasonAny;
		    a[4].action = AuElementActionChangeState;
		    a[4].flow = flow;
		    a[4].elementNum = AuElementAll;
		    a[4].newState = AuStateStop;
		}
		break;
	    case AuElementTypeImportDevice:
		copyActions = el->importdevice.actions.num_actions;
		break;
	    case AuElementTypeImportBucket:
		if (!(copyActions = el->importbucket.actions.num_actions))
		{
		    numActions = numDefaultActions[el->type];

		    a[0].triggerState = AuStatePause;
		    a[0].triggerPrevState = AuStateAny;
		    a[0].triggerReason = AuReasonAny;
		    a[0].action = AuElementActionSendNotify;

		    a[1].triggerState = AuStateStop;
		    a[1].triggerPrevState = AuStateAny;
		    a[1].triggerReason = AuReasonAny;
		    a[1].action = AuElementActionSendNotify;

		    a[2].triggerState = AuStateStop;
		    a[2].triggerPrevState = AuStateAny;
		    a[2].triggerReason = AuReasonAny;
		    a[2].action = AuElementActionChangeState;
		    a[2].flow = flow;
		    a[2].elementNum = AuElementAll;
		    a[2].newState = AuStateStop;
		}
		break;
	    case AuElementTypeImportWaveForm:
		if (!(copyActions = el->importwaveform.actions.num_actions))
		{
		    numActions = numDefaultActions[el->type];

		    a[0].triggerState = AuStatePause;
		    a[0].triggerPrevState = AuStateAny;
		    a[0].triggerReason = AuReasonAny;
		    a[0].action = AuElementActionSendNotify;

		    a[1].triggerState = AuStateStop;
		    a[1].triggerPrevState = AuStateAny;
		    a[1].triggerReason = AuReasonAny;
		    a[1].action = AuElementActionSendNotify;

		    a[2].triggerState = AuStateStop;
		    a[2].triggerPrevState = AuStateAny;
		    a[2].triggerReason = AuReasonAny;
		    a[2].action = AuElementActionChangeState;
		    a[2].flow = flow;
		    a[2].elementNum = AuElementAll;
		    a[2].newState = AuStateStop;
		}
		break;
	    case AuElementTypeBundle:
		varLen = el->bundle.num_inputs * sizeof(auInputTrack);
		e->isComponent = AuFalse;
		break;
	    case AuElementTypeMultiplyConstant:
		e->isComponent = AuFalse;
		break;
	    case AuElementTypeAddConstant:
		e->isComponent = AuFalse;
		break;
	    case AuElementTypeSum:
		varLen = PAD4(el->sum.num_inputs * sizeof(CARD16));
		e->isComponent = AuFalse;
		break;
	    case AuElementTypeExportClient:
		if (!(copyActions = el->exportclient.actions.num_actions))
		{
		    numActions = numDefaultActions[el->type];

		    a[0].triggerState = AuStatePause;
		    a[0].triggerPrevState = AuStateAny;
		    a[0].triggerReason = AuReasonAny;
		    a[0].action = AuElementActionSendNotify;

		    a[1].triggerState = AuStateStop;
		    a[1].triggerPrevState = AuStateAny;
		    a[1].triggerReason = AuReasonAny;
		    a[1].action = AuElementActionSendNotify;

		    a[2].triggerState = AuStatePause;
		    a[2].triggerPrevState = AuStateAny;
		    a[2].triggerReason = AuReasonAny;
		    a[2].action = AuElementActionChangeState;
		    a[2].flow = flow;
		    a[2].elementNum = AuElementAll;
		    a[2].newState = AuStatePause;

		    a[3].triggerState = AuStateStart;
		    a[3].triggerPrevState = AuStatePause;
		    a[3].triggerReason = AuReasonAny;
		    a[3].action = AuElementActionChangeState;
		    a[3].flow = flow;
		    a[3].elementNum = AuElementAll;
		    a[3].newState = AuStateStart;

		    a[4].triggerState = AuStateStop;
		    a[4].triggerPrevState = AuStateAny;
		    a[4].triggerReason = AuReasonAny;
		    a[4].action = AuElementActionChangeState;
		    a[4].flow = flow;
		    a[4].elementNum = AuElementAll;
		    a[4].newState = AuStateStop;
		}
		break;
	    case AuElementTypeExportDevice:
		copyActions = el->exportdevice.actions.num_actions;
		break;
	    case AuElementTypeExportBucket:
		if (!(copyActions = el->exportbucket.actions.num_actions))
		{
		    numActions = numDefaultActions[el->type];

		    a[0].triggerState = AuStatePause;
		    a[0].triggerPrevState = AuStateAny;
		    a[0].triggerReason = AuReasonAny;
		    a[0].action = AuElementActionSendNotify;

		    a[1].triggerState = AuStateStop;
		    a[1].triggerPrevState = AuStateAny;
		    a[1].triggerReason = AuReasonAny;
		    a[1].action = AuElementActionSendNotify;

		    a[2].triggerState = AuStateStop;
		    a[2].triggerPrevState = AuStateAny;
		    a[2].triggerReason = AuReasonAny;
		    a[2].action = AuElementActionChangeState;
		    a[2].flow = flow;
		    a[2].elementNum = AuElementAll;
		    a[2].newState = AuStateStop;
		}
		break;
	    case AuElementTypeExportMonitor:
		break;
	}

	if (copyActions)
	{
	    auElementAction *raw = (auElementAction *) (el + 1);
	    ActionPtr       act = a;
	    FlowPtr         fl;

	    numActions = copyActions;
	    varLen = numActions * sizeof(auElementAction);

	    while (copyActions--)
	    {
		if (!AuValidTriggerState(raw->trigger_state))
		    FREE_FLOW_ERROR(AuBadValue, raw->trigger_state);

		if (!AuValidTriggerState(raw->trigger_prev_state))
		    FREE_FLOW_ERROR(AuBadValue, raw->trigger_prev_state);

		if (!AuValidTriggerReason(raw->trigger_reason))
		    FREE_FLOW_ERROR(AuBadValue, raw->trigger_reason);

		act->triggerState = raw->trigger_state;
		act->triggerPrevState = raw->trigger_prev_state;
		act->triggerReason = raw->trigger_reason;
		act->action = raw->action;

		if (raw->action == AuElementActionChangeState)
		{
		    if (!(fl = (FlowPtr) LookupIDByType(raw->flow, auFlowType)))
			FREE_FLOW_ERROR(AuBadFlow, raw->flow);

		    if (raw->element_num != AuElementAll &&
			raw->element_num >= flow->numElements)
			FREE_FLOW_ERROR(AuBadElement, raw->element_num);

		    if (!AuValidState(raw->new_state))
			FREE_FLOW_ERROR(AuBadValue, raw->new_state);

		    act->flow = fl;
		    act->elementNum = raw->element_num;
		    act->newState = raw->new_state;
		}
		else if (raw->action != AuElementActionSendNotify &&
			 raw->action != AuElementActionNoop)
		    FREE_FLOW_ERROR(AuBadValue, raw->action);

		raw++;
		act++;
	    }
	}

	e->numActions = numActions;
	e->actions = a;
	e->varLen = varLen;

	tmp = (AuUint8 *) el;
	tmp += sizeof(auElement) + varLen;
	el = (auElement *) tmp;
    }

    if (flow->clocked)
    {
	AddToLinkedList(auClockedFlows, flow);
    }
    else
    {
	AddToLinkedList(auUnclockedFlows, flow);
    }

    if ((status = AuCompileFlow(client, flow)) == AuSuccess)
    {
	flow->trivial = flow->numElements == 2 &&
	    (((flow->elements[0].raw->type == AuElementTypeImportClient &&
	       flow->elements[1].raw->type == AuElementTypeExportBucket) ||
	      (flow->elements[1].raw->type == AuElementTypeImportClient &&
	       flow->elements[0].raw->type == AuElementTypeExportBucket)) ||
	     (flow->elements[0].raw->type == AuElementTypeImportBucket &&
	      flow->elements[1].raw->type == AuElementTypeExportClient) ||
	     (flow->elements[1].raw->type == AuElementTypeImportBucket &&
	      flow->elements[0].raw->type == AuElementTypeExportClient));

	flow->count = 0;
    }
    else
	AuFreeFlowElements(flow);

    return status;
}

int
ProcAuSetElementStates(client)
ClientPtr       client;
{
    REQUEST(auSetElementStatesReq);
    int             i;
    auElementState *st;
    FlowPtr        *flows;

    REQUEST_AT_LEAST_SIZE(auSetElementStatesReq);

    /* check the length */
    if (stuff->length != (sizeof(auSetElementStatesReq) +
			  stuff->numStates * sizeof(auElementState)) >> 2)
	AU_ERROR(AuBadLength, 0);

    if (!(flows = (FlowPtr *) aualloc(sizeof(FlowPtr) * stuff->numStates)))
	AU_ERROR(AuBadAlloc, 0);

    st = (auElementState *) & stuff[1];

    /* do some error checking */
    for (i = 0; i < stuff->numStates; i++, st++)
    {
	FlowPtr         flow;

	/* look up the flow */
	if (!(flow = (FlowPtr) LookupIDByType(st->flow, auFlowType)))
	{
	    aufree(flows);
	    AU_ERROR(AuBadFlow, st->flow);
	}

	/*
	 * make sure the element_num is in range and that the element is a
	 * component
	 */
	if (st->element_num != AuElementAll &&
	    (st->element_num >= flow->numElements ||
	     !flow->elements[st->element_num].isComponent))
	{
	    aufree(flows);
	    AU_ERROR(AuBadElement, st->element_num);
	}

	flows[i] = flow;
    }

    AuProcessStateChanges(stuff->numStates, &stuff[1], flows);

    aufree(flows);
    return AuSuccess;
}

int
ProcAuGetElementStates(client)
ClientPtr       client;
{
    REQUEST(auGetElementStatesReq);
    auGetElementStatesReply rep;
    int             i,
                    totalElements = 0;
    auElementState *st;
    FlowPtr        *flows;

    REQUEST_AT_LEAST_SIZE(auGetElementStatesReq);

    /* check the length */
    if (stuff->length != (sizeof(auSetElementStatesReq) +
			  stuff->numStates * sizeof(auElementState)) >> 2)
	AU_ERROR(AuBadLength, 0);

    if (!(flows = (FlowPtr *) aualloc(sizeof(FlowPtr) * stuff->numStates)))
	AU_ERROR(AuBadAlloc, 0);

    st = (auElementState *) & stuff[1];

    /* do some error checking */
    for (i = 0; i < stuff->numStates; i++, st++)
    {
	FlowPtr         flow;

	/* look up the flow */
	if (!(flow = (FlowPtr) LookupIDByType(st->flow, auFlowType)))
	{
	    aufree(flows);
	    AU_ERROR(AuBadFlow, st->flow);
	}

	/*
	 * make sure the element_num is in range and that the element is a
	 * component
	 */
	if (st->element_num != AuElementAll &&
	    (st->element_num >= flow->numElements ||
	     !flow->elements[st->element_num].isComponent))
	{
	    aufree(flows);
	    AU_ERROR(AuBadElement, st->element_num);
	}

	if (st->element_num == AuElementAll)
	    totalElements += flow->numElements;
	else
	    totalElements++;

	flows[i] = flow;
    }

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.length = (sizeof(auElementState) * totalElements) >> 2;
    rep.numStates = totalElements;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    st = (auElementState *) & stuff[1];

    /* write out the states */
    for (i = 0; i < stuff->numStates; i++, st++)
    {
	auElementState  state;
	int             j,
	                last;

	if (st->element_num == AuElementAll)
	{
	    j = 0;
	    last = flows[i]->numElements - 1;
	}
	else
	    j = last = st->element_num;

	state.flow = st->flow;

	for (; j <= last; j++)
	{
	    state.element_num = j;
	    state.state = flows[i]->elements[j].state;
	    WriteAuSwappedDataToClient(client, sizeof(state), &state,
				       AuSwapElementStateWrite);
	}
    }

    aufree(flows);
    return AuSuccess;
}

int
ProcAuSetElementParameters(client)
ClientPtr       client;
{
    REQUEST(auSetElementParametersReq);
    FlowPtr         flow;
    auElement      *el;
    auElementParameters *parms;
    CARD32         *p;
    AuUint8	   *tmp;
    int             status,
                    len,
                    i;
    AuBool *changed;

    REQUEST_AT_LEAST_SIZE(auSetElementParametersReq);

    parms = (auElementParameters *) & stuff[1];

    len = (stuff->length << 2) - sizeof(auSetElementParametersReq);

    for (i = 0; len && i < stuff->numParameters; i++, parms++)
    {
	if (!(flow = (FlowPtr) LookupIDByType(parms->flow, auFlowType)))
	    AU_ERROR(AuBadFlow, parms->flow);

	if (parms->element_num >= flow->numElements)
	    AU_ERROR(AuBadElement, parms->element_num);

	len -= sizeof(auElementParameters) -
	    parms->num_parameters * sizeof(CARD32);

	if (len < 0)
	    break;

	el = flow->elements[parms->element_num].raw;
	changed = &flow->elements[parms->element_num].parmsChanged;
	p = (CARD32 *) & parms[1];

	tmp = (AuUint8 *) parms;
	tmp += parms->num_parameters * sizeof(CARD32);
	parms = (auElementParameters *) tmp;

	switch (el->type)
	{
	    case AuElementTypeImportClient:
		break;
	    case AuElementTypeImportDevice:
		break;
	    case AuElementTypeImportBucket:
		el->importbucket.offset = p[AuParmsImportBucketOffset];
		flow->count = 0;
		break;
	    case AuElementTypeImportWaveForm:
		el->importwaveform.frequency =
		    p[AuParmsImportWaveFormFrequency];
		el->importwaveform.num_samples =
		    p[AuParmsImportWaveFormNumSamples];
		break;
	    case AuElementTypeBundle:
		break;
	    case AuElementTypeMultiplyConstant:
		el->multiplyconstant.constant =
		    p[AuParmsMultiplyConstantConstant];
		break;
	    case AuElementTypeAddConstant:
		el->addconstant.constant = p[AuParmsAddConstantConstant];
		break;
	    case AuElementTypeSum:
		break;
	    case AuElementTypeExportClient:
		break;
	    case AuElementTypeExportDevice:
		break;
	    case AuElementTypeExportBucket:
		el->exportbucket.offset = p[AuParmsExportBucketOffset];
		flow->count = 0;
		break;
	}

	flow->needsRecompile = AuTrue;
	*changed = AuTrue;
    }

    status = AuSuccess;
    flow = auUnclockedFlows;

    while (flow && (status == AuSuccess))
    {
	if (flow->needsRecompile)
	{
	    status = AuCompileFlow(client, flow);
	    flow->needsRecompile = AuFalse;
	}

	flow = flow->next;
    }

    if (status == AuSuccess)
	AuProcessUnclockedFlows();

    flow = auClockedFlows;

    while (flow && (status == AuSuccess))
    {
	if (flow->needsRecompile)
	{
	    status = AuCompileFlow(client, flow);
	    flow->needsRecompile = AuFalse;
	}

	flow = flow->next;
    }

    if (status == AuSuccess)
	AuProcessClockedFlows();

    return status;
}

#define AuValidDataTransferState(s)					       \
    ((s) == AuTransferStateReady || (s) == AuTransferStatePending ||	       \
     (s) == AuTransferStateEnd)

int
ProcAuWriteElement(client)
ClientPtr       client;
{
    REQUEST(auWriteElementReq);
    FlowPtr         flow;
    AuUint32           currentSize,
                    n;
    AuBlock           l;
    ComponentPtr    c;
    AuUint8          *s,
                   *d;

    REQUEST_AT_LEAST_SIZE(auWriteElementReq);

    if (!(flow = (FlowPtr) LookupIDByType(stuff->flow, auFlowType)))
	AU_ERROR(AuBadFlow, stuff->flow);

    if (stuff->element_num >= flow->numElements ||
	flow->elements[stuff->element_num].raw->type !=
	AuElementTypeImportClient)
	AU_ERROR(AuBadElement, stuff->element_num);

    if (!AuValidDataTransferState(stuff->state))
	AU_ERROR(AuBadValue, stuff->state);

    if (stuff->length - (sizeof(auWriteElementReq) >> 2) !=
	PAD4(stuff->num_bytes) >> 2)
	AU_ERROR(AuBadLength, 0);

    /*
     * if this is a trivial flow (an import to a bucket) then just bcopy the
     * data directly to the bucket
     */
    if (flow->trivial)
    {
	AuUint8          *d;
	FlowElementPtr  el;

	el = &flow->elements[stuff->element_num ? 0 : 1];
	c = el->component;
	d = c->data + el->raw->exportbucket.offset * c->bytesPerSample +
	    flow->count;

	if (d + stuff->num_bytes > c->dataEnd)
	    AU_ERROR(AuBadValue, stuff->num_bytes);

	bcopy((AuUint8 *) &stuff[1], d, stuff->num_bytes);

	flow->count = stuff->state == AuTransferStateEnd ? 0 :
	    flow->count + stuff->num_bytes;

	return AuSuccess;
    }

    c = flow->elements[stuff->element_num].component;

    l = AuBlockAudio();
    currentSize = c->currentSize;
    AuUnBlockAudio(l);

    if (stuff->num_bytes > c->dataSize - currentSize)
	AU_ERROR(AuBadValue, stuff->num_bytes);

    if (stuff->num_bytes)
    {
	s = (AuUint8 *) &stuff[1];
	d = c->write;
	n = aumin(stuff->num_bytes, c->dataEnd - d);
	bcopy(s, d, n);

	/* wrap if necessary */
	if (n != stuff->num_bytes)
	    bcopy(s + n, c->data, stuff->num_bytes - n);

	if ((d += stuff->num_bytes) >= c->dataEnd)
	    d -= c->dataSize;

	l = AuBlockAudio();
	c->write = d;
	c->currentSize += stuff->num_bytes;
	c->incoming -= stuff->num_bytes;
	AuUnBlockAudio(l);
    }

    if (stuff->state != AuTransferStatePending)
    {
	if (stuff->state == AuTransferStateEnd)
	{
	    c->eof = AuTrue;
	    l = AuBlockAudio();
	    c->incoming = 0;
	    AuUnBlockAudio(l);
	}

	/*
	 * if the port is paused and we got some data then start the port
	 * back up
	 */
	if (stuff->num_bytes &&
	    flow->elements[stuff->element_num].state == AuStatePause)
	    AuChangeElementState(flow, stuff->element_num, AuStateStart,
				 AuTrue, AuReasonUser);
    }

    return AuSuccess;
}

int
ProcAuReadElement(client)
ClientPtr       client;
{
    REQUEST(auReadElementReq);
    FlowPtr         flow;
    AuUint32           numBytes,
                    totalBytes,
                    size;
    AuBlock           l;
    ComponentPtr    c;
    AuUint8          *p;
    auReadElementReply rep;

    REQUEST_SIZE_MATCH(auReadElementReq);

    if (!(flow = (FlowPtr) LookupIDByType(stuff->flow, auFlowType)))
	AU_ERROR(AuBadFlow, stuff->flow);

    if (stuff->element_num >= flow->numElements ||
	flow->elements[stuff->element_num].raw->type !=
	AuElementTypeExportClient)
	AU_ERROR(AuBadElement, stuff->element_num);

    /* write out the reply */
    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;

    /*
     * if this is a trivial flow (a bucket to an export) then just copy the
     * data directly from the bucket
     */
    if (flow->trivial)
    {
	AuUint8          *s;
	FlowElementPtr  el;

	el = &flow->elements[stuff->element_num ? 0 : 1];
	c = el->component;
	s = c->data + el->raw->importbucket.offset * c->bytesPerSample +
	    flow->count;

	numBytes = aumin(stuff->num_bytes, c->dataEnd - s);

	rep.length = (numBytes + 3) >> 2;
	rep.num_bytes = numBytes;
	WriteAuReplyToClient(client, sizeof(rep), &rep);

	(void) WriteToClient(client, numBytes, s);
	flow->count += numBytes;

	return AuSuccess;
    }

    c = flow->elements[stuff->element_num].component;

    l = AuBlockAudio();
    size = c->currentSize;
    AuUnBlockAudio(l);

    numBytes = totalBytes = aumin(stuff->num_bytes, size);

    rep.length = (numBytes + 3) >> 2;
    rep.num_bytes = numBytes;
    WriteAuReplyToClient(client, sizeof(rep), &rep);

    p = c->read;

    /* see if the read needs to wrap */
    if (p + numBytes > c->dataEnd)
    {
	unsigned int    bytesLeft = c->dataEnd - p;

	/* don't let (void) WriteToClient put in any padding */
	if (bytesLeft & 3)
	{
	    unsigned int    n = bytesLeft & ~3;
	    unsigned char   buf[4];

	    (void) WriteToClient(client, n, p);
	    numBytes -= n;

	    /* copy from end of port to buffer */
	    bytesLeft &= 3;
	    bcopy(p + n, buf, bytesLeft);

	    /* copy from beginning of port to buffer */
	    n = numBytes > 4 - bytesLeft ? 4 - bytesLeft : numBytes;
	    bcopy(c->data, buf + bytesLeft, n);

	    (void) WriteToClient(client, bytesLeft + n, buf);
	    numBytes -= bytesLeft + n;
	    p = c->data + n;
	}
	else
	{
	    (void) WriteToClient(client, bytesLeft, p);
	    p = c->data;
	    numBytes -= bytesLeft;
	}
    }

    if (numBytes)
	(void) WriteToClient(client, numBytes, p);

    p += numBytes;

    if (p >= c->dataEnd)
	p -= c->dataSize;

    l = AuBlockAudio();
    c->outgoing -= totalBytes;
    c->currentSize -= totalBytes;
    c->read = p;
    AuUnBlockAudio(l);

    /* if the port was paused and we read some data, start it back up */
    if (flow->elements[stuff->element_num].state == AuStatePause)
	AuChangeElementState(flow, stuff->element_num, AuStateStart,
			     AuTrue, AuReasonUser);

    return client->noClientException;
}

int
ProcAuGetServerTime(client)
ClientPtr       client;
{
    REQUEST(auReq);
    auGetServerTimeReply rep;

    REQUEST_SIZE_MATCH(auReq);

    UpdateCurrentTimeIf();

    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.time = currentTime.milliseconds;
    rep.length = 0;

    WriteAuReplyToClient(client, sizeof(auGetCloseDownModeReply), &rep);
    return client->noClientException;
}

int
ProcAuGetCloseDownMode(client)
ClientPtr       client;
{
    REQUEST(auReq);
    auGetCloseDownModeReply rep;

    REQUEST_SIZE_MATCH(auReq);

    rep.type = Au_Reply;
    rep.sequenceNumber = client->sequence;
    rep.closeDownMode = client->closeDownMode;
    rep.length = 0;

    WriteAuReplyToClient(client, sizeof(auGetCloseDownModeReply), &rep);
    return client->noClientException;
}

int
ProcAuSetCloseDownMode(client)
ClientPtr       client;
{
    REQUEST(auSetCloseDownModeReq);

    REQUEST_SIZE_MATCH(auSetCloseDownModeReq);

    if ((stuff->mode == AuCloseDownDestroy) ||
	(stuff->mode == AuCloseDownRetainPermanent) ||
	(stuff->mode == AuCloseDownRetainTemporary))
    {
	client->closeDownMode = stuff->mode;
	return client->noClientException;
    }
    else
    {
	client->errorValue = stuff->mode;
	return AuBadValue;
    }
}

/*
 * XXX maybe just call to ProcKillClient()?
 */

int
ProcAuKillClient(client)
ClientPtr       client;
{
    REQUEST(auResourceReq);

    pointer        *pResource;
    int             clientIndex,
                    myIndex;

    REQUEST_SIZE_MATCH(auResourceReq);

    if (stuff->id == AuCloseDownDestroy)
    {
	CloseDownRetainedResources();
	return (client->noClientException);
    }
    pResource = (pointer *) LookupIDByClass(stuff->id, RC_ANY);
    clientIndex = CLIENT_ID(stuff->id);

    if (clientIndex && pResource && clients[clientIndex] &&
	!(stuff->id & SERVER_BIT) &&
	(clients[clientIndex]->requestVector != InitialVector))
    {
	myIndex = client->index;
	CloseDownClient(clients[clientIndex]);
	if (myIndex == clientIndex)
	{
	    /*
	     * force yield and return Success, so that Dispatch() doesn't try
	     * to touch client
	     */
	    isItTimeToYield = AuTrue;
	    return (AuSuccess);
	}
	return (client->noClientException);
    }
    else
    {
	client->errorValue = stuff->id;
	return (AuBadValue);
    }
}

int
ProcAuNoOperation(client)
ClientPtr       client;
{
    return AuSuccess;
}

int
AuInitSetupReply()
{
    int             len;
    char           *rel = release + 4;
    static char    *string;

    if (!AuInitDevice(&auSetup, &len))
	return 0;

    if (string)
    {
	aufree(string);
	string = (char *) 0;
    }

    if (!(string = (char *) aualloc(strlen(rel) +
				    3 + strlen(VENDOR_STRING) + 1)))
	return 0;

    strcpy(string, rel);
    strcat(string, " - ");
    strcat(string, VENDOR_STRING);

    auVendorString = string;
    auSetupPrefix.success = AuTrue;
    auSetupPrefix.majorVersion = AuProtocolMajorVersion;
    auSetupPrefix.minorVersion = AuProtocolMinorVersion;

    auSetup.release = VENDOR_RELEASE;
    auSetup.nbytesVendor = strlen(auVendorString);
    auSetup.maxRequestSize = MAX_REQUEST_SIZE;

    auSetupPrefix.length = (sizeof(auConnSetup) + len +
			    PAD4(auSetup.nbytesVendor)) >> 2;

    return 1;
}

/* sends return init packet */
int
AuSendInitResponse(client)
ClientPtr       client;
{
    extern ComponentPtr *auServerDevices,	/* array of devices */
                   *auServerBuckets,		/* array of server owned
						 * buckets */
                   *auServerRadios;		/* array of server owned
						 * radios */
    extern AuUint32    auNumServerDevices,		/* number of devices */
                    auNumServerBuckets,		/* number of server owned
						 * buckets */
                    auNumServerRadios;		/* number of server owned
						 * radios */
    extern unsigned char auFormats[],
                    auElementTypes[],
                    auWaveForms[],
                    auActions[];
    int             i;

    auSetup.ridBase = client->clientAsMask;
    auSetup.ridMask = RESOURCE_ID_MASK;

    WriteAuSwappedDataToClient(client, sizeof(auSetupPrefix),
			       &auSetupPrefix, AuSwapSetupPrefix);
    WriteAuSwappedDataToClient(client, sizeof(auSetup), &auSetup,
			       AuSwapSetup);
    (void) WriteToClient(client, auSetup.nbytesVendor, auVendorString);
    (void) WriteToClient(client, auSetup.numFormats, auFormats);
    (void) WriteToClient(client, auSetup.numElementTypes, auElementTypes);
    (void) WriteToClient(client, auSetup.numWaveForms, auWaveForms);
    (void) WriteToClient(client, auSetup.numActions, auActions);

    for (i = 0; i < auNumServerDevices; i++)
    {
	auDeviceAttributes da;
	ComponentPtr    d;

	d = auServerDevices[i];

       if (d->kind == AuComponentKindPhysicalInput)
       {
           AuGetInputGain(&d->gain);
           AuGetInputMode(&d->lineMode);
       }
       else if (d->kind == AuComponentKindPhysicalOutput)
	    AuGetOutputGainAndMode(&d->gain, &d->lineMode);
       else if (d->kind == AuComponentKindPhysicalFeedback)
           AuGetFeedbackGain(&d->gain);

	xferDeviceAttributes(d, da);

	WriteAuSwappedDataToClient(client, sizeof(da), &da,
				   AuSwapDeviceAttributesWrite);
	(void) WriteToClient(client, d->description.len, d->description.string);

	if (d->numChildren)
	{
	    bcopy(d->children, d->childSwap,
		  d->numChildren * sizeof(AuDeviceID));
	    WriteAuSwappedDataToClient(client,
				       d->numChildren * sizeof(AuDeviceID),
				       d->childSwap, Swap32Write);
	}
    }

    for (i = 0; i < auNumServerBuckets; i++)
    {
	auBucketAttributes ba;
	ComponentPtr    c;

	c = auServerBuckets[i];

	xferBucketAttributes(c, ba);

	WriteAuSwappedDataToClient(client, sizeof(ba), &ba,
				   AuSwapBucketAttributesWrite);
	(void) WriteToClient(client, c->description.len, c->description.string);
    }

    return (client->noClientException);
}

/* XXX - could probably toss this and just use SendErrorToClient */
void
SendAuErrorToClient(client, majorCode, minorCode, resId, errorCode)
ClientPtr       client;
unsigned        majorCode;
unsigned short  minorCode;
AuID             resId;
int             errorCode;
{
    auError         rep;

    rep.type = Au_Error;
    rep.sequenceNumber = client->sequence;
    rep.errorCode = errorCode;
    rep.majorCode = majorCode;
    rep.minorCode = minorCode;
    rep.resourceID = resId;

    WriteEventsToClient(client, 1, (auEvent *) & rep);
}
