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.
 
 
 
 
 
 

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
//**************************************************************************