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.
531 lines
16 KiB
531 lines
16 KiB
// @doc
|
|
/**********************************************************************
|
|
*
|
|
* @module CTRL_Ioctl.c |
|
|
*
|
|
* Implements basic IOCTL entry points and their handler functions
|
|
* for control device objects.
|
|
*
|
|
* History
|
|
* ----------------------------------------------------------
|
|
* Mitchell S. Dernis Original
|
|
*
|
|
* (c) 1986-1998 Microsoft Corporation. All right reserved.
|
|
*
|
|
* @topic CTRL_Ioctl |
|
|
* Any IOCTL call to the Control Device Object gets
|
|
* filtered through here.
|
|
*
|
|
**********************************************************************/
|
|
#define __DEBUG_MODULE_IN_USE__ GCK_CTRL_IOCTL_C
|
|
|
|
#include <WDM.H>
|
|
#include <basetyps.h>
|
|
#include <initguid.h>
|
|
#include "GckShell.h"
|
|
#include "debug.h"
|
|
|
|
DECLARE_MODULE_DEBUG_LEVEL((DBG_WARN|DBG_ERROR|DBG_CRITICAL));
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Alloc_text pragma to specify routines that can be paged out.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, GCK_CTRL_Ioctl)
|
|
#pragma alloc_text (PAGE, GCK_FindDeviceObject)
|
|
#pragma alloc_text (PAGE, GCK_FindDeviceObject)
|
|
#endif
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** NTSTATUS GCK_CTRL_Ioctl (IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
|
|
**
|
|
** @mfunc Handles all IOCTL's to the control object
|
|
**
|
|
** @rdesc STATUS_SUCCESS, or various errors
|
|
**
|
|
*************************************************************************************/
|
|
NTSTATUS GCK_CTRL_Ioctl
|
|
(
|
|
IN PDEVICE_OBJECT pDeviceObject, // @parm pointer to Device Object
|
|
IN PIRP pIrp // @parm pointer to IRP
|
|
)
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PGCK_CONTROL_EXT pControlExt;
|
|
PIO_STACK_LOCATION pIrpStack;
|
|
PVOID pvIoBuffer;
|
|
ULONG uInLength;
|
|
ULONG uOutLength;
|
|
ULONG uIoctl;
|
|
PDEVICE_OBJECT pFilterHandle;
|
|
PULONG puHandle;
|
|
PGCK_FILTER_EXT pFilterExt;
|
|
PDEVICE_OBJECT pCurDeviceObject;
|
|
BOOLEAN bCompleteRequest = TRUE;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
GCK_DBG_ENTRY_PRINT(("Entering GCK_CTRL_Ioctl, pDeviceObject = 0x%0.8x, pIRP = 0x%0.8x\n", pDeviceObject, pIrp));
|
|
|
|
//
|
|
// Get all the inputs we need
|
|
//
|
|
pControlExt = (PGCK_CONTROL_EXT) pDeviceObject->DeviceExtension;
|
|
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
|
|
uIoctl = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
|
|
pvIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
|
|
uInLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
uOutLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Assume we will succeed with no data, we will change if necessary
|
|
// later
|
|
//
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
if(IOCTL_GCK_GET_HANDLE == uIoctl)
|
|
{
|
|
//
|
|
// Check buffer size
|
|
//
|
|
if( uOutLength < sizeof(PVOID) )
|
|
{
|
|
pIrp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
|
goto complete_and_return;
|
|
}
|
|
|
|
//
|
|
// Get handle (device extension) of requested device
|
|
//
|
|
pFilterHandle = GCK_FindDeviceObject( (LPWSTR)pvIoBuffer, uInLength );
|
|
|
|
//
|
|
// If we couldn't find the handle,
|
|
// it must have been a bad path,
|
|
// so return invalid parameter
|
|
//
|
|
if( NULL == pFilterHandle)
|
|
{
|
|
pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
goto complete_and_return;
|
|
}
|
|
|
|
//
|
|
// copy handle into user buffer
|
|
//
|
|
puHandle = (ULONG *)pvIoBuffer;
|
|
*puHandle = (ULONG)pFilterHandle;
|
|
GCK_DBG_TRACE_PRINT(("Returning 0x%0.8x as handle.\n", *puHandle));
|
|
pIrp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
GCKF_ResetKeyboardQueue(pFilterHandle);
|
|
|
|
goto complete_and_return;
|
|
}
|
|
|
|
//DEBUG only IOCTL to allow changing debug level
|
|
#if (DBG==1)
|
|
if(IOCTL_GCK_SET_MODULE_DBG_LEVEL == uIoctl)
|
|
{
|
|
ASSERT(uInLength >= sizeof(ULONG)*2);
|
|
//Reusing the name handle which is a misnomer
|
|
puHandle = (ULONG *)pvIoBuffer;
|
|
//first parameter is the module ID, the second is the flags
|
|
SetDebugLevel(puHandle[0], puHandle[1]);
|
|
goto complete_and_return;
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// If the call is not IOCTL_GCK_GET_HANDLE, we expect the first byte to be the handle
|
|
//
|
|
|
|
//
|
|
// Check that the inlength is at least large enough
|
|
// for a handle PGCK_FILTER_EXT.
|
|
//
|
|
if( uInLength < sizeof(PGCK_FILTER_EXT) )
|
|
{
|
|
pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
goto complete_and_return;
|
|
}
|
|
|
|
//
|
|
// Get the extension, and the DeviceObject itself
|
|
//
|
|
pFilterHandle = *((PDEVICE_OBJECT *)pvIoBuffer);
|
|
GCK_DBG_TRACE_PRINT(("Filter Handle = 0x%0.8x\n", pFilterHandle));
|
|
|
|
//
|
|
// Make sure the device object is in our linked list
|
|
// *** Do not dereference it, until we know it is in the list ***
|
|
// *** If it is not in the list it may be garbage ***
|
|
pCurDeviceObject = Globals.pFilterObjectList;
|
|
while ( pCurDeviceObject )
|
|
{
|
|
if( pCurDeviceObject == pFilterHandle ) break;
|
|
pCurDeviceObject = NEXT_FILTER_DEVICE_OBJECT(pCurDeviceObject);
|
|
}
|
|
if(!pCurDeviceObject)
|
|
{
|
|
GCK_DBG_ERROR_PRINT(("Filter Handle, 0x%0.8x, is not valid\n", pFilterHandle));
|
|
pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
goto complete_and_return;
|
|
}
|
|
|
|
//
|
|
// Get the device extension
|
|
//
|
|
pFilterExt = pFilterHandle->DeviceExtension;
|
|
ASSERT(GCK_DO_TYPE_FILTER == pFilterExt->ulGckDevObjType);
|
|
if(
|
|
GCK_STATE_STARTED != pFilterExt->eDeviceState &&
|
|
GCK_STATE_STOP_PENDING != pFilterExt->eDeviceState
|
|
)
|
|
{
|
|
GCK_DBG_ERROR_PRINT(("Device is stopped or removed or \n"));
|
|
pIrp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
NtStatus = STATUS_DEVICE_NOT_CONNECTED; //Causes ERROR_NOT_READY at Win32 level
|
|
goto complete_and_return;
|
|
}
|
|
//
|
|
// Determine which IOCTL and handle it
|
|
//
|
|
switch(uIoctl)
|
|
{
|
|
case IOCTL_GCK_SEND_COMMAND:
|
|
NtStatus = GCKF_ProcessCommands
|
|
(
|
|
pFilterExt,
|
|
((PCHAR)pvIoBuffer) + sizeof(PDEVICE_OBJECT), //skip the handle
|
|
uInLength-sizeof(PDEVICE_OBJECT),
|
|
TRUE
|
|
);
|
|
pIrp->IoStatus.Status = NtStatus;
|
|
break;
|
|
case IOCTL_GCK_SET_INTERNAL_POLLING:
|
|
{
|
|
if( uInLength < sizeof(GCK_SET_INTERNAL_POLLING_DATA) )
|
|
{
|
|
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
GCK_IP_FullTimePoll(pFilterExt, ((PGCK_SET_INTERNAL_POLLING_DATA)pvIoBuffer)->fEnable);
|
|
}
|
|
break;
|
|
}
|
|
case IOCTL_GCK_ENABLE_TEST_KEYBOARD:
|
|
{
|
|
if( uInLength < sizeof(GCK_ENABLE_TEST_KEYBOARD) )
|
|
{
|
|
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
NtStatus = GCKF_EnableTestKeyboard(pFilterExt, ((PGCK_ENABLE_TEST_KEYBOARD)pvIoBuffer)->fEnable, pIrpStack->FileObject);
|
|
}
|
|
break;
|
|
}
|
|
case IOCTL_GCK_BEGIN_TEST_SCHEME:
|
|
NtStatus = GCKF_BeginTestScheme
|
|
(
|
|
pFilterExt,
|
|
((PCHAR)pvIoBuffer) + sizeof(PDEVICE_OBJECT), //skip the handle
|
|
uInLength-sizeof(PDEVICE_OBJECT),
|
|
pIrpStack->FileObject
|
|
);
|
|
ASSERT(NT_SUCCESS(NtStatus));
|
|
break;
|
|
case IOCTL_GCK_UPDATE_TEST_SCHEME:
|
|
NtStatus = GCKF_UpdateTestScheme
|
|
(
|
|
pFilterExt,
|
|
((PCHAR)pvIoBuffer) + sizeof(PDEVICE_OBJECT), //skip the handle
|
|
uInLength-sizeof(PDEVICE_OBJECT),
|
|
pIrpStack->FileObject
|
|
);
|
|
ASSERT(NT_SUCCESS(NtStatus));
|
|
break;
|
|
case IOCTL_GCK_END_TEST_SCHEME:
|
|
NtStatus = GCKF_EndTestScheme(pFilterExt, pIrpStack->FileObject);
|
|
ASSERT(NT_SUCCESS(NtStatus));
|
|
break;
|
|
case IOCTL_GCK_BACKDOOR_POLL:
|
|
if( uInLength < sizeof(GCK_BACKDOOR_POLL_DATA) )
|
|
{
|
|
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
//Polling is asynchronous and the filter will deal with that,
|
|
//It is extremely important that we just return the status returned by the backdoor poll routine,
|
|
//and not complete the IRP
|
|
NtStatus = GCKF_BackdoorPoll(pFilterExt, pIrp, ((PGCK_BACKDOOR_POLL_DATA)pvIoBuffer)->ePollingMode);
|
|
|
|
//Make sure a poll is pending to the actual hardware
|
|
GCK_IP_OneTimePoll(pFilterExt);
|
|
ASSERT(NT_SUCCESS(NtStatus));
|
|
return NtStatus;
|
|
}
|
|
break;
|
|
case IOCTL_GCK_NOTIFY_FF_SCHEME_CHANGE: // Queue up IOCTL
|
|
NtStatus = GCKF_IncomingForceFeedbackChangeNotificationRequest(pFilterExt, pIrp);
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{ // Failed, IOCTL is completed below
|
|
pIrp->IoStatus.Status = NtStatus;
|
|
}
|
|
else
|
|
{ // Success, IOCTL was queued - don't complete
|
|
bCompleteRequest = FALSE;
|
|
}
|
|
break;
|
|
case IOCTL_GCK_END_FF_NOTIFICATION: // Complete the Queued FF Ioctls
|
|
NtStatus = pIrp->IoStatus.Status = GCKF_ProcessForceFeedbackChangeNotificationRequests(pFilterExt);
|
|
break;
|
|
case IOCTL_GCK_GET_FF_SCHEME_DATA:
|
|
NtStatus = GCKF_GetForceFeedbackData(pIrp, pFilterExt);
|
|
break;
|
|
case IOCTL_GCK_SET_WORKINGSET:
|
|
NtStatus = GCKF_SetWorkingSet(pFilterExt, ((GCK_SET_WORKINGSET*)pvIoBuffer)->ucWorkingSet);
|
|
break;
|
|
case IOCTL_GCK_QUERY_PROFILESET:
|
|
NtStatus = GCKF_QueryProfileSet(pIrp, pFilterExt);
|
|
break;
|
|
case IOCTL_GCK_LED_BEHAVIOUR:
|
|
NtStatus = GCKF_SetLEDBehaviour(pIrp, pFilterExt);
|
|
break;
|
|
case IOCTL_GCK_TRIGGER:
|
|
if ((uInLength < sizeof(GCK_TRIGGER_OUT)) || (uOutLength < sizeof(ULONG)))
|
|
{
|
|
NtStatus = pIrp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
NtStatus = GCKF_TriggerRequest(pIrp, pFilterExt);
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{ // Failed, IOCTL is completed below
|
|
pIrp->IoStatus.Status = NtStatus;
|
|
}
|
|
else
|
|
{ // Success, IOCTL was queued (or completed) - don't complete here
|
|
bCompleteRequest = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case IOCTL_GCK_GET_CAPS:
|
|
case IOCTL_GCK_ENABLE_DEVICE:
|
|
default:
|
|
pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
NtStatus = STATUS_NOT_SUPPORTED;
|
|
GCK_DBG_WARN_PRINT( ("Unknown IOCTL: 0x%0.8x\n", uIoctl) );
|
|
}
|
|
|
|
complete_and_return:
|
|
if (bCompleteRequest != FALSE)
|
|
{
|
|
// pIrp->IoStatus.Status = NtStatus; -- This might be nice investigate
|
|
IoCompleteRequest (pIrp, IO_NO_INCREMENT);
|
|
}
|
|
GCK_DBG_EXIT_PRINT(("Exiting GCK_ControlIoctl(2), Status: 0x%0.8x\n", NtStatus));
|
|
return NtStatus;
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** PDEVICE_OBJECT GCK_FindDeviceObject(IN PWSTR pwszInterfaceReq, IN ULONG uInLength)
|
|
**
|
|
** @mfunc Given a Win32 HID interface finds the corresponding PDO among the filter devices
|
|
**
|
|
** @rdesc pointer to the PDO on success, NULL if a match is not found
|
|
**
|
|
*************************************************************************************/
|
|
PDEVICE_OBJECT GCK_FindDeviceObject
|
|
(
|
|
IN PWSTR pwszInterfaceReq, // @parm pointer to Win32 interface name
|
|
IN ULONG uInLength // @parm length of interface string
|
|
)
|
|
{
|
|
NTSTATUS NtStatus;
|
|
PWSTR pInterfaces;
|
|
ULONG uIndex;
|
|
ULONG uStringLen;
|
|
BOOLEAN fMatchFound;
|
|
PDEVICE_OBJECT pCurDeviceObject;
|
|
ULONG uInWideChars;
|
|
|
|
PAGED_CODE();
|
|
|
|
GCK_DBG_ENTRY_PRINT(("Entering GCK_FindDeviceObject, pwszInterfaceReq = %ws, uInLength = %d\n", pwszInterfaceReq, uInLength));
|
|
//
|
|
// Get length of string including NULL, but don't overrun uInLength
|
|
//
|
|
uIndex = 0;
|
|
uStringLen = 0;
|
|
uInWideChars = uInLength/2; // uInLength is the number of BYTES not WideChars
|
|
while( uIndex < uInWideChars )
|
|
{
|
|
if( 0 == pwszInterfaceReq[uIndex++] )
|
|
{
|
|
uStringLen = uIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// if the string is not terminated or if it is a NULL string, return NULL for the device.
|
|
// plus every string starts with "\\.\" plus at least two or more chars
|
|
//
|
|
if( 6 > uStringLen ) return NULL;
|
|
|
|
//
|
|
// Walk through all known devices
|
|
//
|
|
pCurDeviceObject = Globals.pFilterObjectList;
|
|
while ( pCurDeviceObject )
|
|
{
|
|
//
|
|
// Get the interfaces for the PDO
|
|
//
|
|
NtStatus = IoGetDeviceInterfaces(
|
|
(LPGUID)&GUID_CLASS_INPUT,
|
|
FILTER_DEVICE_OBJECT_PDO(pCurDeviceObject),
|
|
0,
|
|
&pInterfaces
|
|
);
|
|
|
|
//
|
|
// If we have got the interfaces, then look for match
|
|
//
|
|
if( STATUS_SUCCESS == NtStatus )
|
|
{
|
|
fMatchFound=GCK_MatchReqPathtoInterfaces(pwszInterfaceReq, uStringLen, pInterfaces);
|
|
ExFreePool(pInterfaces);
|
|
if(fMatchFound)
|
|
{
|
|
GCK_DBG_EXIT_PRINT(("Exiting GCK_FindDeviceObject - match found returning 0x%0.8x\n", pCurDeviceObject));
|
|
return pCurDeviceObject;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Advance to next known device
|
|
//
|
|
pCurDeviceObject = NEXT_FILTER_DEVICE_OBJECT(pCurDeviceObject);
|
|
}
|
|
|
|
//
|
|
// If we are here, there is no match
|
|
//
|
|
GCK_DBG_EXIT_PRINT(("Exiting GCK_FindDeviceObject - no match found returning NULL\n"));
|
|
return NULL;
|
|
}
|
|
|
|
#define UPPERCASE(_x_) (((L'a'<=_x_) && (L'z'>=_x_)) ? ((_x_) - (L'a'-L'A')) : _x_)
|
|
/***********************************************************************************
|
|
**
|
|
** BOOLEAN GCK_MatchReqPathtoInterfaces(IN PWSTR pwszPath, IN ULONG uStringLen, IN PWSTR pmwszInterfaces)
|
|
**
|
|
** @mfunc Determines if a Win32 Path matches any of the Interfaces. This replaces a match between
|
|
** String and a Multi-String. The previous was not sufficient (even though the caller tried
|
|
** to compensate.) The new algorithm is to find the last '\\' in each string before comparing
|
|
** them. It is still a string against any of a multi-string though.
|
|
**
|
|
** @rdesc TRUE if a match is found, FALSE otherwise
|
|
**
|
|
*************************************************************************************/
|
|
BOOLEAN GCK_MatchReqPathtoInterfaces
|
|
(
|
|
IN PWSTR pwszPath, // @parm String to find match
|
|
IN ULONG uStringLen, // @parm length of string in WCHARs
|
|
IN PWSTR pmwszInterfaces // @parm MutliString
|
|
)
|
|
{
|
|
PWSTR pwszCurInterface;
|
|
PWSTR pwszPathInterface;
|
|
ULONG uCharIndex;
|
|
ULONG uCurIntefaceLen;
|
|
ULONG uDiff;
|
|
|
|
GCK_DBG_ENTRY_PRINT(("Entering GCK_MatchReqPathtoInterfaces, pwszPath = \'%ws\'\n, uStringLen = %d, pmwszStrings = \'%ws\'", pwszPath, uStringLen, pmwszInterfaces));
|
|
|
|
//
|
|
// Find last '\\' in pwszPath and set pszPathInterface to the next character
|
|
//
|
|
pwszPathInterface = pwszPath;
|
|
uCharIndex = 0;
|
|
while( pwszPathInterface[uCharIndex] && (uCharIndex != uStringLen)) uCharIndex++; //go to end
|
|
while( uCharIndex && (L'\\' != pwszPathInterface[uCharIndex]) ) uCharIndex--; //go to last '\\'
|
|
ASSERT(uCharIndex < uStringLen);
|
|
pwszPathInterface += uCharIndex+1; //skip last '\\'
|
|
|
|
GCK_DBG_TRACE_PRINT(("Path to compare is %ws\n", pwszPathInterface));
|
|
|
|
//
|
|
// check if the szString matches any of the strings in mszStrings
|
|
//
|
|
pwszCurInterface = pmwszInterfaces;
|
|
|
|
//
|
|
// Loop over all strings in pmwszStrings
|
|
//
|
|
do
|
|
{
|
|
//Find last '\\'
|
|
uCharIndex = 0;
|
|
while( pwszCurInterface[uCharIndex]) uCharIndex++; //go to end
|
|
uCurIntefaceLen = uCharIndex; //save string length
|
|
while( uCharIndex && (L'\\' != pwszCurInterface[uCharIndex]) ) uCharIndex--; //go to last '\\'
|
|
pwszCurInterface += uCharIndex+1;
|
|
uCurIntefaceLen -= uCharIndex+1; //length after we skip some stuff
|
|
|
|
GCK_DBG_TRACE_PRINT(("Comparing path with %ws\n", pwszCurInterface));
|
|
|
|
//
|
|
// look for differences in each string.
|
|
//
|
|
uCharIndex = 0;
|
|
uDiff = 0;
|
|
do
|
|
{
|
|
//
|
|
// Check if characters match
|
|
//
|
|
if( UPPERCASE(pwszCurInterface[uCharIndex]) != UPPERCASE(pwszPathInterface[uCharIndex]) )
|
|
{
|
|
uDiff++; //increment number of differences
|
|
break; //One difference is enough
|
|
}
|
|
} while( (pwszCurInterface[uCharIndex] != 0) && (pwszCurInterface[uCharIndex++] != '}') );
|
|
|
|
//
|
|
// Check for match
|
|
//
|
|
if( 0 == uDiff )
|
|
{
|
|
GCK_DBG_EXIT_PRINT(("Exiting GCK_MatchReqPathtoInterfaces - match found returning TRUE\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// move to the next string in list
|
|
//
|
|
pwszCurInterface += uCurIntefaceLen;
|
|
} while(pwszCurInterface[0] != 0); //continue while there are more strings
|
|
|
|
//
|
|
// if we fell out we didn't find a match
|
|
//
|
|
GCK_DBG_EXIT_PRINT(("Exiting GCK_MatchReqPathtoInterfaces - no match found returning FALSE\n"));
|
|
return FALSE;
|
|
}
|