/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-2000 Microsoft Corporation.  All rights reserved.
Copyright (c) 2001-2002 SAMSUNG Electronics Corporation.  All rights reserved.

Author	hmseo@sec.samsung.com (SAMSUNG Electronics)

Module Name:  

ser2440_hw.c

Abstract:  

    This file implements the standard device specific functions for a S3C2440
    based serial device.

Functions:
SER_RegAlloc
SER_VirtualAlloc
SER_VirtualFree
ReadLSR
ReadMSR
ShowSerialRegisters
ClearPendingInts
S2440_SetIrDAIOP
S2440_SetSerialIOP
S2440_SetIOP
SL_Open
SL_Close
SL_Init
SL_PostInit
SL_Deinit
SL_ClearDTR
SL_SetDTR
SL_ClearRTS
SL_SetRTS
SL_ClearBreak
SL_SetBreak
SetBaudRate
SL_SetBaudRate
SL_SetByteSize
SL_SetParity
SL_SetStopBits
SL_GetRxBufferSize
SL_GetRxStart
SL_GetInterruptType
SL_RxIntr
SL_PutBytes
SL_TxIntr
SL_TxIntrEx
SL_LineIntr
SL_OtherIntr
SL_ModemIntr
SL_GetStatus
SL_Reset
SL_GetModemStatus
SL_PurgeComm
SL_XmitComChar
SL_PowerOff
SL_PowerOn
SL_SetDCB
SL_SetCommTimeouts
SL_Ioctl

--*/

#define DEBUGMODE 0
#define ZONE_FUNCTION	1
#define USERDBG			1
#define ZONE_WRITE		1

#include <windows.h>
#include <types.h>
#include <ceddk.h>
#include <memory.h>
#include <serhw.h>
#include <ser16550.h>
#include <hw16550.h>
#include <nkintr.h>
#include <devload.h>
#include <windev.h>
#include <notify.h>

#include <pm.h>
#include <excpt.h>

#include "pmplatform.h"
#include "S2440.h"
#include "ser2440.h"

#define PUBLIC		
#define PRIVATE		static

PRIVATE volatile IOPreg   * v_pIOPregs;
PRIVATE volatile UART0reg * v_pUART0regs;
PRIVATE volatile UART1reg * v_pUART1regs;
PRIVATE volatile UART2reg * v_pUART2regs;
PRIVATE volatile INTreg   * v_pINTregs;
PRIVATE volatile PWMreg   * v_pPWMregs;

VOID SER_VirtualFree(VOID);
BOOL SER_VirtualAlloc(VOID);

VOID ReadLSR(PVOID pHead);
VOID ReadMSR(PVOID pHead);

BOOL SL_SetByteSize(PVOID pHead, ULONG ByteSize);
BOOL SL_SetStopBits(PVOID pHead, ULONG StopBits);
BOOL SL_SetParity(PVOID pHead, ULONG Parity);
BOOL SerPowerOn(PVOID   pHead);
BOOL SerPowerOff(PVOID   pHead);

#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION 

PRIVATE PVOID
SER_RegAlloc(PVOID addr, INT sz)
{
	PVOID reg;

	reg = (PVOID)VirtualAlloc(0, sz, MEM_RESERVE, PAGE_NOACCESS);

	if (reg)
	{
		if (!VirtualCopy(reg, addr, sz, PAGE_READWRITE | PAGE_NOCACHE )) 
		{
			VirtualFree(reg, sz, MEM_RELEASE);
			reg = NULL;
		}
	}

	return reg;
}


PRIVATE BOOL
SER_VirtualAlloc(VOID)
{
	BOOL r = FALSE;

    RETAILMSG(DEBUGMODE,(TEXT("::: SER_VirtualAlloc()\r\n")));

	do
	{
		v_pIOPregs = (volatile IOPreg *)SER_RegAlloc((PVOID)IOP_BASE, sizeof(IOPreg));
		if (v_pIOPregs == NULL) 
		{
			ERRORMSG(1,(TEXT("For IOPreg: VirtualAlloc failed!\r\n")));
			break;
		}
	
		v_pUART0regs = (volatile UART0reg *)SER_RegAlloc((PVOID)UART0_BASE, sizeof(UART0reg));
		if (v_pUART0regs == NULL) 
		{
	    	ERRORMSG(1,(TEXT("For UART0reg: VirtualAlloc failed!\r\n")));
		    break;
		}

		v_pUART1regs = (volatile UART1reg *)SER_RegAlloc((PVOID)UART1_BASE, sizeof(UART1reg));
		if (v_pUART1regs == NULL) 
		{
	    	ERRORMSG(1,(TEXT("For UART1reg: VirtualAlloc failed!\r\n")));
		    break;
		}

		v_pUART2regs = (volatile UART2reg *)SER_RegAlloc((PVOID)UART2_BASE, sizeof(UART2reg));
		if (v_pUART2regs == NULL) 
		{
	    	ERRORMSG(1,(TEXT("For UART2reg: VirtualAlloc failed!\r\n")));
		    break;
		}

		v_pINTregs = (volatile INTreg *)SER_RegAlloc((PVOID)INT_BASE, sizeof(INTreg));
		if (v_pINTregs == NULL) 
		{
	    	ERRORMSG(1,(TEXT("For INTregs: VirtualAlloc failed!\r\n")));
		    break;
		}

		v_pPWMregs = (volatile PWMreg *)SER_RegAlloc((PVOID)PWM_BASE, sizeof(PWMreg));
		if (v_pPWMregs == NULL) 
		{
	    	ERRORMSG(1,(TEXT("For PWMregs: VirtualAlloc failed!\r\n")));
		    break;
		}
		
		r = TRUE;
	} while (0);

	if (!r)
	{
		SER_VirtualFree();

		RETAILMSG(DEBUGMODE,(TEXT("::: SER_VirtualAlloc() - Fail\r\n")));
	}
	else
	{
		RETAILMSG(DEBUGMODE,(TEXT("::: SER_VirtualAlloc() - Success\r\n")));
	}


	return r;
}

PRIVATE void
SER_VirtualFree(VOID)
{
    RETAILMSG(DEBUGMODE,(TEXT("::: SER_VirtualFree()\r\n")));

	if (v_pIOPregs)
    {
        VirtualFree((PVOID)v_pIOPregs, sizeof(IOPreg), MEM_RELEASE);
        v_pIOPregs = NULL;
    }
    if (v_pUART0regs)
    {   
        VirtualFree((PVOID)v_pUART0regs, sizeof(UART0reg), MEM_RELEASE);
        v_pUART0regs = NULL;
    } 
    if (v_pUART1regs)
    {   
        VirtualFree((PVOID)v_pUART1regs, sizeof(UART1reg), MEM_RELEASE);
        v_pUART1regs = NULL;
    } 
    if (v_pUART2regs)
    {   
        VirtualFree((PVOID)v_pUART2regs, sizeof(UART2reg), MEM_RELEASE);
        v_pUART2regs = NULL;
    } 
    if (v_pINTregs)
    {   
        VirtualFree((PVOID)v_pINTregs, sizeof(INTreg), MEM_RELEASE);
        v_pINTregs = NULL;
    } 
    if (v_pPWMregs)
    {   
        VirtualFree((PVOID)v_pPWMregs, sizeof(INTreg), MEM_RELEASE);
        v_pPWMregs = NULL;
    } 
}

//
// Reading the LSR clears most of its bits.  So, we provide this wrapper,
// which reads the register, records any interesting values, and
// stores the current LSR contents in the shadow register.
//
//__inline
VOID
ReadLSR(PVOID pHead)
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;
	ULONG LineEvents             = 0;
	ULONG LineStatus             = 0;
	UCHAR tmp;

	pHWHead->CommErrors = 0;	// Clear old errors each time

	RETAILMSG(DEBUGMODE, (TEXT("ReadLSR \r\n")));

	try 
	{
		LineStatus = INREG(pHWHead,rUERSTAT);
		RETAILMSG(DEBUGMODE,  (TEXT("Line Status Register : 0x%x\r\n"), LineStatus));
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) 
	{
		// pHWHead->LSR = SERIAL_LSR_THRE;
	}

	if ( LineStatus & (COM2440_LSR_OE | COM2440_LSR_PE | COM2440_LSR_FE))
	{
		// Note: Its not wise to do debug msgs in here since they will
		// pretty much guarantee that the FIFO gets overrun.
		if ( LineStatus & COM2440_LSR_OE )
		{
			// When overrun error occurs, S2440 rURXH must be read.
			tmp = *(pHWHead->pUFRXH);
			pHWHead->DroppedBytes++;
			pHWHead->CommErrors |= CE_OVERRUN;
			LineEvents |= EV_ERR;
		}

		if ( LineStatus & COM2440_LSR_PE )
		{
			pHWHead->CommErrors |= CE_RXPARITY;
			LineEvents |= EV_ERR;
		}

		if ( LineStatus & COM2440_LSR_FE )
		{
			pHWHead->CommErrors |= CE_FRAME;
			LineEvents |= EV_ERR;
		}
	}

	if ( LineStatus & COM2440_LSR_BI )
		LineEvents |= EV_BREAK;

	// Let WaitCommEvent know about this error
	if ( LineEvents )
		pHWHead->EventCallback( pHWHead->pMddHead, LineEvents );
}

//
// Reading the MSR clears many of its bits.  So, we provide this wrapper,
// which reads the register, records any interesting values, and
// stores the current MSR contents in the shadow register.
// Note that we always have DDCD and DCTS enabled, so if someone
// wants to keep an eye on these lines, its OK to simply read the
// shadow register, since if the value changes, the interrupt
// will cause the shadow to be updated.
//
//__inline
VOID
ReadMSR(PVOID pHead)
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;
	ULONG Events = 0;
	ULONG msr, msr_DSR;

	RETAILMSG(DEBUGMODE, (TEXT("ReadMSR \r\n")));
	try 
	{
		// read nCTS status
		msr = INREG(pHWHead,rUMSTAT);
		RETAILMSG(DEBUGMODE,  (TEXT("Modem Status Register : 0x%x\r\n"), msr));

		// read nDSR status 
		// - As you know this routine is valid only at the SMDK2440 board.
		if ( !pHWHead->UseIrDA )
		{
			if(*(pHWHead->rDSRport) & (1<<(pHWHead->DsrPortNum)))	// Low active
			    msr_DSR = 0;
			else
			    msr_DSR = 1;
		}
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		msr = 0;
	}

    // Save the MSR value in a shadow
    pHWHead->MSR = msr;

	// For changes, we use callback to evaluate the event
	if (msr & COM2440_MSR_DCTS)  // Delta CTS (SERIAL_MSR_DCTS)
	{
		Events |= EV_CTS;
		pHWHead->vUMSTAT |= COM2440_MSR_DCTS;
	} 

	if(msr & COM2440_MSR_CTS) 
		pHWHead->vUMSTAT |= COM2440_MSR_CTS;
	else
		pHWHead->vUMSTAT &= ~COM2440_MSR_CTS;

	// S2440 does not support Delta DSR status by H/W,
	// so we have to check this by S/W.
	//
	// if(DSR : High->Low) - became active
	if (msr_DSR)			// S2440 Evaluation Board bidged these two pins.
	{
		if(!(pHWHead->vUMSTAT & COM2440_MSR_DSR))
		{
			pHWHead->vUMSTAT |= COM2440_MSR_DDSR;
			Events |= EV_DSR;
			Events |= EV_RLSD;  // DCD
		}    // there is a change... H->L(became active)
		else
			pHWHead->vUMSTAT &= ~COM2440_MSR_DDSR;
		pHWHead->vUMSTAT |= COM2440_MSR_DSR;
	}
	// if(DSR : Low->High) - became in-active
	else
	{
		if((pHWHead->vUMSTAT & COM2440_MSR_DSR))
		{
			pHWHead->vUMSTAT |= COM2440_MSR_DDSR;
			Events |= EV_DSR;
			Events |= EV_RLSD;  // DCD
		}   // there is a change... L->H (became inactive)
		else
			pHWHead->vUMSTAT &= ~COM2440_MSR_DDSR;
		pHWHead->vUMSTAT &= ~COM2440_MSR_DSR;
	}

	RETAILMSG(DEBUGMODE,  (TEXT("ReadMSR Events:0x%x\r\n"), Events));
	RETAILMSG(DEBUGMODE,  (TEXT("ReadMSR MSR:0x%x\r\n"), msr));
	RETAILMSG(DEBUGMODE,  (TEXT("ReadMSR MSR_DSR:0x%x\r\n"), msr_DSR));   

	if ( Events )
		pHWHead->EventCallback( pHWHead->pMddHead, Events );
}

VOID
ShowSerialRegisters(PVOID  pHead)
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(1, (TEXT("InterruptType : 0x%X, 0x%X, 0x%X, 0x%X\r\n"), 		
					*(pHWHead->UART_INTMASK), 		
					*(pHWHead->UART_INTSUBMASK),  		
					*(pHWHead->UART_INTSRCPND), 		
					*(pHWHead->UART_INTSUBSRCPND)));
	RETAILMSG(1,  (TEXT("rULCON: 0x%x "), pHWHead->s2440SerReg->rULCON));
	RETAILMSG(1,  (TEXT("rUCON: 0x%x "), pHWHead->s2440SerReg->rUCON));
	RETAILMSG(1,  (TEXT("rUFCON: 0x%x "), pHWHead->s2440SerReg->rUFCON));
	RETAILMSG(1,  (TEXT("rUMCON: 0x%x "), pHWHead->s2440SerReg->rUMCON));
	RETAILMSG(1,  (TEXT("rUERSTAT: 0x%x "), pHWHead->s2440SerReg->rUERSTAT));
	RETAILMSG(1,  (TEXT("rUFSTAT: 0x%x "), pHWHead->s2440SerReg->rUFSTAT));
	RETAILMSG(1,  (TEXT("rUMSTAT: 0x%x "), pHWHead->s2440SerReg->rUMSTAT));
	RETAILMSG(1,  (TEXT("rUTXH: 0x%x "), pHWHead->s2440SerReg->rUTXH));
	RETAILMSG(1,  (TEXT("rURXH: 0x%x "), pHWHead->s2440SerReg->rURXH));
	RETAILMSG(1,  (TEXT("rUBRDIV: 0x%x\r\n"), pHWHead->s2440SerReg->rUBRDIV));
	RETAILMSG(1,  (TEXT("pHWHead->vUMSTAT: 0x%x \r\n"), pHWHead->vUMSTAT));
	RETAILMSG(1,  (TEXT("pHWHead->CTSFlowOff: 0x%x \r\n\r\n"), pHWHead->CTSFlowOff));
}

#define BAUD_TABLE_SIZE 23
static const
PAIRS    LS_BaudPairs[BAUD_TABLE_SIZE] =
{
	{50,        2307},
	{75,        1538},
	{110,       1049},
	{135,        858},
	{150,        769},
	{300,        384},
	{600,        192},
	{1200,        96},
	{1800,        64},
	{2000,        58},
	{2400,        48},
	{3600,        32},
	{4800,        24},
	{7200,        16},
	{9600,        12},
	{12800,        9},
	{14400,        8},
	{19200,        6},
	{23040,        5},
	{28800,        4},
	{38400,        3},
	{57600,        2},
	{115200,       1}
};

static const
LOOKUP_TBL  LS_BaudTable = {BAUD_TABLE_SIZE, (PAIRS *) LS_BaudPairs};

// Routine to clear any pending interrupts.  Called from Init and PostInit
// to make sure we atart out in a known state.
VOID
ClearPendingInts(PVOID   pHead) // @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
	UINT32 tmpReg;

	EnterCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE,(TEXT("ClearPendingInts \r\n")));
	try
	{
		SETREG(pHWHead,rUFCON,0x6);    // tx, rx fifo reset
		ClearSubINTPnd(pHWHead, pHWHead->bTxINT | pHWHead->bRxINT | pHWHead->bErrINT);
		ClearINTPnd(pHWHead, pHWHead->bINT);
		tmpReg = INREG(pHWHead,rUERSTAT);
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		RETAILMSG(1,(TEXT("SL_PostInit, 0x%X - ERROR\r\n"), pHWHead));
		// Just fall through & release CritSec
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));
}

// Routine to set some 2440 IO Ports.
VOID
S2440_SetIrDAIOP(PVOID   pHead)			// @parm points to device head
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;
	RETAILMSG(DEBUGMODE, (TEXT("S2440_SetIrDAIOP \r\n")));

	EnterCriticalSection(&(pHWHead->RegCritSec));

	// by jylee 2003.04.15
	v_pIOPregs->rGPBCON &= ~(0x3<<2);	// clear GPBCON for GPB1 (nIRDAEN)
	v_pIOPregs->rGPBCON |=  (0x1<<2);	// set GPBCON for GPB1 output
	v_pIOPregs->rGPBUP  |=  (0x1<<1);		// set GPB1 pullup disable (external pullup)
	v_pIOPregs->rGPBDAT &= ~(0x1<<1);	// set GPB1 signal low 
	v_pIOPregs->rGPHCON &= ~(0x3<<12 | 0x3<<14); // clear uart 2 - rx, tx
	v_pIOPregs->rGPHCON |=  (0x2<<12 | 0x2<<14); 
	v_pIOPregs->rGPHUP  |=  0xc0;

	LeaveCriticalSection(&(pHWHead->RegCritSec));
}

S2440_SetSerialIOP(PVOID   pHead)		// @parm points to device head
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;
	PSER_INFO	   pHWHead1  = (PSER_INFO)pHead;

	if(pHWHead1->dwIOBase == 0x50000000)	//UART0		
	{
		EnterCriticalSection(&(pHWHead->RegCritSec));
		v_pIOPregs->rGPHCON &= ~(0x3<<0 | 0x3<<2 | 0x3<<4 | 0x3<<6);	// clear uart 0 - rx, tx
		v_pIOPregs->rGPHCON |= (0x2<<4 | 0x2<<6);	//enabled RXD[0] TXD[0]
		//v_pIOPregs->rGPDCON &= ~(0x3<<0 | 0x3<<2);
		//v_pIOPregs->rGPDCON |= (0x1<<0 | 0x0<<2);
		v_pIOPregs->rGPHUP  |= 0xc3;	//disable pull up
	#if USE_AFC
		// set nRTS to GPO mode
		// for software flow-control
		v_pIOPregs->rGPHCON |= (0x0<<0 | 0x1<<2 );
	#else
		v_pIOPregs->rGPHCON |= (0x2<<0 | 0x2<<2 );
	#endif

		/*
		In the SMDK2440 board, if IRDA is enabled, following values are ignored.
		*/
	#if 0 // Board version is not 0.17
		v_pIOPregs->rGPHUP  |= 0xc3;
		pHWHead->rDTRport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT);
		pHWHead->rDSRport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT);
		pHWHead->DtrPortNum = 6;
		pHWHead->DsrPortNum = 7;
	#endif
/*
		v_pIOPregs->rGPDUP  |= 0x3;
		pHWHead->rDTRport = (volatile unsigned int *)&(v_pIOPregs->rGPDDAT);
		pHWHead->rDSRport = (volatile unsigned int *)&(v_pIOPregs->rGPDDAT);
		pHWHead->DtrPortNum = 0;
		pHWHead->DsrPortNum = 1;
*/		
	#if USE_AFC    	
 		pHWHead->rCTSport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT);
		pHWHead->rRTSport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT);
		pHWHead->CtsPortNum = 0;
		pHWHead->RtsPortNum = 1;
	#endif
	}

	else if(pHWHead1->dwIOBase == 0x50004000)	// UART1	
	{
		EnterCriticalSection(&(pHWHead->RegCritSec));
		v_pIOPregs->rGPHCON &= ~(0x3<<8 | 0x3<<10 );	// clear uart 1 - rx, tx
		v_pIOPregs->rGPHCON |= (0x2<<8 | 0x2<<10 );		//enabled RXD[1] TXD[1]
		//v_pIOPregs->rGPHUP  |= 0xc0;
		v_pIOPregs->rGPHUP  |=   (1 << 4) | (1 << 5);	//disable pull up
		//pHWHead->rDTRport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT);
		//pHWHead->rDSRport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT);
		//pHWHead->DtrPortNum = 0;
		//pHWHead->DsrPortNum = 1;
	}
	
	else if(pHWHead1->dwIOBase == 0x50008000)	//UART2		
	{ 
		EnterCriticalSection(&(pHWHead->RegCritSec)); 
		v_pIOPregs->rGPHCON &= ~(0x3<<12 | 0x3<<14 );	//clear uart 2 - rx, tx
		v_pIOPregs->rGPHCON |= (0x2<<12 | 0x2<<14 );	//enabled RXD[2] TXD[2] 
		v_pIOPregs->rGPHUP  |= 0xc0; 
		/* pHWHead->rDTRport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT); 
		pHWHead->rDSRport = (volatile unsigned int *)&(v_pIOPregs->rGPHDAT); 
		pHWHead->DtrPortNum = 6; 
		pHWHead->DsrPortNum = 7;*/ 
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));
}

VOID
S2440_SetIOP(PVOID   pHead)			// @parm points to device head
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	if ( pHWHead->UseIrDA )
	{
		RETAILMSG(DEBUGMODE, (TEXT("S2440_SetIOP : IRDA\r\n")));
		S2440_SetIrDAIOP(pHWHead);
	}
	else
	{
		RETAILMSG(DEBUGMODE, (TEXT("S2440_SetIOP : SERIAL\r\n")));
		S2440_SetSerialIOP(pHWHead);
	}
}


//
/////////////////// Start of exported entrypoints ////////////////
//
//
// @doc OEM 
// @func PVOID | SL_Open | Configures 16550 for default behaviour.
//
VOID
SL_Open(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;

	RETAILMSG(1, (TEXT("SL_Open 0x%X (%d opens)\r\n"), pHead, pHWHead->OpenCount));
	// If the device is already open, all we do is increment count
	if ( pHWHead->OpenCount++ )
	{
		RETAILMSG(1, (TEXT("SL_Open 0x%X (%d opens)\r\n"), pHead, pHWHead->OpenCount));
		return ;
	}

	pHWHead->DroppedBytes = 0;
	pHWHead->CTSFlowOff   = FALSE;  // Not flowed off yet
	pHWHead->DSRFlowOff   = FALSE;  // Not flowed off yet
	pHWHead->CommErrors   = 0;
	pHWHead->ModemStatus  = 0;
	pHWHead->ConSetup     = 6;
	pHWHead->vUMSTAT      = 0;
	pHWHead->fSW_EnTxINT  = FALSE;

	EnterCriticalSection(&(pHWHead->RegCritSec));
	try
	{
		S2440_SetIOP(pHead);

		// Get defaults from the DCB structure
		SL_SetBaudRate( pHead, pHWHead->dcb.BaudRate );
		SL_SetByteSize( pHead, pHWHead->dcb.ByteSize );
		SL_SetStopBits( pHead, pHWHead->dcb.StopBits );
		SL_SetParity(pHead, pHWHead->dcb.Parity );

		// UART Control, Modem, Fifo register setting.
		OUTREG(pHWHead,rUCON,0x2c5);   //Clock selection=PCLK, tx=level,rx=pulse,enable timeout int.,
		//enable rx error int., Tx int, Rx int
		OUTREG(pHWHead,rUFCON,0x6);    // Reset FIFO
		
		if (pHWHead->UseIrDA) 
		{		
			OUTREG(pHWHead,rUFCON,0x41);   //FIFO enable : tx-4bytes, rx-4bytes
			OUTREG(pHWHead,rUMCON,0x00);   //Disable auto flow control.
		}
		else 
		{
			#if USE_AFC
			// by shin.0212.
			OUTREG(pHWHead,rUFCON,0x61);   //FIFO enable : tx-4bytes, rx-12bytes
			OUTREG(pHWHead,rUMCON,0x10);   //Enable auto flow control.  
			#else
			OUTREG(pHWHead,rUFCON,0x41);   //FIFO enable : tx-4bytes, rx-4bytes
			OUTREG(pHWHead,rUMCON,0x00);   //Disable auto flow control.
			#endif
		}
		
		if ( pHWHead->UseIrDA )
			SETREG(pHWHead,rULCON,SER2440_IRMODE_MASK);   // Infra-red mode enable.

		ClearPendingInts( pHWHead );

		EnINT(pHWHead, pHWHead->bINT);
		EnSubINT(pHWHead, pHWHead->bRxINT);

		// For CE 3.0, we are still supporting
		// the old style MDDs, and they don't call our PostInit, which
		// needs to happen sometime prior to this.  So for now, we go ahead
		// ahead and clear out interrupts one last time.  In 4.0, we can
		// kill the old serial MDD and assume that everyone uses the new
		// MDD and calls post init.  

		SL_PostInit(pHWHead);
		ReadLSR(pHWHead);
	}
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Just get out of here.
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));
}

//
// @doc OEM 
// @func PVOID | SL_Close | Does nothing except keep track of the
// open count so that other routines know what to do.
//
VOID
SL_Close(PVOID   pHead) // @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;

	RETAILMSG(1, (TEXT("SL_Close \r\n")));
	if ( pHWHead->OpenCount )	pHWHead->OpenCount--;

	EnterCriticalSection(&(pHWHead->RegCritSec));
	try
	{
		SL_ClearRTS( pHWHead );
		OUTREG(pHWHead,rUMSTAT,0x0);

		// Disable all interrupts and clear MCR.
		DisEnSubINT(pHWHead, pHWHead->bRxINT);
		pHWHead->fSW_EnTxINT = FALSE;

		// This routhine for auto detect.
		S2440_SetIrDAIOP(pHead);
	}
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Just get out of here.
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));
}

//
// @doc OEM 
// @func PVOID | SL_Init | Initializes 16550 device head.  
//

VOID
SL_Init(
       PVOID   pHead, // @parm points to device head
       PUCHAR  pRegBase, // Pointer to 16550 register base
       UINT8   RegStride, // Stride amongst the 16550 registers
       EVENT_FUNC EventCallback, // This callback exists in MDD
       PVOID   pMddHead,   // This is the first parm to callback
       PLOOKUP_TBL   pBaudTable  // BaudRate Table
       )
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;
	PSER_INFO	   pHWHead1  = (PSER_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("SL_Init : IRDA = %d \r\n"), pHWHead->UseIrDA));

	SER_VirtualAlloc();

	if ( pHWHead->UseIrDA )
	{
		pHWHead->bINT = BIT_UART2;
		pHWHead->bTxINT = INTSUB_TXD2;
		pHWHead->bRxINT = INTSUB_RXD2;
		pHWHead->bErrINT = INTSUB_ERR2;
		pHWHead->s2440SerReg = (S2440_UART_REG *)v_pUART2regs;
		pRegBase = (PUCHAR)pHWHead->s2440SerReg;
	}
	else
	{
		if(pHWHead1->dwIOBase == 0x50000000)	//select UART0		
		{
			pHWHead->bINT = BIT_UART0;
			pHWHead->bTxINT = INTSUB_TXD0;
			pHWHead->bRxINT = INTSUB_RXD0;
			pHWHead->bErrINT = INTSUB_ERR0;
			pHWHead->s2440SerReg = (S2440_UART_REG *)v_pUART0regs;
			pRegBase = (PUCHAR)pHWHead->s2440SerReg;
		}
		
		else if(pHWHead1->dwIOBase == 0x50004000)	//select UART1		
		{
			pHWHead->bINT = BIT_UART1;
			pHWHead->bTxINT = INTSUB_TXD1;
			pHWHead->bRxINT = INTSUB_RXD1;
			pHWHead->bErrINT = INTSUB_ERR1;
			pHWHead->s2440SerReg = (S2440_UART_REG *)v_pUART1regs;
			pRegBase = (PUCHAR)pHWHead->s2440SerReg;
		}
		
		else if(pHWHead1->dwIOBase == 0x50008000)	//select UART2		
		{      
			pHWHead->bINT = BIT_UART2; 
			pHWHead->bTxINT = INTSUB_TXD2; 
			pHWHead->bRxINT = INTSUB_RXD2; 
			pHWHead->bErrINT = INTSUB_ERR2; 
			pHWHead->s2440SerReg = (S2440_UART_REG *)v_pUART2regs; 
			pRegBase = (PUCHAR)pHWHead->s2440SerReg; 

		}
	}
	pHWHead->UART_INTMASK = (volatile unsigned int *)&(v_pINTregs->rINTMSK);
	pHWHead->UART_INTSUBMASK = (volatile unsigned int *)&(v_pINTregs->rINTSUBMSK);
	pHWHead->UART_INTPND = (volatile unsigned int *)&(v_pINTregs->rINTPND);
	pHWHead->UART_INTSRCPND = (volatile unsigned int *)&(v_pINTregs->rSRCPND);
	pHWHead->UART_INTSUBSRCPND = (volatile unsigned int *)&(v_pINTregs->rSUBSRCPND);
	pHWHead->vUMSTAT = 0;

	if ( pHWHead->UseIrDA )
	{
		pHWHead->pUFTXH = (volatile unsigned char *)&(v_pUART2regs->rUTXH);
		pHWHead->pUFRXH = (volatile unsigned char *)&(v_pUART2regs->rURXH);    
	}
	else
	{
		
		if(pHWHead1->dwIOBase == 0x50000000)		
		{
		pHWHead->pUFTXH = (volatile unsigned char *)&(v_pUART0regs->rUTXH);
		pHWHead->pUFRXH = (volatile unsigned char *)&(v_pUART0regs->rURXH);    
		}
		
		else if(pHWHead1->dwIOBase == 0x50004000)	
		{
			pHWHead->pUFTXH = (volatile unsigned char *)&(v_pUART1regs->rUTXH);
			pHWHead->pUFRXH = (volatile unsigned char *)&(v_pUART1regs->rURXH); 
		}

		else if(pHWHead1->dwIOBase == 0x50008000)	
		{ 
		  pHWHead->pUFTXH = (volatile unsigned char *)&(v_pUART2regs->rUTXH); 
		  pHWHead->pUFRXH = (volatile unsigned char *)&(v_pUART2regs->rURXH);     
		} 
	}

	RETAILMSG(DEBUGMODE, (TEXT("+ S2440_SetIrDAIOP \r\n")));
	S2440_SetIrDAIOP(pHead);
	RETAILMSG(DEBUGMODE, (TEXT("- S2440_SetIrDAIOP \r\n")));

	// Store info for callback function
	pHWHead->EventCallback = EventCallback;
	pHWHead->pMddHead = pMddHead;

	// Now set up remaining fields
	if ( pBaudTable != NULL )
		pHWHead->pBaudTable = (LOOKUP_TBL *) pBaudTable;
	else
		pHWHead->pBaudTable = (LOOKUP_TBL *) &LS_BaudTable;
	pHWHead->FlushDone      = CreateEvent(0, FALSE, FALSE, NULL);

	pHWHead->OpenCount = 0;

	// Don't allow any interrupts till PostInit.
	DisEnINT(pHWHead, pHWHead->bINT);
	DisEnSubINT(pHWHead, pHWHead->bTxINT | pHWHead->bRxINT | pHWHead->bErrINT);

	pHWHead->fSW_EnTxINT = FALSE;
	pHWHead->RxDiscard = FALSE;

	InitializeCriticalSection(&(pHWHead->TransmitCritSec));
	InitializeCriticalSection(&(pHWHead->RegCritSec));

	// Clear any interrupts which may be pending.  Normally only
	// happens if we were warm reset.
	ClearPendingInts( pHWHead );
}

//
// @doc OEM
// @func void | SL_PostInit | This routine takes care of final initialization.
//
// @rdesc None.
//
BOOL
SL_PostInit(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("SL_PostInit \r\n")));
	// Since we are just a library which might get used for 
	// builtin ports which init at boot, or by PCMCIA ports
	// which init at Open, we can't do anything too fancy.
	// Lets just make sure we cancel any pending interrupts so
	// that if we are being used with an edge triggered PIC, he
	// will see an edge after the MDD hooks the interrupt.
	ClearPendingInts( pHWHead );
	return(TRUE);
}

//
// @doc OEM 
// @func PVOID | SL_Deinit | De-initializes 16550 device head.  
//
VOID
SL_Deinit(PVOID   pHead)			// @parm points to device head
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("SL_Deinit \r\n")));
	DeleteCriticalSection(&(pHWHead->TransmitCritSec));
	DeleteCriticalSection(&(pHWHead->RegCritSec));

	// Free the flushdone event
	if ( pHWHead->FlushDone )
	    CloseHandle( pHWHead->FlushDone );

	SER_VirtualFree();
}

//
// @doc OEM
// @func void | SL_ClearDtr | This routine clears DTR.
//
// @rdesc None.
//
VOID
SL_ClearDTR(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("+SL_ClearDTR, 0x%X\r\n"), pHead));
	RETAILMSG(DEBUGMODE, (TEXT("UseIrDA, %d\r\n"), pHWHead->UseIrDA));
	
	EnterCriticalSection(&(pHWHead->RegCritSec));

	try
	{
		// Low active pin.
		if ( !pHWHead->UseIrDA )
			*(pHWHead->rDTRport) |= (1<<(pHWHead->DtrPortNum));
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Just exit
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE, (TEXT("-SL_ClearDTR, 0x%X\r\n"), pHead));
}

//
// @doc OEM
// @func VOID | SL_SetDTR | This routine sets DTR.
// 
// @rdesc None.
//
VOID
SL_SetDTR(PVOID   pHead)			// @parm PVOID returned by HWinit.
{    
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("+SL_SetDTR, 0x%X\r\n"), pHead));

	EnterCriticalSection(&(pHWHead->RegCritSec));

	try
	{
		// Low active
		if ( !pHWHead->UseIrDA )
			*(pHWHead->rDTRport) &= ~(1<<(pHWHead->DtrPortNum));
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Just exit
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE, (TEXT("-SL_SetDTR, 0x%X\r\n"), pHead));
}

//
// @doc OEM
// @func VOID | SL_ClearRTS | This routine clears RTS.
// 
// @rdesc None.
// 
VOID
SL_ClearRTS(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("+SL_ClearRTS, 0x%X\r\n"), pHead));

	if ( !pHWHead->UseIrDA )
	{
#if USE_AFC
		// by 0212.
		*(pHWHead->rRTSport) |= (1<<(pHWHead->RtsPortNum));
#else
		EnterCriticalSection(&(pHWHead->RegCritSec));
		try
		{
			CLEARREG(pHWHead, rUMCON, SER2440_RTS);
		}
		
		except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
			EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
		{
			// Just exit
		}

		LeaveCriticalSection(&(pHWHead->RegCritSec));
#endif	
	}

	RETAILMSG(DEBUGMODE, (TEXT("-SL_ClearRTS, 0x%X\r\n"), pHead));

}

//
// @doc OEM
// @func VOID | SL_SetRTS | This routine sets RTS.
// 
// @rdesc None.
//
VOID
SL_SetRTS(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
#if 0
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(0, (TEXT("+SL_SetRTS, 0x%X\r\n"), pHead));
	EnterCriticalSection(&(pHWHead->RegCritSec));
	try
	{
		SETREG(pHWHead, rUMCON, SER2440_RTS);
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Just exit
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE, (TEXT("-SL_SetRTS, 0x%X\r\n"), pHead));
#else
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("+SL_SetRTS, 0x%X\r\n"), pHead));

	if ( !pHWHead->UseIrDA )
	{
#if USE_AFC
		// by .0212.
		*(pHWHead->rRTSport) &= ~(1<<(pHWHead->RtsPortNum));
#else
		EnterCriticalSection(&(pHWHead->RegCritSec));
		try
		{
			SETREG(pHWHead, rUMCON, SER2440_RTS);
		}

		except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
			EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
		{
			// Just exit
		}

		LeaveCriticalSection(&(pHWHead->RegCritSec));
#endif
	}
	RETAILMSG(DEBUGMODE, (TEXT("-SL_SetRTS, 0x%X\r\n"), pHead));
#endif	
}

//
// @doc OEM
// @func VOID | SL_ClearBreak | This routine clears break.
// 
// @rdesc None.
// 
VOID
SL_ClearBreak(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE,  (TEXT("SL_ClearBreak:\r\n"))); 
	// S2440 does not need to clear break signal, for cleared by automatic.
}

//
// @doc OEM
// @func VOID | SL_SetBreak | This routine sets break.
// 
// @rdesc None.
//
VOID
SL_SetBreak(PVOID   pHead)			// @parm PVOID returned by HWinit.
{
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE, (TEXT("+SL_SetBreak, 0x%X\r\n"), pHead));
	EnterCriticalSection(&(pHWHead->RegCritSec));
	try
	{
		SETREG(pHWHead,rUCON,BS_SEND);
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Just exit
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE, (TEXT("-SL_SetBreak, 0x%X\r\n"), pHead));
}

//
// SetBaudRate
//
// Internal function.  The only real reason for splitting this out
// is so that we can call it from PowerOn and still allow SL_SetBaud
// to do debug messages, acquire critical sections, etc.
//
BOOL
SetBaudRate(PVOID   pHead, ULONG   BaudRate)
{
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;

	ClearPendingInts( pHWHead );

	RETAILMSG(1, (TEXT("SetBaudRate -> %d\r\n"), BaudRate));

	if ( (pHWHead->s2440SerReg->rUCON & CS_MASK) == CS_PCLK )
	{
//		if ( BaudRate == 115200 || BaudRate == 38400 )
//			OUTREG(pHWHead,rUBRDIV,( (int)(S2440PCLK/16.0/BaudRate)));
//		else
			OUTREG(pHWHead,rUBRDIV,( (int)(S2440PCLK/16.0/BaudRate) -1 ));
	}
	else		// if  ( (pHWHead2->s2440SerReg->rUCON & CS_MASK) == CS_UCLK )
		OUTREG(pHWHead,rUBRDIV,( (int)(S2440UCLK/16.0/BaudRate) -1 ));

	return( TRUE );
}

//
// @doc OEM
// @func BOOL | SL_SetBaudRate |
//  This routine sets the baud rate of the device.
//
// @rdesc None.
//
BOOL
SL_SetBaudRate(
              PVOID   pHead,    // @parm     PVOID returned by HWInit
              ULONG   BaudRate    // @parm     ULONG representing decimal baud rate.
              )
{
	BOOL fRet;
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;

	RETAILMSG (DEBUGMODE, (TEXT("+SL_SetbaudRate 0x%X, %d\r\n"), pHead, BaudRate));

	try
	{
		// Enter critical section before calling function, since
		// we can't make sys calls inside SetBaudRate
		EnterCriticalSection(&(pHWHead->RegCritSec));
		fRet = SetBaudRate(pHead, BaudRate);
		LeaveCriticalSection(&(pHWHead->RegCritSec));
	}
	
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		return( FALSE );
	}

	if ( fRet )
	{
		pHWHead->dcb.BaudRate = BaudRate;

		RETAILMSG(DEBUGMODE, (TEXT("-SL_SetbaudRate 0x%X (%d Baud)\r\n"), pHead, BaudRate));
		return( TRUE );
	}
	else
	{
		RETAILMSG(DEBUGMODE, (TEXT("-SL_SetbaudRate - Error setting %d, failing to %d\r\n"), BaudRate, pHWHead->dcb.BaudRate));
		return( FALSE );
	}
}

//
// @doc OEM
// @func BOOL | SL_SetByteSize |
//  This routine sets the WordSize of the device.
//
// @rdesc None.
//
BOOL
SL_SetByteSize(
              PVOID   pHead,        // @parm     PVOID returned by HWInit
              ULONG   ByteSize    // @parm     ULONG ByteSize field from DCB.
              )
{
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;
	UINT32 lcr;
	BOOL bRet;

	RETAILMSG(DEBUGMODE,(TEXT("+SL_SetByteSize 0x%X, 0x%X\r\n"), pHead, ByteSize));

	bRet = TRUE;

	EnterCriticalSection(&(pHWHead->RegCritSec));

	try {      
		lcr = (UINT32)INREG(pHWHead,rULCON);
		lcr &= ~SER2440_DATABIT_MASK;

		switch ( ByteSize )
		{
			case 5:
				lcr |= 0;//SERIAL_5_DATA;
				break;
			case 6:
				lcr |= 1;//SERIAL_6_DATA;
				break;
			case 7:
				lcr |= 2;//SERIAL_7_DATA;
				break;
			case 8:
				lcr |= 3;//SERIAL_8_DATA;
				break;
			default:
				bRet = FALSE;
				break;
		}

		if (bRet)
		{
			OUTREG(pHWHead,rULCON,lcr); 
		}
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		bRet = FALSE;
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE,(TEXT("-SL_SetByteSize 0x%X\r\n"), pHead));

	return(bRet);
}

//
// @doc OEM
// @func BOOL | SL_SetParity |
//  This routine sets the parity of the device.
//
// @rdesc None.
//
BOOL
SL_SetParity(
            PVOID   pHead,    // @parm     PVOID returned by HWInit
            ULONG   Parity    // @parm     ULONG parity field from DCB.
            )
{
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;

	UINT32 lcr;
	BOOL bRet;

	RETAILMSG(DEBUGMODE,(TEXT("+SL_SetParity 0x%X, 0x%X\r\n"), pHead, Parity));

	bRet = TRUE;

	EnterCriticalSection(&(pHWHead->RegCritSec));

	try
	{
		lcr = (UINT32)INREG(pHWHead,rULCON);      
		lcr &= ~SER2440_PARITY_MASK;

		switch ( Parity )
		{
			case ODDPARITY:
				lcr |= 0x20;//SERIAL_ODD_PARITY;
				break;

			case EVENPARITY:
				lcr |= 0x28;//SERIAL_EVEN_PARITY;
				break;

			case MARKPARITY:
				lcr |= 0x30;//SERIAL_MARK_PARITY;
				break;

			case SPACEPARITY:
				lcr |= 0x38;//SERIAL_SPACE_PARITY;
				break;

			case NOPARITY:
				lcr |= 0;//SERIAL_NONE_PARITY;
				break;
			
			default:
				bRet = FALSE;
				break;
		}
		if (bRet)
		{
			OUTREG(pHWHead,rULCON,lcr) ;
		}
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		bRet = FALSE;
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE, (TEXT("-SL_SetParity 0x%X\r\n"), pHead));

	return(bRet);
}
//
// @doc OEM
// @func VOID | SL_SetStopBits |
//  This routine sets the Stop Bits for the device.
//
// @rdesc None.
//
BOOL
SL_SetStopBits(
              PVOID   pHead,      // @parm     PVOID returned by HWInit
              ULONG   StopBits  // @parm     ULONG StopBits field from DCB.
              )
{
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;

	UINT32 lcr;
	BOOL bRet;

	RETAILMSG (DEBUGMODE,(TEXT("+SL_SetStopBits 0x%X, 0x%X\r\n"), pHead, StopBits));

	bRet = TRUE;

	EnterCriticalSection(&(pHWHead->RegCritSec));

	try
	{
		lcr = INREG(pHWHead,rULCON);
		lcr &= ~SER2440_STOPBIT_MASK;

		// Note that 1.5 stop bits only works if the word size
		// is 5 bits.  Any other xmit word size will cause the
		// 1.5 stop bit setting to generate 2 stop bits.        
		switch ( StopBits )
		{
			case ONESTOPBIT :
				lcr |= 0;//SERIAL_1_STOP ;
				break;
			
			case ONE5STOPBITS :
				//lcr |= SERIAL_1_5_STOP ;
				//break;
			
			case TWOSTOPBITS :
				lcr |= 4;//SERIAL_2_STOP ;
				break;
			
			default:
				bRet = FALSE;
				break;
		}

		if (bRet)
		{
			OUTREG(pHWHead,rULCON,lcr);
		}
	}
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		bRet = FALSE;
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	RETAILMSG(DEBUGMODE,(TEXT("-SL_SetStopBits 0x%X\r\n"), pHead));

	return(bRet);
}

//
// @doc OEM
// @func ULONG | SL_GetRxBufferSize | This function returns
// the size of the hardware buffer passed to the interrupt
// initialize function.  It would be used only for devices
// which share a buffer between the MDD/PDD and an ISR.
//
// 
// @rdesc This routine always returns 0 for 16550 UARTS.
// 
ULONG
SL_GetRxBufferSize(PVOID pHead)
{
	RETAILMSG(DEBUGMODE, (TEXT("SL_GetRxBufferSize \r\n")));
	return(0);
}

//
// @doc OEM
// @func PVOID | SC_GetRxStart | This routine returns the start of the hardware
// receive buffer.  See SL_GetRxBufferSize.
// 
// @rdesc The return value is a pointer to the start of the device receive buffer.
// 
PVOID
SL_GetRxStart(PVOID   pHead)			// @parm PVOID returned by SC_init.
{
	RETAILMSG(DEBUGMODE, (TEXT("SL_GetRxStart \r\n")));
	return(NULL);
}

//
// @doc OEM
// @func ULONG | SL_GetInterruptType | This function is called
//   by the MDD whenever an interrupt occurs.  The return code
//   is then checked by the MDD to determine which of the four
//   interrupt handling routines are to be called.
// 
// @rdesc This routine returns a bitmask indicating which interrupts
//   are currently pending.
// 
INTERRUPT_TYPE
SL_GetInterruptType(PVOID pHead)			// Pointer to hardware head
{
	PS2440_UART_INFO    pHWHead = (PS2440_UART_INFO)pHead;
	INTERRUPT_TYPE interrupts;
	ULONG IntPndVal=0;
	ULONG IntSubPndVal=0;

	try
	{
		IntPndVal = *(pHWHead->UART_INTSRCPND);
		IntSubPndVal = *(pHWHead->UART_INTSUBSRCPND);
	}
	
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		RETAILMSG(DEBUGMODE, (TEXT("ACCESS VIOLATION ERROR \r\n")));
		IntPndVal = SER2440_INT_INVALID; // simulate no interrupt
	}

	RETAILMSG(DEBUGMODE, (TEXT("SL_GetInterruptType : 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X\r\n"),
		*(pHWHead->UART_INTMASK),
		*(pHWHead->UART_INTSUBMASK),
		*(pHWHead->UART_INTSRCPND),
		*(pHWHead->UART_INTSUBSRCPND),
		~(*(pHWHead->UART_INTSUBMASK)) & *(pHWHead->UART_INTSUBSRCPND),
		pHWHead->fSW_EnTxINT));

	if ( IntPndVal & (pHWHead->bINT) )
	{
		if ( IntSubPndVal == SER2440_INT_INVALID )
		{      
			RETAILMSG (DEBUGMODE, (TEXT("SL_GetInterruptType: SER2440_INT_INVALID\r\n")));
			interrupts = INTR_NONE;
		}
		else if(IntSubPndVal & (pHWHead->bErrINT) )
		{
			RETAILMSG (DEBUGMODE, (TEXT("SL_GetInterruptType: INTR_LINE\r\n")));
			interrupts = INTR_LINE;  // Error status
		}
		else if((IntSubPndVal & (pHWHead->bTxINT)) && pHWHead->fSW_EnTxINT )
		{
			RETAILMSG (DEBUGMODE, (TEXT("SL_GetInterruptType: INTR_TX\r\n")));
			interrupts = INTR_TX;
		}
		else if(IntSubPndVal & (pHWHead->bRxINT) )
		{
			RETAILMSG (DEBUGMODE, (TEXT("SL_GetInterruptType: INTR_RX\r\n")));
			interrupts = INTR_RX;    // Received valid data.
		}
		else 
		{
			interrupts = INTR_NONE;  // No interrupts pending, vector is useless
			ClearINTPnd(pHWHead, pHWHead->bINT);
		}
	}
	else
	{
		RETAILMSG (DEBUGMODE, (TEXT("SL_GetInterruptType: INTR_NONE(pHWHead->bINT)\r\n")));
		interrupts = INTR_NONE;  // No interrupts pending, vector is useless
	}

	return(interrupts);
}


// @doc OEM
// @func ULONG | SL_RxIntr | This routine gets several characters from the hardware
//   receive buffer and puts them in a buffer provided via the second argument.
//   It returns the number of bytes lost to overrun.
// 
// @rdesc The return value indicates the number of overruns detected.
//   The actual number of dropped characters may be higher.
//
ULONG
SL_RxIntr(
         PVOID pHead,                // @parm Pointer to hardware head
         PUCHAR pRxBuffer,           // @parm Pointer to receive buffer
         ULONG *pBufflen             // @parm In = max bytes to read, out = bytes read
         )
{
	PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;

	ULONG	RetVal               = 0;
	ULONG	TargetRoom           = *pBufflen;
	UCHAR	cEvtChar, cRXChar;
	ULONG	rFifoStat, RxFifoCnt, RxDataReady, nRxIntSrc;
	BOOL	fRXFlag              = FALSE;
	BOOL	fReplaceparityErrors = FALSE;
	BOOL	fNull;

	*pBufflen = 0;
	nRxIntSrc = pHWHead->bRxINT;

	RETAILMSG(DEBUGMODE, (TEXT("SL_RxIntr \r\n")));
	// LAM - I have local copies of some DCB elements since I don't
	// want to keep dereferencing inside my read loop and there are too
	// many of them to trust the compiler.
	cEvtChar = pHWHead->dcb.EvtChar;
	fNull = pHWHead->dcb.fNull;
	if ( pHWHead->dcb.fErrorChar && pHWHead->dcb.fParity )
		fReplaceparityErrors = TRUE;

	try
	{
		// S2440 UART Rx strategy is different from SER16550..
		rFifoStat = INREG(pHWHead,rUFSTAT);
		RxFifoCnt = rFifoStat & 0x3f;

		if((rFifoStat & (1<<6))||(RxFifoCnt > 0))
			RxDataReady = 1;
		else
			RxDataReady = 0;

		while ( TargetRoom && RxDataReady)
		{
			// If the line is unstable, we may check the error status...
			// But now, I ignore it....
			// ReadLSR( pHWHead );

			// Read the byte
			cRXChar = *(pHWHead->pUFRXH);
			RETAILMSG(DEBUGMODE, (TEXT("SL_RxIntr : cRXChar = 0x%x \r\n"), cRXChar));

			// But we may want to discard it
			if ( pHWHead->dcb.fDsrSensitivity &&
				(! (pHWHead->vUMSTAT & COM2440_MSR_DSR)) )
			{
				// Do nothing - byte gets discarded
				RETAILMSG (1, (TEXT("Dropping byte because DSR is low\r\n")));
			}
			else if (!cRXChar && fNull)
			{
				// Do nothing - byte gets discarded
				RETAILMSG (1, (TEXT("Dropping NULL byte due to fNull\r\n")));
			}
			else
			{
				// Do character replacement if parity error detected.
				// See if we need to generate an EV_RXFLAG for the received char.
				if ( cRXChar == cEvtChar )
					fRXFlag = TRUE;
 
				// Finally, we can get byte, update status and save.
				if (!pHWHead->RxDiscard )
				{
					*pRxBuffer++ = cRXChar;
					(*pBufflen)++;
				}
				else
				{
					RETAILMSG (1,(TEXT("SL_RXIntr : Dropping byte because now Txing\r\n")));
				}

				--TargetRoom;

				rFifoStat = INREG(pHWHead,rUFSTAT);
				RxFifoCnt = rFifoStat & 0x3f;

				if((rFifoStat & (1<<6))||(RxFifoCnt > 0))
					RxDataReady = 1;
				else
					RxDataReady = 0;
			}
		}

		//Clear interrupt pending bit
		ClearSubINTPnd(pHWHead, nRxIntSrc);
		if ( *(pHWHead->UART_INTSUBSRCPND) & ( pHWHead->bRxINT | pHWHead->bErrINT ) )
		{
			RETAILMSG (DEBUGMODE, (TEXT("*(pHWHead->UART_INTSUBSRCPND) = %x \r\n"), *(pHWHead->UART_INTSUBSRCPND)));
			RETAILMSG (DEBUGMODE, (TEXT("Do Not Clear bINT \r\n")));
		}
		else
		{
			ClearINTPnd(pHWHead, pHWHead->bINT);
		}
		EnINT(pHWHead, pHWHead->bINT);
		EnSubINT(pHWHead, nRxIntSrc);
		if ( !pHWHead->UseIrDA )
		{
			#if USE_AFC
			// Assert nRTS
			*(pHWHead->rRTSport) &= ~(1<<(pHWHead->RtsPortNum));
			#endif
		}
	}
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// just exit
	}

	// if we saw one (or more) EVT chars, then generate an event
	if ( fRXFlag )
		pHWHead->EventCallback( pHWHead->pMddHead, EV_RXFLAG );

	if ( pHWHead->DroppedBytes )
	{
		RETAILMSG (DEBUGMODE, (TEXT("Rx drop %d.\r\n"), pHWHead->DroppedBytes));
	}
    
	RetVal = pHWHead->DroppedBytes;
	pHWHead->DroppedBytes = 0;
	return(RetVal);
}

// @doc OEM
// @func ULONG | SL_PutBytes | This routine is called from the MDD
//   in order to write a stream of data to the device. (Obselete)
// 
// @rdesc Always returns 0
//
ULONG
SL_PutBytes(
           PVOID   pHead,        // @parm    PVOID returned by HWInit.
           PUCHAR  pSrc,        // @parm    Pointer to bytes to be sent.
           ULONG   NumberOfBytes,  // @parm    Number of bytes to be sent.
           PULONG  pBytesSent        // @parm    Pointer to actual number of bytes put.
           )
{
	PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;
	RETAILMSG(1,(TEXT("This routine is called by old MDD\r\n")));
	return(0);
}

//
// @doc OEM
// @func ULONG | SL_TXIntr | This routine is called from the old MDD
//   whenever INTR_TX is returned by SL_GetInterruptType (Obselete)
// 
// @rdesc None
//
VOID
SL_TxIntr(PVOID pHead)			// Hardware Head
{
    PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;
    RETAILMSG(1, (TEXT("SL_TxIntr(From old MDD)\n")));
}

//
// @doc OEM
// @func ULONG | SL_TXIntrEx | This routine is called from the new MDD
//   whenever INTR_TX is returned by SL_GetInterruptType
// 
// @rdesc None
//
VOID
SL_TxIntrEx(
           PVOID pHead,                // Hardware Head
           PUCHAR pTxBuffer,          // @parm Pointer to receive buffer
           ULONG *pBufflen            // @parm In = max bytes to transmit, out = bytes transmitted
           )
{
	PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;

	UCHAR	byteCount;
	ULONG	NumberOfBytes = *pBufflen;
	ULONG	rFifoStat, TxFifoCnt;
	unsigned int FifoModeReg;
   
	RETAILMSG(DEBUGMODE, (TEXT("SL_TxIntrEx \r\n")));
	// We may be done sending.  If so, just disable the TX interrupts and return to the MDD.  
	if( ! *pBufflen )
	{
		DisEnINT(pHWHead, pHWHead->bINT);
		DisEnSubINT(pHWHead, pHWHead->bTxINT);
		pHWHead->fSW_EnTxINT = FALSE;

		ClearINTPnd(pHWHead, pHWHead->bINT);
		return;
	}
    
	*pBufflen = 0;  // In case we don't send anything below.

	// Disable xmit intr.  Most 16550s will keep hammering
	// us with xmit interrupts if we don't turn them off
	// Whoever gets the FlushDone will then need to turn
	// TX Ints back on if needed.
	EnterCriticalSection(&(pHWHead->RegCritSec));
	try
	{
		// Need to signal FlushDone for XmitComChar
		PulseEvent(pHWHead->FlushDone);

		pHWHead->CommErrors &= ~CE_TXFULL;

#if 0
        // If CTS flow control is desired, check cts. If clear, don't send,
        // but loop.  When CTS comes back on, the OtherInt routine will
        // detect this and re-enable TX interrupts (causing Flushdone).
        // For finest granularity, we would check this in the loop below,
        // but for speed, I check it here (up to 8 xmit characters before
        // we actually flow off.
        if ( pHWHead->dcb.fOutxCtsFlow )
		{
            // ReadMSR( pHWHead );
            // We don't need to explicitly read the MSR, since we always enable
            // IER_MS, which ensures that we will get an interrupt and read
            // the MSR whenever CTS, DSR, TERI, or DCD change.

            if (! (pHWHead->MSR & SERIAL_MSR_CTS) )
			{
                unsigned char byte;
                RETAILMSG (DEBUGMODE, (TEXT("SL_TxIntrEx, flowed off via CTS\n") ) );
                pHWHead->CTSFlowOff = TRUE;  // Record flowed off state

                // We could return a positive value here, which would
                // cause the MDD to periodically check the flow control
                // status.  However, we don't need to since we know that
                // the DCTS interrupt will cause the MDD to call us, and we
                // will subsequently fake a TX interrupt to the MDD, causing
                // him to call back into PutBytes.
				DisEnINT(pHWHead, pHWHead->bINT);
				DisEnSubINT(pHWHead, pHWHead->bTxINT);
				pHWHead->fSW_EnTxINT = FALSE;

				ClearINTPnd(pHWHead, pHWHead->bINT);

                LeaveCriticalSection(&(pHWHead->RegCritSec));
                return;
            }
		}
#endif
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Do nothing.  The worst case is that this was a fluke,
		// and a TX Intr will come right back at us and we will
		// resume transmission.
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	//  OK, now lets actually transmit some data.
	EnterCriticalSection(&(pHWHead->TransmitCritSec));
	EnterCriticalSection(&(pHWHead->RegCritSec));

	FifoModeReg = INREG(pHWHead, rUFCON);

	try
	{
		rFifoStat = INREG(pHWHead,rUFSTAT);
		TxFifoCnt = ((rFifoStat & SER2440_FIFOCNT_MASK_TX) >> 8);

		if (pHWHead->UseIrDA)
		{
			// IR is half-duplex.
			pHWHead->RxDiscard = TRUE;
			DisEnSubINT(pHWHead, pHWHead->bRxINT);
		}

		if (!(rFifoStat & SER2440_FIFOFULL_TX) && (TxFifoCnt <= (SER2440_FIFO_DEPTH_TX-1)))
		{
			byteCount = (unsigned char)(SER2440_FIFO_DEPTH_TX-TxFifoCnt);
			RETAILMSG(DEBUGMODE, (TEXT("SL_TxIntrEx : %d \r\n"), NumberOfBytes));
			for ( *pBufflen=0; NumberOfBytes && byteCount; NumberOfBytes--, byteCount-- )
			{
				unsigned int i, tmpreg;
				*(pHWHead->pUFTXH) = *pTxBuffer;
				RETAILMSG(DEBUGMODE, (TEXT("SL_TxInt - *pTxBuffer = 0x%x \r\n"), *pTxBuffer));
				if (FifoModeReg&0x1) // FIFO Mode enabled.
				{
					for ( i = 0; i < 600000; i++ )
					{
						tmpreg = INREG(pHWHead, rUFSTAT);
						//tmpreg & (1<<14) == 1  ->  Fifo full -> waiting...
						//tmpreg & 0x3f00 == 0 -> There is no data to send -> break loop.

						if ( (tmpreg & (1<<14)) == (1<<14) )
						{
							continue;
						}
						else break;
					}
					if ( i == 600000 )	// Time Out Error
					{
						RETAILMSG (1, (TEXT("SL_TxInt : Error : i = %d \r\n"), i));
					}
				}
				else		// FIFO Mode disabled.
				{
					for ( i = 0; i < 600000; i++ )
					{
						tmpreg = INREG(pHWHead, rUTRSTAT);
						if ( tmpreg & 0x2 ) break;
					}
					if ( i == 600000 )
					{
						RETAILMSG (1, (TEXT("SL_TxInt : Error : i = %d \r\n"), i));
					}
				}

				++pTxBuffer;
				(*pBufflen)++;
				//irda test...
				//RETAILMSG (1,(TEXT("T ")));
			}
			if (pHWHead->UseIrDA )
			{
				if (FifoModeReg&0x1) // FIFO Mode enabled.
				{
					unsigned int cnt = 0;
					for ( ;; )
					{
						unsigned int tmpreg;
						tmpreg = INREG(pHWHead, rUFSTAT);

						if ( (tmpreg & 0x3f00) == 0) break; // waitint to empty the tx buffer empty...
						if ( cnt++ > 600000 )
						{
							RETAILMSG(DEBUGMODE, (TEXT("SL_TxInt : Reset TX FIFO :  tmpreg = %x \r\n"), tmpreg));
							SETREG(pHWHead,rUFCON,0x4);    // tx, rx fifo reset
							break;
						}
					}
					RETAILMSG(DEBUGMODE, (TEXT("SL_TxInt : Exit for loop \r\n")));
				}
			}
		}
		else
		{
			RETAILMSG (1, (TEXT("SL_TxIntrEx: Not Send - rFifoStat[0x%x] TxFifoCnt[0x%x]\r\n"),
							rFifoStat, TxFifoCnt));
		}

		if (pHWHead->UseIrDA )
		{
			// IR is half-duplex.
			ClearPendingInts( pHWHead );
			EnSubINT(pHWHead, pHWHead->bRxINT);
			pHWHead->RxDiscard = FALSE;
		}

		// Enable xmit intr. We need to do this no matter what, 
		// since the MDD relies on one final interrupt before
		// returning to the application. 
//		ClearPendingInts( pHWHead );

		// Clear TX interrupt pending bit.
		ClearSubINTPnd(pHWHead, pHWHead->bTxINT);
		if ( !(GetSubINTStatus(pHWHead) & pHWHead->bRxINT) ) 		ClearINTPnd(pHWHead, pHWHead->bINT);

		EnINT(pHWHead, pHWHead->bINT);
		EnSubINT(pHWHead, pHWHead->bTxINT);
		pHWHead->fSW_EnTxINT = TRUE;  // FALSE;
	}
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Hmm, not sure what would cause this.  Lets just tell
		// the MDD to go away until we get another TX
		// interrupt.
	}
	
	LeaveCriticalSection(&(pHWHead->RegCritSec));
	LeaveCriticalSection(&(pHWHead->TransmitCritSec));
	return;
}

//
// @doc OEM
// @func ULONG | SL_LineIntr | This routine is called from the MDD
//   whenever INTR_LINE is returned by SL_GetInterruptType.
// 
// @rdesc None
//
VOID
SL_LineIntr(PVOID pHead)			// Hardware Head
{
	PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE,(TEXT("INTR_LINE \r\n")));

	SL_ModemIntr( pHWHead );
	ReadLSR( pHWHead );

	ClearSubINTPnd(pHWHead, pHWHead->bErrINT);
	if ( !(GetSubINTStatus(pHWHead) & pHWHead->bRxINT) ) 		
		ClearINTPnd(pHWHead, pHWHead->bINT);

	EnINT(pHWHead, pHWHead->bINT);
	EnSubINT(pHWHead, pHWHead->bErrINT);
}

//
// @doc OEM
// @func ULONG | SL_OtherIntr | This routine is called from the MDD
//   whenever INTR_MODEM is returned by SL_GetInterruptType.
// 
// @rdesc None
//
VOID
SL_OtherIntr(PVOID pHead)			// Hardware Head
{
    PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;

    // S2440 does not have modem interrupt.

    RETAILMSG(DEBUGMODE,(TEXT("+SL_OtherIntr \r\n")));

    ReadMSR( pHWHead );

    EnterCriticalSection(&(pHWHead->RegCritSec));
    try
	{
        // If we are currently flowed off via CTS or DSR, then
        // we better signal the TX thread when one of them changes
        // so that TX can resume sending.
        if ( pHWHead->DSRFlowOff && (pHWHead->vUMSTAT & COM2440_MSR_DSR))
		{
            RETAILMSG (DEBUGMODE, (TEXT("**********************PutBytes, flowed on via DSR\n") ) );
            pHWHead->DSRFlowOff = FALSE;
            // DSR is set, so go ahead and resume sending            
            EnINT(pHWHead, pHWHead->bINT);
            EnSubINT(pHWHead, pHWHead->bTxINT | pHWHead->bRxINT | pHWHead->bErrINT);
            pHWHead->fSW_EnTxINT = TRUE;
            // Then simulate a TX intr to get things moving
            pHWHead->AddTXIntr = TRUE;
        }
        if ( pHWHead->CTSFlowOff && (pHWHead->vUMSTAT & COM2440_MSR_CTS))
		{
            RETAILMSG(DEBUGMODE, (TEXT("***********************PutBytes, flowed on via CTS\n") ) );
            pHWHead->CTSFlowOff = FALSE;
            // CTS is set, so go ahead and resume sending
            EnINT(pHWHead, pHWHead->bINT);
            EnSubINT(pHWHead, pHWHead->bTxINT | pHWHead->bRxINT | pHWHead->bErrINT);
            pHWHead->fSW_EnTxINT = TRUE;
            // Then simulate a TX intr to get things moving
            pHWHead->AddTXIntr = TRUE;
        }
        
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        // Just exit
    }

    LeaveCriticalSection(&(pHWHead->RegCritSec));

    RETAILMSG(DEBUGMODE,(TEXT("-SL_OtherIntr 0x%X\r\n"), pHead));

}


//
// @doc OEM
// @func ULONG | SL_OtherIntr | This routine is called from the MDD
//   whenever INTR_MODEM is returned by SL_GetInterruptType.
// 
// @rdesc None
//
VOID
SL_ModemIntr(PVOID pHead)			// Hardware Head
{
	PS2440_UART_INFO   pHWHead    = (PS2440_UART_INFO)pHead;
	INTERRUPT_TYPE      it = INTR_NONE;
	ULONG               win32status = 0;

	RETAILMSG (1, (TEXT("SL_ModemIntr - pHWHead->OpenCount %d \r\n"), pHWHead->OpenCount));
	if ( !pHWHead->OpenCount )
	{
		// We want to indicate a cable event.
		RETAILMSG(1, (TEXT("!pHWHead->OpenCount : 0x%X\r\n"), pHWHead->OpenCount));

		if ( IsAPIReady(SH_WMGR))
		{
			RETAILMSG(1, (TEXT("IsAPIReady(SH_WMGR) == true\r\n")));
			CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED,NULL);
			RETAILMSG(1, (TEXT("Indicating RS232 Cable Event\r\n")));
			RETAILMSG(1, (TEXT("Event occurred : NOTIFICATION_EVENT_RS232_DETECTED\r\n")));
		}
	}
	else
	{
		RETAILMSG(1,(TEXT("+SL_ModemIntr 0x%X\r\n"), pHead));
		SL_OtherIntr(pHead);
	}
}

//  
// @doc OEM
// @func    ULONG | SL_GetStatus | This structure is called by the MDD
//   to retrieve the contents of a COMSTAT structure.
//
// @rdesc    The return is a ULONG, representing success (0) or failure (-1).
//
ULONG
SL_GetStatus(
            PVOID    pHead,    // @parm PVOID returned by HWInit.
            LPCOMSTAT    lpStat    // Pointer to LPCOMMSTAT to hold status.
            )
{
    PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
    ULONG      RetVal  = pHWHead->CommErrors;

	RETAILMSG(1, (TEXT("+SL_GetStatus 0x%X\r\n"), pHead));
	pHWHead->CommErrors = 0; // Clear old errors each time

    if ( lpStat )
	{
		try
		{
            if (pHWHead->CTSFlowOff)
                pHWHead->Status.fCtsHold = 1;
            else
                pHWHead->Status.fCtsHold = 0;

            if (pHWHead->DSRFlowOff)
                pHWHead->Status.fDsrHold = 1;
            else
                pHWHead->Status.fDsrHold = 0;

            // NOTE - I think what they really want to know here is
            // the amount of data in the MDD buffer, not the amount
            // in the UART itself.  Just set to 0 for now since the
            // MDD doesn't take care of this.
            pHWHead->Status.cbInQue  = 0;
            pHWHead->Status.cbOutQue = 0;

            memcpy(lpStat, &(pHWHead->Status), sizeof(COMSTAT));
        }
        except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
                EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
		{
            RetVal = (ULONG)-1;
        }        
    } 
	else
        RetVal = (ULONG)-1;

    DEBUGMSG (ZONE_FUNCTION|USERDBG,
              (TEXT("-SL_GetStatus 0x%X\r\n"), pHead));
    return(RetVal);
}

//
// @doc OEM
// @func    ULONG | SL_Reset | Perform any operations associated
//   with a device reset
//
// @rdesc    None.
//
VOID
SL_Reset(PVOID   pHead)			// @parm PVOID returned by HWInit.
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE,(TEXT("+SL_Reset 0x%X\r\n"), pHead));

	memset(&pHWHead->Status, 0, sizeof(COMSTAT));

	EnterCriticalSection(&(pHWHead->RegCritSec));
	try
	{
		DisEnINT(pHWHead, pHWHead->bINT);
		DisEnSubINT(pHWHead, pHWHead->bTxINT);
		//	if ( GetSubINTStatus(pHWHead) )
		pHWHead->fSW_EnTxINT = FALSE;
		pHWHead->RxDiscard = FALSE;
	}

	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Do nothing
	}

	LeaveCriticalSection(&(pHWHead->RegCritSec));

	DEBUGMSG (ZONE_FUNCTION, (TEXT("-SL_Reset 0x%X\r\n"), pHead));
}

//
// @doc OEM
// @func    VOID | SL_GetModemStatus | Retrieves modem status.
//
// @rdesc    None.
//
VOID
SL_GetModemStatus(
                 PVOID   pHead,        // @parm PVOID returned by HWInit.
                 PULONG  pModemStatus    // @parm PULONG passed in by user.
                 )
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
	UINT8 ubModemStatus;

	RETAILMSG(DEBUGMODE,  (TEXT("SL_GetModemStatus:\r\n")));

	ReadMSR( pHWHead );
	ubModemStatus = (unsigned char)pHWHead->vUMSTAT;
	RETAILMSG(DEBUGMODE,  (TEXT("SL_GetModemStatus: ubModemStatus = 0x%x\r\n"), ubModemStatus));

	if ( ubModemStatus & COM2440_MSR_CTS )
		*pModemStatus |= MS_CTS_ON;
	
	if ( ubModemStatus & COM2440_MSR_DSR )
	{
		*pModemStatus |= MS_DSR_ON;
		*pModemStatus |= MS_RLSD_ON;
	}

	RETAILMSG(DEBUGMODE,(TEXT("-SL_GetModemStatus 0x%X (stat x%X) \r\n"), pHead, *pModemStatus));
	return;
}

//
// @doc OEM
// @func    VOID | SL_PurgeComm | Purge RX and/or TX
// 
// @rdesc    None.
//

VOID
SL_PurgeComm(
            PVOID   pHead,        // @parm PVOID returned by HWInit.
            DWORD   fdwAction        // @parm Action to take. 
            )
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;

	RETAILMSG(DEBUGMODE,(TEXT("+SL_PurgeComm 0x%X\r\n"), fdwAction));
	return;
}

//
// @doc OEM
// @func    BOOL | SL_XmitComChar | Transmit a char immediately
// 
// @rdesc    TRUE if succesful
//
BOOL
SL_XmitComChar(
              PVOID   pHead,    // @parm PVOID returned by HWInit.
              UCHAR   ComChar   // @parm Character to transmit. 
              )
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
	ULONG       rFifoStat, TxFifoCnt; 

	RETAILMSG (DEBUGMODE,(TEXT("+SL_XmitComChar 0x%X\r\n"), pHead));
	// Get critical section, then transmit when buffer empties
	RETAILMSG(DEBUGMODE, (TEXT("XmitComChar wait for CritSec %x.\r\n"),&(pHWHead->TransmitCritSec)));
	EnterCriticalSection(&(pHWHead->TransmitCritSec));
	RETAILMSG(DEBUGMODE, (TEXT("XmitComChar got CritSec %x.\r\n"),&(pHWHead->TransmitCritSec)));
	try
	{
		while ( TRUE )			// We know THR will eventually empty
		{
			EnterCriticalSection(&(pHWHead->RegCritSec));

			// Write the character if we can
			rFifoStat = INREG(pHWHead,rUFSTAT);
			TxFifoCnt = (rFifoStat & SER2440_FIFOCNT_MASK_TX) >> 8;

			if (!(rFifoStat & SER2440_FIFOFULL_TX) && (TxFifoCnt < (SER2440_FIFO_DEPTH_TX-1)))
			{
				// FIFO is empty, send this character
				//OUTB(pHWHead, pData, ComChar);
				OUTREG(pHWHead,rUTXH,ComChar);
				// Make sure we release the register critical section
				LeaveCriticalSection(&(pHWHead->RegCritSec));

				RETAILMSG(DEBUGMODE, (TEXT("XmitComChar wrote x%X\r\n"),ComChar));
				break;
			}

			// If we couldn't write the data yet, then wait for a
			// TXINTR to come in and try it again.

			// Enable xmit intr.
			EnINT(pHWHead, pHWHead->bINT);
			EnSubINT(pHWHead, pHWHead->bTxINT | pHWHead->bRxINT | pHWHead->bErrINT);
			pHWHead->fSW_EnTxINT = TRUE;
			LeaveCriticalSection(&(pHWHead->RegCritSec));

			// Wait until the txintr has signalled.
			DEBUGMSG (ZONE_WRITE, (TEXT("XmitComChar WaitIntr x%X\r\n"),
						pHWHead->FlushDone));
			WaitForSingleObject(pHWHead->FlushDone, (ULONG)1000);
		}
	}
	except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
		EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
	{
		// Make sure we release the register critical section
		LeaveCriticalSection(&(pHWHead->RegCritSec));
	}

	LeaveCriticalSection(&(pHWHead->TransmitCritSec));

	DEBUGMSG (ZONE_WRITE, (TEXT("XmitComChar released CritSec %x.\r\n"), &(pHWHead->TransmitCritSec)));

	DEBUGMSG (ZONE_FUNCTION, (TEXT("-SL_XmitComChar 0x%X\r\n"), pHead));

	return(TRUE);
}

//
// @doc OEM
// @func    BOOL | SL_PowerOff | Perform powerdown sequence.
// 
// @rdesc    TRUE if succesful
//
VOID
SL_PowerOff(PVOID   pHead)			// @parm    PVOID returned by HWInit.
{
	PS2440_UART_INFO   pHWHead   = (PS2440_UART_INFO)pHead;

	RETAILMSG(0, (TEXT("SL_PowerOff \r\n")));

	if ( !pHWHead->UseIrDA )
	{
		// DeAssert DTR
		*(pHWHead->rDTRport) |= (1<<(pHWHead->DtrPortNum));
		
		// DeAssert RTS
#if USE_AFC
			*(pHWHead->rRTSport) |= (1<<(pHWHead->RtsPortNum));
#else
			CLEARREG(pHWHead, rUMCON, SER2440_RTS);
#endif
	}

	pHWHead->sULCON = INREG(pHWHead, rULCON);
	pHWHead->sUCON  = INREG(pHWHead, rUCON);
	pHWHead->sUMCON = INREG(pHWHead, rUMCON);
	pHWHead->sUFCON = INREG(pHWHead, rUFCON);        
	pHWHead->sINTstat = (*(pHWHead->UART_INTSUBMASK) & (pHWHead->bTxINT | pHWHead->bRxINT));
}

//
// @doc OEM
// @func    BOOL | SL_PowerOn | Perform poweron sequence.
// 
// @rdesc    TRUE if succesful
//
VOID
SL_PowerOn(PVOID   pHead)			// @parm    PVOID returned by HWInit.
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;

	// Restore any registers that we need
	RETAILMSG(DEBUGMODE, (TEXT("SL_PowerOn \r\n")));

	if ( !pHWHead->UseIrDA )
	{
		// Assert DTR
		*(pHWHead->rDTRport) &= ~(1<<(pHWHead->DtrPortNum));
		
		// Assert RTS
#if USE_AFC
			*(pHWHead->rRTSport) &= ~(1<<(pHWHead->RtsPortNum));
#else
			SETREG(pHWHead, rUMCON, SER2440_RTS);
#endif
	}
	
	// In power handler context, so don't try to do a critical section
	OUTREG(pHWHead, rULCON, pHWHead->sULCON); 
	OUTREG(pHWHead, rUCON,  pHWHead->sUCON); 
	OUTREG(pHWHead, rUMCON, pHWHead->sUMCON); 
	OUTREG(pHWHead, rUFCON, pHWHead->sUFCON); 

	if((pHWHead->sINTstat) & (pHWHead->bTxINT))
	{
		EnINT(pHWHead, pHWHead->bINT);
		EnSubINT(pHWHead, pHWHead->bTxINT);
		pHWHead->fSW_EnTxINT = TRUE;
		pHWHead->RxDiscard = FALSE;
	}

	if((pHWHead->sINTstat) & (pHWHead->bRxINT))
	{
		EnINT(pHWHead, pHWHead->bINT);
		EnSubINT(pHWHead, pHWHead->bRxINT);
	}

	// And we didn't save the Divisor Reg, so set baud rate
	// But don't call SL_SetBaud, since it does DebugMsg.
	// Call our internal function instead.  Can't acquire
	// the RegCritSec, but shouldn't really need to since
	// we are in power context.
	SetBaudRate( pHWHead, pHWHead->dcb.BaudRate );

	return;

	pHWHead->EventCallback( pHWHead->pMddHead, EV_POWER );

}

//
// @doc OEM
// @func    BOOL | SL_SetDCB | Sets new values for DCB.  This
// routine gets a DCB from the MDD.  It must then compare
// this to the current DCB, and if any fields have changed take
// appropriate action.
// 
// @rdesc    BOOL
//
BOOL
SL_SetDCB(
         PVOID   pHead,        // @parm    PVOID returned by HWInit.
         LPDCB   lpDCB       // @parm    Pointer to DCB structure
         )
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
	BOOL bRet;

	bRet = TRUE;

	RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : BaudRate is %d\r\n"), lpDCB->BaudRate));
	RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set ByteSize is %d\r\n"), lpDCB->ByteSize));
	RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set Parity is %d\r\n"), lpDCB->Parity));
	RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set StopBits is %d\r\n"), lpDCB->StopBits));

	// If the device is open, scan for changes and do whatever
	// is needed for the changed fields.  if the device isn't
	// open yet, just save the DCB for later use by the open.
	if ( pHWHead->OpenCount )
	{
		// Note, fparity just says whether we should check
		// receive parity.  And the 16550 won't let us NOT
		// check parity if we generate it.  So this field
		// has no effect on the hardware.

		if ( lpDCB->BaudRate != pHWHead->dcb.BaudRate )
		{
			RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set BaudRate to %d\r\n"), lpDCB->BaudRate));
			bRet = SL_SetBaudRate( pHWHead, lpDCB->BaudRate );
		}

		if ( bRet && (lpDCB->ByteSize != pHWHead->dcb.ByteSize ))
		{
			RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set ByteSize to %d\r\n"), lpDCB->ByteSize));
			bRet = SL_SetByteSize( pHWHead, lpDCB->ByteSize );
		}

		if ( bRet && (lpDCB->Parity != pHWHead->dcb.Parity ))
		{
			RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set Parity to %d\r\n"), lpDCB->Parity));
			bRet = SL_SetParity( pHWHead, lpDCB->Parity );
		}

		if ( bRet && (lpDCB->StopBits != pHWHead->dcb.StopBits ))
		{
			RETAILMSG(DEBUGMODE,(TEXT("SL_SetDCB : Set StopBits to %d\r\n"), lpDCB->StopBits));
			bRet = SL_SetStopBits( pHWHead, lpDCB->StopBits );
		}

		// Don't worry about fOutxCtsFlow.  It is a flag which
		// will be examined every time we load the TX buffer.
		// No special action required here.
	}

	if (bRet)
	{
		// Now that we have done the right thing, store this DCB
		pHWHead->dcb = *lpDCB;
	}

	RETAILMSG(DEBUGMODE,(TEXT("-SL_SetDCB 0x%X\r\n"), pHead));

	return(bRet);
}

//
// @doc OEM
// @func    BOOL | SL_SetCommTimeouts | Sets new values for the
// CommTimeouts structure. routine gets a DCB from the MDD.  It
// must then compare this to the current DCB, and if any fields
// have changed take appropriate action.
// 
// @rdesc    ULONG
//
ULONG
SL_SetCommTimeouts(
                  PVOID   pHead,        // @parm    PVOID returned by HWInit.
                  LPCOMMTIMEOUTS   lpCommTimeouts // @parm Pointer to CommTimeout structure
                  )
{
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
	ULONG retval = 0;

	RETAILMSG(DEBUGMODE,(TEXT("+SL_SetCommTimeout 0x%X\r\n"), pHead));
//	ShowSerialRegisters(pHead);

	// OK, first check for any changes and act upon them
	if ( lpCommTimeouts->WriteTotalTimeoutMultiplier !=
		pHWHead->CommTimeouts.WriteTotalTimeoutMultiplier )
	{

	}

	// Now that we have done the right thing, store this DCB
	pHWHead->CommTimeouts = *lpCommTimeouts;

	RETAILMSG(DEBUGMODE,(TEXT("-SL_SetCommTimeout 0x%X\r\n"), pHead));

	return(retval);
}



//
//  @doc OEM
//  @func    BOOL | SL_Ioctl | Device IO control routine.  
//  @parm DWORD | dwOpenData | value returned from COM_Open call
//    @parm DWORD | dwCode | io control code to be performed
//    @parm PBYTE | pBufIn | input data to the device
//    @parm DWORD | dwLenIn | number of bytes being passed in
//    @parm PBYTE | pBufOut | output data from the device
//    @parm DWORD | dwLenOut |maximum number of bytes to receive from device
//    @parm PDWORD | pdwActualOut | actual number of bytes received from device
//
//    @rdesc        Returns TRUE for success, FALSE for failure
//
//  @remark  The MDD will pass any unrecognized IOCTLs through to this function.
//
BOOL
SL_Ioctl(PVOID pHead, DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,
         PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut)
{
//    PSER_INFO pHWHead = (PSER_INFO)pHead;
	PS2440_UART_INFO pHWHead = (PS2440_UART_INFO)pHead;
    DWORD dwErr = ERROR_SUCCESS;    
    BOOL  bRc = TRUE;
    
    switch (dwCode)
	{
        //
        // Power Management
        //
        case IOCTL_POWER_CAPABILITIES: 
        {
            PPOWER_CAPABILITIES ppc;
            
            if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(POWER_CAPABILITIES)))
			{
                bRc = FALSE;
                dwErr = ERROR_INVALID_PARAMETER;
                break;
            }
            
            ppc = (PPOWER_CAPABILITIES)pBufOut;
            
            memset(ppc, 0, sizeof(POWER_CAPABILITIES));


//			ppc->DeviceDx = 0x11;	// support D0, D4
			ppc->DeviceDx = 0x11;	// support D0-D2, D4 (no D3)

            // no wake
            // no inrush

            // Report our power consumption in uAmps rather than mWatts. 
            ppc->Flags = POWER_CAP_PREFIX_MICRO | POWER_CAP_UNIT_AMPS;
            
            // (UART @ 1.2 mA + IR Transmitter LED ON  @ 32 mA) = 33.2 mA = 33200 uA @ 200 MHz
            //                  IR Transmitter LED OFF @ 1.5 nA
            ppc->Power[D0] = 33200;
            
            *pdwActualOut = sizeof(POWER_CAPABILITIES);
        } 
			break;

        case IOCTL_POWER_SET: 
        {
            CEDEVICE_POWER_STATE NewDx;
            
            if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)))
			{
                bRc = FALSE;
                dwErr = ERROR_INVALID_PARAMETER;
                break;
            }
            
            NewDx = *(PCEDEVICE_POWER_STATE)pBufOut;

            if ( VALID_DX(NewDx))
			{
                // grab the CS since the normal Xxx_PowerXxx can not.
                EnterCriticalSection( &pHWHead->RegCritSec );

                switch ( NewDx )
				{
                    case D0:
                        SerPowerOn(pHead);
                        break;

                    default:
                        SerPowerOff(pHead);
                        break;
                }

                // return our Dx
                *(PCEDEVICE_POWER_STATE)pBufOut = pHWHead->Dx;
                    
                RETAILMSG(1, (TEXT("IrDA: IOCTL_POWER_SET: D%u => D%u \r\n"),
                    NewDx, pHWHead->Dx));
                
                LeaveCriticalSection( &pHWHead->RegCritSec );
                
                *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
            }
			else
			{
                bRc = FALSE;
                dwErr = ERROR_INVALID_PARAMETER;
            }
            
        } 
			break;

        case IOCTL_POWER_GET: 
            if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)))
			{
                bRc = FALSE;
                dwErr = ERROR_INVALID_PARAMETER;
                break;
            }

            *(PCEDEVICE_POWER_STATE)pBufOut = pHWHead->Dx;

            DEBUGMSG(1, (TEXT("IrDA: IOCTL_POWER_GET: D%u \r\n"), pHWHead->Dx));

            *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
            
			break;

        default:
            bRc = FALSE;
            DEBUGMSG (ZONE_FUNCTION, (TEXT(" Unsupported ioctl 0x%X\r\n"), dwCode));
            break;            
    }
    
    return(bRc);
}