www.pudn.com > DMBDRV.rar > SmsNdis.c


 
#include "SmsNdis.h" 
#include "SmsGenDrv.h" 
 
/////////////////////////////////////////////////////////////////////////////// 
 
#define ENABLED_DEBUG_ZONE   ZONE_ERROR_SET | ZONE_WARNING_SET //| ZONE_INIT_SET | ZONE_INFO_SET 
 
 DBGPARAM dpCurSettings = 
 { 
   TEXT("Siano NDIS"), 
   {	/* Note: the order of the following must */ 
		/* match the definitions in debug.h:     */ 
     TEXT("Errors"),    TEXT("Warnings"),   TEXT("Init"),             
     TEXT("Info"), TEXT("Undefined"),  TEXT("Undefined"), 
     TEXT("Undefined"), TEXT("Temporary"),  TEXT("Undefined"), 
     TEXT("Undefined"), TEXT("Undefined"),  TEXT("Undefined"), 
     TEXT("Undefined"), TEXT("Undefined"),  TEXT("Undefined"), 
     TEXT("Undefined") 
   }, 
   ENABLED_DEBUG_ZONE 
  }; 
 
/////////////////////////////////////////////////////////////////////////////// 
 
static UCHAR LocalMAC[] = {0x00,0x04,0x76,0x0D,0xAD,0x7A}; 
static UCHAR RemoteMAC[] = {0x00,0x01,0x01,0x03,0x04,0x40}; 
 
/////////////////////////////////////////////////////////////////////////////// 
 
/*****************************************************************************/ 
/* The SmsNdis Miniport device structure                                              */ 
/*****************************************************************************/ 
typedef struct SMS_SMS_NDIS_S 
{ 
	NDIS_HANDLE hMiniportAdapter; 
	NDIS_HANDLE hWrapperConfigContext; 
 
	HANDLE	hGenDrv; 
 
	UCHAR	MulticastList[MAX_MULTICAST_LIST_SIZE][ETHERNET_ADDRESS_LEN]; 
	UINT32	MulticastListCount; 
	ULONG	CurrentPacketFilter; 
	ULONG	CurrentLookahead; 
 
	ULONG	RxPacketCount; 
	BYTE	PrevPktPortion[MAX_IP_FRAME_LEN]; 
	ULONG	PrevPktPortionCount; 
 
} SMS_SMS_NDIS_ST, *PSMS_SMS_NDIS_ST; 
 
/////////////////////////////////////////////////////////////////////////////// 
 
void ReadIpCB(DWORD hContext, void* pBuffer, UINT32 BufSize); 
 
BOOL RegisterReadIpCBFunc(PSMS_SMS_NDIS_ST pSmsNdis); 
 
/////////////////////////////////////////////////////////////////////////////// 
 
#ifdef LOG_TO_FILE 
 
#define LOG_FILE TEXT("\\Program Files\\SianoSmsUtils\\SmsNdisLog.txt") 
 
HANDLE hLogFile = INVALID_HANDLE_VALUE; 
CRITICAL_SECTION    g_CriticalSection; 
 
VOID LogStrToFile(TCHAR *pLogStr,...) 
{ 
	va_list		argumentList; 
	int			length; 
	CHAR		charBuffer[500]; 
	TCHAR		tcharBuffer[500]; 
	DWORD		written; 
	INT			i; 
 
	EnterCriticalSection(&g_CriticalSection); 
 
	va_start(argumentList, pLogStr); 
 
	do { 
 
		length = _vsntprintf(tcharBuffer, 
			(500 - 1), 
			pLogStr, 
			argumentList); 
 
		if (length < 0) { 
			break; 
		} 
 
		if (hLogFile == INVALID_HANDLE_VALUE) { 
			break; 
		} 
 
		for (i = 0; i < length; i++) { 
			if ((CHAR)tcharBuffer != '\n') { 
				charBuffer[i] = (CHAR)tcharBuffer[i]; 
			} else { 
				charBuffer[i] = ' '; 
			} 
		} 
 
		WriteFile(hLogFile,charBuffer,length,&written,NULL); 
 
	} while (FALSE); 
 
	va_end(argumentList); 
 
	LeaveCriticalSection(&g_CriticalSection); 
} 
#endif 
 
/////////////////////////////////////////////////////////////////////////////// 
 
BOOL WINAPI DllMain( 
   HANDLE hDllHandle, 
   DWORD  dwReason, 
   LPVOID lpreserved 
   ) 
{ 
    BOOL bRc = TRUE; 
    
    UNREFERENCED_PARAMETER(hDllHandle); 
    UNREFERENCED_PARAMETER(lpreserved); 
 
	switch (dwReason) 
	{ 
		case DLL_PROCESS_ATTACH: 
			REGISTERZONES(hDllHandle); 
#ifdef LOG_TO_FILE 
			hLogFile = CreateFile(LOG_FILE, 
                                  GENERIC_WRITE, 
                                  FILE_SHARE_READ | FILE_SHARE_WRITE, 
                                  NULL, 
                                  CREATE_ALWAYS, 
                                  0, 
                                  NULL); 
 
			if (hLogFile == INVALID_HANDLE_VALUE) 
				return FALSE; 
			InitializeCriticalSection(&g_CriticalSection); 
#endif 
			DBGMSG(ZONE_INIT, (TEXT("SmsNdis: DLL_PROCESS_ATTACH\r\n"))); 
			break; 
 
		case DLL_PROCESS_DETACH: 
			DBGMSG(ZONE_INIT, (TEXT("SmsNdis: DLL_PROCESS_DETACH\r\n"))); 
#ifdef LOG_TO_FILE 
			if (hLogFile != INVALID_HANDLE_VALUE) 
			{ 
				CloseHandle(hLogFile); 
			} 
			DeleteCriticalSection(&g_CriticalSection); 
#endif 
			break; 
 
		default: 
			break; 
	} 
 
    return bRc; 
} 
 
////////////////////////////////////////////////////////////////////////////// 
// SmsNdis_InitializeHandler - starts an adapter and registers resources with 
//				the wrapper 
// Inputs:   MediumArray - array of media types for the driver to choose from 
//           MediumArraySize - number of entries in the array 
//           MiniportAdapterHandle - handle for passing to the wrapper when 
//           referring to this adapter 
//           WrapperConfigurationContext - a handle to pass to NdisOpenConfiguration 
// Outputs:  pOpenErrorStatus - extra status bytes for opening token ring 
//           adapters 
//           pSelectedMediumIndex - index of the media type chosen by the 
//           driver 
//  Returns: NDIS_STATUS 
// Notes: 
////////////////////////////////////////////////////////////////////////////// 
NDIS_STATUS SmsNdis_InitializeHandler(	OUT PNDIS_STATUS	pOpenErrorStatus, 
                             			PUINT				pSelectedMediumIndex, 
                             			PNDIS_MEDIUM   		MediumArray, 
                             			UINT           		MediumArraySize, 
                             			NDIS_HANDLE    		MiniportAdapterHandle, 
                             			NDIS_HANDLE    		WrapperConfigurationContext) 
{ 
    NDIS_STATUS ndisStatus; 
	UINT mIdx; 
	PSMS_SMS_NDIS_ST pSmsNdis; 
	NDIS_PHYSICAL_ADDRESS HighestAcceptableMax = NDIS_PHYSICAL_ADDRESS_CONST(-1,-1); 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +SmsNdis_InitializeHandler\r\n"))); 
 
	// search for the medium type (802.3) in the given array. 
	for (mIdx = 0; mIdx < MediumArraySize; mIdx++) 
	{ 
		if (MediumArray[mIdx] == NdisMedium802_3) 
		{ 
			// found what we're looking for 
			break; 
		} 
	} 
 
	if (mIdx == MediumArraySize) 
	{ 
		DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: Unsupported mediums\r\n"))); 
	} 
 
	*pSelectedMediumIndex = mIdx; 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: Selected Medium Index %d\r\n"),mIdx)); 
 
	// allocate device structure 
	ndisStatus = NdisAllocateMemory((PVOID*)&pSmsNdis,sizeof(SMS_SMS_NDIS_ST),0,HighestAcceptableMax); 
	if (ndisStatus == NDIS_STATUS_SUCCESS) 
	{ 
		NdisZeroMemory(pSmsNdis,sizeof(SMS_SMS_NDIS_ST)); 
 
		// register the allocated device as adapter context for use in SmsNdis_XXX 
		NdisMSetAttributesEx(MiniportAdapterHandle,(NDIS_HANDLE)pSmsNdis,0,0,NdisInterfaceInternal); 
 
		pSmsNdis->hMiniportAdapter = MiniportAdapterHandle; 
		pSmsNdis->hWrapperConfigContext = WrapperConfigurationContext; 
		pSmsNdis->CurrentPacketFilter = SUPPORTED_PACKET_FILTER; 
		pSmsNdis->CurrentLookahead = MAX_IP_PACKET_LEN; 
 
		if (RegisterReadIpCBFunc(pSmsNdis) == FALSE) 
		{ 
			NdisFreeMemory(pSmsNdis,sizeof(SMS_SMS_NDIS_ST),0); 
			pSmsNdis = NULL; 
			ndisStatus = NDIS_STATUS_FAILURE; 
		} 
	} 
	else 
	{ 
		pSmsNdis = NULL; 
		DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: Failed to allocate SmsNdis device\r\n"))); 
	} 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -SmsNdis_InitializeHandler\r\n"))); 
	return ndisStatus; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// SmsNdis_HaltHandler 
// Inputs:  MiniportAdapterContext - the device context (pointer to our 
//          adapter) 
// Outputs: 
// Returns: 
// Notes:   
///////////////////////////////////////////////////////////////////////////// 
VOID SmsNdis_HaltHandler(NDIS_HANDLE MiniportAdapterContext) 
{ 
	PSMS_SMS_NDIS_ST pSmsNdis = (PSMS_SMS_NDIS_ST)MiniportAdapterContext; 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +SmsNdis_HaltHandler\r\n"))); 
 
	if (pSmsNdis) 
	{ 
		if (pSmsNdis->hGenDrv) 
		{ 
			CloseHandle(pSmsNdis->hGenDrv); 
			pSmsNdis->hGenDrv = NULL; 
		} 
 
		NdisTerminateWrapper(pSmsNdis->hWrapperConfigContext,NULL); 
 
		// freeing the SmsNdis device 
		NdisFreeMemory(pSmsNdis,sizeof(SMS_SMS_NDIS_ST),0); 
	} 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -SmsNdis_HaltHandler\r\n"))); 
} 
 
////////////////////////////////////////////////////////////////////////////// 
// SmsNdis_QueryInformationHandler - 
// Inputs:   MiniportAdapterContext - a pointer to the adapter 
//           Oid - the NDIS_OID to process 
//           pInfoBuffer -  a pointer into the NdisRequest->InformationBuffer 
//            into which store the result of the query 
//           InfoBufferLen - the number of bytes at pInfoBuffer 
// Outputs:  pBytesWritten - a pointer to the number of bytes written into the 
//            InformationBuffer 
//           pBytesNeeded - if there is not enough room the information buffer 
//            then this will contathe number of bytes needed to complete the 
//            request 
// Notes:    
////////////////////////////////////////////////////////////////////////////// 
NDIS_STATUS SmsNdis_QueryInformationHandler(NDIS_HANDLE  MiniportAdapterContext, 
                                   			NDIS_OID     Oid, 
                                   			PVOID        pInfoBuffer, 
                                   			ULONG        InfoBufferLen, 
                                   			PULONG       pBytesWritten, 
                                   			PULONG       pBytesNeeded) 
{ 
    NDIS_STATUS ndisStatus; 
	PSMS_SMS_NDIS_ST pSmsNdis; 
	PVOID pReturnBuff; 
	ULONG returnBuffLen; 
	ULONG tmpULong; 
	USHORT tmpUshort; 
	NDIS_HARDWARE_STATUS hwStatus; 
	NDIS_MEDIUM ndisMedium; 
    const PCHAR strVendor = "Siano NDIS Miniport\0"; 
	const UCHAR EmuEthernetAddr[] = {0x00,0x04,0x76,0x0D,0xAD,0x7A}; 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +SmsNdis_QueryInformationHandler\r\n"),Oid)); 
 
	pSmsNdis = (PSMS_SMS_NDIS_ST)MiniportAdapterContext; 
	*pBytesWritten = 0; 
	*pBytesNeeded = 0; 
	ndisStatus = NDIS_STATUS_SUCCESS; 
	pReturnBuff = NULL; 
	returnBuffLen = 0; 
 
#define CASE_OID(Oid) \ 
	case Oid: \ 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: Query 0x%08X ") TEXT(#Oid) TEXT("\r\n"),Oid)); 
 
#define CASE_OID_NOT_SUPPORTED(Oid) \ 
	case Oid: \ 
	DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: Query 0x%08X ") TEXT(#Oid) TEXT(" not supported\r\n"),Oid)); \ 
	ndisStatus = NDIS_STATUS_NOT_SUPPORTED; 
 
	switch(Oid) 
	{ 
		CASE_OID(OID_GEN_HARDWARE_STATUS) 
            hwStatus = NdisHardwareStatusReady; 
            pReturnBuff = (PVOID)&hwStatus; 
            returnBuffLen  = sizeof(hwStatus); 
            break; 
		CASE_OID(OID_GEN_MEDIA_SUPPORTED) 
            ndisMedium = NdisMedium802_3; 
            pReturnBuff = (PVOID)&ndisMedium; 
            returnBuffLen  = sizeof(ndisMedium); 
            break; 
		CASE_OID(OID_GEN_MEDIA_IN_USE) 
            ndisMedium = NdisMedium802_3; 
            pReturnBuff = (PVOID)&ndisMedium; 
            returnBuffLen  = sizeof(ndisMedium); 
            break; 
		CASE_OID(OID_GEN_MAXIMUM_LOOKAHEAD) 
            tmpULong = MAX_IP_PACKET_LEN; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_MAXIMUM_FRAME_SIZE) 
            tmpULong = MAX_IP_PACKET_LEN; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_LINK_SPEED) 
            tmpULong = 100000; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_TRANSMIT_BUFFER_SPACE) 
            tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_RECEIVE_BUFFER_SPACE) 
            tmpULong = MAX_IP_PACKET_LEN; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_TRANSMIT_BLOCK_SIZE) 
            tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_RECEIVE_BLOCK_SIZE) 
            tmpULong = MAX_IP_PACKET_LEN; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_VENDOR_DESCRIPTION) 
			pReturnBuff = (PVOID)strVendor; 
            returnBuffLen  = strlen(strVendor) + 1; 
            break; 
        CASE_OID(OID_GEN_VENDOR_DRIVER_VERSION) 
            tmpUshort = 0x0001; 
            pReturnBuff = (PVOID)&tmpUshort; 
            returnBuffLen  = sizeof(tmpUshort); 
            break; 
        CASE_OID(OID_GEN_DRIVER_VERSION) 
            tmpUshort = 0x0001; 
            pReturnBuff = (PVOID)&tmpUshort; 
            returnBuffLen  = sizeof(tmpUshort); 
            break; 
		CASE_OID(OID_GEN_CURRENT_PACKET_FILTER) 
			tmpULong = pSmsNdis->CurrentPacketFilter; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_CURRENT_LOOKAHEAD) 
            tmpULong = pSmsNdis->CurrentLookahead; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_MAXIMUM_TOTAL_SIZE) 
            tmpULong = MAX_IP_PACKET_LEN; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
        CASE_OID(OID_GEN_MAC_OPTIONS) 
			tmpULong = NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |  
						NDIS_MAC_OPTION_RECEIVE_SERIALIZED |  
						NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | 
						NDIS_MAC_OPTION_NO_LOOPBACK; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_MAXIMUM_SEND_PACKETS) 
            tmpULong = 1; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_XMIT_OK) 
            tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break;			 
		CASE_OID(OID_GEN_RCV_OK) 
			tmpULong = pSmsNdis->RxPacketCount; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_XMIT_ERROR) 
			tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_RCV_ERROR) 
			tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_RCV_NO_BUFFER) 
			tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_GEN_INIT_TIME_MS) 
			tmpULong = 0; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_802_3_PERMANENT_ADDRESS) 
            pReturnBuff = (PVOID)EmuEthernetAddr; 
            returnBuffLen  = sizeof(EmuEthernetAddr); 
            break; 
		CASE_OID(OID_802_3_CURRENT_ADDRESS) 
            pReturnBuff = (PVOID)EmuEthernetAddr; 
            returnBuffLen  = sizeof(EmuEthernetAddr); 
            break; 
		CASE_OID(OID_802_3_MAXIMUM_LIST_SIZE) 
            tmpULong = MAX_MULTICAST_LIST_SIZE; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
		CASE_OID(OID_802_3_MULTICAST_LIST) 
			pReturnBuff = (PVOID)pSmsNdis->MulticastList; 
            returnBuffLen  = pSmsNdis->MulticastListCount * ETHERNET_ADDRESS_LEN;			 
            break; 
		CASE_OID(OID_GEN_MEDIA_CONNECT_STATUS) 
            tmpULong = NdisMediaStateConnected; 
            pReturnBuff = (PVOID)&tmpULong; 
            returnBuffLen  = sizeof(tmpULong); 
            break; 
 
		CASE_OID_NOT_SUPPORTED(OID_GEN_SUPPORTED_LIST) 
            break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_VENDOR_ID) 
            break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_PHYSICAL_MEDIUM) 
            break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_MEDIA_CAPABILITIES) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_PNP_CAPABILITIES) 
            break; 
		CASE_OID_NOT_SUPPORTED(OID_TCP_TASK_OFFLOAD) 
            break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_DIRECTED_BYTES_XMIT) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_DIRECTED_FRAMES_XMIT) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_MULTICAST_BYTES_XMIT) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_MULTICAST_FRAMES_XMIT) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_BROADCAST_BYTES_XMIT) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_BROADCAST_FRAMES_XMIT) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_DIRECTED_BYTES_RCV) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_DIRECTED_FRAMES_RCV) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_MULTICAST_BYTES_RCV) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_MULTICAST_FRAMES_RCV) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_BROADCAST_BYTES_RCV) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_BROADCAST_FRAMES_RCV) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_RCV_CRC_ERROR) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_TRANSMIT_QUEUE_LENGTH) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_GET_TIME_CAPS) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_GET_NETCARD_TIME) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_NETCARD_LOAD) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_DEVICE_PROFILE) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_GEN_MEDIA_SENSE_COUNTS) 
			break; 
		 
		default: 
			DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: Query Oid 0x%08X invalid or not supported\r\n"),Oid)); 
			ndisStatus = NDIS_STATUS_INVALID_OID; 
			break; 
	} 
 
#undef CASE_OID 
#undef CASE_OID_NOT_SUPPORTED 
 
	if (ndisStatus == NDIS_STATUS_SUCCESS) 
	{ 
		if (returnBuffLen > InfoBufferLen) 
		{ 
			*pBytesNeeded = returnBuffLen; 
			DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: pInfoBuffer len too small. Len %d Need %d\r\n"),InfoBufferLen,returnBuffLen)); 
		} 
		else 
		{ 
			if (pReturnBuff) 
			{ 
				NdisMoveMemory(pInfoBuffer, pReturnBuff, returnBuffLen); 
				*pBytesWritten = returnBuffLen; 
				*pBytesNeeded = 0; 
			} 
			else 
			{ 
				DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: Wrong handling of OID\r\n"))); 
				ndisStatus = NDIS_STATUS_INVALID_OID; 
			} 
		} 
	} 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -SmsNdis_QueryInformationHandler. status 0x%X\r\n"),ndisStatus)); 
	return ndisStatus; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// SmsNdis_SetInformationHandler - 
// Inputs:   pMiniportAdapterContext - a pointer to the adapter 
//           Oid - the NDIS_OID to process 
//           pInfoBuffer - data to be set 
//           InfoBufferLen - length of information buffer 
// Outputs:  pBytesRead - If the call is successful, returns the number 
//            of bytes read from InformationBuffer 
//           pBytesNeeded - If there is not enough data InformationBuffer 
//            to satisfy the OID, returns the amount of storage needed 
// Returns: NDIS_STATUS_SUCCESS on success 
// Notes:    
///////////////////////////////////////////////////////////////////////////// 
NDIS_STATUS SmsNdis_SetInformationHandler(	NDIS_HANDLE    MiniportAdapterContext, 
                                 			NDIS_OID       Oid, 
                                 			PVOID          pInfoBuffer, 
                                 			ULONG          InfoBufferLen, 
                                 			PULONG         pBytesRead, 
                                 			PULONG         pBytesNeeded) 
{ 
    NDIS_STATUS ndisStatus; 
	PSMS_SMS_NDIS_ST pSmsNdis; 
	UINT32 i; 
	ULONG tmpULong; 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +SmsNdis_SetInformationHandler\r\n"))); 
 
	pSmsNdis = (PSMS_SMS_NDIS_ST)MiniportAdapterContext; 
	ndisStatus = NDIS_STATUS_SUCCESS; 
	*pBytesRead = 0; 
	*pBytesNeeded = 0; 
	 
#define CASE_OID(Oid) \ 
	case Oid: \ 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: Set 0x%08X ") TEXT(#Oid) TEXT("\r\n"),Oid)); 
 
#define CASE_OID_NOT_SUPPORTED(Oid) \ 
	case Oid: \ 
	DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: Set 0x%08X ") TEXT(#Oid) TEXT(" not supported\r\n"),Oid)); \ 
	ndisStatus = NDIS_STATUS_NOT_SUPPORTED; 
 
	switch(Oid) 
	{ 
		CASE_OID(OID_GEN_CURRENT_PACKET_FILTER) 
            if (InfoBufferLen != sizeof(ULONG)) 
			{ 
				*pBytesNeeded = ETHERNET_ADDRESS_LEN; 
				ndisStatus = NDIS_STATUS_INVALID_LENGTH; 
				break; 
			} 
			tmpULong = *((ULONG*)pInfoBuffer); 
			//if (tmpULong & SUPPORTED_PACKET_FILTER) 
			//{ 
			//	*pBytesRead = InfoBufferLen; 
			//	*pBytesNeeded = 0; 
			//	ndisStatus = NDIS_STATUS_NOT_SUPPORTED; 
			//} 
			//else 
			{ 
				pSmsNdis->CurrentPacketFilter = tmpULong; 
			}	 
			DBGMSG(ZONE_INFO, (TEXT("SmsNdis: Set Current packet filter 0x%X\r\n"),tmpULong)); 
			break; 
		CASE_OID(OID_GEN_CURRENT_LOOKAHEAD) 
            if (InfoBufferLen != sizeof(ULONG)) 
			{ 
				*pBytesNeeded = sizeof(ULONG); 
				ndisStatus = NDIS_STATUS_INVALID_LENGTH; 
				break; 
			} 
			pSmsNdis->CurrentLookahead = *((ULONG*)pInfoBuffer); 
			*pBytesRead = InfoBufferLen; 
			*pBytesNeeded = 0; 
			break; 
		CASE_OID(OID_802_3_MULTICAST_LIST) 
 
            if (((InfoBufferLen % ETHERNET_ADDRESS_LEN) != 0) || 
                 (InfoBufferLen > (ETHERNET_ADDRESS_LEN * MAX_MULTICAST_LIST_SIZE))) 
			{ 
				*pBytesNeeded = ETHERNET_ADDRESS_LEN; 
				ndisStatus = NDIS_STATUS_INVALID_LENGTH; 
				break; 
			} 
 
			NdisZeroMemory(pSmsNdis->MulticastList,ETHERNET_ADDRESS_LEN * MAX_MULTICAST_LIST_SIZE); 
			if (InfoBufferLen > 0) 
			{ 
				NdisMoveMemory(pSmsNdis->MulticastList,pInfoBuffer,InfoBufferLen); 
			} 
			pSmsNdis->MulticastListCount = InfoBufferLen / ETHERNET_ADDRESS_LEN; 
			 
			*pBytesRead = InfoBufferLen; 
 
			DBGMSG(ZONE_INFO, (TEXT("SmsNdis: Multicast Address List :\r\n"))); 
			for (i = 0;i < pSmsNdis->MulticastListCount;i++) 
			{ 
				DBGMSG(ZONE_INFO, (TEXT("SmsNdis: %02x:%02x:%02x:%02x:%02x:%02x\r\n"), 
					pSmsNdis->MulticastList[i][0],pSmsNdis->MulticastList[i][1], 
					pSmsNdis->MulticastList[i][2],pSmsNdis->MulticastList[i][3], 
					pSmsNdis->MulticastList[i][4],pSmsNdis->MulticastList[i][5])); 
			} 
			break; 
		CASE_OID(OID_GEN_NETWORK_LAYER_ADDRESSES) 
			*pBytesRead = InfoBufferLen; 
			*pBytesNeeded = 0; 
			break; 
		CASE_OID(OID_GEN_TRANSPORT_HEADER_OFFSET) 
			*pBytesRead = InfoBufferLen; 
			*pBytesNeeded = 0; 
			break; 
 
		CASE_OID_NOT_SUPPORTED(OID_GEN_PROTOCOL_OPTIONS) 
			break; 
		CASE_OID_NOT_SUPPORTED(OID_PNP_ADD_WAKE_UP_PATTERN) 
			break; 
		default: 
			DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: Set Oid 0x%08X invalid or not supported\r\n"),Oid)); 
			ndisStatus = NDIS_STATUS_INVALID_OID; 
			break; 
	} 
 
#undef CASE_OID 
#undef CASE_OID_NOT_SUPPORTED 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -SmsNdis_SetInformationHandler. status 0x%X\r\n"),ndisStatus)); 
	return ndisStatus; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// SmsNdis_SendHandler -  
// Inputs:   MiniportAdapterContext - device context 
//           pPacket - descriptor for the packet that is to be transmitted 
//           Flags - optional send flags 
// Outputs: 
// Returns: NDIS_STATUS 
// Notes:    
///////////////////////////////////////////////////////////////////////////// 
NDIS_STATUS SmsNdis_SendHandler(NDIS_HANDLE MiniportAdapterContext, 
                        PNDIS_PACKET pPacket, 
                        UINT Flags) 
{ 
	//PSMS_SMS_NDIS_ST pSmsNdis = (PSMS_SMS_NDIS_ST)MiniportAdapterContext; 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +SmsNdis_SendHandler\r\n"))); 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -SmsNdis_SendHandler\n"))); 
	return NDIS_STATUS_SUCCESS; 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//  DriverEntry - initial driver entry point for Ndis driver 
//  Input:  pDriverObject - NT driver object 
//          pRegistryPath - path to this drivers registry entries 
//  Output: 
//  Returns: NDIS_STATUS 
//  Notes:  
////////////////////////////////////////////////////////////////////////////// 
NDIS_STATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath) 
{ 
    NDIS_STATUS ndisStatus; 
	NDIS_HANDLE hNdisWrapper; 
	NDIS_MINIPORT_CHARACTERISTICS mChars; 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +DriverEntry\r\n"))); 
 
	NdisMInitializeWrapper (&hNdisWrapper,pDriverObject,pRegistryPath,NULL); 
 
	NdisZeroMemory(&mChars,sizeof(mChars)); 
 
	mChars.MajorNdisVersion = 0x04; 
	mChars.MinorNdisVersion = 0x00; 
	mChars.HaltHandler = SmsNdis_HaltHandler; 
	mChars.InitializeHandler = SmsNdis_InitializeHandler; 
	mChars.QueryInformationHandler = SmsNdis_QueryInformationHandler; 
	mChars.SendHandler = SmsNdis_SendHandler; 
	mChars.SetInformationHandler = SmsNdis_SetInformationHandler; 
 
	// register NIC entry point 
	ndisStatus = NdisMRegisterMiniport(hNdisWrapper,&mChars,sizeof(mChars)); 
	if (ndisStatus != NDIS_STATUS_SUCCESS) 
	{ 
		NdisTerminateWrapper(hNdisWrapper,NULL); 
		DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: Failed to register miniport. status 0x%X\r\n"),ndisStatus)); 
	} 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -DriverEntry\r\n"))); 
	return ndisStatus; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
 
BOOL RegisterReadIpCBFunc(PSMS_SMS_NDIS_ST pSmsNdis) 
{ 
	GEN_IOCTL_REGISTER_READ_IP_PARAMS_ST readIpParams; 
 
	pSmsNdis->hGenDrv = CreateFile(TEXT("SGD1:"),GENERIC_READ | GENERIC_WRITE,  
									0,NULL,OPEN_EXISTING,0,NULL); 
	if (pSmsNdis->hGenDrv == NULL) 
	{ 
		DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: Failed to open connection to GenDrv\r\n"))); 
		return FALSE; 
	} 
 
	readIpParams.pReadIpCBFunc = ReadIpCB; 
	readIpParams.hContext = (DWORD)pSmsNdis; 
	if (DeviceIoControl(pSmsNdis->hGenDrv,SIANO_GEN_IOCTL_REGISTER_READ_IP_CB, 
						&readIpParams,sizeof(readIpParams),NULL,0,NULL,NULL) == FALSE) 
	{ 
		DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: Failed to register ReadIpCB\r\n"))); 
		CloseHandle(pSmsNdis->hGenDrv); 
		pSmsNdis->hGenDrv = NULL; 
	} 
 
	return TRUE; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
/* 
 *	MulticastEthAddrFromIpAddr 
 *  Populate the ethAddr ethernet address with the 
 *  IANA multicast address from the IP multicast group in ipAddr 
 */ 
void MulticastEthAddrFromIpAddr( 
	ULONG	ipAddr, 
	UCHAR	*ethAddr) 
{ 
	ethAddr[0] = 0x01; 
	ethAddr[1] = 0x00; 
	ethAddr[2] = 0x5E; 
	ethAddr[3] = (UCHAR)((ipAddr & 0x00ff00)>>8) & 0x7f; 
	ethAddr[4] = (UCHAR)((ipAddr & 0x00ff0000)>>16); 
	ethAddr[5] = (UCHAR)((ipAddr & 0xff000000)>>24); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
/* 
 * Return true if the IP address is in the multicast range (class D) 
 * of 224.0.0.0 to 239.255.255.255 
 * !!Address should be in network byte order!! 
 */ 
static BOOL IsAnIPMulticastGroup( 
	ULONG IpAddr 
	) 
{ 
	UCHAR *AddrArray = (PUCHAR)&IpAddr; 
 
	if (AddrArray[3] >= 224 && AddrArray[3] < 240) { 
		return TRUE; 
	} else { 
		return FALSE; 
	} 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
 
BOOL IsAnIPv6MulticastGroup(UCHAR *ipAddr) 
{ 
	if(ipAddr[0]==IPV6_MULTICAST_INDICATOR) 
	{ 
		return TRUE; 
	} 
	else 
	{ 
		return FALSE; 
	} 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
 
void MulticastEthAddrFromIPv6Addr(UCHAR	*ipAddr/*destanattion address*/,UCHAR	*ethAddr) 
{ 
	/* 
	For IPv6 Multicast addresses,  
	the Ethernet MAC is derived by the four low-order octets  
	OR'ed with the MAC 33:33:00:00:00:00,  
	so for example the IPv6 address FF02:DEAD:BEEF:1:3  
	would map to the Ethernet MAC address 33:33:00:01:00:03 
	*/ 
 
	ethAddr[0] = 0x33; 
	ethAddr[1] = 0x33; 
	ethAddr[2] = ipAddr[12]; 
	ethAddr[3] = ipAddr[13]; 
	ethAddr[4] = ipAddr[14]; 
	ethAddr[5] = ipAddr[15]; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
 
void IndicateIpPacketReceive(PSMS_SMS_NDIS_ST pSmsNdis,PBYTE pPacket,UINT32 PacketLen) 
{ 
	ETHERNETHEADER_ST EthHdr; 
	ULONG u32; 
 
	if ((pPacket[0] & 0xF0) == 0x60) 
	{ 
		// fill ethernet header 
		if (IsAnIPv6MulticastGroup( pPacket + 24 /*DstAddress*/ )) 
		{ 
			MulticastEthAddrFromIPv6Addr(pPacket + 24 /*DstAddress*/ , EthHdr.DestAdd); 
		} 
		else 
		{ 
			memcpy(EthHdr.DestAdd, LocalMAC, 6); 
		} 
		memcpy(EthHdr.SrcAdd,RemoteMAC,6); 
		EthHdr.PacketType = htons(PACKET_TYPE_IPV6); 
	} 
	else // v4 
	{ 
		// fill ethernet header 
		u32 = (pPacket[16] << 24) | (pPacket[17] << 16) | (pPacket[18] << 8) | pPacket[19]; /* DestAdd */ 
		if (IsAnIPMulticastGroup( ntohl(u32) )) 
		{ 
			MulticastEthAddrFromIpAddr(u32, EthHdr.DestAdd); 
		} 
		else 
		{ 
			memcpy(EthHdr.DestAdd, LocalMAC, 6); 
		} 
		memcpy(EthHdr.SrcAdd,RemoteMAC,6); 
		EthHdr.PacketType = htons(PACKET_TYPE_IP); 
	} 
 
#if 0 // to print RTP sequence num. and check it, enable this line 
		{ 
			PBYTE pUdpHdr; 
			UINT16 curSeq,destPort; 
			static UINT16 basePort = 0xFFFF; 
 
			if ((pPacket[0] & 0xF0) == 0x60) 
				pUdpHdr = pPacket + 40; 
			else 
				pUdpHdr = pPacket + 20; 
			destPort = (pUdpHdr[2] << 8) + pUdpHdr[3]; /* Udp DestinationPort */ 
			curSeq = (pUdpHdr[10] << 8) + pUdpHdr[11]; /* seq num */ 
 
			//DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: %u %u\r\n"),destPort,curSeq)); 
 
			if (basePort == 0xFFFF) 
			{ 
				// get the port that will be checked. assumption: the audio port is bigger by 2 from the video 
				basePort = destPort & 0xFFFC; 
			} 
 
			if (destPort == basePort) 
			{ 
				static UINT16 prevSeq = 0xFFFF; 
				if (prevSeq == 0xFFFF) 
				{ 
					DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: checking vd seq port %d\r\n"),destPort)); 
				} 
				if ((prevSeq < curSeq) && ((prevSeq+1) != curSeq)) 
				{ 
					DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: miss vd t %d %d %d miss %d\r\n"),GetTickCount(),prevSeq,curSeq,curSeq-prevSeq-1)); 
					MessageBeep(0xFFFFFFFF); 
				} 
				prevSeq = curSeq; 
			} 
			else if (destPort == (basePort+2)) 
			{ 
				static UINT16 prevSeq = 0xFFFF; 
				if (prevSeq == 0xFFFF) 
				{ 
					DBGMSG(ZONE_WARNING, (TEXT("SmsNdis: checking au seq port %d\r\n"),destPort)); 
				} 
				if ((prevSeq < curSeq) && ((prevSeq+1) != curSeq)) 
				{ 
					DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: miss au t %d %d %d miss %d\r\n"),GetTickCount(),prevSeq,curSeq,curSeq-prevSeq-1)); 
					MessageBeep(0xFFFFFFFF); 
				} 
				prevSeq = curSeq; 
			} 
		} 
#endif 
 
		// insert ip to ip stack 
		NdisMEthIndicateReceive(pSmsNdis->hMiniportAdapter, 
						        NULL,					// TransferData will not be called 
        						(PCHAR)&EthHdr,			// pointer to header 
        						ETHERNET_HEADER_LEN,	// header size 
								pPacket,				// pointer to buffer 
								PacketLen,				// lookahead buffer size 
        						PacketLen);				// packet size 
 
		// update statistics counter 
		pSmsNdis->RxPacketCount++; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
 
void ReadIpCB(DWORD hContext, void* pBuffer, UINT32 BufSize) 
{ 
	PSMS_SMS_NDIS_ST pSmsNdis = (PSMS_SMS_NDIS_ST)hContext; 
	NDIS_HANDLE SwitchHandle; 
	ULONG validIpCount = 0; 
	ULONG u32; 
	ULONG PktLen; 
	PBYTE pBuff = (PBYTE)pBuffer; 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: +ReadIpCB 0x08%X %d\r\n"),pBuffer,BufSize)); 
 
	NdisIMSwitchToMiniport(pSmsNdis->hMiniportAdapter,&SwitchHandle); 
 
	if (pSmsNdis->PrevPktPortionCount > 0) 
	{ 
		DBGMSG(ZONE_INFO, (TEXT("SmsNdis: Handle split packet. PrevPktPortionCount %d\r\n"),pSmsNdis->PrevPktPortionCount)); 
 
		// handle prev partial packet 
		if (pSmsNdis->PrevPktPortionCount < (sizeof(UINT32) * 2)) 
		{ 
			// can't extract ip len form prev packet bytes. 
			u32 = (sizeof(UINT32) * 2) - pSmsNdis->PrevPktPortionCount; 
			if (u32 > BufSize) 
			{ 
				// append to prev 
				NdisMoveMappedMemory(pSmsNdis->PrevPktPortion + pSmsNdis->PrevPktPortionCount,pBuff,BufSize); 
				pSmsNdis->PrevPktPortionCount += BufSize; 
				NdisIMRevertBack(pSmsNdis->hMiniportAdapter,&SwitchHandle); 
				return; 
			} 
			// append to prev 
			NdisMoveMappedMemory(pSmsNdis->PrevPktPortion + pSmsNdis->PrevPktPortionCount,pBuff,u32); 
			pSmsNdis->PrevPktPortionCount += u32; 
			pBuff += u32; 
			BufSize -= u32; 
		} 
 
		if ((pSmsNdis->PrevPktPortion[0] & 0xF0) == 0x60) 
		{ 
			PktLen = ((pSmsNdis->PrevPktPortion[4] << 8) + pSmsNdis->PrevPktPortion[5]); /* PayloadLen */ 
			PktLen += IPV6_HEADER_LEN; 
		} 
		else if ((pSmsNdis->PrevPktPortion[0] & 0xF0) == 0x40) 
		{ 
			PktLen = (pSmsNdis->PrevPktPortion[2] << 8) + pSmsNdis->PrevPktPortion[3]; /* TotalLen */ 
		} 
		else 
		{ 
			PktLen = 0; 
		} 
 
		u32 = PktLen - pSmsNdis->PrevPktPortionCount; // bytes needed from pBuff 
		if (PktLen > pSmsNdis->PrevPktPortionCount && PktLen < MAX_IP_FRAME_LEN &&  // is valid ip len 
			(pSmsNdis->PrevPktPortionCount + u32 <= MAX_IP_FRAME_LEN)) // there is enough space in buff 
		{ 
			if (u32 > BufSize) 
			{ 
				// append to prev 
				NdisMoveMappedMemory(pSmsNdis->PrevPktPortion + pSmsNdis->PrevPktPortionCount,pBuff,BufSize); 
				pSmsNdis->PrevPktPortionCount += BufSize; 
				NdisIMRevertBack(pSmsNdis->hMiniportAdapter,&SwitchHandle); 
				return; 
			} 
			// concatenate the two portion and update pBuff pointer 
			NdisMoveMappedMemory(pSmsNdis->PrevPktPortion + pSmsNdis->PrevPktPortionCount,pBuff,u32); 
			pBuff += u32; 
			BufSize -= u32; 
 
			// insert ip to stack 
			IndicateIpPacketReceive(pSmsNdis,pSmsNdis->PrevPktPortion,PktLen); 
			validIpCount++; 
		} 
		else 
		{ 
			DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: IP concatenation failed. %d\r\n"),PktLen)); 
		} 
		pSmsNdis->PrevPktPortionCount = 0; 
	} 
 
	while (BufSize >= (sizeof(UINT32) * 2)) // atleast part of header include len 
	{ 
		if ((pBuff[0] & 0xF0) == 0x60) 
		{ 
			PktLen = ((pBuff[4] << 8) + pBuff[5]); /* PayloadLen */ 
			PktLen += IPV6_HEADER_LEN; 
		} 
		else if ((pBuff[0] & 0xF0) == 0x40) 
		{ 
			PktLen = (pBuff[2] << 8) + pBuff[3]; /* TotalLen */ 
		} 
		else 
		{ 
			PktLen = 0; 
		} 
 
		if (PktLen == 0 || PktLen > MAX_IP_FRAME_LEN) 
		{ 
			// search for sync bytes 
			pBuff++; 
			BufSize--; 
			if (BufSize < (sizeof(UINT32) * 2)) 
			{ 
				// no chance to find sync bytes 
				BufSize = 0; 
				break; 
			} 
			DBGMSG(ZONE_ERROR, (TEXT("SmsNdis: resync %02X %d %d\r\n"),pBuff[0],PktLen,BufSize)); 
			continue; 
		} 
 
		if (PktLen > BufSize) 
		{ 
			// partial packet 
			break; 
		} 
 
		// insert ip to stack 
		IndicateIpPacketReceive(pSmsNdis,pBuff,PktLen); 
 
		validIpCount++; 
		pBuff += PktLen; 
		BufSize -= PktLen; 
	} 
 
	if (BufSize > 0) 
	{ 
		// save partial packet 
		NdisMoveMappedMemory(pSmsNdis->PrevPktPortion,pBuff,BufSize); 
		pSmsNdis->PrevPktPortionCount = BufSize; 
	} 
 
	if (validIpCount > 0) 
	{ 
		// flush ip stack 
		NdisMEthIndicateReceiveComplete(pSmsNdis->hMiniportAdapter); 
		DBGMSG(ZONE_INFO, (TEXT("SmsNdis: %d valid packets\r\n"),validIpCount)); 
	} 
 
	NdisIMRevertBack(pSmsNdis->hMiniportAdapter,&SwitchHandle); 
 
	DBGMSG(ZONE_INFO, (TEXT("SmsNdis: -ReadIpCB\r\n"))); 
} 
 
///////////////////////////////////////////////////////////////////////////////