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