/*++
*******************************************************************************
* Copyright (c) 1999-2000 Digi International Inc., All Rights Reserved        *
*                                                                             *
* This software contains proprietary and confidential information of Digi     *
* International Inc.  By accepting transfer of this copy, Recipient agrees    *
* to retain this software in confidence, to prevent disclosure to others,     *
* and to make no use of this software other than that for which it was        *
* delivered.  This is an unpublished copyrighted work of Digi International   *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly   *
* prohibited.                                                                 *
*                                                                             *
* Restricted Rights Legend                                                    *
*                                                                             *
* Use, duplication, or disclosure by the Government is subject to             *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in         *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or        *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -          *
* Restricted Rights at 48 CFR 52.227-19, as applicable.                       *
*                                                                             *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343          *
*                                                                             *
*******************************************************************************

Module Name:

   digiasyn.c

Abstract:

	Serial (async) driver for DXB-based adapters, e.g. Houston or Hercules.  This 
	driver communicates with the S/DXB firmware module.

Author:

   Michael Zarns

--*/

#include "digiasyn.h"
#include "digidga.h"



PDRIVER_OBJECT gpDriverObject = NULL;
UNICODE_STRING gDriverRegPath = {0};
LIST_ENTRY     gDeviceExtList = {0};

// Default pool tag for ExAllocatePoolWithTag memory allocations
ULONG DefaultPoolTag = (ULONG)('igiD');

extern WCHAR gAsynverVersion[];

#if NT5_BUILD

VOID
AsynDeleteGlobalCardExt()
{
    UNICODE_STRING		symName;

#define ASYNSYMBOLICNAME L"\\DosDevices\\DigiAsyn2dj"
	
    RtlInitUnicodeString(&symName, ASYNSYMBOLICNAME);

    if (GlobalDgaObject)
    {
        IoDeleteSymbolicLink(&symName);
        IoDeleteDevice(GlobalDgaObject);
        GlobalDgaObject = NULL;
    }
}


NTSTATUS
AsynCreateGlobalCardExt( PDRIVER_OBJECT DriverObject )
/*
	Create global DeviceObject and SymbolicLink for dgamode.
*/
{
    NTSTATUS		Status;
    PDEVICE_OBJECT	pDevice;
    PASYN_PORT_EXT	pCardExt = NULL;
    UNICODE_STRING	devName;
    UNICODE_STRING	symName;

#define ASYNSYMBOLICNAME L"\\DosDevices\\DigiAsyn2dj"
#define ASYNDEVICENAME   L"\\Device\\DigiAsyn2dj"

    RtlInitUnicodeString(&devName, ASYNDEVICENAME);
    Status = IoCreateDevice(DriverObject, sizeof(ASYN_PORT_EXT), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevice);

    if ( NT_SUCCESS(Status) )
    {
        pDevice->Flags &= ~DO_DEVICE_INITIALIZING;
        pCardExt = pDevice->DeviceExtension;

        RtlInitUnicodeString(&symName, ASYNSYMBOLICNAME);
        Status = IoCreateSymbolicLink( &symName, &devName );
    }
    else
        pDevice = NULL;

    GlobalDgaObject = pDevice;
    return Status;
}
#endif

STATIC VOID
DriverUnload(
	PDRIVER_OBJECT pDriverObject )
/*++

Description:

	Last chance to cleanup before driver is unloaded.  Everything alloc'd, created,
	or started in DriverEntry or otherwise should be freed, destroyed or stopped.

Parameters:

	pDriverObject
		The driver object for this driver.

--*/
{
    UNREFERENCED_PARAMETER( pDriverObject );
    DigiDump( DIGIINIT,  ( "Asyn!DriverUnload\n" ) );

#if !NT5_BUILD
    AsynDestructAllPorts();
#endif

    DigiStrFreeUnicodeString( &gDriverRegPath );

    MemPrintQuit();
    DigiDisplayMemoryLeaks();
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT  DriverObject, IN PUNICODE_STRING pRegistryPath );
#pragma alloc_text( INIT, DriverEntry )
NTSTATUS
DriverEntry(
	IN PDRIVER_OBJECT  DriverObject,
	IN PUNICODE_STRING pRegistryPath )
/*++

Routine Description:

   Driver main entry point.  NT calls this routine when loading the driver.

Arguments:

   DriverObject
		Defines this driver

   pRegistryPath
   	Unicode string which defines the registry path for this driver's service key.
		Typically, ...\ControlSetXXX\Services\DigiDxb.

Return Value:

   STATUS_SUCCESS - If the driver was successfully loaded, or error.

--*/
{
    NTSTATUS Status;
    HANDLE hkDriver = NULL;

    // Init globals first!
    InitializeListHead( &gDeviceExtList );
    gpDriverObject = DriverObject;
    GlobalDgaObject = NULL;

    DigiInitMem( 'nysA' );	// "Asyn" backwards
    MemPrintInitialize( "\\SystemRoot\\digiasyn.log" );

    // DebugInit initializes three global values from registry settings:
    //    - gDebugMask 
    //    - gDebugMode
    //    - gDebugBreak (this one is private to the lib)
    // all defined in digilib.  The lib reads DebugMask, DebugMode, DebugBreak
    // from the registry key pRegistryPath.  Defaults are passed into DebugInit,
    // DebugBreak defaults to FALSE.
    DigiDebugInitEx( pRegistryPath, 
                     DIGIINIT|DIGIPNP|DIGIPOWER|DIGICREATE|DIGICLOSE|DIGIUNLOAD|DIGIWARN|DIGIERROR|DIGIBREAK, 
                     DEBUGMODE_CONSOLE,
                     "sraasyn", gAsynverVersion );

#if DBG   
    // Handy debug spew.  You can always trust these pointers.
    (void)DbgPrint( "\n"
                    "Asyn!DriverEntry:\n"
                    "   gDebugMask      = 0x%x\n"
                    "   gDebugMode      = 0x%x\n\n",
                    &gDebugMask, &gDebugMode );
#endif

    // Wanna break on entry to step through code?  Set the DebugBreak regval
    // the driver registry key.
    {
        if( DigiDbgBreakOnEntryEnabled() != 0 )
            DbgBreakPoint();
    }

    // Stash away a copy of the driver registry path, in case we need it later.
    Status = DigiStrAllocUnicodeString( &gDriverRegPath, pRegistryPath->Buffer, NULL, NULL );
    if ( NT_ERROR( Status ) )
        goto Exit;

    Status = DigiRegOpenKey( &hkDriver, pRegistryPath->Buffer );
    if ( NT_ERROR( Status ) )
        goto Exit;

    DriverObject->DriverUnload                          = DriverUnload;
    DriverObject->MajorFunction[IRP_MJ_CREATE]          = AsynDeviceCreate;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]           = AsynDeviceClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = AsynDeviceIoControl;
    DriverObject->MajorFunction[IRP_MJ_CLEANUP]         = AsynDeviceCleanup;
    DriverObject->MajorFunction[IRP_MJ_READ]            = AsynDeviceRead;
    DriverObject->MajorFunction[IRP_MJ_WRITE]           = AsynDeviceWrite;
    DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]   = AsynDeviceFlushBuffers;
#ifdef DRIVER_GOOD_TO_GO
    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]        = AsynDeviceShutdown;
#endif

#if NT5_BUILD 
    DriverObject->MajorFunction[IRP_MJ_PNP]             = AsynDevicePnp;
    DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL]
        = AsynDeviceInternalIoControl;
    DriverObject->MajorFunction[IRP_MJ_POWER]           = AsynPowerDispatch;
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]  = AsynSystemControl;
	
    DriverObject->DriverExtension->AddDevice            = AsynAddDevice;

    Status = AsynCreateGlobalCardExt( DriverObject );
    if ( NT_ERROR(Status) )
        AsynDeleteGlobalCardExt();


#else
    Status = AsynConstructAllPorts( hkDriver );
#endif

 Exit:

    if ( hkDriver )
        DigiRegCloseKey( hkDriver );

    if( NT_ERROR( Status ) )
    {
        DigiStrFreeUnicodeString( &gDriverRegPath );
        MemPrintQuit();
        DigiDisplayMemoryLeaks();
    }

    DigiDump( DIGIINIT, ( "Asyn!DriverEntry: Leave, Status 0x%08x\n", Status ) );
    return Status;
}
/*******************************************************************************************************
**
**	VOID AsynIoCompleteRequest( IN PIRP pIrp, IN NTSTATUS Status )
**
**	Purpose: This will complete the irp request, but the main purpose is to check if there are
**		any tracing irps waiting.  If there are then we will spew back data to the calling app.
**
**	Given:
**
********************************************************************************************************/
VOID  AsynIoCompleteRequest( PIRP pIrp, NTSTATUS Status, PASYN_PORT_EXT pPortExt )
{
    PIO_STACK_LOCATION		currentIrpStack = IoGetCurrentIrpStackLocation( pIrp );

    DigiDump( DIGIFLOW, ("DigiAsyn Irp %p completing with status=0x%x\n",pIrp,Status));

    pIrp->IoStatus.Status = Status;

    SRAPRINTIRPCOMPLETIONEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",NULL,pIrp,FALSE));

    if ( (!pPortExt) || (pPortExt->Type != ASYN_TYPE_PORT_EXT) )
    {
        IoCompleteRequest( pIrp, IO_NO_INCREMENT );
        return;
    }

    DigiAcquireSpinLock( &pPortExt->Trace.TraceLock );

    if ( IsListEmpty( &pPortExt->Trace.TraceQueue ) )
    {
        DigiReleaseSpinLock( &pPortExt->Trace.TraceLock );

        if (SRACHECKLEVEL2(DBGTYPE_IRP,DBGLVL_DETAILED) &&
            currentIrpStack->MajorFunction == IRP_MJ_READ &&
            pIrp->AssociatedIrp.SystemBuffer &&
            pIrp->IoStatus.Information) {
            ULONG Length = pIrp->IoStatus.Information;

            if (Length > 0x20)
                Length = 0x20;

            SraDbgPrintBufferEx("    ",0,1,pIrp->AssociatedIrp.SystemBuffer,Length);
        }

        IoCompleteRequest( pIrp, IO_NO_INCREMENT );
        return;
    }

    DigiReleaseSpinLock( &pPortExt->Trace.TraceLock );

    switch ( currentIrpStack->MajorFunction )
    {
    case IRP_MJ_READ:
    {
        AsynTraceReadIrp( pPortExt, pIrp );
        break;
    }
    case IRP_MJ_WRITE:
    {
        AsynTraceWriteIrp( pPortExt, pIrp );
        break;
    }
    case IRP_MJ_DEVICE_CONTROL:
    {
        AsynTraceIoctlIrp( pPortExt, pIrp, FALSE, FALSE );
        break;
    }
    default:
        DigiDump( DIGIFLOW, ("Asyn!CompleteIrp: Complete IRP = %p\n", pIrp ));
        break;
    }

    IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    return;
}
