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.
1424 lines
39 KiB
1424 lines
39 KiB
//**************************************************************************
|
|
//
|
|
// SWGAMPAD.C -- Xena Gaming Project
|
|
//
|
|
// Version 3.XX
|
|
//
|
|
// Copyright (c) 1997 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// @doc
|
|
// @module SWGAMPAD.C | Gameport mini-driver for GamePads
|
|
//**************************************************************************
|
|
|
|
#ifndef SAITEK
|
|
#include "msgame.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Definitions
|
|
//---------------------------------------------------------------------------
|
|
|
|
#define DEVICENAME "SWGAMPAD"
|
|
#define DEVICE_PID 0x0003
|
|
#define HARDWARE_ID L"Gameport\\SideWindergamepad\0\0"
|
|
|
|
//
|
|
// Packet Constants
|
|
//
|
|
|
|
#define GAME_PACKET_SIZE 32
|
|
#define GAME_PACKET_BUTTONS 10
|
|
|
|
#define GAME_Y_UP_BIT 0x01
|
|
#define GAME_Y_DOWN_BIT 0x02
|
|
#define GAME_Y_BITS (GAME_Y_UP_BIT|GAME_Y_DOWN_BIT)
|
|
#define GAME_X_LEFT_BIT 0x04
|
|
#define GAME_X_RIGHT_BIT 0x08
|
|
#define GAME_X_BITS (GAME_X_LEFT_BIT|GAME_X_RIGHT_BIT)
|
|
#define GAME_BUTTON_BITS 0x3ff0
|
|
|
|
//
|
|
// Id Definitions
|
|
//
|
|
|
|
#define GAME_ID_STRING "H0003"
|
|
|
|
//
|
|
// Timing Constants
|
|
//
|
|
|
|
#define PACKET_START_TIMEOUT 500
|
|
#define PACKET_LOWHIGH_TIMEOUT 75
|
|
#define PACKET_HIGHLOW_TIMEOUT 150
|
|
#define PACKET_INTERRUPT_DELAY 45
|
|
#define ID_START_TIMEOUT 500
|
|
#define ID_LOWHIGH_TIMEOUT 75
|
|
#define ID_HIGHLOW_TIMEOUT 150
|
|
#define MAX_CLOCK_DUTY_CYCLE 50
|
|
|
|
#define MAX_STD_SCLKS 150
|
|
|
|
//
|
|
// Joystick Extents
|
|
//
|
|
|
|
#define EXTENTS_X_MIN 1
|
|
#define EXTENTS_X_MID 0x80
|
|
#define EXTENTS_X_MAX 0xff
|
|
#define EXTENTS_Y_MIN 1
|
|
#define EXTENTS_Y_MID 0x80
|
|
#define EXTENTS_Y_MAX 0xff
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Types
|
|
//---------------------------------------------------------------------------
|
|
|
|
typedef struct
|
|
{ // @struct SWGAMPAD_ID | GamePad Id String
|
|
#pragma pack(1)
|
|
UCHAR OpenParen; // @field Open parentheses
|
|
UCHAR EisaId[5]; // @field Eisa bus Id
|
|
USHORT Version[3]; // @field Firmware version
|
|
UCHAR CloseParen; // @field Close parentheses
|
|
UCHAR Reserved[22]; // @field Reserved
|
|
#pragma pack()
|
|
} SWGAMPAD_ID, *PSWGAMPAD_ID;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Procedures
|
|
//---------------------------------------------------------------------------
|
|
|
|
static VOID SWGAMPAD_Calibrate (PGAMEPORT PortInfo);
|
|
static BOOLEAN SWGAMPAD_GoAnalog (PPACKETINFO Packet1, PPACKETINFO Packet2);
|
|
|
|
static BOOLEAN SWGAMPAD_ReadId (PPACKETINFO DataPacket, PPACKETINFO IdPacket);
|
|
static BOOLEAN SWGAMPAD_GetId (PPACKETINFO IdPacket);
|
|
|
|
static NTSTATUS SWGAMPAD_ReadData (PPACKETINFO DataPacket);
|
|
static BOOLEAN SWGAMPAD_Read1Wide (PPACKETINFO DataPacket);
|
|
static BOOLEAN SWGAMPAD_Read3Wide (PPACKETINFO DataPacket);
|
|
static BOOLEAN SWGAMPAD_ValidateData (PPACKETINFO DataPacket);
|
|
static VOID SWGAMPAD_ProcessData (ULONG UnitId, USHORT Data[], PDEVICE_PACKET Report);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Services
|
|
//---------------------------------------------------------------------------
|
|
|
|
static NTSTATUS SWGAMPAD_DriverEntry (VOID);
|
|
static NTSTATUS SWGAMPAD_ConnectDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS SWGAMPAD_StartDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS SWGAMPAD_ReadReport (PGAMEPORT PortInfo, PDEVICE_PACKET Report);
|
|
static NTSTATUS SWGAMPAD_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Alloc_text pragma to specify routines that can be paged out.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, SWGAMPAD_DriverEntry)
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Private Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
// HID Descriptors
|
|
//
|
|
|
|
static UCHAR ReportDescriptor[] =
|
|
{
|
|
HIDP_GLOBAL_USAGE_PAGE_1, HID_USAGE_PAGE_GENERIC, // USAGE_PAGE (Generic Desktop)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK,// USAGE (Joystick)
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_APP, // COLLECTION (Application)
|
|
|
|
//id
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//do_other
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//dwX / dwY
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_POINTER, // USAGE (Pointer)
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Linked)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x01, // LOGICAL_MINIMUM (1)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0xFF, 0x00, 0x00, 0x00, // LOGICAL_MAXIMUM (255)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x01, // PHYSICAL_MINIMUM (1)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0xFF, 0x00, 0x00, 0x00, // PHYSICAL_MAXIMUM (255)
|
|
HIDP_GLOBAL_UNIT_2, 0x00, 0x00, // UNIT (None)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (32)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_X, // USAGE (X)
|
|
HIDP_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,Abs)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_Y, // USAGE (Y)
|
|
HIDP_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,Abs)
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//dwZ
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (32)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//dwR
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (32)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//dwU
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//dwV
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//dwPOV
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (32)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//dwButtons
|
|
HIDP_GLOBAL_USAGE_PAGE_1, HID_USAGE_PAGE_BUTTON, // USAGE_PAGE (Button)
|
|
HIDP_LOCAL_USAGE_MIN_1, 0x01, // USAGE_MINIMUM (Button 1)
|
|
HIDP_LOCAL_USAGE_MAX_1, 0x0A, // USAGE_MAXIMUM (Button 10)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_1, 0x01, // LOGICAL_MAXIMUM (1)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_1, 0x01, // PHYSICAL_MAXIMUM (1)
|
|
HIDP_GLOBAL_UNIT_2, 0x00, 0x00, // UNIT (None)
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x01, // REPORT_SIZE (1)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x20, // REPORT_COUNT (32)
|
|
HIDP_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,Abs)
|
|
|
|
//dwButtonNumber
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_MAIN_INPUT_1, 0x01, // INPUT (Cnst,Ary,Abs)
|
|
|
|
//END OF COLLECTION
|
|
HIDP_MAIN_ENDCOLLECTION // END_COLLECTION
|
|
};
|
|
|
|
static HID_DESCRIPTOR DeviceDescriptor =
|
|
{
|
|
sizeof (HID_DESCRIPTOR),
|
|
HID_HID_DESCRIPTOR_TYPE,
|
|
MSGAME_HID_VERSION,
|
|
MSGAME_HID_COUNTRY,
|
|
MSGAME_HID_DESCRIPTORS,
|
|
{HID_REPORT_DESCRIPTOR_TYPE,
|
|
sizeof(ReportDescriptor)}
|
|
};
|
|
|
|
//
|
|
// Raw Data Buffer
|
|
//
|
|
|
|
static USHORT RawData[GAME_PACKET_SIZE/sizeof(USHORT)] =
|
|
{
|
|
0
|
|
};
|
|
//
|
|
// Raw Id Buffer
|
|
//
|
|
|
|
static SWGAMPAD_ID RawId =
|
|
{
|
|
0
|
|
};
|
|
|
|
//
|
|
// Timing Variables
|
|
//
|
|
|
|
static DEVICE_VALUES Delays =
|
|
{
|
|
PACKET_START_TIMEOUT,
|
|
PACKET_HIGHLOW_TIMEOUT,
|
|
PACKET_LOWHIGH_TIMEOUT,
|
|
ID_START_TIMEOUT,
|
|
ID_HIGHLOW_TIMEOUT,
|
|
ID_LOWHIGH_TIMEOUT,
|
|
PACKET_INTERRUPT_DELAY,
|
|
MAX_CLOCK_DUTY_CYCLE,
|
|
0,0,0,0 // No status packet used
|
|
};
|
|
|
|
//
|
|
// Data Packet Info
|
|
//
|
|
|
|
static PACKETINFO DataInfo =
|
|
{
|
|
sizeof (PACKETINFO), // Size of structure
|
|
DEVICENAME, // Name of device
|
|
MSGAME_TRANSACT_NONE, // Transaction type
|
|
IMODE_DIGITAL_STD, // Interface mode
|
|
GAME_SPEED_100K, // Transmission speed
|
|
ERROR_SUCCESS, // Last internal error result
|
|
{0}, // Game port info
|
|
FLAG_WAIT_FOR_CLOCK, // Packet acquisition mode
|
|
1, // Number of packets received
|
|
0, // Last valid acquisition time stamp
|
|
0, // Number of clocks sampled
|
|
0, // Number of B4 line transitions (std mode only)
|
|
0, // Start timeout period (in samples)
|
|
0, // Clock High to Low timeout period (in samples)
|
|
0, // Clock Low to High timeout period (in samples)
|
|
0, // Interrupt Timeout period
|
|
0, // Maximum clock duty cycle
|
|
0, // Number of Packet Failures
|
|
0, // Number of Packet Attempts
|
|
sizeof (RawData), // Size of raw data buffer
|
|
RawData // Pointer to Raw data
|
|
};
|
|
|
|
//
|
|
// ID Packet Info
|
|
//
|
|
|
|
static PACKETINFO IdInfo =
|
|
{
|
|
sizeof (PACKETINFO), // Size of structure
|
|
DEVICENAME, // Name of device
|
|
MSGAME_TRANSACT_NONE, // Transaction type
|
|
IMODE_DIGITAL_STD, // Interface mode
|
|
GAME_SPEED_100K, // Transmission speed
|
|
ERROR_SUCCESS, // Last internal error result
|
|
{0}, // Game port info
|
|
FLAG_START_CLOCK_LOW, // Packet acquisition mode
|
|
1, // Number of packets received
|
|
0, // Last valid acquisition time stamp
|
|
0, // Number of clocks sampled
|
|
0, // Number of B4 line transitions (std mode only)
|
|
0, // Start timeout period (in samples)
|
|
0, // Clock High to Low timeout period (in samples)
|
|
0, // Clock Low to High timeout period (in samples)
|
|
0, // Interrupt Timeout period
|
|
0, // Maximum clock duty cycle
|
|
0, // Number of Packet Failures
|
|
0, // Number of Packet Attempts
|
|
sizeof (RawId), // Size of raw id buffer
|
|
&RawId // Pointer to Raw data
|
|
};
|
|
|
|
//
|
|
// Services Table
|
|
//
|
|
|
|
static DRIVERSERVICES Services =
|
|
{
|
|
SWGAMPAD_DriverEntry, // DriverEntry
|
|
SWGAMPAD_ConnectDevice, // ConnectDevice
|
|
SWGAMPAD_StartDevice, // StartDevice
|
|
SWGAMPAD_ReadReport, // ReadReport
|
|
SWGAMPAD_StopDevice, // StopDevice
|
|
NULL // GetFeature
|
|
};
|
|
|
|
//
|
|
// Last Valid Data
|
|
//
|
|
|
|
static USHORT ValidData[GAME_PACKET_SIZE/sizeof(USHORT)] =
|
|
{
|
|
GAME_BUTTON_BITS,
|
|
GAME_BUTTON_BITS,
|
|
GAME_BUTTON_BITS,
|
|
GAME_BUTTON_BITS
|
|
};
|
|
|
|
//
|
|
// Interrupt Flags
|
|
//
|
|
|
|
static UCHAR InterruptFlags = 0;
|
|
|
|
//
|
|
// Hardware ID String
|
|
//
|
|
|
|
static WCHAR HardwareId[] = HARDWARE_ID;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Public Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
public DEVICEINFO GamePadInfo =
|
|
{
|
|
&Services, // Service table
|
|
NULL, // Sibling device list
|
|
&DeviceDescriptor, // Device descriptor data
|
|
ReportDescriptor, // Report descriptor data
|
|
sizeof(ReportDescriptor), // Report descriptor size
|
|
0, // Number of devices detected
|
|
0, // Number of devices started
|
|
0, // Number of devices pending
|
|
DEVICENAME, // Name of device
|
|
DETECT_NORMAL, // Detection order
|
|
FALSE, // Analog device flag
|
|
DEVICE_PID, // Hid device identifier
|
|
HardwareId // PnP hardware identifier
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads registry timing values and calibrates them
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns nothing
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID SWGAMPAD_Calibrate (PGAMEPORT PortInfo)
|
|
{
|
|
MsGamePrint((DBG_INFORM,"SWGAMPAD: SWGAMPAD_Calibrate Enter\n"));
|
|
|
|
//
|
|
// Convert timing values to counts
|
|
//
|
|
|
|
DataInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: DataInfo.StartTimeout = %ld\n", DataInfo.StartTimeout));
|
|
DataInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: DataInfo.LowHighTimeout = %ld\n", DataInfo.LowHighTimeout));
|
|
DataInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: DataInfo.HighLowTimeout = %ld\n", DataInfo.HighLowTimeout));
|
|
IdInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.IdStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: IdInfo.StartTimeout = %ld\n", IdInfo.StartTimeout));
|
|
IdInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.IdLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: IdInfo.LowHighTimeout=%ld\n", IdInfo.LowHighTimeout));
|
|
IdInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.IdHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: IdInfo.HighLowTimeout=%ld\n", IdInfo.HighLowTimeout));
|
|
DataInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: DataInfo.ClockDutyCycle = %ld\n", DataInfo.ClockDutyCycle));
|
|
IdInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: IdInfo.ClockDutyCycle = %ld\n", IdInfo.ClockDutyCycle));
|
|
DataInfo.InterruptDelay = Delays.InterruptDelay;
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: DataInfo.InterruptDelay = %ld\n", DataInfo.InterruptDelay));
|
|
IdInfo.InterruptDelay = Delays.InterruptDelay;
|
|
MsGamePrint((DBG_VERBOSE, "SWGAMPAD: IdInfo.InterruptDelay = %ld\n", IdInfo.InterruptDelay));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Puts Gamepads into analog mode
|
|
// @parm PPACKETINFO | Packet1 | Data packet
|
|
// @parm PPACKETINFO | Packet2 | Id packet
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWGAMPAD_GoAnalog (PPACKETINFO Packet1, PPACKETINFO Packet2)
|
|
{
|
|
LONG Result = ERROR_SUCCESS;
|
|
PGAMEPORT PortInfo = &Packet1->PortInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_ResetDevice enter\n"));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
InterruptFlags = INTERRUPT_AFTER_PACKET;
|
|
Packet1->B4Transitions = 0;
|
|
Packet1->ClocksSampled = 0;
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
if (!(PORTIO_Read (PortInfo) & XA_BIT_MASK))
|
|
{
|
|
Result = ERROR_XA_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
if (Packet1->Mode == IMODE_DIGITAL_ENH)
|
|
SWGAMPAD_Read3Wide (Packet1);
|
|
else SWGAMPAD_Read1Wide (Packet1);
|
|
Packet2->B4Transitions = 0;
|
|
Packet2->ClocksSampled = 0;
|
|
if (Packet2->Mode == IMODE_DIGITAL_ENH)
|
|
{
|
|
SWGAMPAD_Read3Wide (Packet2);
|
|
Result = Packet2->LastError;
|
|
}
|
|
else SWGAMPAD_Read1Wide (Packet2);
|
|
}
|
|
|
|
Packet1->B4Transitions = 0;
|
|
Packet1->ClocksSampled = 0;
|
|
Packet2->B4Transitions = 0;
|
|
Packet2->ClocksSampled = 0;
|
|
InterruptFlags = 0;
|
|
|
|
DataInfo.LastError = Result;
|
|
DataInfo.Transaction = MSGAME_TRANSACT_GOANALOG;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
if (Result == ERROR_SUCCESS)
|
|
DataInfo.Mode = IdInfo.Mode = IMODE_ANALOG;
|
|
else MsGamePrint ((DBG_SEVERE, "SWGAMPAD_ResetDevice (GoAnalog) Failed\n"));
|
|
|
|
MSGAME_PostTransaction (&DataInfo);
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads device id string from port
|
|
// @parm PPACKETINFO | DataPacket | Data packet parameters
|
|
// @parm PPACKETINFO | IdPacket | ID packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWGAMPAD_ReadId (PPACKETINFO DataPacket, PPACKETINFO IdPacket)
|
|
{
|
|
LONG Result = ERROR_SUCCESS;
|
|
PGAMEPORT PortInfo = &DataPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_ReadId enter\n"));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
InterruptFlags = INTERRUPT_AFTER_PACKET;
|
|
DataPacket->B4Transitions = 0;
|
|
DataPacket->ClocksSampled = 0;
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
if (!(PORTIO_Read (PortInfo) & XA_BIT_MASK))
|
|
{
|
|
Result = ERROR_XA_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
if (DataPacket->Mode == IMODE_DIGITAL_ENH)
|
|
SWGAMPAD_Read3Wide (DataPacket);
|
|
else SWGAMPAD_Read1Wide (DataPacket);
|
|
InterruptFlags = 0;
|
|
IdPacket->B4Transitions = 0;
|
|
IdPacket->ClocksSampled = 0;
|
|
SWGAMPAD_Read1Wide (IdPacket);
|
|
Result = IdPacket->LastError;
|
|
}
|
|
|
|
IdPacket->LastError = Result;
|
|
IdPacket->Transaction = MSGAME_TRANSACT_ID;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
if (Result != ERROR_SUCCESS)
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_GetId Failed\n"));
|
|
|
|
MSGAME_PostTransaction (IdPacket);
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads and validates device id string
|
|
// @parm PPACKETINFO | IdPacket | ID Packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWGAMPAD_GetId (PPACKETINFO IdPacket)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_GetId enter\n"));
|
|
|
|
IdPacket->Attempts++;
|
|
|
|
if (SWGAMPAD_ReadId (&DataInfo, IdPacket))
|
|
{
|
|
ULONG i;
|
|
PUSHORT p;
|
|
PSWGAMPAD_ID pId;
|
|
//
|
|
// Remove parity bit and convert to words
|
|
//
|
|
p = IdPacket->Data;
|
|
for (i = 0; i < 5; i++, p++)
|
|
*p = ((*p<<1) & 0x7f00) | (*p & 0x7f);
|
|
//
|
|
// Check Id String
|
|
//
|
|
pId = (PSWGAMPAD_ID)IdPacket->Data;
|
|
if (!strncmp (pId->EisaId, GAME_ID_STRING, strlen(GAME_ID_STRING)))
|
|
{
|
|
if (IdPacket->B4Transitions > 10)
|
|
{
|
|
DataInfo.Mode = IdInfo.Mode = IMODE_DIGITAL_ENH;
|
|
}
|
|
else
|
|
{
|
|
SWGAMPAD_GoAnalog (&DataInfo, &IdInfo);
|
|
DataInfo.Mode = IdInfo.Mode = IMODE_DIGITAL_STD;
|
|
}
|
|
Result = TRUE;
|
|
}
|
|
else MsGamePrint ((DBG_SEVERE, "SWGAMPAD_GetId - Id string did not match = 0x%X\n", (ULONG)(*(PULONG)&pId->EisaId)));
|
|
}
|
|
|
|
if (!Result)
|
|
IdPacket->Failures++;
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads 1 wide data packet from gameport
|
|
// @parm PPACKETINFO | DataPacket| Data packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
#if _MSC_FULL_VER >= 13008827 && defined(_M_IX86)
|
|
#pragma warning(disable:4731) // EBP modified with inline asm
|
|
#endif
|
|
|
|
BOOLEAN SWGAMPAD_Read1Wide (PPACKETINFO DataPacket)
|
|
{
|
|
LONG Result;
|
|
|
|
// MsGamePrint ((DBG_VERBOSE, "SWGAMPAD_Read1Wide enter\n"));
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
push ebp
|
|
|
|
mov edi, DataPacket
|
|
mov esi, (PPACKETINFO [edi]).Data
|
|
lea edx, (PPACKETINFO [edi]).PortInfo
|
|
mov ebx, 10000h
|
|
xor ebp, ebp
|
|
xor eax, eax
|
|
|
|
test (PPACKETINFO [edi]).Acquisition, FLAG_START_CLOCK_LOW
|
|
jnz Std_StartClockLow
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, (PPACKETINFO [edi]).StartTimeout
|
|
|
|
Std_StartClockHigh:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Start of Packet ?
|
|
jz Std_StartHighToLow ; Y: jump
|
|
dec ecx
|
|
jnz Std_StartClockHigh ; else keep looping
|
|
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp PacketDone ; Time out error.
|
|
|
|
Std_StartHighToLow:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).StartTimeout
|
|
|
|
Std_StartHighToLow_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock = 0
|
|
jz Std_LowToHigh ; Y: jump.
|
|
dec ecx
|
|
jnz Std_StartHighToLow_1 ; else see if we timed out
|
|
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp PacketDone ; Time out error.
|
|
|
|
Std_StartClockLow:
|
|
|
|
; wait for clock to transition to "high" (sample immediately)
|
|
|
|
mov ecx, (PPACKETINFO [edi]).StartTimeout
|
|
|
|
Std_StartClockLow_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock went high ?
|
|
jnz CollectData ; Y: jump (sample data)
|
|
dec ecx
|
|
jnz Std_StartClockLow_1 ; else keep looping
|
|
|
|
mov eax, ERROR_CLOCKRISING
|
|
jmp PacketDone ; Time out error.
|
|
|
|
Std_CheckClkState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK
|
|
jz Std_LowToHigh
|
|
|
|
;Std_HighToLow:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).HighLowTimeout
|
|
|
|
Std_HighToLow_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock = 0
|
|
jz Std_LowToHigh ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz Std_HighToLow_1 ; else see if we timed out
|
|
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp PacketDone ; Time out error.
|
|
|
|
Std_LowToHigh:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).LowHighTimeout
|
|
|
|
Std_LowToHigh_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock high ?
|
|
jnz CollectData ; Y: jump. (get data)
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx ; else see if we timed out
|
|
jnz Std_LowToHigh_1
|
|
jmp Std_TestInterrupt
|
|
|
|
CollectData:
|
|
|
|
inc ebp
|
|
cmp ebp, MAX_STD_SCLKS
|
|
jg Std_BufferOverFlow
|
|
xor ah, al
|
|
test ah, DATA2_BIT_MASK ; Q: Data 2 is toggled ?
|
|
jz CollectData_1 ; N: jump.
|
|
inc (PPACKETINFO [edi]).B4Transitions ; Y: increment Data 2 count.
|
|
|
|
CollectData_1:
|
|
|
|
mov ah, al
|
|
shr al, 6 ; put data into carry
|
|
rcr bx, 1 ; and then in data counter
|
|
add ebx, 10000h ; inc mini packet clk counter
|
|
test ebx, 100000h ; Q: done mini packet ?
|
|
jz Std_CheckClkState ; N: jump.
|
|
shr bx, 1 ; right align
|
|
mov word ptr [esi], bx ; move mini packet into buffer
|
|
add esi, 2 ; advance data pointer
|
|
mov ebx, 10000h ; init mini-packet counter
|
|
jmp Std_CheckClkState ; go look for more clocks.
|
|
|
|
Std_TestInterrupt:
|
|
|
|
test InterruptFlags, INTERRUPT_AFTER_PACKET; Q: Interrupt packet ?
|
|
jnz Std_IntPacket ; Y: jump.
|
|
|
|
mov eax, ERROR_SUCCESS
|
|
jmp PacketDone
|
|
|
|
Std_IntPacket:
|
|
|
|
mov ecx, 700
|
|
|
|
Std_IntPacket_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, INTXA_BIT_MASK
|
|
jz Std_IntPacket_2
|
|
loop Std_IntPacket_1
|
|
mov eax, ERROR_XA_TIMEOUT
|
|
jmp PacketDone
|
|
|
|
Std_IntPacket_2:
|
|
|
|
cmp ecx, 700
|
|
je Std_IntOut
|
|
mov ecx, (PPACKETINFO [edi]).InterruptDelay
|
|
|
|
Std_IntPacket_3:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, al
|
|
xchg al, ah
|
|
dec ecx
|
|
jnz Std_IntPacket_3
|
|
|
|
Std_IntOut:
|
|
|
|
push 0 ; write byte to gameport
|
|
push edx
|
|
call PORTIO_Write
|
|
|
|
mov eax, ERROR_SUCCESS
|
|
jmp PacketDone
|
|
|
|
Std_BufferOverFlow:
|
|
|
|
mov eax, ERROR_CLOCKOVERFLOW
|
|
|
|
PacketDone:
|
|
|
|
mov (PPACKETINFO [edi]).ClocksSampled, ebp
|
|
|
|
pop ebp
|
|
pop esi
|
|
pop edi
|
|
|
|
mov Result, eax
|
|
}
|
|
|
|
DataPacket->LastError = Result;
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read1Wide - TimeOut@LowClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read1Wide - TimeOut@HighClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read1Wide - TimeOut@ClockFalling, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read1Wide - TimeOut@ClockRising, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads 3 wide data packet from gameport
|
|
// @parm PPACKETINFO | DataPacket| Data packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWGAMPAD_Read3Wide (PPACKETINFO DataPacket)
|
|
{
|
|
LONG Result;
|
|
|
|
// MsGamePrint ((DBG_VERBOSE, "SWGAMPAD_Read3Wide enter\n"));
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
push ebp
|
|
|
|
mov edi, DataPacket
|
|
mov esi, (PPACKETINFO [edi]).Data
|
|
lea edx, (PPACKETINFO [edi]).PortInfo
|
|
|
|
xor eax, eax
|
|
xor ebx, ebx
|
|
xor ebp, ebp
|
|
|
|
;StartEnhancedMode:
|
|
|
|
test (PPACKETINFO [edi]).Acquisition, FLAG_START_CLOCK_LOW
|
|
jnz Enh_LowToHigh
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, (PPACKETINFO [edi]).StartTimeout
|
|
|
|
StartEnhancedMode_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Start of Packet ?
|
|
jnz Enh_StartHighToLow ; Y: jump
|
|
dec ecx
|
|
jnz StartEnhancedMode_1 ; else keep looping
|
|
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp Enh_PacketDone ; Time out error.
|
|
|
|
Enh_StartHighToLow:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).StartTimeout
|
|
|
|
Enh_StartHighToLow_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock = 0
|
|
jz Enh_LowToHigh ; Y: jump.
|
|
dec ecx
|
|
jnz Enh_StartHighToLow_1 ; else see if we timed out
|
|
|
|
mov eax, ERROR_HIGHCLOCKSTART
|
|
jmp Enh_PacketDone ; Time out error.
|
|
|
|
;Enh_StartClockLow:
|
|
|
|
; wait for clock to transition to "high" (sample immediately)
|
|
|
|
mov ecx, (PPACKETINFO [edi]).StartTimeout
|
|
|
|
Enh_StartClockLow_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock went high ?
|
|
jnz Enh_CollectData ; Y: jump (sample data)
|
|
dec ecx
|
|
jnz Enh_StartClockLow_1 ; else keep looping
|
|
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp Enh_PacketDone ; Time out error.
|
|
|
|
Enh_CheckClkState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK
|
|
jz Enh_LowToHigh
|
|
|
|
; Wait for clock to transition from high to low.
|
|
|
|
;Enh_HighToLow:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).HighLowTimeout
|
|
|
|
Enh_HighToLow_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock Low ?
|
|
jz Enh_LowToHigh ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz Enh_HighToLow_1 ; if !Timeout continue looping.
|
|
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp Enh_PacketDone ; Time out error.
|
|
|
|
; Wait for clock to transition from low to high.
|
|
|
|
Enh_LowToHigh:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).LowHighTimeout
|
|
|
|
Enh_LowToHigh_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1 ?
|
|
jnz Enh_CollectData ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz Enh_LowToHigh_1 ; else continue looping.
|
|
jmp Enh_TestInterrupt
|
|
|
|
Enh_CollectData:
|
|
|
|
inc ebp ; inc. total clocks sampled
|
|
|
|
test ebp, 40h
|
|
jnz Enh_BufferOverflow
|
|
|
|
shr al, 5 ; move data to lower 3 bits
|
|
shrd ebx, eax, 3 ; shift data into ebx.
|
|
add ebp, 10000h ; inc hiword of ebp
|
|
mov eax, ebp
|
|
shr eax, 16 ; set ax = hiword of ebp
|
|
cmp al, 5 ; Q: mini-packet done ?
|
|
jne Enh_CheckClkState ; N: jump.
|
|
shr ebx, 17
|
|
mov word ptr [esi],bx
|
|
add esi, 2
|
|
and ebp, 0ffffh ; zero out hiword of ebp
|
|
jmp Enh_CheckClkState
|
|
|
|
|
|
Enh_TestInterrupt:
|
|
|
|
test InterruptFlags, INTERRUPT_AFTER_PACKET ; Q: Interrupt packet ?
|
|
jz Enh_PacketOK ; N: jump.
|
|
|
|
; Wait for XA line to be cleared before we can fire interrupt.
|
|
|
|
mov ecx, 700
|
|
|
|
Enh_Interrupt:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, INTXA_BIT_MASK
|
|
jz Enh_Interrupt_1
|
|
loop Enh_Interrupt
|
|
|
|
mov eax, ERROR_XA_TIMEOUT
|
|
jmp Enh_PacketDone
|
|
|
|
Enh_Interrupt_1:
|
|
|
|
mov ecx, (PPACKETINFO [edi]).InterruptDelay
|
|
|
|
Enh_Interrupt_2:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, al
|
|
dec ecx
|
|
jnz Enh_Interrupt_2
|
|
|
|
push 0 ; write byte to gameport
|
|
push edx
|
|
call PORTIO_Write
|
|
|
|
Enh_PacketOK:
|
|
|
|
and ebp, 0ffffh
|
|
mov (PPACKETINFO [edi]).ClocksSampled, ebp
|
|
mov eax, ERROR_SUCCESS
|
|
jmp Enh_PacketDone
|
|
|
|
Enh_BufferOverflow:
|
|
|
|
mov eax, ERROR_CLOCKOVERFLOW
|
|
|
|
Enh_PacketDone:
|
|
|
|
pop ebp
|
|
pop esi
|
|
pop edi
|
|
|
|
mov Result, eax
|
|
}
|
|
|
|
DataPacket->LastError = Result;
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read3Wide - TimeOut@LowClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read3Wide - TimeOut@HighClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read3Wide - TimeOut@ClockFalling, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_Read3Wide - TimeOut@ClockRising, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads data packet from gameport depending on mode
|
|
// @parm PPACKETINFO | DataPacket| Data packet parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWGAMPAD_ReadData (PPACKETINFO DataPacket)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
PGAMEPORT PortInfo = &DataPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SWGAMPAD_ReadData enter\n"));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (STATUS_DEVICE_BUSY);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
DataPacket->ClocksSampled = 0;
|
|
DataPacket->B4Transitions = 0;
|
|
InterruptFlags = 0;
|
|
|
|
switch (DataPacket->Mode)
|
|
{
|
|
case IMODE_DIGITAL_STD:
|
|
PORTIO_Write (PortInfo, 0);
|
|
Result = SWGAMPAD_Read1Wide (DataPacket);
|
|
break;
|
|
|
|
case IMODE_DIGITAL_ENH:
|
|
PORTIO_Write (PortInfo, 0);
|
|
Result = SWGAMPAD_Read3Wide (DataPacket);
|
|
break;
|
|
|
|
default:
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_ReadData - unknown interface\n"));
|
|
break;
|
|
}
|
|
|
|
DataPacket->TimeStamp = TIMER_GetTickCount ();
|
|
DataPacket->Transaction = MSGAME_TRANSACT_DATA;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
MSGAME_PostTransaction (DataPacket);
|
|
|
|
if (!Result)
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Validates raw packet information
|
|
// @parm PPACKETINFO | DataPacket| Data packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWGAMPAD_ValidateData (PPACKETINFO DataPacket)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
PVOID Data = DataPacket->Data;
|
|
ULONG Packets = DataPacket->NumPackets;
|
|
ULONG Clocks = DataPacket->ClocksSampled;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SWGAMPAD_ValidateData enter\n"));
|
|
|
|
if ((Clocks % 5) || (Clocks > 20))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_ValidateData - wrong number of clocks = %lu\n", Clocks));
|
|
return (Result);
|
|
}
|
|
|
|
__asm
|
|
{
|
|
mov esi, Data
|
|
mov ecx, Packets
|
|
|
|
ValidateLoop:
|
|
|
|
mov ax, [esi]
|
|
xor al, ah
|
|
jpo ValidateDone
|
|
add esi, 2
|
|
loop ValidateLoop
|
|
mov Result, TRUE
|
|
|
|
ValidateDone:
|
|
}
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Converts raw packet information to HID report
|
|
// @parm ULONG | UnitId | UnitId for this device
|
|
// @parm USHORT[] | Data | Pointer to raw data buffer
|
|
// @parm PDEVICE_PACKET | Report | Pointer to device packet
|
|
// @rdesc Returns nothing
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID SWGAMPAD_ProcessData (ULONG UnitId, USHORT Data[], PDEVICE_PACKET Report)
|
|
{
|
|
ULONG B1;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SWGAMPAD_ProcessData enter\n"));
|
|
|
|
//
|
|
// Process X Axis
|
|
//
|
|
|
|
switch (Data[UnitId] & GAME_X_BITS)
|
|
{
|
|
case GAME_X_LEFT_BIT:
|
|
Report->dwX = EXTENTS_X_MIN;
|
|
break;
|
|
|
|
case GAME_X_RIGHT_BIT:
|
|
Report->dwX = EXTENTS_X_MAX;
|
|
break;
|
|
|
|
default:
|
|
Report->dwX = EXTENTS_X_MID;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Process Y Axis
|
|
//
|
|
|
|
switch (Data[UnitId] & GAME_Y_BITS)
|
|
{
|
|
case GAME_Y_DOWN_BIT:
|
|
Report->dwY = EXTENTS_Y_MIN;
|
|
break;
|
|
|
|
case GAME_Y_UP_BIT:
|
|
Report->dwY = EXTENTS_Y_MAX;
|
|
break;
|
|
|
|
default:
|
|
Report->dwY = EXTENTS_Y_MID;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Process Buttons
|
|
//
|
|
|
|
Report->dwButtons = ~((Data[UnitId] & GAME_BUTTON_BITS) >> 4);
|
|
Report->dwButtons &= ((1L << GAME_PACKET_BUTTONS) - 1);
|
|
|
|
Report->dwButtonNumber = 0;
|
|
for (B1 = 1; B1 <= GAME_PACKET_BUTTONS; B1++)
|
|
if (Report->dwButtons & (1L << (B1-1)))
|
|
{
|
|
Report->dwButtonNumber = B1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Driver entry point for device
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWGAMPAD_DriverEntry (VOID)
|
|
{
|
|
MsGamePrint((DBG_INFORM,"SWGAMPAD: SWGAMPAD_DriverEntry Enter\n"));
|
|
|
|
//
|
|
// Read timing values from registry
|
|
//
|
|
|
|
MSGAME_ReadRegistry (DEVICENAME, &Delays);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Establishes connection to device by detection
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT Status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWGAMPAD_ConnectDevice (PGAMEPORT PortInfo)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
ULONG i = MAX_CONNECT_ATTEMPTS;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_ConnectDevice enter\n"));
|
|
|
|
DataInfo.PortInfo = IdInfo.PortInfo = *PortInfo;
|
|
|
|
//
|
|
// Convert registry timing values
|
|
//
|
|
|
|
SWGAMPAD_Calibrate (PortInfo);
|
|
|
|
//
|
|
// Reset to "known" state
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "SWGAMPAD_ConnectDevice - resetting device\n"));
|
|
if (!SWGAMPAD_GoAnalog (&DataInfo, &IdInfo))
|
|
MsGamePrint ((DBG_CONTROL, "SWGAMPAD_ConnectDevice - unable to go Analog\n"));
|
|
else do
|
|
{
|
|
//
|
|
// SWGAMPAD Connection method (try these steps twice)
|
|
//
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
//
|
|
// Get the ID string.
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "SWGAMPAD_ConnectDevice - getting ID string\n"));
|
|
if (!SWGAMPAD_GetId (&IdInfo))
|
|
continue;
|
|
|
|
//
|
|
// Mark device found and return
|
|
//
|
|
|
|
if (!GamePadInfo.NumDevices)
|
|
GamePadInfo.NumDevices = 1;
|
|
return (STATUS_SUCCESS);
|
|
|
|
} while (--i);
|
|
|
|
//
|
|
// Return error
|
|
//
|
|
|
|
GamePadInfo.NumDevices = 0;
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads and converts HID packet for this device
|
|
// @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 Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWGAMPAD_ReadReport (PGAMEPORT PortInfo, PDEVICE_PACKET Report)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SWGAMPAD_ReadReport enter\n"));
|
|
|
|
//
|
|
// Log number of attempts
|
|
//
|
|
|
|
DataInfo.Attempts++;
|
|
|
|
//
|
|
// Set up default data to process
|
|
//
|
|
|
|
memcpy (DataInfo.Data, ValidData, sizeof (ValidData));
|
|
|
|
//
|
|
// Check for collision
|
|
//
|
|
|
|
if (DEVICE_IsCollision (&DataInfo))
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_ReadReport - port collision\n"));
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
goto ReadReportExit;
|
|
}
|
|
|
|
//
|
|
// Get a packet and check for errors
|
|
//
|
|
|
|
ntStatus = SWGAMPAD_ReadData (&DataInfo);
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
if (ntStatus != STATUS_DEVICE_BUSY)
|
|
{
|
|
DataInfo.Failures++;
|
|
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_ReadReport - invalid packet\n"));
|
|
}
|
|
else
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "SWGAMPAD_ReadReport - Port busy or in use\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DataInfo.Mode == IMODE_DIGITAL_ENH)
|
|
DataInfo.NumPackets = DataInfo.ClocksSampled / 5;
|
|
else DataInfo.NumPackets = DataInfo.ClocksSampled / 15;
|
|
if (DataInfo.NumPackets == 0)
|
|
DataInfo.NumPackets = 1;
|
|
else if (DataInfo.NumPackets > 4)
|
|
DataInfo.NumPackets = 4;
|
|
if (!SWGAMPAD_ValidateData (&DataInfo))
|
|
{
|
|
DataInfo.Failures++;
|
|
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
MsGamePrint ((DBG_SEVERE, "SWGAMPAD_ReadReport - invalid packet\n"));
|
|
}
|
|
else memcpy (ValidData, DataInfo.Data, sizeof (ValidData));
|
|
}
|
|
|
|
// ---------------
|
|
ReadReportExit:
|
|
// ---------------
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
GamePadInfo.NumDevices = DataInfo.NumPackets;
|
|
|
|
if (GET_DEVICE_UNIT (PortInfo) < GamePadInfo.NumDevices)
|
|
SWGAMPAD_ProcessData (GET_DEVICE_UNIT (PortInfo), ValidData, Report);
|
|
else ntStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
|
|
return (ntStatus);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for Pnp Start Device
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWGAMPAD_StartDevice (PGAMEPORT PortInfo)
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_StartDevice enter\n"));
|
|
|
|
UNREFERENCED_PARAMETER (PortInfo);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for Pnp Stop Device
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWGAMPAD_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware)
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "SWGAMPAD_StopDevice enter\n"));
|
|
|
|
UNREFERENCED_PARAMETER (PortInfo);
|
|
UNREFERENCED_PARAMETER (TouchHardware);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//**************************************************************************
|
|
#endif // SAITEK
|
|
//**************************************************************************
|