/*++
*******************************************************************************
* Copyright (c) 1990-98 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:

   sohpnp.c

Abstract:

   This module contains NT5 Plug and Play routines.

Author:

   Jeff Romatoski

--*/
#include "digisoh.h"
#include "sohbuild.h"


#if NT5_BUILD

#include "xwan.h"

NTSTATUS
SohAddDevice( 
	IN PDRIVER_OBJECT pDriverObject,
	IN PDEVICE_OBJECT pPhysicalDeviceObject
	)
{

	UNICODE_STRING			Instance	= {0};
	UNICODE_STRING			DeviceName	= {0};
	UNICODE_STRING			Name		= {0};
	LARGE_INTEGER			Time		= {0};
	PVOID					pfdoExtension;
	NTSTATUS				Status;
	PDEVICE_OBJECT			pDeviceObject	= NULL;
	PSOH_DEV_EXT			pDevExt			= NULL;

#if DBG
	KeQuerySystemTime( &Time );
#endif

	// Construct the Device Name (i.e. DigiSoh0001)
	{
		WCHAR buf[16] = {0};
        ULONG bufLen = sizeof(buf);
		Status = DigiGetDeviceInstance( pPhysicalDeviceObject, buf, bufLen );
		if ( NT_SUCCESS( Status ) )
			Status = DigiStrAllocUnicodeString( &Instance, buf, NULL, NULL );
		if ( NT_SUCCESS( Status ) )
			Status = DigiStrAllocUnicodeString( &Name, WANLINKS_SOH_NAME_WIDE, buf, NULL );
	}
	
	DigiDump( DEBUG_DIGIPNP,
			 ( "SOH!SohAddDevice: time 0x%08x%08x, Device = %S, PDO device %08X\n",
							Time.HighPart, Time.LowPart,
							Name.Buffer,
							pPhysicalDeviceObject ) );

	InterlockedIncrement(&gSohRuntimeEnv.ulAdapterCount);

	// Let's create the FDO for this Device.  The size makes this driver specific.
	if ( NT_SUCCESS( Status ) )
	{
		Status = DigiStrDeviceName( &DeviceName, Name.Buffer );
		if ( NT_SUCCESS( Status ) )
		{
			Status = IoCreateDevice( pDriverObject,
									 sizeof *pDevExt,
									 &DeviceName,
									 FILE_DEVICE_DS,
									 0,
									 FALSE,
									 &pDeviceObject );

			DigiStrFreeUnicodeString( &DeviceName );
		}
		if ( NT_ERROR( Status ) )
		{
				DigiDump( DIGI_ERROR,
						 ( "SOH!SohAddDevice: CreateDevice Error, %S, PDO device %08X\n",
										DeviceName.Buffer, pPhysicalDeviceObject ) );
			goto Exit;
		}
	}

	if ( !NT_ERROR( Status ) )
	{
		// Now Create the PnP specific FDO Extension
		Status = DsPnpAddDevice(pDriverObject,
								pDeviceObject,
								pPhysicalDeviceObject,
								&Instance,
								&Name,
								&pfdoExtension);
	}

	if ( NT_ERROR( Status ) )
	{
		IoDeleteDevice(pDeviceObject);
		InterlockedDecrement(&gSohRuntimeEnv.ulAdapterCount);

		DigiDump( DIGI_ERROR,
				 ( "SOH!SohAddDevice: PNP Add Device Error, PDO device %08X\n",
								pPhysicalDeviceObject ) );
		goto Exit;
	}

	ASSERT(pfdoExtension);

	// Initialize the Device Extension
	pDeviceObject->Flags |= DO_BUFFERED_IO;

	pDevExt = ( PSOH_DEV_EXT ) pDeviceObject->DeviceExtension;

	pDevExt->PnpType				= PNP_DEVICE_TYPE_USER;
	pDevExt->Type					= SOH_OBJECT_TYPE_DEV_EXT;
	pDevExt->pfdoExtension			= pfdoExtension;

	pDevExt->ucName.Buffer			= pDevExt->aName;
	pDevExt->ucName.MaximumLength	= sizeof( pDevExt->aName );
	RtlCopyUnicodeString( &pDevExt->ucName, &Name );

	pDevExt->pDeviceObject = pDeviceObject;
	pDevExt->pDriverObject = pDriverObject;

	RtlInitUnicodeString( &pDevExt->ucSymbolicLinkName, NULL );

Exit:

	DigiDump( DEBUG_DIGIPNP, (	"SOH!SohAddDevice:	DriverObject 0x%x"
								"	                Device Object  0x%x\n"
								"					pExt 0x%x,\n"
								"					Status 0x%x\n",
								pDriverObject,
								pDeviceObject, 
	                            pDevExt, 
	                            Status ) );

	if ( Instance.Buffer )
		DigiStrFreeUnicodeString( &Instance );

	if ( Name.Buffer )
		DigiStrFreeUnicodeString( &Name );

	return Status;
}


NTSTATUS
SohDevicePower(
	IN PDEVICE_OBJECT pDeviceObject,
	IN PIRP           pIrp
	)
/*++

Description:

	The primary dispatch routine for IRP_MJ_POWER requests.

Parameters:

	Standard NT Dispatch routine parameters.

Return Value:

	STATUS_SUCCESS if request completed successfully.

--*/
{
	PNP_DEVICE_TYPE		PnpType	= *(PULONG)pDeviceObject->DeviceExtension;
	NTSTATUS			Status	= STATUS_UNSUCCESSFUL;
	PSOH_DEV_EXT		pDevExt;

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohDevicePower: Enter, pDeviceObject 0x%08x, pIrp 0x%08x\n", 
							pDeviceObject, pIrp ) );

	// We handle the irp differently depending on the target device.  If it's our Soh
	// adapter device (FDO), use PnpPowerFdo.  If it's a child device (PDO), use 
	// PnpPowerPdo.

	switch ( PnpType )
	{
		case PNP_DEVICE_TYPE_USER:
			pDevExt = pDeviceObject->DeviceExtension;
			Status = PnpPowerFdo( pDevExt->pfdoExtension, pIrp);
			break;

		case PNP_DEVICE_TYPE_PDO:
			Status = PnpPowerPdo( pDeviceObject, pIrp);
			break;

		default:
			DigiDump( DIGIERRORS, ( "*** ERROR: Power Irp received with bad device ext PnpType, pDeviceObject 0x%x,Type 0x%x! ***\n",
														pDeviceObject, PnpType ) );
			Status = STATUS_SUCCESS;
			DsPnpIoCompleteRequest( pIrp, Status, 0 );
			break;
	}

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohDevicePower: Leave, pDeviceObject 0x%08x, pIrp 0x%08x, Status 0x%x\n",
												pDeviceObject, pIrp, Status ) );
	return Status;
}


NTSTATUS
SohDevicePnp(
	IN PDEVICE_OBJECT pDeviceObject,
	IN PIRP           pIrp
	)

/*++

Description:

	The primary dispatch routine for IRP_MJ_PNP requests.

Parameters:

	Standard NT Dispatch routine parameters.

Return Value:

	STATUS_SUCCESS if request completed successfully.

--*/
{
	PNP_DEVICE_TYPE		PnpType	= *(PULONG)pDeviceObject->DeviceExtension;
	NTSTATUS			Status	= STATUS_UNSUCCESSFUL;
	PSOH_DEV_EXT		pDevExt;

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SOhDevicePnp: Enter, pDeviceObject 0x%08x, pIrp 0x%08x\n", 
													pDeviceObject, pIrp ) );

	// We handle the irp differently depending on the target device.  If it's our Soh
	// adapter device (FDO), use the FDO PnP Dispatch Routine, otherwise, if it
	// is a child PDO and we pass it off to the PnP PDO Dispatch routine.
	//
	// The Plug and play library will call back into Soh if a callback routine
	// has been defined for the specific IRP.  Callback routines are assigned 
	// when DsPnPInitialize is called.

	switch ( PnpType )
	{
		case PNP_DEVICE_TYPE_USER:
			pDevExt = pDeviceObject->DeviceExtension;
			Status = PnpFdoDispatch( pDevExt->pfdoExtension, pIrp);
			break;

		case PNP_DEVICE_TYPE_PDO:
			Status = PnpPdoDispatch( pDeviceObject, pIrp);
			break;

		default:
			DigiDump( DIGIERRORS, ( "*** ERROR: PNP Irp received with bad device ext PnpType, pDeviceObject 0x%x, (0x%x)! ***\n",
												pDeviceObject, PnpType ) );
			Status = STATUS_UNSUCCESSFUL;
			DsPnpIoCompleteRequest( pIrp, Status, 0 );
			break;
	}

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohDevicePnp: Leave, pDeviceObject 0x%08x, pIrp 0x%08x, Status 0x%x\n",
														pDeviceObject, pIrp, Status ) );
	return Status;
}



NTSTATUS
SohPnpStartDevice(
	IN PDEVICE_OBJECT		pDeviceObject,
	PDIGI_RESOURCE_LIST		pResources,
	HANDLE					hkDriver, 
	HANDLE					hkParameters, 
	PUNICODE_STRING			Option
	)
/*++

Description:

	Handle an IRP_MN_START_DEVICE Irp.  The PnP Manager sends this IRP after it has 
	assigned resources, if any, to the device. The device may have been recently 
	enumerated and is being started for the first time, or the device may be 
	restarting after being stopped for resource reconfiguration. 

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pResources
		Adapter Resources.
	
	hkDriver
		Handle to the Driver Registry Key.

	hkParameters
		Handle to the Driver Parameters Registry Key.

	Option
		The Option for this line

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS		Status;
	ULONG			nLines;
	PSOH_DEV_EXT	pDevExt	= pDeviceObject->DeviceExtension; 

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohPnpStartDevice: Enter, pDeviceObject 0x%08x\n",
														pDeviceObject ) );

	// Find and call the adapter startup routine based on the Option found.
	Status = STATUS_UNSUCCESSFUL;

	// Get the Line Count for this Adapter
	Status = DigiRegQueryDword( hkParameters, DXB_REGVAL_NUMBEROFLINES, &nLines, DigiRegFlagRequired, 0 );
	if ( !NT_SUCCESS( Status ) )
	{
		return Status;
	}

	// Start the Minport Adapter for this Option.
	Status = XwanStartAdapter(Option->Buffer,
							  pDevExt->pDriverObject,
							  pDevExt->pDeviceObject,
							  &pDevExt->ucName,
							  pResources,
							  nLines,
							  &pDevExt->pXwanAdapter
							  );

	if ( NT_SUCCESS( Status ) )
	{

		ULONG			i;

		// Register the Ports with the Wan Layer 
		for ( i = 0; i < nLines; i++ )
		{
			ULONG ulSnid = 0;
			HANDLE hkLine = NULL;
			UNICODE_STRING	strLine;
			WCHAR LineBuffer[8];
			strLine.Buffer = LineBuffer;
			strLine.Length = 0;
			strLine.MaximumLength = sizeof LineBuffer;

			RtlIntegerToUnicodeString(i,10,&strLine);
       		Status = DigiRegOpenSubkey( &hkLine, hkParameters, strLine.Buffer );
			if ( NT_SUCCESS(Status) )
			{
				Status = DigiRegQueryDword( hkLine, DXB_REGVAL_LINE_SNID, &ulSnid, DigiRegFlagRequired, 0 );
			}

			if ( NT_SUCCESS( Status ) )
			{
				// Register the Miniport
				Status = XwanRegisterMiniport( pDevExt->pXwanAdapter,i,ulSnid);
			}

			if ( NT_SUCCESS( Status ) )
			{
				// Build the Controlling Streams for the line
				Status = DigiSohCreateControllingStreams( pDevExt->pDeviceObject,pDevExt,i,hkLine);
			}

			if (hkLine)
			{
				DigiRegCloseKey( hkLine );
			}

			if ( !NT_SUCCESS( Status ) )
			{
				SohPnpStopDevice(pDevExt->pDeviceObject);
				break;
			}

		}

	}

	// Create Symbolic link
	if ( NT_SUCCESS( Status ) )
	{

		UNICODE_STRING DeviceName   = {0};
		UNICODE_STRING SymbolicName = {0};

		Status = DigiStrDeviceName( &DeviceName, pDevExt->ucName.Buffer );
		if ( NT_SUCCESS( Status ) )
		{
			Status = DigiStrSymbolicName( &SymbolicName, pDevExt->ucName.Buffer );
			if ( NT_SUCCESS( Status ) )
			{
				DigiDump( DEBUG_DIGIPNP, ( "SOH!SohPnpStartDevice: Synmbolic Link = %S\n",SymbolicName.Buffer) );

				// Create a symbolic link name for this device
				Status = DigiStrAllocUnicodeString( &pDevExt->ucSymbolicLinkName, SymbolicName.Buffer, NULL, NULL );

				if ( NT_SUCCESS( Status ) )
				{

					(void)IoCreateSymbolicLink( &SymbolicName, &DeviceName );
					DigiStrFreeUnicodeString( &SymbolicName );
					IoRegisterShutdownNotification( pDevExt->pDeviceObject );

				}

			}
			DigiStrFreeUnicodeString( &DeviceName );
		}

	}

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohPnpStartDevice: Leave, pDeviceObject 0x%08x, Status 0x%x\n",
														pDeviceObject, Status ) );

	return Status;
}


NTSTATUS
SohPnpStopDevice(
	IN PDEVICE_OBJECT pDeviceObject
	)
/*++

Description:

	Stop the DigiSoh adapter.   Reset the card, release resources.

Parameters:

	pExt
		Device extension defining adapter

Return Value:

	None

--*/
{

	NTSTATUS Status = STATUS_SUCCESS;

	PSOH_DEV_EXT pDevExt	= pDeviceObject->DeviceExtension; 

	// ShutDown the Controlling Streams
	DigiSohCloseControllingStreams(pDevExt);

	if (pDevExt->pXwanAdapter)
	{

		// Deregister the MiniPorts
		XwanDeregisterMiniports(pDevExt->pXwanAdapter);

		// Stop the Miniport Adapter
		XwanStopAdapter(pDevExt->pXwanAdapter);

		pDevExt->pXwanAdapter = NULL;

	}

	IoUnregisterShutdownNotification( pDeviceObject );

	return Status;

}


NTSTATUS
SohPnpRemoveDevice(
	IN PDEVICE_OBJECT pDeviceObject
	)
/*++

Description:

	Stop the DigiSoh adapter.   Reset the card, release resources.

Parameters:

	pExt
		Device extension defining adapter

Return Value:

	None

--*/
{

	NTSTATUS Status = STATUS_SUCCESS;

	PSOH_DEV_EXT pDevExt	= pDeviceObject->DeviceExtension; 

	// ShutDown the Controlling Streams
	DigiSohCloseControllingStreams(pDevExt);

	if (pDevExt->pXwanAdapter)
	{

		// Deregister the MiniPorts
		XwanDeregisterMiniports(pDevExt->pXwanAdapter);

		// Stop the Miniport Adapter
		XwanStopAdapter(pDevExt->pXwanAdapter);

		pDevExt->pXwanAdapter = NULL;

	}

	if (pDevExt->ucSymbolicLinkName.Buffer) {

		// Delete our Symbolic Link
		Status = IoDeleteSymbolicLink( &pDevExt->ucSymbolicLinkName );

		// Clean up the Device Extension Resources
		DigiStrFreeUnicodeString( &pDevExt->ucSymbolicLinkName );

	}

	// Delete the Device
	IoDeleteDevice( pDevExt->pDeviceObject );

	InterlockedDecrement(&gSohRuntimeEnv.ulAdapterCount);

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohPnpRemoveDevice: Leave, pDeviceObject 0x%08x, Status 0x%x\n",
														pDeviceObject, Status ) );

	return Status;

}

NTSTATUS
SohPnpQueryRemoveDevice(
	IN PDEVICE_OBJECT pDeviceObject
	)
/*++

Description:

	Don't allow the remove if multiple adapters are in the system.  The firmware
	only allows on controlling streams per firmware image.  The only way to
	free streams resources is to close the controlling stream, which would close
	all the resources for all adapters.  We don't allow the device to be removed
	if there are multiple adapters.  A reboot is required for any configuration
	changes when multiple SYNC/570's are in the system.

Parameters:

	pExt
		Device extension defining adapter

Return Value:

	None

--*/
{

	NTSTATUS Status = STATUS_SUCCESS;

	if (gSohRuntimeEnv.ulAdapterCount > 1)
	{
		Status = STATUS_UNSUCCESSFUL;
	}

	DigiDump( DEBUG_DIGIPNP, ( "SOH!SohPnpQueryRemoveDevice: Leave, pDeviceObject 0x%08x, Status 0x%x\n",
														     pDeviceObject, Status ) );

	return Status;

}

#endif  // NT5_BUILD
