/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    vwdos.c

Abstract:

    ntVdm netWare (Vw) IPX/SPX Functions

    Vw: The peoples' network

    Contains handlers for DOS IPX/SPX calls (netware functions). The IPX APIs
    use WinSock to perform the actual operations

    Contents:
        VwIPXCancelEvent
        VwIPXCloseSocket
        VwIPXDisconnectFromTarget
        VwIPXGenerateChecksum
        VwIPXGetInformation
        VwIPXGetInternetworkAddress
        VwIPXGetIntervalMarker
        VwIPXGetLocalTarget
        VwIPXGetLocalTargetAsync
        VwIPXGetMaxPacketSize
        VwIPXInitialize
        VwIPXListenForPacket
        VwIPXOpenSocket
        VwIPXRelinquishControl
        VwIPXScheduleAESEvent
        VwIPXScheduleIPXEvent
        VwIPXSendPacket
        VwIPXSendWithChecksum
        VwIPXSPXDeinit
        VwIPXVerifyChecksum

        VwSPXAbortConnection
        VwSPXEstablishConnection
        VwSPXGetConnectionStatus
        VwSPXInitialize
        VwSPXListenForConnection
        VwSPXListenForSequencedPacket
        VwSPXSendSequencedPacket
        VwSPXTerminateConnection

Author:

    Richard L Firth (rfirth) 30-Sep-1993

Environment:

    User-mode Win32

Revision History:

    30-Sep-1993 rfirth
        Created

--*/

#include "vw.h"
#pragma hdrstop

//
// functions
//


VOID
VwIPXCancelEvent(
    VOID
    )

/*++

Routine Description:

    Cancels event described by an ECB

    This call is Synchronous

Arguments:

    Inputs
        BX      06h
        ES:SI   ECB

    Outputs
        AL      Completion code:
                    00h Success
                    F9h Can't cancel ECB
                    FFh ECB not in use

Return Value:

    None.

--*/

{
    LPECB pEcb;
    WORD status;

    CHECK_INTERRUPTS("VwIPXCancelEvent");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXCancelEvent,
                IPXDBG_LEVEL_INFO,
                "VwIPXCancelEvent(%04x:%04x)\n",
                getES(),
                getSI()
                ));

    IPX_GET_IPX_ECB(pEcb);

    status = _VwIPXCancelEvent( pEcb );

    IPX_SET_STATUS(status);
}


VOID
VwIPXCloseSocket(
    VOID
    )

/*++

Routine Description:

    Closes a socket and cancels any outstanding events on the socket.
    Closing an unopened socket does not return an error
    ESRs in cancelled ECBs are not called

    This call is Synchronous

Arguments:

    Inputs
        BX      01h
        DX      Socket Number

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    WORD socketNumber;

    CHECK_INTERRUPTS("VwIPXCloseSocket");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXCloseSocket,
                IPXDBG_LEVEL_INFO,
                "VwIPXCloseSocket(%#x)\n",
                B2LW(IPX_SOCKET_PARM())
                ));

    IPX_GET_SOCKET(socketNumber);

    _VwIPXCloseSocket( socketNumber );

}


VOID
VwIPXDisconnectFromTarget(
    VOID
    )

/*++

Routine Description:

    Performs no action for NTVDM IPX

    This call is Synchronous

Arguments:

    Inputs
        BX      0Bh
        ES:SI   Request buffer:
                    Destination Network DB 4 DUP (?)
                    Destination Node    DB 6 DUP (?)
                    Destination Socket  DB 2 DUP (?)

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXDisconnectFromTarget");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXDisconnectFromTarget,
                IPXDBG_LEVEL_INFO,
                "VwIPXDisconnectFromTarget\n"
                ));
}


VOID
VwIPXGenerateChecksum(
    VOID
    )

/*++

Routine Description:

    Generates checksum for a transmit ECB

    This call is Synchronous

Arguments:

    Inputs
        BX      21h
        ES:SI   ECB address

    Outputs
        No registers
        ECB checksum field is updated

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXGenerateChecksum");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXGenerateChecksum,
                IPXDBG_LEVEL_INFO,
                "VwIPXGenerateChecksum\n"
                ));
}


VOID
VwIPXGetInformation(
    VOID
    )

/*++

Routine Description:

    Returns a bit-map of supported functions

    This call is Synchronous

Arguments:

    Inputs
        BX      1Fh
        DX      0000h

    Outputs
        DX      Bit map:
                    0001h   Set if IPX is IPXODI.COM, not dedicated IPX
                    0002h   Set if checksum functions (20h, 21h, 22h) supported

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXGetInformation");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXGetInformation,
                IPXDBG_LEVEL_INFO,
                "VwIPXGetInformation\n"
                ));

    IPX_SET_INFORMATION(IPX_ODI);
}


VOID
VwIPXGetInternetworkAddress(
    VOID
    )

/*++

Routine Description:

    Returns a buffer containing the net number and node number for this
    station.

    This function cannot return an error (!)

    Assumes:    1. GetInternetAddress has been successfully called in the
                   DLL initialization phase

    This call is Synchronous

Arguments:

    Inputs
        BX      09h

    Outputs
        ES:SI   Buffer
                    Network Address DB 4 DUP (?)
                    Node Address    DB 6 DUP (?)

Return Value:

    None.

--*/

{
    LPINTERNET_ADDRESS pAddr;

    CHECK_INTERRUPTS("VwIPXGetInternetworkAddress");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXGetInternetworkAddress,
                IPXDBG_LEVEL_INFO,
                "VwIPXGetInternetworkAddress(%04x:%04x)\n",
                getES(),
                getSI()
                ));

    pAddr = (LPINTERNET_ADDRESS)IPX_BUFFER_PARM(sizeof(*pAddr));
    if (pAddr) {
        _VwIPXGetInternetworkAddress( pAddr );
    }
}


VOID
VwIPXGetIntervalMarker(
    VOID
    )

/*++

Routine Description:

    Just returns the tick count maintained by Asynchronous Event Scheduler

    This call is Synchronous

Arguments:

    Inputs
        BX      08h

    Outputs
        AX      Interval marker

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXGetIntervalMarker");

    setAX( _VwIPXGetIntervalMarker() );

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXGetIntervalMarker,
                IPXDBG_LEVEL_INFO,
                "VwIPXGetIntervalMarker: Returning %04x\n",
                getAX()
                ));
}


VOID
VwIPXGetLocalTarget(
    VOID
    )

/*++

Routine Description:

    Given a target address of the form (network address {4}, node address {6}),
    returns the node address of the target if on the same network, or the node
    address of the router which knows how to get to the next hop in reaching the
    eventual target

    This call is Synchronous

Arguments:

    Inputs
        BX      02h
        ES:SI   Request buffer
                    Destination Network DB 4 DUP (?)
                    Destination Node    DB 6 DUP (?)
                    Destination Socket  DB 2 DUP (?)
        ES:DI   Response buffer
                    Local Target DB 6 DUP (?)

    Outputs
        AL      Completion code
                    00h Success
                    FAh No path to destination node found
        AH      Number of hops to destination
        CX      Transport time

Return Value:

    None.

--*/

{
    LPBYTE pImmediateAddress;
    LPBYTE pNetworkAddress;
    WORD   transportTime;
    WORD   status;

    CHECK_INTERRUPTS("VwIPXGetLocalTarget");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXGetLocalTarget,
                IPXDBG_LEVEL_INFO,
                "VwIPXGetLocalTarget(target buf @ %04x:%04x, local buf @ %04x:%04x)\n",
                getES(),
                getSI(),
                getES(),
                getDI()
                ));


    pImmediateAddress = POINTER_FROM_WORDS(getES(), getDI(), 6);
    pNetworkAddress = POINTER_FROM_WORDS(getES(), getSI(), 12);

    if (pImmediateAddress && pNetworkAddress) {
        status = _VwIPXGetLocalTarget( pNetworkAddress,
                                       pImmediateAddress,
                                       &transportTime );
    }
    else {
        status = IPX_BAD_REQUEST;
    }


    setCX( transportTime );
    setAH(1);

    IPX_SET_STATUS(status);
}


VOID
VwIPXGetLocalTargetAsync(
    VOID
    )

/*++

Routine Description:

    description-of-function.

    This call is Asynchronous

Arguments:

    None.

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXGetLocalTargetAsync");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_ANY,
                IPXDBG_LEVEL_INFO,
                "VwIPXGetLocalTargetAsync\n"
                ));
}


VOID
VwIPXGetMaxPacketSize(
    VOID
    )

/*++

Routine Description:

    Returns the maximum packet size the underlying network can handle

    Assumes:    1. A successfull call to GetMaxPacketSize has been made during
                   DLL initialization
                2. Maximum packet size is constant

    This call is Synchronous

Arguments:

    Inputs
        BX      1Ah

    Outputs
        AX      Maximum packet size
        CX      IPX retry count

Return Value:

    None.

--*/

{
    WORD maxPacketSize;
    WORD retryCount;

    CHECK_INTERRUPTS("VwIPXGetMaxPacketSize");

    maxPacketSize = _VwIPXGetMaxPacketSize( &retryCount );

    setAX(maxPacketSize);

    //
    // The DOS Assembly and C manuals differ slightly here: DOS says
    // we return the IPX retry count in CX. There is no corresponding parameter
    // in the C interface?
    //

    setCX(retryCount);

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXGetMaxPacketSize,
                IPXDBG_LEVEL_INFO,
                "VwIPXGetMaxPacketSize: PacketSize=%d, RetryCount=%d\n",
                getAX(),
                getCX()
                ));
}


VOID
VwIPXInitialize(
    VOID
    )

/*++

Routine Description:

    description-of-function.

Arguments:

    None.

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXInitialize");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_ANY,
                IPXDBG_LEVEL_INFO,
                "VwIPXInitialize\n"
                ));
}


VOID
VwIPXListenForPacket(
    VOID
    )

/*++

Routine Description:

    Queue a listen request against a socket. All listen requests will be
    completed asynchronously, unless cancelled by app

    This call is Asynchronous

Arguments:

    Inputs
        BX      04h
        ES:SI   ECB address

    Outputs
        AL      Completion code
                    FFh Socket doesn't exist

Return Value:

    None.

--*/

{
    LPECB pEcb;
    WORD  status;

    CHECK_INTERRUPTS("VwIPXListenForPacket");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXListenForPacket,
                IPXDBG_LEVEL_INFO,
                "VwIPXListenForPacket(%04x:%04x)\n",
                getES(),
                getSI()
                ));

    IPX_GET_IPX_ECB(pEcb);

    status = _VwIPXListenForPacket( pEcb, ECB_PARM_ADDRESS() );

    IPX_SET_STATUS(status);
}


VOID
VwIPXOpenSocket(
    VOID
    )

/*++

Routine Description:

    Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode.
    The socket will be bound to IPX

    This call is Synchronous

Arguments:

    Inputs
        AL      Socket Longevity flag
                    This parameter is actually in BP - AX has been sequestered
                    by the VDD dispatcher
        BX      00h
        DX      Requested Socket Number

        CX      DOS PDB. This parameter is not part of the IPX API.
                Added because we need to remember which DOS executable created
                the socket: we need to clean-up short-lived sockets when the
                executable terminates

    Outputs
        AL      Completion code:
                    00h Success
                    FFh Socket already open
                    FEh Socket table full
        DX      Assigned socket number

Return Value:

    None.

--*/

{
    BYTE socketLife;
    WORD socketNumber;
    WORD status;

    CHECK_INTERRUPTS("VwIPXOpenSocket");

    IPX_GET_SOCKET_LIFE(socketLife);
    IPX_GET_SOCKET(socketNumber);

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXOpenSocket,
                IPXDBG_LEVEL_INFO,
                "VwIPXOpenSocket(Life=%02x, Socket=%04x, Owner=%04x)\n",
                socketLife,
                B2LW(socketNumber),
                IPX_SOCKET_OWNER_PARM()
                ));


    status = _VwIPXOpenSocket( &socketNumber,
                               socketLife,
                               IPX_SOCKET_OWNER_PARM() );

    if ( status == IPX_SUCCESS )
        IPX_SET_SOCKET(socketNumber);

    IPX_SET_STATUS(status);
}


VOID
VwIPXRelinquishControl(
    VOID
    )

/*++

Routine Description:

    Just sleep for a nominal amount. Netware seems to be dependent on the
    default setting of the PC clock, so one timer tick (1/18 second) would
    seem to be a good value

    This call is Synchronous

Arguments:

    Inputs
        BX      0Ah

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXRelinquishControl");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXRelinquishControl,
                IPXDBG_LEVEL_INFO,
                "VwIPXRelinquishControl\n"
                ));

    _VwIPXRelinquishControl();

}


VOID
VwIPXScheduleAESEvent(
    VOID
    )

/*++

Routine Description:

    Schedules a an event to occur in some number of ticks. When the tick count
    reaches 0, the ECB InUse field is cleared and any ESR called

    This call is Asynchronous

Arguments:

    Inputs
        BX      07h
        AX      Delay time - number of 1/18 second ticks
        ES:SI   ECB address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    LPXECB pXecb = AES_ECB_PARM();
    WORD ticks = IPX_TICKS_PARM();

    if (pXecb == NULL) {
        return;
    }

    CHECK_INTERRUPTS("VwIPXScheduleAESEvent");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXScheduleAESEvent,
                IPXDBG_LEVEL_INFO,
                "VwIPXScheduleAESEvent(%04x:%04x, %04x)\n",
                getES(),
                getSI(),
                ticks
                ));

    ScheduleEvent(pXecb, ticks);
}


VOID
VwIPXScheduleIPXEvent(
    VOID
    )

/*++

Routine Description:

    Schedules a an event to occur in some number of ticks. When the tick count
    reaches 0, the ECB InUse field is cleared and any ESR called

    This call is Asynchronous

Arguments:

    Inputs
        BX      05h
        AX      Delay time - number of 1/18 second ticks
        ES:SI   ECB address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    LPECB pEcb;
    WORD ticks = IPX_TICKS_PARM();

    CHECK_INTERRUPTS("VwIPXScheduleIPXEvent");

    IPX_GET_IPX_ECB(pEcb);

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXScheduleIPXEvent,
                IPXDBG_LEVEL_INFO,
                "VwIPXScheduleIPXEvent(%04x:%04x, %04x)\n",
                getES(),
                getSI(),
                ticks
                ));

    _VwIPXScheduleIPXEvent( ticks, pEcb, ECB_PARM_ADDRESS() );

}


VOID
VwIPXSendPacket(
    VOID
    )

/*++

Routine Description:

    Sends a packet to the target machine/router. This call can be made on a
    socket that is not open

    The app must have filled in the following IPX_ECB fields:

        EsrAddress
        Socket
        ImmediateAddress
        FragmentCount
        fragment descriptor fields

    and the following IPX_PACKET fields:

        PacketType
        Destination.Net
        Destination.Node
        Destination.Socket

    This call is Asynchronous

Arguments:

    Inputs
        BX      03h
        CX      DOS PDB. This parameter is not part of the IPX API.
                Added because we need to remember which DOS executable owns the
                socket IF WE MUST CREATE A TEMPORTARY SOCKET: we need to clean-up
                short-lived sockets when the executable terminates
        ES:SI   ECB Address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    LPECB pEcb;
    WORD owner;

    CHECK_INTERRUPTS("VwIPXSendPacket");

    IPX_GET_IPX_ECB(pEcb);

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXSendPacket,
                IPXDBG_LEVEL_INFO,
                "VwIPXSendPacket(%04x:%04x), owner = %04x\n",
                getES(),
                getSI(),
                IPX_SOCKET_OWNER_PARM()
                ));

    _VwIPXSendPacket(pEcb,
                     ECB_PARM_ADDRESS(),
                     IPX_SOCKET_OWNER_PARM()
                     );
}


VOID
VwIPXSendWithChecksum(
    VOID
    )

/*++

Routine Description:

    description-of-function.

    This call is Asynchronous

Arguments:

    None.

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXSendWithChecksum");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXSendWithChecksum,
                IPXDBG_LEVEL_INFO,
                "VwIPXSendWithChecksum\n"
                ));
}


VOID
VwIPXSPXDeinit(
    VOID
    )

/*++

Routine Description:

    description-of-function.

    This call is Synchronous

Arguments:

    None.

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXSPXDeinit");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_ANY,
                IPXDBG_LEVEL_INFO,
                "VwIPXSPXDeinit\n"
                ));
}


VOID
VwIPXVerifyChecksum(
    VOID
    )

/*++

Routine Description:

    description-of-function.

    This call is Synchronous

Arguments:

    None.

Return Value:

    None.

--*/

{
    CHECK_INTERRUPTS("VwIPXVerifyChecksum");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_IPXVerifyChecksum,
                IPXDBG_LEVEL_INFO,
                "VwIPXVerifyChecksum\n"
                ));
}


VOID
VwSPXAbortConnection(
    VOID
    )

/*++

Routine Description:

    Aborts this end of a connection

    This call is Asynchronous

Arguments:

    Inputs
        BX      14h
        DX      Connection ID

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    WORD connectionId = SPX_CONNECTION_PARM();

    CHECK_INTERRUPTS("VwSPXAbortConnection");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXAbortConnection,
                IPXDBG_LEVEL_INFO,
                "VwSPXAbortConnection(%04x)\n",
                connectionId
                ));

    _VwSPXAbortConnection(connectionId);
}


VOID
VwSPXEstablishConnection(
    VOID
    )

/*++

Routine Description:

    Creates a connection with a remote SPX socket. The remote end can be on
    this machine (i.e. same app in DOS world)

    This call is Asynchronous

Arguments:

    Inputs
        BX      11h
        AL      Retry count
        AH      WatchDog flag
        ES:SI   ECB Address

    Outputs
        AL      Completion code:
                    00h Attempting to talk to remote
                    EFh Local connection table full
                    FDh Fragment count not 1; buffer size not 42
                    FFh Send socket not open
        DX      Connection ID

Return Value:

    None.

--*/

{
    WORD status;
    BYTE retryCount = SPX_RETRY_COUNT_PARM();
    BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM();
    WORD connectionId = 0;
    LPECB pEcb;

    CHECK_INTERRUPTS("VwSPXEstablishConnection");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXEstablishConnection,
                IPXDBG_LEVEL_INFO,
                "VwSPXEstablishConnection(%02x, %02x, %04x:%04x)\n",
                retryCount,
                watchDogFlag,
                ECB_PARM_SEGMENT(),
                ECB_PARM_OFFSET()
                ));

    IPX_GET_IPX_ECB( pEcb );

    IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE));

    status = _VwSPXEstablishConnection( retryCount,
                                        watchDogFlag,
                                        &connectionId,
                                        pEcb,
                                        ECB_PARM_ADDRESS() );


    SPX_SET_CONNECTION_ID( connectionId );
    SPX_SET_STATUS( status );
}


VOID
VwSPXGetConnectionStatus(
    VOID
    )

/*++

Routine Description:

    Returns buffer crammed full of useful statistics or something (hu hu huh)

    This call is Synchronous

Arguments:

    Inputs
        BX      15h
        DX      Connection ID
        ES:SI   Buffer address

    Outputs
        AL      Completion code:
                    00h Connection is active
                    EEh No such connection

        on output, buffer in ES:SI contains:

            BYTE    ConnectionStatus
            BYTE    WatchDogActive
            WORD    LocalConnectionID
            WORD    RemoteConnectionID
            WORD    SequenceNumber
            WORD    LocalAckNumber
            WORD    LocalAllocationNumber
            WORD    RemoteAckNumber
            WORD    RemoteAllocationNumber
            WORD    LocalSocket
            BYTE    ImmediateAddress[6]
            BYTE    RemoteNetwork[4]
            WORD    RetransmissionCount
            WORD    RetransmittedPackets
            WORD    SuppressedPackets

Return Value:

    None.

--*/

{
    WORD status;
    WORD connectionId = SPX_CONNECTION_PARM();
    LPSPX_CONNECTION_STATS pStats = (LPSPX_CONNECTION_STATS)SPX_BUFFER_PARM(sizeof(*pStats));

    CHECK_INTERRUPTS("VwSPXGetConnectionStatus");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXGetConnectionStatus,
                IPXDBG_LEVEL_INFO,
                "VwSPXGetConnectionStatus: connectionId=%04x\n",
                connectionId
                ));

    status = _VwSPXGetConnectionStatus( connectionId,
                                        pStats );


    SPX_SET_STATUS(status);
}


VOID
VwSPXInitialize(
    VOID
    )

/*++

Routine Description:

    Informs the app that SPX is present on this station

    This call is Synchronous

Arguments:

    Inputs
        BX      10h
        AL      00h

    Outputs
        AL      Installation flag:
                    00h Not installed
                    FFh Installed
        BH      SPX Major revision number
        BL      SPX Minor revision number
        CX      Maximum SPX connections supported
                    normally from SHELL.CFG
        DX      Available SPX connections

Return Value:

    None.

--*/

{
    WORD status;
    BYTE majorRevisionNumber;
    BYTE minorRevisionNumber;
    WORD maxConnections;
    WORD availableConnections;

    CHECK_INTERRUPTS("VwSPXInitialize");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXInitialize,
                IPXDBG_LEVEL_INFO,
                "VwSPXInitialize\n"
                ));


    status = _VwSPXInitialize( &majorRevisionNumber,
                               &minorRevisionNumber,
                               &maxConnections,
                               &availableConnections );


    setBH( majorRevisionNumber );
    setBL( minorRevisionNumber );
    setCX( maxConnections );
    setDX( availableConnections );
    SPX_SET_STATUS(status);
}


VOID
VwSPXListenForConnection(
    VOID
    )

/*++

Routine Description:

    Listens for an incoming connection request

    This call is Asynchronous

Arguments:

    Inputs
        BX      12h
        AL      Retry count
        AH      SPX WatchDog flag
        ES:SI   ECB Address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    BYTE retryCount = SPX_RETRY_COUNT_PARM();
    BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM();
    LPECB pEcb;

    CHECK_INTERRUPTS("VwSPXListenForConnection");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXListenForConnection,
                IPXDBG_LEVEL_INFO,
                "VwSPXListenForConnection(%02x, %02x, %04x:%04x)\n",
                retryCount,
                watchDogFlag,
                ECB_PARM_SEGMENT(),
                ECB_PARM_OFFSET()
                ));

    IPX_GET_IPX_ECB( pEcb );

    IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE));

    _VwSPXListenForConnection( retryCount,
                               watchDogFlag,
                               pEcb,
                               ECB_PARM_ADDRESS() );
}


VOID
VwSPXListenForSequencedPacket(
    VOID
    )

/*++

Routine Description:

    Attempts to receive an SPX packet. This call is made against the top-level
    socket (the socket in SPX-speak, not the connection). We can receive a
    packet from any connection assigned to this socket. In this function, we
    just queue the ECB (since there is no return status, we expect that the
    app has supplied an ESR) and let AES handle it

    This call is Asynchronous

Arguments:

    Inputs
        BX      17h
        ES:SI   ECB Address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    LPECB pEcb;

    CHECK_INTERRUPTS("VwSPXListenForSequencedPacket");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXListenForSequencedPacket,
                IPXDBG_LEVEL_INFO,
                "VwSPXListenForSequencedPacket(%04x:%04x)\n",
                ECB_PARM_SEGMENT(),
                ECB_PARM_OFFSET()
                ));

    IPX_GET_IPX_ECB( pEcb );

    IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE));

    _VwSPXListenForSequencedPacket( pEcb,
                                    ECB_PARM_ADDRESS());

}


VOID
VwSPXSendSequencedPacket(
    VOID
    )

/*++

Routine Description:

    Sends a packet on an SPX connection

    This call is Asynchronous

Arguments:

    Inputs
        BX      16h
        DX      Connection ID
        ES:SI   ECB address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    WORD connectionId = SPX_CONNECTION_PARM();
    LPECB pEcb;

    CHECK_INTERRUPTS("VwSPXSendSequencedPacket""VwSPXSendSequencedPacket");

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXSendSequencedPacket,
                IPXDBG_LEVEL_INFO,
                "VwSPXSendSequencedPacket(%04x, %04x:%04x)\n",
                connectionId,
                getES(),
                getSI()
                ));

    IPX_GET_IPX_ECB( pEcb );

    IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE));

    _VwSPXSendSequencedPacket( connectionId,
                               pEcb,
                               ECB_PARM_ADDRESS() );

}


VOID
VwSPXTerminateConnection(
    VOID
    )

/*++

Routine Description:

    Terminates a connection

    This call is Asynchronous

Arguments:

    Inputs
        BX      13h
        DX      Connection ID
        ES:SI   ECB Address

    Outputs
        Nothing

Return Value:

    None.

--*/

{
    WORD connectionId = SPX_CONNECTION_PARM();
    LPECB pEcb;

    CHECK_INTERRUPTS("VwSPXTerminateConnection");

    IPX_GET_IPX_ECB( pEcb );

    IPXDBGPRINT((__FILE__, __LINE__,
                FUNCTION_SPXTerminateConnection,
                IPXDBG_LEVEL_INFO,
                "VwSPXTerminateConnection(%04x, %04x:%04x)\n",
                connectionId,
                ECB_PARM_SEGMENT(),
                ECB_PARM_OFFSET()
                ));

    _VwSPXTerminateConnection(connectionId, pEcb, ECB_PARM_ADDRESS());
}