/*
	File:		PPCNetwork.c

	Contains:	PPCNetwork related functions.

	Written by:	Sangam, Eric M. Trehus

	Copyright:	© 1989-1992 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<46>	 4/14/92	BBM		<JSM>: Remove unfinished PPC code that is under the contitional
									ÒTheFututeÓ, and remove the conditional ÒCubeEÓ since that is
									reality. Remove conditionals, since all they do is confuse.
		<45>	 10/4/91	JSM		Change PsychoticFarmerOrLater conditionals to TheFuture.
		<44>	 9/29/91	DTY		Conditionalize <42> and <43> out of CubeE.
		<43>	 6/10/91	EMT		continue code reduction
		<42>	  6/4/91	EMT		Roll in StoreAndForward Revisions
		<41>	 1/14/91	EMT		<VC> Fix race condition in RemoveNetworkConnection.
		<40>	 1/11/91	EMT		<VC> Fix bug in IPCListPorts.
		<39>	12/13/90	EMT		<JSM> Remove race condition, add debug code (for debug versions)
		<38>	11/13/90	EMT		<stb> Remove redundant error checks in StartNetworkSession and
									ListNetworkPorts, add noUserNameErr back in (bug fix).
		<37>	 11/8/90	EMT		<stb> Change key used in scramble, and some error codes.
		<36>	 11/6/90	EMT		use STATIC in place of static for link maps
		<35>	10/30/90	EMT		Remove a tiny window of oppertunity
		<34>	10/24/90	EMT		Update error codes
		<33>	10/11/90	EMT		Add conditionals around debug code, remove reference to
	   								Assembly language completion routine glue.
		<32>	 9/21/90	EMT		Update constants, types, and field names as dictated by
									PPCToolBox.h
		<31>	 9/14/90	EMT		Fix type problem
		<30>	  9/5/90	EMT		Remove a conditional that appears to cause 3.0C some problems
		<29>	  9/4/90	EMT		Fix priority 1 bug
	   <27+>	  9/4/90	EMT		Roll in Eric's changes
		<27>	 8/14/90	S		Fixed Bug in copying the user name in ProcessInform.
		<26>	  8/6/90	S		Bug Fixes.
		<25>	 6/28/90	S		To Improve Dynamic allocation of sessions.
	   <23+>	  6/1/90	EMT		Fix ConnectionRequestCompletion bug that causes failure in
									RepostConnectionListener when ADSP calls it directly
		<23>	  6/1/90	VC		Added the checking of UserFlag after getting the user record.
		<22>	 5/31/90	EMT		Added support to clear the originated flag on inform calls, and
									set it on start.
		<21>	 4/23/90	JSM		Add CopyReadBlk routine to copy readBlk to writeHdrBlk and
									writeBlk in NetIPCParams, fix bug in AuthAcceptCompletion, add
									AvoidDuplicateCompletion prototype, misc. other code size
									reductions.
		<20>	 4/10/90	S		To Include Checking of PPC Global Flag.
		<19>	 3/20/90	S		Fix Bugs Related NetInvisible Ports.
		<17>	 2/28/90	S		IPCListPort Info Returned to refelect Guest Selected Icon.
		<16>	 2/27/90	S		Fix the Guest Stuff.
		<15>	 2/27/90	S		To Make it work with ADSP 1.5(it did not).
		<14>	 2/23/90	S		Bug fix
		<13>	 2/20/90	S		Fix the actual length in write completion during error
									condition.
		<12>	 2/20/90	S		Bug Fix.
		<11>	 2/13/90	S		Bug Fix.
		<10>	  2/8/90	S		No Change.
		 <9>	  2/5/90	S		Changed Error Code For PPCStart(portClosedErr) if user close the
									port after making start.
		 <8>	  2/2/90	S		Fixed Crash on noInform Error.
		 <7>	 1/30/90	S		Changed ErrorCode on Open Completion.
		 <6>	 1/30/90	S		Bug Fixes for some error codes.
		 <5>	 1/26/90	S		Fix Writeing > 32k problem & reading 0 byte
		 <4>	 1/24/90	S		Fix Bug related ppcportclose when the session is being opened.
	   <1.7>	 11/8/89	CVC		Made changes to fix ppcreject crash
	   <1.6>	 11/6/89	KON		Fixed typo: SdssReject -> SessReject
	   <1.5>	 11/6/89	CVC		Fixed a bug in PPCReject
	   <1.4>	10/12/89	CCH		Fixed header
	  <¥1.3>	10/12/89	CVC		Added access control stuff
	   <1.2>	 10/2/89	CVC		Fixed a bug in PPCInform call.
	   <1.1>	 9/25/89	ss		Added support for 'more' in PPCwrite call
	   <1.0>	 9/18/89	CVC		Adding PPC toolbox for the first time.

	   Old Revision History:

		06/05/89 	Sangam	    New Today
		07/20/89    Sangam      1.0d2 release today!
		08/01/89	Sangam		Fixed Bug in End session call
		08/02/89    Sangam      Fixed ADSP bug with a work around (ReadCompletion & RemoveCompletion)
		08/10/89	Sangam		Made Change to IPCListPorts calls
		08/10/89	Sangam		Added userEntry filed for read/write headers
		08/10/89	Sangam		Fixed a bug in listport call
		08/29/89    Sangam		Fixed duplicate completion with informPB
		08/30/89	Sangam		Fixed actualLength filed in Read Calls
		09/07/89	Sangam		Added Appletalk Phase2 Transition Routines
								so that Network can be switched dynamicallly.
		09/07/89    Sangam		Took out the gracefull end for outstanding write.
								Looks like ADSP will not time out anyway
		09/08/89	Sangam		Fixed a bug in WriteData (i.e. res->deadLock was set to 1
								which prevented completion routines being not called
		09/21/89	Sangam		Changed GetPortNames interface
		09/22/89 	Sangam		Removed destportName and destlocname from session data structure
		09/25/89	Sangam		Added support for 'more' in PPCwrite call
		09/26/89	Sangam      Major changes to support authentication
		09/27/89	Sangam      Made change to ListRead to take size of PortInfo
		10/09/89	Sangam		Made changes to support guest login
		11/08/89	Sangam      Made changes to fix ppcreject crash
		1/4/90      Sangam      Several Bug Fixes

*/

#include "PPCCommon.h"
#include <String.h>

/*----------------------------------------------------------------------------------------
	Constants used only in this file.
----------------------------------------------------------------------------------------*/
#define MaxADSPWrite 0x7FFF	// 32767 bytes is the most we should ever write.

/*----------------------------------------------------------------------------------------
	Prototypes for procedures used only in this file.
----------------------------------------------------------------------------------------*/
STATIC NetIPCParamsPtr GetRemSess(PPCGlobalParamsPtr ppcglobPtr);
STATIC void FreeRemSess(NetIPCParamsPtr theSess);
STATIC void RemoveCompletion(void);			// <44>
STATIC void BeginNetworkConnection(NetIPCParamsPtr sessPtr);
STATIC void AcceptNetworkConnection(NetIPCParamsPtr sessPtr);
STATIC void RemoveNetworkConnection(NetIPCParamsPtr sessPtr,Boolean abortFlag);
STATIC void NetLookUpCompletion(void);
STATIC void ConnectionEndCompletion(void);
STATIC void OpenCompletion(void);
STATIC void StartSendCompletion(void);
STATIC void StartCommandCompletion(void);
STATIC void AuthRespSendCompletion(void);
STATIC void AuthAcceptCompletion(void);
STATIC void ListSendCompletion(void);
STATIC void ListReadCompletion(void);
STATIC void ListCommandCompletion(void);
STATIC void RespConnectionEndCompletion(void);
STATIC void RespOpenCompletion(void);
STATIC void RecvSessReqCompletion(void);
STATIC void GuestRecCompletion(UserRecPb *uRec);
STATIC void UserRecCompletion(UserRecPb	*uRec);
STATIC void AuthSendCompletion(void);
STATIC void AuthRespCompletion(void);
STATIC void ProcessInform(NetIPCParams *sessPtr,unsigned short userFlags);
STATIC void RespSendCompletion(void);
STATIC void ARepostConnectionListener(void);
STATIC void RepostConnectionListener(DSPParamBlock *dsp);
STATIC void ListWrite(NetIPCParams *sessPtr);
STATIC void ListWriteCompletion(void);
STATIC void ListRespCompletion(void);

STATIC void BeginWriteBlock(NetIPCParamsPtr sessPtr);
STATIC void WriteHdrCompletion(void);
STATIC void WriteBlockData(NetIPCParamsPtr sessPtr);
STATIC void WriteBlockDataCompletion(void);
STATIC void CompleteWriteRequest(NetIPCParamsPtr sessPtr,OSErr result);

STATIC void BeginReadBlock(NetIPCParamsPtr sessPtr);
STATIC void ReadHdrCompletion(void);
STATIC void ReadBlockData(NetIPCParamsPtr sessPtr);
STATIC void ReadDataCompletion(void);
STATIC void ReadData(NetIPCParamsPtr sessPtr);
STATIC void CompleteReadRequest(NetIPCParamsPtr sessPtr,OSErr result);

STATIC void CopyReadBlk(NetIPCParamsPtr sessPtr);

/*----------------------------------------------------------------------------------------
	GetRemSess
----------------------------------------------------------------------------------------*/
STATIC NetIPCParamsPtr GetRemSess(PPCGlobalParamsPtr ppcglobPtr)
{
	NetIPCParamsPtr sessPtr;
	
	if(sessPtr = ServeQueue(&ppcglobPtr->freeRemSessQueue))
	{
		memset(sessPtr,0,sizeof(NetIPCParams));	// Wipe it clean
		sessPtr->sessUse = netUse;
		EnQueue(sessPtr,&ppcglobPtr->RemSessQueue);
	}
	return(sessPtr);
}

/*----------------------------------------------------------------------------------------
	FreeRemSess
----------------------------------------------------------------------------------------*/
STATIC void FreeRemSess(NetIPCParamsPtr theSess)
{
	PPCGlobalParamsPtr ppcglobPtr;
	PPCPortEntryPtr portPtr;
	
	ppcglobPtr = (PPCGlobalParamsPtr)getGlobal();
	portPtr = theSess->portEntry;
	DeleteFromQueue(&ppcglobPtr->RemSessQueue,(unsigned long)theSess,PointerCompare);
	if(portPtr)								// <44>if this session belongs to a port.
		--portPtr->sessCount;				// <44> update the port table.
	theSess->portEntry = (void *)-1;		// check for bug!
	EnQueue(theSess,&ppcglobPtr->freeRemSessQueue);
}

/*----------------------------------------------------------------------------------------
	RemoveNetworkConnection is responsible for scheduling a job to remove the network connection.
----------------------------------------------------------------------------------------*/
STATIC void RemoveNetworkConnection(NetIPCParamsPtr sessPtr,Boolean abortFlag)
{
	PPCGlobalParamsPtr ppcglobPtr;
	short sr;

	sr = spl(kNoInterrupts);
	if(sessPtr->sessState != AwaitAbortComp)	// if we arn't already aborting it thenÉ
	{
		sessPtr->sessState = AwaitAbortComp;
		sessPtr->abortFlag = abortFlag;			// remember how we are aborting this.
		
		ppcglobPtr = (PPCGlobalParamsPtr)getGlobal();
		DeleteFromQueue(&ppcglobPtr->RemSessQueue,(unsigned long)sessPtr,PointerCompare);
		EnQueue(sessPtr,&ppcglobPtr->dspAbortQueue);	// place it in the queue of things to do.
		if(!ppcglobPtr->killSessPtr)					// if we are not busy thenÉ
		{
			ppcglobPtr->killSessPtr = ServeQueue(&ppcglobPtr->dspAbortQueue);
			spl(sr);
			BlockMove((Ptr)&sessPtr->readBlk.dsp,(Ptr)&ppcglobPtr->dspRemovePB,sizeof(DSPParamBlock));
			RemoveConnectionEnd(sessPtr->abortFlag, // abort flag (Kill all pending requests)
								true,
								(ProcPtr)RemoveCompletion, // Completion Routine
								&ppcglobPtr->dspRemovePB);
			return;
		}
	}
	spl(sr);
}

/*---------------------------------------------------------------------------------------------------
	RemoveCompletion
---------------------------------------------------------------------------------------------------*/
STATIC void RemoveCompletion(void)
{
	PPCGlobalParamsPtr ppcglobPtr;
	PPCPortEntryPtr		portPtr;
	PPCParamBlockPtr	PB;
	NetIPCParams *sessPtr;
	
	ppcglobPtr = (PPCGlobalParamsPtr)getGlobal();
	sessPtr = ppcglobPtr->killSessPtr;

#ifdef DEBUG
	if(ppcglobPtr->dspRemovePB.ioResult)
		DebugStr("\pError in removing a network connection");
#endif
	PB = (PPCParamBlockPtr)sessPtr->endPB;
	KillReadAndWrite(sessPtr);
	portPtr = (PPCPortEntryPtr)sessPtr->portEntry; 
	FreeRemSess(sessPtr);
	if (PB)
		CompleteWithResult(PB,noErr);
	else if (portPtr) // Must be a PPCClose operation.
		CompleteClosePort(portPtr);
	
	// Don't change the next two lines or some day you may be sorry!!!
	ppcglobPtr->killSessPtr = sessPtr = ServeQueue(&ppcglobPtr->dspAbortQueue);
	if(sessPtr)
	{
		BlockMove((Ptr)&sessPtr->readBlk.dsp,(Ptr)&ppcglobPtr->dspRemovePB,sizeof(DSPParamBlock));
		RemoveConnectionEnd(sessPtr->abortFlag,
							true,
							(ProcPtr)RemoveCompletion, // Completion Routine
							&ppcglobPtr->dspRemovePB);
	}	
}

/*----------------------------------------------------------------------------------------
	StartNetworkSession
----------------------------------------------------------------------------------------*/
void StartNetworkSession(PPCStartPBPtr  startPB,
						  PPCPortEntryPtr    portPtr,
						  PPCGlobalParamsPtr ppcglobPtr)
{
	NetIPCParamsPtr	sessPtr;
		
	if ((startPB->userRefNum) && (!findUserByRef(startPB->userRefNum)))
	{
		CompleteWithResult(startPB,noUserRecErr);
		return;
	}
	if (!(sessPtr = GetRemSess(ppcglobPtr)))
	{
		CompleteWithResult(startPB,sessTableErr);
		return;
	}

	SetSessionTable((CommonSessionParamsPtr)sessPtr, portPtr, ppcglobPtr); 
	sessPtr->PBPtr = startPB; 				// First PB for this Session
	BeginNetworkConnection(sessPtr);
}

/*----------------------------------------------------------------------------------------
	AcceptNetworkSession
----------------------------------------------------------------------------------------*/
void AcceptNetworkSession(PPCAcceptPBPtr      acceptPB,
						   NetIPCParamsPtr  sessPtr)
{
	IPCAcceptBlkPtr        accept;
	
	accept =  (IPCAcceptBlkPtr)&((sessPtr)->writeBlk.buffer);
	accept->respType = SessAccept; 			// Stuff the message
	sessPtr->PBPtr   = acceptPB;
	AcceptNetworkConnection(sessPtr);
	
} // AcceptNetworkSession

/*----------------------------------------------------------------------------------------
	RejectNetworkSession
----------------------------------------------------------------------------------------*/
void RejectNetworkSession(PPCRejectPBPtr rejectPB,
						   NetIPCParamsPtr sessPtr)
{
	IPCRejectBlkPtr reject;

	reject =  (IPCRejectBlkPtr)&((sessPtr)->writeBlk.buffer);
	reject->respType = UserReject; 			// Stuff the message
	reject->rejectInfo = rejectPB->rejectInfo;
	sessPtr->PBPtr = rejectPB; 				// This will make sure that connection gets closed
	AcceptNetworkConnection(sessPtr);
} // RejectNetworkSession

/*----------------------------------------------------------------------------------------
	WriteNetworkData Begins to write the clients data immediatly if not already doing so in a
	previous request, otherwise it just leaves it on the queue for processing later.
----------------------------------------------------------------------------------------*/
void WriteNetworkData(PPCWritePBPtr writePB,
					  NetIPCParamsPtr  sessPtr) 
{
	short sr;
	
	AddWritePB(sessPtr,writePB);
	sr = spl(kNoInterrupts);
	if(!sessPtr->writePB)
	{
		sessPtr->writePB = GetWritePB(sessPtr);
		spl(sr);
		BeginWriteBlock(sessPtr);
	}
	else
		spl(sr);
} // WriteNetworkData

/*----------------------------------------------------------------------------------------
	ReadNetworkData begins to read data immediatly if not already doing so in a previous request,
	otherwise it just places the read on the queue for later processing.
----------------------------------------------------------------------------------------*/
void ReadNetworkData(PPCReadPBPtr readPB,
					  NetIPCParamsPtr  sessPtr)
{
	short sr;
	
	AddReadPB(sessPtr,readPB);
	sr = spl(kNoInterrupts);
	if(!sessPtr->readPB)
	{
		sessPtr->readPB = GetReadPB(sessPtr);
		spl(sr);
		BeginReadBlock(sessPtr);
	}
	else
		spl(sr);
} // ReadNetworkData


/*----------------------------------------------------------------------------------------
	EndNetworkSession
----------------------------------------------------------------------------------------*/
void EndNetworkSession(PPCEndPBPtr endPB,NetIPCParamsPtr sessPtr) 
{
	sessPtr->endPB = endPB; // Save it for later use.
	RemoveNetworkConnection(sessPtr,true);
}

/*----------------------------------------------------------------------------------------
	ListNetworkPorts is the entry point for a network Initiated IPCListPorts requests.  A session
	table is gotten, and initialized here.
----------------------------------------------------------------------------------------*/
void ListNetworkPorts(IPCListPortsPBPtr listPB,PPCGlobalParamsPtr ppcglobPtr)
{
	NetIPCParamsPtr sessPtr;
	PBWriteResPtr        res;
	
	res =(PBWriteResPtr)(listPB->Reserved);

	if (sessPtr = GetRemSess(ppcglobPtr))
	{
		SetSessionTable((CommonSessionParamsPtr)sessPtr, nil, ppcglobPtr); 
		sessPtr->PBPtr = listPB; // First PB for this Session
		sessPtr->portEntry = nil;
		BeginNetworkConnection(sessPtr);
	}
	else
		CompleteWithResult(listPB,sessTableErr);

} // ListNetworkPorts

/*----------------------------------------------------------------------------------------
	BeginNetworkConnection is where session establishment begins for both IPCListPorts and PPCStart.
	The first thing that occurs is an NBPLookup to find the destination address.
----------------------------------------------------------------------------------------*/
STATIC void BeginNetworkConnection(NetIPCParamsPtr sessPtr)
{
	IPCListPortsPBPtr listPB = sessPtr->PBPtr;
	PPCStartPBPtr  startPB = sessPtr->PBPtr;
	LocationNamePtr		  locationName;

	if (startPB->csCode == PPCStartCall)
	  locationName = startPB->locationName;
	else
	  locationName = listPB->locationName;

	
	sessPtr->sessState = AwaitLookupResp;
	NBPSetEntity((unsigned char *)sessPtr->miscData.nbpData.lkupentity,
				 (Ptr)&(locationName->u.nbpEntity.objStr),
				 (Ptr)&(locationName->u.nbpEntity.typeStr),
				 (Ptr)&(locationName->u.nbpEntity.zoneStr));
	
	LookupName((EntityName *)sessPtr->miscData.nbpData.lkupentity,
			   true,        // Get Asynchronously
			   (ProcPtr)NetLookUpCompletion, // Completion Routine
			   (unsigned char *)sessPtr->miscData.nbpData.lkupbuf, //lookup bufferptr
			   lkupbufSize, // BufferSize
			   (NBPparms *)&(sessPtr->miscData.nbpData.nbpPB));

}	// BeginNetworkConnection

/*---------------------------------------------------------------------------------------------------
	AcceptNetworkConnection
---------------------------------------------------------------------------------------------------*/
STATIC void AcceptNetworkConnection(NetIPCParamsPtr sessPtr)
{			
	sessPtr->sessState = AwaitAcceptSendComp;
			
	WriteToConnection(sizeof(IPCAcceptBlk),
					 (char *)&(sessPtr->writeBlk.buffer),
					 1, // eom 
					 1, // flush
					 true, // Asynchronously
					 (ProcPtr)RespSendCompletion, // Completion Routine
					 (DSPParamBlock *)&(sessPtr->writeBlk.dsp));
} // AcceptNetworkConnection

/*----------------------------------------------------------------------------------------
	NetLookUpCompletion is chained from BeginNetworkConnection.  This is a completion routine for
	an NBPLookup.  We are either trying to perform an IPCListPorts, or a PPCStart.
----------------------------------------------------------------------------------------*/
STATIC void NetLookUpCompletion(void)
{
	NBPparms			*nbpPB;
	NetIPCParamsPtr		sessPtr;
	PPCGlobalParamsPtr	ppcglobPtr;
	PPCStartPBPtr		startPB; 

	nbpPB = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(nbpPB,NetIPCParams,miscData.nbpData.nbpPB);
	startPB = (PPCStartPBPtr)sessPtr->PBPtr;	// Might be IPCListPorts as well.
	
	if (nbpPB->ioResult)
	{
		FreeRemSess(sessPtr);
		CompleteWithResult(startPB,nbpPB->ioResult);
	}
	else if	(nbpPB->parm.Lookup.numGotten == 0)
	{
		FreeRemSess(sessPtr);
		CompleteWithResult(startPB,noResponseErr);
	}
	else
	{
		BlockMove((char *)(nbpPB->parm.Lookup.retBuffPtr),
				  (char *)&(sessPtr->destAddr),
				  sizeof(AddrBlock));
				  	
	    ppcglobPtr = (PPCGlobalParamsPtr)getGlobal();
		sessPtr->sessState = AwaitDSPInit;
		CreateConnectionEnd((TRCCB *)&(sessPtr->ce), //ccbPtr
							    nil, // no user Routine
								DspSendQSize,
								sessPtr->sendQ,
								DspRecvQSize,
								sessPtr->recvQ,
								sessPtr->attnBuffer,
								0,	// ADSP picks our socket.
								ppcglobPtr->dspDrvrRef,
								true, // Asynhcronous
								(ProcPtr)ConnectionEndCompletion, // CompletionRoutine
								&sessPtr->readBlk.dsp);
	}
} // NetLookUpCompletion

/*----------------------------------------------------------------------------------------
	ConnectionEndCompletion is chained from NetLookUpCompletion.  This is a completion routine for
	a dspInit.  A new ADSP Connection Listener has been established.  The socket for this
	connection listener is different than the one where PPCToolBox name is registered.
----------------------------------------------------------------------------------------*/
STATIC void ConnectionEndCompletion(void)
{
  	NetIPCParams	*sessPtr;
  	PPCStartPBPtr	startPB;
	DSPParamBlock	*dsp;
  
	dsp = GetA0();
	sessPtr= TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	startPB = sessPtr->PBPtr;
    
  	if (dsp->ioResult)
	{
		CompleteWithResult(startPB,dsp->ioResult);
		FreeRemSess(sessPtr);
	}
	else
	{
		sessPtr->sessState = AwaitOpenComp;
		/* Copy the parameter Block */
		CopyReadBlk(sessPtr);

		OpenConnectionEnd(0, // remoteCID
						 (AddrBlock *)&(sessPtr->destAddr), // Remote Addess
						 (AddrBlock *)&(sessPtr->destAddr), // Filter Addess
						  0, // Initial sendSeq
						  0, // Initial sendWindow
						  0, // attnSendSeq
						  ocRequest, // ocMode
						  true, // Asynchronous
						  (ProcPtr)OpenCompletion, // Completion Routine
						  &sessPtr->readBlk.dsp);

	}
}

/*----------------------------------------------------------------------------------------
	OpenCompletion is chained from ConnectionEndCompletion.  This is a completion routine for
	a dspOpen call.  If all went well, we will have an ADSP connection established at this time.
	At this point we begin to diverge if we are performing an IPCListPorts, or a PPCStart.
	
	If PPCStart, then we transmit an IPCStartBlk structure (See PPCCommon.h for the format).
	If IPCListPorts, then we transmit an IPCListReqBlk structure. (See PPCCommon.h for the format).
----------------------------------------------------------------------------------------*/
STATIC void OpenCompletion(void)
{
	DSPParamBlock		*dsp;
  	NetIPCParams		*sessPtr;
	PPCStartPBPtr		startPB;
	UserEntryPtr		uEntry;

	dsp = GetA0();
	sessPtr= TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	startPB = sessPtr->PBPtr;	// in fact may be an IPCListPorts PB.
	if (dsp->ioResult)  
	{
		CompleteWithResult(startPB,sessPtr->sessState != AwaitAbortComp?
							noResponseErr:portClosedErr);
		sessPtr->PBPtr = NULL;
		RemoveNetworkConnection(sessPtr,false);
		return;

	}
	
	CopyReadBlk(sessPtr);	/* Copy the parameter Block */
	if (startPB->csCode == PPCStartCall)
	{
		IPCStartBlkPtr	startBlk;	// Protocol to begin PPCStart.

		startBlk =  &sessPtr->writeBlk.buffer.buffer.startBlk;
		if (startPB->userRefNum == 0)
			startBlk->userName[0] = 0; // No User Name
		
		// FIXME -- Can this be done earlier in the game???
		else if ((uEntry = findUserByRef(startPB->userRefNum)) == nil)
		{
			CompleteWithResult(startPB,noUserRecErr);
			RemoveNetworkConnection(sessPtr,false);
			return;
		}
		else
		{
			// This unscrambles the name we just looked up in the table.
			scramble (kNameKey, uEntry->name, (char *)(startBlk->userName));
		}
		sessPtr->sessState = AwaitStartSendComp;

		/* Prepare the header block */
		startBlk->reqType = SessRequest;
		startBlk->userData = startPB->userData;
		
		CopyPortName(&sessPtr->portEntry->portName,&startBlk->reqportName);
		CopyPortName(startPB->portName,&startBlk->destportName);
		GetLocationName((PPCGlobalParamsPtr)getGlobal(),sessPtr->portEntry,&startBlk->locationName);
		
		WriteToConnection(sizeof(IPCStartBlk),
						(char *)startBlk,
						1, // eom 
						1, // flush
						true, // Asynchronously
						(ProcPtr)StartSendCompletion, // Completion Routine
						(DSPParamBlock *)&(sessPtr->writeBlk.dsp)
						);
	}
	else if (startPB->csCode == IPCListPortsCall)
	{
		IPCListReqBlkPtr	list;
		IPCListPortsPBPtr 	listPB;
		
		sessPtr->sessState = AwaitListSendComp;
		list =  (IPCListReqBlkPtr)&(sessPtr->writeBlk.buffer);
		listPB = (IPCListPortsPBPtr)(startPB);
		list->reqType = ListRequest;
		list->startIndex = listPB->startIndex;
		list->requestCount = listPB->requestCount;
		CopyPortName(listPB->portName, &(list->portName));

		WriteToConnection(sizeof(IPCListReqBlk),
						 (char *)list,
						 1, // eom 
						 1, // flush
						 true, // Asynchronously
						 (ProcPtr)ListSendCompletion, // Completion Routine
						 &sessPtr->writeBlk.dsp);
	}
} // OpenCompletion

/*----------------------------------------------------------------------------------------
	StartSendCompletion is chained from OpenCompletion.  This is a completion routine for a 
	dspWrite call.  At this point we have written the IPCStartBlk structure.  We now prepare
	to read in the reply.  The format of the reply is described in an AuthInfoBlk structure.
----------------------------------------------------------------------------------------*/
STATIC void StartSendCompletion(void)
{
	DSPParamBlock	*dsp;
  	NetIPCParams	*sessPtr;
	PPCStartPBPtr	startPB;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);
	if (dsp->ioResult)
	{
		if(startPB = sessPtr->PBPtr)
		{
			sessPtr->PBPtr = NULL;
			CompleteWithResult(startPB,dsp->ioResult);
		}
		
		RemoveNetworkConnection(sessPtr,false);
	}
	else
	{
		if(sessPtr->sessState == AwaitStartSendComp)
			sessPtr->sessState = AwaitAcceptResp;
		else
			sessPtr->sessState = AwaitAuthAccept;
		ReadFromConnection(sizeof(IPCBufBlk),
						  (char *)&(sessPtr->readBlk.buffer),
						  true, // Asynchronously
						  (ProcPtr)StartCommandCompletion, // completion Routine
						  &sessPtr->readBlk.dsp);
	}
} // StartSendCompletion

/*----------------------------------------------------------------------------------------
	StartCommandCompletion
----------------------------------------------------------------------------------------*/

STATIC void StartCommandCompletion(void)
{
	DSPParamBlock	*dsp;
	IPCReadBlk		*rblk;
  	NetIPCParams	*sessPtr;
	PPCStartPBPtr   startPB;
	IPCAcceptBlk	*ablk;
	AuthInfoBlk		*auth1, *auth2;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	rblk = &sessPtr->readBlk;

	startPB = sessPtr->PBPtr;
	startPB->rejectInfo = nil; // Assume this (If reject, this will change)

	if(sessPtr->sessState != AwaitAcceptResp &&
	   sessPtr->sessState != AwaitAuthAccept)
		startPB->ioResult = portClosedErr;		// Port must be closing.
	else if(dsp->ioResult)
		startPB->ioResult = dsp->ioResult;
	else if(!dsp->u.ioParams.actCount && !dsp->u.ioParams.eom)
		startPB->ioResult = destPortErr;
	else
	{

		ablk = (IPCAcceptBlk *)&(rblk->buffer);
		switch (ablk->respType)
		{
			case SessAccept:
			{
				sessPtr->sessState = DataXferState;
				sessPtr->PBPtr = NULL; // Nothing in the Que
				sessPtr->originator = true;	// we started this session.
				startPB->sessRefNum = sessPtr->sessRefNum;
				CompleteWithResult(startPB,noErr);
				return;
			}
			break;
			case AuthContinue:
			{
#ifdef DEBUG
				if(sessPtr->sessState != AwaitAcceptResp)
					Debugger();
#endif
				// Server wants the connection to be authenticated
				auth1 = (AuthInfoBlk *)ablk;
				if (!(GetAuthInfo((long *)auth1->authInfo, startPB->userRefNum)))
					startPB->ioResult = noUserRecErr;
				else
				{
					sessPtr->sessState = AwaitAuthRespSend;
					auth2 =(AuthInfoBlk *)&(sessPtr->writeBlk.buffer);
					auth2->respType = AuthInfoResp;
					auth2->authInfo[0] = auth1->authInfo[0];
					auth2->authInfo[1] = auth1->authInfo[1];
					WriteToConnection(sizeof(AuthInfoBlk),
									 (char *)auth2,
									 1, // eom 
									 1, // flush
									 true, // Asynchronously
									 (ProcPtr)StartSendCompletion, // Completion Routine
									 (DSPParamBlock *)&(sessPtr->writeBlk.dsp)
									 );
					return;
	
				}
			}
			break;
			case SessReject:
			{
				switch (ablk->rejectInfo)
				{
					case notVisible:
					case noPort:
						startPB->ioResult = destPortErr;
						break;
					case noInforms:
						startPB->ioResult = noInformErr;
						break;
					case noGuestEnabled:
						startPB->ioResult = guestNotAllowedErr;
						break;
					case iacDisabled:
						startPB->ioResult = userRejectErr;
						break;
					case noUserRec:	// UnKnown User on target computer.
						startPB->ioResult = noUserNameErr;
						break;
					case AuthFailure:
						startPB->ioResult = authFailErr;
						break;
				} // end switch ablk->rejectInfo
				startPB->rejectInfo = 0;
				
			}
			break;
			case UserReject:
			{
				startPB->ioResult = userRejectErr;
				startPB->rejectInfo = ablk->rejectInfo;
			}
			break;
			default:	// I don't think we can get here, unless were talking to a non-PPCToolBox.
				startPB->ioResult = networkErr;
			break;
		} // end switch ablk->respType
  } // End else
  CompleteWithResult(startPB,startPB->ioResult);
  sessPtr->PBPtr = NULL;
	RemoveNetworkConnection(sessPtr,false);

}

/*----------------------------------------------------------------------------------------
	ListSendCompletion is chained from OpenCompletion.
----------------------------------------------------------------------------------------*/

STATIC void ListSendCompletion(void)
{
	DSPParamBlock		*dsp;
	NetIPCParams        *sessPtr;
	IPCListPortsPBPtr    listPB;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);
	listPB = sessPtr->PBPtr;
	if (dsp->ioResult != noErr)
	{
		CompleteWithResult(listPB,dsp->ioResult);
  		sessPtr->PBPtr = NULL;
		RemoveNetworkConnection(sessPtr,false);
	}
	else
	{
		sessPtr->sessState = AwaitListRead;
		ReadFromConnection(sizeof(PortInfoRec) * listPB->requestCount,
						  (Ptr)listPB->bufferPtr,
						  true, // Asynchronously
						  (ProcPtr)ListReadCompletion, // completion Routine
						  &sessPtr->readBlk.dsp);
	}
} // ListSendCompletion

/*----------------------------------------------------------------------------------------
	ListReadCompletion is chained from ListSendCompletion.
----------------------------------------------------------------------------------------*/
STATIC void ListReadCompletion(void)
{
	DSPParamBlock		*dsp;
	NetIPCParams		*sessPtr;
	IPCListPortsPBPtr   listPB;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	listPB = sessPtr->PBPtr;
	if (dsp->ioResult != noErr)
	{
		CompleteWithResult(listPB,dsp->ioResult);
	  	sessPtr->PBPtr = NULL;
		RemoveNetworkConnection(sessPtr,false);
	}
	else
	{
		sessPtr->sessState = AwaitListResp;
		ReadFromConnection(sizeof(IPCListRespBlk),
						  (char *)&(sessPtr->readBlk.buffer),
						  true, // Asynchronously
						  (ProcPtr)ListCommandCompletion, // completion Routine
						  &sessPtr->readBlk.dsp);
	}
} // ListReadCompletion

/*----------------------------------------------------------------------------------------
	ListCommandCompletion is chained from ListReadCompletion.
----------------------------------------------------------------------------------------*/
STATIC void ListCommandCompletion(void)
{
	DSPParamBlock		*dsp;
	IPCReadBlk			*rblk;
  	NetIPCParams		*sessPtr;
	IPCListPortsPBPtr   listPB;
	IPCListRespBlk		*lblk;
	
	dsp = GetA0();
	rblk = (IPCReadBlk *)dsp;
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	listPB = sessPtr->PBPtr;
  	sessPtr->PBPtr = NULL;
	if (dsp->ioResult != noErr)
		listPB->ioResult = dsp->ioResult;
	else
	{

		lblk = (IPCListRespBlk *)&(rblk->buffer);
		if (lblk->respType == ListResp)
		{
			listPB->ioResult = noErr;
			listPB->actualCount = lblk->actualCount;
		}
		else
			listPB->ioResult = networkErr;
	}
	CompleteWithResult(listPB,listPB->ioResult);
  	sessPtr->PBPtr = NULL;
	RemoveNetworkConnection(sessPtr,false);
} // ListCommandCompletion

/*----------------------------------------------------------------------------------------
	ConnectionRequestCompletion is chained from PPCInit.c:SetUpConnectionListeners, and 
	RepostConnectionListener.
	
	On Entry A0 has a pointer to a completed dspCLListen parameter block.  This is the
	point at which session conception begins for PPCToolbox.  Either its an IPCListPorts
	or a PPCStart that initiated the session, and we are here receiving it.
----------------------------------------------------------------------------------------*/
void ConnectionRequestCompletion(void)
{
	DSPParamBlock *dsp;
	NetIPCParamsPtr sessPtr;
	TRopenParams *open, *open2;
	DSPParamBlock *dsp2;
	PPCGlobalParams *ppcglobPtr;
	
	dsp = GetA0();
	ppcglobPtr = (PPCGlobalParams *)getGlobal();
	if (dsp->ioResult == errAborted)
		return; // Looks like someone wants to remove listeners
	else if ((!(ppcglobPtr->allowIncommingRequests)) ||
				(!(sessPtr = GetRemSess(ppcglobPtr))))
		RejectConnectionRequest(dsp, 
								true, // Asynchronous
								(ProcPtr)ARepostConnectionListener
								);
    else
	{
		sessPtr->portEntry = 0; // we don't have them yet
		sessPtr->endPB = 0;     // we don't have them yet
		dsp2 = (DSPParamBlock *)&(sessPtr->writeBlk.dsp);
		open = (TRopenParams *)&(dsp->u.initParams);
		/* Copy the Session Parameters */
		open2 = (TRopenParams *)&(dsp2->u.initParams);
		open2->remoteCID = open->remoteCID;
	    open2->remoteAddress.aNet = open->remoteAddress.aNet;
		open2->remoteAddress.aNode = open->remoteAddress.aNode;
		open2->remoteAddress.aSocket = open->remoteAddress.aSocket;
		open2->sendSeq = open->sendSeq;
		open2->sendWindow = open->sendWindow;
		open2->attnSendSeq = open->attnSendSeq;
		sessPtr->sessState = AwaitRecvDSPInit;
		CreateConnectionEnd((TRCCB *)&(sessPtr->ce), //ccbPtr
							  nil, // no user Routine (we need a routine to release session
							  DspSendQSize,
							  sessPtr->sendQ,
							  DspRecvQSize,
							  sessPtr->recvQ,
							  sessPtr->attnBuffer,
							  ppcglobPtr->adspSocket,
							  ppcglobPtr->dspDrvrRef,
							  true, // Asynhcronous
							  (ProcPtr)RespConnectionEndCompletion, // CompletionRoutine
							  (DSPParamBlock *)&(sessPtr->readBlk.dsp)
					  		);

		RepostConnectionListener(dsp);
	}

} // ConnectionRequestCompletion

/*----------------------------------------------------------------------------------------
	RespConnectionEndCompletion is chained from ConnectionRequestCompletion.
	
	On Entry A0 contains a pointer to a completed dspInit parameter block that
	corresponds to the readBlk of our session.
	In the session table there is a copy of a completed dspCLListen PB in the
	writeBlk.  We use this PB to make the dspOpen since the remoteCID, remoteAddress,
	sendSeq, sendWindow, and attnSendSeq numbers are already correctly filled in.
----------------------------------------------------------------------------------------*/
STATIC void RespConnectionEndCompletion(void)
{
	DSPParamBlock *dsp;
	IPCReadBlk         *rblk;
  	NetIPCParams       *sessPtr;
	TRopenParams *open;
	DSPParamBlock *dsp2;
	
	dsp = GetA0();
	rblk = (IPCReadBlk *)dsp;
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	dsp2 = (DSPParamBlock *)&(sessPtr->writeBlk.dsp);
	open = (TRopenParams *)&(dsp2->u.initParams);

	if (dsp->ioResult) // some error occured
		FreeRemSess(sessPtr);
	else
	{
	  sessPtr->sessState = AwaitocAcceptComp;
	  OpenConnectionEnd(open->remoteCID, // remoteCID
					 (AddrBlock *)&(open->remoteAddress), // Remote Addess
					 (AddrBlock *)&(open->remoteAddress), // Filter Addess
					  open->sendSeq, // Initial sendSeq
					  open->sendWindow, // Initial sendWindow
					  open->attnSendSeq, // attnSendSeq
					  ocAccept, // ocMode
					  true, // Asynchronous
					  (ProcPtr)RespOpenCompletion, // Completion Routine
					  (DSPParamBlock *)&(sessPtr->readBlk.dsp)
					);
	}
} // RespConnectionEndCompletion

/*----------------------------------------------------------------------------------------
	RespOpenCompletion is chained from RespConnectionEndCompletion.  At this point the 
	ADSP session is fully established.
	
	On Entry A0 cointains a pointer to a completed dspOpen parameter block that corresponds
	to the sessions writeBlk.  Read in the first block of data that was written by PPC.
----------------------------------------------------------------------------------------*/
STATIC void RespOpenCompletion(void)
{
	DSPParamBlock *dsp;
  	NetIPCParams       *sessPtr;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);

	if (dsp->ioResult)
	{
		// some Error occured
		RemoveNetworkConnection(sessPtr,false);
	}
	else
	{
		sessPtr->sessState = AwaitRecvSessReq;
		ReadFromConnection(sizeof(IPCStartBlk),
					   (char *)&(sessPtr->readBlk.buffer),
					   true, // Asynchronously
					   (ProcPtr)RecvSessReqCompletion, // completion Routine
					   &sessPtr->readBlk.dsp);
	}


} // RespOpenCompletion

/*----------------------------------------------------------------------------------------
	RecvSessReqCompletion is chained from RespOpenCompletion.
----------------------------------------------------------------------------------------*/
STATIC void RecvSessReqCompletion(void)
{
	DSPParamBlock *dsp;
	IPCReadBlk         *rblk;
  	NetIPCParams       *sessPtr;
	IPCStartBlk        *sblk;
	PPCPortEntryPtr    portPtr;
	PPCGlobalParams    *ppcglobPtr;
	IPCAcceptBlkPtr    accept;
	IPCListReqBlkPtr   list;
	unsigned short sr;
	
	dsp = GetA0();
	ppcglobPtr = (PPCGlobalParams *)getGlobal();
	rblk = (IPCReadBlk *)dsp;
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	sblk = (IPCStartBlk *)&(rblk->buffer);
	accept =  (IPCAcceptBlkPtr)&(sessPtr->writeBlk.buffer);

	
	if (dsp->ioResult == noErr && dsp->u.ioParams.actCount != 0) 
	{
		
    	/* Copy the parameter Block */
		CopyReadBlk(sessPtr);
		switch(sblk->reqType)
		{
			case SessRequest:
				if (portPtr = CheckPortName((PPCPortPtr)&(sblk->destportName),ppcglobPtr))
				{
					// We Have a valid Port
							
					SetSessionTable((CommonSessionParamsPtr)sessPtr, portPtr, ppcglobPtr); 

					if (portPtr->networkVisible)
					{
						// We have Port exported for network access
						if (portPtr->authRequest)
						{
							if (sblk->userName[0] == 0)
							{
								sessPtr->miscData.btreeData.guestRec = true;
							}
							else
								sessPtr->miscData.btreeData.guestRec = false;
							
							// Requires Authentication
							sessPtr->sessState = AwaitBtreeComp;
							if (ppcglobPtr->ugFile.refNum != 0)
							{
								getUserRec((UserRecPb *)&(sessPtr->miscData.btreeData),
										0, // Key ID
										(char *)sblk->userName,
										(ProcPtr)UserRecCompletion,
										ppcglobPtr->ugFile.refNum);
							}
							else
							{
								accept->rejectInfo = noUserRec;	// Trouble with BTree, deny knowing this user.
								accept->respType = SessReject;
								sessPtr->PBPtr = NULL;
								AcceptNetworkConnection(sessPtr);

							}
							return;
						}
						else
						{
							ProcessInform(sessPtr, 0 );
							return;
						}
					}
					else
						accept->rejectInfo = notVisible; //Only Internal
				}
				else
					accept->rejectInfo = noPort;
				accept->respType = SessReject;
				sessPtr->PBPtr = NULL;
				AcceptNetworkConnection(sessPtr);
				return;

			case ListRequest:
				DeleteFromQueue(&ppcglobPtr->RemSessQueue,(unsigned long)sessPtr,PointerCompare);
				SetSessionTable((CommonSessionParamsPtr)sessPtr, nil, ppcglobPtr); 
				sessPtr->PBPtr = NULL;
				sessPtr->portEntry = nil;
				sessPtr->sessState = AwaitListWriteComp;
				
				// For Later Guest Record Checkup
				list = (IPCListReqBlkPtr)sblk;
				list->userName[0] =0;

				sessPtr->miscData.btreeData.guestRec = true;

				EnQueue(sessPtr,&ppcglobPtr->ListSessQueue);
				sr = spl(kNoInterrupts);
				if (!ppcglobPtr->listsessPtr)
				{
					// we can process the request
					ppcglobPtr->listsessPtr = ServeQueue(&ppcglobPtr->ListSessQueue);
					spl(sr);
					ppcglobPtr->totalCount =  0; // Total count so far
					ppcglobPtr->listIndex = list->startIndex;
	                if (ppcglobPtr->ugFile.refNum != 0)
					{
						getUserRec((UserRecPb *)&(sessPtr->miscData.btreeData),
										0,	// Key ID
										(char *)list->userName,
										(ProcPtr)GuestRecCompletion,
										ppcglobPtr->ugFile.refNum);
					}
					else
					{
						ppcglobPtr->guestUnchecked = false;
						ListWrite(sessPtr);
					}
	
	    		}
				else
				{
					spl(sr);
				}
				return;
		} // end switch
	}
	// If it is not anyyone of the above abort it any way
	RemoveNetworkConnection(sessPtr,false);
} // RecvSessReqCompletion 


/*----------------------------------------------------------------------------------------
	GuestRecCompletion chained from RecvSessReqCompletion and UserRecCompletion.
----------------------------------------------------------------------------------------*/
STATIC void GuestRecCompletion(UserRecPb	*uRec)
{
	 NetIPCParams       *sessPtr;
	 PPCGlobalParams    *ppcglobPtr;


	 sessPtr = TOP_OF_RECORD_PTR(uRec,NetIPCParams,miscData.btreeData);
	 ppcglobPtr = (PPCGlobalParams *)getGlobal();

	 if (sessPtr->sessState == AwaitListWriteComp)
	 {
		ppcglobPtr->guestUnchecked = false;
		if ((uRec->btPb.ioResult == noErr) && 
			(uRec->u.ulRecord.ServerFlags & IACGuestEnabled))
			ppcglobPtr->guestUnchecked = true;
		ListWrite(sessPtr);
	 }

} // GuestRecCompletion
 
/*----------------------------------------------------------------------------------------
	UserRecCompletion chained from RecvSessReqCompletion.
----------------------------------------------------------------------------------------*/
STATIC void UserRecCompletion(UserRecPb	*uRec)
{
  	NetIPCParams       *sessPtr;
	IPCAcceptBlkPtr    accept;				// <44>
	AuthInfoBlkPtr	   auth;				// <44>
	
	sessPtr = TOP_OF_RECORD_PTR(uRec,NetIPCParams,miscData.btreeData);
	accept =  (IPCAcceptBlkPtr)&(sessPtr->writeBlk.buffer);

	if (sessPtr->sessState != AwaitBtreeComp)
	{
		RemoveNetworkConnection(sessPtr,false);
	}
	else if (uRec->btPb.ioResult) 
	{
		accept->rejectInfo = noUserRec;		// User name not in my BTree.
		accept->respType = SessReject;
  		sessPtr->PBPtr = NULL;
		AcceptNetworkConnection(sessPtr);
	}
	else if (uRec->guestRec) 
	{
		if (uRec->u.ulRecord.ServerFlags & IACGuestEnabled)
					// Guest is enabled
			ProcessInform(sessPtr, 0x0001);		
		else
		{
			accept->rejectInfo = noGuestEnabled;
			accept->respType = SessReject;
		  	sessPtr->PBPtr = NULL;
			AcceptNetworkConnection(sessPtr);
		}
	}
	else if (uRec->u.record.UserFlags & 0x0001)
	{
		sessPtr->sessState = AwaitRandWrite;
		auth = (AuthInfoBlkPtr)&(sessPtr->writeBlk.buffer);
		auth->respType = AuthContinue; 		// Send AuthInfo
		auth->authInfo[0] = uRec->random[0];
		auth->authInfo[1] = uRec->random[1];
		WriteToConnection(sizeof(AuthInfoBlk),
						(char *)&(sessPtr->writeBlk.buffer),
						1, 					// eom 
						1, 					// flush
						true, 				// Asynchronously
						(ProcPtr)AuthSendCompletion, // Completion Routine
						(DSPParamBlock *)&(sessPtr->writeBlk.dsp));
	}
	else
	{
		accept->rejectInfo = iacDisabled;	// <44>
		accept->respType = SessReject;		// <44>
	  	sessPtr->PBPtr = NULL;				// <44>
		AcceptNetworkConnection(sessPtr);
	}
}

/*----------------------------------------------------------------------------------------
	AuthSendCompletion is chained from UserRecCompletion.
----------------------------------------------------------------------------------------*/
STATIC void AuthSendCompletion(void)
{
	DSPParamBlock	*dsp;
	NetIPCParams    *sessPtr;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);
	
	if ((dsp->ioResult) || (sessPtr->sessState != AwaitRandWrite))
	{
		RemoveNetworkConnection(sessPtr,false);
	}
	else
	{
		sessPtr->sessState = AwaitAuthResp;
		ReadFromConnection(sizeof(AuthInfoBlk),
						  (char *)&(sessPtr->readBlk.authBuf),
						  true, 			// Asynchronously
						  (ProcPtr)AuthRespCompletion, // completion Routine
						  &sessPtr->readBlk.dsp);
	}

} // AuthSendCompletion

/*----------------------------------------------------------------------------------------
	AuthRespCompletion is chained from AuthSendCompletion.
----------------------------------------------------------------------------------------*/
STATIC void AuthRespCompletion(void)
{
	DSPParamBlock *dsp;						// <44>
	AuthInfoBlkPtr 	   auth;				// <44>
	NetIPCParams		*sessPtr;
	IPCReadBlk         *rblk;
	UserRecPbPtr		uRec;
	IPCAcceptBlkPtr    accept;

	dsp = GetA0();
	rblk = (IPCReadBlk *)dsp;
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk.dsp);
	auth = (AuthInfoBlkPtr)&(sessPtr->readBlk.authBuf);


	if ((dsp->ioResult) || (sessPtr->sessState != AwaitAuthResp)
	     || (dsp->u.ioParams.actCount == 0) || (auth->respType != AuthInfoResp))
	{
		RemoveNetworkConnection(sessPtr,false);
	}
	else 
	{
		uRec = (UserRecPbPtr)&(sessPtr->miscData.btreeData);
		if ((auth->authInfo[0] == uRec->encryptedRandom[0]) &&
			(auth->authInfo[1] == uRec->encryptedRandom[1]))
		{
			ProcessInform(sessPtr, uRec->u.record.UserFlags); // Authenticated
		}
		else
		{
			accept =  (IPCAcceptBlkPtr)&(sessPtr->writeBlk.buffer);
			accept->rejectInfo = AuthFailure;
			accept->respType = SessReject;
		  	sessPtr->PBPtr = NULL;
			AcceptNetworkConnection(sessPtr);
		}
	}
}


/*----------------------------------------------------------------------------------------
	ProcessInform gets a pending PPCInform for the associated port and completes it, it also
	writes back the session accept response.  If no Informs are pending for the port, PPC Rejects
	the request and sets the rejectInfo to noInforms.
----------------------------------------------------------------------------------------*/
STATIC void ProcessInform(NetIPCParams *sessPtr,unsigned short userFlags)
{
	IPCStartBlk        *sblk;				// <44>
	PPCPortEntryPtr	portPtr;
	PPCInformPBPtr	informPB; 
	IPCAcceptBlkPtr    accept;
	PBWriteResPtr      res;

    
	sblk = (IPCStartBlk *)&(sessPtr->readBlk.buffer);
	portPtr = sessPtr->portEntry;
	accept =  (IPCAcceptBlkPtr)&(sessPtr->writeBlk.buffer);

	if (!(informPB = ServeQueue(&portPtr->informPBQueue)))
	{
		accept->respType = SessReject; 		// <44>  Stuff the message
		accept->rejectInfo = noInforms; 	// <44> No Oustanding Informs
	  	sessPtr->PBPtr = NULL;				// <44>
		AcceptNetworkConnection(sessPtr);
		return;
	}
		
	informPB->sessRefNum = sessPtr->sessRefNum;
	informPB->userData = sblk->userData;	// <44>
	informPB->serviceType = ppcServiceRealTime;
	informPB->requestType = ppcRemoteOrigin;

	res = (PBWriteResPtr)informPB->Reserved;
	if (userFlags & 0x0001)
		res->multiFinder = 0x01;
	else
		res->multiFinder = 0x00;

	if (informPB->portName)
		CopyPortName(&(sblk->reqportName), informPB->portName);
	
	if (informPB->locationName)
		BlockMove((char *)&(sblk->locationName), (char *)(informPB->locationName), sizeof(LocationNameRec));
	
	if(sblk->userName[0] < 31)
	{
		if (sblk->userName[0] == 0)
			BlockMove("\p<Guest>",sessPtr->userName,8); 
		else
			BlockMove((char *)sblk->userName,sessPtr->userName, sblk->userName[0] +1); 
		if (informPB->userName)
			BlockMove((char *)sblk->userName, (informPB->userName), sblk->userName[0] +1); 
	}

	sessPtr->originator = false;			// We did not originate this session.
	
	if (informPB->autoAccept)
	{
		sessPtr->PBPtr = informPB;
		accept->respType = SessAccept; 		// <44> Stuff the message
		AcceptNetworkConnection(sessPtr);
	}
	else
	{
	  	sessPtr->PBPtr = NULL;
		sessPtr->sessState = AwaitAcceptReq;
		CompleteWithResult(informPB,noErr);
	}
}

/*----------------------------------------------------------------------------------------
	RespSendCompletion is chained from AcceptNetworkConnection.
----------------------------------------------------------------------------------------*/
STATIC void RespSendCompletion(void)
{
	DSPParamBlock		*dsp;
    NetIPCParams		*sessPtr;
	PPCAcceptPBPtr		acceptPB;
	PBWriteResPtr		res;
	OSErr               result;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);

	acceptPB = sessPtr->PBPtr;
	res =(PBWriteResPtr)(acceptPB->Reserved);

	if ((acceptPB) && (acceptPB->csCode == PPCRejectCall))
		sessPtr->PBPtr = NULL;
		
	if ((result=dsp->ioResult)  || (acceptPB==nil) || acceptPB->csCode == PPCRejectCall)
	{
		sessPtr->PBPtr = NULL;
		RemoveNetworkConnection(sessPtr,false);
	}
	else
	{
		sessPtr->sessState = DataXferState;
		sessPtr->PBPtr = NULL;
	}
	if (result && acceptPB && acceptPB->csCode == PPCInformCall)
		SetPortInformQue((PPCInformPBPtr)acceptPB, sessPtr->portEntry); // Add the PB back
	else if (acceptPB)
		CompleteWithResult(acceptPB,result?noSessionErr:noErr);
} // RespSendCompletion

/*----------------------------------------------------------------------------------------
	ARepostConnectionListener is chained from ConnectionRequestCompletion.
----------------------------------------------------------------------------------------*/
STATIC void ARepostConnectionListener(void)
{
	RepostConnectionListener(GetA0());
}

STATIC void RepostConnectionListener(DSPParamBlock *dsp)
{
	PPCGlobalParams    *ppcglobPtr;
	ppcglobPtr = (PPCGlobalParams *)getGlobal();
	if (ppcglobPtr->allowIncommingRequests)
	{
		// Incomming Requests are allowed.
    	if ((ListenConnectionRequest(dsp,   true,
								(ProcPtr)ConnectionRequestCompletion)))
		{
#ifdef DEBUG
			Debugger(); // should not happen
#endif
		}
	}
} // RepostConnectionListener

/*----------------------------------------------------------------------------------------
	ListWrite is called to write out the portInfo on an established network connection.  
----------------------------------------------------------------------------------------*/
STATIC void ListWrite(NetIPCParams *sessPtr)
{
	IPCReadBlk         *rblk;				// <44>
	PPCGlobalParams    *ppcglobPtr;
	IPCListReqBlkPtr   list;				// <44>
	Boolean            eom;
	unsigned short     actCount=0, remCount, reqCount;

	ppcglobPtr = (PPCGlobalParams *)getGlobal();
	rblk = (IPCReadBlk *)&(sessPtr->readBlk);
	list = (IPCListReqBlkPtr)&(rblk->buffer);
	remCount = list->requestCount - ppcglobPtr->totalCount;
	if (remCount > MaxListEntries)
		reqCount = MaxListEntries;
	else
		reqCount = remCount;
		
	eom = true;                       		// assume we are going to exhaust
	ppcglobPtr->listContinue = false; 		//  assume we are going to exhaust
	if (!(GetPortNames(ppcglobPtr,
					   &list->portName,		// <44>
					   false,         		// nework request
					   ppcglobPtr->guestUnchecked, // Secure Machine ports or Not
					   ppcglobPtr->listIndex,
					   ppcglobPtr->listBuffer,
					   reqCount,
					   &actCount))) 
    {
		ppcglobPtr->totalCount += actCount;
		if (list->requestCount > ppcglobPtr->totalCount)
		{
			// User can receive some more
			eom = false; // we have some more processing
			ppcglobPtr->listContinue = true;

		 }
	}
	else
		ppcglobPtr->totalCount += actCount;
	
	ppcglobPtr->listIndex += actCount;

	WriteToConnection(sizeof(PortInfoRec) * actCount,
					(char *)ppcglobPtr->listBuffer,
					eom, 					// eom 
					1, 						// flush
					true, 					// Asynchronously
					(ProcPtr)ListWriteCompletion, // Completion Routine
					(DSPParamBlock *)&(sessPtr->writeBlk.dsp));
} // ListWrite

/*----------------------------------------------------------------------------------------
	ListWriteCompletion is chained from ListWrite.
----------------------------------------------------------------------------------------*/
STATIC void ListWriteCompletion(void)
{
	DSPParamBlock		*dsp;
	IPCReadBlk         *rblk;
  	NetIPCParams       *sessPtr, *sessPtr1;
	IPCListReqBlkPtr   list;
	IPCListRespBlkPtr  resp;
	PPCGlobalParams    *ppcglobPtr;

	dsp = GetA0();
	ppcglobPtr = (PPCGlobalParams *)getGlobal();
	rblk = (IPCReadBlk *)dsp;
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);
    list = (IPCListReqBlkPtr)&(rblk->buffer);

	if (dsp->ioResult)
	{
		// some Error occured
		RemoveNetworkConnection(sessPtr,false);
		ppcglobPtr->listsessPtr = sessPtr1 = ServeQueue(&ppcglobPtr->ListSessQueue);
		if (sessPtr1)
		{
		    // First Lookfor Guest Icon 2/28/90
			list = (IPCListReqBlkPtr)&sessPtr1->readBlk.buffer;
			ppcglobPtr->totalCount = 0;
			ppcglobPtr->listIndex = list->startIndex;

			if (ppcglobPtr->ugFile.refNum != 0)
			{
				getUserRec((UserRecPb *)&(sessPtr1->miscData.btreeData),
						0, // Key ID
						(char *)list->userName,
						(ProcPtr)GuestRecCompletion,
						ppcglobPtr->ugFile.refNum);
			}
			else
			{
				ppcglobPtr->guestUnchecked = false;
				ListWrite((NetIPCParams *)(ppcglobPtr->listsessPtr));
			}
		}
	}
	else if (ppcglobPtr->listContinue)
		ListWrite(sessPtr);   // need some more writing
	else
	{
		resp =  (IPCListRespBlkPtr)&(sessPtr->writeBlk.buffer);
	    resp->respType = ListResp; // Stuff the message
		resp->actualCount = ppcglobPtr->totalCount;
		sessPtr->sessState = AwaitListRespComp;
	    WriteToConnection(sizeof(IPCListRespBlk),
						  (char *)&(sessPtr->writeBlk.buffer),
						  1, // eom 
						  1, // flush
						  true, // Asynchronously
						  (ProcPtr)ListRespCompletion, // Completion Routine
						  &sessPtr->writeBlk.dsp);
	}
} // ListWriteCompletion

/*----------------------------------------------------------------------------------------
	ListRespCompletion is chained from ListWriteCompletion.  We have just completed the writing
	the response to an IPCListPorts request.  If we have another request pending, we begin processing
	that request here.
----------------------------------------------------------------------------------------*/

STATIC void ListRespCompletion(void)
{
	DSPParamBlock *dsp;
	IPCReadBlk         *rblk;
  	NetIPCParams       *sessPtr, *sessPtr1;
	IPCListReqBlkPtr   list;
	PPCGlobalParams    *ppcglobPtr;

	dsp = GetA0();
	ppcglobPtr = (PPCGlobalParams *)getGlobal();
	rblk = (IPCReadBlk *)dsp;
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);
	list = (IPCListReqBlkPtr)&(rblk->buffer);

	RemoveNetworkConnection(sessPtr,false);
    
	ppcglobPtr->listsessPtr = sessPtr1 = ServeQueue(&ppcglobPtr->ListSessQueue);
	if (sessPtr1)
	{
		// First Lookfor Guest Icon 2/28/90
		list = (IPCListReqBlkPtr)&sessPtr1->readBlk.buffer;
		ppcglobPtr->totalCount = 0;
		ppcglobPtr->listIndex = list->startIndex;
		if (ppcglobPtr->ugFile.refNum != 0)
		{
			getUserRec((UserRecPb *)&(sessPtr1->miscData.btreeData),
					0, // Key ID
					(char *)list->userName,
					(ProcPtr)GuestRecCompletion,
					ppcglobPtr->ugFile.refNum);
		}
		else
		{
			ppcglobPtr->guestUnchecked = false;
			ListWrite(ppcglobPtr->listsessPtr);
		}
	}
} // ListRespCompletion

/*----------------------------------------------------------------------------------------
	BeginWriteBlock is the first real step in processing of a writePB.  It initializes the reserved
	fields for in the PPCWrite PB, and if we are to write a header, we do so, otherwise we call
	WriteBlockData to append data onto the current block.
----------------------------------------------------------------------------------------*/
STATIC void BeginWriteBlock(NetIPCParamsPtr sessPtr)
{
	PBWriteResPtr          res;				// <44>
	PPCWritePBPtr		   writePB;
	
	// Setup reserved fields in PPCWrite PB to initial values.
	writePB = sessPtr->writePB;
	
	res = (PBWriteResPtr)(writePB->Reserved);
	res->length = writePB->bufferLength;
	res->curBufPos = writePB->bufferPtr;
	if(sessPtr->writeHdr)
	{
		IPCHdrBlk *hdr;						// <44>
		Boolean HeaderOnly;					// True if client just wants the header only written.
		
		// This pisses me off, If I could change the protocol,
		// I could save 12 bytes, and the following 4 lines of code.
		hdr = &sessPtr->writeBlk.buffer.buffer.headerBlk;
		hdr->blockCreator = writePB->blockCreator;
		hdr->blockType = writePB->blockType;
		hdr->userData = writePB->userData;
		HeaderOnly = !writePB->more &&  !writePB->bufferLength;	// usually false!
		sessPtr->writeHdr = !writePB->more;						// for next PPCWrite pb.

		WriteToConnection(sizeof(IPCHdrBlk),
						  (char *)hdr,
						  HeaderOnly,		// eom.
						  HeaderOnly,		// flush.
						  true,				// Asynchronously
						  (ProcPtr)WriteHdrCompletion, // Completion Routine
						  &sessPtr->writeBlk.dsp);
	}
	else
		WriteBlockData(sessPtr);
}


/*----------------------------------------------------------------------------------------
	WriteHdrCompletion is chained from BeginWriteBlock.  Assuming success, and their is data that
	is to follow the header, we call WrtieBlockData to handle that, otherwise we complete their
	PPCWrite call.
----------------------------------------------------------------------------------------*/
STATIC void WriteHdrCompletion(void)
{
	DSPParamBlock *dsp;						// <44>
	NetIPCParamsPtr sessPtr;
	PPCWritePBPtr writePB;

	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);

	if (dsp->ioResult)
	{
		RemoveNetworkConnection(sessPtr,false);
		return;
	}
	
	writePB = sessPtr->writePB;
	if(writePB->bufferLength)
		WriteBlockData(sessPtr);
	else
		CompleteWriteRequest(sessPtr,noErr);
}

/*----------------------------------------------------------------------------------------
	WriteBlockData is responsible for writing the data portion of a PPCBlock.  It breaks a block
	into MaxADSPWrite size chuncks so that ADSP can easily swallow it.
----------------------------------------------------------------------------------------*/
STATIC void WriteBlockData(NetIPCParamsPtr sessPtr)
{
	unsigned short         len; 
	unsigned char          eom;
	PBWriteResPtr          res;			// <44>
	PPCWritePBPtr		writePB;
	
	writePB = sessPtr->writePB;
	res = (PBWriteResPtr)(writePB->Reserved);
	if (res->length > MaxADSPWrite)
	{
		len = MaxADSPWrite;
		eom =false;
	}
	else
	{
		len =  res->length;				// <44>
		eom = !writePB->more;
	}
	sessPtr->writeHdr = !writePB->more;

    WriteToConnection(len,
					  res->curBufPos,
					  eom, 				// eom 
					  eom, 				// flush when we finish a block.
					  true, 			// Asynchronously
					  (ProcPtr)WriteBlockDataCompletion, // Completion Routine
					  &sessPtr->writeBlk.dsp);
}

/*----------------------------------------------------------------------------------------
	WriteBlockDataCompletion is chained from WriteBlockData.  If more data for a given PPCWrite
	is to be written, it calls WriteBlockData, otherwise the Write PB has completed, and it is
	time to complete that, and process any pending writes.
----------------------------------------------------------------------------------------*/
STATIC void WriteBlockDataCompletion(void)
{
	DSPParamBlock *dsp;					// <44>
	NetIPCParamsPtr sessPtr;
	PPCWritePBPtr writePB;
	PBWriteResPtr res;
	
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,writeBlk.dsp);
	writePB = sessPtr->writePB;
	res = (PBWriteResPtr)(writePB->Reserved);

	// Update reserved fields to indicate the amount to be written.
	res->length -= dsp->u.ioParams.actCount;
	res->curBufPos += dsp->u.ioParams.actCount;
	writePB->actualLength += dsp->u.ioParams.actCount;
	
	if (dsp->ioResult)
	{
		RemoveNetworkConnection(sessPtr,false);
		return;
	}

	if(res->length)						// <44> if there is more to write
	{
		WriteBlockData(sessPtr);
	}
	else
	{
		CompleteWriteRequest(sessPtr,noErr);
	}
}

/*----------------------------------------------------------------------------------------
	CompleteWriteRequest is responsible for completing a current write request, and start the next
	if any are pending.
----------------------------------------------------------------------------------------*/
STATIC void CompleteWriteRequest(NetIPCParamsPtr sessPtr,OSErr result)
{
	short sr;
	
	CompleteWithResult(sessPtr->writePB,result);
	
	sr = spl(kNoInterrupts);
	if(sessPtr->writePB = GetWritePB(sessPtr))
	{
		spl(sr);
		BeginWriteBlock(sessPtr);
	}
	else
		spl(sr);
}


/*----------------------------------------------------------------------------------------
	BeginReadBlock
----------------------------------------------------------------------------------------*/

STATIC void BeginReadBlock(NetIPCParamsPtr sessPtr)
{
	PPCReadPBPtr readPB;
	PBReadResPtr	res;					// <44>
    IPCHdrBlk		*hdr;					// <44>
	readPB = sessPtr->readPB;
	// Setup reserved fields in PPCRead PB to initial values.
	res = (PBReadResPtr)(readPB->Reserved);
	res->length = readPB->bufferLength;		// Count down from this length.
	res->curBufPos = readPB->bufferPtr;
	if(!sessPtr->readMore)					// if readHeader.
	{
		ReadFromConnection(sizeof(IPCHdrBlk),
						   (Ptr)&sessPtr->readBlk.buffer,
						   true, 			// Asynchronously
						   (ProcPtr)ReadHdrCompletion, // completion Routine
						   &sessPtr->readBlk.dsp);
	}
	else
	{
		hdr = &sessPtr->readBlk.buffer.buffer.headerBlk;
		readPB->blockCreator = hdr->blockCreator;
		readPB->blockType = hdr->blockType;
		readPB->userData = hdr->userData;
		ReadBlockData(sessPtr);
	}
}

/*----------------------------------------------------------------------------------------
	ReadHdrCompletion is chained from ReadData.
----------------------------------------------------------------------------------------*/
STATIC void ReadHdrCompletion(void)
{
	DSPParamBlock		*dsp;				// <44>
	PPCReadPBPtr		readPB;
	NetIPCParamsPtr		sessPtr;
	PBReadResPtr		res;				// <44>
    IPCHdrBlk			*hdr;
	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk);
	readPB = sessPtr->readPB;
	res = (PBReadResPtr)(readPB->Reserved);
	readPB->more = sessPtr->readMore = !dsp->u.ioParams.eom;	// do we need to read a different header?
	
	if (dsp->ioResult || (dsp->u.ioParams.actCount == 0 && dsp->u.ioParams.eom == 0) &&
						 (sessPtr->ce.state == sClosing || sessPtr->ce.state == sClosed))
	{
		RemoveNetworkConnection(sessPtr,false);
		return;
	}

	hdr = &sessPtr->readBlk.buffer.buffer.headerBlk;
	readPB->blockCreator = hdr->blockCreator;
	readPB->blockType = hdr->blockType;
	readPB->userData = hdr->userData;
	
	if(sessPtr->readMore)
		ReadBlockData(sessPtr);
	else
		CompleteReadRequest(sessPtr,noErr);
}

/*----------------------------------------------------------------------------------------
	ReadBlockData
----------------------------------------------------------------------------------------*/

STATIC void ReadBlockData(NetIPCParamsPtr sessPtr)
{
	PBReadResPtr	res;
	unsigned short	len;
	
	res = (PBReadResPtr)(sessPtr->readPB->Reserved);

	len = res->length < MaxADSPWrite?res->length:MaxADSPWrite;
	if (len) // User Does not want to read zero bytes
	{
		ReadFromConnection(len,
						   res->curBufPos,
						   true, // Asynchronously
						   (ProcPtr)ReadDataCompletion, // completion Routine
						   &sessPtr->readBlk.dsp);
	}
	else
		CompleteReadRequest(sessPtr,noErr);
}

/*----------------------------------------------------------------------------------------
	ReadDataCompletion
----------------------------------------------------------------------------------------*/
STATIC void ReadDataCompletion(void)
{
	DSPParamBlock	*dsp;					// <44>
	PPCReadPBPtr	readPB;
	NetIPCParamsPtr	sessPtr;
	PBReadResPtr	res;


	dsp = GetA0();
	sessPtr = TOP_OF_RECORD_PTR(dsp,NetIPCParams,readBlk);
	readPB = sessPtr->readPB;
	res = (PBReadResPtr)(readPB->Reserved);
	
	res->length -= dsp->u.ioParams.actCount;					// we have this much less to read.
	res->curBufPos += dsp->u.ioParams.actCount;					// next spot to append to clients buffer.
	readPB->actualLength += dsp->u.ioParams.actCount;			// update how much we put into clients buffer.
	readPB->more = sessPtr->readMore = !dsp->u.ioParams.eom;	// do we need to read a different header?
	

	if (dsp->ioResult || (dsp->u.ioParams.actCount == 0 && dsp->u.ioParams.eom == 0) &&
						 (sessPtr->ce.state == sClosing || sessPtr->ce.state == sClosed))
	{
		RemoveNetworkConnection(sessPtr,false);
		return;
	}

	if(res->length && sessPtr->readMore)	// <44>
		ReadBlockData(sessPtr);
	else	// EOM or clients buffer full.
		CompleteReadRequest(sessPtr,noErr);
} // ReadDataCompletion


/*----------------------------------------------------------------------------------------
	CompleteReadRequest is responsible for completing a current read request, and start the next
	if any are pending for this session.
----------------------------------------------------------------------------------------*/
STATIC void CompleteReadRequest(NetIPCParamsPtr sessPtr,OSErr result)
{
	short sr;
	
	CompleteWithResult(sessPtr->readPB,result);
	
	sr = spl(kNoInterrupts);
	if(sessPtr->readPB = GetReadPB(sessPtr))
	{
		spl(sr);
		BeginReadBlock(sessPtr);
	}
	else
		spl(sr);
}

/*----------------------------------------------------------------------------------------
	CopyReadBlk, copies the contents of a DSP parameter block (used for read operations). To a
	another parameter block (used for writing).
----------------------------------------------------------------------------------------*/
STATIC void CopyReadBlk(NetIPCParamsPtr sessPtr)
{
	BlockMove((char *)&(sessPtr->readBlk.dsp),
	(char *)&(sessPtr->writeBlk.dsp),
			  sizeof(DSPParamBlock));

} // CopyReadBlk
