#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_T123PSTN);

/*    X224.cpp
 *
 *    Copyright (c) 1994 by DataBeam Corporation, Lexington, KY
 *
 *    Abstract:
 *
 *    Private Instance Variables:
 *        Default_PDU_Size             -    Default PDU size, if no arb. is done
 *        Data_Request_Memory_Manager -    Memory manager
 *        Lower_Layer_Prepend            -    Number of bytes prepended to packet by
 *                                        lower layer
 *        Lower_Layer_Append            -    Number of bytes appended to packet byt
 *                                        lower layer
 *        Shutdown_Receiver            -    TRUE if we aren't to receive any more
 *                                        packets from the lower layer
 *        Shutdown_Transmitter        -    TRUE if we aren't to transmit any more
 *                                        packets
 *        Data_Request_Queue            -    Queue that keeps the pending user data
 *                                        requests
 *        Data_Indication_Queue        -    Queue that holds the pending user data
 *                                        indications
 *        Data_Indication_Memory_Pool    -    List that holds available data
 *                                        indication buffers.
 *
 *        Active_Data_Indication        -    Address of packet structure.  This
 *                                        packet holds the current data indication
 *                                        that we are reassembling
 *        m_pT123                -    Address of owner object.  Used for
 *                                        callbacks
 *        m_pQ922                    -    Address of lower layer.
 *        m_nMsgBase                -    Message base to be used for owner
 *                                        callbacks
 *        Maximum_PDU_Size            -    Max. PDU size
 *        Arbitrated_PDU_Size            -    Max. arbitrated packet size.
 *        Identifier                    -    Identifier passed to lower layer to
 *                                        register ourselves.
 *        Data_Indication_Queue_Size    -    Number of data indications we will
 *                                        buffer
 *        Data_Indication_Reassembly_Active    -    Flag set if we are in the middle
 *                                        of a packet reassembly.
 *        State                        -    Holds the current state of the object
 *        Packet_Pending                -    Tells which packet will be sent next.
 *        Reject_Cause                -    The reason why the error packet was sent
 *        Packet_Size_Respond            -    Set to TRUE if we are to send a TPDU
 *                                        size element in the CC packet
 *        Error_Buffer                -    Address of error buffer.
 *        Error_Buffer_Length            -    Length of error buffer.
 *
 *        m_nLocalLogicalHandle    -    Local transport connection id.
 *        m_nRemoteLogicalHandle    -    Remote transport connection id.
 *         User_Data_Pending            -    Set to the size of the last packet that
 *                                        the user attempted to pass to us, that
 *                                        we couldn't accept because we ran out
 *                                        of memory.
 *
 *    Caveats:
 *        None.
 *
 *    Authors:
 *        James W. Lawwill
 */

#include <windowsx.h>
#include "x224.h"



/*
 *    CLayerX224::CLayerX224 (
 *                PTransportResources    transport_resources,
 *                IObject *                owner_object,
 *                IProtocolLayer *        lower_layer,
 *                USHORT                message_base,
 *                USHORT                logical_handle,
 *                USHORT                identifier,
 *                USHORT                data_indication_queue_size,
 *                USHORT                default_PDU_size,
 *                PMemoryManager        dr_memory_manager,
 *                BOOL *            initialization_success)
 *
 *    Public
 *
 *    Functional Description:
 *        This is the Transport constructor.  This routine initializes all
 *        variables and allocates the buffers needed to operate.
 */
CLayerX224::CLayerX224
(
    T123               *owner_object,
    CLayerQ922         *pQ922, // lower layer
    USHORT              message_base,
    LogicalHandle       logical_handle,
    ULONG               identifier,
    USHORT              data_indication_queue_size,
    USHORT              default_PDU_size,
    PMemoryManager      dr_memory_manager,
    BOOL               *initialization_success
)
:
    m_pT123(owner_object),
    m_nMsgBase(message_base),
    m_pQ922(pQ922)
{
    TRACE_OUT(("CLayerX224::CLayerX224"));

    ProtocolLayerError    error;

    m_nLocalLogicalHandle = logical_handle;
    Identifier = identifier;
    Default_PDU_Size = default_PDU_size;
    Data_Request_Memory_Manager = dr_memory_manager;
    *initialization_success = TRUE;

    Shutdown_Receiver = FALSE;
    Shutdown_Transmitter = FALSE;
    Reject_Cause = 0;


     /*
     **    Find the maximum packet size
     */
    m_pQ922->GetParameters(
                    &Maximum_PDU_Size,
                    &Lower_Layer_Prepend,
                    &Lower_Layer_Append);

    Arbitrated_PDU_Size = Default_PDU_Size;

     /*
     **    Figure out what our largest PDU could be.  We will use this value to
     **    arbitrate the maximum PDU size.
     */
    Maximum_PDU_Size = (USHORT)GetMaxTPDUSize (Maximum_PDU_Size);

     /*
     **    Register with the lower layer, so we can send and receive packets.
     */
    error = m_pQ922->RegisterHigherLayer(
                            identifier,
                            Data_Request_Memory_Manager,
                            (IProtocolLayer *) this);

    if (error != PROTOCOL_LAYER_NO_ERROR)
    {
        ERROR_OUT(("X224: constructor:  Error registering with lower layer"));
        *initialization_success = FALSE;
    }

     /*
     **    Prepare for buffer allocation
     */
    Data_Indication_Queue_Size = data_indication_queue_size;
    Error_Buffer = NULL;

     /*
     **    Set member variables appropriately
     */
    Active_Data_Indication = NULL;
    Data_Indication_Reassembly_Active = FALSE;
    Packet_Pending = TRANSPORT_NO_PACKET;
    User_Data_Pending = 0;

    m_nRemoteLogicalHandle = 0;
    Packet_Size_Respond = FALSE;

    if (*initialization_success == FALSE)
        State = FAILED_TO_INITIALIZE;
    else
        State = NO_CONNECTION;
}


/*
 *    CLayerX224::~CLayerX224 (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This is the Transport destructor.  This routine cleans up everything.
 */
CLayerX224::~CLayerX224(void)
{
    TRACE_OUT(("CLayerX224::~CLayerX224"));

    PMemory     lpMemory;
    PTMemory    lptMem;
     /*
     **    Notify the lower layer that we are terminating
     */
    m_pQ922->RemoveHigherLayer(Identifier);

     /*
     **    Go thru the data request queue and delete the structures held in the
     **    queue.
     */
    Data_Request_Queue.reset();
    while (Data_Request_Queue.iterate ((PDWORD_PTR) &lpMemory))
    {
        Data_Request_Memory_Manager-> FreeMemory (lpMemory);
    }

     /*
     **    Go thru the data indication queue and delete the structures held in the
     **    queue.
     */
    Data_Indication_Queue.reset();
    while (Data_Indication_Queue.iterate ((PDWORD_PTR) &lptMem))
        delete lptMem;

     /*
     **    Go thru the data request free structure pool and delete the structures
     **    held in the    pool.
     */
    Data_Indication_Memory_Pool.reset();
    while (Data_Indication_Memory_Pool.iterate ((PDWORD_PTR) &lptMem))
        delete lptMem;

     /*
     **    If there is a data indication active, delete that structure.
     */
    delete Active_Data_Indication;

     /*
     **    If the error buffer holds a packet, delete it
     */
    delete [] Error_Buffer;

    return;
}


/*
 *    TransportError    CLayerX224::ConnectRequest (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function initiates a connect request.
 */
TransportError    CLayerX224::ConnectRequest (void)
{
    TRACE_OUT(("CLayerX224::ConnectRequest"));

    if (State != NO_CONNECTION)
    {
        ERROR_OUT(("Transport: Illegal ConnectRequest packet"));
        return (TRANSPORT_CONNECT_REQUEST_FAILED);
    }

    Packet_Pending = CONNECTION_REQUEST_PACKET;
    return (TRANSPORT_NO_ERROR);
}


/*
 *    TransportError    CLayerX224::ShutdownReceiver (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function stops us from receiving any more packets from the lower
 *        layer
 */
void    CLayerX224::ShutdownReceiver (void)
{
    TRACE_OUT(("CLayerX224::ShutdownReceiver"));

    Shutdown_Receiver = TRUE;
}


/*
 *    TransportError    CLayerX224::EnableReceiver (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function permits us to send packets to the user application.
 */
void    CLayerX224::EnableReceiver (void)
{
    TRACE_OUT(("CLayerX224::EnableReceiver"));

    Shutdown_Receiver = FALSE;
}


/*
 *    TransportError    CLayerX224::ShutdownTransmitter (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function keeps us from transmitting any more packets
 */
void    CLayerX224::ShutdownTransmitter (void)
{
    TRACE_OUT(("CLayerX224::ShutdownTransmitter"));

    Shutdown_Transmitter = TRUE;
}


/*
 *    TransportError    CLayerX224::PurgeRequest (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function removes all packets from out output queue that aren't
 *        active
 */
void    CLayerX224::PurgeRequest (void)
{
    TRACE_OUT(("CLayerX224::PurgeRequest"));

    DWORD    entries;
    DWORD    keep_counter = 0;
    PMemory    memory;
    LPBYTE    packet_address;
    DWORD    i;

    if (Data_Request_Queue.isEmpty() == FALSE)
    {
        entries = Data_Request_Queue.entries ();

         /*
         **    Go thru packets looking for the last PDU in the SDU
         */
        Data_Request_Queue.reset();
        while (Data_Request_Queue.iterate ((PDWORD_PTR) &memory))
        {
            keep_counter++;
            packet_address = memory -> GetPointer ();
            if (*(packet_address + 2) == EOT_BIT)
                break;
        }

        TRACE_OUT(("PurgeRequest: Removing %d packets", entries-keep_counter));
        for (i=keep_counter; i<entries; i++)
        {
            Data_Request_Memory_Manager->FreeMemory ((PMemory) Data_Request_Queue.removeLast ());
        }
    }
    return;
}


/*
 *    TransportError    CLayerX224::ConnectResponse (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function initiates a connect response.
 */
TransportError    CLayerX224::ConnectResponse (void)
{
    TRACE_OUT(("CLayerX224::ConnectResponse"));

    if (State != RECEIVED_CONNECT_REQUEST_PACKET)
    {
        ERROR_OUT(("Transport: Illegal ConnectResponse packet"));
        return (TRANSPORT_CONNECT_RESPONSE_FAILED);
    }

    Packet_Pending = CONNECTION_CONFIRM_PACKET;
    return (TRANSPORT_NO_ERROR);
}


/*
 *    TransportError    CLayerX224::DisconnectRequest (void)
 *
 *    Public
 *
 *    Functional Description:
 *        This function initiates a disconnect request.
 */
TransportError    CLayerX224::DisconnectRequest (void)
{
    TRACE_OUT(("CLayerX224::DisconnectRequest"));

    if (State == SENT_CONNECT_REQUEST_PACKET)
    {
         /*
         **    The connection is being rejected, send out the DISCONNECT
         **    packet and wait for termination
         */
        Packet_Pending = DISCONNECT_REQUEST_PACKET;
    }
    else
    {
         /*
         **    Normal disconnects don't send any notification to the remote site.
         **    It depends on the Network layer to terminate the link.
         */
        m_pQ922->RemoveHigherLayer(Identifier);

        m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                               (void *) m_nLocalLogicalHandle);
    }

    return (TRANSPORT_NO_ERROR);
}


/*
 *    TransportError    CLayerX224::DataIndication (
 *                                LPBYTE        packet_address,
 *                                ULONG        buffer_size,
 *                                PULong        packet_length)
 *
 *    Public
 *
 *    Functional Description:
 *        This function is called by the lower layer when it has a packet for us.
 */
ProtocolLayerError    CLayerX224::DataIndication (
                                LPBYTE        packet_address,
                                ULONG        packet_length,
                                PULong        bytes_accepted)
{
    TRACE_OUT(("CLayerX224::DataIndication"));

    ULONG            remainder_length;
    USHORT            class_request;
    USHORT            packet_type;
    USHORT            length;
    USHORT            destination_reference;
    LegacyTransportData    transport_data;
    BOOL            packet_accepted;
    ULONG            user_accepted;
    UChar            eot;
    PTMemory        packet;
    TMemoryError    packet_error;
    LPBYTE            temp_address;
    BOOL            use_default_PDU_size;


    *bytes_accepted = 0;
    packet_accepted = FALSE;

     /*
     ** If the receiver is shutdown, don't accept any data
     */
    if (Shutdown_Receiver)
        return (PROTOCOL_LAYER_NO_ERROR);

     /*
     **    The packet must be at least 2 bytes long
     */
    if (packet_length < 2)
    {
        ERROR_OUT(("X224: DataIndication:  Invalid packet received from lower layer: length = %d", packet_length));
        return (PROTOCOL_LAYER_NO_ERROR);
    }

    remainder_length = packet_length;
    temp_address = packet_address;
    packet_address++;
    packet_type = *(packet_address++) & TPDU_CODE_MASK;
    remainder_length -= 2;

    switch (packet_type)
    {
        case CONNECTION_REQUEST_PACKET:
            packet_accepted = TRUE;

             /*
             **    There should be at least 5 bytes remaining in this packet
             */
            if (remainder_length < 5)
            {
                ERROR_OUT(("X224: DataIndication: CR: Invalid packet received from lower layer: length = %d", packet_length));
                break;
            }

             /*
             **    Increment the packet address by 2 to get past the DST_REF
             */
            packet_address += 2;
            m_nRemoteLogicalHandle = *(packet_address++);
            m_nRemoteLogicalHandle <<= 8;
            m_nRemoteLogicalHandle |= *(packet_address++);
            remainder_length -= 4;

             /*
             **    Look at the class request to make sure it is 0
             */
            class_request = *(packet_address++) >> 4;
            remainder_length -= 1;
            if (class_request != 0)
            {
                ERROR_OUT(("X224: DataIndication: CR packet: Illegal class request"));
                ErrorPacket (
                    temp_address,
                    (USHORT) (packet_length - remainder_length));
                break;
            }
            use_default_PDU_size = TRUE;

            while (remainder_length != 0)
            {
                switch (*(packet_address++))
                {
                    case TPDU_SIZE:
                        length = *(packet_address++);
                        remainder_length -= 1;
                        if (length != 1)
                        {
                            TRACE_OUT(("X224: DataIndication: CR packet: Illegal TPDU_Size length"));

                            ErrorPacket (
                                temp_address,
                                (USHORT) (packet_length - remainder_length));
                            break;
                        }

                         /*
                         **    Figure out the actual PDU size
                         */
                        Arbitrated_PDU_Size = (1 << *(packet_address++));
                        remainder_length -= 1;
                        TRACE_OUT(("X224: CR_Packet: Packet size = %d", Arbitrated_PDU_Size));
                        if (Arbitrated_PDU_Size > Maximum_PDU_Size)
                        {
                            Packet_Size_Respond = TRUE;
                            Arbitrated_PDU_Size = Maximum_PDU_Size;
                        }
                        if (AllocateBuffers() == FALSE)
                        {
                            m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                                                   (void *) m_nLocalLogicalHandle);
                        }
                        use_default_PDU_size = FALSE;
                        break;

                    default:
                        ERROR_OUT(("X224: DataIndication: CR packet Unsupported parameter 0x%x", *(packet_address - 1)));
                        length = *(packet_address++);
                        remainder_length--;

                        packet_address += length;
                        remainder_length -= length;
                        break;
                }
                remainder_length--;
            }

             /*
             **    If the initiator wants to use the default PDU size, we need to
             **    check the default size with the Max. size to make sure it is
             **    valid for us.
             */
            if (use_default_PDU_size)
            {
                if (Default_PDU_Size > Maximum_PDU_Size)
                {
                    Packet_Size_Respond = TRUE;
                    Arbitrated_PDU_Size = Maximum_PDU_Size;
                }
                if (AllocateBuffers() == FALSE)
                {
                    m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                                           (void *) m_nLocalLogicalHandle);
                }
            }

            State = RECEIVED_CONNECT_REQUEST_PACKET;

             /*
             **    Notify the owner that the remote site wants to start a
             **    connection
             */
            m_pT123->OwnerCallback(m_nMsgBase + TPRT_CONNECT_INDICATION,
                                   (void *) m_nLocalLogicalHandle);
            TRACE_OUT(("X224: DataInd: ConnectRequest: max pkt = %d", Arbitrated_PDU_Size));
            break;

        case CONNECTION_CONFIRM_PACKET:
            packet_accepted = TRUE;

             /*
             **    There should be at least 5 bytes remaining in this packet
             */
            if (remainder_length < 5)
            {
                ERROR_OUT(("X224: DataIndication: CC: Invalid packet received from lower layer: length = %d",
                        packet_length));
                break;
            }

            destination_reference = *(packet_address++);
            destination_reference <<= 8;
            destination_reference |= *(packet_address++);
            remainder_length -= 2;
            if (destination_reference != m_nLocalLogicalHandle)
            {
                ERROR_OUT(("X224: DataIndication: CC packet: DST-REF incorrect"));
                ErrorPacket (
                    temp_address,
                    (USHORT) (packet_length - remainder_length));
                break;
            }

            m_nRemoteLogicalHandle = *(packet_address++);
            m_nRemoteLogicalHandle <<= 8;
            m_nRemoteLogicalHandle |= *(packet_address++);

            class_request = *(packet_address++) >> 4;
            remainder_length -= 3;
            if (class_request != 0)
            {
                ERROR_OUT(("X224: DataIndication: CR packet: Illegal class request"));
                ErrorPacket (
                    temp_address,
                    (USHORT) (packet_length - remainder_length));
                break;
            }
            use_default_PDU_size = TRUE;

            while (remainder_length != 0)
            {
                switch (*(packet_address++))
                {
                    case TPDU_SIZE:
                        length = *(packet_address++);
                        remainder_length -= 1;
                        if (length != 1)
                        {
                            ERROR_OUT(("X224: DataIndication: CR packet: Illegal TPDU_Size length"));

                            ErrorPacket (
                                temp_address,
                                (USHORT) (packet_length - remainder_length));
                        }
                        Arbitrated_PDU_Size = (1 << *(packet_address++));
                        remainder_length -= 1;
                        TRACE_OUT(("X224: CC_Packet: Packet size = %d", Arbitrated_PDU_Size));
                        use_default_PDU_size = FALSE;

                         /*
                         **    Allocate the buffers
                         */
                        if (AllocateBuffers() == FALSE)
                        {
                            m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                                                   (void *) m_nLocalLogicalHandle);
                        }
                        break;

                    default:
                        ERROR_OUT(("X224: DataIndication: CC packet Unsupported parameter"));
                        length = *(packet_address++);
                        remainder_length--;

                        packet_address += length;
                        remainder_length -= length;
                        break;
                }
                remainder_length--;
            }
            if (use_default_PDU_size)
            {
                if (AllocateBuffers () == FALSE)
                {
                    m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                                           (void *) m_nLocalLogicalHandle);
                }
            }

            State = CONNECTION_ACTIVE;

             /*
             **    Notify the owner that the connect request has been confirmed
             */
            m_pT123->OwnerCallback(m_nMsgBase + TPRT_CONNECT_CONFIRM,
                                   (void *) m_nLocalLogicalHandle);
            TRACE_OUT(("X224: DataInd: ConnectConfirm max pkt = %d", Arbitrated_PDU_Size));
            break;

        case DISCONNECT_REQUEST_PACKET:
            TRACE_OUT(("X224: DataIndication: Disconnect req. received"));

             /*
             **    Notify the owner that a disconnect has been requested.  This
             **    message is only valid during establishment of the connection.
             */
            m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                                   (void *) m_nLocalLogicalHandle);
            packet_accepted = TRUE;
            break;

        case ERROR_PACKET:
            TRACE_OUT(("X224: DataIndication: ERROR REQUEST received"));

             /*
             **    Notify the owner that the remote site has detected an error in
             **    one of our packets.
             */
            m_pT123->OwnerCallback(m_nMsgBase + TPRT_DISCONNECT_INDICATION,
                                   (void *) m_nLocalLogicalHandle);
            packet_accepted = TRUE;
            break;

        case DATA_PACKET:
            if ((Data_Indication_Reassembly_Active == FALSE) &&
                Data_Indication_Memory_Pool.isEmpty())
            {
                break;
            }

            packet_accepted = TRUE;

             /*
             **    There should be at least 1 bytes remaining in this packet
             */
            if (remainder_length < 1)
            {
                ERROR_OUT(("X224: DataIndication: DATA: Invalid packet "
                    "received from lower layer: length = %d", packet_length));
                break;
            }

            eot = *(packet_address++);
            remainder_length--;

             /*
             **    The EOT_BIT is set if this is the last TPDU of the TSDU
             */
            if ((eot & EOT_BIT) == EOT_BIT)
            {
                if (Data_Indication_Reassembly_Active == FALSE)
                {
                     /*
                     **    If the remote site has passed us an empty packet,
                     **    just return
                     */
                    if (remainder_length == 0)
                        break;

                     /*
                     **    If this is a single packet and there aren't any
                     **    other packets preceeding it, try to send it to the
                     **    user without copying it into our own buffers
                     */
                    if (Data_Indication_Queue.isEmpty())
                    {
                        transport_data.logical_handle = m_nLocalLogicalHandle;
                        transport_data.pbData = packet_address;
                        transport_data.cbDataSize = remainder_length;

                         /*
                         **    Issue the user callback to give the user the data.
                         */
                        user_accepted = ::NotifyT120(TRANSPORT_DATA_INDICATION, &transport_data);

                         /*
                         **    If the user appliction does NOT accept the packet
                         **    shutdown the receiver and wait for the user
                         **    to re-enable it.
                         */
                        if (user_accepted == TRANSPORT_NO_ERROR)
                            break;
                        else
                            Shutdown_Receiver = TRUE;
                    }

                     /*
                     **    Put the packet into the DataIndication queue
                     */
                    packet = (PTMemory) Data_Indication_Memory_Pool.get ();
                    packet_error = packet->Append (packet_address, remainder_length);
                    switch (packet_error)
                    {
                        case TMEMORY_NO_ERROR:
                            Data_Indication_Queue.append ((DWORD_PTR) packet);
                            break;

                        case TMEMORY_NONFATAL_ERROR:
                        case TMEMORY_FATAL_ERROR:
                            packet_accepted = FALSE;
                            break;
                    }
                }
                else
                {
                     /*
                     **    Add this PDU to the currently active SDU
                     */
                    packet_error = Active_Data_Indication -> Append (
                                    packet_address,
                                    remainder_length);

                    switch (packet_error)
                    {
                        case TMEMORY_NO_ERROR:
                            Data_Indication_Reassembly_Active = FALSE;
                            Data_Indication_Queue.append ((DWORD_PTR) Active_Data_Indication);
                            Active_Data_Indication = NULL;

                             /*
                             **    Call PollReceiver (), it will attempt to pass
                             **    the packet on up to the user.
                             */
                            PollReceiver();
                            break;

                        case TMEMORY_NONFATAL_ERROR:
                        case TMEMORY_FATAL_ERROR:
                            packet_accepted = FALSE;
                            break;
                    }
                }
            }
            else
            {
                 /*
                 **    If the remote site is passing us a zero-length packet,
                 **    just return
                 */
                if (remainder_length == 0)
                    break;

                 /*
                 **    This is NOT the last packet in the incoming SDU, copy it
                 **    into the data indication buffer and wait for the next packet
                 */
                if (Data_Indication_Reassembly_Active == FALSE)
                {
                    Data_Indication_Reassembly_Active = TRUE;
                    Active_Data_Indication = (PTMemory) Data_Indication_Memory_Pool.get ();
                }

                packet_error = Active_Data_Indication -> Append (
                                                            packet_address,
                                                            remainder_length);
                switch (packet_error)
                {
                    case TMEMORY_NO_ERROR:
                        break;

                    case TMEMORY_NONFATAL_ERROR:
                    case TMEMORY_FATAL_ERROR:
                        packet_accepted = FALSE;
                        break;
                }
            }
            break;

        default:
            ERROR_OUT(("X224: Illegal packet"));
            break;
    }

    if (packet_accepted)
        *bytes_accepted = packet_length;

    return (PROTOCOL_LAYER_NO_ERROR);
}


/*
 *    ProtocolLayerError    CLayerX224::PollTransmitter (
 *                                    ULONG,
 *                                    USHORT,
 *                                    USHORT *    pending_data,
 *                                    USHORT *)
 *
 *    Public
 *
 *    Functional Description:
 *        This function is called periodically to give X224 a chance to transmit
 *        data.
 */
ProtocolLayerError    CLayerX224::PollTransmitter (
                                ULONG_PTR,
                                USHORT,
                                USHORT *    pending_data,
                                USHORT *)
{
    // TRACE_OUT(("CLayerX224::PollTransmitter"));

    LPBYTE        packet_address;
    ULONG        bytes_accepted;
    USHORT        counter;
    USHORT        packet_size;
    ULONG        total_length;
    USHORT        packet_length;
    PMemory        memory;
    BOOL        continue_loop = TRUE;

    while (continue_loop)
    {
        switch (Packet_Pending)
        {
            case CONNECTION_REQUEST_PACKET:
                 /*
                 **    Add up the packet length, don't forget the 1 byte
                 **    for the Length Indicator
                 */
                total_length =
                    CONNECT_REQUEST_HEADER_SIZE +
                    TPDU_ARBITRATION_PACKET_SIZE +
                    1 +
                    Lower_Layer_Prepend +
                    Lower_Layer_Append;

                memory = Data_Request_Memory_Manager -> AllocateMemory (
                                            NULL,
                                            total_length);
                if (memory == NULL)
                {
                    continue_loop = FALSE;
                    break;
                }

                packet_address = memory -> GetPointer ();
                packet_address += Lower_Layer_Prepend;

                *(packet_address++) =
                    CONNECT_REQUEST_HEADER_SIZE +
                    TPDU_ARBITRATION_PACKET_SIZE;
                *(packet_address++) = CONNECTION_REQUEST_PACKET;

                 /*
                 **    The following 2 bytes are the destination reference
                 */
                *(packet_address++) = 0;
                *(packet_address++) = 0;
                *(packet_address++) = (BYTE)(m_nLocalLogicalHandle >> 8);
                *(packet_address++) = (BYTE)(m_nLocalLogicalHandle & 0xff);

                 /*
                 **    The following byte is the Class/Options
                 */
                *(packet_address++) = 0;

                 /*
                 **    Add TPDU arbitration data
                 */
                *(packet_address++) = TPDU_SIZE;
                *(packet_address++) = 1;

                 /*
                 **    Code our maximum PDU size into the X224 scheme
                 */
                Arbitrated_PDU_Size = Maximum_PDU_Size;
                packet_size = Arbitrated_PDU_Size;
                counter = 0;
                while (packet_size > 1)
                {
                    packet_size >>= 1;
                    counter++;
                }
                *(packet_address++) = (unsigned char) counter;


                 /*
                 **    Attempt to send the packet to the lower layer
                 */
                m_pQ922->DataRequest(Identifier, memory, &bytes_accepted);

                 /*
                 **    We assume that the lower layer has a packet input
                 **    interface, if it does not, there has been a major error.
                 */
                if (bytes_accepted == total_length)
                {
                    Packet_Pending = TRANSPORT_NO_PACKET;
                    State = SENT_CONNECT_REQUEST_PACKET;
                }
                else
                    continue_loop = FALSE;

                Data_Request_Memory_Manager -> FreeMemory (memory);
                break;

            case CONNECTION_CONFIRM_PACKET:
                packet_length = CONNECT_CONFIRM_HEADER_SIZE;
                if (Packet_Size_Respond)
                    packet_length += TPDU_ARBITRATION_PACKET_SIZE;

                total_length = packet_length +
                                1 +
                                Lower_Layer_Prepend +
                                Lower_Layer_Append;

                memory = Data_Request_Memory_Manager -> AllocateMemory (
                                            NULL,
                                            total_length);
                if (memory == NULL)
                {
                    continue_loop = FALSE;
                    break;
                }

                packet_address = memory -> GetPointer ();
                packet_address += Lower_Layer_Prepend;

                 /*
                 **    Build the packet
                 */
                *(packet_address++) = (UChar) packet_length;
                *(packet_address++) = CONNECTION_CONFIRM_PACKET;
                *(packet_address++) = (BYTE)(m_nRemoteLogicalHandle >> 8);
                *(packet_address++) = (BYTE)(m_nRemoteLogicalHandle & 0xff);
                *(packet_address++) = (BYTE)(m_nLocalLogicalHandle >> 8);
                *(packet_address++) = (BYTE)(m_nLocalLogicalHandle & 0xff);

                 /*
                 **    Set the Class/Options to 0
                 */
                *(packet_address++) = 0;

                 /*
                 **    Packet_Size_Respond is TRUE if we are suppose to respond
                 **    to the TPDU element in the Connect Request packet
                 */
                if (Packet_Size_Respond)
                {
                     /*
                     **    Add TPDU arbitration data
                     */
                    *(packet_address++) = TPDU_SIZE;
                    *(packet_address++) = 1;
                    packet_size = Arbitrated_PDU_Size;
                    counter = 0;
                    while (packet_size > 1)
                    {
                        packet_size >>= 1;
                        counter++;
                    }
                    *(packet_address++) = (unsigned char) counter;
                }

                 /*
                 **    Attempt to send the packet to the lower layer
                 */
                m_pQ922->DataRequest(Identifier, memory, &bytes_accepted);

                if (bytes_accepted == total_length)
                {
                    Packet_Pending = TRANSPORT_NO_PACKET;
                    State = CONNECTION_ACTIVE;
                }
                else
                    continue_loop = FALSE;
                Data_Request_Memory_Manager -> FreeMemory (memory);
                break;

            case DISCONNECT_REQUEST_PACKET:
                 /*
                 **    Add 1 to the length for the Length Indicator
                 */
                total_length = DISCONNECT_REQUEST_HEADER_SIZE +
                                1 +
                                Lower_Layer_Prepend +
                                Lower_Layer_Append;

                memory = Data_Request_Memory_Manager -> AllocateMemory (
                                            NULL,
                                            total_length);
                if (memory == NULL)
                {
                    continue_loop = FALSE;
                    break;
                }

                packet_address = memory -> GetPointer ();
                packet_address += Lower_Layer_Prepend;

                TRACE_OUT(("X224: Sending Disconnect Request Packet"));
                *(packet_address++) = DISCONNECT_REQUEST_HEADER_SIZE;
                *(packet_address++) = DISCONNECT_REQUEST_PACKET;
                *(packet_address++) = (BYTE)(m_nRemoteLogicalHandle >> 8);
                *(packet_address++) = (BYTE)(m_nRemoteLogicalHandle & 0xff);

                 /*
                 **    Set the source reference to 0,  this packet will only
                 **    be sent as a refusal to a Connect Request, therefore
                 **    this value should be 0
                 */
                *(packet_address++) = 0;
                *(packet_address++) = 0;
                *(packet_address++) = DISCONNECT_REASON_NOT_SPECIFIED;

                 /*
                 **    Attempt to send packet to lower layer
                 */
                m_pQ922->DataRequest(Identifier, memory, &bytes_accepted);

                if (bytes_accepted == total_length)
                {
                    Packet_Pending = TRANSPORT_NO_PACKET;
                    State = SENT_DISCONNECT_REQUEST_PACKET;
                }
                continue_loop = FALSE;
                Data_Request_Memory_Manager -> FreeMemory (memory);
                break;

            case ERROR_PACKET:
                TRACE_OUT(("X224: Sending Error Packet"));
                total_length = ERROR_HEADER_SIZE +
                                Error_Buffer_Length +
                                1 +
                                2 +
                                Lower_Layer_Prepend +
                                Lower_Layer_Append;

                memory = Data_Request_Memory_Manager -> AllocateMemory (
                                            NULL,
                                            total_length);
                if (memory == NULL)
                {
                    continue_loop = FALSE;
                    break;
                }

                packet_address = memory -> GetPointer ();
                packet_address += Lower_Layer_Prepend;


                *(packet_address++) =
                    ERROR_HEADER_SIZE + Error_Buffer_Length;
                *(packet_address++) = ERROR_PACKET;
                *(packet_address++) = (BYTE)(m_nRemoteLogicalHandle >> 8);
                *(packet_address++) = (BYTE)(m_nRemoteLogicalHandle & 0xff);
                *(packet_address++) = Reject_Cause;

                *(packet_address++) = INVALID_TPDU;
                *(packet_address++) = (UChar) Error_Buffer_Length;
                memcpy (packet_address, Error_Buffer, Error_Buffer_Length);

                 /*
                 **    Attempt to send packet to lower layer
                 */
                m_pQ922->DataRequest(Identifier, memory, &bytes_accepted);

                if (bytes_accepted == total_length)
                {
                    delete [] Error_Buffer;
                    Error_Buffer = NULL;

                    Packet_Pending = TRANSPORT_NO_PACKET;
                    State = SENT_CONNECT_REQUEST_PACKET;
                }
                else
                    continue_loop = FALSE;
                Data_Request_Memory_Manager -> FreeMemory (memory);
                break;

            case TRANSPORT_NO_PACKET:
                if (Data_Request_Queue.isEmpty() == FALSE)
                {
                     /*
                     **    Get the next packet from the queue
                     */
                    memory = (PMemory) Data_Request_Queue.read ();
                    total_length = memory -> GetLength ();

                    m_pQ922->DataRequest(Identifier, memory, &bytes_accepted);

                    if (bytes_accepted == total_length)
                    {
                        Data_Request_Queue.get ();
                        Data_Request_Memory_Manager -> FreeMemory (memory);
                    }
                    else
                        continue_loop = FALSE;
                }
                else
                    continue_loop = FALSE;
                break;
        }
    }

    if (Data_Request_Queue.isEmpty())
        *pending_data = 0;
    else
        *pending_data = PROTOCOL_USER_DATA;

    return (PROTOCOL_LAYER_NO_ERROR);
}


/*
 *    TransportError    CLayerX224::DataRequest (
 *                                ULONG,
 *                                LPBYTE    packet_address,
 *                                USHORT    packet_length,
 *                                USHORT *    bytes_accepted)
 *
 *    Public
 *
 *    Functional Description:
 *        This function takes a packet from the user and queues it for
 *        transmission.
 */
ProtocolLayerError    CLayerX224::DataRequest (
                                ULONG_PTR,
                                LPBYTE        packet_address,
                                ULONG        packet_length,
                                PULong        bytes_accepted)
{
    TRACE_OUT(("CLayerX224::DataRequest"));

    ULONG                total_packet_size;
    ULONG                packet_size;
    DataRequestQueue    temporary_queue;
    PMemory                memory;
    BOOL                packet_failed = FALSE;
    LPBYTE                address;

    *bytes_accepted = 0;

    if (Shutdown_Transmitter)
        return (PROTOCOL_LAYER_NO_ERROR);

    total_packet_size = packet_length;

     /*
     **    Create enough PDUs to hold the packet.  We don't actually copy the
     **    packet into the new buffers until we know that we can get enough
     **    space.
     */
    while (total_packet_size != 0)
    {
        if (total_packet_size >
            (ULONG) (Arbitrated_PDU_Size - DATA_PACKET_HEADER_SIZE))
        {
            packet_size = Arbitrated_PDU_Size - DATA_PACKET_HEADER_SIZE;
        }
        else
            packet_size = total_packet_size;

        total_packet_size -= packet_size;

        memory = Data_Request_Memory_Manager -> AllocateMemory (
                                    NULL,
                                    packet_size +
                                        DATA_PACKET_HEADER_SIZE +
                                        Lower_Layer_Prepend +
                                        Lower_Layer_Append);
        if (memory == NULL)
        {
            packet_failed = TRUE;
            break;
        }

        temporary_queue.append ((DWORD_PTR) memory);
    }


     /*
     **    If we were unable to allocate memory for the packet, release the memory
     **    that we did allocate.
     */
    if (packet_failed)
    {
        temporary_queue.reset();
        while (temporary_queue.iterate ((PDWORD_PTR) &memory))
        {
            Data_Request_Memory_Manager->FreeMemory (memory);
        }

         /*
         **    Set the User_Data_Pending flag to the packet_length so we can
         **    notify the user when buffer space is available.
         */
        User_Data_Pending = packet_length;
    }
    else
    {
        User_Data_Pending = 0;

        total_packet_size = packet_length;

         /*
         **    Go thru each of the PDUs and actually create them.
         */
        temporary_queue.reset();
        while (temporary_queue.iterate ((PDWORD_PTR) &memory))
        {
            if (total_packet_size >
                (ULONG) (Arbitrated_PDU_Size - DATA_PACKET_HEADER_SIZE))
            {
                packet_size = Arbitrated_PDU_Size - DATA_PACKET_HEADER_SIZE;
            }
            else
                packet_size = total_packet_size;

            address = memory -> GetPointer ();

            memcpy (
                address + DATA_PACKET_HEADER_SIZE + Lower_Layer_Prepend,
                packet_address + (USHORT) (packet_length - total_packet_size),
                packet_size);

            total_packet_size -= packet_size;

             /*
             **    This is the header for a data packet
             */
            address += Lower_Layer_Prepend;
            *address = 2;
            *(address + 1) = DATA_PACKET;
            if (total_packet_size == 0)
                *(address + 2) = EOT_BIT;
            else
                *(address + 2) = 0;

             /*
             **    Load the memory object into the queue
             */
            Data_Request_Queue.append ((DWORD_PTR) memory);
        }
        *bytes_accepted = packet_length;
    }

    return (PROTOCOL_LAYER_NO_ERROR);
}


/*
 *    ProtocolLayerError    CLayerX224::DataRequest (
 *                                    ULONG,
 *                                    PMemory,
 *                                    USHORT *        bytes_accepted)
 *
 *    Public
 *
 *    Functional Description:
 *        This function takes a packet from the user and queues it for
 *        transmission.
 */
ProtocolLayerError    CLayerX224::DataRequest (
                                ULONG_PTR,
                                PMemory,
                                PULong        bytes_accepted)
{
    *bytes_accepted = 0;

    return (PROTOCOL_LAYER_ERROR);
}


/*
 *    ProtocolLayerError    CLayerX224::PollReceiver (
 *                                    ULONG)
 *
 *    Public
 *
 *    Functional Description:
 *        This function should be called periodically to allow us to send received
 *        packets to the user.
 */
ProtocolLayerError CLayerX224::PollReceiver(void)
{
    // TRACE_OUT(("CLayerX224::PollReceiver"));

    LegacyTransportData    transport_data;
    ULONG            packet_accepted;
    PTMemory        packet;
    HPUChar            packet_address;
    ULONG            packet_length;

    if (Shutdown_Receiver)
        return (PROTOCOL_LAYER_NO_ERROR);

     /*
     **    If I have any packets in my receive buffers that
     **    need to go to higher layers, do it now
     */
    while (Data_Indication_Queue.isEmpty () == FALSE)
    {
        packet = (PTMemory) Data_Indication_Queue.read ();
        packet -> GetMemory (
                    &packet_address,
                    &packet_length);
        transport_data.logical_handle = m_nLocalLogicalHandle;
        transport_data.pbData = (LPBYTE) packet_address;
        transport_data.cbDataSize = packet_length;

        packet_accepted = ::NotifyT120(TRANSPORT_DATA_INDICATION, &transport_data);

         /*
         **    If the user returns anything but TRANSPORT_NO_ERROR, it could not
         **    accept the packet.  We will try to send the packet again later.
         */
        if (packet_accepted == TRANSPORT_NO_ERROR)
        {
            Data_Indication_Queue.get ();
            packet -> Reset ();
            Data_Indication_Memory_Pool.append ((DWORD_PTR) packet);
        }
        else
        {
             /*
             **    If the user appliction does NOT accept the packet
             **    shutdown the receiver and wait for the user to re-enable it.
             */
            Shutdown_Receiver = TRUE;
            break;
        }
    }

    return (PROTOCOL_LAYER_NO_ERROR);
}


/*
 *    ProtocolLayerError    CLayerX224::GetParameters (
 *                                    ULONG,
 *                                    USHORT *    packet_size)
 *
 *    Public
 *
 *    Functional Description:
 *        This function returns the maximum allowable TSDU.
 */
ProtocolLayerError    CLayerX224::GetParameters (
                                USHORT *,
                                USHORT *,
                                USHORT *)
{
    return (PROTOCOL_LAYER_NO_ERROR);
}


/*
 *    ProtocolLayerError    CLayerX224::RegisterHigherLayer (
 *                                    ULONG,
 *                                    PMemoryManager,
 *                                    IProtocolLayer *)
 *
 *    Public
 *
 *    Functional Description:
 *        This function does nothing.  The only reason it is here is because this
 *        class inherits from ProtocolLayer and this function is pure virtual in
 *        that class.
 */
ProtocolLayerError    CLayerX224::RegisterHigherLayer (
                                ULONG_PTR,
                                PMemoryManager,
                                IProtocolLayer *)
{
    return (PROTOCOL_LAYER_REGISTRATION_ERROR);
}


/*
 *    ProtocolLayerError    CLayerX224::RemoveHigherLayer (
 *                                    ULONG)
 *
 *    Public
 *
 *    Functional Description:
 *        This function does nothing.  The only reason it is here is because this
 *        class inherits from ProtocolLayer and this function is pure virtual in
 *        that class.
 */
ProtocolLayerError    CLayerX224::RemoveHigherLayer (
                                ULONG_PTR)
{
    return (PROTOCOL_LAYER_REGISTRATION_ERROR);
}


/*
 *    BOOL        CLayerX224::AllocateBuffers ()
 *
 *    Functional Description
 *        This function allocates the data request and data indication buffers.
 *        and sets up the memory pools necessary.  It also sets up the Control
 *        buffer for control packets.
 *
 *    Formal Parameters
 *        None
 *
 *    Return Value
 *        None.
 *
 *    Side Effects
 *        None
 *
 *    Caveats
 *        None
 */
BOOL        CLayerX224::AllocateBuffers ()
{
    TRACE_OUT(("CLayerX224::AllocateBuffers"));

    PTMemory        packet;
    USHORT            i;
    ULONG            total_packet_size;
    TMemoryError    error;

    total_packet_size = MAXIMUM_USER_DATA_SIZE;
    for (i=0; i<Data_Indication_Queue_Size; i++)
    {
        DBG_SAVE_FILE_LINE
        packet = new TMemory (
                        total_packet_size,
                        0,
                        &error);

        if (error == TMEMORY_NO_ERROR)
            Data_Indication_Memory_Pool.append ((DWORD_PTR) packet);
        else
            return (FALSE);
    }

    return (TRUE);
}


/*
 *    void    CLayerX224::ErrorPacket (
 *                        LPBYTE    packet_address,
 *                        USHORT    packet_length)
 *
 *    Functional Description
 *        This function stores the packet into our own error buffer and prepares
 *        to send it out
 *
 *    Formal Parameters
 *        None
 *
 *    Return Value
 *        None.
 *
 *    Side Effects
 *        None
 *
 *    Caveats
 *        None
 */
void    CLayerX224::ErrorPacket (
                    LPBYTE    packet_address,
                    USHORT    packet_length)
{
    TRACE_OUT(("CLayerX224::ErrorPacket"));

    DBG_SAVE_FILE_LINE
    Error_Buffer = new BYTE[packet_length];
    if (NULL != Error_Buffer)
    {
        Error_Buffer_Length = packet_length;

        memcpy (Error_Buffer, packet_address, packet_length);

        Packet_Pending = ERROR_PACKET;
    }
}


/*
 *    void    CLayerX224::CheckUserBuffers ()
 *
 *    Public
 *
 *    Functional Description:
 *        This function issues TRANSPORT_BUFFER_AVAILABLE_INDICATIONs to the
 *        user if available.
 */
void    CLayerX224::CheckUserBuffers ()
{
    // TRACE_OUT(("CLayerX224::CheckUserBuffers"));

    ULONG    user_data_size;
    ULONG    buffer_size;
    ULONG    full_size_buffers_needed;
    ULONG    full_size_buffer_count;
    ULONG    partial_buffer_size;
    ULONG    partial_buffer_count;


    if (User_Data_Pending == 0)
        return;

     /*
     **    Determine the user data size in a packet, then determine
     **    how many buffers will be needed to accept that packet.
     */
    user_data_size = Arbitrated_PDU_Size - DATA_PACKET_HEADER_SIZE;
    full_size_buffers_needed = User_Data_Pending / user_data_size;

     /*
     **    Find out how many full size buffers are available
     */
    if (full_size_buffers_needed != 0)
    {
         /*
         **    Increment full_size_buffers_needed to account for our priority
         **    value.
         */
        buffer_size =
            Arbitrated_PDU_Size + Lower_Layer_Prepend + Lower_Layer_Append;

        full_size_buffer_count = Data_Request_Memory_Manager ->
                                    GetBufferCount (buffer_size);
        if (full_size_buffer_count < full_size_buffers_needed)
            return;
    }

    partial_buffer_size = User_Data_Pending % user_data_size;
    if (partial_buffer_size != 0)
    {
        if ((full_size_buffers_needed == 0) ||
            (full_size_buffer_count == full_size_buffers_needed))
        {
            buffer_size = partial_buffer_size +
                            DATA_PACKET_HEADER_SIZE +
                            Lower_Layer_Prepend +
                            Lower_Layer_Append;

            partial_buffer_count = Data_Request_Memory_Manager ->
                                    GetBufferCount (buffer_size);

            if (full_size_buffers_needed == 0)
            {
                if (partial_buffer_count == 0)
                    return;
            }
            else
            {
                if ((partial_buffer_count == full_size_buffer_count) ||
                    (partial_buffer_count == 0))
                {
                    return;
                }
            }
        }
    }

    User_Data_Pending = 0;

    ::NotifyT120(TRANSPORT_BUFFER_EMPTY_INDICATION, (void *) m_nLocalLogicalHandle);

    return;
}


/*
 *    static    ULONG    CLayerX224::GetMaxTPDUSize (
 *                                ULONG    max_lower_layer_pdu)
 *
 *    Public
 *
 *    Functional Description:
 *        This function accepts a value for the lower layer max. PDU size
 *        and returns the max. PDU size that this Transport can support
 *        based on it.  X224 only suports max PDU sizes of 128, 256, 512,
 *        1024, and 2048.  So, if the max_lower_layer_pdu is 260, the
 *        Transport can only have a max pdu size of 256.
 */
ULONG    CLayerX224::GetMaxTPDUSize (
                    ULONG    max_lower_layer_pdu)
{
    TRACE_OUT(("CLayerX224::GetMaxTPDUSize"));

    ULONG    max_tpdu_size;

    if (max_lower_layer_pdu < 256)
        max_tpdu_size = 128;
    else if (max_lower_layer_pdu < 512)
        max_tpdu_size = 256;
    else if (max_lower_layer_pdu < 1024)
        max_tpdu_size = 512;
    else if (max_lower_layer_pdu < 2048)
        max_tpdu_size = 1024;
    else
        max_tpdu_size = 2048;

    return (max_tpdu_size);
}