#include "precomp.h"

/////////////////////////////////////////////////////////////////////////////////////
//  This file contains all functions that are needed to integrate between the 
//  generic PnP code and the product specific code.
/////////////////////////////////////////////////////////////////////////////////////

#define FILE_ID		SPD_PNP_C		// File ID for Event Logging see SPD_DEFS.H for values.


// Prototypes
// End of Prototypes

NTSTATUS
XXX_CardGetResources(IN PDEVICE_OBJECT pDevObject,
					 IN PCM_RESOURCE_LIST PResList,
					 IN PCM_RESOURCE_LIST PTrResList) 	
/* ++
Routine Description:

	Stores resources given to us by the PnP manager 
	in the card's device extension.

Arguments:

    pDevObject - Pointer to the device object.
    
    PResList - Pointer to the untranslated resources requested.
    
    PTrResList - Pointer to the translated resources requested.

Return Value:

    STATUS_SUCCESS.

--*/
{

	PCARD_DEVICE_EXTENSION pCard		= pDevObject->DeviceExtension;
	NTSTATUS status						= STATUS_NOT_IMPLEMENTED;

	CHAR szErrorMsg[MAX_ERROR_LOG_INSERT];	// Limited to 51 characters + 1 null 
	ULONG count				= 0;
	ULONG i					= 0;
	USHORT MemoryResource	= 0;
	USHORT IOResource		= 0;

	PCM_FULL_RESOURCE_DESCRIPTOR	pFullResourceDesc		= NULL;
	PCM_PARTIAL_RESOURCE_LIST		pPartialResourceList	= NULL;
	PCM_PARTIAL_RESOURCE_DESCRIPTOR	pPartialResourceDesc	= NULL;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_CardGetResources for Card %d.\n", 
		PRODUCT_NAME, pCard->CardNumber));

	SpxDbgMsg(SPX_MISC_DBG, ("%s: Resource pointer is 0x%X\n", PRODUCT_NAME, PResList));
	SpxDbgMsg(SPX_MISC_DBG, ("%s: Translated resource pointer is 0x%X\n", PRODUCT_NAME, PTrResList));

	if((PResList == NULL) || (PTrResList == NULL)) 
	{
		// This shouldn't happen in theory
		ASSERT(PResList != NULL);
		ASSERT(PTrResList != NULL);

		sprintf(szErrorMsg, "Card %d has been given no resources.", pCard->CardNumber);
		
		Spx_LogMessage(	STATUS_SEVERITY_ERROR,
						pCard->DriverObject,		// Driver Object
						pCard->DeviceObject,		// Device Object (Optional)
						PhysicalZero,				// Physical Address 1
						PhysicalZero,				// Physical Address 2
						0,							// SequenceNumber
						0,							// Major Function Code
						0,							// RetryCount
						FILE_ID | __LINE__,			// UniqueErrorValue
						STATUS_SUCCESS,				// FinalStatus
						szErrorMsg);				// Error Message

		// This status is as appropriate as I can think of
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	// Each resource list should have only one set of resources
	ASSERT(PResList->Count == 1);
	ASSERT(PTrResList->Count == 1);

	// Find out the card type... 
	if((pCard->CardType = SpxGetNtCardType(pCard->DeviceObject)) == -1)
	{
		sprintf(szErrorMsg, "Card %d is unrecognised.", pCard->CardNumber);

		Spx_LogMessage(	STATUS_SEVERITY_ERROR,
						pCard->DriverObject,			// Driver Object
						pCard->DeviceObject,			// Device Object (Optional)
						PhysicalZero,					// Physical Address 1
						PhysicalZero,					// Physical Address 2
						0,								// SequenceNumber
						0,								// Major Function Code
						0,								// RetryCount
						FILE_ID | __LINE__,				// UniqueErrorValue
						STATUS_SUCCESS,					// FinalStatus
						szErrorMsg);					// Error Message


		return(STATUS_DEVICE_DOES_NOT_EXIST);
	}


	// Find out which raw resources have been given to us.
	pFullResourceDesc = &PResList->List[0];

	if(pFullResourceDesc)
	{
		pPartialResourceList    = &pFullResourceDesc->PartialResourceList;
		pPartialResourceDesc    = pPartialResourceList->PartialDescriptors;
		count                   = pPartialResourceList->Count;	// Number of Partial Resource Descriptors

		// Pull out the stuff that is in the full descriptor.
		pCard->InterfaceType	= pFullResourceDesc->InterfaceType;
		pCard->BusNumber		= pFullResourceDesc->BusNumber;

		// Now run through the partial resource descriptors looking for the port and interrupt.
		for(i = 0; i < count; i++, pPartialResourceDesc++) 
		{
			switch(pPartialResourceDesc->Type) 
			{

			case CmResourceTypeMemory:
				break;

			case CmResourceTypePort: 
				{
					switch(pCard->CardType)
					{
					case Fast4_Isa:
					case Fast8_Isa:
					case Fast16_Isa:
					case Fast4_Pci:
					case Fast8_Pci:
					case Fast16_Pci:
					case Fast16FMC_Pci:
					case RAS4_Pci:
					case RAS8_Pci:
						pCard->PhysAddr			= pPartialResourceDesc->u.Memory.Start;
						pCard->SpanOfController	= pPartialResourceDesc->u.Memory.Length;
						break;

					default:	// Speed cards
						break;
					}

					IOResource++;
					break;
				}

			case CmResourceTypeInterrupt: 
				{
					pCard->OriginalIrql		= pPartialResourceDesc->u.Interrupt.Level;
					pCard->OriginalVector	= pPartialResourceDesc->u.Interrupt.Vector;
					pCard->ProcessorAffinity= pPartialResourceDesc->u.Interrupt.Affinity;

					if(pPartialResourceDesc->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
						pCard->InterruptMode	= Latched;
					else
						pCard->InterruptMode	= LevelSensitive; 

					switch(pPartialResourceDesc->ShareDisposition)
					{
					case CmResourceShareDeviceExclusive:
						pCard->InterruptShareable	= FALSE;
						break;

					case CmResourceShareDriverExclusive:
						pCard->InterruptShareable	= FALSE;
						break;

					case CmResourceShareShared:
					default:
						pCard->InterruptShareable	= TRUE;
						break;
					}

					break;
				}

			default:
				break;

			}

		}
	}

	// Do the same for the translated resources.
	pFullResourceDesc = &PTrResList->List[0];

	if(pFullResourceDesc)
	{
		pPartialResourceList	= &pFullResourceDesc->PartialResourceList;
		pPartialResourceDesc	= pPartialResourceList->PartialDescriptors;
		count					= pPartialResourceList->Count;	// Number of Partial Resource Descriptors

		// Pull out the stuff that is in the full descriptor.
		pCard->InterfaceType		= pFullResourceDesc->InterfaceType;
		pCard->BusNumber			= pFullResourceDesc->BusNumber;

		// Now run through the partial resource descriptors looking for the interrupt,
		for(i = 0; i < count; i++, pPartialResourceDesc++) 
		{
			switch(pPartialResourceDesc->Type) 
			{

			case CmResourceTypeMemory: 
				{
					switch(pCard->CardType)
					{
					case Fast4_Isa:
					case Fast8_Isa:
					case Fast16_Isa:
						break;		// No Memory resource for these

					case Fast4_Pci:
					case Fast8_Pci:
					case Fast16_Pci:
					case Fast16FMC_Pci:
					case RAS4_Pci:
					case RAS8_Pci:
						{	// Must be config space 
							pCard->PCIConfigRegisters		= pPartialResourceDesc->u.Memory.Start;
							pCard->SpanOfPCIConfigRegisters	= pPartialResourceDesc->u.Memory.Length;
							break;
						}

					default:	// Speed cards
						{
							if(MemoryResource == 0)	
							{
								pCard->PhysAddr			= pPartialResourceDesc->u.Memory.Start;
								pCard->SpanOfController	= pPartialResourceDesc->u.Memory.Length;
							}
							else
							{	// Must be config space 
								pCard->PCIConfigRegisters		= pPartialResourceDesc->u.Memory.Start;
								pCard->SpanOfPCIConfigRegisters	= pPartialResourceDesc->u.Memory.Length;
							}

							break;
						}

					}
		
					MemoryResource++;
					break;
				}
			
			case CmResourceTypePort: 
				break;

			case CmResourceTypeInterrupt: 
				{
					pCard->TrIrql				= (KIRQL) pPartialResourceDesc->u.Interrupt.Level;
					pCard->TrVector				= pPartialResourceDesc->u.Interrupt.Vector;
					pCard->ProcessorAffinity	= pPartialResourceDesc->u.Interrupt.Affinity;
					break;
				}

			default:
				break;
			}
		}
	}

	// If we have 1 Mem or 1 I/O Resources and an interrupt the resource allocation most probably succeeded.
	if(((MemoryResource >= 1) || (IOResource >= 1)) && (pCard->TrVector))
		status = STATUS_SUCCESS;

	return status;
}




NTSTATUS
XXX_CardInit(IN PCARD_DEVICE_EXTENSION pCard)
{
	NTSTATUS status = STATUS_SUCCESS;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_CardInit for Card %d.\n", 
		PRODUCT_NAME, pCard->CardNumber));



    pCard->OurIsr			= SerialISR;
    pCard->OurIsrContext	= pCard;


	return status;
}

NTSTATUS
XXX_CardStart(IN PCARD_DEVICE_EXTENSION pCard)
{
	NTSTATUS status = STATUS_SUCCESS;
	UCHAR CardID	= 0;
	CHAR szErrorMsg[MAX_ERROR_LOG_INSERT];	// Limited to 51 characters + 1 null 
	BOOLEAN bInterruptConnnected = FALSE;
	
	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_CardStart for Card %d.\n", 
		PRODUCT_NAME, pCard->CardNumber));

	switch(pCard->CardType)
	{	
	case Fast4_Isa:
	case Fast4_Pci:
	case RAS4_Pci:
		pCard->UARTOffset = 8;			// I/O address offset between UARTs
		pCard->UARTRegStride = 1;
		pCard->NumberOfPorts = 4;
		pCard->ClockRate = CLOCK_FREQ_7M3728Hz;		// 7.3728 MHz
		pCard->Controller = (PUCHAR) pCard->PhysAddr.LowPart;

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C65X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Fast8_Isa:
	case Fast8_Pci:
	case RAS8_Pci:
		pCard->UARTOffset = 8;			// I/O address offset between UARTs
		pCard->UARTRegStride = 1;
		pCard->NumberOfPorts = 8;
		pCard->ClockRate = CLOCK_FREQ_7M3728Hz;		// 7.3728 MHz
		pCard->Controller = (PUCHAR) pCard->PhysAddr.LowPart;

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C65X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Fast16_Isa:
	case Fast16_Pci:
	case Fast16FMC_Pci:
		pCard->UARTOffset = 8;			// I/O address offset between UARTs
		pCard->UARTRegStride = 1;
		pCard->NumberOfPorts = 16;
		pCard->ClockRate = CLOCK_FREQ_7M3728Hz;		// 7.3728 MHz
		pCard->Controller = (PUCHAR) pCard->PhysAddr.LowPart;

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C65X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Speed2_Pci:
		pCard->UARTOffset = OXPCI_INTERNAL_MEM_OFFSET;		// Memory address offset between internal UARTs
		pCard->UARTRegStride = 4;
		pCard->NumberOfPorts = 2;
		pCard->ClockRate = CLOCK_FREQ_1M8432Hz;		// 1.8432 MHz
		pCard->Controller = MmMapIoSpace(pCard->PhysAddr, pCard->SpanOfController, FALSE);	// Map in the card's memory base address

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C95X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Speed2P_Pci:
		pCard->UARTOffset = OXPCI_INTERNAL_MEM_OFFSET;		// Memory address offset between internal UARTs
		pCard->UARTRegStride = 4;
		pCard->NumberOfPorts = 2;
		pCard->ClockRate = CLOCK_FREQ_14M7456Hz;	// 14.7456 MHz
		pCard->Controller = MmMapIoSpace(pCard->PhysAddr, pCard->SpanOfController, FALSE);	// Map in the card's memory base address

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C95X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Speed4_Pci:
		pCard->UARTOffset = OXPCI_INTERNAL_MEM_OFFSET;		// Memory address offset between internal UARTs
		pCard->UARTRegStride = 4;
		pCard->NumberOfPorts = 4;
		pCard->ClockRate = CLOCK_FREQ_1M8432Hz;		// 1.8432 MHz
		pCard->Controller = MmMapIoSpace(pCard->PhysAddr, pCard->SpanOfController, FALSE);	// Map in the card's memory base address

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C95X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Speed4P_Pci:
		pCard->UARTOffset = OXPCI_INTERNAL_MEM_OFFSET;		// Memory address offset between internal UARTs
		pCard->UARTRegStride = 4;
		pCard->NumberOfPorts = 4;
		pCard->ClockRate = CLOCK_FREQ_14M7456Hz;	// 14.7456 MHz
		pCard->Controller = MmMapIoSpace(pCard->PhysAddr, pCard->SpanOfController, FALSE);	// Map in the card's memory base address

		if(UL_InitUartLibrary(&pCard->UartLib, UL_LIB_16C95X_UART) != UL_STATUS_SUCCESS)	// Init table of UART library functions pointers.
			goto Error;

		break;

	case Speed2and4_Pci_8BitBus:
		pCard->UARTOffset = OXPCI_LOCAL_MEM_OFFSET;			// Memory address offset between local bus UARTs
		pCard->UARTRegStride = 4;
		pCard->NumberOfPorts = 0;	// No ports.
		pCard->ClockRate = CLOCK_FREQ_1M8432Hz;
		pCard->Controller = MmMapIoSpace(pCard->PhysAddr, pCard->SpanOfController, FALSE);	// Map in the card's memory base address
		break;

	case Speed2P_Pci_8BitBus:
	case Speed4P_Pci_8BitBus:
		pCard->UARTOffset = OXPCI_LOCAL_MEM_OFFSET;			// Memory address offset between local bus UARTs
		pCard->UARTRegStride = 4;
		pCard->NumberOfPorts = 0;	// No ports on a Speed2/4+ card.
		pCard->ClockRate = CLOCK_FREQ_14M7456Hz;
		pCard->Controller = MmMapIoSpace(pCard->PhysAddr, pCard->SpanOfController, FALSE);	// Map in the card's memory base address
		break;

	default:
		pCard->NumberOfPorts = 0;	// Default = No ports.
		break;
	}



// Map in the card's memory base address

	if(!pCard->Controller)
	{
		status = STATUS_INSUFFICIENT_RESOURCES;
		goto Error;
	}

// Map in the card's Local Configuration Registers... 
	if(pCard->InterfaceType == PCIBus)	// If we have some PCI config registers
	{
		pCard->LocalConfigRegisters = MmMapIoSpace(pCard->PCIConfigRegisters, pCard->SpanOfPCIConfigRegisters, FALSE);

		if(!pCard->LocalConfigRegisters)
		{
			status = STATUS_INSUFFICIENT_RESOURCES;
			goto Error;
		}
	}


	// Try to connect to interrupt.
	if(SPX_SUCCESS(status = IoConnectInterrupt(&pCard->Interrupt,			// Interrupt object
												pCard->OurIsr,				// Service routine  
												pCard->OurIsrContext,		// Service context 
												NULL,						// SpinLock (optional) 
												pCard->TrVector,			// Vector 
												pCard->TrIrql,				// IRQL 
												pCard->TrIrql,				// Synchronize IRQL
												pCard->InterruptMode,		// Mode (Latched/Level Sensitive) 
												pCard->InterruptShareable,	// Sharing mode 
												pCard->ProcessorAffinity,	// Processors to handle ints 
												FALSE)))					// Floating point save 	
	{
		bInterruptConnnected = TRUE;	// Set Interrupt Connected flag. 
	}
	else
	{	
		// Tell user the problem 
		sprintf(szErrorMsg, "Card at %08X%08X: Interrupt unavailable.", pCard->PhysAddr.HighPart, pCard->PhysAddr.LowPart);

		Spx_LogMessage(	STATUS_SEVERITY_ERROR,
						pCard->DriverObject,		// Driver Object
						pCard->DeviceObject,		// Device Object (Optional)
						PhysicalZero,				// Physical Address 1
						PhysicalZero,				// Physical Address 2
						0,							// SequenceNumber
						0,							// Major Function Code
						0,							// RetryCount
						FILE_ID | __LINE__,			// UniqueErrorValue
						STATUS_SUCCESS,				// FinalStatus
						szErrorMsg);				// Error Message
		
		goto Error;
	}


	switch(pCard->CardType)
	{	
	case Fast4_Isa:		// If ISA card try to verify the card is present at selected address
	case Fast8_Isa:		// by trying to verify first UART on the Card.
	case Fast16_Isa:
		{
			INIT_UART InitUart = {0};

			// Set base address of 1st UART
			InitUart.UartNumber		= 0;
			InitUart.BaseAddress	= pCard->Controller;
			InitUart.RegisterStride = pCard->UARTRegStride;
			InitUart.ClockFreq		= pCard->ClockRate; 
			
			pCard->pFirstUart = NULL;

			// Init a UART structure.
			if(pCard->UartLib.UL_InitUart_XXXX(&InitUart, pCard->pFirstUart, &(pCard->pFirstUart)) != UL_STATUS_SUCCESS)
			{
				status = STATUS_INSUFFICIENT_RESOURCES;
				goto Error;
			}

			// Reset and try to verify the UART.
			if(!KeSynchronizeExecution(pCard->Interrupt, SerialResetAndVerifyUart, pCard->DeviceObject))	// Verify UART exists.
			{
				SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Failed to find 1st UART on Card %d.\n", PRODUCT_NAME, pCard->CardNumber));
				pCard->UartLib.UL_DeInitUart_XXXX(pCard->pFirstUart);	// DeInit UART
				status = STATUS_DEVICE_DOES_NOT_EXIST;
				goto Error;
			}

			// DeInit the UART structure.
			pCard->UartLib.UL_DeInitUart_XXXX(pCard->pFirstUart);	// DeInit UART
			
			pCard->pFirstUart = NULL;
		}
	
	default:
		break;
	}



	switch(pCard->CardType)
	{	
	case Speed2and4_Pci_8BitBus:
	case Speed2P_Pci_8BitBus:
	case Speed4P_Pci_8BitBus:
		break;

	default:
		GetCardSettings(pCard->DeviceObject);	// Get Card settings if present.

#ifdef WMI_SUPPORT
		// Register for WMI
		SpeedCard_WmiInitializeWmilibContext(&pCard->WmiLibInfo);
		IoWMIRegistrationControl(pCard->DeviceObject, WMIREG_ACTION_REGISTER);
#endif
		break;
	}


	return status;


Error:

	if(bInterruptConnnected)
		IoDisconnectInterrupt(pCard->Interrupt);	// Disconnect Interrupt.

	switch(pCard->CardType)
	{	
	case Fast4_Isa:
	case Fast8_Isa:
	case Fast16_Isa:	
	case Fast4_Pci:
	case Fast8_Pci:
	case Fast16_Pci:
	case Fast16FMC_Pci:
	case RAS4_Pci:
	case RAS8_Pci:
		pCard->Controller = NULL;
		break;

	default:	// Speed cards
		if(pCard->Controller)	// If mapped in.
		{
			MmUnmapIoSpace(pCard->Controller, pCard->SpanOfController);	// Unmap.
			pCard->Controller = NULL;
		}

		break;
	}


	if(pCard->LocalConfigRegisters)	// If PCI Config registers are mapped in.
	{
		MmUnmapIoSpace(pCard->LocalConfigRegisters, pCard->SpanOfPCIConfigRegisters);	// Unmap.
		pCard->LocalConfigRegisters = NULL;
	}


	UL_DeInitUartLibrary(&pCard->UartLib);	// DeInit table of UART library functions pointers.

	switch(status)
	{
	case STATUS_DEVICE_DOES_NOT_EXIST:
	case STATUS_UNSUCCESSFUL:
		{
			sprintf(szErrorMsg, "Card at %08X%08X: Unrecognised or malfunctioning.", pCard->PhysAddr.HighPart, pCard->PhysAddr.LowPart);
			Spx_LogMessage(	STATUS_SEVERITY_ERROR,
							pCard->DriverObject,			// Driver Object
							pCard->DeviceObject,			// Device Object (Optional)
							PhysicalZero,					// Physical Address 1
							PhysicalZero,					// Physical Address 2
							0,								// SequenceNumber
							0,								// Major Function Code
							0,								// RetryCount
							FILE_ID | __LINE__,				// UniqueErrorValue
							STATUS_SUCCESS,					// FinalStatus
							szErrorMsg);					// Error Message

			break;
		}

	case STATUS_INSUFFICIENT_RESOURCES:
		{
			sprintf(szErrorMsg, "Card at %08X%08X: Insufficient resources.", pCard->PhysAddr.HighPart, pCard->PhysAddr.LowPart);

			Spx_LogMessage(	STATUS_SEVERITY_ERROR,
							pCard->DriverObject,			// Driver Object
							pCard->DeviceObject,			// Device Object (Optional)
							PhysicalZero,					// Physical Address 1
							PhysicalZero,					// Physical Address 2
							0,								// SequenceNumber
							0,								// Major Function Code
							0,								// RetryCount
							FILE_ID | __LINE__,				// UniqueErrorValue
							STATUS_SUCCESS,					// FinalStatus
							szErrorMsg);					// Error Message

			break;
		}

	default:
		break;

	}



	return status;
}

NTSTATUS
XXX_CardStop(IN PCARD_DEVICE_EXTENSION pCard)
{
	NTSTATUS status = STATUS_SUCCESS;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_CardStop for Card %d.\n", 
		PRODUCT_NAME, pCard->CardNumber));
	
	// Stop Card from interrupting

	IoDisconnectInterrupt(pCard->Interrupt);		// Disconnect from Interrupt.
	
	
#ifdef WMI_SUPPORT
	switch(pCard->CardType)
	{	
	case Speed2and4_Pci_8BitBus:
	case Speed2P_Pci_8BitBus:
	case Speed4P_Pci_8BitBus:
		break;

	default:
		// Deregister for WMI
		IoWMIRegistrationControl(pCard->DeviceObject, WMIREG_ACTION_DEREGISTER);
		break;
	}
#endif


	switch(pCard->CardType)
	{	
	case Fast4_Isa:
	case Fast8_Isa:
	case Fast16_Isa:	
	case Fast4_Pci:
	case Fast8_Pci:
	case Fast16_Pci:
	case Fast16FMC_Pci:
	case RAS4_Pci:
	case RAS8_Pci:
		pCard->Controller = NULL;
		break;

	default:	// Speed cards
		if(pCard->Controller)	// If mapped in.
		{
			MmUnmapIoSpace(pCard->Controller, pCard->SpanOfController);	// Unmap.
			pCard->Controller = NULL;
		}

		break;
	}

	// Unmap PCI card's Local Configuration Registers...
	if(pCard->LocalConfigRegisters)	// If mapped in.
	{
		MmUnmapIoSpace(pCard->LocalConfigRegisters, pCard->SpanOfPCIConfigRegisters);
		pCard->LocalConfigRegisters = NULL;
	}

	UL_DeInitUartLibrary(&pCard->UartLib);	// DeInit table of UART library functions pointers.


	pCard->InterfaceType			= InterfaceTypeUndefined;
	pCard->PhysAddr					= PhysicalZero;
	pCard->SpanOfController			= 0;
	pCard->OriginalIrql				= 0;
	pCard->OriginalVector			= 0;
	pCard->ProcessorAffinity		= 0;
	pCard->TrIrql					= 0;
	pCard->TrVector					= 0;
	pCard->Controller				= NULL;
	pCard->LocalConfigRegisters		= NULL;
	pCard->SpanOfPCIConfigRegisters = 0;

	return status;
}


NTSTATUS
XXX_CardDeInit(IN PCARD_DEVICE_EXTENSION pCard)
{
	NTSTATUS status = STATUS_SUCCESS;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_CardDeInit for Card %d.\n", 
		PRODUCT_NAME, pCard->CardNumber));

    pCard->OurIsr				= NULL;
    pCard->OurIsrContext		= NULL;

	pCard->pFirstUart = NULL;

	return status;
}


NTSTATUS
XXX_PortInit(IN	PPORT_DEVICE_EXTENSION pPort)
{
	// Initialise port device extension.	

	PCARD_DEVICE_EXTENSION pCard	= pPort->pParentCardExt;
	NTSTATUS status					= STATUS_SUCCESS;
	SHORT PortType = 0;
	CHAR szTemp[50];		// Space to hold string 
	CHAR szCard[10];		// Space to hold card type string 
	SHORT i = 0;
	int Result = 0;
	INIT_UART InitUart;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_PortInit for Port %d.\n", 
		PRODUCT_NAME, pPort->PortNumber));
	
#ifndef BUILD_SPXMINIPORT
	// Form an InstanceID for the port.
	if(!SPX_SUCCESS(status = Spx_CreatePortInstanceID(pPort)))
		return status;
#endif

	switch(pCard->CardType)
	{
	case Fast4_Isa:
	case Fast4_Pci:
		sprintf(szCard, "FAST");	// Fast card
		PortType = FAST_8PIN_RJ45;	// 8 pin RJ45 ports	with Chase pinouts
		break;

	case Fast8_Isa:
	case Fast8_Pci:
		sprintf(szCard, "FAST");	// Fast card
		PortType = FAST_8PIN_XXXX;	// 8 pin ports 
		break;

	case Fast16_Isa:
	case Fast16_Pci:
		sprintf(szCard, "FAST");	// Fast card
		PortType = FAST_6PIN_XXXX;	// 6 pin ports 
		break;

	case Fast16FMC_Pci:
		sprintf(szCard, "FAST");	// Fast card
		PortType = FAST_8PIN_XXXX;	// 8 pin Full Modem Control (FMC) ports 
		break;

 	case RAS4_Pci:
	case RAS8_Pci:
		sprintf(szCard, "SPDRAS");	// RAS card
		PortType = MODEM_PORT;		// Modem Ports
		break;

	case Speed2_Pci:
		sprintf(szCard, "SPD2");	// Speed 2 card
		PortType = SPD_8PIN_RJ45;	// 8 pin RJ45 ports
		break;

	case Speed2P_Pci:
		sprintf(szCard, "SPD2P");	// Speed 2+ card
		PortType = SPD_10PIN_RJ45;	// 10 pin RJ45 ports
		break;

	case Speed4_Pci:
		sprintf(szCard, "SPD4");	// Speed 4 card
		PortType = SPD_8PIN_RJ45;	// 8 pin RJ45 ports
		break;

	case Speed4P_Pci:
		sprintf(szCard, "SPD4P");	// Speed 4+ card
		PortType = SPD_10PIN_RJ45;	// 10 pin RJ45 ports
		break;

	default:
		sprintf(szCard, "XXX");		// Unknown card type
		break;
	}



	// Initialise device identifiers... 
	switch(PortType)
	{	
	case FAST_8PIN_RJ45:
		sprintf(szTemp,"FAST\\%s&8PINRJ45", szCard);
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"FAST\\%s&8PINRJ45", szCard);
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);
		
		sprintf(szTemp,"Perle FAST Serial Port %d", pPort->PortNumber+1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);
		break;

	case FAST_8PIN_XXXX:
		sprintf(szTemp,"FAST\\%s&8PINXXXX", szCard);
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"FAST\\%s&8PINXXXX", szCard);
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);
		
		sprintf(szTemp,"Perle FAST Serial Port %d", pPort->PortNumber+1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);
		break;

	case FAST_6PIN_XXXX:
		sprintf(szTemp,"FAST\\%s&6PINXXXX", szCard);
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"FAST\\%s&6PINXXXX", szCard);
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);
		
		sprintf(szTemp,"Perle FAST Serial Port %d", pPort->PortNumber+1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);
		break;

	case MODEM_PORT:
		sprintf(szTemp,"SPDRAS\\RASPort");
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"SPDRAS\\RASPort");
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);
		
		sprintf(szTemp,"Perle RAS Port %d", pPort->PortNumber+1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);
		break;

	case SPD_8PIN_RJ45:
		sprintf(szTemp,"SPEED\\%s&8PINRJ45", szCard);
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"SPEED\\%s&8PINRJ45", szCard);
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);
		
		sprintf(szTemp,"Perle SPEED Serial Port %d", pPort->PortNumber+1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);
		break;

	case SPD_10PIN_RJ45:
		sprintf(szTemp,"SPEED\\%s&10PINRJ45", szCard);
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"SPEED\\%s&10PINRJ45", szCard);
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);
		
		sprintf(szTemp,"Perle SPEED Serial Port %d", pPort->PortNumber+1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);
		break;

	default:
		sprintf(szTemp,"SPEED\\%s&XXXXXXXX", szCard);
		Spx_InitMultiString(FALSE, &pPort->DeviceID, szTemp, NULL);

		sprintf(szTemp,"SPEED\\%s&XXXXXXXX", szCard);
		Spx_InitMultiString(TRUE, &pPort->HardwareIDs, szTemp, NULL);

		sprintf(szTemp,"Perle Serial Port %d of Unknown Type.", pPort->PortNumber + 1);
		Spx_InitMultiString(FALSE, &pPort->DevDesc, szTemp, NULL);

		status = STATUS_UNSUCCESSFUL;
		break;
	}


/* Not required as we are using INF file 
	i = sprintf(szTemp, "Port %d on ", pPort->PortNumber + 1);

	sprintf(szTemp+i, "PCI Card 0x%08lX", pCard->PhysAddr);

	Spx_InitMultiString(FALSE, &pPort->DevLocation, szTemp, NULL);
*/



	pPort->pUartLib = &pCard->UartLib;	// Store pointer to UART library functions in port.

	// Set base address of port
	InitUart.UartNumber		= pPort->PortNumber;
	InitUart.BaseAddress	= pCard->Controller + (pPort->PortNumber * pCard->UARTOffset);
	InitUart.RegisterStride = pCard->UARTRegStride;
	InitUart.ClockFreq		= pCard->ClockRate; 
	
	if(pPort->pUartLib->UL_InitUart_XXXX(&InitUart, pCard->pFirstUart, &(pPort->pUart)) != UL_STATUS_SUCCESS)
	{
		pPort->pUartLib = NULL;	// NULL pointer to UART library functions.
		return STATUS_UNSUCCESSFUL;
	}

	pPort->pUartLib->UL_SetAppBackPtr_XXXX(pPort->pUart, pPort);	// Set back ptr.

	if(pCard->pFirstUart == NULL)
		pCard->pFirstUart = pPort->pUart;

	pPort->Interrupt = pCard->Interrupt;



/*	pPort->RFLAddress = pCard->LocalConfigRegisters + URL + pPort->PortNumber;
	pPort->TFLAddress = pCard->LocalConfigRegisters + UTL + pPort->PortNumber;
//	pPort->InterruptStatus = pCard->LocalConfigRegisters
*/
	return status;
}




NTSTATUS
XXX_PortStart(IN PPORT_DEVICE_EXTENSION pPort)
{
	NTSTATUS status = STATUS_SUCCESS;
	PCARD_DEVICE_EXTENSION pCard = pPort->pParentCardExt;
	SET_BUFFER_SIZES BufferSizes;
	UART_INFO	UartInfo;
	
	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_PortStart for Port %d.\n", 
		PRODUCT_NAME, pPort->PortNumber));

	if(!KeSynchronizeExecution(pPort->Interrupt, SerialResetAndVerifyUart, pPort->DeviceObject))	// Verify UART exists.
	{
		SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Failed to find 16Cx5x Port %d.\n", PRODUCT_NAME, pPort->PortNumber));
		return STATUS_UNSUCCESSFUL;
	}

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Found 16Cx5x Port %d.\n", PRODUCT_NAME, pPort->PortNumber));
	
	KeSynchronizeExecution(pPort->Interrupt, SerialReset, pPort);		// Resets the port

	pPort->pUartLib->UL_GetUartInfo_XXXX(pPort->pUart, &UartInfo);	// Get UART Capabilities


	switch(pCard->CardType)
	{
	case Fast4_Isa:
	case Fast4_Pci:
	case Fast8_Isa:
	case Fast8_Pci:
	case Fast16_Isa:
	case Fast16_Pci:
	case Fast16FMC_Pci:
	case RAS4_Pci:
	case RAS8_Pci:
		{
			pPort->MaxTxFIFOSize		= UartInfo.MaxTxFIFOSize;	// Max Tx FIFO Size.
			pPort->MaxRxFIFOSize		= UartInfo.MaxRxFIFOSize;	// Max Rx FIFO Size.
			pPort->TxFIFOSize			= pPort->MaxTxFIFOSize;		// Default Tx FIFO Size.
			pPort->RxFIFOSize			= pPort->MaxRxFIFOSize;		// Default Rx FIFO Size.
			pPort->TxFIFOTrigLevel		= 8;						// Default Tx FIFO Trigger Level. 
			pPort->RxFIFOTrigLevel		= 56;						// Default Rx FIFO Trigger Level.
			pPort->LoFlowCtrlThreshold	= 16;						// Default Low Flow Control Threshold.
			pPort->HiFlowCtrlThreshold	= 60;						// Default High Flow Control Threshold.
			break;
		}

	case Speed2_Pci:
	case Speed2P_Pci:
	case Speed4_Pci:
	case Speed4P_Pci:
		{
			pPort->MaxTxFIFOSize		= UartInfo.MaxTxFIFOSize;	// Max Tx FIFO Size.
			pPort->MaxRxFIFOSize		= UartInfo.MaxRxFIFOSize;	// Max Rx FIFO Size.
			pPort->TxFIFOSize			= pPort->MaxTxFIFOSize;		// Default Tx FIFO Size.
			pPort->RxFIFOSize			= pPort->MaxRxFIFOSize;		// Default Rx FIFO Size.
			pPort->TxFIFOTrigLevel		= 16;						// Default Tx FIFO Trigger Level. 
			pPort->RxFIFOTrigLevel		= 100;						// Default Rx FIFO Trigger Level.
			pPort->LoFlowCtrlThreshold	= 16;						// Default Low Flow Control Threshold.
			pPort->HiFlowCtrlThreshold	= 112;						// Default High Flow Control Threshold.
			break;
		}

	default:
		break;
	}


#ifdef WMI_SUPPORT
	// Store Default FIFO settings for WMI
	pPort->SpeedWmiFifoProp.MaxTxFiFoSize				= pPort->MaxTxFIFOSize;
	pPort->SpeedWmiFifoProp.MaxRxFiFoSize				= pPort->MaxRxFIFOSize;
	pPort->SpeedWmiFifoProp.DefaultTxFiFoLimit			= pPort->TxFIFOSize;
	pPort->SpeedWmiFifoProp.DefaultTxFiFoTrigger		= pPort->TxFIFOTrigLevel;
	pPort->SpeedWmiFifoProp.DefaultRxFiFoTrigger		= pPort->RxFIFOTrigLevel;
	pPort->SpeedWmiFifoProp.DefaultLoFlowCtrlThreshold	= pPort->LoFlowCtrlThreshold;
	pPort->SpeedWmiFifoProp.DefaultHiFlowCtrlThreshold	= pPort->HiFlowCtrlThreshold;
#endif

	GetPortSettings(pPort->DeviceObject);	// Get Saved Port Settings if present.

	// Initialize the list heads for the read, write, and mask queues... 
	InitializeListHead(&pPort->ReadQueue);
	InitializeListHead(&pPort->WriteQueue);
	InitializeListHead(&pPort->MaskQueue);
	InitializeListHead(&pPort->PurgeQueue);

	// Initialize the spinlock associated with fields read (& set) by IO Control functions... 
	KeInitializeSpinLock(&pPort->ControlLock);

	// Initialize the timers used to timeout operations... 
	KeInitializeTimer(&pPort->ReadRequestTotalTimer);
	KeInitializeTimer(&pPort->ReadRequestIntervalTimer);
	KeInitializeTimer(&pPort->WriteRequestTotalTimer);
	KeInitializeTimer(&pPort->ImmediateTotalTimer);
	KeInitializeTimer(&pPort->XoffCountTimer);
	KeInitializeTimer(&pPort->LowerRTSTimer);

	// Initialise the dpcs that will be used to complete or timeout various IO operations... 
	KeInitializeDpc(&pPort->CommWaitDpc, SerialCompleteWait, pPort);
	KeInitializeDpc(&pPort->CompleteReadDpc, SerialCompleteRead, pPort);
	KeInitializeDpc(&pPort->CompleteWriteDpc, SerialCompleteWrite, pPort);
	KeInitializeDpc(&pPort->TotalImmediateTimeoutDpc, SerialTimeoutImmediate, pPort);
	KeInitializeDpc(&pPort->TotalReadTimeoutDpc, SerialReadTimeout, pPort);
	KeInitializeDpc(&pPort->IntervalReadTimeoutDpc, SerialIntervalReadTimeout, pPort);
	KeInitializeDpc(&pPort->TotalWriteTimeoutDpc, SerialWriteTimeout, pPort);
	KeInitializeDpc(&pPort->CommErrorDpc, SerialCommError, pPort);
	KeInitializeDpc(&pPort->CompleteImmediateDpc, SerialCompleteImmediate, pPort);
	KeInitializeDpc(&pPort->XoffCountTimeoutDpc, SerialTimeoutXoff, pPort);
	KeInitializeDpc(&pPort->XoffCountCompleteDpc, SerialCompleteXoff, pPort);
	KeInitializeDpc(&pPort->StartTimerLowerRTSDpc, SerialStartTimerLowerRTS, pPort);
	KeInitializeDpc(&pPort->PerhapsLowerRTSDpc, SerialInvokePerhapsLowerRTS, pPort);


	// Default device control fields... 
	pPort->SpecialChars.XonChar			= SERIAL_DEF_XON;
	pPort->SpecialChars.XoffChar		= SERIAL_DEF_XOFF;
	pPort->HandFlow.ControlHandShake	= SERIAL_DTR_CONTROL;
	pPort->HandFlow.FlowReplace			= SERIAL_RTS_CONTROL;


	// Define which baud rates can be supported... 
	pPort->SupportedBauds = SERIAL_BAUD_USER;

	pPort->UartConfig.TxBaud = 75;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_075;

	pPort->UartConfig.TxBaud = 110;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_110;

	pPort->UartConfig.TxBaud = 134;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_134_5;

	pPort->UartConfig.TxBaud = 150;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_150;

	pPort->UartConfig.TxBaud = 300;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_300;

	pPort->UartConfig.TxBaud = 600;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_600;

	pPort->UartConfig.TxBaud = 1200;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_1200;

	pPort->UartConfig.TxBaud = 1800;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_1800;

	pPort->UartConfig.TxBaud = 2400;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_2400;

	pPort->UartConfig.TxBaud = 4800;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_4800;

	pPort->UartConfig.TxBaud = 7200;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_7200;

	pPort->UartConfig.TxBaud = 9600;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_9600;

	pPort->UartConfig.TxBaud = 14400;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_14400;

	pPort->UartConfig.TxBaud = 19200;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_19200;

	pPort->UartConfig.TxBaud = 38400;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_38400;

	pPort->UartConfig.TxBaud = 56000;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_56K;

	pPort->UartConfig.TxBaud = 57600;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_57600;

	pPort->UartConfig.TxBaud = 115200;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_115200;

	pPort->UartConfig.TxBaud = 128000;
	if(KeSynchronizeExecution(pPort->Interrupt, SerialSetBaud, pPort) == TRUE)
		pPort->SupportedBauds |= SERIAL_BAUD_128K;


	// Default line configuration: 1200,E,7,1 
	pPort->UartConfig.TxBaud	= 1200;
	pPort->LineControl			= SERIAL_EVEN_PARITY | SERIAL_7_DATA | SERIAL_1_STOP;
	pPort->ValidDataMask		= 0x7F;

	// Set Frame Config 
	pPort->UartConfig.FrameConfig = (pPort->UartConfig.FrameConfig & ~UC_FCFG_DATALEN_MASK) | UC_FCFG_DATALEN_7;
	pPort->UartConfig.FrameConfig = (pPort->UartConfig.FrameConfig & ~UC_FCFG_PARITY_MASK) | UC_FCFG_EVEN_PARITY;
	pPort->UartConfig.FrameConfig = (pPort->UartConfig.FrameConfig & ~UC_FCFG_STOPBITS_MASK) | UC_FCFG_STOPBITS_1;




    // Mark this device as not being opened by anyone.  We keep a variable
	// around so that spurious interrupts are easily dismissed by the ISR.
    pPort->DeviceIsOpen		= FALSE;


//	pPort->UartConfig.SpecialMode |= UC_SM_LOOPBACK_MODE;   // Internal Loopback mode

	// Set up values for interval timing... 
	
	// Store values into the extension for interval timing. If the interval
	// timer is less than a second then come in with a short "polling" loop.
    // For large (> then 2 seconds) use a 1 second poller.
    pPort->ShortIntervalAmount.QuadPart = -1;
    pPort->LongIntervalAmount.QuadPart	= -10000000;
    pPort->CutOverAmount.QuadPart		= 200000000;


#ifdef WMI_SUPPORT
	//
	// Fill in WMI hardware data
	//

	pPort->WmiHwData.IrqNumber			= pCard->TrIrql;
	pPort->WmiHwData.IrqVector			= pCard->TrVector;
	pPort->WmiHwData.IrqLevel			= pCard->TrIrql;
	pPort->WmiHwData.IrqAffinityMask	= pCard->ProcessorAffinity;
	
	if(pCard->InterruptMode == Latched)
		pPort->WmiHwData.InterruptType = SERIAL_WMI_INTTYPE_LATCHED;
	else
		pPort->WmiHwData.InterruptType = SERIAL_WMI_INTTYPE_LEVEL;

	pPort->WmiHwData.BaseIOAddress = (ULONG_PTR)pCard->Controller;


	//
	// Fill in WMI device state data (as defaults)
	//

	pPort->WmiCommData.BaudRate					= pPort->UartConfig.TxBaud;
	UPDATE_WMI_LINE_CONTROL(pPort->WmiCommData, pPort->LineControl);
	UPDATE_WMI_XON_XOFF_CHARS(pPort->WmiCommData, pPort->SpecialChars);
	UPDATE_WMI_XMIT_THRESHOLDS(pPort->WmiCommData, pPort->HandFlow);

	pPort->WmiCommData.MaximumBaudRate			= 115200U;	// 115200k baud max
	pPort->WmiCommData.MaximumOutputBufferSize	= (UINT32)((ULONG)-1);
	pPort->WmiCommData.MaximumInputBufferSize	= (UINT32)((ULONG)-1);

	pPort->WmiCommData.Support16BitMode			= FALSE;
	pPort->WmiCommData.SupportDTRDSR			= TRUE;
	pPort->WmiCommData.SupportIntervalTimeouts	= TRUE;
	pPort->WmiCommData.SupportParityCheck		= TRUE;
	pPort->WmiCommData.SupportRTSCTS			= TRUE;
	pPort->WmiCommData.SupportXonXoff			= TRUE;
	pPort->WmiCommData.SettableBaudRate			= TRUE;
	pPort->WmiCommData.SettableDataBits			= TRUE;
	pPort->WmiCommData.SettableFlowControl		= TRUE;
	pPort->WmiCommData.SettableParity			= TRUE;
	pPort->WmiCommData.SettableParityCheck		= TRUE;
	pPort->WmiCommData.SettableStopBits			= TRUE;
	pPort->WmiCommData.IsBusy					= FALSE;


	// Fill in wmi perf data (all zero's)
	RtlZeroMemory(&pPort->WmiPerfData, sizeof(pPort->WmiPerfData));



	//
    // Register for WMI
	//
	
	SpeedPort_WmiInitializeWmilibContext(&pPort->WmiLibInfo);

	IoWMIRegistrationControl(pPort->DeviceObject, WMIREG_ACTION_REGISTER);
#endif


	// Initialise the port hardware... 
	KeSynchronizeExecution(pPort->Interrupt, SerialReset, pPort);					// Resets the port
	KeSynchronizeExecution(pPort->Interrupt, ApplyInitialPortSettings, pPort);		// Apply settings
	KeSynchronizeExecution(pPort->Interrupt, SerialMarkClose, pPort);				// Disables the FIFO 
	KeSynchronizeExecution(pPort->Interrupt, SerialClrRTS, pPort);					// Clear RTS signal
	KeSynchronizeExecution(pPort->Interrupt, SerialClrDTR, pPort);					// Clear DTR signal 
	
	return status;
}


NTSTATUS
XXX_PortStop(IN PPORT_DEVICE_EXTENSION pPort)
{
	NTSTATUS status = STATUS_SUCCESS;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_PortStop for Port %d.\n", 
		PRODUCT_NAME, pPort->PortNumber));

#ifdef WMI_SUPPORT
	IoWMIRegistrationControl(pPort->DeviceObject, WMIREG_ACTION_DEREGISTER);

	RtlZeroMemory(&pPort->WmiLibInfo, sizeof(WMILIB_CONTEXT));
#endif

	// Cancel timers...
    KeCancelTimer(&pPort->ReadRequestTotalTimer);
    KeCancelTimer(&pPort->ReadRequestIntervalTimer);
    KeCancelTimer(&pPort->WriteRequestTotalTimer);
    KeCancelTimer(&pPort->ImmediateTotalTimer);
    KeCancelTimer(&pPort->XoffCountTimer);
	KeCancelTimer(&pPort->LowerRTSTimer);

	// Cancel pending DPCs...
	KeRemoveQueueDpc(&pPort->CommWaitDpc);
	KeRemoveQueueDpc(&pPort->CompleteReadDpc);
	KeRemoveQueueDpc(&pPort->CompleteWriteDpc);
	KeRemoveQueueDpc(&pPort->TotalReadTimeoutDpc);
	KeRemoveQueueDpc(&pPort->IntervalReadTimeoutDpc);
	KeRemoveQueueDpc(&pPort->TotalWriteTimeoutDpc);
	KeRemoveQueueDpc(&pPort->CommErrorDpc);
	KeRemoveQueueDpc(&pPort->CompleteImmediateDpc);
	KeRemoveQueueDpc(&pPort->TotalImmediateTimeoutDpc);
	KeRemoveQueueDpc(&pPort->XoffCountTimeoutDpc);
	KeRemoveQueueDpc(&pPort->XoffCountCompleteDpc);
	KeRemoveQueueDpc(&pPort->StartTimerLowerRTSDpc);
	KeRemoveQueueDpc(&pPort->PerhapsLowerRTSDpc);

	KeSynchronizeExecution(pPort->Interrupt, SerialReset, pPort);		// Resets the port

	return status;
}

NTSTATUS
XXX_PortDeInit(IN PPORT_DEVICE_EXTENSION pPort)
{
	PCARD_DEVICE_EXTENSION pCard = pPort->pParentCardExt;
	NTSTATUS status = STATUS_SUCCESS;

	PAGED_CODE();	// Macro in checked build to assert if pagable code is run at or above dispatch IRQL 

	SpxDbgMsg(SPX_TRACE_CALLS, ("%s: Entering XXX_PortDeInit for Port %d.\n", 
		PRODUCT_NAME, pPort->PortNumber));

	// If we are about to DeInit the first UART object to be serviced next
	// Make the pFirstUart point to the next UART in the list.
	if(pPort->pUart == pCard->pFirstUart)
		pCard->pFirstUart = pCard->UartLib.UL_GetUartObject_XXXX(pPort->pUart, UL_OP_GET_NEXT_UART);

	pCard->UartLib.UL_DeInitUart_XXXX(pPort->pUart);	// DeInit UART
	pPort->pUart = NULL;

	pPort->pUartLib = NULL;	// NULL pointer to UART library functions.


	// Free identifier string allocations... 
	if(pPort->DeviceID.Buffer != NULL)
		ExFreePool(pPort->DeviceID.Buffer);

	if(pPort->InstanceID.Buffer != NULL)
		ExFreePool(pPort->InstanceID.Buffer);
	
	if(pPort->HardwareIDs.Buffer != NULL)	
		ExFreePool(pPort->HardwareIDs.Buffer);

	if(pPort->DevDesc.Buffer != NULL)
		ExFreePool(pPort->DevDesc.Buffer);

	if(pPort->DevLocation.Buffer != NULL)
		ExFreePool(pPort->DevLocation.Buffer);


	return status;
}