/******************************************************************************
 *                                                                            *
 *                                LayGO (tm)                                  *
 *           Copyright (c) 1994-2001 by Advanced Relay Corporation            *
 *                           All rights reserved.                             *
 *                                                                            *
 ******************************************************************************

  PROGRAM DESCRIPTION

    This is a demonstration of the LayGO API.  Based on the command-line
    argument shown, it constructs one of the following devices:

        -p : X21 bis only
        -b : X21 bis and HDLC LAPB
        -x : X21 bis, HDLC LAPB, and X.25
        -f : X21 bis and Frame Relay

    It then establishes a connection on this device.  If the device is
    X.25 or Frame Relay, it opens a PVC, establishes a connection and
    sends and receives numbered data packets on the PVC.  Otherwise,
    packets are sent and received directly on the device.  All devices
    are then disconnected, disassembled and closed.

    This program can be run using the hardware emulation driver, using
    the hardware's internal loopback mode or in a back-to-back connection
    between two computers, each running this program.

    This program can be compiled as a Win32 console program for Windows 95,
    Windows NT and Windows 2000 with -Dx_WIN32, for Solaris with -Dx_SOLARIS
    or for Linux with -Dx_LINUX.

*******************************************************************************/

/*
** #includes ******************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "options.h"
#include "hex.h"
#include "kbhit.h"
#include "laygo.h"
#include "laygomsg.h"
#include "laygover.h"
#include "spin.h"
#include "timing.h"
#include "vtxt.h"

/*
** #defines *******************************************************************
*/

#define dlg_PRINT_SUCCESS(f,c)                      \
    printf("lgo_" f "() succeeded on CID %d\n", (c))

#define dlg_PRINT_FAILURE(f,c,e)                    \
    printf("lgo_" f "() failed on CID %d: ", (c));  \
    lgo_PRINT_ERROR_MESSAGE(e,"")

#define dlg_PRINT_FATAL(f,c,e)                      \
    printf("Fatal error: "); dlg_PRINT_FAILURE(f,c,e)

#define dlg_BUFFER_SIZE                           256
#define dlg_PACKET_SIZE                           128

/* Define as PHYS0 for line 0 and PHYS1 for line 1   */
#define dlg_PHYSICAL_SERVICE_ID                 "PHYS0"

/* Define as LAPB0 for line 0 and LAPB1 for line 1   */
#define dlg_LINK_SERVICE_ID                     "LAPB0"

/* Define as PKT0 for line 0 and PKT1 for line 1     */
#define dlg_PACKET_SERVICE_ID                   "PKT0"

/* Define as FRM0 for line 0 and FRM1 for line 1     */
#define dlg_FRAME_RELAY_SERVICE_ID              "FRM0"

/* Define as PVC01 for line 0 and PVC11 for line 1   */
#define dlg_X25_PVC_SERVICE_ID                  "PVC01"

/* Define as FRM016 for line 0 and FRM116 for line 1 */
#define dlg_FRM_PVC_SERVICE_ID                  "FRM016"

/*
** typedefs *******************************************************************
*/

typedef enum
{
    dlg_DEV_PHYS,
    dlg_DEV_LAPB,
    dlg_DEV_X25,
    dlg_DEV_FRM,
    dlg_DEV_MAX
}
DlgDeviceId;

/*
** extern variable definitions ************************************************
*/

    /* None */

/*
** static variable definitions ************************************************
*/

static LCid             dlg_PhysicalCid = lgo_INVALID_CID;  /* CID for physical layer   */
static LCid             dlg_LinkCid     = lgo_INVALID_CID;  /* CID for link layer       */
static LCid             dlg_PacketCid   = lgo_INVALID_CID;  /* CID for packet layer     */
static LCid             dlg_FrameCid    = lgo_INVALID_CID;  /* CID for frame layer      */
static LCid             dlg_PvcCid      = lgo_INVALID_CID;  /* CID for PVC sublayer     */
static LCid             dlg_TransferCid = lgo_INVALID_CID;  /* CID for data transfer    */
static char **          dlg_CfgList     = NULL;
static int              dlg_CfgCount    = 0;
static unsigned char    dlg_RxBuffer[dlg_BUFFER_SIZE];

/* Default stack is physical layer only */
static int              dlg_DevConfig   = dlg_DEV_PHYS;

/*
** static function declarations ***********************************************
*/

static LResult  dlg_AssembleMajorDevice(void);
static void     dlg_AtExit(void);
static LResult  dlg_ConnectLayer(LCid, char *);
static LResult  dlg_ConnectMajorDevice(void);
static void     dlg_DisassembleMajorDevice(void);
static LResult  dlg_DisconnectLayer(LCid);
static LResult  dlg_DisconnectMajorDevice(void);
static void     dlg_ExchangeData(LCid);
static LResult  dlg_InitializeStack(int, char **);
static LCid     dlg_OpenLayer(LService);

/******************************************************************************
                                    Main Line
*******************************************************************************/
int
main
    (
    int     argc,
    char *  argv[]
    )
{
    char *  pvc = NULL;
    LResult result;

    lgo_PRINT_BANNER("LayGO (tm) Protocol Stack Demonstration Program");

    opt_PARSE_COMMAND_LINE(argc, argv, ParseCommandLine);

    if (dlg_InitializeStack(dlg_CfgCount, dlg_CfgList) < 0)
    {
        exit(EXIT_FAILURE);
    }

    printf("\nReady to assemble major device.\n");

    kb_PAUSE();

    /* Assemble the protocol stack */
    if (dlg_AssembleMajorDevice() < 0)
    {
        exit(EXIT_FAILURE);
    }

    printf("\nReady to connect and detach major device.\n");

    kb_PAUSE();

    if (dlg_ConnectMajorDevice() < 0)
    {
        exit(EXIT_FAILURE);
    }

    if (dlg_DevConfig == dlg_DEV_X25)
    {
        pvc = dlg_X25_PVC_SERVICE_ID;
    }
    else if (dlg_DevConfig == dlg_DEV_FRM)
    {
        pvc = dlg_FRM_PVC_SERVICE_ID;
    }

    if (pvc != NULL)
    {
        printf("\nReady to open and connect PVC.\n");

        kb_PAUSE();

        /* Once the major device is connected, open any VC */
        if ((dlg_PvcCid = dlg_OpenLayer(pvc)) < 0)
        {
            exit(EXIT_FAILURE);
        }

        if (dlg_ConnectLayer(dlg_PvcCid, pvc) < 0)
        {
            exit(EXIT_FAILURE);
        }

        dlg_TransferCid = dlg_PvcCid;
    }

    /* Send and receive data on the established connection */
    printf("\nReady for full duplex operation on CID %d.\n", dlg_TransferCid);

    kb_PAUSE();

    dlg_ExchangeData(dlg_TransferCid);

    if (pvc != NULL)
    {
        printf("\nReady to disconnect and close PVC.\n");

        kb_PAUSE();

        dlg_DisconnectLayer(dlg_PvcCid);

        if ((result = lgo_Close(dlg_PvcCid)) < 0)
        {
            dlg_PRINT_FAILURE("Close", dlg_PvcCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Close", dlg_PvcCid);
        }
    }

    printf("\nReady to attach and disconnect major device.\n");

    kb_PAUSE();

    if (dlg_DisconnectMajorDevice() < 0)
    {
        exit(EXIT_FAILURE);
    }

    /*
        Could terminate program now, but we will disassemble the device
        as if we were going to build a different device on the same line.
    */
    printf("\nReady to disassemble major device.\n");

    kb_PAUSE();

    dlg_DisassembleMajorDevice();

    printf("\n    *** End of LayGO demonstration - Normal termination ***\n");

    exit(EXIT_SUCCESS);

} /* end of main */

/******************************************************************************
                            Function Definitions
*******************************************************************************/

/******************************************************************************

  <*> ParseCommandLine()

  DESCRIPTION
    Parses the command-line for command-line options.  The following
    options are always defined:

        -?  :   Print syntax message
        -h  :   Print syntax message

  SIDE EFFECTS
    Writes to global variables.
    Writes to stderr.

  RETURNS
    opt_SUCCESS, if successful.
    A negative error code, otherwise.

  NOTES
    None

*******************************************************************************/
static
int
ParseCommandLine
    (
    int             argc,   /* count of arguments in list       */
    char **         argv,   /* argument list                    */
    int             start   /* argument to start parsing from   */
    )
{
    const char *    optionSyntax    = ""
        "   [-b X21 bis and HDLC LAPB]\n"
        "   [-f X21 bis and Frame Relay]\n"
        "   [-p X21 bis only]\n"
        "   [-x X21 bis, HDLC LAPB, and X.25]\n";
    const char *    optionList      = "?hbfpx";
    int             optionIndex     = start;
    char *          optionArgument  = NULL;
    int             optionError     = FALSE;
    int             option;

    while (!optionError &&
                (option = opt_GetOption(argc, argv, optionList,
                                    &optionIndex, &optionArgument)) > 0)
    {
        switch (option)
        {
            case 'b':

                dlg_DevConfig = dlg_DEV_LAPB;
                break;

            case 'f':

                dlg_DevConfig = dlg_DEV_FRM;
                break;

            case 'p':

                dlg_DevConfig = dlg_DEV_PHYS;
                break;

            case 'x':

                dlg_DevConfig = dlg_DEV_X25;
                break;

            opt_SWITCH_DEFAULT(argv, optionIndex, optionError);
        }
    }

    /* Process any remaining command-line arguments */
    if (!optionError && optionIndex < argc)
    {
        dlg_CfgList     = argv + optionIndex;
        dlg_CfgCount    = argc - optionIndex;
    }

    /* Print syntax if error and return option error code */
    opt_RETURN(argv[0], optionError, optionSyntax);

} /* end of ParseCommandLine */


/******************************************************************************

  <*> dlg_AssembleMajorDevice()

  DESCRIPTION
    Opens the protocol layers and assembles them into a protocol stack using
    lgo_Push().  The physical layer is the base of the stack and doesn't need
    to be pushed onto anything.

  SIDE EFFECTS
    Sets value of CID global variables.

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
LResult
dlg_AssembleMajorDevice
    (
    void
    )
{
    LResult result;

    /* Always open physical layer */
    if ((dlg_PhysicalCid = dlg_OpenLayer(dlg_PHYSICAL_SERVICE_ID)) < 0)
    {
        return (FAILURE);
    }

    switch (dlg_DevConfig)
    {
        case dlg_DEV_PHYS:

            dlg_TransferCid = dlg_PhysicalCid;
            break;

        case dlg_DEV_LAPB:
        case dlg_DEV_X25:

            if ((dlg_LinkCid = dlg_OpenLayer(dlg_LINK_SERVICE_ID)) < 0)
            {
                lgo_Close(dlg_PhysicalCid);

                return (FAILURE);
            }
            dlg_TransferCid = dlg_LinkCid;
            break;

        case dlg_DEV_FRM:

            if ((dlg_FrameCid = dlg_OpenLayer(dlg_FRAME_RELAY_SERVICE_ID)) < 0)
            {
                lgo_Close(dlg_PhysicalCid);

                return (FAILURE);
            }
            break;

        default:

            return (FAILURE);
    }


    if (dlg_DevConfig == dlg_DEV_X25)
    {
        /* Open packet layer */
        if ((dlg_PacketCid = dlg_OpenLayer(dlg_PACKET_SERVICE_ID)) < 0)
        {
            return (FAILURE);
        }
    }

    switch (dlg_DevConfig)
    {
        case dlg_DEV_LAPB:
        case dlg_DEV_X25:

            /* Push link layer onto physical layer */
            if ((result = lgo_Push(dlg_PhysicalCid, dlg_LinkCid)) < 0)
            {
                printf("Fatal error: lgo_Push() failed for CIDs %d and %d: %s\n",
                        dlg_PhysicalCid, dlg_LinkCid,
                        lgo_ErrorMessage(result));

                return (FAILURE);
            }
            else
            {
                printf("lgo_Push() succeeded for CIDs %d and %d.\n",
                        dlg_PhysicalCid, dlg_LinkCid);
            }
            break;

        case dlg_DEV_FRM:

            /* Push frame relay layer onto physical layer */
            if ((result = lgo_Push(dlg_PhysicalCid, dlg_FrameCid)) < 0)
            {
                printf("Fatal error: lgo_Push() failed for CIDs %d and %d: %s\n",
                        dlg_PhysicalCid, dlg_FrameCid,
                        lgo_ErrorMessage(result));

                return (FAILURE);
            }
            else
            {
                printf("lgo_Push() succeeded for CIDs %d and %d.\n",
                        dlg_PhysicalCid, dlg_FrameCid);
            }
            break;
    }

    if (dlg_DevConfig == dlg_DEV_X25)
    {
        /* Push packet layer onto link layer */
        if ((result = lgo_Push(dlg_LinkCid, dlg_PacketCid)) < 0)
        {
            printf("Fatal error: lgo_Push() failed for CIDs %d and %d: %s\n",
                    dlg_LinkCid, dlg_PacketCid,
                    lgo_ErrorMessage(result));

            return (FAILURE);
        }
        else
        {
            printf("lgo_Push() succeeded for CIDs %d and %d.\n", dlg_LinkCid, dlg_PacketCid);
        }
    }

    printf("Major device assembled.\n");

    return (SUCCESS);

} /* end of dlg_AssembleMajorDevice */


/******************************************************************************

  <*> dlg_AtExit()

  DESCRIPTION
    General cleanup function to be registered with atexit().

  SIDE EFFECTS
    None

  RETURNS
    Nothing

  NOTES
    None

*******************************************************************************/
static
void
dlg_AtExit
    (
    void
    )
{

    lgo_DisableStack();
    lgo_UninitializeStack();

} /* end of dlg_AtExit */


/******************************************************************************

  <*> dlg_ConnectLayer()

  DESCRIPTION
    Initiates a connection on an open protocol layer.

  SIDE EFFECTS
    None

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
LResult
dlg_ConnectLayer
    (
    LCid        cid,    /* CID of layer to initiate connection on   */
    char *      service /* service name of CID                      */
    )
{
    int         retries = 5;
    LError      error   = -1;
    int         done    = FALSE;
    LBufferSize size;
    LEvent      event;

    printf("Connecting %s service '%s'...\n", lgo_ProtocolMessage(lgo_Protocol(cid)), service);

    /* Make sure stack has had time to get ready */
    while (error < 0 && retries)
    {
        /* Connect the layer */
        if ((error = lgo_ConnectRequest(cid, NULL, 0)) < 0)
        {
            retries--;
        }
        timing_YIELD();
    }

    if (error < 0)
    {
        dlg_PRINT_FATAL("ConnectRequest", cid, error);

        return (FAILURE);
    }
    else
    {
        dlg_PRINT_SUCCESS("ConnectRequest", cid);
    }

    /* Wait for other side to respond */
    printf("Waiting for response...\n");

    while (!done)
    {
        timing_YIELD();

        size = (LBufferSize) sizeof(dlg_RxBuffer);

        switch (event = lgo_Event(cid, dlg_RxBuffer, &size))
        {
            case lgo_EVENT_CONNECTED:
            case lgo_EVENT_CONNECT_ACCEPT:

                printf("Connected.\n");
                return (SUCCESS);

            case lgo_EVENT_CONNECT_REFUSE:

                printf("Fatal error: Connect request refused.\n");
                return (FAILURE);

            case lgo_EVENT_CONNECT_TIMEOUT:

                printf("Fatal error: Connect request timed out.\n");
                return (FAILURE);

            case lgo_EVENT_NONE:

                break;

            default:

                if (event >= 0)
                {
                    printf("Unexpected event: '%s'.\n", lgo_EventMessage(event));
                }
                else
                {
                    printf("Fatal error: '%s'.\n", lgo_ErrorMessage(event));

                    return (FAILURE);
                }
                break;
        }
    }

    return (FAILURE);

} /* end of dlg_ConnectLayer */


/******************************************************************************

  <*> dlg_ConnectMajorDevice()

  DESCRIPTION
    Connects the major device layers, bottom up.

  SIDE EFFECTS
    None

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
LResult
dlg_ConnectMajorDevice
    (
    void
    )
{
    LResult result;

    /* Connect the physical, link and X.25 major devices */
    if (dlg_ConnectLayer(dlg_PhysicalCid, dlg_PHYSICAL_SERVICE_ID) < 0)
    {
        return (FAILURE);
    }

    if (dlg_DevConfig == dlg_DEV_LAPB || dlg_DevConfig == dlg_DEV_X25)
    {
        if (dlg_ConnectLayer(dlg_LinkCid, dlg_LINK_SERVICE_ID) < 0)
        {
            return (FAILURE);
        }
    }

    if (dlg_DevConfig == dlg_DEV_X25)
    {
        if (dlg_ConnectLayer(dlg_PacketCid, dlg_PACKET_SERVICE_ID) < 0)
        {
            return (FAILURE);
        }
    }

    if (dlg_DevConfig == dlg_DEV_FRM)
    {
        if (dlg_ConnectLayer(dlg_FrameCid, dlg_FRAME_RELAY_SERVICE_ID) < 0)
        {
            return (FAILURE);
        }
    }

    /* Detach layers we no longer need */
    if (dlg_DevConfig != dlg_DEV_PHYS)
    {
        if ((result = lgo_Detach(dlg_PhysicalCid)) < 0)
        {
            dlg_PRINT_FAILURE("Detach", dlg_PhysicalCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Detach", dlg_PhysicalCid);
        }
    }

    if (dlg_DevConfig == dlg_DEV_X25)
    {
        if ((result = lgo_Detach(dlg_LinkCid)) < 0)
        {
            dlg_PRINT_FAILURE("Detach", dlg_LinkCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Detach", dlg_LinkCid);
        }

        if ((result = lgo_Detach(dlg_PacketCid)) < 0)
        {
            dlg_PRINT_FAILURE("Detach", dlg_PacketCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Detach", dlg_PacketCid);
        }
    }

    if (dlg_DevConfig == dlg_DEV_FRM)
    {
        if ((result = lgo_Detach(dlg_FrameCid)) < 0)
        {
            dlg_PRINT_FAILURE("Detach", dlg_FrameCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Detach", dlg_FrameCid);
        }
    }

    printf("Major device connected.\n");

    return (SUCCESS);

} /* end of dlg_ConnectMajorDevice */


/******************************************************************************

  <*> dlg_DisassembleMajorDevice()

  DESCRIPTION
    Removes protocol layers from the top of the stack and closes
    them, releasing all resources associated with them.

  SIDE EFFECTS
    None

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
void
dlg_DisassembleMajorDevice
    (
    void
    )
{
    LResult result;

    printf("Disassembling major device.\n");

    if (dlg_DevConfig == dlg_DEV_X25)
    {
        /* Remove X.25 layer from top of device and close it */
        if ((result = lgo_Pop(dlg_PacketCid)) < 0)
        {
            dlg_PRINT_FAILURE("Pop", dlg_PacketCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Pop", dlg_PacketCid);
        }

        if ((result = lgo_Close(dlg_PacketCid)) < 0)
        {
            dlg_PRINT_FAILURE("Close", dlg_PacketCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Close", dlg_PacketCid);
        }
    }

    if (dlg_DevConfig == dlg_DEV_LAPB || dlg_DevConfig == dlg_DEV_X25)
    {
        /* Remove link layer from top of device and close it */
        if ((result = lgo_Pop(dlg_LinkCid)) < 0)
        {
            dlg_PRINT_FAILURE("Pop", dlg_LinkCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Pop", dlg_LinkCid);
        }

        if ((result = lgo_Close(dlg_LinkCid)) < 0)
        {
            dlg_PRINT_FAILURE("Close", dlg_LinkCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Close", dlg_LinkCid);
        }
    }

    if (dlg_DevConfig == dlg_DEV_FRM)
    {
        /* Remove Frame Relay from top of device and close it */
        if ((result = lgo_Pop(dlg_FrameCid)) < 0)
        {
            dlg_PRINT_FAILURE("Pop", dlg_FrameCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Pop", dlg_FrameCid);
        }

        if ((result = lgo_Close(dlg_FrameCid)) < 0)
        {
            dlg_PRINT_FAILURE("Close", dlg_FrameCid, result);
        }
        else
        {
            dlg_PRINT_SUCCESS("Close", dlg_FrameCid);
        }
    }

    /* Physical layer is always the bottom, is never pushed or popped */
    if ((result = lgo_Close(dlg_PhysicalCid)) < 0)
    {
        dlg_PRINT_FAILURE("Close", dlg_PhysicalCid, result);
    }
    else
    {
        dlg_PRINT_SUCCESS("Close", dlg_PhysicalCid);
    }

} /* end of dlg_DisassembleMajorDevice */


/******************************************************************************

  <*> dlg_DisconnectLayer()

  DESCRIPTION
    Disconnects a layer that is in data transfer state.

  SIDE EFFECTS
    None

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
LResult
dlg_DisconnectLayer
    (
    LCid            cid /* CID to disconnect */
    )
{
    LBufferSize     size;
    LEvent          event;
    LError          error;
    LBoolean        done;

    /* Read any events first, before checking state */
    done = FALSE;

    while (!done)
    {
        timing_YIELD();
        size = (LBufferSize) sizeof(dlg_RxBuffer);

        switch (event = lgo_Event(cid, dlg_RxBuffer, &size))
        {
            case lgo_ERROR_NOTHING_WAITING:

                done = TRUE;
                break;

            case lgo_ERROR_DATA_WAITING:

                lgo_DiscardData(cid);
                break;

            default:

                if (event < 0)
                {
                    done = TRUE;
                }
                break;
        }
    }

    switch (lgo_State(cid))
    {
        case lgo_STATE_OPEN:

            return (SUCCESS);

        case lgo_STATE_WAITING_FOR_LOCAL_CONFIRMATION:

            if ((error = lgo_DisconnectConfirm(cid, NULL, 0)) < 0)
            {
                return (error);
            }

            return (SUCCESS);

        case lgo_STATE_DATA_TRANSFER_XON:
        case lgo_STATE_DATA_TRANSFER_XOFF:

            break;

        default:

            /* No point in trying to issue disconnect request */
            if ((error = lgo_Reopen(cid)) < 0)
            {
                return (error);
            }
            return (SUCCESS);
    }

    /* Send disconnect */
    if ((error = lgo_DisconnectRequest(cid, NULL, 0)) < 0)
    {
        dlg_PRINT_FAILURE("DisconnectRequest", cid, error);

        return (FAILURE);
    }
    else
    {
        dlg_PRINT_SUCCESS("DisconnectRequest", cid);
    }

    /* Wait for response */
    printf("Waiting for response...\n");

    done = FALSE;

    while (!done)
    {
        timing_YIELD();
        size = (LBufferSize) sizeof(dlg_RxBuffer);

        switch (event = lgo_Event(cid, dlg_RxBuffer, &size))
        {
            case lgo_EVENT_DISCONNECTED:
            case lgo_EVENT_DISCONNECT_CONFIRMATION:

                printf("Disconnected.\n");
                return (SUCCESS);

            case lgo_EVENT_DISCONNECT_TIMEOUT:

                printf("Disconnect timed out.\n");
                return (FAILURE);

            case lgo_ERROR_TRANSMISSION_BLOCKED:

                printf("Reopened.\n");
                return (lgo_Reopen(cid));

            case lgo_ERROR_DATA_WAITING:

                lgo_DiscardData(cid);
                break;

            case lgo_EVENT_NONE:

                break;

            default:

                if (event >= 0)
                {
                    printf("Unexpected event: '%s'.\n", lgo_EventMessage(event));
                }
                else
                {
                    printf("Fatal error: '%s'.\n", lgo_ErrorMessage(event));
                    return (FAILURE);
                }
                break;
        }
    }

    return (FAILURE);

} /* end of dlg_DisconnectLayer */


/******************************************************************************

  <*> dlg_DisconnectMajorDevice()

  DESCRIPTION
    Attaches and disconnects the major device so that it can be
    disassembled.

  SIDE EFFECTS
    Sets value of CID global variables.

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
LResult
dlg_DisconnectMajorDevice
    (
    void
    )
{

    printf("Disconnecting major device.\n");

    /*
        Regain control of layers to disconnect and close them.
    */
    if (dlg_DevConfig != dlg_DEV_PHYS)
    {
        if ((dlg_PhysicalCid = lgo_Attach(dlg_PHYSICAL_SERVICE_ID)) < 0)
        {
            printf("Fatal error: lgo_Attach() failed for service '%s': %s\n",
                    dlg_PHYSICAL_SERVICE_ID, lgo_ErrorMessage(dlg_PhysicalCid));
            return (FAILURE);
        }
        else
        {
            printf("lgo_Attach() succeeded for service '%s', CID %d\n",
                    dlg_PHYSICAL_SERVICE_ID, dlg_PhysicalCid);
        }
    }


    if (dlg_DevConfig == dlg_DEV_X25)
    {
        if ((dlg_LinkCid = lgo_Attach(dlg_LINK_SERVICE_ID)) < 0)
        {
            printf("Fatal error: lgo_Attach() failed for service '%s': %s\n",
                        dlg_LINK_SERVICE_ID, lgo_ErrorMessage(dlg_LinkCid));
            return (FAILURE);
        }
        else
        {
            printf("lgo_Attach() succeeded for service '%s', CID %d\n",
                        dlg_LINK_SERVICE_ID, dlg_LinkCid);
        }

        if ((dlg_PacketCid = lgo_Attach(dlg_PACKET_SERVICE_ID)) < 0)
        {
            printf("Fatal error: lgo_Attach() failed for service '%s': %s\n",
                        dlg_PACKET_SERVICE_ID, lgo_ErrorMessage(dlg_PacketCid));
            return (FAILURE);
        }
        else
        {
            printf("lgo_Attach() succeeded for service '%s', CID %d\n",
                        dlg_PACKET_SERVICE_ID, dlg_PacketCid);
        }
        dlg_DisconnectLayer(dlg_PacketCid);
        dlg_DisconnectLayer(dlg_LinkCid);
    }
    else if (dlg_DevConfig == dlg_DEV_FRM)
    {
        if ((dlg_FrameCid = lgo_Attach(dlg_FRAME_RELAY_SERVICE_ID)) < 0)
        {
            printf("Fatal error: lgo_Attach() failed for service '%s': %s\n",
                        dlg_FRAME_RELAY_SERVICE_ID, lgo_ErrorMessage(dlg_FrameCid));
            return (FAILURE);
        }
        else
        {
            printf("lgo_Attach() succeeded for service '%s', CID %d\n",
                    dlg_FRAME_RELAY_SERVICE_ID, dlg_FrameCid);
        }
        dlg_DisconnectLayer(dlg_FrameCid);
    }
    else if (dlg_DevConfig == dlg_DEV_LAPB)
    {
        dlg_DisconnectLayer(dlg_LinkCid);
    }

    /*
        Physical layer is last since terminate connections bottom up.
        Can fail, but this is not handled since we are hanging up anyway.
    */
    dlg_DisconnectLayer(dlg_PhysicalCid);

    return (SUCCESS);

} /* end of dlg_DisconnectMajorDevice */


/******************************************************************************

  <*> dlg_ExchangeData()

  DESCRIPTION
    Sends and receives data to verify data transfer state has been achieved.
    Validates data using vtxt functions.

  SIDE EFFECTS
    None

  RETURNS
    Nothing

  NOTES
    None

*******************************************************************************/
static
void
dlg_ExchangeData
    (
    LCid            cid /* the CID to exchange data on */
    )
{
    unsigned int    failures    = 0;
    unsigned int    successes   = 0;
    unsigned int    bytesSent   = 0;
    unsigned int    nonDataPkts = 0;
    unsigned int    dataPkts    = 0;
    unsigned int    bytesRead   = 0;
    unsigned int    polls       = 0;
    unsigned int    reps        = 100;
    LBufferSize     count       = dlg_PACKET_SIZE;
    LBoolean        done        = FALSE;
    LBufferSize     rxCount;
    LEvent          event;
    VtxtResult      vtxtResult;
    Vtxt            vtxt;
    VtxtBuffer      vtxtBuffer;


    printf("Starting full-duplex data transfer:\n\n"
           "%u buffers of %d bytes on CID %d...\n",
           reps, count, cid);

    if ((vtxt = vtxt_New(count)) == NULL)
    {
        printf("Error: verified transmission not available.\n");
        return;
    }

    kb_INITIALIZE();

    while (!done && !kb_KBHIT())
    {

        timing_YIELD();

        if (successes < reps)
        {

            vtxtBuffer = vtxt_GetTx(vtxt,count);

            /* Write the buffer */
            if (lgo_Write(cid, vtxtBuffer, count) < 0)
            {
                /*
                    Failure generally means we are trying to write buffers
                    faster than the transmitter can handle due to line speed
                */
                failures++;
            }
            else
            {
                vtxt_NextTx(vtxt);

                successes++;
                bytesSent += count;
                spin_SPIN();

            }
        }

        timing_YIELD();

        rxCount = (LBufferSize) sizeof(dlg_RxBuffer);

        while (polls++, (event = lgo_Poll(cid, &rxCount)) >= 0)
        {
            if (event == lgo_EVENT_DATA_OK)
            {
                bytesRead += lgo_Read(cid, dlg_RxBuffer, rxCount);
                dataPkts++;

                if ((vtxtResult = vtxt_ValidateRx(vtxt, dlg_RxBuffer, count)) < 0)
                {
                    printf("Error: data did not validate - %d.\n", vtxtResult);
                    hex_DUMP(dlg_RxBuffer, rxCount);
                }

            }
            else
            {
                /* Event received other than lgo_EVENT_DATA_OK */
                rxCount = (LBufferSize) sizeof(dlg_RxBuffer);
                event   = lgo_Event(cid, dlg_RxBuffer, &rxCount);
                printf("Nondata event %d: %s (%d)\n",
                        rxCount, lgo_EventMessage(event), event);
                nonDataPkts++;
            }
            if (dataPkts == reps)
            {
                done = TRUE;
            }
        }
    }

    vtxt_Dispose(vtxt);

    kb_FINISH();

    printf( "\nSend summary:\n"
            "  Write attempts  %10u\n"
            "  Successes       %10u out of %u\n"
            "  Bytes sent      %10u\n",
            failures + successes, successes, reps, bytesSent);

    printf( "\nReceive summary:\n"
            "  Polls           %10u\n"
            "  Data packets    %10u\n"
            "  Other packets   %10u\n"
            "  Bytes received  %10u\n",
            polls, dataPkts, nonDataPkts, bytesRead);

    printf("Data transfer complete.\n");

} /* end of dlg_ExchangeData */


/******************************************************************************

  <*> dlg_InitializeStack()

  DESCRIPTION
    Configures, initializes and enables the stack.

  SIDE EFFECTS
    None

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    If successful, installs an atexit procedure to uninitialize the
    stack when the program terminates.

*******************************************************************************/
static
LResult
dlg_InitializeStack
    (
    int         cfgCount,   /* count of cfg files in list   */
    char **     cfgList     /* list of cfg files            */
    )
{
    LBoolean    error;
    LResult     result;
    int         i;

    printf("\nReady to configure and initialize protocol stack.\n");

    kb_PAUSE();

    /* Configure stack from file */
    if (cfgCount > 0)
    {
        printf("Configuring the protocol stack:\n\n");
        for (i = 0, error = FALSE; i < cfgCount; i++)
        {
            if ((result = lgo_ConfigureStack(cfgList[i])) < 0)
            {
                printf("   Error loading configuration file '%s': %s\n",
                            cfgList[i], lgo_ErrorMessage(result));
                error++;
            }
            else
            {
                printf("   Configuration file '%s' processed\n", cfgList[i]);

            }
        }
        printf("\n");

        /* Check all configuration files before exiting if there are errors */
        if (error)
        {
            printf("Fatal error: Stack configuration failed\n");
            return (FAILURE);
        }
    }

    /* Allocate buffers and initialize hardware */
    if ((result = lgo_InitializeStack()) < 0)
    {
        printf("Fatal error: Stack initialization failed: %s\n", lgo_ErrorMessage(result));
        return (FAILURE);
    }

    /* Allow protocol stack activation */
    lgo_EnableStack();

    /* Protocol stack cleanup at program termination */
    atexit(dlg_AtExit);

    printf("Stack configured and initialized.\n");

    return (SUCCESS);

} /* end of dlg_InitializeStack */


/******************************************************************************

  <*> dlg_OpenLayer()

  DESCRIPTION
    Attempts to open a service.

  SIDE EFFECTS
    None

  RETURNS
    SUCCESS if successful.
    FAILURE otherwise.

  NOTES
    None

*******************************************************************************/
static
LCid
dlg_OpenLayer
    (
    LService    service /* The service to open */
    )
{
    int         retries = 5;
    LCid        cid;

    /* Make sure stack has had time to get ready */
    do
    {
        /* Open the layer */
        if ((cid = lgo_Open(service)) < 0)
        {
            retries--;
        }
        timing_YIELD();
    }
    while (lgo_IS_INVALID_CID(cid) && retries);

    if (lgo_IS_INVALID_CID(cid))
    {
        printf("Fatal error: lgo_Open() failed for service '%s': %s.\n",
                service,
                lgo_ErrorMessage(cid));
        return (FAILURE);
    }
    else
    {
        printf( "lgo_Open() succeeded for service '%s'.\n"
                "   Protocol: %s   CID: %d\n",
                service,
                lgo_ProtocolMessage(lgo_Protocol(cid)),
                cid);
    }

    return (cid);

} /* end of dlg_OpenLayer */


/******************************************************************************
                                   End of File
*******************************************************************************/
