/*++

*****************************************************************************
*                                                                           *
*  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.                                                              *
*                                                                           *
*****************************************************************************

Module Name:

   digix25.c

Abstract:

   This module is responsible for interfacing NDIS 3.x to the DigiSXB
   interface.  It impliments the expected NDIS 3.x interface.

--*/
#include <ntddk.h>
#include <ndis.h>
#include <ndiswan.h>

#include "buildctrl.h"
#include "DsApi.h"
#include "digix25.h"
#include "digix25p.h"
#include "digifile.h"
#include <digialloc.h>

#define SERVICE_NAME          NDIS_STRING_CONST("ServiceName")
#define MAXSENDWINDOW         NDIS_STRING_CONST("MaxSendWindow")

#define TAPIDEVICES           NDIS_STRING_CONST("Tapi Devices")
#define ADDRESS               NDIS_STRING_CONST("Address")
#define MEDIA_TYPE            NDIS_STRING_CONST("Media Type")

#define REGKEY_DEVICECOUNT    NDIS_STRING_CONST("NumRASPorts")

#define UNICODE_NULLSTRING    {0, 0, NULL}

//
// General function prototypes
//
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject,
                      IN PUNICODE_STRING RegistryPath );

//
// NDIS Miniport driver entry point function prototypes
//
NDIS_STATUS DigiX25Initialize( OUT PNDIS_STATUS OpenErrorStatus,
                               OUT PUINT SelectedMediumIndex,
                               IN PNDIS_MEDIUM MediumArray,
                               IN UINT MediumArraySize,
                               IN NDIS_HANDLE MiniportAdapterHandle,
                               IN NDIS_HANDLE ConfigurationHandle );

VOID DigiX25Halt( IN NDIS_HANDLE MiniportAdapterContext );

//
// DigiX25 Helper function prototypes
//

VOID UpdateRegistry( PUNICODE_STRING RegistryPath );

NDIS_STATUS DigiCreatePhysAdapter( OUT PDIGIWAN_PHYS_ADAPTER *pPhysAdapter,
                                   IN PUNICODE_STRING Device,
                                   IN PDIGIWAN_ADAPTER_OBJECT AdapterObject );

//
// Global Variables
//

/*
  * TODO - Fix this Stuff - Parag
  * *
  */



ULONG	DigiPrintFlags;
ULONG	TurnOffSniffer;



PDIGIWAN_DRIVER_OBJECT DigiX25DriverObject=NULL;

ULONG DigiDebugLevel=( DIGIMAKECALL | 
                       DIGIDROP | 
                       DIGICLOSECALL | 
					   DIGITAPIEVENT | 
					   DIGITAPIFLOW | 
                       DIGIERRORS );

ULONG DigiTxElapsed = 0;	// 0:  Don't check for Tx elapsed time
									// >0: Dump if Tx time >= DigiTxElapsed time in mS

ULONG DigiDontLoadDriver = FALSE;
ULONG NdisTxBenchmark = 0; // This to benchmark NDISWAN only Tx path

const PHYSICAL_ADDRESS DigiPhysicalZero = {0};
extern NDIS_SPIN_LOCK SendStatusLock;

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

Routine Description:

    This is the primary initialization routine for the DigiX25 driver.
    It is simply responsible for the intializing the wrapper and registering
    the Miniport driver.  It then calls a system and architecture specific
    routine that will initialize and register each adapter.

    We determine if the SXB driver is accessible and get everything ready
    to communicate with it.

Arguments:

    DriverObject - Pointer to driver object created by the system.

Return Value:

    The status of the operation.

--*/
{
   NDIS_STATUS Status;
   NDIS_WAN_MINIPORT_CHARACTERISTICS X25WanChar;
   RTL_QUERY_REGISTRY_TABLE paramTable[6];
   ULONG zero = 0;
   ULONG debugLevel;
   ULONG shouldBreak = 0;
   PWCHAR path;
//   ULONG defaultDigiPrintFlags=MEM_PRINT_FLAG_CONSOLE;
	ULONG defaultDigiPrintFlags=0;
   UCHAR defaultTurnOffSniffer=1;
	ULONG defaultDigiTxElapsed = 0;

   DigiDump( DIGIFLOW, ("Enter DriverEntry\n") );

   DigiInitMem( '52xD' );	  // 'Dx25' is our memory tag

   if( path = DigiAllocMem( PagedPool, RegistryPath->Length+sizeof(WCHAR) ) )
   {
      RtlZeroMemory( &paramTable[0], sizeof(paramTable) );
      RtlZeroMemory( path, RegistryPath->Length+sizeof(WCHAR) );
      RtlMoveMemory( path, RegistryPath->Buffer, RegistryPath->Length );

      paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
      paramTable[0].Name = L"DigiBreakOnEntry";
      paramTable[0].EntryContext = &shouldBreak;
      paramTable[0].DefaultType = REG_DWORD;
      paramTable[0].DefaultData = &zero;
      paramTable[0].DefaultLength = sizeof(ULONG);

      paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
      paramTable[1].Name = L"DigiDebugLevel";
      paramTable[1].EntryContext = &debugLevel;
      paramTable[1].DefaultType = REG_DWORD;
      paramTable[1].DefaultData = &DigiDebugLevel;
      paramTable[1].DefaultLength = sizeof(ULONG);

      paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
      paramTable[2].Name = L"DigiPrintFlags";
      paramTable[2].EntryContext = &DigiPrintFlags;
      paramTable[2].DefaultType = REG_DWORD;
      paramTable[2].DefaultData = &defaultDigiPrintFlags;
      paramTable[2].DefaultLength = sizeof(ULONG);

      paramTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
      paramTable[3].Name = L"TurnOffSniffer";
      paramTable[3].EntryContext = &TurnOffSniffer;
      paramTable[3].DefaultType = REG_DWORD;
      paramTable[3].DefaultData = &defaultTurnOffSniffer;
      paramTable[3].DefaultLength = sizeof(UCHAR);

      paramTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
      paramTable[4].Name = L"DigiTxElapsed";
      paramTable[4].EntryContext = &DigiTxElapsed;
      paramTable[4].DefaultType = REG_DWORD;
      paramTable[4].DefaultData = &defaultDigiTxElapsed;
      paramTable[4].DefaultLength = sizeof(ULONG);

      if( !NT_SUCCESS(RtlQueryRegistryValues(
                          RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
                          path,
                          &paramTable[0],
                          NULL, NULL )))
      {
         // No, don't break on entry if there isn't anything to override.
         shouldBreak = 0;
         // Set debug level to what ever was compiled into the driver.
         debugLevel = DigiDebugLevel;
      }
   }

   //
   // We don't need that path anymore.
   //

   if( path )
       DigiFreeMem(path);

   DigiDebugLevel = debugLevel;

#if DBG
   if( shouldBreak )
      DbgBreakPoint();
#endif

   if( DigiDontLoadDriver )
      return( STATUS_CANCELLED );

#if DBG
// MemPrintPreInitSettings( "\\SystemRoot\\digix25.log", (65536 * 8) );
   MemPrintInitialize("\\SystemRoot\\" WANLINKS_NAME_PREFIX_LC_QUOT "x25.log");
#endif

   DigiX25DriverObject = DigiAllocMem( NonPagedPool, sizeof(DIGIWAN_DRIVER_OBJECT) );
   if( DigiX25DriverObject == NULL )
   {
      MemPrintQuit();
      return( NDIS_STATUS_RESOURCES );
   }

   NdisZeroMemory( DigiX25DriverObject, sizeof(DIGIWAN_DRIVER_OBJECT) );
   DigiX25DriverObject->Type = DIGIWAN_TYPE_DRIVER_OBJECT;
   DigiX25DriverObject->DriverObject = DriverObject;

   //
   // Initialize the wrapper.
   //

   NdisMInitializeWrapper( &DigiX25DriverObject->WrapperHandle,
                           DriverObject,
                           RegistryPath,
                           NULL );

   //
   // Initialize the MAC characteristics for the call to
   // NdisMRegisterMiniport.
   //

   NdisZeroMemory( &X25WanChar, sizeof(X25WanChar) );
   X25WanChar.Reserved = ( NDIS_USE_WAN_WRAPPER );
   X25WanChar.MajorNdisVersion = DIGIX25_NDIS_MAJOR_VER;
   X25WanChar.MinorNdisVersion = DIGIX25_NDIS_MINOR_VER;
   X25WanChar.HaltHandler             = DigiX25Halt;
   X25WanChar.InitializeHandler       = DigiX25Initialize;
   X25WanChar.QueryInformationHandler = MiniportQueryInfo;
   X25WanChar.SetInformationHandler   = MiniportSetInfo;
   X25WanChar.WanSendHandler          = DigiWanSend;

   Status = NdisMRegisterMiniport( DigiX25DriverObject->WrapperHandle,
                                   (PNDIS_MINIPORT_CHARACTERISTICS)&X25WanChar,
                                   sizeof(X25WanChar) );

   if( Status == NDIS_STATUS_SUCCESS )
      UpdateRegistry( RegistryPath );
   else
   {
		NdisTerminateWrapper( DigiX25DriverObject->WrapperHandle, NULL );
      MemPrintQuit();
   }

   DigiDump( DIGIFLOW, ("Exit DriverEntry\n") );
   return( Status );
}  // end DriverEntry



NDIS_STATUS DigiX25Initialize( OUT PNDIS_STATUS OpenErrorStatus,
                               OUT PUINT SelectedMediumIndex,
                               IN PNDIS_MEDIUM MediumArray,
                               IN UINT MediumArraySize,
                               IN NDIS_HANDLE MiniportAdapterHandle,
                               IN NDIS_HANDLE ConfigurationHandle )
/*++

Routine Description:

    DigiX25Initialize starts an adapter and registers resources with the
    wrapper.

Arguments:

    OpenErrorStatus - Extra status bytes for opening token ring adapters.

    SelectedMediumIndex - Index of the media type chosen by the driver.

    MediumArray - Array of media types for the driver to chose from.

    MediumArraySize - Number of entries in the array.

    MiniportAdapterHandle - Handle for passing to the wrapper when
       referring to this adapter.

    ConfigurationHandle - A handle to pass to NdisOpenConfiguration.

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_PENDING

--*/
{
   NDIS_HANDLE ConfigHandle;	// // Registry handle
   PVOID NetworkAddress;
   ULONG AddressLength;
   PNDIS_CONFIGURATION_PARAMETER ReturnedValue;	// Value read from registry

   NDIS_STRING ServiceNameStr = SERVICE_NAME;
   NDIS_STRING DeviceCountStr = REGKEY_DEVICECOUNT;
   NDIS_STRING MaxSendWindowStr = MAXSENDWINDOW;

   ULONG i;
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = NULL;
   NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
	PALIAS_ENTRY AliasEntry;

   DigiDump( DIGIFLOW, ("Enter DigiX25Initialize\n") );

   //
   // Search for the medium type in the given array.
   //
   for( i = 0; i < MediumArraySize; i++ )
   {
      if( MediumArray[i] == NdisMediumWan )
         break;
   }

   if( i == MediumArraySize )
   {
      DigiDump( DIGIERRORS, ("  Unable to find a medium type to return!\n") );
      return( NDIS_STATUS_UNSUPPORTED_MEDIA );
   }

   *SelectedMediumIndex = i;
   AdapterObject = DigiAllocMem( NonPagedPool, sizeof(DIGIWAN_ADAPTER_OBJECT) );

   if( AdapterObject == NULL )
   {
      DigiDump( DIGIERRORS, 
                ("  Unable to allocate resources!  (line:%d)\n", __LINE__) );
      return(NDIS_STATUS_RESOURCES);
   }

	// Init spinlock used for syncronizing DigiWanSend completions.
	NdisAllocateSpinLock( &SendStatusLock );

   // Init adapter object, which sets all default values to FALSE or NULL.
   NdisZeroMemory( AdapterObject, sizeof(DIGIWAN_ADAPTER_OBJECT) );
   AdapterObject->Type = DIGIWAN_TYPE_ADAPTER_OBJECT;

   // 4 bytes for a RAS frame header <SYN>-<SOH>-<LEN(2)>
	AdapterObject->HeaderPadding = 4;

	// Worst case is 3 bytes: 1 closing flag, and 2 for CRC.  Round up to 4.
	AdapterObject->TailPadding = 4;

   NdisOpenConfiguration( &Status, &ConfigHandle, ConfigurationHandle );
   if( Status != NDIS_STATUS_SUCCESS )
   {
      DigiDump( DIGIERRORS, ("   NdisOpenConfiguration failed!\n") );
		goto X25InitializeExitError;
   }

   //
   // Read net address
   //
   NdisReadNetworkAddress( &Status, &NetworkAddress, &AddressLength, ConfigHandle );
   if( (AddressLength == DIGIX25_LENGTH_OF_ADDRESS) &&
       (Status == NDIS_STATUS_SUCCESS) )
   {
      // Save it
      DigiDump( DIGIINIT, ("   Using NetworkAddress = ") );
      for( i = 0; i < DIGIX25_LENGTH_OF_ADDRESS-1; i++ )
      {
         DigiDump( DIGIINIT, ("%x-", *(((PUCHAR)NetworkAddress) + i)) );
      }
      DigiDump( DIGIINIT, ("%x\n", *(((PUCHAR)NetworkAddress) + i)) );
      DigiDump( DIGIINIT, ("                          ") );
      for( i = 0; i < DIGIX25_LENGTH_OF_ADDRESS-1; i++ )
      {
         DigiDump( DIGIINIT, ("%c-", *(((PUCHAR)NetworkAddress) + i)) );
      }
      DigiDump( DIGIINIT, ("%c\n", *(((PUCHAR)NetworkAddress) + i)) );

      NdisMoveMemory( AdapterObject->StationAddress,
                      NetworkAddress,
                      DIGIX25_LENGTH_OF_ADDRESS );
   }
   else
   	// Oh well, it's not disasterous... continue on.
      DigiDump( DIGIERRORS, ("   NdisReadNetworkAddress failed!\n") );

   //
   // Number of tapi devices to expose (i.e. number of devices available 
   // for use by RAS)
   //
   NdisReadConfiguration( &Status, &ReturnedValue, ConfigHandle, 
                          &DeviceCountStr, NdisParameterInteger );

   if( Status == NDIS_STATUS_SUCCESS )
		AdapterObject->NumberOfEndpoints = ReturnedValue->ParameterData.IntegerData;
   else
   {
      DigiDump( DIGIERRORS, ("   NdisReadConfiguration, DeviceCount failed!\n") );
		goto X25InitializeExitError;
   }

   //
   // Max send window
   //
   NdisReadConfiguration( &Status, &ReturnedValue, ConfigHandle,
                          &MaxSendWindowStr, NdisParameterInteger );

   if( Status == NDIS_STATUS_SUCCESS )
      AdapterObject->MaxSendWindow = (USHORT)ReturnedValue->ParameterData.IntegerData;
   else
   {
      DigiDump( DIGIERRORS, ("   NdisReadConfiguration, MaxSendWindow failed!\n") );
		goto X25InitializeExitError;
   }

   // Get the Service Name string and use it to initialize the AdapterName
   NdisReadConfiguration( &Status, &ReturnedValue, ConfigHandle,
                          &ServiceNameStr,  NdisParameterString );

   if( NT_SUCCESS( Status ) )
   {
      ANSI_STRING AnsiString;

      DigiDump( DIGIINIT, ("ServiceName = %wZ\n",
                           &(ReturnedValue->ParameterData.StringData)) );

		// Initialize adapter name
      RtlUnicodeStringToAnsiString( &AnsiString,
                                    &(ReturnedValue->ParameterData.StringData),
                                    TRUE );

      strcpy( AdapterObject->Name, AnsiString.Buffer );

      RtlFreeAnsiString( &AnsiString );

      NdisMoveMemory( &(AdapterObject->UAdapterName),
                      &(ReturnedValue->ParameterData.StringData),
                      sizeof(UNICODE_STRING) );

      AdapterObject->UAdapterName.Buffer = DigiAllocMem( NonPagedPool,
                                           AdapterObject->UAdapterName.MaximumLength );

      if( AdapterObject->UAdapterName.Buffer == NULL )
      {
      	Status = NDIS_STATUS_FAILURE;
			goto X25InitializeExitError;
      }

      // We zero the memory to insure the last wchar is a null terminator.
      NdisZeroMemory( AdapterObject->UAdapterName.Buffer,
                      AdapterObject->UAdapterName.MaximumLength );

      NdisMoveMemory( AdapterObject->UAdapterName.Buffer,
                      ReturnedValue->ParameterData.StringData.Buffer,
                      AdapterObject->UAdapterName.Length );
   }
   else
   {
      DigiDump( DIGIERRORS, ("Unable to find ServiceName\n") );
      return( NDIS_STATUS_FAILURE );
   }

   // Find the instance number from the driver service key, 
   // i.e the 1000 in DigiWan1000

	// Start by finding the terminating null...
	i = 0;
	while ( AdapterObject->Name[i] )	
		i++;

	// ...then work backwards to find beginning of instance number
	while( AdapterObject->Name[i-1] <= '9' ) 
		i--;

	RtlMoveMemory( &AdapterObject->ServiceName, AdapterObject->Name, i );
	strcpy( (PVOID)&AdapterObject->Instance, &AdapterObject->Name[i] );

	//
	// Build X.25 Alias table.  This is a table of aliases (or "friendly names")
	// and their associated device/snid pairs, where device is a PhysAdapter and
	// snid is a line identifier.
	//
	Status = BuildX25AliasTable( ConfigHandle );
	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERRORS, ("*** BuildX25AliasTable failed! ***\n") );
		goto X25InitializeExitError;
	}

	//
	// Alloc and init PhysAdapter objects and insert them on the adapter object
	// physAdapter list.
	//

   InitializeListHead( &AdapterObject->PhysAdapters );
	AliasEntry = gpAliasList;
	while( AliasEntry )
	{
		PDIGIWAN_PHYS_ADAPTER PhysAdapter = NULL;

		if ( !IsAdapterCreated( &AliasEntry->DeviceName, 
		                        &AdapterObject->PhysAdapters,
		                        &PhysAdapter ) )
		{
			Status = DigiCreatePhysAdapter( &PhysAdapter, 
			                                &AliasEntry->DeviceName,
			                                AdapterObject );
			if ( NT_SUCCESS( Status ) )
				// Successfully opened the device.  Add it to list
				InsertTailList( &AdapterObject->PhysAdapters, &PhysAdapter->List );
		}
		AliasEntry->PhysAdapter = PhysAdapter;
		AliasEntry = AliasEntry->pNext;
	}

	if ( IsListEmpty( &AdapterObject->PhysAdapters ) )
	{
		// No successfully opened devices
		Status = NDIS_STATUS_FAILURE;
		goto X25InitializeExitError;
	}

	//
	// Build outgoing X.25 address Route table
	//

	Status = BuildX25RouterTable( ConfigHandle, &AdapterObject->PhysAdapters );
	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERRORS, ("*** BuildX25RouterTable failed! ***\n") );
		goto X25InitializeExitError;
	}

	Status = InitializeCallList( AdapterObject->NumberOfEndpoints, 
	                             MiniportAdapterHandle );
	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERRORS, ("*** InitializeCallList failed! ***\n") );
		goto X25InitializeExitError;
	}

   NdisCloseConfiguration( ConfigHandle );

   //
   // Register with the wrapper and initialize the adapter.
   //

   AdapterObject->MiniportAdapterHandle = MiniportAdapterHandle;

   NdisAllocateSpinLock( &AdapterObject->AdapterContext.Lock );
   NdisMInitializeTimer( &AdapterObject->AdapterContext.Timer,
                         MiniportAdapterHandle,
                         AdapterContextTimerFunction,
                         AdapterObject );
   InitializeListHead( &AdapterObject->AdapterContext.Queue );

	NdisAllocateSpinLock( &AdapterObject->Lock );
   DigiX25DriverObject->WanAdapter = AdapterObject;

   //
   // Finish the registration with ndis.
   //
   NdisMSetAttributes( AdapterObject->MiniportAdapterHandle, 
                       (NDIS_HANDLE)AdapterObject, FALSE, NdisInterfaceIsa );

   DigiDump( DIGIFLOW, ("Exit DigiX25Initialize\n") );
	return( NDIS_STATUS_SUCCESS );

X25InitializeExitError:;

   NdisCloseConfiguration( ConfigHandle );

	if ( AdapterObject )
	{
		if ( AdapterObject->UAdapterName.Buffer )
			DigiFreeMem( AdapterObject->UAdapterName.Buffer );

		while ( !IsListEmpty( &AdapterObject->PhysAdapters ) )
		{
			PLIST_ENTRY pEntry;
			PDIGIWAN_PHYS_ADAPTER pAdapter;
			pEntry = RemoveHeadList( &AdapterObject->PhysAdapters );
			pAdapter = CONTAINING_RECORD( pEntry, DIGIWAN_PHYS_ADAPTER, List );
			DigiFreeMem( pAdapter );
		}

		DigiFreeMem( AdapterObject );
	}

   DigiDump( DIGIFLOW, ("Exit DigiX25Initialize\n") );
	return( Status );
}  // end DigiX25Initialize



VOID DigiX25Halt( IN NDIS_HANDLE MiniportAdapterContext )
/*++

Routine Description:

    DigiX25Halt removes an adapter that was previously initialized.

Arguments:

    MiniportAdapterContext - The context value that the Miniport returned
        from DigiX25Initialize; actually as pointer to an DigiX25_ADAPTER.

Return Value:

    None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PDIGIWAN_ADAPTER_OBJECT)MiniportAdapterContext;
	PLIST_ENTRY Entry;
	PDIGIWAN_PHYS_ADAPTER pPhysAdapter;

   DigiDump( DIGIFLOW, ("Enter DigiX25Halt\n") );

	if ( AdapterObject->UAdapterName.Buffer )
		DigiFreeMem( AdapterObject->UAdapterName.Buffer );

   while ( !IsListEmpty( &AdapterObject->PhysAdapters ) )
   {
		Entry = RemoveHeadList( &AdapterObject->PhysAdapters );
		pPhysAdapter = CONTAINING_RECORD( Entry, DIGIWAN_PHYS_ADAPTER, List );
		ASSERT( pPhysAdapter->Type == DIGIWAN_TYPE_PHYS_ADAPTER );

		DsCloseDevice( pPhysAdapter->hDevice );
		DigiFreeMem( pPhysAdapter );
   }

	NdisTerminateWrapper( AdapterObject->MiniportAdapterHandle, NULL );
	DigiFreeMem( AdapterObject );
	NdisFreeSpinLock( &SendStatusLock );
   DigiDump( DIGIFLOW, ("Exit DigiX25Halt\n") );
}  // end DigiX25Halt



VOID UpdateRegistry( PUNICODE_STRING RegistryPath )
/*++

Routine Description:

   Writes required information to the registry which is needed for RAS, and any
   other related information.  We'll add the following keys:

		"\Registry\machine\hardware\devicemap\[ServiceName]"
		"\Registry\machine\hardware\devicemap\Tapi Devices"
		"\Registry\machine\hardware\devicemap\Tapi Devices\[ServiceName]"

	Then under "\...\Tapi Devices\[ServiceName]" we'll add the following 
	values:

		Media Type: REG_SZ       X25
		Address:    REG_MULTI_SZ 1000-0-0
		                         1000-0-1
									    1000-0-2 ...

Arguments:

   RegistryPath - pointer to the driver's registry key.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject;
	UNICODE_STRING TapiDevicesStr = TAPIDEVICES;
	UNICODE_STRING TapiDevices = UNICODE_NULLSTRING;
	UNICODE_STRING AddressStr = ADDRESS;
   UNICODE_STRING Address = UNICODE_NULLSTRING;
	UNICODE_STRING MediaType = MEDIA_TYPE;
	UNICODE_STRING MediaTypeX25 = MEDIA_TYPE_X25_UNICODE;
	UNICODE_STRING ServiceName = UNICODE_NULLSTRING;
	PWCHAR pAdapterNumber;
	ULONG i;

   AdapterObject = DigiX25DriverObject->WanAdapter;
   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

	//
	// Build the driver service name from the adapterobject AdapterName string
	//

   RtlInitUnicodeString( &ServiceName, NULL );
   ServiceName.MaximumLength = AdapterObject->UAdapterName.Length;
   ServiceName.Buffer = DigiAllocMem( NonPagedPool, ServiceName.MaximumLength );
   if( ServiceName.Buffer == NULL )
      goto UpdateRegistryExit;
	NdisZeroMemory( ServiceName.Buffer, ServiceName.MaximumLength );
	RtlAppendUnicodeStringToString( &ServiceName, &AdapterObject->UAdapterName );

	// Strip off trailing adapter number, leaving the driver service name
	i = ServiceName.Length / sizeof( WCHAR ) - 1;
	while ( ServiceName.Buffer[i] >= '0' && ServiceName.Buffer[i] <= '9' )
	{
		ServiceName.Buffer[i--] = '\0';
		ServiceName.Length -= sizeof( WCHAR );
	}
	
	//
	// Build the "Tapi Devices\\[ServiceName]" string
	//

   RtlInitUnicodeString( &TapiDevices, NULL );
   TapiDevices.MaximumLength = TapiDevicesStr.Length + 	// "Tapi Devices"
                               sizeof( WCHAR ) + 			// "\\"
                               ServiceName.Length + 		// "DigiWan"
                               sizeof( WCHAR );				// "\0"
	TapiDevices.Buffer = DigiAllocMem( NonPagedPool, TapiDevices.MaximumLength );
   if( TapiDevices.Buffer == NULL )
      goto UpdateRegistryExit;
	NdisZeroMemory( TapiDevices.Buffer, TapiDevices.MaximumLength );
	RtlAppendUnicodeStringToString( &TapiDevices, &TapiDevicesStr );
	RtlAppendUnicodeToString( &TapiDevices, L"\\" );
	RtlAppendUnicodeStringToString( &TapiDevices, &ServiceName );

	//
	// Create registry keys in \Registry\machine\hardare\devices
	//

   // Create Tapi Devices key
   RtlCreateRegistryKey( RTL_REGISTRY_DEVICEMAP, TapiDevicesStr.Buffer );

   // Create our Service Provider key
   RtlCreateRegistryKey( RTL_REGISTRY_DEVICEMAP, ServiceName.Buffer );

   // Add our media type value under our tapi devices key
	// i.e. MediaType="X25" under the ...\Tapi Devices\[ServiceName] key.
   RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP,
                          TapiDevices.Buffer,
                          MediaType.Buffer,
                          REG_SZ,
                          MediaTypeX25.Buffer,
                          MediaTypeX25.Length );

   //
   // Build the Address multi-string.  We expose all endpoints on a single 
   // virtual adapter with a single virtual line, so the value will look like 
   // this: [adapternumber]-[line]-[endpoint], where adapternumber and line 
   // are constant, i.e. 
	//    1000-0-0
	//    1000-0-1
	//    1000-0-2, etc.
   //
   
   RtlInitUnicodeString( &Address, NULL );
   Address.MaximumLength = (USHORT)( AdapterObject->NumberOfEndpoints*12+1) * sizeof( WCHAR );
   Address.Buffer = DigiAllocMem( NonPagedPool, Address.MaximumLength );
   if( Address.Buffer == NULL )
      goto UpdateRegistryExit;
   NdisZeroMemory( Address.Buffer, Address.MaximumLength );
   Address.Length = 0;

	// Get the adapter number.  Scan backwards from end of the adaptername to point at 
	// the adapter number, i.e. 1000 in DigiWan1000.
	pAdapterNumber = AdapterObject->UAdapterName.Buffer + 
	                 AdapterObject->UAdapterName.Length / sizeof( WCHAR ) - 1;

	while ( *pAdapterNumber <= L'9' && *pAdapterNumber >= L'0' )
		pAdapterNumber--;

	// Backed up one too far, increment one char.
	pAdapterNumber++;

	// Build the address multi-string which defines all available endpoints
	// for this wan miniport.  
	for ( i = 0; i < AdapterObject->NumberOfEndpoints; i++ )
	{
		UNICODE_STRING Endpoint;
		WCHAR Buffer[16];
		
		// Convert endpoint number to a string
		RtlZeroMemory( Buffer, sizeof( Buffer ) );
		Endpoint.Length = 0;
		Endpoint.Buffer = Buffer;
		Endpoint.MaximumLength = sizeof( Buffer );
		RtlIntegerToUnicodeString( i, 10, &Endpoint );

		// Build a single address string, i.e. 1000-0-0.
		RtlAppendUnicodeToString( &Address, pAdapterNumber );
		RtlAppendUnicodeToString( &Address, L"-0-" );
		RtlAppendUnicodeStringToString( &Address, &Endpoint );

		// Insert a null.  Note: already zeroed out buffer, just increment length
		Address.Length += sizeof( WCHAR );

		// Always need room for the concluding NULL char
		ASSERT( Address.Length < Address.MaximumLength - sizeof( WCHAR ) );
	}

   Address.Length += sizeof(WCHAR);

   //
   // Write the Address value
   //
   RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP,
                          TapiDevices.Buffer,
                          AddressStr.Buffer,
                          REG_MULTI_SZ,
                          Address.Buffer,
                          Address.Length );

UpdateRegistryExit:;

	if ( ServiceName.Buffer )
		DigiFreeMem( ServiceName.Buffer );

	if ( TapiDevices.Buffer )
		DigiFreeMem( TapiDevices.Buffer );

	if ( Address.Buffer )
		DigiFreeMem( Address.Buffer );

}  // end UpdateRegistry



NDIS_STATUS 
DigiCreatePhysAdapter( 
	OUT PDIGIWAN_PHYS_ADAPTER *pPhysAdapter,
	IN PUNICODE_STRING Device,
	IN PDIGIWAN_ADAPTER_OBJECT AdapterObject )
/*++

Routine Description:

    Alloc and init a physAdapter object defining a device (physical adapter)
	 to communicate with.  Open the device.

Arguments:

    pPhysAdapter - Address to store pointer to newly allocated PhysAdapter object.

    Device - Unicode string defining the name of the adapter/device to 
    create/open.

	AdapterObject - Adapter object which owns this phys adapter.

Return Value:

    NDIS_STATUS_SUCCESS,
	 NDIS_STATUS_RESOURCES,
	 NDIS_STATUS_FAILURE

--*/
{
	PDIGIWAN_PHYS_ADAPTER PhysAdapter = NULL;
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

	*pPhysAdapter = NULL;

	// Alloc and initialize new physAdapter object 

	PhysAdapter = DigiAllocMem( NonPagedPool, sizeof( DIGIWAN_PHYS_ADAPTER ) );
	if ( !PhysAdapter )
		return( NDIS_STATUS_RESOURCES );

	NdisZeroMemory( PhysAdapter, sizeof( DIGIWAN_PHYS_ADAPTER ) );

	PhysAdapter->Type = DIGIWAN_TYPE_PHYS_ADAPTER;
	PhysAdapter->WanAdapter = AdapterObject;
	KeInitializeSpinLock( &PhysAdapter->Lock );
	KeInitializeDpc( &PhysAdapter->Dpc, DigiWanDeferredGetMsg, PhysAdapter );
	InitializeListHead( &PhysAdapter->OfferingCalls );

	// Store name of this device in the object 

	PhysAdapter->Name.Length = 0;
	PhysAdapter->Name.MaximumLength = Device->Length + sizeof( WCHAR );
	PhysAdapter->Name.Buffer = DigiAllocMem( NonPagedPool, 
	                                         PhysAdapter->Name.MaximumLength );
	if ( !PhysAdapter->Name.Buffer )
	{
		// Memory alloc failure
		Status = NDIS_STATUS_RESOURCES;
		goto DigiCreatePhysAdapterExit;
	}

	RtlCopyUnicodeString( &PhysAdapter->Name, Device );

	// Try to open the device
	Status = DsOpenDevice( &PhysAdapter->Name, &PhysAdapter->hDevice );
	
	if ( !NT_SUCCESS( Status ) )
	{
		// Failure opening device, return SxbOpenDevice Status code
		goto DigiCreatePhysAdapterExit;
	}

	Status = NDIS_STATUS_SUCCESS;
	*pPhysAdapter = PhysAdapter;

DigiCreatePhysAdapterExit:;

	if ( !NT_SUCCESS( Status ) )
	{
		if ( PhysAdapter )
		{
			if ( PhysAdapter->Name.Buffer )
				DigiFreeMem( PhysAdapter->Name.Buffer );

			DigiFreeMem( PhysAdapter );
		}
	}

	return( Status );
}