/*++
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) 2001. Samsung Electronics, co. ltd  All rights reserved.

Module Name:  

Abstract:

 	NK Kernel Generic I/O Control Interface

rev:
	2002.4.3	: First S3C2410 version (SOC)
	2002.1.28	: CE.NET port (kwangyoon LEE, kwangyoon@samsung.com)

Notes: 
--*/

#include <windows.h>
#include <p2.h>
#include <drv_glob.h>
#include <ethdbg.h>
#include <halether.h>
#include <nkintr.h>
#include <pkfuncs.h>
#include <iltiming.h>
#include <kitl.h>
#include <s2440.h>
                     

#ifdef INTERNAL_HAL_TESTING
#include "intioctl.h"
#include "intioctl.c"
#endif


#ifdef IMGSHAREETH
BOOL OEMEthCurrentPacketFilter(PDWORD pdwRequestedFilter);
BOOL OEMEthMulticastList(PUCHAR  pucMulticastAddressList, DWORD  dwNoOfAddresses);
#endif	// IMGSHAREETH.

unsigned int strlenW(LPCWSTR str);

#define pDriverGlobals  ((PDRIVER_GLOBALS) DRIVER_GLOBALS_PHYSICAL_MEMORY_START)

/*
	@func	BOOL | OEMIoControl | generic HAL request
	@rdesc	none
	@comm 	OEMIoControl is called by the Kernel when a device driver or application
	        program calls <f KernelIoControl>. The system is fully preemtible when this
			function is called. The kernel does no processing of this API. It is
			provided to allow an OEM device driver to communicate with kernel mode
			HAL code.
	@xref <l Overview.Windows CE Kernel OEM Interface> <f KernelIoControl>
*/

const WCHAR HALPlatformStr[] = L"S3C2440 multi-platform";
const WCHAR HALOEMStr[] = L"S3C2440";

// ILTIMING Globals
BOOL  fIntrTime;
WORD  wNumInterrupts;
DWORD dwIsrTime1, dwIsrTime2;
DWORD dwSPC;                                                            
DWORD dwIntrTimeCountdown;
DWORD dwIntrTimeCountdownRef;
//void SynchClocks(void);
//void FixupIntrTimeISR(void);


extern EDBG_ADDR MyAddr;
void CreateDeviceName(EDBG_ADDR *pMyAddr, char *szBuf);
DWORD OEMTranslateIrq(DWORD dwIrq);
extern VOID OEMCPUPowerReset();

BOOL OEMIoControl(DWORD dwIoControlCode, LPVOID lpInBuf, DWORD nInBufSize,
	LPVOID lpOutBuf, DWORD nOutBufSize, LPDWORD lpBytesReturned) {
	BOOL retval = FALSE;
	DWORD len;
	PIP_INFO pIPInfo;
	EDBG_ADDR *pEdbgAddr;
    
	switch (dwIoControlCode) {

    case IOCTL_PROCESSOR_INFORMATION:
        if (!lpOutBuf) {
            SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
        }
    
        if (sizeof(PROCESSOR_INFO) > nOutBufSize) {
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return FALSE;
        } else {
            const WCHAR OEMProcCore[] = L"ARM";
            const WCHAR OEMProcName[] = L"ARM920";
            const WCHAR OEMProcVendor[] = L"SAMSUNG";
            PPROCESSOR_INFO pProcInfo = (PPROCESSOR_INFO)lpOutBuf;
    
            if (lpBytesReturned) *lpBytesReturned = sizeof(PROCESSOR_INFO);
            memset(pProcInfo, 0, *lpBytesReturned);
    		
            pProcInfo->wVersion = 1;
    			
            memcpy(pProcInfo->szProcessCore, OEMProcCore, (strlenW(OEMProcCore) + 1) * sizeof(WCHAR));
            memcpy(pProcInfo->szProcessorName, OEMProcName, (strlenW(OEMProcName) + 1) * sizeof(WCHAR));
            memcpy(pProcInfo->szVendor, OEMProcVendor, (strlenW(OEMProcVendor) + 1 ) * sizeof(WCHAR));
    
            pProcInfo->dwInstructionSet = 0;
    
            return TRUE;
        }

	case IOCTL_HAL_GET_DEVICE_INFO :
		if (nInBufSize == 4) {
			switch (*(LPDWORD)lpInBuf) {
			case SPI_GETPLATFORMTYPE:
				len = (strlenW(HALPlatformStr)+1)*sizeof(WCHAR);
				if (nOutBufSize >= len) {
					memcpy(lpOutBuf,HALPlatformStr,len);
					retval = TRUE;
				} else
					SetLastError(ERROR_INSUFFICIENT_BUFFER);
				break;
			case SPI_GETOEMINFO:
				len = (strlenW(HALOEMStr)+1)*sizeof(WCHAR);
				if (nOutBufSize >= len) {
					memcpy(lpOutBuf,HALOEMStr,len);
					retval = TRUE;
				} else
					SetLastError(ERROR_INSUFFICIENT_BUFFER);
				break;
			default:
				SetLastError(ERROR_INVALID_PARAMETER);
			}
		} else {
			SetLastError(ERROR_INVALID_PARAMETER);
		}
		break;
	case IOCTL_HAL_GET_IP_ADDR :
		if ((lpOutBuf == NULL) || (NULL == lpBytesReturned) ||
			(nOutBufSize < sizeof(IP_INFO))) {
			SetLastError(ERROR_INVALID_PARAMETER);
			break;
		}
		// Default address to download is IPINFO_DOWNLOAD
		len = IPINFO_DOWNLOAD;
		
		if ((NULL != lpInBuf) && (nInBufSize ==sizeof(len))) {
			len = *((DWORD *)lpInBuf);
		}
		switch (len) {
		case IPINFO_ODO :
			pEdbgAddr = &(pDriverGlobals->eth.TargetAddr);
			break;
		case IPINFO_DEBUGMSG :
			pEdbgAddr = &(pDriverGlobals->eth.DbgHostAddr);
			break;
		case IPINFO_KDEBUG :
			pEdbgAddr = &(pDriverGlobals->eth.KdbgHostAddr);
			break;
		case IPINFO_ESHELL :
			pEdbgAddr = &(pDriverGlobals->eth.PpshHostAddr);
			break;
		default :
		case IPINFO_DOWNLOAD :
			pEdbgAddr = &(pDriverGlobals->eth.DownloadHostAddr);
			break;
			
		}
		pIPInfo = (PIP_INFO)lpOutBuf;
		pIPInfo->dwIP = pEdbgAddr->dwIP;
		memcpy (pIPInfo->MAC, (char *)pEdbgAddr->wMAC, sizeof(pIPInfo->MAC));
		*lpBytesReturned = sizeof(IP_INFO);
		retval = TRUE;
		
		break;

    case IOCTL_SET_KERNEL_COMM_DEV:
        // Routine to change underlying communications device for kernel services
        return SetKernelCommDev((UCHAR)nInBufSize,(UCHAR)nOutBufSize);
        
    case IOCTL_HAL_INIT_RTC:
        // The kernel has detected a cold-boot.  We probably need to reset our Real Time Clock
        if( nInBufSize >= sizeof(SYSTEMTIME) )
            return FALSE;//OEMSetRealTime( (LPSYSTEMTIME)lpInBuf );
        else
            return FALSE;
        break;

	case IOCTL_HAL_REBOOT:
		// Warm-reset the device.  Since the SMDK2440 doesn't have any software-accessable board
		// logic to assert nRESET to the CPU, we'll use the watchdog timer.  Note that nRSTOUT isn't
		// asserted - thus none of the other board-level logic is hardware reset.  Wish there was a 
		// better way to do this...
		//
		OEMCPUPowerReset();
		//CPUPowerReset();
        return TRUE;
/*
		{
			volatile WATCHreg *pWDReg = (volatile WATCHreg *)WATCH_BASE;

			// Setup the watchdog.
			//
			pWDReg->rWTDAT = 0;
			pWDReg->rWTCNT = 5;			// Load count with low value (value can be used to hold nRSTOUT assertion).
			pWDReg->rWTCON = 0x8021;	// Enable watchdog timer...

			// Wait for watchdog reset...
			//
			while(TRUE);
		}
*/
        break;

	case IOCTL_QUERY_PHYSICALMEM: {
		// Return information about physical memory
		if (nOutBufSize>=sizeof(PHYSICAL_BASIC_INFORMATION) && lpOutBuf!=NULL) {
			((PPHYSICAL_BASIC_INFORMATION)lpOutBuf)->lpBaseAddress = (LPVOID)0x00000000;
			((PPHYSICAL_BASIC_INFORMATION)lpOutBuf)->dwRegionSize = 0x00000000;
			((PPHYSICAL_BASIC_INFORMATION)lpOutBuf)->dwType = PHYSICAL_UNKNOWN;
			return TRUE;
		}
		else {
			SetLastError(ERROR_INVALID_PARAMETER);
			return FALSE;
		}
		break;
	}
	case IOCTL_HAL_TRANSLATE_IRQ:
		// Translate a logical interrupt number to a SYSINTR value.
		//
		if (nInBufSize>=sizeof(PULONG) && nOutBufSize>=sizeof(PULONG) &&
			lpOutBuf && lpInBuf) {
            *(PULONG)lpOutBuf = OEMTranslateIrq(*(PULONG)lpInBuf);
            if (lpBytesReturned)
                *lpBytesReturned = sizeof(ULONG);
            retval = TRUE;
		}
		else {
			SetLastError(ERROR_INVALID_PARAMETER);
			retval=FALSE;
		}
		break;

    case IOCTL_HAL_GET_DEVICEID :
        if (!lpOutBuf || (nOutBufSize < sizeof(DEVICE_ID))) {
            SetLastError (ERROR_INVALID_PARAMETER);
            return FALSE;
        } else {
            PDEVICE_ID  pDeviceID = (PDEVICE_ID)lpOutBuf;
            
#define ROUNDUP(len)        ((len+3)&0xFFFFFFFC)
#define REQ_SIZE            (ROUNDUP(sizeof(DEVICE_ID)) + ROUNDUP(sizeof(HALOEMStr)) + ROUNDUP(KITL_MAX_DEV_NAMELEN))
            
            if (pDeviceID->dwSize >= REQ_SIZE) {
                // Tell them how much we actually used.
                pDeviceID->dwSize = REQ_SIZE;

                pDeviceID->dwPresetIDOffset = ROUNDUP(sizeof(DEVICE_ID));
                pDeviceID->dwPresetIDBytes = sizeof(HALOEMStr);
                memcpy ((PBYTE)lpOutBuf + pDeviceID->dwPresetIDOffset, (PBYTE)HALOEMStr, sizeof(HALOEMStr));
                
                pDeviceID->dwPlatformIDOffset = pDeviceID->dwPresetIDOffset + ROUNDUP(pDeviceID->dwPresetIDBytes);
                pDeviceID->dwPlatformIDBytes = KITL_MAX_DEV_NAMELEN;
                CreateDeviceName(&pDriverGlobals->eth.TargetAddr, (PBYTE)lpOutBuf + pDeviceID->dwPlatformIDOffset);
                if (lpBytesReturned) {
                    *lpBytesReturned = REQ_SIZE;
                }
                SetLastError(0);
                return TRUE;
                
            } else {
                // Tell them how much we actually need.
                pDeviceID->dwSize = REQ_SIZE;
                SetLastError (ERROR_INSUFFICIENT_BUFFER);
                return FALSE;
            }
        }
        break;
    case IOCTL_HAL_ILTIMING : {
        
        if ((nInBufSize == sizeof(ILTIMING_MESSAGE)) && (lpInBuf != NULL)) {
            
            extern DWORD PerfCountSinceTick();
            extern DWORD PerfCountFreq();
            
            PILTIMING_MESSAGE pITM = (PILTIMING_MESSAGE) lpInBuf;
            
            switch (pITM->wMsg) {
                
                case ILTIMING_MSG_ENABLE : {
                    dwIntrTimeCountdownRef = pITM->dwFrequency;
                    RETAILMSG (1, (TEXT("ILTiming Enable (@ every %d ticks)\r\n"), dwIntrTimeCountdownRef));
                    dwIntrTimeCountdown = dwIntrTimeCountdownRef;
                    wNumInterrupts = 0;
                    dwIsrTime1 = 0xFFFFFFFF;
                    fIntrTime = TRUE;
                    break;
                }
                
                case ILTIMING_MSG_DISABLE : {
                    RETAILMSG (1, (TEXT("ILTiming Disable\r\n")));
                    fIntrTime = FALSE;
                    break;
                }
                
                case ILTIMING_MSG_GET_TIMES : {
                    pITM->dwIsrTime1 = dwIsrTime1;
                    pITM->dwIsrTime2 = dwIsrTime2;
                    pITM->wNumInterrupts = wNumInterrupts;
                    pITM->dwSPC = dwSPC;
                    pITM->dwFrequency = PerfCountFreq();
                    wNumInterrupts = 0;
                    // RETAILMSG (1, (TEXT("ILTiming GetTime @ 0x%08X:%08X\r\n"), pITM->dwParam1, pITM->dwParam2));
                    break;
                }
                
                case ILTIMING_MSG_GET_PFN : {
                    pITM->pfnPerfCountSinceTick = (PVOID) PerfCountSinceTick;
                    RETAILMSG (1, (TEXT("ILTiming GetPFN\r\n")));
                    break;
                }
                
                default : {
                    RETAILMSG (1, (TEXT("IOCTL_HAL_ILTIMING : BAD MESSAGE!!!\r\n")));
                    SetLastError(ERROR_INVALID_PARAMETER);
                    return (FALSE);
                }
            }
    
        } else {
            RETAILMSG (1, (TEXT("IOCTL_HAL_ILTIMING : BAD PARAMETERS!!!\r\n")));
            SetLastError(ERROR_INVALID_PARAMETER);
            return (FALSE);
        }
        return (TRUE);
    }

#if IMGSHAREETH

    ////////////////////////////////////////////////////////////////////////////
    //  The support for VMini..
    //
    case IOCTL_VBRIDGE_GET_TX_PACKET:
        return VBridgeUGetOneTxPacket((PUCHAR *)lpOutBuf, nInBufSize);

    case IOCTL_VBRIDGE_GET_TX_PACKET_COMPLETE:
        VBridgeUGetOneTxPacketComplete((PUCHAR)lpInBuf, nInBufSize);
        return TRUE;

    case IOCTL_VBRIDGE_GET_RX_PACKET:
        return VBridgeUGetOneRxPacket((PUCHAR *)lpOutBuf, lpBytesReturned);

    case IOCTL_VBRIDGE_GET_RX_PACKET_COMPLETE:
        VBridgeUGetOneRxPacketComplete((PUCHAR)lpInBuf);
        return TRUE;

    case IOCTL_VBRIDGE_GET_ETHERNET_MAC:
        VBridgeUGetEDBGMac((PBYTE)lpOutBuf);
        return TRUE;

	case IOCTL_VBRIDGE_CURRENT_PACKET_FILTER:
		////////////////////////////////////////////////////////////////////////
		//	First, see if filter setting is implemented, then inform vbridge
		//	on the new filtering.
		//
		if (OEMEthCurrentPacketFilter((PDWORD)lpInBuf))
		{
			VBridgeUCurrentPacketFilter((PDWORD)lpInBuf);
			return TRUE;
		}
		return FALSE;
		
	case IOCTL_VBRIDGE_802_3_MULTICAST_LIST:
		if (OEMEthMulticastList((PUCHAR)lpInBuf, nInBufSize))			
			return TRUE;		
		return FALSE;

	case IOCTL_VBRIDGE_WILD_CARD:
		return VBridgeUWildCard(
					lpInBuf,
					nInBufSize,
					lpOutBuf,
					nOutBufSize,
					lpBytesReturned);
        
    case IOCTL_VBRIDGE_SHARED_ETHERNET:
        ////////////////////////////////////////////////////////////////////////
        //  Yes, this kernel supports shared ethernet port.
        //
        return TRUE;
#endif	// IMGSHAREETH.

	default :
#ifdef INTERNAL_HAL_TESTING
		if (retval = InternalHalTesting (dwIoControlCode, lpInBuf, nInBufSize, lpOutBuf, nOutBufSize, lpBytesReturned)) {
			break;
		}
#endif
		SetLastError(ERROR_NOT_SUPPORTED);
		break;
	}
    return retval;
}

