/*++
*******************************************************************************
* 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');

#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.
	DigiDebugInit( pRegistryPath, DIGIINIT|DIGIPNP|DIGIPOWER|DIGICREATE|DIGICLOSE|DIGIUNLOAD|DIGIWARN|DIGIERROR|DIGIBREAK, DEBUGMODE_CONSOLE );
	// Handy debug spew.  You can always trust these pointers.
#if DBG   
	(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;

	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 );
		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;
}
