/*
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

    (C) Copyright 1999
        All rights reserved.

§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

  Portions of this software are:

    (C) Copyright 1995 TriplePoint, Inc. -- http://www.TriplePoint.com
        License to use this software is granted under the same terms
        outlined in the Microsoft Windows Device Driver Development Kit.

    (C) Copyright 1992 Microsoft Corp. -- http://www.Microsoft.com
        License to use this software is granted under the terms outlined in
        the Microsoft Windows Device Driver Development Kit.

§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@doc INTERNAL DChannel DChannel_c

@module DChannel.c |

    This module implements the interface to the <t DCHANNEL_OBJECT>.
    Supports the high-level channel control functions used by the CONDIS WAN
    Minport driver.

@comm

    This module isolates most the vendor specific Call Control interfaces.
    It will require some changes to accomodate your hardware device's call
    control mechanism.

@head3 Contents |
@index class,mfunc,func,msg,mdata,struct,enum | DChannel_c

@end
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
*/

#define  __FILEID__             DCHANNEL_OBJECT_TYPE
// Unique file ID for error logging

#include "Miniport.h"                   // Defines all the miniport objects

#if defined(NDIS_LCODE)
#   pragma NDIS_LCODE   // Windows 9x wants this code locked down!
#   pragma NDIS_LDATA
#endif


DBG_STATIC ULONG                g_DChannelInstanceCounter   // @globalv
// Keeps track of how many <t DCHANNEL_OBJECT>s are created.
                                = 0;


/* @doc EXTERNAL INTERNAL DChannel DChannel_c g_DChannelParameters
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@topic 5.4 DChannel Parameters |

    This section describes the registry parameters read into the
    <t DCHANNEL_OBJECT>.

@globalv PARAM_TABLE | g_DChannelParameters |

    This table defines the registry based parameters to be assigned to data
    members of the <t DCHANNEL_OBJECT>.

    <f Note>:
    If you add any registry based data members to <t DCHANNEL_OBJECT>
    you will need to modify <f DChannelReadParameters> and add the parameter
    definitions to the <f g_DChannelParameters> table.

*/

DBG_STATIC PARAM_TABLE          g_DChannelParameters[] =
{
    PARAM_ENTRY(DCHANNEL_OBJECT,
                TODO, PARAM_TODO,
                FALSE, NdisParameterInteger, 0,
                0, 0, 0),

    /* The last entry must be an empty string! */
    { { 0 } }
};


/* @doc INTERNAL DChannel DChannel_c DChannelReadParameters
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelReadParameters> reads the DChannel parameters from the registry
    and initializes the associated data members.  This should only be called
    by <f DChannelCreate>.

    <f Note>:
    If you add any registry based data members to <t DCHANNEL_OBJECT>
    you will need to modify <f DChannelReadParameters> and add the parameter
    definitions to the <f g_DChannelParameters> table.

@rdesc

    <f DChannelReadParameters> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

DBG_STATIC NDIS_STATUS DChannelReadParameters(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelReadParameters")

    NDIS_STATUS                 Status;
    // Status result returned from an NDIS function call.

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    /*
    // Parse the registry parameters.
    */
    Status = ParamParseRegistry(
                    pAdapter->MiniportAdapterHandle,
                    pAdapter->WrapperConfigurationContext,
                    (PUCHAR)pDChannel,
                    g_DChannelParameters
                    );

    if (Status == NDIS_STATUS_SUCCESS)
    {
        /*
        // Make sure the parameters are valid.
        */
        if (pDChannel->TODO)
        {
            DBG_ERROR(pAdapter,("Invalid parameter\n"
                      ));
            NdisWriteErrorLogEntry(
                    pAdapter->MiniportAdapterHandle,
                    NDIS_ERROR_CODE_UNSUPPORTED_CONFIGURATION,
                    3,
                    pDChannel->TODO,
                    __FILEID__,
                    __LINE__
                    );
            Status = NDIS_STATUS_FAILURE;
        }
        else
        {
            /*
            // Finish setting up data members based on registry settings.
            */
        }
    }

    DBG_RETURN(pAdapter, Status);
    return (Status);
}


/* @doc INTERNAL DChannel DChannel_c DChannelCreateObjects
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelCreateObjects> calls the create routines for all the objects
    contained in <t DCHANNEL_OBJECT>.  This should only be called
    by <f DChannelCreate>.

    <f Note>:
    If you add any new objects to <t DCHANNEL_OBJECT> you will need
    to modify <f DChannelCreateObjects> and <f DChannelDestroyObjects> so they
    will get created and destroyed properly.

@rdesc

    <f DChannelCreateObjects> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

DBG_STATIC NDIS_STATUS DChannelCreateObjects(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelCreateObjects")

    NDIS_STATUS                 Result = NDIS_STATUS_SUCCESS;
    // Holds the result code returned by this function.

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    // TODO - Add code here to allocate any sub-objects needed to support
    // your physical DChannels.

    DBG_RETURN(pAdapter, Result);
    return (Result);
}


/* @doc INTERNAL DChannel DChannel_c DChannelCreate
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelCreate> allocates memory for a <t DCHANNEL_OBJECT> and then
    initializes the data members to their starting state.
    If successful, <p ppDChannel> will be set to point to the newly created
    <t DCHANNEL_OBJECT>.  Otherwise, <p ppDChannel> will be set to NULL.

@comm

    This function should be called only once when the Miniport is loaded.
    Before the Miniport is unloaded, <f DChannelDestroy> must be called to
    release the <t DCHANNEL_OBJECT> created by this function.

@rdesc

    <f DChannelCreate> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

NDIS_STATUS DChannelCreate(
    OUT PDCHANNEL_OBJECT *      ppDChannel,                 // @parm
    // Points to a caller-defined memory location to which this function
    // writes the virtual address of the allocated <t DCHANNEL_OBJECT>.

    IN PMINIPORT_ADAPTER_OBJECT pAdapter                    // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
    )
{
    DBG_FUNC("DChannelCreate")

    PDCHANNEL_OBJECT            pDChannel;
    // Pointer to our newly allocated object.

    NDIS_STATUS                 Result = NDIS_STATUS_SUCCESS;
    // Holds the result code returned by this function.

    ASSERT(pAdapter && pAdapter->ObjectType == MINIPORT_ADAPTER_OBJECT_TYPE);

    DBG_ENTER(pAdapter);

    /*
    // Make sure the caller's object pointer is NULL to begin with.
    // It will be set later only if everything is successful.
    */
    *ppDChannel = NULL;

    /*
    // Allocate memory for the object.
    */
    Result = ALLOCATE_OBJECT(pDChannel, pAdapter->MiniportAdapterHandle);

    if (Result == NDIS_STATUS_SUCCESS)
    {
        /*
        // Zero everything to begin with.
        // Then set the object type and assign a unique ID .
        */
        pDChannel->ObjectType = DCHANNEL_OBJECT_TYPE;
        pDChannel->ObjectID = ++g_DChannelInstanceCounter;

        /*
        // Initialize the member variables to their default settings.
        */
        pDChannel->pAdapter = pAdapter;

        // TODO - Add code here to allocate any resources needed to support
        // your physical DChannels.

        /*
        // Parse the registry parameters.
        */
        Result = DChannelReadParameters(pDChannel);

        /*
        // If all goes well, we are ready to create the sub-components.
        */
        if (Result == NDIS_STATUS_SUCCESS)
        {
            Result = DChannelCreateObjects(pDChannel);
        }

        if (Result == NDIS_STATUS_SUCCESS)
        {
            /*
            // All is well, so return the object pointer to the caller.
            */
            *ppDChannel = pDChannel;
        }
        else
        {
            /*
            // Something went wrong, so let's make sure everything is
            // cleaned up.
            */
            DChannelDestroy(pDChannel);
        }
    }

    DBG_RETURN(pAdapter, Result);
    return (Result);
}


/* @doc INTERNAL DChannel DChannel_c DChannelDestroyObjects
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelDestroyObjects> calls the destroy routines for all the objects
    contained in <t DCHANNEL_OBJECT>.  This should only be called by
    <f DChannelDestroy>.

    <f Note>: If you add any new objects to <t PDCHANNEL_OBJECT> you will need to
    modify <f DChannelCreateObjects> and <f DChannelDestroyObjects> so they
    will get created and destroyed properly.

*/

DBG_STATIC void DChannelDestroyObjects(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelDestroyObjects")

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    // TODO - Add code here to release any sub-objects allocated by
    // DChannelCreateObjects.

    DBG_LEAVE(pAdapter);
}


/* @doc INTERNAL DChannel DChannel_c DChannelDestroy
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelDestroy> frees the memory for this <t DCHANNEL_OBJECT>.
    All memory allocated by <f DChannelCreate> will be released back to the
    OS.

*/

void DChannelDestroy(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelDestroy")

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    if (pDChannel)
    {
        ASSERT(pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);

        pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

        DBG_ENTER(pAdapter);

        // TODO - Add code here to release any resources allocated by
        // DChannelCreate.

        /*
        // Release all objects allocated within this object.
        */
        DChannelDestroyObjects(pDChannel);

        /*
        // Make sure we fail the ASSERT if we see this object again.
        */
        pDChannel->ObjectType = 0;
        FREE_OBJECT(pDChannel);

        DBG_LEAVE(pAdapter);
    }
}


/* @doc INTERNAL DChannel DChannel_c DChannelInitialize
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelInitialize> resets all the internal data members contained
    in <t BCHANNEL_OBJECT> back to their initial state.

    <f Note>:
    If you add any new members to <t DCHANNEL_OBJECT> you will need to
    modify <f DChannelInitialize> to initialize your new data mamebers.

*/

void DChannelInitialize(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t BCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelInitialize")

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    // TODO - Add code here to initialize all the physical D-Channels on
    // your adapter.

    DBG_LEAVE(pAdapter);
}


/* @doc INTERNAL DChannel DChannel_c DChannelOpen
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelOpen> establishes a communications path between the miniport
    and the DChannel.

@rdesc

    <f DChannelOpen> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

NDIS_STATUS DChannelOpen(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelOpen")

    NDIS_STATUS                 Result = NDIS_STATUS_SUCCESS;
    // Holds the result code returned by this function.

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    if (++pDChannel->IsOpen == 1)
    {
        DBG_NOTICE(pAdapter,("Opening DChannel #%d\n",
                   pDChannel->ObjectID));

        // TODO - Add code here to open all the physical D-Channels on
        // your adapter.
    }
    else
    {
        DBG_NOTICE(pAdapter,("DChannel #%d already opened\n",
                   pDChannel->ObjectID));
    }

    DBG_RETURN(pAdapter, Result);
    return (Result);
}


/* @doc INTERNAL DChannel DChannel_c DChannelClose
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelClose> tears down the communications path between the miniport
    and the DChannel.

*/

void DChannelClose(
    IN PDCHANNEL_OBJECT         pDChannel                   // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.
    )
{
    DBG_FUNC("DChannelClose")

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    if (pDChannel->IsOpen == 1)
    {
        DBG_NOTICE(pAdapter,("Closing DChannel #%d\n",
                   pDChannel->ObjectID));

        // TODO - Add code here to close all the physical D-Channels on
        // your adapter.
        pDChannel->IsOpen = 0;
    }
    else if (pDChannel->IsOpen == 0)
    {
        DBG_WARNING(pAdapter,("DChannel #%d already closed\n",
                    pDChannel->ObjectID));
    }
    else
    {
        pDChannel->IsOpen--;
    }

    DBG_LEAVE(pAdapter);
}


/* @doc INTERNAL DChannel DChannel_c DChannelMakeCall
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelMakeCall> places a call over the selected line device.

@rdesc

    <f DChannelMakeCall> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

NDIS_STATUS DChannelMakeCall(
    IN PDCHANNEL_OBJECT         pDChannel,                  // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.

    IN PBCHANNEL_OBJECT         pBChannel,                  // @parm
    // A pointer to the <t BCHANNEL_OBJECT> returned by <f BChannelCreate>.

    IN PUCHAR                   DialString,                 // @parm
    // A pointer to an ASCII null-terminated string of digits.

    IN USHORT                   DialStringLength,           // @parm
    // Number of bytes in dial string.

    IN PLINE_CALL_PARAMS        pLineCallParams             // @parm
    // A pointer to the TAPI <t LINE_CALL_PARAMS> to be used for this call.
    )
{
    DBG_FUNC("DChannelMakeCall")

    NDIS_STATUS                 Result = NDIS_STATUS_RESOURCES;
    // Holds the result code returned by this function.

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    ASSERT(pDChannel->IsOpen);

    pDChannel->TotalMakeCalls++;
    pBChannel->Flags |= VCF_OUTGOING_CALL;
    pBChannel->CallState = LINECALLSTATE_DIALING;

#if defined(SAMPLE_DRIVER)
    // This sample code uses the phone number to select one of the other
    // BChannels on which to complete the connection.
{
    PBCHANNEL_OBJECT            pPeerBChannel;
    PCARD_EVENT_OBJECT          pEvent;

    pPeerBChannel = GET_BCHANNEL_FROM_PHONE_NUMBER(DialString);
    if (pPeerBChannel)
    {
        pEvent = CardEventAllocate(pPeerBChannel->pAdapter->pCard);
        if (pEvent)
        {
            pEvent->ulEventCode      = CARD_EVENT_RING;
            pEvent->pSendingObject   = pBChannel;
            pEvent->pReceivingObject = pPeerBChannel;
            pBChannel->pPeerBChannel = pPeerBChannel;
            CardNotifyEvent(pPeerBChannel->pAdapter->pCard, pEvent);
            Result = NDIS_STATUS_PENDING;
        }
    }
    else
    {
        DBG_ERROR(pAdapter,("Cannot map phone number '%s' to BChannel\n",
                  DialString));
    }
}
#else  // SAMPLE_DRIVER
    // TODO - Add code here to place a call.
#endif // SAMPLE_DRIVER

    DBG_RETURN(pAdapter, Result);
    return (Result);
}


/* @doc INTERNAL DChannel DChannel_c DChannelAnswerCall
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelAnswerCall> answers the incoming call so it can be connected.

@rdesc

    <f DChannelAnswerCall> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

NDIS_STATUS DChannelAnswerCall(
    IN PDCHANNEL_OBJECT         pDChannel,                  // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.

    IN PBCHANNEL_OBJECT         pBChannel                   // @parm
    // A pointer to the <t BCHANNEL_OBJECT> returned by <f BChannelCreate>.
    )
{
    DBG_FUNC("DChannelAnswerCall")

    NDIS_STATUS                 Result = NDIS_STATUS_SUCCESS;
    // Holds the result code returned by this function.

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    ASSERT(pDChannel->IsOpen);

    pDChannel->TotalAnswers++;

#if defined(SAMPLE_DRIVER)
    // This sample code sends a connect over to the calling BChannel.
{
    PCARD_EVENT_OBJECT          pEvent;
    PBCHANNEL_OBJECT            pPeerBChannel = pBChannel->pPeerBChannel;

    if (pPeerBChannel)
    {
        pEvent = CardEventAllocate(pPeerBChannel->pAdapter->pCard);
        if (pEvent)
        {
            pEvent->ulEventCode      = CARD_EVENT_CONNECT;
            pEvent->pSendingObject   = pBChannel;
            pEvent->pReceivingObject = pPeerBChannel;
            CardNotifyEvent(pPeerBChannel->pAdapter->pCard, pEvent);
        }
        else
        {
            Result = NDIS_STATUS_RESOURCES;
        }
    }
    else
    {
        DBG_ERROR(pAdapter,("pPeerBChannel == NULL\n"));
        Result = NDIS_STATUS_RESOURCES;
    }
}
#else  // SAMPLE_DRIVER
    // TODO - Add code here to answer a call.

    // If you return NDIS_STATUS_PENDING from here, you must perform the
    // following actions when the call is finally answered.
    //      pBChannel->CallState = LINECALLSTATE_CONNECTED;
    //      NdisMCmDispatchCallConnected(pBChannel->NdisVcHandle);
    // If you fail to answer for some reason you must call:
    //      InitiateCallTeardown(pAdapter, pBChannel);

#endif // SAMPLE_DRIVER

    DBG_RETURN(pAdapter, Result);
    return (Result);
}


/* @doc INTERNAL DChannel DChannel_c DChannelCloseCall
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelCloseCall> closes a previously opened call instance as
    initiated from <f DChannelMakeCall> or <f DChannelAnswerCall>.  After
    the call is closed, no one else should reference it.

@rdesc

    <f DChannelCloseCall> returns zero if it is successful.<nl>
    Otherwise, a non-zero return value indicates an error condition.

*/

NDIS_STATUS DChannelCloseCall(
    IN PDCHANNEL_OBJECT         pDChannel,                  // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.

    IN PBCHANNEL_OBJECT         pBChannel                   // @parm
    // A pointer to the <t BCHANNEL_OBJECT> returned by <f BChannelCreate>.
    )
{
    DBG_FUNC("DChannelCloseCall")

    NDIS_STATUS                 Result = NDIS_STATUS_SUCCESS;
    // Holds the result code returned by this function.

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    ASSERT(pDChannel->IsOpen);

    // This routine may be called several times during line/call cleanup.
    // If the call is already closed, just return success.
    if (pBChannel->CallState != 0 &&
        pBChannel->CallState != LINECALLSTATE_IDLE)
    {
#if defined(SAMPLE_DRIVER)
        // This sample code sends a disconnect over to the connected BChannel.
        PCARD_EVENT_OBJECT      pEvent;
        PBCHANNEL_OBJECT        pPeerBChannel = pBChannel->pPeerBChannel;

        if (pPeerBChannel)
        {
            pEvent = CardEventAllocate(pPeerBChannel->pAdapter->pCard);
            if (pEvent)
            {
                // Indicate call disconnect to the other channel.
                pEvent->ulEventCode      = CARD_EVENT_DISCONNECT;
                pEvent->pSendingObject   = pBChannel;
                pEvent->pReceivingObject = pPeerBChannel;
                CardNotifyEvent(pPeerBChannel->pAdapter->pCard, pEvent);
            }
            pBChannel->pPeerBChannel = NULL;
        }
        else
        {
            DBG_WARNING(pAdapter,("#%d NO PEER CHANNEL - CALLSTATE=%X\n",
                        pBChannel->ObjectID, pBChannel->CallState));
        }
        // Return any pending packets to the protocol stack.
        pBChannel->CallState = LINECALLSTATE_IDLE;
#else  // SAMPLE_DRIVER
        // TODO - Add code here to drop a call.
#endif // SAMPLE_DRIVER

        // Make sure there are no packets left on this channel before it closes.
        FlushSendPackets(pAdapter, pBChannel);
    }
    else
    {
        DBG_NOTICE(pAdapter,("#%d ALREADY IDLE - CALLSTATE=%X\n",
                   pBChannel->ObjectID, pBChannel->CallState));
    }

    DBG_RETURN(pAdapter, Result);
    return (Result);
}


/* @doc INTERNAL DChannel DChannel_c DChannelRejectCall
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DChannelRejectCall> rejects an incoming call on the specified
    channel.

*/

VOID DChannelRejectCall(
    IN PDCHANNEL_OBJECT         pDChannel,                  // @parm
    // A pointer to the <t DCHANNEL_OBJECT> returned by <f DChannelCreate>.

    IN PBCHANNEL_OBJECT         pBChannel                   // @parm
    // A pointer to the <t BCHANNEL_OBJECT> returned by <f BChannelCreate>.
    )
{
    DBG_FUNC("DChannelRejectCall")

    PMINIPORT_ADAPTER_OBJECT    pAdapter;
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT>.

    ASSERT(pDChannel && pDChannel->ObjectType == DCHANNEL_OBJECT_TYPE);
    pAdapter = GET_ADAPTER_FROM_DCHANNEL(pDChannel);

    DBG_ENTER(pAdapter);

    if (pBChannel->CallState != 0 &&
        pBChannel->CallState != LINECALLSTATE_IDLE)
    {
#if defined(SAMPLE_DRIVER)
        // This sample code sends a disconnect over to the connected BChannel.
        PCARD_EVENT_OBJECT      pEvent;
        PBCHANNEL_OBJECT        pPeerBChannel = pBChannel->pPeerBChannel;

        if (pPeerBChannel)
        {
            pEvent = CardEventAllocate(pPeerBChannel->pAdapter->pCard);
            if (pEvent)
            {
                // Indicate call disconnect to the other channel.
                pEvent->ulEventCode      = CARD_EVENT_DISCONNECT;
                pEvent->pSendingObject   = pBChannel;
                pEvent->pReceivingObject = pPeerBChannel;
                CardNotifyEvent(pPeerBChannel->pAdapter->pCard, pEvent);
            }
            pBChannel->pPeerBChannel = NULL;
        }
        else
        {
            DBG_WARNING(pAdapter,("#%d NO PEER CHANNEL - CALLSTATE=%X\n",
                        pBChannel->ObjectID, pBChannel->CallState));
        }
        // Return any pending packets to the protocol stack.
        pBChannel->CallState = LINECALLSTATE_IDLE;
#else  // SAMPLE_DRIVER
        // TODO - Add code here to reject an incoming call.
#endif // SAMPLE_DRIVER
    }

    DBG_LEAVE(pAdapter);
}

