/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    net\routing\ipx\adptif\watcher.c

Abstract:

	Debug mode code that display adapter information reported
	by the stack. It also provides UI mechanism to manually
	"disable" (make invisible to clients) and "reenable" adapters
Author:

	Vadim Eydelman

Revision History:

--*/
	// State information maintained for UI dialog
typedef struct _ADAPTER_STATE {
		USHORT			AdapterId;	// Nic ID
		BOOLEAN			Status;		// Status as supplied by the stack
		BOOLEAN			Enabled;	// User settable status
		} ADAPTER_STATE, *PADAPTER_STATE;

DWORD WINAPI
WatcherThread (
	LPVOID param
	);

BOOL CALLBACK
WatcherDlgProc (
    HWND  	hDlg,
    UINT  	uMsg,
    WPARAM  wParam,
    LPARAM  lParam
   );

VOID
NotifyClients (
	INT			i,
	BOOLEAN		Status
	);
VOID
UpdateAdapterList (
	HWND	AdapterLB
	);

HWND			WatcherDlg=NULL;			// UI dialog handle
PADAPTER_STATE	AdapterArray=NULL;			// Current adapter info
ULONG			AdapterArraySize=0;			// Number of adapters in array
PVOID			AdapterDataBuffer=NULL;		// Buffer to receive nic info from driver
HANDLE			DebugWatchEvent=NULL;		// Event to be nofified when nic info changes
DWORD			DbgFlags=0;					// Debug flags
#define DEBUG_SHOW_DIALOG	0x00000001		// Display UI dialog if set
HINSTANCE		HDLLInstance;				// Dll instance handle
HANDLE			WatcherThreadHdl=NULL;		// UI thread handle
DWORD			WatcherThreadID=0;			// Its id


/*++
*******************************************************************
        I n i t i a l i z e W a t c h e r

Routine Description:
		Initializes UI resorces and starts watcher thread

Arguments:

      	hinstDLL,		handle of DLL module 

Return Value:
		None
Remarks:
*******************************************************************
--*/
VOID
InitializeWatcher (
	HINSTANCE	hinstDLL
	) {
	HDLLInstance = hinstDLL;
	InitCommonControls ();
	DebugWatchEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
	ASSERT (DebugWatchEvent!=NULL);
	EnterCriticalSection (&ConfigInfoLock);
	if (IpxDriverHandle==NULL) {
		DWORD status = OpenAdapterConfigPort ();
		ASSERTMSG ("Could not open adapter config port", status==NO_ERROR);
		if (!InRouter ())
			PostAdapterConfigRequest (DebugWatchEvent);
		}
	LeaveCriticalSection (&ConfigInfoLock);

	WatcherThreadHdl = CreateThread (NULL,	// default security
				0,				// default stack
				&WatcherThread,
				(LPVOID)NULL,			
				0,				// default flags
				&WatcherThreadID);
	ASSERTMSG ("Could not create watcher thread ",
								 WatcherThreadHdl!=NULL);
	}

/*++
*******************************************************************
        C l e a n u p W a t c h e r

Routine Description:

		Disposes of resources allocated for UI

Arguments:
		None

Return Value:
		None
Remarks:
*******************************************************************
--*/
VOID
CleanupWatcher (
	void 
	) {
	PostThreadMessage (WatcherThreadID, WM_QUIT, 0, 0);
	WaitForSingleObject (WatcherThreadHdl, INFINITE);
	if (IpxDriverHandle!=NULL)
		CloseAdapterConfigPort ();
	CloseHandle (WatcherThreadHdl);
	CloseHandle (DebugWatchEvent);
	}

/*++
*******************************************************************
        W a t c h e r T h r e a d

Routine Description:

		Event loop for Watcher Dialog 

Arguments:

		param	- Not used

Return Value:

		None

*******************************************************************
--*/

DWORD WINAPI
WatcherThread (
	LPVOID param
	) {
	DWORD			status;
	HANDLE			WaitObjects[2];
#define RegChangeEvt (WaitObjects[0])
	HKEY			regHdl;
	DWORD			length, disposition, value;
	BOOL			Done=FALSE;

	RegChangeEvt = CreateEvent (NULL, FALSE, TRUE, NULL);
	ASSERT (RegChangeEvt!=NULL);

	WaitObjects[1] = DebugWatchEvent;

		// Create registry key that controls dialog display
	status = RegCreateKeyEx (HKEY_LOCAL_MACHINE,
								InRouter()
									? TEXT ("System\\CurrentControlSet\\Services\\RemoteAccess\\Adptif")
									: TEXT ("System\\CurrentControlSet\\Services\\NwSapAgent\\Adptif"),
								0,
								NULL,
								REG_OPTION_NON_VOLATILE,
								KEY_READ,
								NULL,
								&regHdl,
								&disposition
								);
	ASSERTMSG ("Can't create registry key. ", status==NO_ERROR);

	while (!Done) {
		status = MsgWaitForMultipleObjects (2, WaitObjects,
											FALSE, INFINITE, QS_ALLINPUT);
		if (status==(WAIT_OBJECT_0+2)) {
			MSG		msg;
			while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
				if (msg.message==WM_QUIT) {
					Done = TRUE;
					break;
					}
				else if (!IsWindow(WatcherDlg)
					|| !IsDialogMessage(WatcherDlg, &msg)) {
					TranslateMessage (&msg);
					DispatchMessage (&msg);
					}
				}
			}
		else if (status==WAIT_OBJECT_0) {
				// Registry change event signalled
			EnterCriticalSection (&ConfigInfoLock);
			length = sizeof (DWORD);
			status = RegQueryValueEx (regHdl, TEXT ("DbgFlags"), NULL, NULL,
											 (PUCHAR)&value, &length);
			if (status==NO_ERROR)
				DbgFlags = value;

			if (DbgFlags & DEBUG_SHOW_DIALOG) {
				if (!IsWindow(WatcherDlg)) {
					WatcherDlg = CreateDialog (HDLLInstance,
									MAKEINTRESOURCE (IDD_WATCHER),
									NULL,
									&WatcherDlgProc);

					ASSERT (WatcherDlg!=NULL);
					}
				}
			else {
				if (IsWindow (WatcherDlg)) {
					DestroyWindow (WatcherDlg);
					WatcherDlg = NULL;
					}
				}
					

			status = RegNotifyChangeKeyValue (regHdl,
									 FALSE,
									 REG_NOTIFY_CHANGE_LAST_SET,
									 RegChangeEvt,
									 TRUE);
			ASSERTMSG ("Can't start registry notifications. ",
													 status==NO_ERROR);
			LeaveCriticalSection (&ConfigInfoLock);
			}
		else if (status==WAIT_OBJECT_0+1) {
				// Adapter change IRP has completed
			EnterCriticalSection (&ConfigInfoLock);
			if (WatcherDlg!=NULL)	// Update dialog
				UpdateAdapterList (GetDlgItem (WatcherDlg, IDL_ADAPTERS));
			if (!InRouter ()) {		// Inform clients and repost IRP
				ProcessAdapterConfigInfo ();
				PostAdapterConfigRequest (DebugWatchEvent);
				}			// When in router, IRP is processed
							// by APC routine and new one is immediately
							// posted
			LeaveCriticalSection (&ConfigInfoLock);
			}			
		}


	if (IsWindow (WatcherDlg)) {
		DestroyWindow (WatcherDlg);
		WatcherDlg = NULL;
		}
	RegCloseKey (regHdl);
	CloseHandle (RegChangeEvt);
	return 0;
#undef RegChangeEvent
	}


/*++
*******************************************************************
        W a t c h e r D i a l o g P r o c

Routine Description:

		Window Proc for watcher dialog.
		Implements UI for adapter info changes

Arguments:

    	hDlg		- handle of dialog box
    	uMsg		- message
    	wParam		- first message parameter
    	lParam 		- second message parameter

Return Value:
		TRUE		if procedure processed tne message
		FALSE		if default procedure is to process the message

*******************************************************************
--*/
BOOL CALLBACK
WatcherDlgProc (
    HWND  	hDlg,
    UINT  	uMsg,
    WPARAM  wParam,
    LPARAM  lParam
   ) {
	UINT		i,aa;
	CHAR		buffer[60];		// Buffer to print adapter info to
	HWND		hLB;			// Adapter listbox window handle
	BOOL		res=FALSE;		// Return value
	DWORD		status;
	LV_COLUMN	lvc;
	HICON		hIcon;
    HIMAGELIST	hIml;
	RECT		rect;
	static RECT	lbPos;
	static LPTSTR Headers[]={TEXT(" Nic Name"), TEXT("NicId"), TEXT("ItfId"),
			TEXT("Network#"), TEXT("Local Node #"), TEXT("Remote Node#"),
			TEXT("Ln.Spd"), TEXT("MaxSz"), TEXT("Type"), TEXT("Medium"),
			TEXT("State"), NULL};

	switch (uMsg) {
		case WM_INITDIALOG:		// Dialog is being created
			hLB = GetDlgItem (hDlg, IDL_ADAPTERS);
			GetWindowRect (hLB, &lbPos);
			MapWindowPoints (HWND_DESKTOP, hDlg,
								(POINT *)&lbPos, 2);
			GetClientRect (hDlg, &rect);
			lbPos.bottom = rect.bottom - lbPos.bottom;
			lbPos.right = rect.right - lbPos.right;
			hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
							GetSystemMetrics(SM_CYSMICON), TRUE, 2, 2);
			hIcon = LoadIcon(HDLLInstance,
								MAKEINTRESOURCE(ID_ICON_DOWN));
			ImageList_AddIcon(hIml, hIcon);
			DeleteObject(hIcon);
			hIcon = LoadIcon(HDLLInstance,
								MAKEINTRESOURCE(ID_ICON_UP));
			ImageList_AddIcon(hIml, hIcon);
			DeleteObject(hIcon);
			ListView_SetImageList(hLB, hIml, LVSIL_STATE);


 		   // Initialize the LV_COLUMN structure.
		    lvc.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
		    lvc.fmt = LVCFMT_LEFT;
			aa = ListView_GetStringWidth (hLB, TEXT("MM"));
			for (i=0; Headers[i]!=NULL; i++) {
				lvc.pszText = Headers[i];
				lvc.iSubItem = i;
				lvc.cx = ListView_GetStringWidth (hLB, lvc.pszText)+aa;
				status = ListView_InsertColumn(hLB, i, &lvc);
				ASSERTMSG ("Could not insert list column ", status!=-1);
				}
				
			EnterCriticalSection (&ConfigInfoLock);
			UpdateAdapterList (GetDlgItem (hDlg, IDL_ADAPTERS));
								// Disable all buttons (nothing selected)
			LeaveCriticalSection (&ConfigInfoLock);
			break;

		case WM_COMMAND:		// Process child window messages only
			switch (LOWORD(wParam)) {
				case IDCANCEL:	// Do not allow to close the Dialog Box
					MessageBeep (MB_ICONHAND);
					res = TRUE;
					break;
				}
			break;
		case WM_NOTIFY:
#define pnmv ((NM_LISTVIEW *)lParam)
			if ((pnmv->hdr.code==LVN_ITEMCHANGED)
					&& (pnmv->uChanged&LVIF_STATE)
					&& (pnmv->uNewState&LVIS_SELECTED)
					&& (pnmv->iItem>0)) {
				}
			else if (pnmv->hdr.code==NM_DBLCLK) {
				LV_HITTESTINFO	hit;
				status = GetMessagePos ();
				hit.pt.x = LOWORD(status);
				hit.pt.y = HIWORD(status);
				ScreenToClient (pnmv->hdr.hwndFrom, &hit.pt);
				ListView_HitTest(pnmv->hdr.hwndFrom, &hit);
				if ((hit.iItem>0)
						&& (hit.flags&LVHT_ONITEMSTATEICON)) {
					i = hit.iItem - 1;
					EnterCriticalSection (&ConfigInfoLock);
					if (!AdapterArray[i].Enabled) {
						AdapterArray[i].Enabled = TRUE;
						if ((AdapterArray[i].Status==NIC_CONFIGURED)
								||(AdapterArray[i].Status==NIC_LINE_UP))
							NotifyClients (i, NIC_LINE_UP);
						}
					else {
						AdapterArray[i].Enabled = FALSE;
						if ((AdapterArray[i].Status==NIC_CONFIGURED)
								||(AdapterArray[i].Status==NIC_LINE_UP))
							NotifyClients (i, NIC_LINE_DOWN);
						}
					ListView_SetItemState (pnmv->hdr.hwndFrom, hit.iItem,
								INDEXTOSTATEIMAGEMASK (
									AdapterArray[i].Enabled
										? (((AdapterArray[i].Status==NIC_CONFIGURED)
											|| (AdapterArray[i].Status==NIC_LINE_UP))
											? 2 : 1)
										: 0),
								LVIS_STATEIMAGEMASK);
					LeaveCriticalSection (&ConfigInfoLock);
					ListView_Update(pnmv->hdr.hwndFrom, hit.iItem);
					}
				}
			break;
#undef pnmw
		case WM_SIZE:
			hLB = GetDlgItem (hDlg, IDL_ADAPTERS);
			MoveWindow (hLB, lbPos.left, lbPos.top,
						LOWORD (lParam)-lbPos.right-lbPos.left,
						HIWORD (lParam)-lbPos.bottom-lbPos.top,
						TRUE);
			break;
		case WM_DESTROY:
			EnterCriticalSection (&ConfigInfoLock);
			if (AdapterDataBuffer!=NULL) {
				RtlFreeHeap (RtlProcessHeap (), 0, AdapterDataBuffer);
				AdapterDataBuffer = NULL;
				}
			if (AdapterArray!=NULL) {
				RtlFreeHeap (RtlProcessHeap (), 0, AdapterArray);
				AdapterArray = NULL;
				}
			LeaveCriticalSection (&ConfigInfoLock);
		break;
		}

	return res;
	}


/*++
*******************************************************************
		N o t i f y C l i e n t s

Routine Description:

		Notifies clients of adapter status changes made through UI
Arguments:
		i - index of the adapter that was modified
		Status - new state of the adapter

Return Value:
		None
*******************************************************************
--*/
VOID
NotifyClients (
	INT			i,
	BOOLEAN		Status
	) {
	PLIST_ENTRY		cur;
	PIPX_NIC_INFO 	NicPtr = 
			&((PIPX_NIC_INFO)
				((PIPX_NICS)
					((PNWLINK_ACTION)AdapterDataBuffer)
					->Data)
				->Data)[i];
	PADAPTER_MSG msg = (PADAPTER_MSG)RtlAllocateHeap (
							RtlProcessHeap (), 0,
								sizeof (ADAPTER_MSG));
	ASSERTMSG ("Could not allocate adapter message ",
												msg!=NULL);
	RtlCopyMemory (&msg->info, NicPtr, sizeof (IPX_NIC_INFO));
	msg->info.Status = Status;
	msg->refcnt = 0;
	cur = PortListHead.Flink;
	while (cur!=&PortListHead) {
		PCONFIG_PORT	config = CONTAINING_RECORD (cur,
					CONFIG_PORT, link);
		msg->refcnt += 1;;
		if (config->curmsg==NULL) {
			BOOL	res = SetEvent (config->event);
			ASSERTMSG ("Can't set client event ", res);
			config->curmsg = &msg->info;
			}
		cur = cur->Flink;
		}
	InsertTailList (&MessageListHead, &msg->link);
	}

/*++
*******************************************************************
		U p d a t e A d a p t e r L i s t

Routine Description:
		Updates adapter info displayed in the list
Arguments:
		AdapterLB - list view control window handle
Return Value:
		None
*******************************************************************
--*/
VOID
UpdateAdapterList (
	HWND	AdapterLB
	) {
	PNWLINK_ACTION		action;
	PIPX_NICS			request;
	IO_STATUS_BLOCK		IoStatus;
	PIPX_NIC_INFO		NicPtr;
	PISN_ACTION_GET_DETAILS	details;
	CHAR				IoctlBuffer[
								sizeof (NWLINK_ACTION)
								+sizeof (ISN_ACTION_GET_DETAILS)];
	ULONG				i, j;
	PADAPTER_STATE			newArray;
	WCHAR				namebuf[64];
	char				buf[128];
	ULONG				length;
	DWORD				status;
	LV_ITEM				lvi;

	status = ListView_DeleteAllItems(AdapterLB);
	ASSERTMSG ("Could not all list items ", status);

	action = (PNWLINK_ACTION)IoctlBuffer;
	action->Header.TransportId = ISN_ACTION_TRANSPORT_ID;
	action->OptionType = NWLINK_OPTION_CONTROL;
	action->BufferLength = sizeof (action->Option)
							+sizeof (ISN_ACTION_GET_DETAILS);
	action->Option = MIPX_CONFIG;
	details = (PISN_ACTION_GET_DETAILS)action->Data;
	details->NicId = 0;

	status = NtDeviceIoControlFile(
						IpxDriverHandle,
						NULL,
						NULL,
						NULL,
						&IoStatus,
						IOCTL_TDI_ACTION,
						NULL,
						0,
						action,
						sizeof(NWLINK_ACTION)
						+sizeof (ISN_ACTION_GET_DETAILS));
	if (status==STATUS_PENDING){
		status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL);
		if (NT_SUCCESS (status))
			status = IoStatus.Status;
		}

	ASSERTMSG ("Ioclt MIPX_CONFIG failed ", NT_SUCCESS (status));

	ListView_SetItemCount(AdapterLB, details->NicId);
	lvi.mask = LVIF_TEXT;
	lvi.pszText = buf;

	lvi.iItem = 0;

	lvi.iSubItem = 0;
	sprintf (buf, "%ls", L"Internal");
	status = ListView_InsertItem (AdapterLB, &lvi);
	ASSERTMSG ("Could not insert list item ", status!=-1);

	lvi.iSubItem = 1;
	sprintf (buf, "%d", 0);
	status = ListView_SetItem (AdapterLB, &lvi);
	ASSERTMSG ("Could not set list subitem ", status);

	lvi.iSubItem = 2;
	sprintf (buf, "%d", 0);
	status = ListView_SetItem (AdapterLB, &lvi);
	ASSERTMSG ("Could not set list subitem ", status);

	lvi.iSubItem = 3;
	sprintf (buf,"%08x", GETLONG2ULONGdirect(&details->NetworkNumber));
	status = ListView_SetItem (AdapterLB, &lvi);
	ASSERTMSG ("Could not set list subitem ", status);

	lvi.iSubItem = 4;
	sprintf (buf, "%02x%02x%02x%02x%02x%02x",
			INTERNAL_NODE_ADDRESS[0], INTERNAL_NODE_ADDRESS[1],
				INTERNAL_NODE_ADDRESS[2], INTERNAL_NODE_ADDRESS[3],
				INTERNAL_NODE_ADDRESS[4], INTERNAL_NODE_ADDRESS[5]);
	status = ListView_SetItem (AdapterLB, &lvi);
	ASSERTMSG ("Could not set list subitem ", status);

	newArray = (PADAPTER_STATE)RtlAllocateHeap (RtlProcessHeap (), 0,
								sizeof (ADAPTER_STATE)*details->NicId);
	ASSERTMSG ("Could not allocate Adapter state array ", newArray!=NULL);

	if (AdapterDataBuffer!=NULL)
		RtlFreeHeap (RtlProcessHeap (), 0, AdapterDataBuffer);

	AdapterDataBuffer = RtlAllocateHeap (RtlProcessHeap (), 0,
							FIELD_OFFSET (NWLINK_ACTION, Data)
								+FIELD_OFFSET (IPX_NICS, Data)
								+sizeof (IPX_NIC_INFO)*details->NicId);
	ASSERTMSG ("Could not allocate request buffer ", action!=NULL);

	action = (PNWLINK_ACTION)AdapterDataBuffer;
	action->Header.TransportId = ISN_ACTION_TRANSPORT_ID;
	action->OptionType = NWLINK_OPTION_CONTROL;
	action->BufferLength = sizeof (action->Option)
						+FIELD_OFFSET(IPX_NICS,Data)
						+sizeof (IPX_NIC_INFO)*details->NicId;
	action->Option = MIPX_GETNEWNICINFO;
	request = (PIPX_NICS)action->Data;
	request->NoOfNics = 0;
	request->TotalNoOfNics = 0;
	request->fAllNicsDesired = TRUE;

	status = NtDeviceIoControlFile(
					IpxDriverHandle,
					NULL,
					NULL,
					NULL,
					&IoStatus,
					IOCTL_TDI_ACTION,
					NULL,
					0,
					action,
					FIELD_OFFSET(NWLINK_ACTION, Data)
						+FIELD_OFFSET(IPX_NICS,Data)
						+sizeof (IPX_NIC_INFO)*details->NicId);
	if (status==STATUS_PENDING) {
		status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL);
		if (NT_SUCCESS (status))
			status = IoStatus.Status;
		}

	ASSERTMSG ("Ioctl MIPX_GETNEWNICINFO failed ", NT_SUCCESS (status));
	NicPtr = (PIPX_NIC_INFO)request->Data;
	NumAdapters = request->TotalNoOfNics;
	for (i=0; i<NumAdapters; i++, NicPtr++) {
		UINT j;
		for (j=0; (j<AdapterArraySize)
				&& (AdapterArray[j].AdapterId
					!=NicPtr->NicId); j++);
		newArray[i].AdapterId = NicPtr->NicId;
		newArray[i].Status = NicPtr->Status;
		if (j<AdapterArraySize)
			newArray[i].Enabled = AdapterArray[j].Enabled;
		else
			newArray[i].Enabled = TRUE;
		
		length = sizeof (namebuf);
		GetAdapterNameW (newArray[i].AdapterId, &length, namebuf);
		
		lvi.iItem = i+1;

		lvi.mask |= LVIF_STATE;
		lvi.iSubItem = 0;
		lvi.stateMask = LVIS_STATEIMAGEMASK;
		lvi.state = INDEXTOSTATEIMAGEMASK (
					newArray[i].Enabled
						? (((NicPtr->Status==NIC_CONFIGURED)
							|| (NicPtr->Status==NIC_LINE_UP))
							? 2 : 1)
						: 0);
		sprintf (buf, "%ls", namebuf);
		status = ListView_InsertItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not insert list item ", status!=-1);
		lvi.mask &= (~LVIF_STATE);

		sprintf (buf, "%d", newArray[i].AdapterId);
		lvi.iSubItem = 1;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%d",
				(NicPtr->NdisMediumType==NdisMediumWan)
						&& (newArray[i].Status==NIC_LINE_UP)
					? NicPtr->InterfaceIndex
					: -1);
		lvi.iSubItem = 2;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%08x", GETLONG2ULONGdirect(&NicPtr->NetworkAddress));
		lvi.iSubItem = 3;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%02x%02x%02x%02x%02x%02x",
				NicPtr->LocalNodeAddress[0], NicPtr->LocalNodeAddress[1],
					NicPtr->LocalNodeAddress[2], NicPtr->LocalNodeAddress[3],
					NicPtr->LocalNodeAddress[4], NicPtr->LocalNodeAddress[5]);
		lvi.iSubItem = 4;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%02x%02x%02x%02x%02x%02x",
				NicPtr->RemoteNodeAddress[0], NicPtr->RemoteNodeAddress[1],
					NicPtr->RemoteNodeAddress[2], NicPtr->RemoteNodeAddress[3],
					NicPtr->RemoteNodeAddress[4], NicPtr->RemoteNodeAddress[5]);
		lvi.iSubItem = 5;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%d", NicPtr->LinkSpeed);
		lvi.iSubItem = 6;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%d", NicPtr->MaxPacketSize);
		lvi.iSubItem = 7;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		sprintf (buf, "%d", NicPtr->PacketType);
		lvi.iSubItem = 8;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		switch (NicPtr->NdisMediumType) {
			case NdisMedium802_3:
				sprintf (buf, "%s", "802.3");
				break;
    		case NdisMedium802_5:
				sprintf (buf, "%s", "802.5");
				break;
 			case NdisMediumFddi:
				sprintf (buf, "%s", "FDDI");
				break;
 			case NdisMediumWan:
				sprintf (buf, "%s", "Wan");
				break;
 			case NdisMediumLocalTalk:
				sprintf (buf, "%s", "LTalk");
				break;
 			case NdisMediumDix:
				sprintf (buf, "%s", "Dix");
				break;
 			case NdisMediumArcnetRaw:
				sprintf (buf, "%s", "ArcnetRaw");
				break;
 			case NdisMediumArcnet878_2:
				sprintf (buf, "%s", "Arcnet878.2");
				break;
			}
		lvi.iSubItem = 9;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);

		switch (NicPtr->Status) {
			case NIC_CREATED:
				sprintf (buf, "%s", "Created");
				break;
			case NIC_DELETED:
				sprintf (buf, "%s", "Deleted");
				break;
			case NIC_CONFIGURED:
				sprintf (buf, "%s", "Configured");
				break;
			case NIC_LINE_UP:
				sprintf (buf, "%s", "Up");
				break;
			case NIC_LINE_DOWN:
				sprintf (buf, "%s", "Down");
				break;
			default:
				ASSERTMSG ("Unknown nic status ", FALSE);
				break;
			}
		lvi.iSubItem = 10;
		status = ListView_SetItem (AdapterLB, &lvi);
		ASSERTMSG ("Could not set list subitem ", status);
		}

	if (AdapterArray!=NULL)
		RtlFreeHeap (RtlProcessHeap (), 0, AdapterArray);
	AdapterArray = newArray;
	AdapterArraySize = NumAdapters;
	}

/*++
*******************************************************************
		I p x R e c v C o m p l e t i o n W a t c h

Routine Description:
	Substitute completion routine that filters out packets
	received on the adapters disabled by the UI
Arguments:
		Context - Pointer to client completion routine
		IoStatus - status of completed io operation (clients overlapped
					structure is used as the buffer)
		Reserved - ???
Return Value:
		None
--*/

VOID
IpxRecvCompletionWatch (
	IN	PVOID				Context,
	IN	PIO_STATUS_BLOCK	IoStatus,
	IN	ULONG				Reserved
	) {
	LPOVERLAPPED	ovrp = (LPOVERLAPPED)IoStatus;
	if (NT_SUCCESS(IoStatus->Status)) {
			// Check if adapter is disabled through the UI and repost recv if so
		USHORT	NicId = GetNicId (ovrp->OffsetHigh);
		UINT		i;
		EnterCriticalSection (&ConfigInfoLock);
		if (AdapterArray!=NULL) {
			for (i=0; (i<AdapterArraySize) && (AdapterArray[i].AdapterId!=NicId); i++);
			ASSERTMSG ("Invalid Nic id ", i<AdapterArraySize);
			if (!AdapterArray[i].Enabled) {
				LeaveCriticalSection (&ConfigInfoLock);
				IoStatus->Status = NtDeviceIoControlFile(
									(HANDLE)ovrp->hEvent,
									NULL,
									IpxRecvCompletionWatch,
									Context,				// APC Context
									IoStatus,
									MIPX_RCV_DATAGRAM,
									NULL,
									0,
									(PVOID)ovrp->OffsetHigh,
									FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)
										+ ovrp->Offset
									);
				if (NT_SUCCESS (IoStatus->Status))
					return;
				}
			else
				LeaveCriticalSection (&ConfigInfoLock);
			}
		else
			LeaveCriticalSection (&ConfigInfoLock);
		}		
	ovrp->hEvent = NULL;
	IpxRecvCompletion (Context, IoStatus, Reserved);
	}

/*++
*******************************************************************
		I s A d a p t e r D i s a b l e d

Routine Description:
	Chacks if adapter with given id is disabled by the UI (it wont be
		reported to the clients)
Arguments:
	NicId - id of the adapter to check
Return Value:
	TRUE - adapter is disabled
	FALSE - adapter is not disabled
--*/
BOOL
IsAdapterDisabled (
	USHORT	NicId
	) {
	if (AdapterArray!=NULL) {
		UINT	j;
		for (j=0; j<AdapterArraySize; j++) {
			if (AdapterArray[j].AdapterId==NicId)
				break;
			}						
		ASSERTMSG ("Nic not in the array ", j<AdapterArraySize);
		if (!AdapterArray[j].Enabled)
			return TRUE;
		}

	return FALSE;
	}

/*++
*******************************************************************
	I n f o r m W a t c h e r

Routine Description:
	Signals event to inform let watcher dialog update its adapter
	configuration info
Arguments:
	None
Return Value:
	None
--*/
VOID
InformWatcher (
	void
	) {
	if (WatcherDlg!=NULL) {
		BOOL	res = SetEvent (DebugWatchEvent);
		ASSERTMSG ("Could not set debug watch event ", res);
		}
	}