/*++ Copyright (c) 2000 Microsoft Corporation Module Name: ntdisp.c Abstract: NT Entry points and dispatch routines for NDISUIO. Environment: Kernel mode only. Revision History: arvindm 4/6/2000 Created --*/ #include "precomp.h" #define __FILENUMBER 'PSID' #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGE, NdisuioUnload) #pragma alloc_text(PAGE, NdisuioOpen) #pragma alloc_text(PAGE, NdisuioClose) #pragma alloc_text(PAGE, NdisuioIoControl) #endif // ALLOC_PRAGMA // // Globals: // NDISUIO_GLOBALS Globals = {0}; NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath ) /*++ Routine Description: Called on loading. We create a device object to handle user-mode requests on, and register ourselves as a protocol with NDIS. Arguments: pDriverObject - Pointer to driver object created by system. pRegistryPath - Pointer to the Unicode name of the registry path for this driver. Return Value: NT Status code --*/ { NDIS_PROTOCOL_CHARACTERISTICS protocolChar; NTSTATUS status = STATUS_SUCCESS; NDIS_STRING protoName = NDIS_STRING_CONST("NDISUIO"); UNICODE_STRING ntDeviceName; UNICODE_STRING win32DeviceName; BOOLEAN fSymbolicLink = FALSE; PDEVICE_OBJECT deviceObject = NULL; UNREFERENCED_PARAMETER(pRegistryPath); DEBUGP(DL_LOUD, ("DriverEntry\n")); Globals.pDriverObject = pDriverObject; Globals.EthType = NUIO_ETH_TYPE; NUIO_INIT_EVENT(&Globals.BindsComplete); do { // // Create our device object using which an application can // access NDIS devices. // RtlInitUnicodeString(&ntDeviceName, NT_DEVICE_NAME); status = IoCreateDevice (pDriverObject, 0, &ntDeviceName, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); if (!NT_SUCCESS (status)) { // // Either not enough memory to create a deviceobject or another // deviceobject with the same name exits. This could happen // if you install another instance of this device. // break; } RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); status = IoCreateSymbolicLink(&win32DeviceName, &ntDeviceName); if (!NT_SUCCESS(status)) { break; } fSymbolicLink = TRUE; deviceObject->Flags |= DO_DIRECT_IO; Globals.ControlDeviceObject = deviceObject; NUIO_INIT_LIST_HEAD(&Globals.OpenList); NUIO_INIT_LOCK(&Globals.GlobalLock); // // Initialize the protocol characterstic structure // NdisZeroMemory(&protocolChar,sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); protocolChar.MajorNdisVersion = 5; protocolChar.MinorNdisVersion = 0; protocolChar.Name = protoName; protocolChar.OpenAdapterCompleteHandler = NdisuioOpenAdapterComplete; protocolChar.CloseAdapterCompleteHandler = NdisuioCloseAdapterComplete; protocolChar.SendCompleteHandler = NdisuioSendComplete; protocolChar.TransferDataCompleteHandler = NdisuioTransferDataComplete; protocolChar.ResetCompleteHandler = NdisuioResetComplete; protocolChar.RequestCompleteHandler = NdisuioRequestComplete; protocolChar.ReceiveHandler = NdisuioReceive; protocolChar.ReceiveCompleteHandler = NdisuioReceiveComplete; protocolChar.StatusHandler = NdisuioStatus; protocolChar.StatusCompleteHandler = NdisuioStatusComplete; protocolChar.BindAdapterHandler = NdisuioBindAdapter; protocolChar.UnbindAdapterHandler = NdisuioUnbindAdapter; protocolChar.UnloadHandler = NULL; protocolChar.ReceivePacketHandler = NdisuioReceivePacket; protocolChar.PnPEventHandler = NdisuioPnPEventHandler; // // Register as a protocol driver // NdisRegisterProtocol( (PNDIS_STATUS)&status, &Globals.NdisProtocolHandle, &protocolChar, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); if (status != NDIS_STATUS_SUCCESS) { DEBUGP(DL_WARN, ("Failed to register protocol with NDIS\n")); status = STATUS_UNSUCCESSFUL; break; } #ifdef NDIS51 Globals.PartialCancelId = NdisGeneratePartialCancelId(); Globals.PartialCancelId <<= ((sizeof(PVOID) - 1) * 8); DEBUGP(DL_LOUD, ("DriverEntry: CancelId %lx\n", Globals.PartialCancelId)); #endif // // Now set only the dispatch points we would like to handle. // pDriverObject->MajorFunction[IRP_MJ_CREATE] = NdisuioOpen; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = NdisuioClose; pDriverObject->MajorFunction[IRP_MJ_READ] = NdisuioRead; pDriverObject->MajorFunction[IRP_MJ_WRITE] = NdisuioWrite; pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = NdisuioCleanup; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NdisuioIoControl; pDriverObject->DriverUnload = NdisuioUnload; status = STATUS_SUCCESS; } while (FALSE); if (!NT_SUCCESS(status)) { if (deviceObject) { IoDeleteDevice(deviceObject); Globals.ControlDeviceObject = NULL; } if (fSymbolicLink) { IoDeleteSymbolicLink(&win32DeviceName); } } return status; } VOID NdisuioUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Free all the allocated resources, etc. Arguments: DriverObject - pointer to a driver object. Return Value: VOID. --*/ { UNICODE_STRING win32DeviceName; UNREFERENCED_PARAMETER(DriverObject); DEBUGP(DL_LOUD, ("Unload Enter\n")); // // First delete the Control deviceobject and the corresponding // symbolicLink // RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); IoDeleteSymbolicLink(&win32DeviceName); if (Globals.ControlDeviceObject) { IoDeleteDevice(Globals.ControlDeviceObject); Globals.ControlDeviceObject = NULL; } ndisuioDoProtocolUnload(); #if DBG ndisuioAuditShutdown(); #endif DEBUGP(DL_LOUD, ("Unload Exit\n")); } NTSTATUS NdisuioOpen( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling IRP_MJ_CREATE. We simply succeed this. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus = STATUS_SUCCESS; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pIrpSp->FileObject->FsContext = NULL; DEBUGP(DL_INFO, ("Open: FileObject %p\n", pIrpSp->FileObject)); pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return NtStatus; } NTSTATUS NdisuioClose( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling IRP_MJ_CLOSE. We simply succeed this. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { NTSTATUS NtStatus; PIO_STACK_LOCATION pIrpSp; PNDISUIO_OPEN_CONTEXT pOpenContext; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; DEBUGP(DL_INFO, ("Close: FileObject %p\n", IoGetCurrentIrpStackLocation(pIrp)->FileObject)); if (pOpenContext != NULL) { NUIO_STRUCT_ASSERT(pOpenContext, oc); // // Deref the endpoint // NUIO_DEREF_OPEN(pOpenContext); // Close } pIrpSp->FileObject->FsContext = NULL; NtStatus = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return NtStatus; } NTSTATUS NdisuioCleanup( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling IRP_MJ_CLEANUP. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus; NDIS_STATUS NdisStatus; PNDISUIO_OPEN_CONTEXT pOpenContext; ULONG PacketFilter; ULONG BytesProcessed; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; DEBUGP(DL_VERY_LOUD, ("Cleanup: FileObject %p, Open %p\n", pIrpSp->FileObject, pOpenContext)); if (pOpenContext != NULL) { NUIO_STRUCT_ASSERT(pOpenContext, oc); // // Mark this endpoint. // NUIO_ACQUIRE_LOCK(&pOpenContext->Lock); NUIO_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE); pOpenContext->pFileObject = NULL; NUIO_RELEASE_LOCK(&pOpenContext->Lock); // // Set the packet filter to 0, telling NDIS that we aren't // interested in any more receives. // PacketFilter = 0; NdisStatus = ndisuioValidateOpenAndDoRequest( pOpenContext, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &BytesProcessed, FALSE // Don't wait for device to be powered on ); if (NdisStatus != NDIS_STATUS_SUCCESS) { DEBUGP(DL_INFO, ("Cleanup: Open %p, set packet filter (%x) failed: %x\n", pOpenContext, PacketFilter, NdisStatus)); // // Ignore the result. If this failed, we may continue // to get indicated receives, which will be handled // appropriately. // NdisStatus = NDIS_STATUS_SUCCESS; } // // Cancel any pending reads. // ndisuioCancelPendingReads(pOpenContext); // // Clean up the receive packet queue // ndisuioFlushReceiveQueue(pOpenContext); } NtStatus = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); DEBUGP(DL_INFO, ("Cleanup: OpenContext %p\n", pOpenContext)); return (NtStatus); } NTSTATUS NdisuioIoControl( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling device ioctl requests. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION pIrpSp; ULONG FunctionCode; NTSTATUS NtStatus; NDIS_STATUS Status; PNDISUIO_OPEN_CONTEXT pOpenContext; ULONG BytesReturned; USHORT EthType; #if !DBG UNREFERENCED_PARAMETER(pDeviceObject); #endif DEBUGP(DL_LOUD, ("IoControl: DevObj %p, Irp %p\n", pDeviceObject, pIrp)); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; pOpenContext = (PNDISUIO_OPEN_CONTEXT)pIrpSp->FileObject->FsContext; BytesReturned = 0; switch (FunctionCode) { case IOCTL_NDISUIO_BIND_WAIT: // // Block until we have seen a NetEventBindsComplete event, // meaning that we have finished binding to all running // adapters that we are supposed to bind to. // // If we don't get this event in 5 seconds, time out. // NUIO_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (NUIO_WAIT_EVENT(&Globals.BindsComplete, 5000)) { NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_TIMEOUT; } DEBUGP(DL_INFO, ("IoControl: BindWait returning %x\n", NtStatus)); break; case IOCTL_NDISUIO_QUERY_BINDING: NUIO_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); Status = ndisuioQueryBinding( pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, &BytesReturned ); NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); DEBUGP(DL_LOUD, ("IoControl: QueryBinding returning %x\n", NtStatus)); break; case IOCTL_NDISUIO_OPEN_DEVICE: NUIO_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { NUIO_STRUCT_ASSERT(pOpenContext, oc); DEBUGP(DL_WARN, ("IoControl: OPEN_DEVICE: FileObj %p already" " associated with open %p\n", pIrpSp->FileObject, pOpenContext)); NtStatus = STATUS_DEVICE_BUSY; break; } NtStatus = ndisuioOpenDevice( pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrpSp->FileObject, &pOpenContext ); if (NT_SUCCESS(NtStatus)) { DEBUGP(DL_VERY_LOUD, ("IoControl OPEN_DEVICE: Open %p <-> FileObject %p\n", pOpenContext, pIrpSp->FileObject)); } break; case IOCTL_NDISUIO_QUERY_OID_VALUE: NUIO_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { Status = ndisuioQueryOidValue( pOpenContext, pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, &BytesReturned ); NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; case IOCTL_NDISUIO_SET_OID_VALUE: NUIO_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { Status = ndisuioSetOidValue( pOpenContext, pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength ); BytesReturned = 0; NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; case IOCTL_NDISUIO_SET_ETHER_TYPE: NUIO_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(Globals.EthType)) { NtStatus = STATUS_BUFFER_TOO_SMALL; } else { // // We only allow this value to be set to certain types. // EthType = *(USHORT *)pIrp->AssociatedIrp.SystemBuffer; if (EthType != NUIO_ETH_TYPE) { DEBUGP(DL_WARN, ("IoControl: failed setting EthType to %x\n", EthType)); NtStatus = STATUS_INVALID_PARAMETER; break; } Globals.EthType = EthType; DEBUGP(DL_INFO, ("IoControl: new Ether Type %x\n", Globals.EthType)); NtStatus = STATUS_SUCCESS; } break; default: NtStatus = STATUS_NOT_SUPPORTED; break; } if (NtStatus != STATUS_PENDING) { pIrp->IoStatus.Information = BytesReturned; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } return NtStatus; } NTSTATUS ndisuioOpenDevice( IN PUCHAR pDeviceName, IN ULONG DeviceNameLength, IN PFILE_OBJECT pFileObject, OUT PNDISUIO_OPEN_CONTEXT * ppOpenContext ) /*++ Routine Description: Helper routine called to process IOCTL_NDISUIO_OPEN_DEVICE. Check if there is a binding to the specified device, and is not associated with a file object already. If so, make an association between the binding and this file object. Arguments: pDeviceName - pointer to device name string DeviceNameLength - length of above pFileObject - pointer to file object being associated with the device binding Return Value: Status is returned. --*/ { PNDISUIO_OPEN_CONTEXT pOpenContext; NTSTATUS NtStatus; ULONG PacketFilter; NDIS_STATUS NdisStatus; ULONG BytesProcessed; PNDISUIO_OPEN_CONTEXT pCurrentOpenContext = NULL; pOpenContext = NULL; do { pOpenContext = ndisuioLookupDevice( pDeviceName, DeviceNameLength ); if (pOpenContext == NULL) { DEBUGP(DL_WARN, ("ndisuioOpenDevice: couldn't find device\n")); NtStatus = STATUS_OBJECT_NAME_NOT_FOUND; break; } // // else ndisuioLookupDevice would have addref'ed the open. // NUIO_ACQUIRE_LOCK(&pOpenContext->Lock); if (!NUIO_TEST_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE)) { NUIO_ASSERT(pOpenContext->pFileObject != NULL); DEBUGP(DL_WARN, ("ndisuioOpenDevice: Open %p/%x already associated" " with another FileObject %p\n", pOpenContext, pOpenContext->Flags, pOpenContext->pFileObject)); NUIO_RELEASE_LOCK(&pOpenContext->Lock); NUIO_DEREF_OPEN(pOpenContext); // ndisuioOpenDevice failure NtStatus = STATUS_DEVICE_BUSY; break; } // // This InterlockedXXX function performs an atomic operation: First it compare // pFileObject->FsContext with NULL, if they are equal, the function puts pOpenContext // into FsContext, and return NULL. Otherwise, it return pFileObject->FsContext without // changing anything. // if ((pCurrentOpenContext = InterlockedCompareExchangePointer (& (pFileObject->FsContext), pOpenContext, NULL)) != NULL) { // // pFileObject->FsContext already is used by other open // DEBUGP(DL_WARN, ("ndisuioOpenDevice: FileObject %p already associated" " with another Open %p/%x\n", pFileObject, pCurrentOpenContext, pCurrentOpenContext->Flags)); //BUG NUIO_RELEASE_LOCK(&pOpenContext->Lock); NUIO_DEREF_OPEN(pOpenContext); // ndisuioOpenDevice failure NtStatus = STATUS_INVALID_DEVICE_REQUEST; break; } pOpenContext->pFileObject = pFileObject; NUIO_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_ACTIVE); NUIO_RELEASE_LOCK(&pOpenContext->Lock); // // Set the packet filter now. // PacketFilter = NUIOO_PACKET_FILTER; NdisStatus = ndisuioValidateOpenAndDoRequest( pOpenContext, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &BytesProcessed, TRUE // Do wait for power on ); if (NdisStatus != NDIS_STATUS_SUCCESS) { DEBUGP(DL_WARN, ("openDevice: Open %p: set packet filter (%x) failed: %x\n", pOpenContext, PacketFilter, NdisStatus)); // // Undo all that we did above. // NUIO_ACQUIRE_LOCK(&pOpenContext->Lock); // // Need to set pFileObject->FsContext to NULL again, so others can open a device // for this file object later // pCurrentOpenContext = InterlockedCompareExchangePointer (& (pFileObject->FsContext), NULL, pOpenContext); NUIO_ASSERT(pCurrentOpenContext == pOpenContext); NUIO_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE); pOpenContext->pFileObject = NULL; NUIO_RELEASE_LOCK(&pOpenContext->Lock); NUIO_DEREF_OPEN(pOpenContext); // ndisuioOpenDevice failure NDIS_STATUS_TO_NT_STATUS(NdisStatus, &NtStatus); break; } *ppOpenContext = pOpenContext; NtStatus = STATUS_SUCCESS; } while (FALSE); return (NtStatus); } VOID ndisuioRefOpen( IN PNDISUIO_OPEN_CONTEXT pOpenContext ) /*++ Routine Description: Reference the given open context. NOTE: Can be called with or without holding the opencontext lock. Arguments: pOpenContext - pointer to open context Return Value: None --*/ { NdisInterlockedIncrement((PLONG)&pOpenContext->RefCount); } VOID ndisuioDerefOpen( IN PNDISUIO_OPEN_CONTEXT pOpenContext ) /*++ Routine Description: Dereference the given open context. If the ref count goes to zero, free it. NOTE: called without holding the opencontext lock Arguments: pOpenContext - pointer to open context Return Value: None --*/ { if (NdisInterlockedDecrement((PLONG)&pOpenContext->RefCount) == 0) { DEBUGP(DL_INFO, ("DerefOpen: Open %p, Flags %x, ref count is zero!\n", pOpenContext, pOpenContext->Flags)); NUIO_ASSERT(pOpenContext->BindingHandle == NULL); NUIO_ASSERT(pOpenContext->RefCount == 0); NUIO_ASSERT(pOpenContext->pFileObject == NULL); pOpenContext->oc_sig++; // // Free it. // NUIO_FREE_MEM(pOpenContext); } } #if DBG VOID ndisuioDbgRefOpen( IN PNDISUIO_OPEN_CONTEXT pOpenContext, IN ULONG FileNumber, IN ULONG LineNumber ) { DEBUGP(DL_VERY_LOUD, (" RefOpen: Open %p, old ref %d, File %c%c%c%c, line %d\n", pOpenContext, pOpenContext->RefCount, (CHAR)(FileNumber), (CHAR)(FileNumber >> 8), (CHAR)(FileNumber >> 16), (CHAR)(FileNumber >> 24), LineNumber)); ndisuioRefOpen(pOpenContext); } VOID ndisuioDbgDerefOpen( IN PNDISUIO_OPEN_CONTEXT pOpenContext, IN ULONG FileNumber, IN ULONG LineNumber ) { DEBUGP(DL_VERY_LOUD, ("DerefOpen: Open %p, old ref %d, File %c%c%c%c, line %d\n", pOpenContext, pOpenContext->RefCount, (CHAR)(FileNumber), (CHAR)(FileNumber >> 8), (CHAR)(FileNumber >> 16), (CHAR)(FileNumber >> 24), LineNumber)); ndisuioDerefOpen(pOpenContext); } #endif // DBG