/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    autodial.c

Abstract:

    NT specific routines for interfacing with the
    RAS AutoDial driver (rasacd.sys).

Author:

    Anthony Discolo (adiscolo)     Aug 30, 1995

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    adiscolo    08-30-95    created

Notes:

--*/

#include "precomp.h"
#pragma hdrstop

#ifdef RASAUTODIAL

#include <acd.h>
#include <acdapi.h>

//
// Global variables
//
BOOLEAN fAcdLoadedG;
ACD_DRIVER AcdDriverG;
ULONG ulDriverIdG = 'Nbi ';



VOID
NbiRetryTdiConnect(
    IN BOOLEAN fSuccess,
    IN PVOID *pArgs
    )

/*++

Routine Description:

    This routine is called indirectly by the automatic
    connection driver to continue the connection process
    after an automatic connection has been made.

Arguments:

    fSuccess - TRUE if the connection attempt was successful.

    pArgs - a pointer to the argument vector

Return Value:

    None.

--*/

{
    NTSTATUS status;
    PDEVICE pDevice = pArgs[0];
    PCONNECTION pConnection = pArgs[1];
    PREQUEST pRequest = pArgs[2];
    CTELockHandle ConnectionLH, DeviceLH;
    CTELockHandle CancelLH;
    BOOLEAN bLockFreed = FALSE;

    //
    // Check that the connection is valid. This references
    // the connection.
    //
#if notdef // DBG
    DbgPrint("NbiRetryTdiConnect: fSuccess=%d, pConnection=0x%x\n", fSuccess, pConnection);
#endif

    status = NbiVerifyConnection(pConnection);
    if (!NT_SUCCESS(status)) {
        DbgPrint(
          "NbiRetryTdiConnect: NbiVerifyConnection failed on connection 0x%x (status=0x%x)\n",
          pConnection,
          status);
        return;
    }

    NB_GET_CANCEL_LOCK( &CancelLH );
    NB_GET_LOCK (&pConnection->Lock, &ConnectionLH);
    NB_GET_LOCK (&pDevice->Lock, &DeviceLH);

#if notdef // DBG
    DbgPrint(
      "NbiRetryTdiConnect: AddressFile=0x%x, DisassociatePending=0x%x, ClosePending=0x%x\n",
      pConnection->AddressFile,
      pConnection->DisassociatePending,
      pConnection->ClosePending);
#endif

    if ((pConnection->AddressFile != NULL) &&
        (pConnection->AddressFile != (PVOID)-1) &&
        (pConnection->DisassociatePending == NULL) &&
        (pConnection->ClosePending == NULL))
    {
        NbiReferenceConnectionLock(pConnection, CREF_CONNECT);
        //
        // Clear the AUTOCONNECTING flag since we
        // done with the automatic connection attempt.
        // Set the AUTOCONNECTED flag to prevent us
        // from attempting an automatic connection
        // for this connection again.
        //
        pConnection->Flags &= ~CONNECTION_FLAGS_AUTOCONNECTING;
        pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTED;

        pConnection->State = CONNECTION_STATE_CONNECTING;
        pConnection->Retries = pDevice->ConnectionCount;
        status = NbiTdiConnectFindName(
                   pDevice,
                   pRequest,
                   pConnection,
                   CancelLH,
                   ConnectionLH,
                   DeviceLH,
                   &bLockFreed);
    }
    else {
        DbgPrint("NbiRetryTdiConnect: Connect on invalid connection 0x%x\n", pConnection);

        pConnection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
        NB_FREE_LOCK (&pDevice->Lock, DeviceLH);
        status = STATUS_INVALID_CONNECTION;
    }
    if (!bLockFreed) {
        NB_FREE_LOCK (&pConnection->Lock, ConnectionLH);
        NB_FREE_CANCEL_LOCK(CancelLH);
    }
    //
    // Complete the irp if necessary.
    //
    if (status != STATUS_PENDING) {
        REQUEST_INFORMATION(pRequest) = 0;
        REQUEST_STATUS(pRequest) = status;

        NbiCompleteRequest(pRequest);
        NbiFreeRequest(pDevice, pRequest);
    }
    NbiDereferenceConnection(pConnection, CREF_VERIFY);
} /* NbiRetryTdiConnect */



BOOLEAN
NbiCancelAutoDialRequest(
    IN PVOID pArg,
    IN ULONG ulFlags,
    IN ACD_CONNECT_CALLBACK pProc,
    IN USHORT nArgs,
    IN PVOID *pArgs
    )
{
#if notdef // DBG
    DbgPrint("NbiCancelAutodialRequest: pArg=0x%x\n", pArg);
#endif
    if (nArgs != 2)
        return FALSE;

    return (pArgs[1] == pArg);
} // NbiCancelAutoDialRequest



BOOLEAN
NbiCancelTdiConnect(
    IN PDEVICE pDevice,
    IN PREQUEST pRequest,
    IN PCONNECTION pConnection
    )

/*++

DESCRIPTION
    This routine is called by the I/O system to cancel a connection
    when we are attempting to restore an automatic connection.

ARGUMENTS
    pDevice: a pointer to the device object for this driver

    pRequest: a pointer to the irp to be cancelled

    pConnection: a pointer to the connnection to be cancelled

RETURN VALUE
    TRUE if the request was canceled; FALSE otherwise.

--*/

{
    ACD_ADDR addr;

    //
    // Get the address of the connection.
    //
    addr.fType = ACD_ADDR_NB;
    RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16);
#ifdef notdef // DBG
    DbgPrint(
      "NbiCancelTdiConnect: pIrp=0x%x, RemoteName=%-15.15s, pConnection=0x%x\n",
      pRequest,
      addr.cNetbios,
      pConnection);
#endif
    //
    // Cancel the autodial request.
    //
    return (*AcdDriverG.lpfnCancelConnection)(
              ulDriverIdG,
              &addr,
              NbiCancelAutoDialRequest,
              pConnection);
} // NbiCancelTdiConnect



BOOLEAN
NbiAttemptAutoDial(
    IN PDEVICE pDevice,
    IN PCONNECTION pConnection,
    IN ULONG ulFlags,
    IN ACD_CONNECT_CALLBACK pProc,
    IN PREQUEST pRequest
    )

/*++

Routine Description:

    Call the automatic connection driver to attempt an
    automatic connection.

Arguments:

    pDevice - a pointer to the DEVICE structure for this connection

    pConnection - a pointer to the CONNECTION block for this connection

    ulFlags - connection flags to pass to the automatic
        connection driver

    pProc - a callback procedure when the automatic connection completes

    pRequest - a pointer to the request irp

Return Value:

    TRUE if the automatic connection was started successfully,
    FALSE otherwise.

--*/

{
    ACD_ADDR addr;
    PVOID pArgs[3];
    BOOLEAN bSuccess;

    //
    // If we've already attempted an automatic connection
    // on this connection, don't try it again.
    //
    if (pConnection->Flags & CONNECTION_FLAGS_AUTOCONNECTED)
        return FALSE;
    //
    // Get the address of the connection.
    //
    addr.fType = ACD_ADDR_NB;
    RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16);
#ifdef notdef // DBG
    DbgPrint("NbiAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios);
#endif
    //
    // Attempt to start the connection.
    // NbiRetryTdiConnect() will be called
    // when the connection process has completed.
    //
    pArgs[0] = pDevice;
    pArgs[1] = pConnection;
    pArgs[2] = pRequest;
    bSuccess = (*AcdDriverG.lpfnStartConnection)(
                  ulDriverIdG,
                  &addr,
                  ulFlags,
                  pProc,
                  3,
                  pArgs);
    if (bSuccess) {
        //
        // Set the AUTOCONNECTING flag so we know
        // to also cancel the connection in the
        // automatic connection driver if this
        // request gets canceled.
        //
        pConnection->Flags |= CONNECTION_FLAGS_AUTOCONNECTING;
    }

    return bSuccess;

} // NbiAttemptAutoDial



VOID
NbiNoteNewConnection(
    IN PCONNECTION pConnection
    )
{
    NTSTATUS status;
    ACD_ADDR addr;
    ACD_ADAPTER adapter;
    ULONG i;
    TDI_ADDRESS_IPX tdiIpxAddress;

    addr.fType = ACD_ADDR_NB;
    RtlCopyMemory(&addr.cNetbios, pConnection->RemoteName, 16);
    //
    // Determine the mac address of the adapter
    // over which the connection has been made.
    //
    status = (pConnection->Device->Bind.QueryHandler)(
               IPX_QUERY_IPX_ADDRESS,
#if defined(_PNP_POWER)
               &pConnection->LocalTarget.NicHandle,
#else
               pConnection->LocalTarget.NicId,
#endif _PNP_POWER
               &tdiIpxAddress,
               sizeof(TDI_ADDRESS_IPX),
               NULL);
    if (status != STATUS_SUCCESS) {
#if notdef // DBG
        DbgPrint("NbiNoteNewConnection: QueryHandler(IPX_QUERY_IPX_ADDRESS) failed (status=0x%x)\n", status);
        return;
#endif
    }
    //
    // Copy the source mac address to identify
    // the adapter.
    //
    adapter.fType = ACD_ADAPTER_MAC;
    for (i = 0; i < 6; i++)
        adapter.cMac[i] = tdiIpxAddress.NodeAddress[i];
#if notdef // DBG
    DbgPrint(
      "NbiNoteNewConnection: address=%-15.15s, remote mac=%02x:%02x:%02x:%02x:%02x:%02x\n",
      addr.cNetbios,
      adapter.cMac[0],
      adapter.cMac[1],
      adapter.cMac[2],
      adapter.cMac[3],
      adapter.cMac[4],
      adapter.cMac[5]);
#endif
    //
    // Simply notify the automatic connection driver
    // that a successful connection has been made.
    //
    (*AcdDriverG.lpfnNewConnection)(
        &addr,
        &adapter);
} // NbiNoteNewConnection



VOID
NbiAcdBind()
{
    NTSTATUS status;
    UNICODE_STRING nameString;
    IO_STATUS_BLOCK ioStatusBlock;
    PIRP pIrp;
    PFILE_OBJECT pAcdFileObject;
    PDEVICE_OBJECT pAcdDeviceObject;
    PACD_DRIVER pDriver = &AcdDriverG;

    //
    // Initialize the name of the automatic
    // connection device.
    //
    RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME);
    //
    // Get the file and device objects for the
    // device.
    //
    status = IoGetDeviceObjectPointer(
               &nameString,
               SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
               &pAcdFileObject,
               &pAcdDeviceObject);
    if (status != STATUS_SUCCESS)
        return;
    //
    // Reference the device object.
    //
    ObReferenceObject(pAcdDeviceObject);
    //
    // Remove the reference IoGetDeviceObjectPointer()
    // put on the file object.
    //
    ObDereferenceObject(pAcdFileObject);
    //
    // Initialize our part of the ACD_DRIVER
    // structure.
    //
    KeInitializeSpinLock(&AcdDriverG.SpinLock);
    AcdDriverG.ulDriverId = ulDriverIdG;
    AcdDriverG.fEnabled = FALSE;
    //
    // Build a request to get the automatic
    // connection driver entry points.
    //
    pIrp = IoBuildDeviceIoControlRequest(
             IOCTL_INTERNAL_ACD_BIND,
             pAcdDeviceObject,
             (PVOID)&pDriver,
             sizeof (pDriver),
             NULL,
             0,
             TRUE,
             NULL,
             &ioStatusBlock);
    if (pIrp == NULL) {
        ObDereferenceObject(pAcdDeviceObject);
        return;
    }
    //
    // Submit the request to the
    // automatic connection driver.
    //
    status = IoCallDriver(pAcdDeviceObject, pIrp);
    fAcdLoadedG = (status == STATUS_SUCCESS);
    //
    // Close the device.
    //
    ObDereferenceObject(pAcdDeviceObject);
} // NbiAcdBind



VOID
NbiAcdUnbind()
{
    NTSTATUS status;
    UNICODE_STRING nameString;
    IO_STATUS_BLOCK ioStatusBlock;
    PIRP pIrp;
    PFILE_OBJECT pAcdFileObject;
    PDEVICE_OBJECT pAcdDeviceObject;
    PACD_DRIVER pDriver = &AcdDriverG;

    //
    // Don't bother to unbind if we
    // didn't successfully bind in the
    // first place.
    //
    if (!fAcdLoadedG)
        return;
    //
    // Initialize the name of the automatic
    // connection device.
    //
    RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME);
    //
    // Get the file and device objects for the
    // device.
    //
    status = IoGetDeviceObjectPointer(
               &nameString,
               SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
               &pAcdFileObject,
               &pAcdDeviceObject);
    if (status != STATUS_SUCCESS)
        return;
    //
    // Reference the device object.
    //
    ObReferenceObject(pAcdDeviceObject);
    //
    // Remove the reference IoGetDeviceObjectPointer()
    // put on the file object.
    //
    ObDereferenceObject(pAcdFileObject);
    //
    // Build a request to unbind from
    // the automatic connection driver.
    //
    pIrp = IoBuildDeviceIoControlRequest(
             IOCTL_INTERNAL_ACD_UNBIND,
             pAcdDeviceObject,
             (PVOID)&pDriver,
             sizeof (pDriver),
             NULL,
             0,
             TRUE,
             NULL,
             &ioStatusBlock);
    if (pIrp == NULL) {
        ObDereferenceObject(pAcdDeviceObject);
        return;
    }
    //
    // Submit the request to the
    // automatic connection driver.
    //
    status = IoCallDriver(pAcdDeviceObject, pIrp);
    //
    // Close the device.
    //
    ObDereferenceObject(pAcdDeviceObject);
} // NbiAcdUnbind

#endif // RASAUTODIAL