/*++ Copyright (c) 1992 Microsoft Corporation Module Name: ndisapi.c Abstract: NDIS User-Mode apis to support PnP from the network UI Author: JameelH Environment: Kernel mode, FSD Revision History: Aug 1997 JameelH Initial version --*/ #include #include #include #include #include #ifndef UNICODE_STRING typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; #endif #include #include #define MAC_ADDRESS_SIZE 6 #define VENDOR_ID_SIZE 3 extern VOID InitUnicodeString( IN PUNICODE_STRING DestinationString, IN PCWSTR SourceString ); extern LONG AppendUnicodeStringToString( IN PUNICODE_STRING Destination, IN PUNICODE_STRING Source ); extern HANDLE OpenDevice( IN PUNICODE_STRING DeviceName ); // // UNICODE_STRING_SIZE calculates the size of the buffer needed to // store a given UNICODE_STRING including an appended null-terminator. // // ULONG // UNICODE_STRING_SIZE( // PUNICODE_STRING String // ); // #define UNICODE_STRING_SIZE(x) \ ((((x) == NULL) ? 0 : (x)->Length) + sizeof(WCHAR)) VOID NdispUnicodeStringToVar( IN PVOID Base, IN PUNICODE_STRING String, IN OUT PNDIS_VAR_DATA_DESC NdisVar ) /*++ Routine Description: This function copies the contents of a UNICODE_STRING to an NDIS_VAR_DATA structure. NdisVar->Offset is treated as an input parameter and represents the offset into Base that the string characters should be copied to. Arguments: Base - Specifies the base address of the IOCTL buffer. String - Supplies a pointer to the UNICODE_STRING that should be copied. NdisVar - Supplies a pointer to the target NDIS_VAR_DATA_DESC. Its Offset field is taken as input, and its Length and MaximumLength fields are treated as output. Return Value: None. --*/ { PWCHAR destination; // // NdisVar->Offset is assumed to be filled in and is treated // as an input parameter. // destination = (PWCHAR)(((PCHAR)Base) + NdisVar->Offset); // // Copy over the UNICODE_STRING, if any, and set NdisVar->Length // if ((String != NULL) && (String->Length > 0)) { NdisVar->Length = String->Length; memcpy(destination, String->Buffer, NdisVar->Length ); } else { NdisVar->Length = 0; } // // Null-terminate, fill in MaxiumLength and we're done. // *(destination + NdisVar->Length / sizeof(WCHAR)) = L'\0'; NdisVar->MaximumLength = NdisVar->Length + sizeof(WCHAR); } UINT NdisHandlePnPEvent( IN UINT Layer, IN UINT Operation, IN PUNICODE_STRING LowerComponent OPTIONAL, IN PUNICODE_STRING UpperComponent OPTIONAL, IN PUNICODE_STRING BindList OPTIONAL, IN PVOID ReConfigBuffer OPTIONAL, IN UINT ReConfigBufferSize OPTIONAL ) { PNDIS_PNP_OPERATION Op; NDIS_PNP_OPERATION tempOp; HANDLE hDevice; BOOL fResult = FALSE; UINT cb, Size; DWORD Error; ULONG padding; do { // // Validate Layer & Operation // if (((Layer != NDIS) && (Layer != TDI)) || ((Operation != BIND) && (Operation != UNBIND) && (Operation != RECONFIGURE) && (Operation != UNLOAD) && (Operation != REMOVE_DEVICE) && (Operation != ADD_IGNORE_BINDING) && (Operation != DEL_IGNORE_BINDING) && (Operation != BIND_LIST))) { Error = ERROR_INVALID_PARAMETER; break; } // // Allocate and initialize memory for the block to be passed down. The buffer // will look like this: // // // +=================================+ // | NDIS_PNP_OPERATION | // | ReConfigBufferOff | ----+ // +--- | LowerComponent.Offset | | // | | UpperComponent.Offset | --+ | // +-|--- | BindList.Offset | | | // | +--> +---------------------------------+ | | // | | LowerComponentStringBuffer | | | // | +---------------------------------+ <-+ | // | | UpperComponentStringBuffer | | // +----> +---------------------------------+ | // | BindListStringBuffer | | // +---------------------------------+ | // | Padding to ensure ULONG_PTR | | // | alignment of ReConfigBuffer | | // +---------------------------------+ <---+ // | ReConfigBuffer | // +=================================+ // // tempOp is a temporary structure into which we will store offsets as // they are calculated. This temporary structure will be moved to // the head of the real buffer once its size is known and it is // allocated. // Size = sizeof(NDIS_PNP_OPERATION); tempOp.LowerComponent.Offset = Size; Size += UNICODE_STRING_SIZE(LowerComponent); tempOp.UpperComponent.Offset = Size; Size += UNICODE_STRING_SIZE(UpperComponent); tempOp.BindList.Offset = Size; Size += UNICODE_STRING_SIZE(BindList); padding = (sizeof(ULONG_PTR) - (Size & (sizeof(ULONG_PTR) - 1))) & (sizeof(ULONG_PTR) - 1); Size += padding; tempOp.ReConfigBufferOff = Size; Size += ReConfigBufferSize + 1; Op = (PNDIS_PNP_OPERATION)LocalAlloc(LPTR, Size); if (Op == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; break; } // // We have a buffer of the necessary size. Copy in the partially- // filled in tempOp, then fill in the remaining fields and copy the // data into the buffer. // *Op = tempOp; Op->Layer = Layer; Op->Operation = Operation; // // Copy over the three unicode strings // NdispUnicodeStringToVar( Op, LowerComponent, &Op->LowerComponent ); NdispUnicodeStringToVar( Op, UpperComponent, &Op->UpperComponent ); NdispUnicodeStringToVar( Op, BindList, &Op->BindList ); // // Finally, copy over the ReConfigBuffer // Op->ReConfigBufferSize = ReConfigBufferSize; if (ReConfigBufferSize > 0) { memcpy((PUCHAR)Op + Op->ReConfigBufferOff, ReConfigBuffer, ReConfigBufferSize); } *((PUCHAR)Op + Op->ReConfigBufferOff + ReConfigBufferSize) = 0; hDevice = CreateFile(L"\\\\.\\NDIS", GENERIC_READ | GENERIC_WRITE, 0, // sharing mode - not significant NULL, // security attributes OPEN_EXISTING, 0, // file attributes and flags NULL); // handle to template file if (hDevice != INVALID_HANDLE_VALUE) { fResult = DeviceIoControl(hDevice, IOCTL_NDIS_DO_PNP_OPERATION, Op, // input buffer Size, // input buffer size NULL, // output buffer 0, // output buffer size &cb, // bytes returned NULL); // OVERLAPPED structure Error = GetLastError(); CloseHandle(hDevice); } else { Error = GetLastError(); } LocalFree(Op); } while (FALSE); SetLastError(Error); return(fResult); } NDIS_OID StatsOidList[] = { OID_GEN_LINK_SPEED, OID_GEN_MEDIA_IN_USE | NDIS_OID_PRIVATE, OID_GEN_MEDIA_CONNECT_STATUS | NDIS_OID_PRIVATE, OID_GEN_XMIT_OK, OID_GEN_RCV_OK, OID_GEN_XMIT_ERROR, OID_GEN_RCV_ERROR, OID_GEN_DIRECTED_FRAMES_RCV | NDIS_OID_PRIVATE, OID_GEN_DIRECTED_BYTES_XMIT | NDIS_OID_PRIVATE, OID_GEN_DIRECTED_BYTES_RCV | NDIS_OID_PRIVATE, OID_GEN_ELAPSED_TIME | NDIS_OID_PRIVATE, OID_GEN_INIT_TIME_MS | NDIS_OID_PRIVATE, OID_GEN_RESET_COUNTS | NDIS_OID_PRIVATE, OID_GEN_MEDIA_SENSE_COUNTS | NDIS_OID_PRIVATE, OID_GEN_PHYSICAL_MEDIUM | NDIS_OID_PRIVATE }; UINT NumOidsInList = sizeof(StatsOidList)/sizeof(NDIS_OID); UINT NdisQueryStatistics( IN PUNICODE_STRING Device, OUT PNIC_STATISTICS Statistics ) { NDIS_STATISTICS_VALUE StatsBuf[4*sizeof(StatsOidList)/sizeof(NDIS_OID)]; PNDIS_STATISTICS_VALUE pStatsBuf; HANDLE hDevice; BOOL fResult = FALSE; UINT cb, Size, Index; DWORD Error; if (Statistics->Size != sizeof(NIC_STATISTICS)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return(FALSE); } memset(Statistics, 0, sizeof(NIC_STATISTICS)); Statistics->DeviceState = DEVICE_STATE_DISCONNECTED; Statistics->MediaState = MEDIA_STATE_UNKNOWN; hDevice = OpenDevice(Device); if (hDevice != NULL) { Statistics->MediaState = MEDIA_STATE_CONNECTED; // default, if the device does not support Statistics->DeviceState = DEVICE_STATE_CONNECTED; fResult = DeviceIoControl(hDevice, IOCTL_NDIS_QUERY_SELECTED_STATS, StatsOidList, // input buffer sizeof(StatsOidList), // input buffer size StatsBuf, // output buffer sizeof(StatsBuf), // output buffer size &cb, // bytes returned NULL); // OVERLAPPED structure Error = GetLastError(); CloseHandle(hDevice); if (fResult) { Error = NO_ERROR; for (Index = Size = 0, pStatsBuf = StatsBuf; Size < cb; Index++) { LARGE_INTEGER Value; NDIS_OID Oid; Value.QuadPart = 0; if (pStatsBuf->DataLength == sizeof(LARGE_INTEGER)) { // Use memcpy rather than assignment to avoid unalignment // faults on ia64. // memcpy(&Value.QuadPart, &pStatsBuf->Data[0], pStatsBuf->DataLength); } else { Value.LowPart = *(PULONG)(&pStatsBuf->Data[0]); } Size += (pStatsBuf->DataLength + FIELD_OFFSET(NDIS_STATISTICS_VALUE, Data)); Oid = pStatsBuf->Oid; pStatsBuf = (PNDIS_STATISTICS_VALUE)((PUCHAR)pStatsBuf + FIELD_OFFSET(NDIS_STATISTICS_VALUE, Data) + pStatsBuf->DataLength); switch (Oid & ~NDIS_OID_PRIVATE) { case OID_GEN_LINK_SPEED: Statistics->LinkSpeed = Value.LowPart; break; case OID_GEN_MEDIA_CONNECT_STATUS: Statistics->MediaState = (Value.LowPart == NdisMediaStateConnected) ? MEDIA_STATE_CONNECTED : MEDIA_STATE_DISCONNECTED; break; case OID_GEN_MEDIA_IN_USE: Statistics->MediaType = Value.LowPart; break; case OID_GEN_XMIT_OK: Statistics->PacketsSent = Value.QuadPart; break; case OID_GEN_RCV_OK: Statistics->PacketsReceived = Value.QuadPart; break; case OID_GEN_XMIT_ERROR: Statistics->PacketsSendErrors = Value.LowPart; break; case OID_GEN_RCV_ERROR: Statistics->PacketsReceiveErrors = Value.LowPart; break; case OID_GEN_DIRECTED_BYTES_XMIT: Statistics->BytesSent += Value.QuadPart; break; case OID_GEN_MULTICAST_BYTES_XMIT: Statistics->BytesSent += Value.QuadPart; break; case OID_GEN_BROADCAST_BYTES_XMIT: Statistics->BytesSent += Value.QuadPart; break; case OID_GEN_DIRECTED_BYTES_RCV: Statistics->BytesReceived += Value.QuadPart; Statistics->DirectedBytesReceived = Value.QuadPart; break; case OID_GEN_DIRECTED_FRAMES_RCV: Statistics->DirectedPacketsReceived = Value.QuadPart; break; case OID_GEN_MULTICAST_BYTES_RCV: Statistics->BytesReceived += Value.QuadPart; break; case OID_GEN_BROADCAST_BYTES_RCV: Statistics->BytesReceived += Value.QuadPart; break; case OID_GEN_ELAPSED_TIME: Statistics->ConnectTime = Value.LowPart; break; case OID_GEN_INIT_TIME_MS: Statistics->InitTime = Value.LowPart; break; case OID_GEN_RESET_COUNTS: Statistics->ResetCount = Value.LowPart; break; case OID_GEN_MEDIA_SENSE_COUNTS: Statistics->MediaSenseConnectCount = Value.LowPart >> 16; Statistics->MediaSenseDisconnectCount = Value.LowPart & 0xFFFF; break; case OID_GEN_PHYSICAL_MEDIUM: Statistics->PhysicalMediaType = Value.LowPart; break; default: // ASSERT(0); break; } } } else { Error = GetLastError(); } } else { Error = GetLastError(); } SetLastError(Error); return(fResult); } UINT NdisEnumerateInterfaces( IN PNDIS_ENUM_INTF Interfaces, IN UINT Size ) { HANDLE hDevice; BOOL fResult = FALSE; UINT cb; DWORD Error = NO_ERROR; do { hDevice = CreateFile(L"\\\\.\\NDIS", GENERIC_READ | GENERIC_WRITE, 0, // sharing mode - not significant NULL, // security attributes OPEN_EXISTING, 0, // file attributes and flags NULL); // handle to template file if (hDevice != INVALID_HANDLE_VALUE) { fResult = DeviceIoControl(hDevice, IOCTL_NDIS_ENUMERATE_INTERFACES, NULL, // input buffer 0, // input buffer size Interfaces, // output buffer Size, // output buffer size &cb, // bytes returned NULL); // OVERLAPPED structure Error = GetLastError(); CloseHandle(hDevice); if (Error == NO_ERROR) { UINT i; // // Fix-up pointers // for (i = 0; i < Interfaces->TotalInterfaces; i++) { OFFSET_TO_POINTER(Interfaces->Interface[i].DeviceName.Buffer, Interfaces); OFFSET_TO_POINTER(Interfaces->Interface[i].DeviceDescription.Buffer, Interfaces); } } } else { Error = GetLastError(); } } while (FALSE); SetLastError(Error); return(fResult); } #if 0 UINT NdisQueryDeviceBundle( IN PUNICODE_STRING Device, OUT PDEVICE_BUNDLE BundleBuffer, IN UINT BufferSize ) { HANDLE hDevice; BOOL fResult = FALSE; UINT cb; DWORD Error = NO_ERROR; do { if (BufferSize < (sizeof(DEVICE_BUNDLE) + Device->MaximumLength)) { Error = ERROR_INSUFFICIENT_BUFFER; break; } hDevice = OpenDevice(Device); if (hDevice != NULL) { fResult = DeviceIoControl(hDevice, IOCTL_NDIS_GET_DEVICE_BUNDLE, NULL, // input buffer 0, // input buffer size BundleBuffer, // output buffer BufferSize, // output buffer size &cb, // bytes returned NULL); // OVERLAPPED structure Error = GetLastError(); CloseHandle(hDevice); if (Error == NO_ERROR) { UINT i; // // Fix-up pointers // for (i = 0; i < BundleBuffer->TotalEntries; i++) { OFFSET_TO_POINTER(BundleBuffer->Entries[i].Name.Buffer, BundleBuffer); } } } else { Error = ERROR_FILE_NOT_FOUND; } } while (FALSE); SetLastError(Error); return(fResult); } #endif UINT NdisQueryHwAddress( IN PUNICODE_STRING Device, OUT PUCHAR CurrentAddress, OUT PUCHAR PermanentAddress, OUT PUCHAR VendorId ) { UCHAR Buf[3*sizeof(NDIS_STATISTICS_VALUE) + 48]; PNDIS_STATISTICS_VALUE pBuf; NDIS_OID Oids[] = { OID_802_3_CURRENT_ADDRESS, OID_802_3_PERMANENT_ADDRESS, OID_GEN_VENDOR_ID }; HANDLE hDevice; BOOL fResult = FALSE; UINT cb; DWORD Error; memset(CurrentAddress, 0, MAC_ADDRESS_SIZE); memset(PermanentAddress, 0, MAC_ADDRESS_SIZE); memset(VendorId, 0, VENDOR_ID_SIZE); hDevice = OpenDevice(Device); if (hDevice != NULL) { fResult = DeviceIoControl(hDevice, IOCTL_NDIS_QUERY_SELECTED_STATS, &Oids, // input buffer sizeof(Oids), // input buffer size Buf, // output buffer sizeof(Buf), // output buffer size &cb, // bytes returned NULL); // OVERLAPPED structure Error = GetLastError(); CloseHandle(hDevice); if (fResult) { UINT Size, tmp; Error = NO_ERROR; pBuf = (PNDIS_STATISTICS_VALUE)Buf; for (Size = 0; Size < cb; ) { tmp = (pBuf->DataLength + FIELD_OFFSET(NDIS_STATISTICS_VALUE, Data)); Size += tmp; switch (pBuf->Oid) { case OID_802_3_CURRENT_ADDRESS: memcpy(CurrentAddress, pBuf->Data, MAC_ADDRESS_SIZE); break; case OID_802_3_PERMANENT_ADDRESS: memcpy(PermanentAddress, pBuf->Data, MAC_ADDRESS_SIZE); break; case OID_GEN_VENDOR_ID: memcpy(VendorId, pBuf->Data, VENDOR_ID_SIZE); } pBuf = (PNDIS_STATISTICS_VALUE)((PUCHAR)pBuf + tmp); } } else { Error = GetLastError(); } } else { Error = ERROR_FILE_NOT_FOUND; } SetLastError(Error); return(fResult); } VOID XSetLastError( IN ULONG Error ) { SetLastError(Error); }