ADSP.c

/*****************************************************************
 
    Program:    < ADSP Chat >
    File:       < adsp.c >
    
    Written by  Pete Helm, Scott Kuechle
    of <Apple Macintosh Developer Technical Support>
    
    modified by Scott Kuechle
    10/92 SRK Converted from Pascal to C
    8/94 SRK Modified to use a queue of parameter
             blocks.
 
    Copyright © 1992, 1994 Apple Computer, Inc.
    All rights reserved.
    
*****************************************************************/
 
/*****************************************************************/
/*  I N C L U D E S
/*****************************************************************/
 
#include    "ADSP Chat.h"
 
/********************************************************************
/*  G L O B A L   V A R I A B L E   D E C L A R A T I O N S
/********************************************************************/
 
char myLocalSocket;
Ptr receiveQBuffer;
Ptr sendQBuffer;
Ptr attnBuffer;
Ptr outAttnBuff;
Ptr receiveBuffer;
Ptr outgoingDataBuffer;
 
 
TRCCB   gMyCCB;             /* our ccb */
short   gADSPDrvrRefNum;    /* adsp driver reference number */
short   gCCBRefNum;         /* ccb reference number returned by adsp on a dspInit */
 
Boolean gJustConnected;     /* dspOpen ocRequest was successfully accepted */
 
QHdr    gAvailQueue;
QHdr    gDoneQueue;
QHdr    gReadQueue;
 
/*****************************************************************/
/*
/* E X T E R N A L S
/*
/*****************************************************************/
 
extern void removeMyName();
 
extern Boolean  registerMyName();
extern void ShowError(short index);
extern void DisplayCurrentStatus(Ptr displayStr);
extern void HiliteConnectButton (short mode);
extern void Exit(short message);
 
extern Str255 gZoneString, gObjStr, gTypeStr;
extern Str32 myName;
extern DialogPtr myDialog;
extern QHdr freeQHdr,saveErrorQHdr;
extern AddrBlock ourNetworkAddress;
extern void CopyPstr(Ptr pSource, Ptr pDest);
extern void PStrCat(Ptr sourceStr, Ptr destinationStr);
extern void zeroOutStrings();
extern void PreCompletion();
 
 
 
 
#pragma segment adsp
// *****************************************************************
// *    doADSPinit
// *
// *    calls dspInit, saves the socket and ccb refnum returned
// *****************************************************************
Boolean doADSPinit(unsigned char *localSocket,
                    short *ccbRefNum,
                    TPCCB ccbPtr)
{
TRinitParams *initParmsPtr;
DSPParamBlock dspPB;
 
        /* allocate connection end */
    dspPB.ioCRefNum     = gADSPDrvrRefNum;
    dspPB.csCode        = dspInit;
    
    initParmsPtr = &dspPB.u.initParams;
    
    initParmsPtr->ccbPtr        = ccbPtr;
        /* poll in main event loop instead of using user routine */
    initParmsPtr->userRoutine   = NULL;
    initParmsPtr->sendQSize     = Qsize;
    initParmsPtr->sendQueue     = sendQBuffer;
    initParmsPtr->recvQSize     = Qsize;
    initParmsPtr->recvQueue     = receiveQBuffer;
    initParmsPtr->attnPtr       = attnBuffer;
    initParmsPtr->localSocket   = 0;                /* dynamically allocate a socket for us */
 
    if (PBControl((ParmBlkPtr)&dspPB, false) != noErr)
        Exit(DrvrErr);
    else if ( dspPB.ioResult == noErr)  /* any errors? */
    {
            /* save socket returned to us by ADSP */
        *localSocket = initParmsPtr->localSocket;
 
            /* save ccb reference number returned to us by ADSP */
        *ccbRefNum = dspPB.ccbRefNum;
        return true;
    }
    else    /* got an error on the dspInit */
    {
        *ccbRefNum = 0;
        ShowError(dspInitErr);
        return false;
    }
}
 
// *****************************************************************
// *    initializeADSP
// *
// *    opens the adsp driver, allocates memory for our adsp buffers
// *    then issues a passive open call
// *****************************************************************
void initializeADSP()
{
        /* set up the ADSP connection end. */
    gJustConnected = false;
    gCCBRefNum = 0;
 
    changeConnectButtonState();
    
        /* open the ADSP driver here - we need the reference number
            for all ADSP calls */
    if (OpenDriver("\p.DSP", &gADSPDrvrRefNum) != noErr)
        Exit(atalkErr);
        
    if (InitQueues() == noErr)
    {
            /* allocate memory for our buffers and then
                issue a passive open */
        if (setUpADSPbuffers() == true)
        {
                /* issue a passive open */
            if (WaitForConnectionRequest() != true)
            {
                    /* oops! got an error so deallocate our buffers */
                removeADSPBuffers();
                Exit(memErr);
            }
        }
        else
            Exit(noMemErr);
    }
    else
        Exit(memErr);
 
}
 
 
// *****************************************************************
// *    SetOurCompletionRoutine
// *
// *    Sets the "real" completion routine for our async. calls
// *****************************************************************
 
void SetOurCompletionRoutine(ProcPtr procPtr,
                            DSPPBPtr dspPBPtr)
{
Ptr p;
myDSPParamBlockPtr myDSPPBPtr;
 
    p = (Ptr)dspPBPtr;
        /* the "ourCompletion" field is offset
            8 bytes above the DSPParmBlock in
            our definition */
    myDSPPBPtr = (myDSPParamBlockPtr)(p - 8);
    myDSPPBPtr->ourCompletion = procPtr;
    
}
 
// *****************************************************************
// *    GetQElement
// *
// *    retrieve a queue element from the specified queue
// *****************************************************************
 
DSPPBPtr GetQElement(QHdrPtr qHdrPtr)
{
OSErr err;
QElemPtr qElemPtr;
 
    CheckDoneQueue();
 
    qElemPtr = qHdrPtr->qHead;
    if (qElemPtr != NULL)
    {
        err = Dequeue((QElemPtr)qElemPtr,qHdrPtr);
        if (err != noErr)
        {
            return NULL;
        }
        else
        {
            return (DSPPBPtr)(qElemPtr);
        }
    }
    else
        return NULL;
}
 
// *****************************************************************
// *    InitQueues
// *
// *    initialize our "Available" , "Done" and "Read" queues
// *****************************************************************
 
OSErr InitQueues()
{
short i;
OSErr err;
long myA5;
myDSPParamBlockPtr myQElem;
 
 
    gAvailQueue.qHead = NULL;
    gAvailQueue.qTail = NULL;
 
    gDoneQueue.qHead = NULL;
    gDoneQueue.qTail = NULL;
 
    gReadQueue.qHead = NULL;
    gReadQueue.qTail = NULL;
    
    err = noErr;
    myA5 = *(long *)CurrentA5;
    
    for (i=0; (i < maxQElements) || (err != noErr); ++i)
    {
        myQElem = (myDSPParamBlockPtr)NewPtr(sizeof(myDSPParamBlock));
        if (myQElem != NULL)
        {
            myQElem->myA5 = myA5;
            myQElem->u.ioCompletion = (ProcPtr)&PreCompletion;
            Enqueue((QElemPtr)&myQElem->u,&gAvailQueue);
        }
        else
        {
            err = MemError();
        }
    }
 
    return err;
}
 
// *****************************************************************
// *    setUpADSPbuffers
// *
// *    allocates memory for our adsp buffers
// *****************************************************************
Boolean setUpADSPbuffers()
{
    
    receiveQBuffer = NewPtr(Qsize);
    if ( receiveQBuffer == nil) return false;
 
    sendQBuffer = NewPtr(Qsize);    
    if ( sendQBuffer == nil) return false;
    
    attnBuffer = NewPtr(attnBufSize);
    if ( attnBuffer == nil) return false;
    
    outAttnBuff = NewPtr(attnBufSize);
    if ( outAttnBuff == nil) return false;
    
    receiveBuffer = NewPtr(attnBufSize);
    if ( receiveBuffer == nil) return false;
    
    outgoingDataBuffer = NewPtr(Qsize);
    if ( outgoingDataBuffer == nil) return false;
    
    return true;
    
}
 
 
 
// *****************************************************************
// *    changeConnectButtonState
// *
// *    sets the correct state for our connect button
// *****************************************************************
void changeConnectButtonState()
{
    /* this procedure reflects the state of our connection in the control */
 
    Rect r;
    short kind;
    Handle h;
 
        GetDItem(myDialog, kConnectButtonID, &kind, &h, &r);
        
        if ( (gCCBRefNum == 0) ||
            ((gCCBRefNum != 0) && (gMyCCB.state == sClosed)) )
            SetCTitle((ControlHandle)h, "\pConnect");
        else
        {
            SetCTitle((ControlHandle)h, "\pDisconnect");
            HiliteConnectButton(0);
        }
}
 
// *****************************************************************
// *    DisplayTime
// *
// *    this procedure responds to an attention message from the remote
// *    Mac which contains the time of that Mac.  Silly yes, but it does
// *    show how to use the attnMsg stuff.
// *****************************************************************
void DisplayTime()
{
 
    Rect r;
    short kind;
    Handle h;
    long dateTime;
    Str255 str, str2;
 
        GetDItem(myDialog, kRemoteMacsTimeID, &kind, &h, &r);
        GetIText(h, str2);
        BlockMove(attnBuffer, &dateTime, 4);
        IUTimeString(dateTime, true, str);
 
        /* this just checks to see that we're not already displaying this time already */
        /* flashing text sucks... so sayeth me */
        if (IUCompString(str,str2) != 0)
            SetIText(h, str);
}
 
// *****************************************************************
// *    DisplayTheirName
// *
// *    this procedure responds to an attention message from the remote
// *    Mac which contains the remote Mac's Chooser name.
// *****************************************************************
void DisplayTheirName()
{
    
    Rect r;
    short kind;
    Handle h;
    Str255 str;
    
        GetDItem(myDialog, kRemoteMacsNameID, &kind, &h, &r);
        BlockMove(attnBuffer, &str, 255);
        SetIText(h, str);
}
 
// *****************************************************************
// *    DisplayIncomingText
// *
// *    this procedure takes the text written to our receive buffer from
// *    the remote Mac and displays the text in a dialog item
// *****************************************************************
void DisplayIncomingText(DSPPBPtr dspPBPtr)
{
 
    Rect r;
    short kind;
    Handle h;
    Str255 str;
 
        GetDItem(myDialog, kIncomingTextID, &kind, &h, &r);
        BlockMove(dspPBPtr->u.ioParams.dataPtr, (Ptr)(&str[1]), 255);
 
        str[0] = (char)dspPBPtr->u.ioParams.actCount;
        SetIText(h, &str);
 
}
 
 
// *****************************************************************
// *    removeADSPBuffers
// *
// *    this procedure deallocates the memory for our adsp buffers.
// *****************************************************************
void removeADSPBuffers()
{
    if (receiveQBuffer != NULL)
        DisposPtr(receiveQBuffer);
    if (sendQBuffer != NULL)
        DisposPtr(sendQBuffer);
    if (attnBuffer != NULL)
        DisposPtr(attnBuffer);
    if (outAttnBuff != NULL)
        DisposPtr(outAttnBuff);
    if (receiveBuffer != NULL)
        DisposPtr(receiveBuffer);
    if (outgoingDataBuffer != NULL)
        DisposPtr(outgoingDataBuffer);
 
}
 
// *****************************************************************
// *    CloseTheConnection
// *
// *    calls dspClose to close the connection end.
// *****************************************************************
void CloseTheConnection(short ccbRefNum)
{
    DSPParamBlock pb;
 
        pb.ioCRefNum            = gADSPDrvrRefNum;
        pb.csCode               = dspClose;
        pb.ccbRefNum            = ccbRefNum;
        pb.u.closeParams.abort  = 1;        /* non zero value aborts any outstanding calls */
 
        if (PBControl((ParmBlkPtr)&pb, false) != noErr)
            Exit(DrvrErr);
}
 
 
// *****************************************************************
// *    removeConnectionEnd
// *
// *    calls dspRemove to eliminate the connection end.
// *****************************************************************
void removeConnectionEnd(short ccbRefNum)
{
    DSPParamBlock pb;
 
        pb.ioCRefNum            = gADSPDrvrRefNum;
        pb.csCode               = dspRemove;
        pb.ccbRefNum            = ccbRefNum;
        pb.u.closeParams.abort  = 1;        /* non zero value aborts any outstanding calls */
 
        if (PBControl((ParmBlkPtr)&pb, false) != noErr)
            Exit(DrvrErr);
 
        if (pb.ioResult != noErr)
            ShowError(dspRemoveErr);
}
 
// *****************************************************************
// *    WaitForConnectionRequest
// *
// *    calls dspInit to establish a connection end. Then registers
// *    our name on the net and does a passive open.
// *****************************************************************
Boolean WaitForConnectionRequest()
{
DSPPBPtr dspPBPtr;
 
        /* first issue a dspInit */
    if (doADSPinit(&myLocalSocket,
                    &gCCBRefNum,
                    &gMyCCB) == true)
    {
            /* save our socket for comparison purposes later */
        ourNetworkAddress.aSocket = myLocalSocket;
 
            /* now register our nbp name with type "moof" */
        if (registerMyName() == true)
        {
                /* get QElement for the dspInit call */
            dspPBPtr = GetQElement(&gAvailQueue);
            if (dspPBPtr != NULL)
            {
                    /* do a passive open, waiting for open requests */
                DoPassiveOpen(dspPBPtr,
                                gCCBRefNum);
                return true;
            }
        }
        else    /* we got an error! */
        {       /* remove connection end with dspRemove */
            removeConnectionEnd(gCCBRefNum);
                /* de-allocate memory we got for our buffers */
            removeADSPBuffers();
        }
    }
 
    return false;
}
 
// *****************************************************************
// *    CloseConnection
// *
// *    calls dspRemove to eliminate the connection end. Then removes
// *    our nbp name from the net and re-issues a passive open.
// *****************************************************************
void CloseConnection()
{
        /* do a dspRemove to close the connection end */
    removeConnectionEnd(gCCBRefNum);
    
        /* remove our nbp name "moof" */
    removeMyName();
 
    changeConnectButtonState();
    zeroOutStrings();
 
        /* at this point, we have closed the connection so let's issue
            an asynchronous passive open. This call will complete whenever
            we receive an open connection request from another machine. */
    WaitForConnectionRequest();
 
}
 
 
// *****************************************************************
// *    sendAttnMsgCompRoutine
// *
// *    when our send attention message call completes this procedure
// *    will get called.
// *****************************************************************
pascal void sendAttnMsgCompRoutine(DSPPBPtr dspPBPtr)
{
    if (dspPBPtr->ioResult == noErr)
            /* place parameter block back into the "Available" queue */
        Enqueue((QElemPtr)dspPBPtr,&gAvailQueue);
    else    /* some kind of error was returned */
            /* place parameter block back into the "Done" queue */
        Enqueue((QElemPtr)dspPBPtr,&gDoneQueue);
}
 
// *****************************************************************
// *    sendAttnMsg
// *
// *    this is a generic attention message sending routine.  all 
// *    attention messages are sent async using a shared buffer.   
// *    the message code and the size of the message are passed to
// *    this proc.
// *****************************************************************
void sendAttnMsg(DSPPBPtr dspPBPtr,
                 short buffSize,
                 Ptr attnData,
                 short msg,
                 short ccbRefNum)
{
TRattnParams *attnParmsPtr;
 
/* this is a generic attention message sending routine.  all attention messages are sent*/
/* async using a shared buffer.  the message code and the size of the message are passed to */
/* this proc.*/
 
    SetOurCompletionRoutine((ProcPtr)&sendAttnMsgCompRoutine,
                            dspPBPtr);
                            
    dspPBPtr->ioCRefNum     = gADSPDrvrRefNum;
    dspPBPtr->csCode        = dspAttention;
    dspPBPtr->ccbRefNum     = ccbRefNum;
    
    attnParmsPtr = &dspPBPtr->u.attnParams;
    attnParmsPtr->attnCode      = (unsigned short)msg;
    attnParmsPtr->attnSize      = buffSize;
    attnParmsPtr->attnData      = attnData;
    attnParmsPtr->attnInterval  = 10;
 
    if (PBControl((ParmBlkPtr)dspPBPtr, true) != noErr)
        Exit(DrvrErr);
}
 
 
// *****************************************************************
// *    OpenPassiveCompletionRtn
// *
// *    when our passive open call completes this procedure
// *    will get called.
// *****************************************************************
pascal void OpenPassiveCompletionRtn(DSPPBPtr dspPBPtr)
{
        
    if (dspPBPtr->ioResult == noErr)
    {
        gJustConnected = true;
            /* issue a read so we can catch
                any messages sent to us */
        readIncoming(dspPBPtr,
                    gCCBRefNum);    
    }
    else
            /* place parameter block back into the "Done" queue */
        Enqueue((QElemPtr)dspPBPtr,&gDoneQueue);
}
 
 
// *****************************************************************
// *    DoPassiveOpen
// *
// *    issues an asynchronous passive open call. Any machine wishing
// *    to connect to us must only send us an open request (dspOpen, 
// *    ocRequest mode). Once we get a request, our completion routine
// *    is called and the connection is established.
// *****************************************************************
void DoPassiveOpen(DSPPBPtr dspPBPtr,
                    short ccbRefNum)
{
    AddrBlock filterAddress;
    TRopenParams *openPBPtr;
 
 
            /* send an open request */
        SetOurCompletionRoutine((ProcPtr)&OpenPassiveCompletionRtn,
                                dspPBPtr);
        dspPBPtr->ioCRefNum     = gADSPDrvrRefNum;
        dspPBPtr->csCode        = dspOpen;
        dspPBPtr->ccbRefNum     = ccbRefNum; /* refNum of connection end    */
 
            /* accept open requests from anyone */
        filterAddress.aNet      = 0;
        filterAddress.aNode     = 0;
        filterAddress.aSocket   = 0;
        
        openPBPtr = &dspPBPtr->u.openParams;
        openPBPtr->filterAddress    = filterAddress;
        openPBPtr->ocMode           = ocPassive;
        openPBPtr->ocInterval       = 0;    /* use default value of 6 (1 second) */
        openPBPtr->ocMaximum        = 255;  /* retransmit indefinitely */
 
        if (PBControl((ParmBlkPtr)dspPBPtr, true) != noErr)
            Exit(dspOpenErr);
        
}
 
// *****************************************************************
// *    adspOpenRqstCompletionRtn
// *
// *    when our open request call completes this procedure
// *    will get called. If we dont get back any errors then the
// *    connection is established with the target.
// *****************************************************************
pascal void adspOpenRqstCompletionRtn(DSPPBPtr dspPBPtr)
{
    if (dspPBPtr->ioResult == noErr)
    {
        gJustConnected = true;
            /* issue a read so we can catch
                any messages sent to us */
        readIncoming(dspPBPtr,
                    gCCBRefNum);    
    }
    else
            /* place parameter block back into the "Done" queue */
        Enqueue((QElemPtr)dspPBPtr,&gDoneQueue);    
}
 
// *****************************************************************
// *    sendAnOpenConnReq
// *
// *    issues an open request call to the target.
// *****************************************************************
void sendAnOpenConnReq (DSPPBPtr dspPBPtr,
                        AddrBlock theirAddress,
                        short ccbRefNum)
{
    AddrBlock filterAddress;
    TRopenParams *openPBPtr;
 
            /* make a connection only with the selected target */
        filterAddress = theirAddress;
 
            /* send an open request */
        SetOurCompletionRoutine((ProcPtr)&adspOpenRqstCompletionRtn,
                                dspPBPtr);
        dspPBPtr->ioCRefNum     = gADSPDrvrRefNum;
        dspPBPtr->csCode        = dspOpen;
        dspPBPtr->ccbRefNum     = ccbRefNum;
        
            /* refNum of connection end    */
        openPBPtr = &dspPBPtr->u.openParams;
        openPBPtr->remoteAddress    = theirAddress;
        openPBPtr->filterAddress    = filterAddress;
        openPBPtr->ocMode           = ocRequest;
        openPBPtr->ocInterval       = 6;
        openPBPtr->ocMaximum        = 5;
 
        if (PBControl((ParmBlkPtr)dspPBPtr, true) != noErr)
            Exit(dspOpenErr);
        
}
 
// *****************************************************************
// *    connectToPeer
// *
// *    first checks to see if the target is still there. Next, it
// *    issues an open request to the target (but it first closes
// *    the connection end which forces any prior passive open calls to
// *    complete).
// *****************************************************************
void connectToPeer()
{
    NBPparms        *nbpParms;
    MPPParamBlock   pbLKP;
    char            returnBuffer[kLookupBufSize];
    EntityName      abEntity;
    AddrBlock       address;
    NamesTableEntry namesTableEntry;
    DSPPBPtr        dspPBPtr;
    
        if (gMyCCB.state == sClosed ||
            gMyCCB.state == sPassive)
        {
            NBPSetEntity((Ptr)&namesTableEntry, gObjStr, gTypeStr, gZoneString);
            
            nbpParms = &pbLKP.NBP;
            
            nbpParms->interval                  = 3;
            nbpParms->count                     = 3;
            nbpParms->NBPPtrs.entityPtr         = (Ptr)&namesTableEntry;
            nbpParms->parm.Lookup.retBuffSize   = kLookupBufSize;
            nbpParms->parm.Lookup.retBuffPtr    = &returnBuffer;
            nbpParms->parm.Lookup.maxToGet      = 1;
 
                /* see if the target still exists */
            if (PLookupName(&pbLKP, false) == noErr)
            {
                if ( nbpParms->parm.Lookup.numGotten > 0)
                {
                        /* look for the target in our return buffer */
                    if (NBPExtract(returnBuffer, 
                                    nbpParms->parm.Lookup.numGotten,
                                    1,
                                    &abEntity,
                                    &address) == noErr)
                    {
                            /* if a passive open has been issued, we must first close the connection end (which
                                will cancel the open) before we can issue an open request to the target */
                        if (gMyCCB.state == sPassive)
                        {
                            CloseTheConnection(gCCBRefNum);
                        }
                        
                        dspPBPtr = GetQElement(&gAvailQueue);
                        if (dspPBPtr != NULL)
                        {
                                /*  issue an open request to the target */
                            sendAnOpenConnReq(dspPBPtr,
                                              address,
                                              gCCBRefNum);
                        }
                    }
                            
                }
                else    /* target not found */
                    ShowError(noTargetErr);
            }
            
        }
        else        /* disconnect */
        {
            CloseConnection();
        }
}
 
// *****************************************************************
// *    readIncomingComp
// *
// *    this routine is called when our asynchronous dspRead call
// *    completes.
// *****************************************************************
pascal void readIncomingComp(DSPPBPtr dspPBPtr)
{
    if (dspPBPtr->ioResult == noErr)
    {
            /* place parameter block back into the "Read" queue */
        Enqueue((QElemPtr)dspPBPtr,&gReadQueue);
    }
    else    /* error occurred */
    {
            /* place parameter block back into the "Done" queue */
        Enqueue((QElemPtr)dspPBPtr,&gDoneQueue);
    }
    
}
 
// *****************************************************************
// *    readIncoming
// *
// *    issues an asynchronous dspRead to read any incoming messages
// *****************************************************************
void readIncoming(DSPPBPtr dspPBPtr,
                    short ccbRefNum)
{
TRioParams *ioPBPtr;
 
    SetOurCompletionRoutine((ProcPtr)&readIncomingComp,
                            dspPBPtr);
    dspPBPtr->ioCRefNum     = gADSPDrvrRefNum;
    dspPBPtr->csCode        = dspRead;
    dspPBPtr->ccbRefNum     = ccbRefNum;
    
    ioPBPtr = &dspPBPtr->u.ioParams;
    ioPBPtr->reqCount       = Qsize;
    ioPBPtr->dataPtr        = receiveBuffer;
 
    if (PBControl((ParmBlkPtr)dspPBPtr, true) != noErr)
        Exit(DrvrErr);
        
}
 
// *****************************************************************
// *    writeComp
// *
// *    when our asynchronous dspWrite completes this routine will get
// *    called.
// *****************************************************************
pascal void writeComp(DSPPBPtr dspPBPtr)
{
        /* place parameter block back into the "Done" queue */
    Enqueue((QElemPtr)dspPBPtr,&gDoneQueue);    
}
 
// *****************************************************************
// *    writeOutgoing
// *
// *    issues an asynchronous dspWrite which sends any text that has
// *    been entered to the target machine.
// *****************************************************************
void writeOutgoing(DSPPBPtr dspPBPtr,
                    short ccbRefNum,
                    Ptr dataPtr,
                    short reqCount)
{
    TRioParams *ioPBPtr;
 
        SetOurCompletionRoutine((ProcPtr)&writeComp,
                                dspPBPtr);
        dspPBPtr->ioCRefNum     = gADSPDrvrRefNum;
        dspPBPtr->csCode        = dspWrite;
        dspPBPtr->ccbRefNum     = ccbRefNum;
        
        ioPBPtr = &dspPBPtr->u.ioParams;
        ioPBPtr->reqCount       = reqCount;
        ioPBPtr->dataPtr        = dataPtr;
        ioPBPtr->eom            = 1;
        ioPBPtr->flush          = 1;
 
        if (PBControl((ParmBlkPtr)dspPBPtr, true) != noErr)
            Exit(DrvrErr);
 
}
 
// *****************************************************************
// *    sendTime
// *
// *    this procedure sends an attention message packet with our
// *    time to the remote Mac.
// *****************************************************************
void sendTime()
{
    unsigned long secs;
    DSPPBPtr dspPBPtr;
 
        GetDateTime(&secs);
        BlockMove(&secs, outAttnBuff, 4);
        dspPBPtr = GetQElement(&gAvailQueue);
        if (dspPBPtr != NULL)
        {
            sendAttnMsg(dspPBPtr,
                    4,
                    outAttnBuff,
                    kDisplayTime,
                    gCCBRefNum);
        }
}
 
// *****************************************************************
// *    sendMyName
// *
// *    this procedure sends an attention message packet with our 
// *    Chooser name to the remote Mac.
// *****************************************************************
void sendMyName()
{
    DSPPBPtr dspPBPtr;
    Str255 str;
 
        BlockMove(myName,str,myName[0]+1);
        BlockMove(&str, outAttnBuff, 256);
        
        dspPBPtr = GetQElement(&gAvailQueue);
        if (dspPBPtr != NULL)
        {
            sendAttnMsg(dspPBPtr,
                        256,
                        outAttnBuff,
                        kDisplayTheirName,
                        gCCBRefNum);
        }
}
 
 
// *****************************************************************
// *    signalConnect
// *
// *    after our passive open call has completed with an open request, 
// *    or after an open request that we have issued to the target has
// *    completed, we set a flag which causes this routine to get called.
// *    This routine then sends the Chooser name of our machine to the
// *    target.
// *****************************************************************
void signalConnect()
{
 
    SysBeep(1);
    changeConnectButtonState();
    
        /* send our machine's name to the other end */
    sendMyName();
 
        /*Change selection to outgoing text TERecord */
    SelIText(myDialog, kOutgoingTextID, 0, 32767);
}
 
// *****************************************************************
// *    checkAttnMsgs
// *
// *    If an attention message has been sent, we process it here.
// *****************************************************************
void checkAttnMsgs()
{
    switch(gMyCCB.attnCode)
    {
        case kDisplayTime:
            DisplayTime();
            break;
        case kDisplayTheirName:
            DisplayTheirName();
            break;
    }
}
 
// *****************************************************************
// *    DoConnectionEvents
// *
// *    This routine checks for unsolicited connection events and
// *    processes them accordingly.
// *****************************************************************
void DoConnectionEvents()
{
    short state;
    Byte flags;
 
        state = gMyCCB.state;
        flags = gMyCCB.userFlags;
 
        if (state == sOpen)
            DisplayCurrentStatus("\pConnection established.");
        else if (state == sClosed)
            DisplayCurrentStatus("\pNot currently connected.");
        else if (state == sPassive)
            DisplayCurrentStatus("\pWaiting to accept connection.");
        
        if (flags & eAttention)
        {
            checkAttnMsgs();
                /* we must clear the bit after we have used it or we wont
                    get any more messages */
            BitClr(&gMyCCB.userFlags,2);
        }
        else if (flags & eClosed)
        {
            DisplayCurrentStatus("\pConnection was closed.");
                /* we must clear the bit after we have used it or we wont
                    get any more messages */
            BitClr(&gMyCCB.userFlags,0);
            CloseConnection();
        }
                /* after two minutes or so if the connection has broken we'll be notified by */
                /* the .DSP driver with an eTearDown message */
        else if (flags & eTearDown)
        {
            SysBeep(2);
            SysBeep(2);
            DisplayCurrentStatus("\pConnection torn down.");
                /* we must clear the bit after we have used it or we wont
                    get any more messages */
            BitClr(&gMyCCB.userFlags,1);
            CloseConnection();
        }
 
}
 
// *****************************************************************
// *    CheckDoneQueue
// *
// *    This routine looks through the "Done" queue for calls that
// *    have completed and reports any errors.
// *****************************************************************
void CheckDoneQueue()
{
QElemPtr qElemPtr;
DSPPBPtr dspPBPtr;
OSErr err;
 
        /* have any Writes completed? */
    if (gDoneQueue.qHead != nil)
    {
        qElemPtr = gDoneQueue.qHead;
        err = Dequeue((QElemPtr)qElemPtr,&gDoneQueue);
        if (err == noErr)
        {
            dspPBPtr = (DSPPBPtr)qElemPtr;
                /* were there any errors on this particular call? */
            if (dspPBPtr->ioResult != noErr)
            {
                ShowADSPError(dspPBPtr);
            }
 
                /* place queue element back into the "available" queue */
            Enqueue((QElemPtr)qElemPtr,&gAvailQueue);
        }
    }
 
}
 
// *****************************************************************
// *    CheckCompletedReads
// *
// *    This routine looks through the "Read" queue for calls that
// *    have completed, displays the text that was read and re-issues
// *    another asynchronous read call.
// *****************************************************************
void CheckCompletedReads()
{
QElemPtr qElemPtr;
DSPPBPtr dspPBPtr;
OSErr err;
 
        /* have any reads completed? */
    if (gReadQueue.qHead != nil)
    {
        qElemPtr = gReadQueue.qHead;
        err = Dequeue((QElemPtr)qElemPtr,&gReadQueue);
        if (err == noErr)
        {
            dspPBPtr = (DSPPBPtr)qElemPtr;
                /* were there any errors on this particular read call? */
            if (dspPBPtr->ioResult == noErr)
            {
                    /* show the text */
                DisplayIncomingText(dspPBPtr);
            }
            else
            {
                ShowADSPError(dspPBPtr);
            }
 
                /* go ahead and issue another asynchronous read */
            readIncoming(dspPBPtr,
                        gCCBRefNum);
        }
    }
 
}
 
// *****************************************************************
// *    ADSPLoop
// *
// *    This is called from our idle procedure in our main event
// *    loop. If a connection is made, we learn about it here. If we
// *    do detect that a connection has been made, we check for
// *    connection events and issue read and write calls here.
// *****************************************************************
void ADSPLoop()
{
 
        /* check to see if our passive open completed with an open
            request from another machine OR if an open request that
            we issued to the target has completed */
    if (gJustConnected == true)
    {
            /* reset our flag */
        gJustConnected = false;
 
            /* set status message to indicate we have a connection */
        signalConnect();
    }
 
        /* check for connection events */
    DoConnectionEvents();
 
    if (gMyCCB.state != sClosed)
    {
            /* have any read calls completed? */
        CheckCompletedReads();
    
            /* check for all other calls that have completed */
        CheckDoneQueue();
    }
 
        /* do we currently have a connection? */
    if (gMyCCB.state == sOpen)
    {
            /* we have a connection so send our clock time
                to the other machine */
        sendTime();
 
    }
 
 
}
 
 
// *****************************************************************
// *    SetUpADSPError
// *
// *    sets our error string for the desired error code
// *****************************************************************
void SetUpADSPError(OSErr err, StringPtr displayStr)
{
    switch (err)
    {
        case errAborted:
            PStrCat("\pControl call was aborted.",(Ptr)displayStr);
          break;
          
        case errState:
            PStrCat("\pBad connection state for this operation.",(Ptr)displayStr);
          break;
 
        case errOpening:
            PStrCat("\pOpen connection request failed.",(Ptr)displayStr);
          break;
          
        case errOpenDenied:
            PStrCat("\pOpen connection request was denied.",(Ptr)displayStr);
          break;
          
        default:
            NumToString(err,(Ptr)displayStr);
        
    }
 
}
 
// *****************************************************************
// *    ShowADSPError
// *
// *    If one of our asynchronous adsp calls returned an error, the
// *    parameter block gets stuffed into an error OSQueue by the completion
// *    routine. This error queue is checked periodically by this routine.
// *    If the queue is not empty, then we got an error so we display it here
// *    and do any processing of the error that we want.
// *****************************************************************
void ShowADSPError(DSPPBPtr dspPBPtr)
{
    short itemHit;
    Str255 displayStr,theError;
    
 
            displayStr[0] = 0;
            
                /*  Use cscode field of the parameter block
                   to see which call this error is for*/
            
            switch (dspPBPtr->csCode)
            {
                case dspRead:
                        /* We'll ignore the "errState" error for now,
                            since you'll get this when you close a 
                            connection (and we don't really need to
                            report it in that case) */
                    if (dspPBPtr->ioResult != errState)
                    {
                        CopyPstr("\pdspRead error. ",&displayStr);
                        SetUpADSPError(dspPBPtr->ioResult, &displayStr);
                    }
                 break;
    
                case dspWrite:
                    CopyPstr("\pdspWrite error. ",&displayStr);
                    SetUpADSPError(dspPBPtr->ioResult, &displayStr);
                 break;
            
                case dspAttention:
                        /* ignore aborts that were caused by dspRemove or dspClose */
                    if ((dspPBPtr->ioResult != errAborted) 
                        && (dspPBPtr->ioResult != errState))
                    {
                        CopyPstr("\pdspAttention error. ",&displayStr);
                        SetUpADSPError(dspPBPtr->ioResult, &displayStr);
                    }
                 break;
 
                case dspOpen:
                        /* ignore errAborted (-1279), request aborted by dspRemove or
                            dspClose - the reason being that if we have a dspOpen (ocPassive
                            mode) issued and then we want to connect to another machine, we
                            must first close the connection end (cancelling the passive open)
                            which would result in this error */
                    if (dspPBPtr->ioResult != errAborted)
                    {
                        CopyPstr("\pdspOpen error. ",&displayStr);
                        SetUpADSPError(dspPBPtr->ioResult, &displayStr);
                    }
                 break;
 
                case dspCLListen:
                    CopyPstr("\pdspCLListen error. ",&displayStr);
                    SetUpADSPError(dspPBPtr->ioResult, &displayStr);
                 break;
    
                default:
                    CopyPstr("\pError: ",&displayStr);
                    NumToString(dspPBPtr->ioResult,theError);
                    PStrCat(theError,(Ptr)displayStr);
            }
 
                /* if we have a message we want to show, put up a dialog
                    for the user */
            if (displayStr[0] != 0)
            {
                ParamText(displayStr,"\p","\p","\p");
                itemHit = Alert(rErrorDialog, nil);
            }       
}