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 HID, WDM, and bus driver.
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.
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.
File DescriptionGameSys.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
This code tour discusses the steps an audio driver must complete to setup GameEnum.sys as a functioning FDO for the physical gameport.
The major topics covered in this tour include the following.
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.
The following assumptions are made by GameEnum with regards to its resources:
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.
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 GAMEENUM_PORT_PARAMETERS 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 explained later in this document.
Here is a brief description of fields that are relevant to a driver that would override the supplied GameEnum defaults:
Field Name | Description | Default Value |
ReadAccessor | The function is used by the joystick driver to read analog data from the gameport hardware / joystick | READ_PORT_UCHAR |
ReadAccessorDigital | 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. | NONE |
WriteAccessor | The function is used to by the joystick driver write data to the gameport hardware / joystick | WRITE_PORT_UCHAR |
GameContext | Context passed as a parameter to ReadAccessor, ReadAccessorDigital, and WriteAccessor | Port resource assigned to the stack (0x201 for example) |
AcquirePort |
"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. This function may fail, so the caller must check the return code before using the Accessor functions. |
Game_AcquirePort |
ReleasePort | "Releases" and unlocks the hardware. This function will be called after the joystick driver has finished calling the Accessor functions. | Game_ReleasePort |
PortContext | Context passed as a parameter to AcquirePort and ReleasePort. | Context private to GameEnum |
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.
GameEnum will send an internal IOCTL, IOCTL_GAMEENUM_ACQUIRE_ACCESSORS, 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 GAMEENUM_ACQUIRE_ACCESSORS, defined in gameport.h. The fields in GAMEENUM_ACQUIRE_ACCESSORS match one to one with the relevant fields in GAMEENUM_PORT_PARAMETERS (the structure supplied to GameEnum's PDO).
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.
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.
#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; }
© 1998 Microsoft Corporation