// @doc /********************************************************************** * * @module SWVBENUM.cpp | * * SideWinder Virtual Bus Enumerator * * History * ---------------------------------------------------------- * Mitchell S. Dernis Original * * (c) 1986-1998 Microsoft Corporation. All right reserved. * @index SideWinder Virtual Bus | SWVBENUM * * @topic SWVBENUM | * This module implements the SideWinder Virtual Bus. * The bus is nothing more than attaching this code on top of * a FilterDO of a raw HID PDO, for the purpose of adding DevNodes * for a virtual keyboard, virtual mouse, and the future virtual * mixed devices. All of these devices are expected to be HID * devices. * * The function driver for these devices the SWVBHID.sys (SideWinder * Virtual Bus - HID). This driver is a HID mini-driver, however * all IRPs are simply passed down to their PDO's, i.e. this * code. * * The code in this module is independent of the functionality * of the virtual devices. Basically all Power and PnP IRPs * are handled here. All IRP_MJ_READ, IRP_MJ_WRITE, * IRP_MJ_INTERNAL_IOCTL, and IRP_MJ_IOCTL entries are delegated * via service table provided in the expose call to this module * and stored in the device extension to a code module * in this driver representing the device. * **********************************************************************/ #define __DEBUG_MODULE_IN_USE__ GCK_SWVBENUM_C extern "C" { #include #include "GckShell.h" #include "debug.h" #include DECLARE_MODULE_DEBUG_LEVEL((DBG_WARN|DBG_ERROR|DBG_CRITICAL)); //DECLARE_MODULE_DEBUG_LEVEL((DBG_ALL)); } #include "SWVBENUM.h" // // Mark the pageable routines as such // #ifdef ALLOC_PRAGMA #pragma alloc_text (INIT, GCK_SWVB_DriverEntry) #endif // @globalv Globals for SWVB module SWVB_GLOBALS SwvbGlobals; /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_DriverEntry ** ** @func Initializes SWVB module. In particular the globals. ** ** @rdesc Returns STATUS_SUCCESS always. ** ** @comm Called by DriverEntry of main filter. ** *************************************************************************************/ NTSTATUS GCK_SWVB_DriverEntry ( IN PDRIVER_OBJECT pDriverObject, // @parm DriverObject for module IN PUNICODE_STRING puniRegistryPath // @parm Registry Path ) { GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_DriverEntry\n")); UNREFERENCED_PARAMETER(pDriverObject); UNREFERENCED_PARAMETER(puniRegistryPath); SwvbGlobals.pBusFdo=NULL; SwvbGlobals.pBusPdo=NULL; SwvbGlobals.pDeviceRelations=NULL; SwvbGlobals.ulDeviceRelationsAllocCount=0; SwvbGlobals.ulDeviceNumber=0; GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_DriverEntry\n")); return STATUS_SUCCESS; } VOID GCK_SWVB_UnLoad() { if(SwvbGlobals.pDeviceRelations) { ExFreePool(SwvbGlobals.pDeviceRelations); SwvbGlobals.pDeviceRelations = NULL; } } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_SetBusDOs ** ** @func Sets the Device Object (PDO and FDO), to use as the base of the ** SideWinder Virtual Bus. ** ** @rdesc S_OK on success ** ** @comm A real device is needed on the system in order to ** support the virtual device. When the first such device is detected, ** this function is called to set the filter device object of that ** device to be the Fdo of the SWVB, and its Pdo to be the Pdo of the bus. ** If that device object is removed, this function can be called to move ** the SWVB unto another physical device, or it can be called with NULL ** for both arguments to remove the SWVB. ** *************************************************************************************/ NTSTATUS GCK_SWVB_SetBusDOs ( IN PDEVICE_OBJECT pBusFdo, // @parm [in] Pointer to Fdo (Filter Device Object - actually) IN PDEVICE_OBJECT pBusPdo // @parm [in] Pointer to Pdo ) { PDEVICE_OBJECT pOldBusFdo; PDEVICE_OBJECT pOldBusPdo; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_SetBusDOs\n")); // Save old Bus DO info pOldBusFdo = SwvbGlobals.pBusFdo; pOldBusPdo = SwvbGlobals.pBusPdo; // Update Bus DO info SwvbGlobals.pBusFdo = pBusFdo; SwvbGlobals.pBusPdo = pBusPdo; // Invalidate the old and the new pBusPdo's - iff // (they exist && there is at least one device on the bus) // This will fire up the PnP system and cause it to re-detect // everything. if(SwvbGlobals.pDeviceRelations && SwvbGlobals.pDeviceRelations->Count) { if(pOldBusPdo) { IoInvalidateDeviceRelations(pOldBusPdo, BusRelations); } if(SwvbGlobals.pBusPdo) { IoInvalidateDeviceRelations(SwvbGlobals.pBusPdo, BusRelations); } } GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_SetBusDOs\n")); return STATUS_SUCCESS; } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_HandleBusRelations ** ** @func Handles queries for the BusRelations on behalf of the filter device object, ** which the SWVB is sitting on. Basically all we need do is copy over ** over our device relations, being cognizant that someone may layer on top ** of us and possibly has added stuff already. ** @rdesc Same as in the IoStatus and appropriate to return. ** *************************************************************************************/ NTSTATUS GCK_SWVB_HandleBusRelations ( IN OUT PIO_STATUS_BLOCK pIoStatus // @parm [out] IoStatus block is filled out by this routine. ) { ULONG ulTotalCount; PDEVICE_RELATIONS pExistingRelations; PDEVICE_RELATIONS pDeviceRelations; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_HandleBusRelations. pIoStatus = 0x%0.8x\n", pIoStatus)); // Copy the count of what we know about ulTotalCount = SwvbGlobals.pDeviceRelations->Count; GCK_DBG_TRACE_PRINT(("We have %d PDOs\n", ulTotalCount)); // Read existing relations pExistingRelations = (PDEVICE_RELATIONS)pIoStatus->Information; // Add the count that someone on top of us may have added. if( NULL != pExistingRelations) { GCK_DBG_TRACE_PRINT(("There were %d existing bus relations.\n", pExistingRelations->Count)); ulTotalCount += pExistingRelations->Count; } // Allocate new relations structure pDeviceRelations = (PDEVICE_RELATIONS)EX_ALLOCATE_POOL(NonPagedPool, (sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * (ulTotalCount-1)) ); // Abort if allocation failed if(!pDeviceRelations) { pIoStatus->Status = STATUS_INSUFFICIENT_RESOURCES; GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_BusRelations(1): STATUS_INSUFFICIENT_RESOURCES\n")); return STATUS_INSUFFICIENT_RESOURCES; } pDeviceRelations->Count = 0; // Copy pExistingRelations (from above us perhaps) if there are any. if( pExistingRelations ) { for( pDeviceRelations->Count = 0; pDeviceRelations->Count < pExistingRelations->Count; pDeviceRelations->Count++) { GCK_DBG_TRACE_PRINT(("Exiting relation (PDO = 0x%0.8x)\n", pExistingRelations->Objects[pDeviceRelations->Count])); pDeviceRelations->Objects[pDeviceRelations->Count] = pExistingRelations->Objects[pDeviceRelations->Count]; } ExFreePool(pExistingRelations); } // Add the relations that we know about if(SwvbGlobals.pDeviceRelations) { ULONG ulIndex; for(ulIndex=0; ulIndex < SwvbGlobals.pDeviceRelations->Count; ulIndex++, pDeviceRelations->Count++) { GCK_DBG_TRACE_PRINT(("Our relation (PDO = 0x%0.8x)\n", SwvbGlobals.pDeviceRelations->Objects[ulIndex])); pDeviceRelations->Objects[pDeviceRelations->Count] = SwvbGlobals.pDeviceRelations->Objects[ulIndex]; // Reference these guys as you add them ObReferenceObject(pDeviceRelations->Objects[pDeviceRelations->Count]); } //minor sanity check ASSERT(pDeviceRelations->Count == ulTotalCount); } // Fill out the IoStatus block pIoStatus->Information = (ULONG)pDeviceRelations; pIoStatus->Status = STATUS_SUCCESS; //Get outta here GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_BusRelations(2): STATUS_SUCCESS\n")); return STATUS_SUCCESS; } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_Expose ** ** @func Exposes a new virtual device ** ** @rdesc STATUS_SUCCESS on success, various errors ** ** @comm Expose is called to add a new virtual device to the system. ** The new device object is not returned, rather the InitDevice function ** passed in pSwvbExposeData is called when it is time to initialize the ** new device, the caller also must cache the device during that call ** so that it can remove it later. ** ** @xref SWVB_EXPOSE_DATA *************************************************************************************/ NTSTATUS GCK_SWVB_Expose ( IN PSWVB_EXPOSE_DATA pSwvbExposeData // @parm all the data needed to expose a PDO ) { NTSTATUS NtStatus; UNICODE_STRING uniPdoNameString; PWCHAR pcwPdoName; PDEVICE_OBJECT pVdPdo; PSWVB_PDO_EXT pSwvbPdoExt; ULONG ulTotalExtensionSize; ULONG ulHardwareIDLength; PAGED_CODE(); GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_Expose. pSwvbExposeData = 0x%0.8x\n", pSwvbExposeData)); //Calculate the needed extension size ulTotalExtensionSize = sizeof(SWVB_PDO_EXT) + pSwvbExposeData->ulDeviceExtensionSize; // Create a name for the Pdo pcwPdoName = (PWCHAR)EX_ALLOCATE_POOL(PagedPool, sizeof(SWVB_DEVICE_NAME_BASE)); if( !pcwPdoName ) { GCK_DBG_ERROR_PRINT(("Exiting GCK_SWVB_Expose(1) ERROR:Failed to allocate PDO Name\n")); return STATUS_INSUFFICIENT_RESOURCES; } swprintf(pcwPdoName, SWVB_DEVICE_NAME_TMPLT, SwvbGlobals.ulDeviceNumber++); RtlInitUnicodeString(&uniPdoNameString, pcwPdoName); // Create the PDO NtStatus = IoCreateDevice( SwvbGlobals.pBusFdo->DriverObject, ulTotalExtensionSize, &uniPdoNameString, FILE_DEVICE_UNKNOWN, 0, FALSE, &pVdPdo ); //Done with the name ExFreePool(pcwPdoName); if( !NT_SUCCESS(NtStatus) ) { GCK_DBG_ERROR_PRINT(("Exiting GCK_SWVB_Expose(2) ERROR:Failed to Create PDO, NtStatus = 0x%0.8x\n", NtStatus)); return NtStatus; } // Ensure that we will be able to remember this new Pdo. if(!SwvbGlobals.pDeviceRelations) { // // Three PDO's is pretty cheap and will suffice most of the time, avoiding reallocation. // We hard code this here, as this is not really a parameter that you need to change. // If we run over 3 than it will reallocate as needed anyway. - The device relations // already as room for 1 device object so we just need to add the size of 2 pointers // to get to three. // ULONG ulSize = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT)*2; SwvbGlobals.pDeviceRelations = (PDEVICE_RELATIONS)EX_ALLOCATE_POOL(NonPagedPool, ulSize); if(!SwvbGlobals.pDeviceRelations) { IoDeleteDevice(pVdPdo); //guess we won't be needing this afterall GCK_DBG_ERROR_PRINT(("Exiting GCK_SWVB_Expose(3): Failed to allocate SwvbGlobals.pDeviceRelations\n")); return STATUS_INSUFFICIENT_RESOURCES; } SwvbGlobals.pDeviceRelations->Count = 0; SwvbGlobals.ulDeviceRelationsAllocCount = 3; //we made space for three } // If the DEVICE_RELATIONS structure is not large enough, grow it. if(SwvbGlobals.pDeviceRelations->Count == SwvbGlobals.ulDeviceRelationsAllocCount) { ULONG ulNewAllocCount; ULONG ulNewAllocSize; ULONG ulOldAllocSize; PDEVICE_RELATIONS pTempDeviceRelations; ulNewAllocCount = SwvbGlobals.ulDeviceRelationsAllocCount*2; ulNewAllocSize = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT)*(ulNewAllocCount-1); ulOldAllocSize = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT)*(SwvbGlobals.ulDeviceRelationsAllocCount-1); pTempDeviceRelations = (PDEVICE_RELATIONS)EX_ALLOCATE_POOL(NonPagedPool, ulNewAllocSize); //Make sure that allocation worked if(!pTempDeviceRelations) { IoDeleteDevice(pVdPdo); //guess we won't be needing this afterall GCK_DBG_ERROR_PRINT(("Exiting GCK_SWVB_Expose(4): Failed to grow SwvbGlobals.pDeviceRelations\n")); return STATUS_INSUFFICIENT_RESOURCES; } //Copy all data RtlCopyMemory(pTempDeviceRelations, SwvbGlobals.pDeviceRelations, ulOldAllocSize); //Update info SwvbGlobals.ulDeviceRelationsAllocCount = ulNewAllocCount; SwvbGlobals.pDeviceRelations = pTempDeviceRelations; /* * BUGBUG: Memory Leak. After RC replace above line with the following * * PDEVICE_RELATIONS pTemp2 = SwvbGlobals.pDeviceRelations; * SwvbGlobals.pDeviceRelations = pTempDeviceRelations; * ExFreePool(pTemp2); * */ } // Reference the newly created pdo ObReferenceObject(pVdPdo); // Initialize the device extention pSwvbPdoExt = (PSWVB_PDO_EXT)pVdPdo->DeviceExtension; pSwvbPdoExt->ulGckDevObjType = GCK_DO_TYPE_SWVB; pSwvbPdoExt->fAttached=TRUE; pSwvbPdoExt->fStarted=FALSE; pSwvbPdoExt->fRemoved = FALSE; pSwvbPdoExt->pServiceTable = pSwvbExposeData->pServiceTable; pSwvbPdoExt->ulInstanceNumber = pSwvbExposeData->ulInstanceNumber; pSwvbPdoExt->ulOpenCount = 0; GCK_InitRemoveLock(&pSwvbPdoExt->RemoveLock, "Virtual Device"); // Copy the HardwareID ulHardwareIDLength = MultiSzWByteLength(pSwvbExposeData->pmwszDeviceId); pSwvbPdoExt->pmwszHardwareID = (PWCHAR)EX_ALLOCATE_POOL( NonPagedPool, ulHardwareIDLength); if(!pSwvbPdoExt->pmwszHardwareID) { ObDereferenceObject(pVdPdo); IoDeleteDevice(pVdPdo); //guess we won't be needing this afterall GCK_DBG_ERROR_PRINT(("Exiting GCK_SWVB_Expose(5): Failed to allocate space for HardwareId\n")); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory( pSwvbPdoExt->pmwszHardwareID, pSwvbExposeData->pmwszDeviceId, ulHardwareIDLength); //** CAVEAT From here to end of function must succeed! We //** CAVEAT have no way of telling the virtual device //** CAVEAT that afterall, we decided not to expose that PDO it //** CAVEAT it has already initialized! // Allow virtual device code to init its part of the extension pSwvbExposeData->pfnInitDevice(pVdPdo, pSwvbExposeData->ulInitContext); //mark end of initialization in the device object pVdPdo->Flags |= (DO_DIRECT_IO | DO_POWER_PAGABLE); pVdPdo->Flags &= ~DO_DEVICE_INITIALIZING; //Sanity check of code a few steps ago. ASSERT(SwvbGlobals.pDeviceRelations->Count < SwvbGlobals.ulDeviceRelationsAllocCount); //Add our Pdo to the list SwvbGlobals.pDeviceRelations->Objects[SwvbGlobals.pDeviceRelations->Count++] = pVdPdo; // // Invalidate Device Relations - will pique some interest in what we have done here // Verify that we have a bus if not we are OK, when the bus is set everything will work, // but we assert becuase we really want to force the client code to add the bus before the device. // ASSERT( SwvbGlobals.pBusFdo ); ASSERT( SwvbGlobals.pBusPdo ); if( SwvbGlobals.pBusFdo && SwvbGlobals.pBusPdo) { IoInvalidateDeviceRelations(SwvbGlobals.pBusPdo, BusRelations); } GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_Expose(5): Success\n")); return STATUS_SUCCESS; } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_Remove ** ** @func Removes a virtual device from the system. Actually we just mark it ** for removal and tell PnP to reenumerate. ** ** @rdesc STATUS_SUCCESS on success, various errors ** ** @comm The Pdo should be one that was sent to pfnInitDevice when ** was called. ** *************************************************************************************/ NTSTATUS GCK_SWVB_Remove ( IN PDEVICE_OBJECT pPdo // @parm Pdo to remove ) { ULONG ulMatchIndex = 0xFFFFFFFF; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_Remove: pPdo = 0x%0.8x\n", pPdo)); // Find and Remove Pdo from SwvbGlobals.pDeviceRelations if(SwvbGlobals.pDeviceRelations) { ULONG ulIndex; for(ulIndex = 0; ulIndex < SwvbGlobals.pDeviceRelations->Count; ulIndex++) { if(SwvbGlobals.pDeviceRelations->Objects[ulIndex] == pPdo) { ulMatchIndex = ulIndex; break; } } } //if we found a match remove it from it if(0xFFFFFFFF == ulMatchIndex) { //No one should ever try to remove a device that is not in the list ASSERT(FALSE); GCK_DBG_EXIT_PRINT(("Error GCK_SWVB_Remove: Attempt to remove non-existant device!\n")); return STATUS_UNSUCCESSFUL; } //Copy last PDO over this one and dec count, works even if we are last SwvbGlobals.pDeviceRelations->Objects[ulMatchIndex] = SwvbGlobals.pDeviceRelations->Objects[--(SwvbGlobals.pDeviceRelations->Count)]; // // Mark device as unattached so when PnP says to remove it, we // do remove it and clean up everything, rather than hanging on // to it and waiting for more querying IRPs ((PSWVB_PDO_EXT)pPdo->DeviceExtension)->fAttached =FALSE; // // If it has been removed already, we need to delete, because the PnP system already // doesn't know about, and we just detattached, so once we leave this routine, we don't // know about it. So Delete now, or it sticks to us. Then we go to remove ourselves // we will notice that we still have some Device Objects in our pockets(pDriverObject device object list), // and we will wonder where they came from, and what type they are? So delete them now! // if(TRUE == ((PSWVB_PDO_EXT)pPdo->DeviceExtension)->fRemoved) { PSWVB_PDO_EXT pPdoExt = (PSWVB_PDO_EXT)pPdo->DeviceExtension; NTSTATUS NtStatus; // Give virtual device a chance at the IRP if(pPdoExt->pServiceTable->pfnRemove) { NtStatus = pPdoExt->pServiceTable->pfnRemove(pPdo, NULL); } // failure to succeed is pretty darn serious if(!NT_SUCCESS(NtStatus)) { ASSERT(FALSE); GCK_DBG_CRITICAL_PRINT(("Virtual Device had the gall to fail remove!\n")); } // free memory for storing the HardwareID ASSERT(pPdoExt->pmwszHardwareID); ExFreePool(pPdoExt->pmwszHardwareID); GCK_DBG_TRACE_PRINT(("Detattached device has already been removed by PnP, so clean it up.\n")); if( 0 == ((PSWVB_PDO_EXT)pPdo->DeviceExtension)->ulOpenCount ) { ObDereferenceObject(pPdo); IoDeleteDevice(pPdo); } } // Invalidate the BUS relations so that PnP will renumerate the bus. // Of course since we rely on others, it is possible that we temporarily // don't have a Bus, in which case we skip this step. // // If we don't have DO for the Bus we shouldn't lose any sleep on two accounts: // 1. It is possible that all the real devices have been yanked from the system, in which case // PnP will start removing everyone below the node that was yanked, starting at the bottom. // That means virtual devices have been removed as far as PnP is concerned and our remove // routine for those devices has been called. However, until all the underlying real devices // get removed by PnP (which is later), they don't relealize it is time to tell us to get rid of the // virtual devices. No big woop. We will delete the devices when they tell us. If the virtual // device is shared among real devices (like a virtual keyboard), they will tell us when the last // one is removed. This scenario is infact the normal way things happen when the last device is pulled, // or when the system is powered down. // 2. It is possible that the filter drivers have temporarily decided to pull our bus. In this case, // everything will be fine and dandy when we get a new bus to sit on, as we will Invalidate Bus relations // at that time. if(SwvbGlobals.pBusPdo) { IoInvalidateDeviceRelations(SwvbGlobals.pBusPdo, BusRelations); } GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_Remove: Success\n")); return STATUS_SUCCESS; } /*********************************************************************************** ** ** ULONG MultiSzWByteLength(PWCHAR pmwszBuffer); ** ** @func Calculates the length in bytes of a Wide Multi String, ** including terminating characters. Multi-sz is terminated by two NULLs ** in a row. ** ** @rdesc Size in characters, including terminating characters. ** *************************************************************************************/ ULONG MultiSzWByteLength ( PWCHAR pmwszBuffer // @parm Pointer to UNICODE multi-string ) { PWCHAR pmwszStart = pmwszBuffer; do { while(*pmwszBuffer++); }while(*pmwszBuffer++); return (ULONG)((PCHAR)pmwszBuffer -(PCHAR)pmwszStart); } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_Create ** ** @func Handles IRP_MJ_CREATE for virtual devices. ** delegates using their service table. ** ** @rdesc STATUS_SUCCESS on success, various errors ** ** @todo Add basic checks to make sure device is valid before delegating ** *************************************************************************************/ NTSTATUS GCK_SWVB_Create ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS NtStatus; PSWVB_PDO_EXT pPdoExt; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_Create\n")); // Cast device extension pPdoExt = (PSWVB_PDO_EXT) pDeviceObject->DeviceExtension; // Just an extra sanity check ASSERT(GCK_DO_TYPE_SWVB == pPdoExt->ulGckDevObjType); //Delegate NtStatus = pPdoExt->pServiceTable->pfnCreate(pDeviceObject, pIrp); if( NT_SUCCESS(NtStatus) ) { pPdoExt->ulOpenCount++; } GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_Create, Status = 0x%0.8x\n", NtStatus)); return NtStatus; } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_Close ** ** @func Handles IRP_MJ_CLOSE for virtual devices. ** delegates using their service table. ** ** @rdesc STATUS_SUCCESS on success, various errors ** ** @todo Add basic checks to make sure device is valid before delegating ** *************************************************************************************/ NTSTATUS GCK_SWVB_Close ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS NtStatus; PSWVB_PDO_EXT pPdoExt; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_Close\n")); // Cast device extension pPdoExt = (PSWVB_PDO_EXT) pDeviceObject->DeviceExtension; // Just an extra sanity check ASSERT(GCK_DO_TYPE_SWVB == pPdoExt->ulGckDevObjType); //Delegate NtStatus = pPdoExt->pServiceTable->pfnClose(pDeviceObject, pIrp); //if successfully closed, decrement count if( NT_SUCCESS(NtStatus) ) { if(0==--pPdoExt->ulOpenCount) { //if the device is removed, we need to delete it. if(pPdoExt->fRemoved) { ObDereferenceObject(pDeviceObject); IoDeleteDevice(pDeviceObject); } } } GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_Close, Status = 0x%0.8x\n", NtStatus)); return NtStatus; } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_Read ** ** @func Handles IRP_MJ_READ for virtual devices. ** delegates using their service table. ** ** @rdesc STATUS_SUCCESS on success, various errors ** ** @todo Add basic checks to make sure device is valid before delegating ** *************************************************************************************/ NTSTATUS GCK_SWVB_Read ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS NtStatus; PSWVB_PDO_EXT pPdoExt; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_Reade\n")); // Cast device extension pPdoExt = (PSWVB_PDO_EXT) pDeviceObject->DeviceExtension; // Just an extra sanity check ASSERT(GCK_DO_TYPE_SWVB == pPdoExt->ulGckDevObjType); //Delegate NtStatus = pPdoExt->pServiceTable->pfnRead(pDeviceObject, pIrp); GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_Read, Status = 0x%0.8x\n", NtStatus)); return NtStatus; } /*********************************************************************************** ** ** NTSTATUS GCK_SWVB_Ioctl ** ** @func Handles IRP_MJ_IOCTL and IRP_MJ_INTERNAL_IOCTL for virtual devices. ** delegates using their service table. ** ** @rdesc STATUS_SUCCESS on success, various errors ** ** @todo Add basic checks to make sure device is valid before delegating ** *************************************************************************************/ NTSTATUS GCK_SWVB_Ioctl ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS NtStatus; PSWVB_PDO_EXT pPdoExt; GCK_DBG_ENTRY_PRINT(("Entering GCK_SWVB_Ioctl\n")); // Cast device extension pPdoExt = (PSWVB_PDO_EXT) pDeviceObject->DeviceExtension; // Just an extra sanity check ASSERT(GCK_DO_TYPE_SWVB == pPdoExt->ulGckDevObjType); //if device is stopped, complete here, less work for virtual devices if( (pPdoExt->fRemoved) || (!pPdoExt->fStarted) ) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_DELETE_PENDING; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_DELETE_PENDING; } //Delegate NtStatus = pPdoExt->pServiceTable->pfnIoctl(pDeviceObject, pIrp); GCK_DBG_EXIT_PRINT(("Exiting GCK_SWVB_Ioctl, Status = 0x%0.8x\n", NtStatus)); return NtStatus; }