Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1683 lines
55 KiB

/*++
Copyright (c) 1998 - 1999 Microsoft Corporation
Module Name:
pnp.c
Abstract: This module contains routines Generate the HID report and
configure the joystick.
Environment:
Kernel mode
@@BEGIN_DDKSPLIT
Author:
Eliyas Yakub (Mar, 11, 1997)
Revision History:
Updated by Eliyas on Feb 5 1998
@@END_DDKSPLIT
--*/
#include "hidgame.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, HGM_DriverInit)
#pragma alloc_text (PAGE, HGM_SetupButtons)
#pragma alloc_text (PAGE, HGM_MapAxesFromDevExt)
#pragma alloc_text (PAGE, HGM_GenerateReport)
#pragma alloc_text (PAGE, HGM_JoystickConfig)
#pragma alloc_text (PAGE, HGM_InitAnalog)
/* Sample only functions */
#ifdef CHANGE_DEVICE
#pragma alloc_text (PAGE, HGM_ChangeHandler)
#pragma alloc_text (PAGE, HGM_DeviceChanged)
#endif /* CHANGE_DEVICE */
#endif
/*
* A few look up tables to translate the JOY_HWS_* flags into axis masks.
* These flags allow any axis to be polled on any of the four axis bits in
* the gameport. For example, the X axis on a standard joystick is found on
* bit 0 (LSB) and the Y axis is on bit 1; however many steering wheel/pedal
* controllers have X on bit 0 but Y on bit 2. Although very few of these
* combinations are known to be used, supporting all the flags only causes a
* little extra work on setup. For each axis, there are three flags, one for
* each of the possible non-standard bit masks. Since it is possible that
* more than one of these may be set the invalid combinations are marked so
* that they can be refused.
*/
#define NA ( 0x80 )
/*
* Short versions of bit masks for axes
*/
#define X1 AXIS_X
#define Y1 AXIS_Y
#define X2 AXIS_R
#define Y2 AXIS_Z
/*
* Per axis flag masks and look up tables.
* In each case, combinations with more than one bit set are invalid
*/
#define XMAPBITS (JOY_HWS_XISJ2Y | JOY_HWS_XISJ2X | JOY_HWS_XISJ1Y)
/*
* 0 0 0 0001
* 0 0 1 0010
* 0 1 0 0100
* 1 0 0 1000
*/
static const unsigned char XLU[8] = { X1,Y1,X2,NA,Y2,NA,NA,NA };
#define XMAPSHFT 7
#define YMAPBITS (JOY_HWS_YISJ2Y | JOY_HWS_YISJ2X | JOY_HWS_YISJ1X)
/* 0 0 0 0010
* 0 0 1 0001
* 0 1 0 0100
* 1 0 0 1000
*/
static const unsigned char YLU[8] = { Y1,X1,X2,NA,Y2,NA,NA,NA };
#define YMAPSHFT 10
#define RMAPBITS (JOY_HWS_RISJ2Y | JOY_HWS_RISJ1X | JOY_HWS_RISJ1Y)
/* 0 0 0 0100
* 0 0 1 0010
* 0 1 0 0001
* 1 0 0 1000
*/
static const unsigned char RLU[8] = { X2,Y1,X1,NA,Y2,NA,NA,NA };
#define RMAPSHFT 20
#define ZMAPBITS (JOY_HWS_ZISJ2X | JOY_HWS_ZISJ1X | JOY_HWS_ZISJ1Y)
/* 0 0 0 1000
* 0 0 1 0010
* 0 1 0 0001
* 1 0 0 0100
*/
static const unsigned char ZLU[8] = { Y2,Y1,X1,NA,X2,NA,NA,NA };
#define ZMAPSHFT 13
#define POVMAPBITS (JOY_HWS_POVISJ2X | JOY_HWS_POVISJ1X | JOY_HWS_POVISJ1Y)
/*
* POV is the same as Z but with a larger shift
*/
#define POVMAPSHFT 16
#undef X1
#undef Y1
#undef X2
#undef Y2
/*
* This translates from an axis bitmask to an axis value index. The elements
* used should be as follows (X marks unsed) { X, 0, 1, X, 2, X, X, X, 3 }.
*/
static const unsigned char cAxisIndexTable[9] = { 0, 0, 1, 0, 2, 0, 0, 0, 3 };
typedef enum _POV1
{
P1_NULL = 0x80,
P1_0,
P1_90,
P1_180,
P1_270
} POV1;
typedef enum _POV2
{
P2_NULL = 0xc0,
P2_0,
P2_90,
P2_180,
P2_270
} POV2;
#define POV_MASK ((unsigned char)(~(P1_NULL | P2_NULL)))
/*
* Look up tables for button combos
* Buttons are zero based so use P1_NULL for a zero input so we don't have to
* special case it as a do nothing button.
* The 7th Button can be mapped either from it's unique combination or as
* foreward on a second POV being read as buttons 7 - 10.
*/
static const unsigned char c1PComboLU[] = { P1_NULL,0, 1, P1_270,
2, 4, 8, P1_180,
3, 5, 7, P1_90,
9, 6, 6, P1_0 };
static const unsigned char c2PComboLU[] = { P1_NULL,0, 1, P1_270,
2, 4, P2_180, P1_180,
3, 5, P2_90, P1_90,
P2_270, 6, P2_0, P1_0 };
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HGM_DriverInit |
*
* Perform global initialization.
* <nl>This is called from DriverEntry. Try to initialize a CPU
* specific timer but if it fails set up default
*
* @rvalue STATUS_SUCCESS | success
* @rvalue STATUS_UNSUCCESSFUL | not success
*
*****************************************************************************/
NTSTATUS EXTERNAL
HGM_DriverInit()
{
NTSTATUS ntStatus = STATUS_SUCCESS;
if( !HGM_CPUCounterInit() )
{
LARGE_INTEGER QPCFrequency;
KeQueryPerformanceCounter( &QPCFrequency );
if( ( QPCFrequency.HighPart == 0 )
&& ( QPCFrequency.LowPart <= 10000 ) )
{
ntStatus = STATUS_UNSUCCESSFUL;
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("QPC at %I64u Hz is unusable",
QPCFrequency.QuadPart ));
}
else
{
Global.CounterScale = CALCULATE_SCALE( QPCFrequency.QuadPart );
Global.ReadCounter = (COUNTER_FUNCTION)&KeQueryPerformanceCounter;
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
("QPC at %I64u Hz used with scale %d",
QPCFrequency.QuadPart, Global.CounterScale ));
}
}
return ntStatus;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HGM_SetupButtons |
*
* Use the flags in the DeviceExtension to check and set up buttons.
* <nl>This is called both from HGM_JoystickConfig to validate the
* configuration and HGM_GenerateReport to prepare for polling.
*
* @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
*
* Pointer to the minidriver device extension
*
* @rvalue STATUS_SUCCESS | success
* @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | The configuration is invalid
*
*****************************************************************************/
NTSTATUS INTERNAL
HGM_SetupButtons
(
IN OUT PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
if( DeviceExtension->fSiblingFound )
{
if( DeviceExtension->nButtons > 2 )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_SetupButtons: failing config of sibling device with %u buttons",\
DeviceExtension->nButtons));
}
if( DeviceExtension->HidGameOemData.OemData[1].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_SetupButtons: failing config of sibling device with combo buttons" ));
}
}
else
{
if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
{
if( DeviceExtension->nButtons > MAX_BUTTONS )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_SetupButtons: failing config of button combo device with %u buttons",\
DeviceExtension->nButtons));
}
}
else
{
if( DeviceExtension->nButtons > 4 )
{
if( DeviceExtension->resistiveInputMask & AXIS_R )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_SetupButtons: failing config of device with R axis and %u buttons",\
DeviceExtension->nButtons));
}
else
{
/*
* 5th button always read from R axis.
* Set the inital on/off boundary low
*/
DeviceExtension->resistiveInputMask |= AXIS_R;
DeviceExtension->button5limit = 2;
}
if( DeviceExtension->nButtons > 5 )
{
if( DeviceExtension->resistiveInputMask & AXIS_Z )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_SetupButtons: failing config of device with Z axis and %u buttons",\
DeviceExtension->nButtons));
}
else
{
/*
* 6th button always read from Z axis.
* Set the inital on/off boundary low
*/
DeviceExtension->resistiveInputMask |= AXIS_Z;
DeviceExtension->button6limit = 2;
}
if( DeviceExtension->nButtons > 6 )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_SetupButtons: failing config of device with %u buttons",\
DeviceExtension->nButtons));
}
}
}
}
}
return( ntStatus );
} /* HGM_SetupButtons */
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HGM_MapAxesFromDevExt |
*
* Use the flags in the DeviceExtension to generate mappings for each
* axis.
* <nl>This is called both from HGM_JoystickConfig to validate the
* configuration and HGM_GenerateReport to use the axis maps.
*
*
* @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
*
* Pointer to the minidriver device extension
*
* @rvalue STATUS_SUCCESS | success
* @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | The configuration is invalid
*
*****************************************************************************/
NTSTATUS EXTERNAL
HGM_MapAxesFromDevExt
(
IN OUT PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS ntStatus;
ULONG dwFlags;
int nAxis;
UCHAR AxisMask;
ntStatus = STATUS_SUCCESS;
dwFlags = DeviceExtension->HidGameOemData.OemData[(DeviceExtension->fSiblingFound!=0)].joy_hws_dwFlags;
HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2,\
("HGM_MapAxesFromDevExt: - - - dwFlags=0x%x - - -", dwFlags));
#define XIS (0)
#define YIS (1)
#define ZIS (2)
#define RIS (3)
/*
* Check X and Y last as Z, R and POV must not overlap
* The are no flags to indicate the presence of X or Y so if they
* overlap, this indicates that they are not used,
*/
DeviceExtension->resistiveInputMask = 0;
for( nAxis=MAX_AXES; nAxis>=0; nAxis-- )
{
DeviceExtension->AxisMap[nAxis] = INVALID_INDEX;
}
nAxis = 0;
DeviceExtension->povMap = INVALID_INDEX;
if( dwFlags & JOY_HWS_HASZ )
{
AxisMask = ZLU[(dwFlags & ZMAPBITS) >> ZMAPSHFT];
if( AxisMask >= NA )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: Z axis mapping error dwFlags=0x%x",\
dwFlags));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
}
else
{
nAxis = 1;
DeviceExtension->resistiveInputMask = AxisMask;
DeviceExtension->AxisMap[ZIS] = cAxisIndexTable[AxisMask];
}
}
if( dwFlags & JOY_HWS_HASR )
{
AxisMask = RLU[(dwFlags & RMAPBITS) >> RMAPSHFT];
if( AxisMask >= NA )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: R axis mapping error dwFlags=0x%x",\
dwFlags));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
}
else
{
if( DeviceExtension->resistiveInputMask & AxisMask )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR, \
("HGM_MapAxesFromDevExt: R axis mapped to same as Z axis"));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
}
else
{
nAxis++;
DeviceExtension->resistiveInputMask |= AxisMask;
DeviceExtension->AxisMap[RIS] = cAxisIndexTable[AxisMask];
}
}
}
if( dwFlags & JOY_HWS_HASPOV )
{
switch( dwFlags & ( JOY_HWS_POVISPOLL | JOY_HWS_POVISBUTTONCOMBOS ) )
{
case 0:
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: POV is not polled or button combo"));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
break;
case JOY_HWS_POVISBUTTONCOMBOS:
break;
case JOY_HWS_POVISPOLL:
AxisMask = ZLU[(dwFlags & POVMAPBITS) >> POVMAPSHFT];
if( AxisMask >= NA )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: POV axis mapping error dwFlags=0x%x",\
dwFlags));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
}
else
{
if( DeviceExtension->resistiveInputMask & AxisMask )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: POV axis mapped to same as Z or R axis") );
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
}
else
{
DeviceExtension->resistiveInputMask |= AxisMask;
DeviceExtension->povMap = cAxisIndexTable[AxisMask];
}
}
break;
case JOY_HWS_POVISPOLL | JOY_HWS_POVISBUTTONCOMBOS:
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: POV reports button combo and polled"));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
break;
}
}
else if( dwFlags & ( JOY_HWS_POVISPOLL | JOY_HWS_POVISBUTTONCOMBOS ) )
{
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: non-existant POV is polled or button combo"));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
}
AxisMask = XLU[( dwFlags & XMAPBITS ) >> XMAPSHFT];
if( AxisMask >= NA )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: X axis mapping error dwFlags=0x%x",\
dwFlags));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
}
else
{
if( DeviceExtension->resistiveInputMask & AxisMask )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_WARN,\
("HGM_MapAxesFromDevExt: X axis mapped to same as another axis") );
}
else
{
nAxis++;
DeviceExtension->resistiveInputMask |= AxisMask;
DeviceExtension->AxisMap[XIS] = cAxisIndexTable[AxisMask];
}
}
AxisMask = YLU[( dwFlags & YMAPBITS ) >> YMAPSHFT];
if( AxisMask >= NA )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: Y axis mapping error dwFlags=0x%x",\
dwFlags));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
}
else
{
if( DeviceExtension->resistiveInputMask & AxisMask )
{
HGM_DBGPRINT( FILE_HIDJOY | HGM_WARN,\
("HGM_MapAxesFromDevExt: Y axis mapped to same as another axis") );
}
else
{
nAxis++;
DeviceExtension->resistiveInputMask |= AxisMask;
DeviceExtension->AxisMap[YIS] = cAxisIndexTable[AxisMask];
}
}
#undef XIS
#undef YIS
#undef ZIS
#undef RIS
#undef NA
/*
* Don't fail for this if CHANGE_DEVICE is defined because an exposed
* sibling will always have an nAxis of zero.
*/
#ifdef CHANGE_DEVICE
if( DeviceExtension->nAxes )
{
#endif /* CHANGE_DEVICE */
if( nAxis != DeviceExtension->nAxes )
{
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR ;
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("HGM_MapAxesFromDevExt: nAxis(%d) != DeviceExtension->nAxes(%d)", \
nAxis, (int) DeviceExtension->nAxes));
}
#ifdef CHANGE_DEVICE
}
else
{
/*
* This must be an exposed sibling so store the calculated nAxis and
* a nButton just to look different.
*/
DeviceExtension->nAxes = (USHORT)nAxis;
DeviceExtension->nButtons = MAX_BUTTONS;
}
#endif /* CHANGE_DEVICE */
HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE,\
("HGM_MapAxesFromDevExt: uResistiveInputMask=0x%x",\
DeviceExtension->resistiveInputMask) );
if( NT_SUCCESS(ntStatus) )
{
ntStatus = HGM_SetupButtons( DeviceExtension );
}
return( ntStatus );
} /* HGM_MapAxesFromDevExt */
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HGM_GenerateReport |
*
* Generates a hid report descriptor for a n-axis, m-button joystick,
* depending on number of buttons and joy_hws_flags field.
*
* @parm IN PDEVICE_OBJECT | DeviceObject |
*
* Pointer to the device object
*
* @parm IN OUT UCHAR * | rgGameReport[MAXBYTES_GAME_REPORT] |
*
* Array that receives the HID report descriptor
*
* @parm OUT PUSHORT | pCbReport |
*
* Address of a short integer that receives size of
* HID report descriptor.
*
* @rvalue STATUS_SUCCESS | success
* @rvalue STATUS_BUFFER_TOO_SMALL | Need more memory for HID descriptor
*
*****************************************************************************/
NTSTATUS INTERNAL
HGM_GenerateReport
(
IN PDEVICE_OBJECT DeviceObject,
OUT UCHAR rgGameReport[MAXBYTES_GAME_REPORT],
OUT PUSHORT pCbReport
)
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION DeviceExtension;
UCHAR *pucReport;
int Idx;
int UsageIdx;
ULONG dwFlags;
POEMDATA OemData;
int InitialAxisMappings[MAX_AXES];
typedef struct _USAGES
{
UCHAR UsagePage;
UCHAR Usage;
} USAGES, *PUSAGE;
typedef struct _JOYCLASSPARAMS
{
UCHAR TopLevelUsage;
USAGES Usages[MAX_AXES];
} JOYCLASSPARAMS, *PJOYCLASSPARAMS;
PJOYCLASSPARAMS pJoyParams;
/*
* Canned parameters for devices
* The top-level usage must be either HID_USAGE_GENERIC_JOYSTICK or
* HID_USAGE_GENERIC_GAMEPAD in order for the device to be treated as a
* game controller.
* The poll limits are specified in uSecs so the value stored here is 1000000/x
*/
JOYCLASSPARAMS JoystickParams =
{
HID_USAGE_GENERIC_JOYSTICK,
{ { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
{ HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
};
JOYCLASSPARAMS GamepadParams =
{
HID_USAGE_GENERIC_GAMEPAD,
{ { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
{ HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
};
JOYCLASSPARAMS CarCtrlParams =
{
HID_USAGE_GENERIC_JOYSTICK,
{ { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
{ HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
};
JOYCLASSPARAMS FlightYokeParams =
{
HID_USAGE_GENERIC_JOYSTICK,
{ { HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_X} ,
{ HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_Y} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_THROTTLE} ,
{ HID_USAGE_PAGE_SIMULATION, HID_USAGE_SIMULATION_RUDDER} }
};
PAGED_CODE();
HGM_DBGPRINT( FILE_HIDJOY | HGM_FENTRY,\
("HGM_GenerateReport(ucIn=0x%x,DeviceObject=0x%x)",\
rgGameReport, DeviceObject) );
/*
* Get a pointer to the device extension
*/
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
/*
* Although the axes have already been validated and mapped in
* HGM_JoystickConfig this function destroys the mapping when it compacts
* the axes towards the start of the descriptor report. Since this
* function will be called once to find the descriptor length and then
* again to read the report, the mappings are regenerated again each
* time through. Although this results in the parameters being
* interpreted three times (for validation, descriptor size and
* descriptor content) it avoids the possibility of a discrepancy in
* implementation of separate functions.
*/
ntStatus = HGM_MapAxesFromDevExt( DeviceExtension );
ASSERTMSG( "HGM_GenerateReport:", ntStatus == STATUS_SUCCESS );
pucReport = rgGameReport;
dwFlags = DeviceExtension->HidGameOemData.OemData[(DeviceExtension->fSiblingFound!=0)].joy_hws_dwFlags;
/*
* What manner of beast have we ?
*/
if( dwFlags & JOY_HWS_ISGAMEPAD )
{
pJoyParams = &GamepadParams;
}
else if( dwFlags & JOY_HWS_ISYOKE )
{
pJoyParams = &FlightYokeParams;
}
else if( dwFlags & JOY_HWS_ISCARCTRL )
{
pJoyParams = &CarCtrlParams;
}
else
{
pJoyParams = &JoystickParams;
}
#define NEXT_BYTE( pReport, Data ) \
ASSERTMSG( "HGM_GenerateReport:", pReport+sizeof(UCHAR)-rgGameReport < MAXBYTES_GAME_REPORT ); \
*pReport++ = Data;
#define NEXT_LONG( pReport, Data ) \
ASSERTMSG( "HGM_GenerateReport:", pReport+sizeof(ULONG)-rgGameReport < MAXBYTES_GAME_REPORT); \
*(((LONG UNALIGNED*)(pReport))++) = Data;
#define ITEM_DEFAULT 0x00 /* Data, Array, Absolute, No Wrap, Linear, Preferred State, Has no NULL */
#define ITEM_VARIABLE 0x02 /* as ITEM_DEFAULT but value is a variable, not an array */
#define ITEM_HASNULL 0x40 /* as ITEM_DEFAULT but values out of range are considered NULL */
#define ITEM_ANALOG_AXIS ITEM_VARIABLE
#define ITEM_DIGITAL_POV (ITEM_VARIABLE|ITEM_HASNULL)
#define ITEM_BUTTON ITEM_VARIABLE
#define ITEM_PADDING 0x01 /* Constant (nothing else applies) */
/* USAGE_PAGE (Generic Desktop) */
NEXT_BYTE(pucReport, HIDP_GLOBAL_USAGE_PAGE_1);
NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
/* USAGE (Joystick | GamePad ) */
NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_1);
NEXT_BYTE(pucReport, pJoyParams->TopLevelUsage);
/* Logical Min is the smallest value that could be produced by a poll */
NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MIN_4);
NEXT_LONG(pucReport, 0 );
/* Logical Max is the largest value that could be produced by a poll */
NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MAX_4);
NEXT_LONG(pucReport, AXIS_FULL_SCALE );
/* Start a Linked collection */
/*
* Since this is a generic driver we know knothing about the physical
* distribution of controls on the device so we put everything in a
* single collection. If, for instance, we knew that some buttons were
* on the base and some on the stick we could better describe them by
* reporting them in separate collections.
*/
NEXT_BYTE(pucReport, HIDP_MAIN_COLLECTION);
NEXT_BYTE(pucReport, 0x0 );
/* Define one axis at a time */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_COUNT_1);
NEXT_BYTE(pucReport, 0x1);
/* Each axis is a 32 bits value */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 8 * sizeof(ULONG) );
/*
* Do the axis
* Although HID could cope with the "active" axes being mixed with the
* dummy ones, it makes life simpler to move them to the start.
* Pass through all the axis maps generated by HGM_JoystickConfig
* and map all the active ones into the descriptor, copying the usages
* appropriate for the type of device.
* Since a polled POV is nothing more than a different interpretation
* of axis data, this is added after any axes.
*/
C_ASSERT( sizeof( InitialAxisMappings ) == sizeof( DeviceExtension->AxisMap ) );
RtlCopyMemory( InitialAxisMappings, DeviceExtension->AxisMap, sizeof( InitialAxisMappings ) );
Idx = 0;
for( UsageIdx = 0; UsageIdx < MAX_AXES; UsageIdx++ )
{
if( InitialAxisMappings[UsageIdx] >= INVALID_INDEX )
{
continue;
}
DeviceExtension->AxisMap[Idx] = InitialAxisMappings[UsageIdx];
NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
NEXT_BYTE(pucReport, pJoyParams->Usages[UsageIdx].Usage);
NEXT_BYTE(pucReport, 0x0);
NEXT_BYTE(pucReport, pJoyParams->Usages[UsageIdx].UsagePage);
NEXT_BYTE(pucReport, 0x0);
/* Data Field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_ANALOG_AXIS);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Idx=%d, UsageIdx=%d, Mapping=%d, Usage=%02x, Page=%02x",\
Idx, UsageIdx, DeviceExtension->AxisMap[UsageIdx], \
pJoyParams->Usages[UsageIdx].Usage, pJoyParams->Usages[UsageIdx].UsagePage ) ) ;
Idx++;
}
if( dwFlags & JOY_HWS_POVISPOLL )
{
/*
* A polled POV is just the same as an axis.
* Note, we have already checked that there is an axis for use as the POV.
* Also, this type of POV can be distinguished from a digital POV by it's
* lack of a NULL value.
*/
NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
NEXT_BYTE(pucReport, 0x0);
NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
NEXT_BYTE(pucReport, 0x0);
/* Data Field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_ANALOG_AXIS);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Idx=%d, Set to polled POV", Idx ) ) ;
Idx++;
}
/*
* Now fill in any remaining axis values as dummys
*/
while( Idx < MAX_AXES )
{
/* Constant Field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Idx=%d, Set to constant field", Idx ) ) ;
Idx++;
}
/*
* Now move on to the byte sized fields
*/
if( dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
{
/*
* Redefine the logical and physical ranges from now on
* A digital POV has a NULL value (a value outside the logical range)
* when the POV is centered. To make life easier call the NULL value
* zero, so the logical range is from 1 to 4.
/* Logical Min */
NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MIN_1);
NEXT_BYTE(pucReport, 1 );
/* Logical Max */
NEXT_BYTE(pucReport, HIDP_GLOBAL_LOG_MAX_1);
NEXT_BYTE(pucReport, 4 );
/*
* report for digital POV is 3 bits data plus 5 constant bits to fill
* the byte.
*/
NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
NEXT_BYTE(pucReport, 0x0);
NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
NEXT_BYTE(pucReport, 0x0);
/* Data Field */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x3);
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_DIGITAL_POV);
/* top 5 bits constant */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x5);
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:First button combo POV is on" ) ) ;
if( dwFlags & JOY_HWS_HASPOV2 )
{
NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
NEXT_BYTE(pucReport, HID_USAGE_GENERIC_HATSWITCH);
NEXT_BYTE(pucReport, 0x0);
NEXT_BYTE(pucReport, HID_USAGE_PAGE_GENERIC);
NEXT_BYTE(pucReport, 0x0);
/* Data Field */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x3);
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_DIGITAL_POV);
/* top 5 bits constant */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x5);
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Second button combo POV is on" ) ) ;
}
else
{
/* 8 bits of constant data instead of second POV */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x8);
/* Constant Field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:No second button combo POV" ) ) ;
}
}
else
{
/* 16 bits of constant data instead of button combo POVs */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x10);
/* Constant Field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Button combo POV are off" ) ) ;
}
/*
* Now the buttons
*/
for( Idx = 0x0; Idx < DeviceExtension->nButtons; Idx++ )
{
/* Report size is 1 bit for button */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x1);
NEXT_BYTE(pucReport, HIDP_LOCAL_USAGE_4);
NEXT_BYTE(pucReport, (UCHAR)(Idx + 1) );
NEXT_BYTE(pucReport, 0x0);
NEXT_BYTE(pucReport, HID_USAGE_PAGE_BUTTON);
NEXT_BYTE(pucReport, 0x0);
/* Data field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_BUTTON);
/* 7 bits of constant data */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, 0x7);
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Button %u on",Idx ) ) ;
}
if( Idx < MAX_BUTTONS )
{
/* Constant report for 8 * unused buttons bits */
NEXT_BYTE(pucReport, HIDP_GLOBAL_REPORT_SIZE);
NEXT_BYTE(pucReport, (UCHAR)((MAX_BUTTONS-Idx)*8) );
/* Constant Field */
NEXT_BYTE(pucReport, HIDP_MAIN_INPUT_1);
NEXT_BYTE(pucReport, ITEM_PADDING);
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT, \
("HGM_GenerateReport:Last %u buttons off",MAX_BUTTONS-Idx ) ) ;
}
/* End of collection, We're done ! */
NEXT_BYTE(pucReport, HIDP_MAIN_ENDCOLLECTION);
#undef NEXT_BYTE
#undef NEXT_LONG
if( pucReport - rgGameReport > MAXBYTES_GAME_REPORT)
{
ntStatus = STATUS_BUFFER_TOO_SMALL;
*pCbReport = 0x0;
RtlZeroMemory(rgGameReport, sizeof(rgGameReport));
} else
{
*pCbReport = (USHORT) (pucReport - rgGameReport);
ntStatus = STATUS_SUCCESS;
}
HGM_DBGPRINT( FILE_HIDJOY | HGM_GEN_REPORT,\
("HGM_GenerateReport: ReportSize=0x%x",\
*pCbReport) );
HGM_EXITPROC(FILE_HIDJOY | HGM_FEXIT_STATUSOK, "HGM_GenerateReport", ntStatus);
return ( ntStatus );
} /* HGM_GenerateReport */
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HGM_JoystickConfig |
*
* Check that the configuration is valid whilst there is still time
* to refuse it.
* <nl>HGM_GenerateReport uses the results generated here if the
* settings are OK.
*
* @parm IN PDEVICE_OBJECT | DeviceObject |
*
* Pointer to the device object
*
* @rvalue STATUS_SUCCESS | success
* @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Invalid configuration specified
*
*****************************************************************************/
NTSTATUS INTERNAL
HGM_JoystickConfig
(
IN PDEVICE_OBJECT DeviceObject
)
{
PDEVICE_EXTENSION DeviceExtension;
POEMDATA OemData;
NTSTATUS ntStatus;
int Idx;
PAGED_CODE();
HGM_DBGPRINT( FILE_HIDJOY | HGM_FENTRY,\
("HGM_JoystickConfig(DeviceObject=0x%x)",\
DeviceObject) );
/*
* Get a pointer to the device extension
*/
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
ntStatus = HGM_MapAxesFromDevExt( DeviceExtension );
if( DeviceExtension->ReadAccessorDigital )
{
DeviceExtension->ScaledTimeout = AXIS_TIMEOUT;
}
else
{
/*
* Calculate time thresholds for analog device
*/
if( ( DeviceExtension->HidGameOemData.OemData[0].Timeout < ANALOG_POLL_TIMEOUT_MIN )
||( DeviceExtension->HidGameOemData.OemData[0].Timeout > ANALOG_POLL_TIMEOUT_MAX ) )
{
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
("Ignoring out of range timeout: %u uSecs",\
DeviceExtension->HidGameOemData.OemData[0].Timeout));
DeviceExtension->ScaledTimeout = (ULONG)( ( (ULONGLONG)ANALOG_POLL_TIMEOUT_DFT
* (ULONGLONG)(AXIS_FULL_SCALE<<SCALE_SHIFT) )
/ (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX );
}
else
{
DeviceExtension->ScaledTimeout = (ULONG)( ( (ULONGLONG)DeviceExtension->HidGameOemData.OemData[0].Timeout
* (ULONGLONG)(AXIS_FULL_SCALE<<SCALE_SHIFT) )
/ (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX );
}
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE,\
("ScaledTimeout: %u",\
DeviceExtension->ScaledTimeout));
/*
* Use one quarter of the minimum poll timeout as a starting value
* for the time between two polls which will be considered to have
* been interrupted.
*/
DeviceExtension->ScaledThreshold = (ULONG)( ( (ULONGLONG)ANALOG_POLL_TIMEOUT_MIN
* (ULONGLONG)AXIS_FULL_SCALE )
/ (ULONGLONG)ANALOG_POLL_TIMEOUT_MAX )>>2;
}
/*
* Set initial values of LastGoodAxis so that the device will not show
* up as present until we get at least one valid poll.
*/
for( Idx = MAX_AXES; Idx >= 0; Idx-- )
{
DeviceExtension->LastGoodAxis[Idx] = AXIS_TIMEOUT;
}
HGM_EXITPROC(FILE_HIDJOY | HGM_FEXIT_STATUSOK, "HGM_JoystickConfig", ntStatus);
return ntStatus;
} /* HGM_JoystickConfig */
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func NTSTATUS | HGM_InitAnalog |
*
* Check that the configuration is valid whilst there is still time
* to refuse it.
* <nl>Detect and validate sibling relationships and call
* HGM_JoystickConfig for the rest of the work.
*
* @parm IN PDEVICE_OBJECT | DeviceObject |
*
* Pointer to the device object
*
* @rvalue STATUS_SUCCESS | success
* @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Invalid configuration specified
*
*****************************************************************************/
/*
* Disable warning for variable used before set as it is hard for a compiler
* to see that the use of DeviceExtension_Sibling is gated by a flag which
* can only be set after DeviceExtension_Sibling is initialized.
*/
#pragma warning( disable:4701 )
NTSTATUS EXTERNAL
HGM_InitAnalog
(
IN PDEVICE_OBJECT DeviceObject
)
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION DeviceExtension;
PDEVICE_EXTENSION DeviceExtension_Sibling;
PLIST_ENTRY pEntry;
#define ARE_WE_RELATED(_x_, _y_) \
( \
(_x_)->GameContext == (_y_)->GameContext && \
(_x_)->WriteAccessor == (_y_)->WriteAccessor && \
(_x_)->ReadAccessor == (_y_)->ReadAccessor \
)
PAGED_CODE ();
/*
* Get a pointer to the device extension
*/
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
/*
* No modifications to the Global List while we are looking at it
*/
ExAcquireFastMutex (&Global.Mutex);
/*
* For two joysticks interface two fdos are created to service them
* but physically they both share the same port.
* For the second sibling certain extra rules must be applied so we
* search our list of devices for another device using the same port
* and if we find one mark this one as a sibling.
*/
for(pEntry = Global.DeviceListHead.Flink;
pEntry != &Global.DeviceListHead;
pEntry = pEntry->Flink)
{
/*
* Obtain the device Extension of the Sibling
*/
DeviceExtension_Sibling = CONTAINING_RECORD(pEntry, DEVICE_EXTENSION, Link);
if( DeviceExtension_Sibling != DeviceExtension
&& TRUE == ARE_WE_RELATED(DeviceExtension, DeviceExtension_Sibling)
&& TRUE == DeviceExtension_Sibling->fStarted )
{
#ifdef CHANGE_DEVICE
if( DeviceExtension_Sibling->fReplaced )
{
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Outgoing Sibling found (0x%x)", DeviceExtension_Sibling));
}
else
{
#endif /* CHANGE_DEVICE */
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Sibling found (0x%x)", DeviceExtension_Sibling));
DeviceExtension->fSiblingFound = TRUE;
#ifdef CHANGE_DEVICE
}
#endif /* CHANGE_DEVICE */
break;
}
}
/*
* We are done, release the Mutex
*/
ExReleaseFastMutex (&Global.Mutex);
/*
* check the axis and button configuration for the joystick
*/
ntStatus = HGM_JoystickConfig(DeviceObject);
if( NT_SUCCESS( ntStatus ) )
{
/*
* Make sure that sibling axes are not overlapped
*/
if( DeviceExtension->fSiblingFound &&
(DeviceExtension_Sibling->resistiveInputMask & DeviceExtension->resistiveInputMask) != 0x0 )
{
HGM_DBGPRINT(FILE_HIDJOY |HGM_ERROR,\
("HGM_InitDevice: OverLapping Resources ResisitiveInputMask(0x%x) Sibling(0x%x)",\
DeviceExtension->resistiveInputMask,DeviceExtension_Sibling->resistiveInputMask ));
ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
}
}
else
{
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("HGM_InitDevice: JoystickConfig Failed"));
}
return( ntStatus );
} /* HGM_InitAnalog */
/*
* Change device sample only code
*/
#ifdef CHANGE_DEVICE
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func VOID | HGM_ChangeHandler |
*
* Use IOCTL_GAMEENUM_EXPOSE_SIBLING and IOCTL_GAMEENUM_REMOVE_SELF
* to change the attributes of the device.
*
* @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
*
* Pointer to the mini-driver device extension.
*
*****************************************************************************/
VOID
HGM_ChangeHandler
(
IN OUT PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
KEVENT IoctlCompleteEvent;
IO_STATUS_BLOCK IoStatus;
PIO_STACK_LOCATION irpStack, nextStack;
PIRP pIrp;
PVOID SiblingHandle;
GAMEENUM_EXPOSE_SIBLING ExposeSibling;
PAGED_CODE ();
HGM_DBGPRINT(FILE_HIDJOY | HGM_FENTRY,\
("HGM_ChangeHandler(DeviceExtension=0x%x)", DeviceExtension));
KeInitializeEvent(&IoctlCompleteEvent, NotificationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest (
IOCTL_GAMEENUM_EXPOSE_SIBLING,
DeviceExtension->NextDeviceObject,
&ExposeSibling,
sizeof( ExposeSibling ),
&ExposeSibling,
sizeof( ExposeSibling ),
TRUE,
&IoctlCompleteEvent,
&IoStatus);
if( pIrp )
{
/*
* For demonstration purposes only, we don't actually change the
* device, we just re-expose the same one. If the device really
* needs to be changed, this would be signalled either by a
* change in the OemData on the newly exposed device or by using
* a specific HardwareID string.
* Note the nAxis and nButton fields will always be zero for an
* exposed sibling.
*/
RtlZeroMemory( &ExposeSibling, sizeof( ExposeSibling ) );
ExposeSibling.Size = sizeof( ExposeSibling );
ExposeSibling.HardwareHandle = &SiblingHandle;
C_ASSERT( sizeof( ExposeSibling.OemData ) == sizeof( DeviceExtension->HidGameOemData.Game_Oem_Data ) );
RtlCopyMemory(ExposeSibling.OemData, DeviceExtension->HidGameOemData.Game_Oem_Data, sizeof(ExposeSibling.OemData));
ASSERT( ExposeSibling.UnitID == 0 );
/*
* Setting a NULL pointer causes the HardwareID of this sibling to be used
*/
ExposeSibling.HardwareIDs = NULL;
/*
* issue a synchronous request to GameEnum to expose this new sibling
*/
ntStatus = IoCallDriver( DeviceExtension->NextDeviceObject, pIrp );
if( ntStatus == STATUS_PENDING )
{
ntStatus = KeWaitForSingleObject (&IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL);
}
if( NT_SUCCESS(ntStatus) )
{
/*
* All went well so remove self
*/
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Sibling exposed!"));
pIrp = IoBuildDeviceIoControlRequest (
IOCTL_GAMEENUM_REMOVE_SELF,
DeviceExtension->NextDeviceObject,
NULL,
0,
NULL,
0,
TRUE,
&IoctlCompleteEvent,
&IoStatus);
if( pIrp )
{
/*
* issue a synchronous request to GameEnum to remove self
*/
ntStatus = IoCallDriver( DeviceExtension->NextDeviceObject, pIrp );
if( ntStatus == STATUS_PENDING )
{
ntStatus = KeWaitForSingleObject( &IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL );
}
if( NT_SUCCESS(ntStatus) )
{
/*
* All done
*/
HGM_DBGPRINT(FILE_HIDJOY | HGM_BABBLE, ("Removed self!"));
}
else
{
/*
* Something bad happened but there's little we can do
*/
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR,\
("Failed to remove self with GameEnum error: 0x%08x", \
ntStatus));
}
}
else
{
ntStatus = STATUS_NO_MEMORY;
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR, \
("Failed to create IRP for remove self") );
}
}
else
{
/*
* Something bad happened so reset the flag and carry on
*/
DeviceExtension->fReplaced = FALSE;
HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN,\
("Failed to expose sibling with GameEnum error: 0x%08x", ntStatus));
}
}
else
{
ntStatus = STATUS_NO_MEMORY;
DeviceExtension->fReplaced = FALSE;
HGM_DBGPRINT(FILE_HIDJOY | HGM_ERROR, \
("Failed to create IRP for expose sibling") );
}
/*
* We've finished touching the DeviceExtension now.
*/
HGM_DecRequestCount( DeviceExtension );
HGM_EXITPROC(FILE_HIDJOY|HGM_FEXIT_STATUSOK, "HGM_ChangeHandler", ntStatus);
return;
} /* HGM_ChangeHandler */
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func VOID | HGM_DeviceChanged |
*
* Start the process of changing the device attributes by stashing
* away all the data needed and then initializing and queuing a work
* item to call the IOCTL at the required PASSIVE_LEVEL.
*
* @parm IN OUT PDEVICE_EXTENSION | DeviceExtension |
*
* Pointer to the mini-driver device extension.
*
*****************************************************************************/
VOID
HGM_DeviceChanged
(
IN OUT PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS ntStatus;
/*
* Since the work item will use the device extension, bump the usage
* count up one in case anyone tries to remove the device between
* now and when the work item gets to run. If that fails, forget it.
*/
ntStatus = HGM_IncRequestCount( DeviceExtension );
if( NT_SUCCESS(ntStatus) )
{
DeviceExtension->fReplaced = TRUE;
ExInitializeWorkItem( &DeviceExtension->WorkItem,
(PWORKER_THREAD_ROUTINE)HGM_ChangeHandler, DeviceExtension );
ExQueueWorkItem( &DeviceExtension->WorkItem, DelayedWorkQueue );
}
else
{
HGM_DBGPRINT(FILE_HIDJOY | HGM_WARN, ("Failed to change device") );
}
} /* HGM_DeviceChanged */
#endif /* CHANGE_DEVICE */
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func VOID | HGM_Game2HID |
*
* Process the data returned from polling the gameport into values
* and buttons for returning to HID.
* <nl>The meaning of the data is interpreted according to the
* characteristics of the device described in the hardware settings
* flags.
*
* @parm IN PDEVICE_EXTENSION | DeviceExtension |
*
* Pointer to the mini-driver device extension.
*
* @parm IN OUT PUHIDGAME_INPUT_DATA | pHIDData |
*
* Pointer to the buffer into which the HID report should be written.
* This buffer must be assumed to be unaligned.
*
*****************************************************************************/
VOID
HGM_Game2HID
(
IN PDEVICE_EXTENSION DeviceExtension,
IN OUT PUHIDGAME_INPUT_DATA pHIDData
)
{
LONG Idx;
/*
* Use a local buffer to assemble the report as the real buffer may not
* be aligned.
*/
HIDGAME_INPUT_DATA LocalBuffer;
RtlZeroMemory( &LocalBuffer, sizeof( LocalBuffer ) );
/*
* Remap axis
*/
for(Idx = 0x0; Idx < DeviceExtension->nAxes; Idx++ )
{
LocalBuffer.Axis[Idx] = DeviceExtension->LastGoodAxis[DeviceExtension->AxisMap[Idx]];
}
/*
* Copy buttons and remap any POVs
*/
if( DeviceExtension->fSiblingFound )
{
/*
* Simplest case, 2nd half poll must be 2A 2B
*/
LocalBuffer.Button[0] = DeviceExtension->LastGoodButton[2];
LocalBuffer.Button[1] = DeviceExtension->LastGoodButton[3];
}
else if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISBUTTONCOMBOS )
{
UCHAR Buttons = 0;
for( Idx = 3; Idx >=0; Idx-- )
{
Buttons <<= 1;
Buttons = (UCHAR)(Buttons + (UCHAR)(DeviceExtension->LastGoodButton[Idx] >> BUTTON_BIT));
}
if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_HASPOV2 )
{
Idx = c2PComboLU[Buttons];
}
else
{
Idx = c1PComboLU[Buttons];
}
if( Idx >= P1_NULL )
{
if( Idx < P2_NULL )
{
LocalBuffer.hatswitch[0] = (UCHAR)(Idx & POV_MASK);
}
else
{
LocalBuffer.hatswitch[1] = (UCHAR)(Idx & POV_MASK);
}
}
else
{
#ifdef CHANGE_DEVICE
if( ( Idx >= DeviceExtension->nButtons ) && ( !DeviceExtension->fReplaced ) )
{
/*
* If a higher button was pressed than expected, use
* remove_self/expose_sibling to change expectations.
*/
HGM_DeviceChanged( DeviceExtension );
}
#endif /* CHANGE_DEVICE */
LocalBuffer.Button[Idx] = BUTTON_ON;
}
}
else
{
if( DeviceExtension->HidGameOemData.OemData[0].joy_hws_dwFlags & JOY_HWS_POVISPOLL )
{
/*
* Following the axis mapping loop, Idx is one larger than
* DeviceExtension->nAxes which is the correct index for a
* polled POV.
*/
LocalBuffer.Axis[Idx] = DeviceExtension->LastGoodAxis[DeviceExtension->povMap];
}
/*
* Check buttons on R and Z axes
*/
if( DeviceExtension->nButtons > 5 )
{
if( DeviceExtension->LastGoodAxis[3] > DeviceExtension->button6limit )
{
/*
* New max found so button is off
*/
HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
("HGM_Game2HID: Changing button 6 limit from %u to %u", \
DeviceExtension->button6limit, DeviceExtension->LastGoodAxis[3] ) ) ;
DeviceExtension->button6limit = DeviceExtension->LastGoodAxis[3];
}
else if( DeviceExtension->LastGoodAxis[3] < (DeviceExtension->button6limit>>1) )
{
LocalBuffer.Button[5] = BUTTON_ON;
}
}
if( DeviceExtension->nButtons > 4 )
{
Idx = 4;
if( DeviceExtension->LastGoodAxis[2] > DeviceExtension->button5limit )
{
/*
* New max found so button is off
*/
HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
("HGM_Game2HID: Changing button 5 limit from %u to %u", \
DeviceExtension->button5limit, DeviceExtension->LastGoodAxis[2] ) ) ;
DeviceExtension->button5limit = DeviceExtension->LastGoodAxis[2];
}
else if( DeviceExtension->LastGoodAxis[2] < (DeviceExtension->button5limit>>1) )
{
LocalBuffer.Button[4] = BUTTON_ON;
}
}
else
{
Idx = DeviceExtension->nButtons;
}
/*
* Copy all standard buttons
*/
while( Idx-- )
{
LocalBuffer.Button[Idx] = DeviceExtension->LastGoodButton[Idx];
}
}
C_ASSERT( sizeof( *pHIDData ) == sizeof( LocalBuffer ) );
RtlCopyMemory( pHIDData, &LocalBuffer, sizeof( LocalBuffer ) );
HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
("HGM_Game2HID: Axes: X: %08x Y: %08x Z: %08x R: %08x", \
LocalBuffer.Axis[0], LocalBuffer.Axis[1], LocalBuffer.Axis[2], LocalBuffer.Axis[3] ) ) ;
HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2, \
("HGM_Game2HID: P1: %d P2: %d Buttons %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", \
LocalBuffer.hatswitch[0], LocalBuffer.hatswitch[1], \
LocalBuffer.Button[0], LocalBuffer.Button[1], LocalBuffer.Button[2], LocalBuffer.Button[3], LocalBuffer.Button[4], \
LocalBuffer.Button[5], LocalBuffer.Button[6], LocalBuffer.Button[7], LocalBuffer.Button[8], LocalBuffer.Button[9] ) ) ;
} /* HGM_Game2HID */