You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
744 lines
25 KiB
744 lines
25 KiB
// @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.<nl>
|
|
*
|
|
* 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.<nl>
|
|
*
|
|
* 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.<nl>
|
|
*
|
|
**********************************************************************/
|
|
#define __DEBUG_MODULE_IN_USE__ GCK_SWVBENUM_C
|
|
|
|
extern "C"
|
|
{
|
|
#include <WDM.H>
|
|
#include "GckShell.h"
|
|
#include "debug.h"
|
|
#include <stdio.h>
|
|
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.<nl>
|
|
** 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 <f GCK_SWVB_Expose>
|
|
** was called.<nl>
|
|
**
|
|
*************************************************************************************/
|
|
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;
|
|
}
|