/*++

Copyright (c) 1998-1999  Microsoft Corporation

Module Name:

    driver.c

Abstract:

    ATMEPVC - Driver Entry and associated functions

Author:


Revision History:

    Who         When        What
    --------    --------    ----
    ADube      03-23-00    created, .

--*/


#include "precomp.h"
#pragma hdrstop

#pragma NDIS_INIT_FUNCTION(DriverEntry)


//
// temp global variables
//
NDIS_HANDLE ProtHandle, DriverHandle;


//
// global variables
//

NDIS_PHYSICAL_ADDRESS           HighestAcceptableMax = NDIS_PHYSICAL_ADDRESS_CONST(-1, -1);
NDIS_HANDLE                     ProtHandle = NULL;
NDIS_HANDLE                     DriverHandle = NULL;
NDIS_MEDIUM                     MediumArray[1] =
                                    {
                                        NdisMediumAtm
                                    };


LIST_ENTRY                      g_ProtocolList;                                 
EPVC_GLOBALS                    EpvcGlobals;


RM_STATUS
epvcResHandleGlobalProtocolList(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    );




RM_STATUS
epvcRegisterIMDriver(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    );




RM_STATUS
epvcUnloadDriver(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    );

RM_STATUS
epvcDeRegisterIMDriver(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    );

RM_STATUS
epvcIMDriverRegistration(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    );


//--------------------------------------------------------------------------------
//                                                                              //
//  Global Root structure definitions                                           //
//                                                                              //
//                                                                              //
//--------------------------------------------------------------------------------


// List of fixed resources used by ArpGlobals
//
enum
{
    RTYPE_GLOBAL_PROTOCOL_LIST,
    RTYPE_GLOBAL_REGISTER_IM
    
}; // EPVC_GLOBAL_RESOURCES;

//
// Identifies information pertaining to the use of the above resources.
// Following table MUST be in strict increasing order of the RTYPE_GLOBAL
// enum.
//

RM_RESOURCE_TABLE_ENTRY 
EpvcGlobals_ResourceTable[] =
{

    {RTYPE_GLOBAL_PROTOCOL_LIST,    epvcResHandleGlobalProtocolList},

    {RTYPE_GLOBAL_REGISTER_IM,  epvcIMDriverRegistration}


};

// Static information about ArpGlobals.
//
RM_STATIC_OBJECT_INFO
EpvcGlobals_StaticInfo = 
{
    0, // TypeUID
    0, // TypeFlags
    "EpvcGlobals",  // TypeName
    0, // Timeout

    NULL, // pfnCreate
    NULL, // pfnDelete
    NULL, // pfnVerifyLock

    sizeof(EpvcGlobals_ResourceTable)/sizeof(EpvcGlobals_ResourceTable[1]),
    EpvcGlobals_ResourceTable
};






//--------------------------------------------------------------------------------
//                                                                              //
//  Underlying Adapters. The Protocol gets called at BindAdapter for            //
//  each adapter                                                                //
//                                                                              //
//                                                                              //
//--------------------------------------------------------------------------------



// eovcAdapter_HashInfo contains information required maintain a hashtable
// of EPVC_ADAPTER objects.
//
RM_HASH_INFO
epvcAdapter_HashInfo = 
{
    NULL, // pfnTableAllocator

    NULL, // pfnTableDeallocator

    epvcAdapterCompareKey,  // fnCompare

    // Function to generate a ULONG-sized hash.
    //
    epvcAdapterHash     // pfnHash

};


// EpvcGlobals_AdapterStaticInfo  contains static information about
// objects of type EPVC_ADAPTERS.
// It is a group of Adapters that the protocol has bound to
//
RM_STATIC_OBJECT_INFO
EpvcGlobals_AdapterStaticInfo =
{
    0, // TypeUID
    0, // TypeFlags
    "Adapter",  // TypeName
    0, // Timeout

    epvcAdapterCreate,  // pfnCreate
    epvcAdapterDelete,      // pfnDelete
    NULL, // pfnVerifyLock

    0,    // Size of resource table
    NULL, // ResourceTable

    &epvcAdapter_HashInfo
};



//--------------------------------------------------------------------------------
//                                                                              //
//  Intermediate miniports - each hangs of a protocol block                     //
//                                                                              //
//                                                                              //
//--------------------------------------------------------------------------------

// arpAdapter_HashInfo contains information required maintain a hashtable
// of EPVC_ADAPTER objects.
//
RM_HASH_INFO
epvc_I_Miniport_HashInfo= 
{
    NULL, // pfnTableAllocator

    NULL, // pfnTableDeallocator

    epvcIMiniportCompareKey,    // fnCompare

    // Function to generate a ULONG-sized hash.
    //
    epvcIMiniportHash       // pfnHash

};


RM_STATIC_OBJECT_INFO
EpvcGlobals_I_MiniportStaticInfo =
{
    0, // TypeUID
    0, // TypeFlags
    "IMiniport",    // TypeName
    0, // Timeout

    epvcIMiniportCreate,    // pfnCreate
    epvcIMiniportDelete,        // pfnDelete
    NULL, // pfnVerifyLock

    0,    // Size of resource table
    NULL, // ResourceTable

    &epvc_I_Miniport_HashInfo
};




//
// Variables used in debugging
//
#if DBG
ULONG g_ulTraceLevel= DEFAULTTRACELEVEL;
ULONG g_ulTraceMask = DEFAULTTRACEMASK ;
#endif










NTSTATUS
DriverEntry(
    IN  PDRIVER_OBJECT      DriverObject,
    IN  PUNICODE_STRING     RegistryPath
    )
/*++

Routine Description:


Arguments:

Return Value:


--*/
{
    NDIS_STATUS                     Status;
    NTSTATUS                        NtStatus;

    BOOLEAN     AllocatedGlobals = FALSE;
    ENTER("DriverEntry", 0xbfcb7eb1)


    RM_DECLARE_STACK_RECORD(SR)

    TIMESTAMP("==>DriverEntry");

    
    TRACE ( TL_T, TM_Dr,("==>Atm Epvc DriverEntry\n"));


    do
    {
        //
        // Inititalize the global variables
        //

        
        // Must be done before any RM apis are used.
        //
        RmInitializeRm();

        RmInitializeLock(
                    &EpvcGlobals.Lock,
                    LOCKLEVEL_GLOBAL
                    );

        RmInitializeHeader(
                NULL,                   // pParentObject,
                &EpvcGlobals.Hdr,
                ATMEPVC_GLOBALS_SIG  ,
                &EpvcGlobals.Lock,
                &EpvcGlobals_StaticInfo,
                NULL,                   // szDescription
                &SR
                );


        AllocatedGlobals = TRUE;

        //
        // Initialize globals
        //
        EpvcGlobals.driver.pDriverObject = DriverObject;
        EpvcGlobals.driver.pRegistryPath  = RegistryPath;


        //
        // Register the IM Miniport with NDIS.
        //
        Status = RmLoadGenericResource(
                    &EpvcGlobals.Hdr,
                    RTYPE_GLOBAL_PROTOCOL_LIST,
                    &SR
                    );

        if (FAIL(Status)) break;

        //
        // Register the protocol with NDIS.
        //
        Status = RmLoadGenericResource(
                    &EpvcGlobals.Hdr,
                    RTYPE_GLOBAL_REGISTER_IM,
                    &SR
                    );

        if (FAIL(Status)) break;

    
    } while (FALSE);

    
    if (FAIL(Status))
    {
        if (AllocatedGlobals)
        {
            RmUnloadAllGenericResources(
                    &EpvcGlobals.Hdr,
                    &SR
                    );
            RmDeallocateObject(
                    &EpvcGlobals.Hdr,
                    &SR
                    );
        }

        // Must be done after any RM apis are used and async activity complete.
        //
        RmDeinitializeRm();

        NtStatus = STATUS_UNSUCCESSFUL;
    }
    else
    {
        NtStatus = NDIS_STATUS_SUCCESS;
    }

    EXIT()

    TIMESTAMP("<==DriverEntry");

    RM_ASSERT_CLEAR(&SR);
    
    return Status ;

}




VOID
EpvcUnload(
    IN  PDRIVER_OBJECT              pDriverObject
)
/*++

Routine Description:

    This routine is called by the system prior to unloading us.
    Currently, we just undo everything we did in DriverEntry,
    that is, de-register ourselves as an NDIS protocol, and delete
    the device object we had created.

Arguments:

    pDriverObject   - Pointer to the driver object created by the system.

Return Value:

    None

--*/
{
    NDIS_STATUS NdisStatus; 
    ENTER("Unload", 0xc8482549)
    RM_DECLARE_STACK_RECORD(sr);
    

    TIMESTAMP("==>Unload");


    RmUnloadAllGenericResources(&EpvcGlobals.Hdr, &sr);

    RmDeallocateObject(&EpvcGlobals.Hdr, &sr);

    // Must be done after any RM apis are used and async activity complete.
    //
    RmDeinitializeRm();

    // TODO? Block(250);

    RM_ASSERT_CLEAR(&sr)
    EXIT()
    TIMESTAMP("<==Unload");
    return;
}



RM_STATUS
epvcResHandleGlobalProtocolList(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                pSR
    )
{
    PEPVC_GLOBALS               pGlobals    = NULL;


    ENTER("GlobalAdapterList", 0xb407e79e)
    
    TRACE (TL_T, TM_Dr, ("==>epvcResHandleGlobalProtocolList pObj %x, Op", 
                         pObj , Op ) );


    pGlobals    = CONTAINING_RECORD(
                                      pObj,
                                      EPVC_GLOBALS,
                                      Hdr);


    //
    // 
    if (Op == RM_RESOURCE_OP_LOAD)
    {
        //
        //  Allocate adapter list.
        //
        TR_WARN(("LOADING"));

        RmInitializeGroup(
                        pObj,                                   // pParentObject
                        &EpvcGlobals_AdapterStaticInfo,         // pStaticInfo
                        &(pGlobals->adapters.Group),            // pGroup
                        "Adapters Group",                       // szDescription
                        pSR                                     // pStackRecord
                        );
    }
    else if (Op == RM_RESOURCE_OP_UNLOAD)
    {
        //
        // We're unloading this "resource", i.e., unloading and deallocating the 
        // global adapter list. We first unload and free all the adapters
        // in the list, and then free the list itself.
        //
        TR_WARN(("UNLOADING"));
        
        //
        // We expect there to be no adapter objects at this point.
        //
        ASSERT(pGlobals->adapters.Group.HashTable.NumItems == 0);


        RmDeinitializeGroup(&pGlobals->adapters.Group, pSR);
        NdisZeroMemory(&(pGlobals->adapters), sizeof(pGlobals->adapters));
    }
    else
    {
        // Unexpected op code.
        //
        ASSERT(!"Unexpected OpCode epvcResHandleGlobalProtocolList ");
    }




    TRACE (TL_T, TM_Dr, ("<==epvcResHandleGlobalProtocolList Status %x", 
                         NDIS_STATUS_SUCCESS) );

    EXIT()
    RM_ASSERT_CLEAR(pSR);

    return NDIS_STATUS_SUCCESS;

}



RM_STATUS
epvcIMDriverRegistration(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    )
{
    TRACE (TL_T, TM_Mp, ("epvcIMDriverRegistration Op %x", Op));
    if (RM_RESOURCE_OP_LOAD == Op)
    {
        epvcRegisterIMDriver(pObj,Op,pvUserParams,psr);
    }
    else
    {
        epvcDeRegisterIMDriver(pObj,Op,pvUserParams,psr);
    }

    return NDIS_STATUS_SUCCESS;
}


RM_STATUS
epvcRegisterIMDriver(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    )
{
    NDIS_STATUS                     Status = NDIS_STATUS_FAILURE;
    PEPVC_GLOBALS                   pGlobals    = NULL;
    NDIS_PROTOCOL_CHARACTERISTICS   PChars;
    NDIS_MINIPORT_CHARACTERISTICS   MChars;
    NDIS_STRING                     Name;

    ENTER("epvcRegisterIMDriver", 0x0d0f008a);
    
    pGlobals    = CONTAINING_RECORD(
                                      pObj,
                                      EPVC_GLOBALS,
                                      Hdr);



    TRACE (TL_T, TM_Dr, ("==>epvcRegisterIMDriver Globals %x", 
                         pObj) );

    //
    // Register the miniport with NDIS. Note that it is the miniport
    // which was started as a driver and not the protocol. Also the miniport
    // must be registered prior to the protocol since the protocol's BindAdapter
    // handler can be initiated anytime and when it is, it must be ready to
    // start driver instances.
    //
    NdisMInitializeWrapper(&pGlobals->driver.WrapperHandle, 
                       pGlobals->driver.pDriverObject, 
                       pGlobals->driver.pRegistryPath, 
                       NULL);
    NdisZeroMemory(&MChars, sizeof(NDIS_MINIPORT_CHARACTERISTICS));

    MChars.MajorNdisVersion = 5;
    MChars.MinorNdisVersion = 0;

    MChars.InitializeHandler = EpvcInitialize;
    MChars.QueryInformationHandler = EpvcMpQueryInformation;
    MChars.SetInformationHandler = EpvcMpSetInformation;
    MChars.ResetHandler = MPReset;
    MChars.TransferDataHandler = MPTransferData;
    MChars.HaltHandler = EpvcHalt;

    //
    // We will disable the check for hang timeout so we do not
    // need a check for hang handler!
    //
    MChars.CheckForHangHandler = NULL;
    MChars.SendHandler = NULL;
    MChars.ReturnPacketHandler = EpvcReturnPacket;

    //
    // Either the Send or the SendPackets handler should be specified.
    // If SendPackets handler is specified, SendHandler is ignored
    //
     MChars.SendPacketsHandler = EpvcSendPackets;

    Status = NdisIMRegisterLayeredMiniport(pGlobals->driver.WrapperHandle,
                                           &MChars,
                                           sizeof(MChars),
                                           &EpvcGlobals.driver.DriverHandle);

    ASSERT  (EpvcGlobals.driver.DriverHandle != NULL);                                          
    if (Status != NDIS_STATUS_SUCCESS)
    {
        //
        // todo: fix failure case
        //
        ASSERT (0);
    };


    //
    // Now register the protocol.
    //
    NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
    PChars.MajorNdisVersion = 5;
    PChars.MinorNdisVersion = 0;

    //
    // Make sure the protocol-name matches the service-name under which this protocol is installed.
    // This is needed to ensure that NDIS can correctly determine the binding and call us to bind
    // to miniports below.
    //
    NdisInitUnicodeString(&Name, L"ATMEPVCP");  // Protocol name
    PChars.Name = Name;
    PChars.OpenAdapterCompleteHandler = EpvcOpenAdapterComplete;
    PChars.CloseAdapterCompleteHandler = EpvcCloseAdapterComplete;
    PChars.SendCompleteHandler = NULL;
    PChars.TransferDataCompleteHandler = PtTransferDataComplete;
    
    PChars.ResetCompleteHandler = EpvcResetComplete;
    PChars.RequestCompleteHandler =     EpvcRequestComplete ;
    PChars.ReceiveHandler = PtReceive;
    PChars.ReceiveCompleteHandler = EpvcPtReceiveComplete;
    PChars.StatusHandler = EpvcStatus;
    PChars.StatusCompleteHandler = PtStatusComplete;
    PChars.BindAdapterHandler = EpvcBindAdapter;
    PChars.UnbindAdapterHandler = EpvcUnbindAdapter;
    PChars.UnloadHandler = NULL;
    PChars.ReceivePacketHandler = PtReceivePacket;
    PChars.PnPEventHandler= EpvcPtPNPHandler;
    PChars.CoAfRegisterNotifyHandler = EpvcAfRegisterNotify;
    PChars.CoSendCompleteHandler = EpvcPtSendComplete;  
    PChars.CoReceivePacketHandler = EpvcCoReceive;
    

    {
        //
        // Update client characteristis
        //
        PNDIS_CLIENT_CHARACTERISTICS    pNdisCC     = &(pGlobals->ndis.CC);

        NdisZeroMemory(pNdisCC, sizeof(*pNdisCC));
        pNdisCC->MajorVersion                   = EPVC_NDIS_MAJOR_VERSION;
        pNdisCC->MinorVersion                   = EPVC_NDIS_MINOR_VERSION;
        pNdisCC->ClCreateVcHandler              = EpvcClientCreateVc;
        pNdisCC->ClDeleteVcHandler              = EpvcClientDeleteVc;
        pNdisCC->ClRequestHandler               = EpvcCoRequest;
        pNdisCC->ClRequestCompleteHandler       = EpvcCoRequestComplete;
        pNdisCC->ClOpenAfCompleteHandler        = EpvcCoOpenAfComplete;
        pNdisCC->ClCloseAfCompleteHandler       = EpvcCoCloseAfComplete;
        pNdisCC->ClMakeCallCompleteHandler      = EpvcCoMakeCallComplete;
        pNdisCC->ClModifyCallQoSCompleteHandler = NULL;
        pNdisCC->ClIncomingCloseCallHandler     = EpvcCoIncomingClose;
        pNdisCC->ClCallConnectedHandler         = EpvcCoCallConnected;
        pNdisCC->ClCloseCallCompleteHandler     = EpvcCoCloseCallComplete;
        pNdisCC->ClIncomingCallHandler          = EpvcCoIncomingCall;

    }

    NdisRegisterProtocol(&Status,
                         &pGlobals->driver.ProtocolHandle,
                         &PChars,
                         sizeof(NDIS_PROTOCOL_CHARACTERISTICS));

    ASSERT(Status == NDIS_STATUS_SUCCESS);

    NdisMRegisterUnloadHandler(pGlobals->driver.WrapperHandle, 
                               EpvcUnload);

    ASSERT (pGlobals == &EpvcGlobals);                                     

    
    NdisIMAssociateMiniport(EpvcGlobals.driver.DriverHandle, pGlobals->driver.ProtocolHandle);


    EXIT()
        
    TRACE (TL_T, TM_Dr, ("<==epvcRegisterIMDriver  ") );

    return Status;

}


RM_STATUS
epvcDeRegisterIMDriver(
    PRM_OBJECT_HEADER               pObj,
    RM_RESOURCE_OPERATION           Op,
    PVOID                           pvUserParams,
    PRM_STACK_RECORD                psr
    )
{

    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;

    TRACE (TL_T, TM_Pt, ("== eovcDeRegisterIMDriver"));

    while (NdisStatus != NDIS_STATUS_SUCCESS)
    {
        NdisDeregisterProtocol(&NdisStatus, EpvcGlobals.driver.ProtocolHandle);
        NdisMSleep(1000);
    }   


    return NdisStatus;
}



void
DbgMark(UINT Luid)
{
    // do nothing useful, but do something specific, so that the compiler doesn't
    // alias DbgMark to some other function that happens to do nothing.
    //
    static int i;
    i=Luid;
}