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.
458 lines
15 KiB
458 lines
15 KiB
<HTML>
|
|
<HEAD>
|
|
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
|
|
<META NAME="Generator" CONTENT="Microsoft Word 97">
|
|
<TITLE>GameEnum.sys</TITLE>
|
|
<META NAME="Template" CONTENT="C:\PROGRAM FILES\MICROSOFT OFFICE\OFFICE\html.dot">
|
|
</HEAD>
|
|
<BODY TEXT="#000000" LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff" leftmargin="8">
|
|
<FONT FACE="Verdana"><H2><A NAME="MYSAMPLE">GameEnum.sys</A> </H2>
|
|
|
|
<H3>Summary</H3></FONT><FONT FACE="Verdana" SIZE=2>
|
|
|
|
<P>GameEnum is a WDM driver. It is a bus driver for a legacy Gameport. It works
|
|
closely with Hidgame.sys, which controls the children GameEnum enumerates. Keywords
|
|
include <B>HID</B>, <B>WDM</B>, and <B>bus driver</B>.</P>
|
|
|
|
<H3>Building the Sample</H3></FONT><FONT FACE="Verdana" SIZE=2>
|
|
|
|
<P>The driver works on both x86 and Alpha platforms and is 64-bit compliant. It
|
|
builds properly with Visual C 6.0 and supports Plug and Play, Power Management,
|
|
and device interfaces.</P>
|
|
|
|
<P>The driver is installed when a Gameport is discovered via Plug and Play. There
|
|
are no special .inf requirements; please see GamePort.inf in the system INF
|
|
directory for an example. Once built, the sample produces one binary,
|
|
GameEnum.sys. Necessary files include those in this directory and Gameport.h
|
|
and Wdm.h. Both checked and free builds are available, and there are no known
|
|
bugs or issues to be documented.</P>
|
|
|
|
<H3>CODE TOUR</H3>
|
|
<H4>File Manifest</H4>
|
|
</FONT>
|
|
|
|
<U><PRE>File		Description</U>
|
|
GameSys.htm The documentation for this sample (this file)
|
|
Gameenum.h Definitions
|
|
Gameenum.c External and internal IOCTL support
|
|
Pnp.c Plug and Play functionality
|
|
Gameenum.rc Resources
|
|
</PRE>
|
|
|
|
<h4><font face="Verdana">Programming Tour</font></h4>
|
|
<font face="Verdana" size="2">
|
|
|
|
<p>This code tour discusses the steps an audio driver must complete to setup GameEnum.sys as a functioning FDO for the
|
|
physical gameport.</p>
|
|
|
|
<p>The major topics covered in this tour include the following.
|
|
|
|
</font>
|
|
|
|
<font FACE="Verdana" SIZE="2">
|
|
<ul>
|
|
<li><a href="#requirements">Resources requirements made by GameEnum</a></li>
|
|
<li><a href="#lower_filter">Definition of a lower filter</a></li>
|
|
<li><a href="#supplied">Functions that are supplied to GameEnum's PDOs</a></li>
|
|
<li><a href="#override">How to override the supplied functions</a></li>
|
|
</ul>
|
|
</font>
|
|
|
|
<h4><font face="Verdana" size="2">
|
|
<a name="requirements">Resources requirements made by GameEnum</a>
|
|
</font></h4>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
GameEnum acts as an FDO for a traditional gameport. USB joysticks do not
|
|
need any additional drivers; DirectInput will open them directly. A kernel driver
|
|
for an audio device can enumerate a child device and have GameEnum loaded on top of it.
|
|
GameEnum will handle all the details in enumerating, starting, and handling
|
|
joysticks plugged into the system.
|
|
</font></p>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
The following assumptions are made by GameEnum with regards to its resources:
|
|
<ol>
|
|
<li>If there are resources provided, it does <i>not</i> have to be the port
|
|
<font face="Courier" size="2">0x201</font>. For example, the resources can be in PCI space.</li>
|
|
<li>If no resources are assigned to the GameEnum stack, then the PDO must handle
|
|
an IOCTL to supply GameEnum with the necessary data on how to access the
|
|
gameport hardware. For details on how to implement this,
|
|
see <a href="#override">below</a>.</li>
|
|
<li>There are no interrupts assigned to it.</li>
|
|
</ol>
|
|
|
|
</font></p>
|
|
|
|
<h4><font face="Verdana" size="2">
|
|
<a name="lower_filter">Definition of a lower filter</a>
|
|
</font></h4>
|
|
|
|
|
|
<p><font face="Verdana" size="2">
|
|
The term lower filter refers to a device object in the stack below GameEnum which
|
|
handles the override IOCTL. This device object can be the PDO itself (if the audio
|
|
driver is enumerating the PDO) or an actual filter object that is in between
|
|
GameEnum and the PDO in the stack.
|
|
</font></p>
|
|
|
|
<h4><font face="Verdana" size="2">
|
|
<a name="supplied">Functions that are supplied to GameEnum's PDOs</a>
|
|
</font></h4>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
Once a PDO enumerated by GameEnum has started, it sends down an IOCTL to acquire
|
|
the functions it needs to read from and write to the hardware. This information
|
|
is sent via the <font face="Courier" size="2">GAMEENUM_PORT_PARAMETERS</font>
|
|
structure defined in gameport.h. If the gameport has non-standard ports, then
|
|
the lower filter must override this information by handling the override IOCTL,
|
|
which is <a href="#override">explained later in this document</a>.
|
|
</font></p>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
Here is a brief description of fields that are relevant to a driver that would
|
|
override the supplied GameEnum defaults:
|
|
</font></p>
|
|
|
|
<table border=1>
|
|
<tr>
|
|
<td><font face="Verdana" size="3">
|
|
<b>Field Name</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="3">
|
|
<b>Description</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="3">
|
|
<b>Default Value</b>
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>ReadAccessor</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
The function is used by the joystick driver to read analog data from the gameport hardware / joystick
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
READ_PORT_UCHAR
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>ReadAccessorDigital</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
The function is used by the joystick driver to read data digitally from the gameport hardware /
|
|
joystick. This function is not supplied by default; it is only supplied if
|
|
the lower filter handles the override IOCTL.
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
NONE
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>WriteAccessor</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
The function is used to by the joystick driver write data to the gameport hardware / joystick
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
WRITE_PORT_UCHAR
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>GameContext</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
Context passed as a parameter to ReadAccessor, ReadAccessorDigital, and WriteAccessor
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
Port resource assigned to the stack (0x201 for example)
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>AcquirePort</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
<p>"Acquires" and locks the hardware. This function must be called before any of the Accessor
|
|
functions listed above may be called. The Accessor functions must be functional
|
|
between calls to AcquirePort and ReleasePort.</p>
|
|
<p> This function may fail, so the caller must check the return code before
|
|
using the Accessor functions.</p>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
Game_AcquirePort
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>ReleasePort</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
"Releases" and unlocks the hardware. This function will be called after the
|
|
joystick driver has finished calling the Accessor functions.
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
Game_ReleasePort
|
|
</font></td>
|
|
</tr>
|
|
<tr>
|
|
<td><font face="Verdana" size="2">
|
|
<b>PortContext</b>
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
Context passed as a parameter to AcquirePort and ReleasePort.
|
|
</font></td>
|
|
<td><font face="Verdana" size="2">
|
|
Context private to GameEnum
|
|
</font></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
To override the Accessor functions, all the fields are required, except for
|
|
ReadAccessorDigital, which is an optional field. Only override this function if
|
|
you can support digital reads on your hardware. The AcquirePort and ReleasePort
|
|
functions may be overridden only if the Accessor functions are overridden. All
|
|
the fields must be overridden; there are no optional fields.
|
|
</font></p>
|
|
|
|
<h4><font face="Verdana" size="2">
|
|
<a name="override">How to override the supplied functions</a>
|
|
</font></h4>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
GameEnum will send an internal IOCTL,
|
|
<font face="Courier" size="2">IOCTL_GAMEENUM_ACQUIRE_ACCESSORS</font>, down the
|
|
stack to the lower filter so that it may override GameEnum's default
|
|
functionality. The lower filter must fill in the output buffer for this IOCTL,
|
|
which is of the size <font face="Courier" size="2">GAMEENUM_ACQUIRE_ACCESSORS</font>,
|
|
defined in gameport.h. The fields in
|
|
<font face="Courier" size="2">GAMEENUM_ACQUIRE_ACCESSORS</font> match one to one
|
|
with the relevant fields in <font face="Courier" size="2">GAMEENUM_PORT_PARAMETERS</font>
|
|
(the structure supplied to GameEnum's PDO).
|
|
</font></p>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
When overriding the Accessor functions, you may provide any value for the
|
|
GameContext. It does not have to be limited to a port resource value. For instance,
|
|
you can pass your device extension as the context, and then pull data off of the
|
|
extension to set up the hardware and then perform the actual read. In essence,
|
|
you provide another level of indirection to this function call.
|
|
</font></p>
|
|
|
|
<p><font face="Verdana" size="2">
|
|
Here is a code snippet that shows how to handle the override IOCTL and perform special
|
|
actions on a fictional gameport that requires special init before the port can be
|
|
read or written.
|
|
</font></p>
|
|
|
|
<pre>
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, GameFilter_InternalIoctl)
|
|
#endif
|
|
|
|
typedef struct _DEVICE_EXTENSION {
|
|
// ...
|
|
|
|
// Port address which will enable the game port
|
|
PUCHAR GamePortEnableAddress;
|
|
|
|
// status register
|
|
PUCHAR GamePortStatusAddress;
|
|
|
|
// Port address which we read the data from (ie 0x201 on a traditional ISA card)
|
|
PUCHAR GamePortDataAddress;
|
|
|
|
// Sanity check
|
|
BOOLEAN GamePortEnabled;
|
|
|
|
// ...
|
|
|
|
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING UniRegistryPath
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER (UniRegistryPath);
|
|
|
|
// ...
|
|
DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL]
|
|
= GameFilter_InternalIoctl;
|
|
// ...
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
GameFilter_InternalIoctl (
|
|
PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION stack;
|
|
PDEVICE_EXTENSION devExt;
|
|
PGAMEENUM_ACQUIRE_ACCESSORS pGameEnumAcquireAccessors;
|
|
|
|
PAGED_CODE();
|
|
|
|
stack = IoGetCurrentIrpStackLocation(Irp);
|
|
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
switch (stack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_GAMEENUM_ACQUIRE_ACCESSORS:
|
|
|
|
pGameEnumAcquireAccessors = (PGAMEENUM_ACQUIRE_ACCESSORS)
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (stack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(GAMEENUM_ACQUIRE_ACCESSORS) ||
|
|
|
|
pGameEnumAcquireAccessors->Size <
|
|
sizeof(GAMEENUM_ACQUIRE_ACCESSORS) ) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else {
|
|
//
|
|
// Accessor functions
|
|
//
|
|
|
|
// digital read not supported at this time in this filter
|
|
pGameEnumAcquireAccessors->ReadAccessorDigital = NULL;
|
|
|
|
//
|
|
// GameEnum does not know how to parse the data address out of the
|
|
// range of ports assigned to this PDO, so we must do this on
|
|
// behalf of GameEnum.
|
|
//
|
|
pGameEnumAcquireAccessors->GameContext = (PVOID)
|
|
devExt->GamePortDataAddress;
|
|
|
|
//
|
|
// TBD: Need to abstract out the XXX_PORT functions so that we can
|
|
// handle registers as well (ie, READ / WRITE_REGISTER_UCHAR)
|
|
//
|
|
|
|
//
|
|
// We don't need to do anything special for reads or writes so
|
|
// supply the normal read / write functions. Besides, adding
|
|
// another level of indirection kills performance because joysticks
|
|
// are usually polled continuously.
|
|
//
|
|
pGameEnumAcquireAccessors->ReadAccessor = (PGAMEENUM_READPORT)
|
|
READ_PORT_UCHAR;
|
|
pGameEnumAcquireAccessors->WriteAccessor = (PGAMEENUM_WRITEPORT)
|
|
WRITE_PORT_UCHAR;
|
|
|
|
//
|
|
// Acquire / Release functions
|
|
//
|
|
pGameEnumAcquireAccessors->PortContext = (PVOID) devExt;
|
|
|
|
pGameEnumAcquireAccessors->AcquirePort = (PGAMEENUM_ACQUIRE_PORT)
|
|
GameFilter_AcquirePort;
|
|
pGameEnumAcquireAccessors->ReleasePort = (PGAMEENUM_RELEASE_PORT)
|
|
GameFilter_ReleasePort;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
GameFilter_AcquirePort(
|
|
PVOID Context
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
PDEVICE_EXTENSION devExt = (PDEVICE_EXTENSION) Context;
|
|
UCHAR statusValue;
|
|
|
|
// function cannot be paged because we can be called at high IRQL
|
|
// PAGED_CODE();
|
|
|
|
//
|
|
// TBD: Need to abstract out the XXX_PORT functions so that we can handle
|
|
// registers as well (ie, READ / WRITE_REGISTER_UCHAR)
|
|
//
|
|
|
|
if (devExt->GamePortEnabled) {
|
|
//
|
|
// Already enabled, nothing to do
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// devExt->GamePortEnableAddress and GamePortStatusAddress were set in
|
|
// start device when we parsed the resources assigned to the port
|
|
//
|
|
WRITE_PORT_UCHAR(devExt->GamePortEnableAddress, 0x1);
|
|
statusValue = READ_PORT_UCHAR(devExt->GamePortStatusAddress);
|
|
|
|
//
|
|
// If statusValue's lower 4 bits are set (a magic value really) then the port
|
|
// is enabled. Otherwise, we can't enable it at this time.
|
|
//
|
|
if (statusValue & 0xF) {
|
|
devExt->GamePortEnabled = TRUE;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
//
|
|
// Do something here if we want to retry enabling the gameport in the
|
|
// "near" future (such as wait on an event or spin in a loop)
|
|
//
|
|
;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
GameFilter_ReleasePort(
|
|
PVOID Context
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION devExt = (PDEVICE_EXTENSION) Context;
|
|
|
|
// function cannot be paged because we can be called at high IRQL
|
|
// PAGED_CODE();
|
|
|
|
ASSERT(devExt->GamePortEnabled);
|
|
|
|
WRITE_PORT_UCHAR(devExt->GamePortEnableAddress, 0x0);
|
|
devExt->GamePortEnabled = FALSE;
|
|
}
|
|
|
|
</pre>
|
|
|
|
</FONT><P ALIGN="CENTER"><A HREF="#top"><FONT FACE="Verdana" SIZE=2>Top of page</FONT></A><FONT FACE="Verdana" SIZE=2> </P></FONT>
|
|
<TABLE CELLSPACING=0 BORDER=0 WIDTH=624>
|
|
<TR><TD VALIGN="MIDDLE" BGCOLOR="#00ffff" HEIGHT=2>
|
|
<P></TD>
|
|
</TR>
|
|
</TABLE>
|
|
|
|
<FONT FACE="MS Sans Serif" SIZE=1><P>© 1998 Microsoft Corporation</FONT><FONT FACE="Verdana" SIZE=2> </P></FONT></BODY>
|
|
</HTML>
|