Leaked source code of windows server 2003
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

//**************************************************************************
//
// 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));
}