/*++ 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, ®Hdl, &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; iNicId); j++); newArray[i].AdapterId = NicPtr->NicId; newArray[i].Status = NicPtr->Status; if (jStatus==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; (iStatus = 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