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.
1071 lines
28 KiB
1071 lines
28 KiB
//**************************************************************************
|
|
//
|
|
// DEVICE.C -- Xena Gaming Project
|
|
//
|
|
// Version 3.XX
|
|
//
|
|
// Copyright (c) 1997 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// @doc
|
|
// @module DEVICE.C | Routines to support device class calls
|
|
//**************************************************************************
|
|
|
|
#include "msgame.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Alloc_text pragma to specify routines that can be paged out.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, DEVICE_DriverEntry)
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Device Delcarations
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifndef SAITEK
|
|
DECLARE_DEVICE(Midas);
|
|
DECLARE_DEVICE(Juno);
|
|
DECLARE_DEVICE(Jolt);
|
|
DECLARE_DEVICE(GamePad);
|
|
DECLARE_DEVICE(Tilt);
|
|
#endif
|
|
DECLARE_DEVICE(LedZep);
|
|
|
|
static PDEVICEINFO MiniDrivers[] = {
|
|
#ifndef SAITEK
|
|
INSTANCE_DEVICE(Midas), //default
|
|
INSTANCE_DEVICE(Juno),
|
|
INSTANCE_DEVICE(Jolt),
|
|
INSTANCE_DEVICE(GamePad),
|
|
INSTANCE_DEVICE(Tilt),
|
|
#endif
|
|
INSTANCE_DEVICE(LedZep)
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Private Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
static BOOLEAN DeviceDetected = FALSE;
|
|
static ULONG DetectAttempts = 0;
|
|
static ULONG LastDetectTime = 0;
|
|
static KIRQL SpinLockIrql = PASSIVE_LEVEL;
|
|
static KSPIN_LOCK DevSpinLock = {0};
|
|
static ULONG SuccessPackets[MAX_DEVICE_UNITS] = {0,0,0,0};
|
|
static ULONG PollingAttempts[MAX_DEVICE_UNITS] = {0,0,0,0};
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Public Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
public ULONG POV_Values[] = {
|
|
JOY_POVCENTERED,
|
|
JOY_POVFORWARD,
|
|
JOY_POVFORWARD+4500,
|
|
JOY_POVRIGHT,
|
|
JOY_POVRIGHT+4500,
|
|
JOY_POVBACKWARD,
|
|
JOY_POVBACKWARD+4500,
|
|
JOY_POVLEFT,
|
|
JOY_POVLEFT+4500
|
|
};
|
|
|
|
public ULONG PollingInterval = POLLING_INTERVAL;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Private Procedures
|
|
//---------------------------------------------------------------------------
|
|
|
|
static VOID DEVICE_AcquireDevice (VOID);
|
|
static VOID DEVICE_ReleaseDevice (VOID);
|
|
static NTSTATUS DEVICE_HotPlugDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS DEVICE_RemoveSiblings (PGAMEPORT PortInfo);
|
|
static BOOLEAN DEVICE_DetectClocks (PGAMEPORT PortInfo, ULONG TimeOut);
|
|
static BOOLEAN DEVICE_QuickDetect (PGAMEPORT PortInfo);
|
|
static NTSTATUS DEVICE_DetectDevices (PGAMEPORT PortInfo);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Acquires exclusive access to gameport (mutex)
|
|
// @rdesc Returns nothing
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID DEVICE_AcquireDevice (VOID)
|
|
{
|
|
KeAcquireSpinLock (&DevSpinLock, &SpinLockIrql);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Releases exclusive access to gameport (mutex)
|
|
// @rdesc Returns nothing
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID DEVICE_ReleaseDevice (VOID)
|
|
{
|
|
KeReleaseSpinLock (&DevSpinLock, SpinLockIrql);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Detects hot-plugging of gamepad devices
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns one of the following values:
|
|
// @flag STATUS_DEVICE_NOT_CONNECTED | An error occurred
|
|
// @flag STATUS_SIBLING_REMOVED | An device has been removed
|
|
// @flag STATUS_SIBLING_ADDED | An device has been added
|
|
// @flag STATUS_SUCCESS | Everything is fine
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_HotPlugDevice (PGAMEPORT PortInfo)
|
|
{
|
|
ULONG UnitId;
|
|
PDEVICEINFO DevInfo;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_HotPlugDevice Enter\n"));
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo || !DevInfo->NumDevices)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_HotPlugDevice Called With No Device!\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//
|
|
// Get UnitId for tracking by device
|
|
//
|
|
|
|
UnitId = GET_DEVICE_UNIT(PortInfo);
|
|
|
|
//
|
|
// Check if number devices has changed
|
|
//
|
|
|
|
if (((DevInfo->DeviceCount+DevInfo->DevicePending) != DevInfo->NumDevices) && (SuccessPackets[UnitId]++ > HOT_PLUG_PACKETS))
|
|
{
|
|
SuccessPackets[UnitId] = 0;
|
|
if ((DevInfo->DeviceCount+DevInfo->DevicePending) > DevInfo->NumDevices)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_HotPlugDevice Removing Sibling\n", MSGAME_NAME));
|
|
//
|
|
// Decrement pending count to avoid removing twice
|
|
//
|
|
InterlockedDecrement (&DevInfo->DevicePending);
|
|
return (STATUS_SIBLING_REMOVED);
|
|
}
|
|
else
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_HotPlugDevice Adding Sibling\n", MSGAME_NAME));
|
|
//
|
|
// Increment pending count to avoid adding twice
|
|
//
|
|
InterlockedIncrement (&DevInfo->DevicePending);
|
|
return (STATUS_SIBLING_ADDED);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return status
|
|
//
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_HotPlugDevice Exit\n", MSGAME_NAME));
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Removes sibling lists if possible
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns one of the following values:
|
|
// @flag STATUS_DEVICE_NOT_CONNECTED | An error occurred
|
|
// @flag STATUS_SIBLING_REMOVED | An device has been removed
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_RemoveSiblings (PGAMEPORT PortInfo)
|
|
{
|
|
PDEVICEINFO DevInfo;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_RemoveSiblings Enter\n"));
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_RemoveSiblings Called With No Device!\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//
|
|
// Zero number of devices
|
|
//
|
|
|
|
DevInfo->NumDevices = 1;
|
|
|
|
//
|
|
// Check if more than one device
|
|
//
|
|
|
|
if ((DevInfo->DeviceCount+DevInfo->DevicePending) > 1)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_RemoveSiblings Removing Sibling\n", MSGAME_NAME));
|
|
//
|
|
// Decrement pending count to avoid removing twice
|
|
//
|
|
InterlockedDecrement (&DevInfo->DevicePending);
|
|
return (STATUS_SIBLING_REMOVED);
|
|
}
|
|
|
|
//
|
|
// Return status
|
|
//
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_RemoveSiblings Exit\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Detect digital gameport clocks
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm ULONG | TimeOut | Loops to try for clocks
|
|
// @rdesc Returns true if clocks detected, false otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN DEVICE_DetectClocks (PGAMEPORT PortInfo, ULONG TimeOut)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
|
|
_asm
|
|
{
|
|
;StartLoop:
|
|
xor ebx, ebx
|
|
mov edx, PortInfo
|
|
mov ecx, TimeOut
|
|
|
|
push 0 ; write byte to gameport
|
|
push edx
|
|
call PORTIO_Write
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
mov ah, al
|
|
|
|
ClockLoop:
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
xor al, ah
|
|
test al, CLOCK_BIT_MASK
|
|
je NextLoop
|
|
|
|
;FoundClock:
|
|
inc ebx
|
|
cmp ebx, QUICK_DETECT_CLOCKS
|
|
jb NextLoop
|
|
mov Result, TRUE
|
|
jmp ExitLoop
|
|
|
|
NextLoop:
|
|
loop ClockLoop
|
|
|
|
ExitLoop:
|
|
nop
|
|
}
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Detects whether digital device is connected
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns true if clocks detected, false otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN DEVICE_QuickDetect (PGAMEPORT PortInfo)
|
|
{
|
|
ULONG i;
|
|
ULONG TimeOut;
|
|
DETECT_ORDER DetectOrder;
|
|
|
|
TimeOut = TIMER_CalibratePort (PortInfo, QUICK_DETECT_TIME);
|
|
|
|
if (DEVICE_DetectClocks (PortInfo, TimeOut))
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_QuickDetect Found Digital Clocks!\n", MSGAME_NAME));
|
|
return (TRUE);
|
|
}
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_QuickDetect Trying Analog Devices\n", MSGAME_NAME));
|
|
for (DetectOrder = DETECT_FIRST; DetectOrder <= DETECT_LAST; DetectOrder++)
|
|
for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
|
|
if (MiniDrivers[i]->DetectOrder == DetectOrder)
|
|
if (MiniDrivers[i]->IsAnalog)
|
|
if (MiniDrivers[i]->Services->ConnectDevice (PortInfo) == STATUS_SUCCESS)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_QuickDetect Found Analog Device!\n", MSGAME_NAME));
|
|
return (TRUE);
|
|
}
|
|
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_QuickDetect Failed to Find Digital Device!\n", MSGAME_NAME));
|
|
return (FALSE);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Detects which device type is connected
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns one of the following values:
|
|
// @flag STATUS_DEVICE_NOT_CONNECTED | An error occurred
|
|
// @flag STATUS_DEVICE_CHANGED | The device has changed
|
|
// @flag STATUS_SUCCESS | Everything is fine
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_DetectDevices (PGAMEPORT PortInfo)
|
|
{
|
|
ULONG i, j;
|
|
PDEVICEINFO DevInfo;
|
|
DETECT_ORDER DetectOrder;
|
|
|
|
//
|
|
// This an initial redetect or system startup device
|
|
//
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_DetectDevices Enter\n", MSGAME_NAME));
|
|
|
|
//
|
|
// Skip if we've already detected a new device removed this one
|
|
//
|
|
|
|
if (GET_DEVICE_DETECTED (PortInfo))
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Device Already Detected!\n", MSGAME_NAME));
|
|
return (STATUS_DELETE_PENDING);
|
|
}
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Skip if we're trying too hard
|
|
//
|
|
|
|
if (DetectAttempts++ > MAX_DETECT_ATTEMPTS)
|
|
{
|
|
if (TIMER_GetTickCount () < (LastDetectTime + MAX_DETECT_INTERVAL))
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
LastDetectTime = TIMER_GetTickCount();
|
|
}
|
|
|
|
//
|
|
// Calibrate timer each try
|
|
//
|
|
|
|
TIMER_Calibrate ();
|
|
|
|
//
|
|
// Calibrate port timeouts
|
|
//
|
|
|
|
PORTIO_CalibrateTimeOut (PortInfo);
|
|
|
|
//
|
|
// Perform quick detection in case none attached
|
|
//
|
|
|
|
if (DEVICE_QuickDetect (PortInfo))
|
|
{
|
|
for (DetectOrder = DETECT_FIRST; DetectOrder <= DETECT_LAST; DetectOrder++)
|
|
for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
|
|
if (MiniDrivers[i]->DetectOrder == DetectOrder)
|
|
if (MiniDrivers[i]->Services->ConnectDevice (PortInfo) == STATUS_SUCCESS)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: %s Connected OK\n", MSGAME_NAME, MiniDrivers[i]->DeviceName));
|
|
DeviceDetected = TRUE;
|
|
DetectAttempts = 0;
|
|
PollingAttempts[0] = 0;
|
|
if (!DevInfo)
|
|
{
|
|
//
|
|
// Assign device type
|
|
//
|
|
SET_DEVICE_INFO (PortInfo, MiniDrivers[i]);
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Setting Device\n", MSGAME_NAME));
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
else if (DevInfo != MiniDrivers[i])
|
|
{
|
|
//
|
|
// Change device type
|
|
//
|
|
SET_DEVICE_DETECTED (PortInfo, MiniDrivers[i]);
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Changing Device\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_CHANGED);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Same device found
|
|
//
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Same Device Found\n", MSGAME_NAME));
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark device not detected
|
|
//
|
|
|
|
DeviceDetected = FALSE;
|
|
|
|
//
|
|
// Return status
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_DetectDevices Failed\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Calcuates and returns is data is odd parity
|
|
// @parm PVOID | Data | Pointer to raw data
|
|
// @parm ULONG | Size | Size of raw data buffer
|
|
// @rdesc True if oded parity, False otherwise
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN DEVICE_IsOddParity (PVOID Data, ULONG Count)
|
|
{
|
|
LONG Result = ERROR_SUCCESS;
|
|
LONG Parity;
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov esi, Data
|
|
mov ecx, Count
|
|
xor eax, eax
|
|
|
|
IsOddLoop:
|
|
|
|
xor al, [esi]
|
|
inc esi
|
|
loop IsOddLoop
|
|
|
|
xor al, ah
|
|
jpo IsOddComplete
|
|
|
|
mov Parity, eax
|
|
mov Result, ERROR_PARITYBITS
|
|
|
|
IsOddComplete:
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
if (Result == ERROR_PARITYBITS)
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_IsOddParity - Parity bits failed %ld\n", MSGAME_NAME, Parity));
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Driver entry point for device layer
|
|
// @rdesc Returns NT status code
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_DriverEntry (VOID)
|
|
{
|
|
ULONG i;
|
|
NTSTATUS ntStatus;
|
|
|
|
KeInitializeSpinLock (&DevSpinLock);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
|
|
{
|
|
//
|
|
// Call Mini-DriverEntry
|
|
//
|
|
ntStatus = MiniDrivers[i]->Services->DriverEntry ();
|
|
if (NT_ERROR(ntStatus))
|
|
{
|
|
//
|
|
// Abort driver loading
|
|
//
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_DriverEntry: %s Failed Driver Entry\n", MSGAME_NAME, MiniDrivers[i]->DeviceName));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (ntStatus);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Detects gameport IO collisions
|
|
// @parm PPACKETINFO | DataPacket | Device packet info struct
|
|
// @rdesc Returns True for collision, False otherwise
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN DEVICE_IsCollision (PPACKETINFO DataPacket)
|
|
{
|
|
if ((DataPacket->TimeStamp + PollingInterval) > TIMER_GetTickCount ())
|
|
return (TRUE);
|
|
|
|
return (PORTIO_IsClockActive (&DataPacket->PortInfo, DataPacket->ClockDutyCycle));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Copies and returns HID device descriptor for a device
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PUCHAR | Descriptor | Output buffer for descriptor
|
|
// @parm ULONG | MaxSize | Size of buffer for descriptor
|
|
// @parm PULONG | Copied | Bytes copied to buffer for descriptor
|
|
// @rdesc Returns NT status code
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_GetDeviceDescriptor (PGAMEPORT PortInfo, PUCHAR Descriptor, ULONG MaxSize, PULONG Copied)
|
|
{
|
|
PDEVICEINFO DevInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetDeviceDescriptor Enter\n", MSGAME_NAME));
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Zero returned size first
|
|
//
|
|
|
|
*Copied = 0;
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetDeviceDescriptor Called With No Device!\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//
|
|
// Check output buffer size
|
|
//
|
|
|
|
if (MaxSize < sizeof (HID_DESCRIPTOR))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetDeviceDescriptor - Buffer too small = %lu\n", MSGAME_NAME, MaxSize));
|
|
return (STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// Copy descriptor to output buffer
|
|
//
|
|
|
|
memcpy (Descriptor, DevInfo->DevDescriptor, sizeof (HID_DESCRIPTOR));
|
|
|
|
//
|
|
// Return number bytes copied
|
|
//
|
|
|
|
*Copied = sizeof (HID_DESCRIPTOR);
|
|
|
|
//
|
|
// Return status
|
|
//
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Copies and returns HID report descriptor for a device
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PUCHAR | Descriptor | Output buffer for descriptor
|
|
// @parm ULONG | MaxSize | Size of buffer for descriptor
|
|
// @parm PULONG | Copied | Bytes copied to buffer for descriptor
|
|
// @rdesc Returns NT status code
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_GetReportDescriptor (PGAMEPORT PortInfo, PUCHAR Descriptor, ULONG MaxSize, PULONG Copied)
|
|
{
|
|
PDEVICEINFO DevInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetReportDescriptor Enter\n", MSGAME_NAME));
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Zero returned size first
|
|
//
|
|
|
|
*Copied = 0;
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetReportDescriptor Called With No Device!\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//
|
|
// Check output buffer size
|
|
//
|
|
|
|
if (MaxSize < DevInfo->RptDescSize)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetReportDescriptor Buffer too small = %lu\n", MSGAME_NAME, MaxSize));
|
|
return (STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// Copy descriptor to output buffer
|
|
//
|
|
|
|
memcpy (Descriptor, DevInfo->RptDescriptor, DevInfo->RptDescSize);
|
|
|
|
//
|
|
// Return number bytes copied
|
|
//
|
|
|
|
*Copied = DevInfo->RptDescSize;
|
|
|
|
//
|
|
// Return status
|
|
//
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for Pnp Start Device IRP
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_StartDevice (PGAMEPORT PortInfo, PWCHAR HardwareId)
|
|
{
|
|
ULONG i, UnitId, Default = 0;
|
|
PGAMEPORT p, *Device;
|
|
PDEVICEINFO DevInfo = NULL;
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StartDevice Called For %ws\n", MSGAME_NAME, HardwareId));
|
|
|
|
//
|
|
// Try requested device based on HardwareId
|
|
//
|
|
|
|
for (i = 0; i < ARRAY_SIZE(MiniDrivers); i++)
|
|
if (MSGAME_CompareHardwareIds (HardwareId, MiniDrivers[i]->HardwareId))
|
|
{
|
|
Default = i;
|
|
if (NT_SUCCESS(MiniDrivers[i]->Services->ConnectDevice (PortInfo)))
|
|
DevInfo = MiniDrivers[i];
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If requested device fails, do a detect
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
DEVICE_DetectDevices (PortInfo);
|
|
DevInfo = GET_DEVICE_INFO (PortInfo);
|
|
}
|
|
|
|
//
|
|
// If detect fails, force the requested device
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
DevInfo = MiniDrivers[Default];
|
|
DevInfo->NumDevices++;
|
|
}
|
|
|
|
//
|
|
// Make sure these are set at this point
|
|
//
|
|
|
|
ASSERT(DevInfo);
|
|
SET_DEVICE_INFO(PortInfo, DevInfo);
|
|
|
|
//
|
|
// Add device and allocate unit id
|
|
//
|
|
|
|
DEVICE_AcquireDevice ();
|
|
UnitId = 0;
|
|
Device = &DevInfo->Siblings;
|
|
while (p = *Device)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StartDevice Reassigning UnitId From %lu to %lu\n", MSGAME_NAME, GET_DEVICE_UNIT(p), UnitId));
|
|
SET_DEVICE_UNIT(p, UnitId++);
|
|
Device = &GET_DEVICE_SIBLING(p);
|
|
}
|
|
*Device = PortInfo;
|
|
SET_DEVICE_UNIT(PortInfo, UnitId);
|
|
SET_DEVICE_SIBLING(PortInfo, NULL);
|
|
SET_DEVICE_ID(PortInfo, DevInfo->DeviceId);
|
|
DEVICE_ReleaseDevice ();
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StartDevice Assigned UnitId = %lu\n", MSGAME_NAME, UnitId));
|
|
|
|
//
|
|
// Increment device count
|
|
//
|
|
|
|
InterlockedIncrement (&DevInfo->DeviceCount);
|
|
if (DevInfo->DevicePending)
|
|
InterlockedDecrement (&DevInfo->DevicePending);
|
|
|
|
//
|
|
// Call the mini-driver to process
|
|
//
|
|
|
|
DevInfo->Services->StartDevice (PortInfo);
|
|
|
|
//
|
|
// Return success always
|
|
//
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for HID Read Report IRP
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PUCHAR | Report | Output buffer for report
|
|
// @parm ULONG | MaxSize | Size of buffer for report
|
|
// @parm PULONG | Copied | Bytes copied to buffer for report
|
|
// @rdesc Returns Returns NT status code
|
|
// @comm Public function <en->
|
|
// Performs hot-plugging on success or does device detection if no
|
|
// device selected or error.
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_ReadReport (PGAMEPORT PortInfo, PUCHAR Report, ULONG MaxSize, PULONG Copied)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICEINFO DevInfo;
|
|
DEVICE_PACKET Packet;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s: DEVICE_ReadReport Enter\n", MSGAME_NAME));
|
|
|
|
//
|
|
// Initialize packet members
|
|
//
|
|
|
|
memset (&Packet, 0, sizeof (Packet));
|
|
Packet.id = GET_DEVICE_UNIT (PortInfo);
|
|
|
|
//
|
|
// Check output buffer
|
|
//
|
|
|
|
if (MaxSize < sizeof (DEVICE_PACKET))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_ReadReport Bad Buffer Size = %lu\n", MSGAME_NAME, MaxSize));
|
|
return (STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// Skip if device changed
|
|
//
|
|
|
|
if (GET_DEVICE_DETECTED (PortInfo))
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_ReadReport Device In Process of Being Changed!\n", MSGAME_NAME));
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
goto DEVICE_ReadReport_Exit;
|
|
}
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo || !DeviceDetected)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_ReadReport Called With No Device!\n", MSGAME_NAME));
|
|
SuccessPackets[0] = 0;
|
|
ntStatus = DEVICE_DetectDevices (PortInfo);
|
|
if (!NT_SUCCESS (ntStatus))
|
|
goto DEVICE_ReadReport_Exit;
|
|
|
|
//
|
|
// Get pointer to new device
|
|
//
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
goto DEVICE_ReadReport_Exit;
|
|
}
|
|
|
|
//
|
|
// Call the mini-driver to process
|
|
//
|
|
|
|
ntStatus = DevInfo->Services->ReadReport (PortInfo, &Packet);
|
|
|
|
//
|
|
// Process returned status
|
|
//
|
|
|
|
if (NT_SUCCESS (ntStatus))
|
|
{
|
|
//
|
|
// Check for hot-plugging
|
|
//
|
|
|
|
ntStatus = DEVICE_HotPlugDevice (PortInfo);
|
|
PollingAttempts[Packet.id] = 0;
|
|
goto DEVICE_ReadReport_Exit;
|
|
}
|
|
else if (ntStatus == STATUS_DEVICE_BUSY)
|
|
{
|
|
//
|
|
// Access to port denied
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_ReadReport Device Busy\n", MSGAME_NAME));
|
|
goto DEVICE_ReadReport_Exit;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Force success if just transitory
|
|
//
|
|
|
|
if ((++PollingAttempts[Packet.id] <= MAX_POLLING_ATTEMPTS) && DeviceDetected)
|
|
{
|
|
MsGamePrint ((DBG_CRITICAL, "%s: DEVICE_ReadReport Force Success\n", MSGAME_NAME));
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
else if ((PollingAttempts[Packet.id] % MAX_POLLING_ATTEMPTS) == 0)
|
|
{
|
|
MsGamePrint ((DBG_CRITICAL, "%s: DEVICE_ReadReport Failed %lu In a Row\n", MSGAME_NAME, PollingAttempts[Packet.id]));
|
|
|
|
//
|
|
// Try and see what's out there
|
|
//
|
|
|
|
ntStatus = DEVICE_DetectDevices (PortInfo);
|
|
|
|
//
|
|
// If nothing found, destroy any siblings
|
|
//
|
|
|
|
if (NT_ERROR(ntStatus))
|
|
ntStatus = DEVICE_RemoveSiblings (PortInfo);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just bounce this request
|
|
//
|
|
|
|
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
//
|
|
// Clear sucessful packet counts
|
|
//
|
|
|
|
SuccessPackets[Packet.id] = 0;
|
|
}
|
|
|
|
//---------------------
|
|
DEVICE_ReadReport_Exit:
|
|
//---------------------
|
|
|
|
if( ntStatus == STATUS_DEVICE_BUSY)
|
|
{
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Return packet data always
|
|
//
|
|
|
|
memcpy (Report, &Packet, sizeof (Packet));
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
*Copied += sizeof (Packet);
|
|
}
|
|
else
|
|
*Copied = 0x0;
|
|
//
|
|
// Return status code
|
|
//
|
|
|
|
return (ntStatus);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for Pnp Stop Device IRP
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware)
|
|
{
|
|
ULONG UnitId;
|
|
PGAMEPORT p, *Device;
|
|
PDEVICEINFO DevInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_StopDevice Enter\n", MSGAME_NAME));
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_StopDevice Called With No Device!\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Received for %s[%lu]\n", MSGAME_NAME, DevInfo->DeviceName, GET_DEVICE_UNIT(PortInfo)));
|
|
|
|
//
|
|
// Remove sibling and reallocate unit ids
|
|
//
|
|
|
|
DEVICE_AcquireDevice ();
|
|
UnitId = 0;
|
|
Device = &DevInfo->Siblings;
|
|
while (p = *Device)
|
|
{
|
|
if (p == PortInfo)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Unlinking UnitId = %lu\n", MSGAME_NAME, GET_DEVICE_UNIT(p)));
|
|
*Device = GET_DEVICE_SIBLING(p);
|
|
}
|
|
else
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Reassigning UnitId From %lu to %lu\n", MSGAME_NAME, GET_DEVICE_UNIT(p), UnitId));
|
|
SET_DEVICE_UNIT(p, UnitId++);
|
|
}
|
|
Device = &GET_DEVICE_SIBLING(p);
|
|
}
|
|
DEVICE_ReleaseDevice ();
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DEVICE_StopDevice Released UnitId = %lu\n", MSGAME_NAME, GET_DEVICE_UNIT (PortInfo)));
|
|
|
|
//
|
|
// Decrement device count
|
|
//
|
|
|
|
InterlockedDecrement (&DevInfo->DeviceCount);
|
|
if (DevInfo->DevicePending)
|
|
InterlockedIncrement (&DevInfo->DevicePending);
|
|
|
|
//
|
|
// Call the mini-driver to process
|
|
//
|
|
|
|
return (DevInfo->Services->StopDevice (PortInfo, TouchHardware));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for HID Get Feature IRP
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm HID_REPORT_ID | ReportId | HID feature report id
|
|
// @parm PUCHAR | ReportBuffer | Output buffer for report
|
|
// @parm ULONG | ReportSize | Size of buffer for report
|
|
// @parm PULONG | Returned | Bytes copied to buffer for report
|
|
// @rdesc Returns Returns NT status code
|
|
// @comm Public function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS DEVICE_GetFeature (PGAMEPORT PortInfo, HID_REPORT_ID ReportId, PVOID ReportBuffer, ULONG ReportSize, PULONG Returned)
|
|
{
|
|
PDEVICEINFO DevInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetFeature Enter\n", MSGAME_NAME));
|
|
|
|
//
|
|
// Get pointer to this device
|
|
//
|
|
|
|
DevInfo = GET_DEVICE_INFO(PortInfo);
|
|
|
|
//
|
|
// Skip if no device detected
|
|
//
|
|
|
|
if (!DevInfo)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetFeature Called With No Device!\n", MSGAME_NAME));
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//
|
|
// Skip if features not supported
|
|
//
|
|
|
|
if (!DevInfo->Services->GetFeature)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s: DEVICE_GetFeature Called With No Mini-Driver Support!\n", MSGAME_NAME));
|
|
return (STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
//
|
|
// Call the mini-driver to process
|
|
//
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s: DEVICE_GetFeature For ReportId = %lu\n", MSGAME_NAME, (ULONG)ReportId));
|
|
return (DevInfo->Services->GetFeature (PortInfo, ReportId, ReportBuffer, ReportSize, Returned));
|
|
}
|