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.
1679 lines
47 KiB
1679 lines
47 KiB
//**************************************************************************
|
|
//
|
|
// SW3DPRO.C -- Xena Gaming Project
|
|
//
|
|
// Version 3.XX
|
|
//
|
|
// Copyright (c) 1997 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// @doc
|
|
// @module SW3DPRO.C | Gameport mini-driver for Sidewinder Pro
|
|
//**************************************************************************
|
|
|
|
#ifndef SAITEK
|
|
#include "msgame.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Definitions
|
|
//---------------------------------------------------------------------------
|
|
|
|
#define DEVICENAME "SW3DPRO"
|
|
#define DEVICE_PID 0x0f1f
|
|
#define HARDWARE_ID L"Gameport\\SideWinder3DPro\0\0"
|
|
|
|
//
|
|
// Packet Constants
|
|
//
|
|
|
|
#define GAME_PACKET_SIZE 8
|
|
#define GAME_PACKET_BUTTONS 8
|
|
#define GAME_PACKET_AXIS 4
|
|
|
|
#define GAME_PHASE_BYTE 0 // Packet[0] bits
|
|
#define GAME_PHASE_BIT 0x80
|
|
#define GAME_H3_BYTE 0
|
|
#define GAME_H3_BITS 0x40
|
|
#define GAME_X7_X9_BYTE 0
|
|
#define GAME_X7_X9_BITS 0x38
|
|
#define GAME_Y7_Y9_BYTE 0
|
|
#define GAME_Y7_Y9_BITS 0x07
|
|
|
|
#define GAME_B0_B6_BYTE 1 // Packet[1] bits
|
|
#define GAME_B0_B6_BITS 0x7f
|
|
|
|
#define GAME_X0_X6_BYTE 2 // Packet[2] bits
|
|
#define GAME_X0_X6_BITS 0x7f
|
|
|
|
#define GAME_Y0_Y6_BYTE 3 // Packet[3] bits
|
|
#define GAME_Y0_Y6_BITS 0x7f
|
|
|
|
#define GAME_B7_BYTE 4 // Packet[4] bits
|
|
#define GAME_B7_BITS 0x40
|
|
#define TM_MODE_BYTE 4
|
|
#define TM_MODE_BITS 0x20
|
|
#define GAME_R7_R8_BYTE 4
|
|
#define GAME_R7_R8_BITS 0x18
|
|
#define GAME_T7_T9_BYTE 4
|
|
#define GAME_T7_T9_BITS 0x07
|
|
|
|
#define GAME_R0_R6_BYTE 5 // Packet[5] bits
|
|
#define GAME_R0_R6_BITS 0x7f
|
|
|
|
#define GAME_T0_T6_BYTE 6 // Packet[6] bits
|
|
#define GAME_T0_T6_BITS 0x7f
|
|
|
|
#define GAME_H0_H2_BYTE 7 // Packet[7] bits
|
|
#define GAME_H0_H2_BITS 0x70
|
|
#define GAME_C0_C3_BYTE 7
|
|
#define GAME_C0_C3_BITS 0x0f
|
|
|
|
#define ENH_CLOCK_MIDPACKET 0x00000400
|
|
#define ENH_CLOCK_COMPLETE 0x00200000
|
|
|
|
//
|
|
// Id Definitions
|
|
//
|
|
|
|
#define GAME_ID_CLOCKS 32
|
|
#define GAME_ID_STRING "(\1\x24P"
|
|
|
|
//
|
|
// Timing Constants
|
|
//
|
|
|
|
#define PACKET_START_TIMEOUT 500
|
|
#define PACKET_LOWHIGH_TIMEOUT 75
|
|
#define PACKET_HIGHLOW_TIMEOUT 150
|
|
#define PACKET_INTERRUPT_DELAY 30
|
|
#define ID_START_TIMEOUT 500
|
|
#define ID_LOWHIGH_TIMEOUT 75
|
|
#define ID_HIGHLOW_TIMEOUT 150
|
|
#define MAX_CLOCK_DUTY_CYCLE 50
|
|
|
|
#define GODIGITAL_ATTEMPTS 10
|
|
#define RECALIBRATE_FAIL_COUNT (GODIGITAL_ATTEMPTS/2)
|
|
|
|
//
|
|
// Joystick Extents
|
|
//
|
|
|
|
#define EXTENTS_X_MIN 0
|
|
#define EXTENTS_X_MAX 0xff
|
|
#define EXTENTS_Y_MIN 0
|
|
#define EXTENTS_Y_MAX 0xff
|
|
#define EXTENTS_T_MIN 0
|
|
#define EXTENTS_T_MAX 0xff
|
|
#define EXTENTS_R_MIN 0
|
|
#define EXTENTS_R_MAX 0x3f
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Types
|
|
//---------------------------------------------------------------------------
|
|
|
|
typedef struct
|
|
{ // @struct SW3DPRO_ID | Sidwinder Pro Id String
|
|
#pragma pack(1)
|
|
UCHAR EnumId[3]; // @field Enumerated Id
|
|
UCHAR EisaId[8]; // @field Eisa bus Id
|
|
UCHAR Devices; // @field Number of devices
|
|
USHORT Version[7]; // @field Firmware version
|
|
UCHAR EndOfStr; // @field ID string terminator
|
|
#pragma pack()
|
|
} SW3DPRO_ID, *PSW3DPRO_ID;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Macros
|
|
//---------------------------------------------------------------------------
|
|
|
|
#define LOW_NIBBLE(c) ((c)&GAME_C0_C3_BITS)
|
|
#define HIGH_NIBBLE(c) (((c)&(GAME_C0_C3_BITS<<4))>>4)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Procedures
|
|
//---------------------------------------------------------------------------
|
|
|
|
static VOID SW3DPRO_Calibrate (PGAMEPORT PortInfo);
|
|
static BOOLEAN SW3DPRO_ResetDevice (PGAMEPORT PortInfo);
|
|
|
|
static BOOLEAN SW3DPRO_GoDigital (PPACKETINFO IdPacket, ULONG Sequence[]);
|
|
static LONG SW3DPRO_InterruptPacket (PGAMEPORT PortInfo);
|
|
|
|
static BOOLEAN SW3DPRO_ReadId (PPACKETINFO IdPacket);
|
|
static BOOLEAN SW3DPRO_GetId (PPACKETINFO IdPacket);
|
|
|
|
static NTSTATUS SW3DPRO_ReadData (PPACKETINFO DataPacket);
|
|
static BOOLEAN SW3DPRO_Read1Wide (PPACKETINFO DataPacket);
|
|
static BOOLEAN SW3DPRO_Read3Wide (PPACKETINFO DataPacket);
|
|
static BOOLEAN SW3DPRO_ValidateData (PUCHAR Packet);
|
|
static VOID SW3DPRO_ProcessData (UCHAR Data[], PDEVICE_PACKET Report);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Services
|
|
//---------------------------------------------------------------------------
|
|
|
|
static NTSTATUS SW3DPRO_DriverEntry (VOID);
|
|
static NTSTATUS SW3DPRO_ConnectDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS SW3DPRO_StartDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS SW3DPRO_ReadReport (PGAMEPORT PortInfo, PDEVICE_PACKET Report);
|
|
static NTSTATUS SW3DPRO_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Alloc_text pragma to specify routines that can be paged out.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, SW3DPRO_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, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0xFF, 0x03, 0x00, 0x00, // LOGICAL_MAXIMUM (1023)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0xFF, 0x03, 0x00, 0x00, // PHYSICAL_MAXIMUM (1023)
|
|
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 (X)
|
|
HIDP_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,Abs)
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//dwZ
|
|
HIDP_GLOBAL_USAGE_PAGE_1, HID_USAGE_PAGE_SIMULATION, // USAGE_PAGE (Simulation Controls)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_SIMULATION_THROTTLE,// USAGE (Throttle)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0xFF, 0x03, 0x00, 0x00, // LOGICAL_MAXIMUM (1023)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0xFF, 0x03, 0x00, 0x00, // PHYSICAL_MAXIMUM (1023)
|
|
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_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,Abs)
|
|
|
|
//dwR
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_SIMULATION_RUDDER, // USAGE (Rudder)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0xFF, 0x01, 0x00, 0x00, // LOGICAL_MAXIMUM (511)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0xFF, 0x01, 0x00, 0x00, // PHYSICAL_MAXIMUM (511)
|
|
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_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,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_USAGE_PAGE_1, HID_USAGE_PAGE_GENERIC, // USAGE_PAGE (Generic Desktop)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_HATSWITCH, // USAGE (Hat switch)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0x9F, 0x8C, 0x00, 0x00, // LOGICAL_MAXIMUM (35999)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0x9f, 0x8C, 0x00, 0x00, // PHYSICAL_MAXIMUM (35999)
|
|
HIDP_GLOBAL_UNIT_2, 0x14, 0x00, // Unit (English Rot:Angular Pos)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (32)
|
|
HIDP_MAIN_INPUT_1, 0x42, // Input (Data,Var,Abs,Null)
|
|
|
|
//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, 0x08, // USAGE_MAXIMUM (Button 8)
|
|
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 UCHAR RawData[GAME_PACKET_SIZE] =
|
|
{ // no buttons; x, y, t and r centered
|
|
GAME_PHASE_BIT|((GAME_X7_X9_BITS>>1)&GAME_X7_X9_BITS)|((GAME_Y7_Y9_BITS>>1)&GAME_Y7_Y9_BITS),
|
|
GAME_B0_B6_BITS,
|
|
GAME_X0_X6_BITS,
|
|
GAME_Y0_Y6_BITS,
|
|
((GAME_R7_R8_BITS>>1)&GAME_R7_R8_BITS)|((GAME_T7_T9_BITS>>1)&GAME_T7_T9_BITS)|GAME_B7_BITS,
|
|
GAME_R0_R6_BITS,
|
|
GAME_T0_T6_BITS,
|
|
0
|
|
};
|
|
//
|
|
// Raw Id Buffer
|
|
//
|
|
|
|
static SW3DPRO_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
|
|
0, // 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
|
|
0, // 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 =
|
|
{
|
|
SW3DPRO_DriverEntry, // DriverEntry
|
|
SW3DPRO_ConnectDevice, // ConnectDevice
|
|
SW3DPRO_StartDevice, // StartDevice
|
|
SW3DPRO_ReadReport, // ReadReport
|
|
SW3DPRO_StopDevice, // StopDevice
|
|
NULL // GetFeature
|
|
};
|
|
|
|
//
|
|
// Last Valid Data
|
|
//
|
|
static UCHAR ValidData[GAME_PACKET_SIZE] =
|
|
{ // no buttons; x, y, t and r centered
|
|
GAME_PHASE_BIT|((GAME_X7_X9_BITS>>1)&GAME_X7_X9_BITS)|((GAME_Y7_Y9_BITS>>1)&GAME_Y7_Y9_BITS),
|
|
GAME_B0_B6_BITS,
|
|
GAME_X0_X6_BITS,
|
|
GAME_Y0_Y6_BITS,
|
|
((GAME_R7_R8_BITS>>1)&GAME_R7_R8_BITS)|((GAME_T7_T9_BITS>>1)&GAME_T7_T9_BITS)|GAME_B7_BITS,
|
|
GAME_R0_R6_BITS,
|
|
GAME_T0_T6_BITS,
|
|
0
|
|
};
|
|
|
|
//
|
|
// Go Digital Timing Data
|
|
//
|
|
|
|
static ULONG GoDigitalFastTiming[] =
|
|
{
|
|
T1+60,
|
|
T2+60,
|
|
T3+60
|
|
};
|
|
|
|
static ULONG GoDigitalMediumTiming[] =
|
|
{
|
|
T1,
|
|
T2,
|
|
T3
|
|
};
|
|
|
|
static ULONG GoDigitalSlowTiming[] =
|
|
{
|
|
T1-20,
|
|
T2-20,
|
|
T3-20
|
|
};
|
|
|
|
static ULONG GoDigitalFails = 0;
|
|
|
|
//
|
|
// Hardware ID String
|
|
//
|
|
|
|
static WCHAR HardwareId[] = HARDWARE_ID;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Public Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
public DEVICEINFO MidasInfo =
|
|
{
|
|
&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
|
|
TRUE, // 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 SW3DPRO_Calibrate (PGAMEPORT PortInfo)
|
|
{
|
|
MsGamePrint((DBG_INFORM,"SW3DPRO: SW3DPRO_Calibrate Enter\n"));
|
|
|
|
//
|
|
// Convert timing values to counts
|
|
//
|
|
|
|
DataInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: DataInfo.StartTimeout = %ld\n", DataInfo.StartTimeout));
|
|
DataInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: DataInfo.LowHighTimeout = %ld\n", DataInfo.LowHighTimeout));
|
|
DataInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: DataInfo.HighLowTimeout = %ld\n", DataInfo.HighLowTimeout));
|
|
IdInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.IdStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: IdInfo.StartTimeout = %ld\n", IdInfo.StartTimeout));
|
|
IdInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.IdLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: IdInfo.LowHighTimeout=%ld\n", IdInfo.LowHighTimeout));
|
|
IdInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.IdHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: IdInfo.HighLowTimeout=%ld\n", IdInfo.HighLowTimeout));
|
|
DataInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: DataInfo.ClockDutyCycle = %ld\n", DataInfo.ClockDutyCycle));
|
|
IdInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: IdInfo.ClockDutyCycle = %ld\n", IdInfo.ClockDutyCycle));
|
|
DataInfo.InterruptDelay = Delays.InterruptDelay;
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: DataInfo.InterruptDelay = %ld\n", DataInfo.InterruptDelay));
|
|
IdInfo.InterruptDelay = Delays.InterruptDelay;
|
|
MsGamePrint((DBG_VERBOSE, "SW3DPRO: IdInfo.InterruptDelay = %ld\n", IdInfo.InterruptDelay));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Resets device to known state
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SW3DPRO_ResetDevice (PGAMEPORT PortInfo)
|
|
{
|
|
LONG Result;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_ResetDevice enter\n"));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
DataInfo.Mode = IdInfo.Mode = IMODE_ANALOG;
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
Result = SW3DPRO_InterruptPacket (PortInfo);
|
|
if (Result == ERROR_SUCCESS)
|
|
Result = SW3DPRO_InterruptPacket (PortInfo);
|
|
|
|
if (Result != ERROR_SUCCESS)
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_ResetDevice - InterruptPacket failed\n"));
|
|
|
|
DataInfo.LastError = Result;
|
|
DataInfo.Transaction = MSGAME_TRANSACT_RESET;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
MSGAME_PostTransaction (&DataInfo);
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Interrupts device during packet transfer
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Internal result
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
LONG SW3DPRO_InterruptPacket (PGAMEPORT PortInfo)
|
|
{
|
|
LONG Clks;
|
|
LONG Result;
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
lea edx, IdInfo.PortInfo ; load gameport adddress
|
|
|
|
mov ebx, 64 ; Standard Mode = 64 clks.
|
|
|
|
Int_CheckState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 0 ?
|
|
jz Int_ClockRise ; Y: jump
|
|
|
|
;Int_ClockFall:
|
|
|
|
mov ecx, TIMEOUT
|
|
|
|
Int_ClockFall_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock = 0
|
|
jz Int_ClockRise ; Y: jump - look for rising edge
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz Int_ClockFall_1 ; else see if we timed out
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp IntPacketComplete ; Time out error.
|
|
|
|
Int_ClockRise:
|
|
|
|
mov ecx, TIMEOUT
|
|
|
|
Int_ClockRise_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock high ?
|
|
jnz Int_Transition ; Y: jump. (transition)
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz Int_ClockRise_1 ; else see if we timed out
|
|
mov eax, ERROR_CLOCKRISING
|
|
jmp IntPacketComplete ; Time out error.
|
|
|
|
Int_Transition:
|
|
|
|
cmp bl, 14 ; Q: 14 clocks left ?
|
|
jne Int_Transition_1 ; N: jump.
|
|
|
|
push 0 ; write byte to gameport
|
|
push edx
|
|
call PORTIO_Write
|
|
|
|
Int_Transition_1:
|
|
|
|
dec bl
|
|
jnz Int_CheckState
|
|
|
|
mov eax, ERROR_SUCCESS
|
|
|
|
IntPacketComplete:
|
|
|
|
mov Result, eax
|
|
mov Clks, ebx
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_InterruptPacket - TimeOut@ClockFalling, Clk=%ld\n", Clks));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_InterruptPacket - TimeOut@ClockRising, Clk=%ld\n", Clks));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Puts device into digital mode
|
|
// @parm PPACKETINFO | IdPacket | ID Packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SW3DPRO_GoDigital (PPACKETINFO IdPacket, ULONG Sequence[])
|
|
{
|
|
LONG i;
|
|
ULONG ThreeMilliSecs;
|
|
ULONG GoDigitalTiming[3];
|
|
LONG Result = ERROR_SUCCESS;
|
|
ULONG Attempts = MAX_CONNECT_ATTEMPTS;
|
|
PGAMEPORT PortInfo = &IdPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_GoDigital enter\n"));
|
|
|
|
do
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
GoDigitalTiming[i] = TIMER_GetDelay (Sequence[i]);
|
|
ThreeMilliSecs = TIMER_GetDelay (THREE_MILLI_SECS);
|
|
|
|
if (GoDigitalFails > RECALIBRATE_FAIL_COUNT)
|
|
TIMER_Calibrate ();
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
continue;
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov edx, PortInfo ; load gameport adddress
|
|
|
|
mov ecx, MAX_XA_TIMEOUT
|
|
xor ebx, ebx
|
|
|
|
WaitXA:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, INTXA_BIT_MASK ; Q: X axis low ?
|
|
jz XA_Done ; Y: jump. (complete)
|
|
xor bh, al ; Q: Clock transition ?
|
|
test bh, CLOCK_BIT_MASK
|
|
jz WaitXA_1 ; N: jump.
|
|
inc bl ; inc. clock transition count.
|
|
|
|
WaitXA_1:
|
|
|
|
mov bh, al
|
|
dec ecx
|
|
jnz WaitXA
|
|
|
|
jmp DigitalSwitchFail
|
|
|
|
XA_Done:
|
|
|
|
cmp bl, 3 ; Q: Already in Digital mode ?
|
|
ja DigitalSwitchDone ; Y: jump.
|
|
|
|
xor edi, edi
|
|
mov esi, edx
|
|
|
|
DigitalSwitchLoop:
|
|
|
|
push GoDigitalTiming[edi]
|
|
add edi, 4 ; inc loop counter & index
|
|
call TIMER_DelayMicroSecs
|
|
mov edx, esi
|
|
|
|
push 0 ; write byte to gameport
|
|
push edx
|
|
call PORTIO_Write
|
|
|
|
push edx
|
|
call PORTIO_WaitXA_HighLow ; Wait for XA to transition
|
|
|
|
or al, al
|
|
je DigitalSwitchFail ; if Timeout then fail.
|
|
cmp edi, 12
|
|
jl DigitalSwitchLoop
|
|
push ThreeMilliSecs
|
|
call TIMER_DelayMicroSecs ; Delay so Firmware can update
|
|
jmp DigitalSwitchDone
|
|
|
|
DigitalSwitchFail:
|
|
|
|
mov Result, ERROR_XA_TIMEOUT
|
|
|
|
DigitalSwitchDone:
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
IdPacket->TimeStamp = TIMER_GetTickCount ();
|
|
IdPacket->LastError = Result;
|
|
IdPacket->Transaction = MSGAME_TRANSACT_GODIGITAL;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
if (Result == ERROR_SUCCESS)
|
|
{
|
|
GoDigitalFails = 0;
|
|
break;
|
|
}
|
|
else GoDigitalFails++;
|
|
|
|
if (Result == ERROR_XA_TIMEOUT)
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_GoDigital - TimeOut@XAHighLow\n"));
|
|
|
|
} while(--Attempts);
|
|
|
|
MSGAME_PostTransaction (IdPacket);
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads device id string from port
|
|
// @parm PPACKETINFO | IdPacket | ID Packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SW3DPRO_ReadId (PPACKETINFO IdPacket)
|
|
{
|
|
ULONG B4 = 0L;
|
|
ULONG Data = 0L;
|
|
ULONG Clks = GAME_ID_CLOCKS;
|
|
LONG Result = ERROR_SUCCESS;
|
|
PGAMEPORT PortInfo = &IdPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_ReadId enter\n"));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
Result = SW3DPRO_InterruptPacket (PortInfo);
|
|
if (Result != ERROR_SUCCESS)
|
|
goto ReadIdExit;
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov edx, PortInfo ; load gameport adddress
|
|
|
|
xor eax, eax ; edx = port address
|
|
mov ebx, GAME_ID_CLOCKS ; BL = no of clocks to receive.
|
|
xor edi, edi ; clear B4 transition counter
|
|
xor esi, esi ; clear data accumulator
|
|
|
|
ID_ClockCheck:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jz ID_ClockRise ; N: jump
|
|
|
|
; ID_ClockFall:
|
|
|
|
mov ecx, IdInfo.HighLowTimeout
|
|
|
|
ID_ClockFall_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock = 0
|
|
jz ID_ClockRise ; Y: jump - look for rising edge
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz ID_ClockFall_1 ; else see if we timed out
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp ID_Error ; Time out error.
|
|
|
|
ID_ClockRise:
|
|
|
|
mov ecx, IdInfo.LowHighTimeout
|
|
|
|
ID_ClockRise_1:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock high ?
|
|
jnz ID_Data ; Y: jump. (get data)
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx
|
|
jnz ID_ClockRise_1 ; else see if we timed out
|
|
mov eax, ERROR_CLOCKRISING
|
|
jmp ID_Error ; Time out error.
|
|
|
|
ID_Data:
|
|
|
|
xor ah, al
|
|
test ah, DATA2_BIT_MASK
|
|
jz ID_Data_1
|
|
inc edi ; increment Data 1 counter
|
|
|
|
ID_Data_1:
|
|
|
|
mov ah, al
|
|
shr al, 6 ; put data into carry
|
|
rcr esi, 1 ; and then in data counter
|
|
dec bl ; decrement clk counter.
|
|
jnz ID_ClockCheck ; if != 0 then loop
|
|
|
|
; ID_Success:
|
|
|
|
mov eax, ERROR_SUCCESS
|
|
mov IdInfo.Mode, IMODE_DIGITAL_STD
|
|
cmp edi, 4
|
|
jl ID_Success_1
|
|
mov IdInfo.Mode, IMODE_DIGITAL_ENH
|
|
|
|
ID_Success_1:
|
|
|
|
mov IdInfo.B4Transitions, edi
|
|
mov edx, IdInfo.Data
|
|
mov [edx], esi
|
|
jmp ID_Complete
|
|
|
|
ID_Error:
|
|
|
|
mov IdInfo.B4Transitions, edi
|
|
mov edx, IdInfo.Data
|
|
mov [edx], dword ptr 0
|
|
|
|
ID_Complete:
|
|
|
|
mov Result, eax
|
|
mov Data, esi
|
|
mov B4, edi
|
|
mov Clks, ebx
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
// ----------------
|
|
ReadIdExit:
|
|
// ----------------
|
|
|
|
IdPacket->TimeStamp = TIMER_GetTickCount ();
|
|
IdPacket->ClocksSampled = GAME_ID_CLOCKS - Clks;
|
|
IdPacket->LastError = Result;
|
|
IdPacket->Transaction = MSGAME_TRANSACT_ID;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_ReadId - SUCCEEDED, Data=%ld,B4=%ld,Clk=%ld\n", Data,B4,IdPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_ReadId - TimeOut@ClockFalling, Data=%ld,B4=%ld,Clk=%ld\n", Data,B4,IdPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_ReadId - TimeOut@ClockRising, Data=%ld,B4=%ld,Clk=%ld\n", Data,B4,IdPacket->ClocksSampled));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
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 SW3DPRO_GetId (PPACKETINFO IdPacket)
|
|
{
|
|
BOOLEAN Result;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_GetId enter\n"));
|
|
|
|
if(DataInfo.B4Transitions > 4)
|
|
DataInfo.Mode = IMODE_DIGITAL_ENH;
|
|
else DataInfo.Mode = IMODE_DIGITAL_STD;
|
|
|
|
IdPacket->Attempts++;
|
|
|
|
Result = SW3DPRO_ReadId (IdPacket);
|
|
|
|
if (Result)
|
|
{
|
|
if (!strncmp (IdPacket->Data, GAME_ID_STRING, strlen(GAME_ID_STRING)))
|
|
{
|
|
//
|
|
// Do AZTECH test here
|
|
//
|
|
if (IdPacket->B4Transitions > 3)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "SW3DPRO_GetId - had B4 transitions\n"));
|
|
IdPacket->Mode = DataInfo.Mode = IMODE_DIGITAL_ENH;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// An AZTECH card was detected OR this card didn't seem to support
|
|
// Enhanced mode correctly. Lets reset by going Analog then going
|
|
// digital this should place the device into Standard mode.
|
|
//
|
|
MsGamePrint ((DBG_CONTROL, "SW3DPRO_GetId - 1 Wide Only type card detected\n"));
|
|
SW3DPRO_ResetDevice (&IdPacket->PortInfo);
|
|
SW3DPRO_GoDigital (IdPacket, GoDigitalMediumTiming);
|
|
IdPacket->Mode = DataInfo.Mode = IMODE_DIGITAL_STD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_GetId - Id string did not match = 0x%X\n", (ULONG)(*(PULONG)IdPacket->Data)));
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!Result)
|
|
IdPacket->Failures++;
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay (ONE_MILLI_SEC+200));
|
|
|
|
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
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SW3DPRO_Read1Wide (PPACKETINFO DataPacket)
|
|
{
|
|
ULONG Clks = 0x2002;
|
|
LONG Result;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SW3DPRO_Read1Wide enter\n"));
|
|
|
|
PORTIO_Write (&DataInfo.PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov esi, DataInfo.Data
|
|
|
|
lea edx, DataInfo.PortInfo ; load gameport adddress
|
|
|
|
xor edi, edi
|
|
mov ebx, 2002h
|
|
xor eax, eax
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, DataInfo.StartTimeout
|
|
|
|
Std_ClockStartState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jnz Std_ClockStartFall
|
|
loop Std_ClockStartState ; else keep looping
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp PacketComplete ; Time out error.
|
|
|
|
Std_CheckClkState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK
|
|
jz Std_ClockStartRise
|
|
|
|
Std_ClockStartFall:
|
|
|
|
mov ecx, DataInfo.HighLowTimeout
|
|
|
|
Std_ClockFalling:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock = 0
|
|
jz Std_ClockStartRise ; Y: jump - look for rising edge
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
loop Std_ClockFalling ; else see if we timed out
|
|
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp PacketComplete ; Time out error.
|
|
|
|
Std_ClockStartRise:
|
|
|
|
mov ecx, DataInfo.LowHighTimeout
|
|
|
|
Std_ClockRising:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: clock high ?
|
|
jnz CollectData ; Y: jump. (get data)
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
loop Std_ClockRising ; else see if we timed out
|
|
|
|
mov eax, ERROR_CLOCKRISING
|
|
jmp PacketComplete ; Time out error.
|
|
|
|
CollectData:
|
|
|
|
xor ah, al
|
|
test ah, DATA1_BIT_MASK ; Q: Data 1 is toggled ?
|
|
jnz CollectData_1 ; N: jump.
|
|
inc edi ; Y: increment Data 1 count.
|
|
|
|
CollectData_1:
|
|
|
|
mov ah, al
|
|
shr al, 6 ; put data into carry
|
|
rcr DWORD PTR [esi], 1 ; and then in data counter
|
|
dec bh ; Q: 32 bits received ?
|
|
jnz Std_CheckClkState ; N: continue.
|
|
dec bl ; dec dword count.
|
|
jz PacketSuccess ; if dword count = 0 then exit.
|
|
add esi, 4 ; else advance packet pointer
|
|
mov bh, 32 ; set bit counter = 32+32=64.
|
|
jmp Std_CheckClkState ; stay in receive loop.
|
|
|
|
PacketSuccess:
|
|
|
|
mov eax, ERROR_SUCCESS
|
|
|
|
PacketComplete:
|
|
|
|
mov Result, eax
|
|
mov Clks, ebx
|
|
mov DataInfo.B4Transitions, edi
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
switch (Clks & 0xFF)
|
|
{
|
|
case 0:
|
|
DataPacket->ClocksSampled = 64;
|
|
break;
|
|
|
|
case 1:
|
|
DataPacket->ClocksSampled = 32 + (32-(Clks>>8));
|
|
break;
|
|
|
|
case 2:
|
|
DataPacket->ClocksSampled = 32 - (Clks>>8);
|
|
break;
|
|
}
|
|
|
|
DataPacket->LastError = Result;
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_Read1Wide - TimeOut@LowClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_Read1Wide - TimeOut@HighClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_Read1Wide - TimeOut@ClockFalling, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_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 SW3DPRO_Read3Wide (PPACKETINFO DataPacket)
|
|
{
|
|
LONG Clks = 1L;
|
|
LONG Result;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SW3DPRO_Read3Wide enter\n"));
|
|
|
|
PORTIO_Write (&DataInfo.PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov esi, DataInfo.Data
|
|
|
|
lea edx, DataInfo.PortInfo ; load gameport adddress
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, DataInfo.StartTimeout
|
|
mov ebx, 1
|
|
|
|
Enh_ClockStartState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jnz Enh_ClockStartFall ; Y: jump
|
|
loop Enh_ClockStartState ; else keep looping
|
|
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp Enh_Complete ; Time out error.
|
|
|
|
Enh_CheckClkState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK
|
|
jz Enh_ClockStartRise
|
|
|
|
Enh_ClockStartFall:
|
|
|
|
mov ecx, DataInfo.HighLowTimeout
|
|
|
|
Enh_ClockFalling:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock Low ?
|
|
jz Enh_ClockStartRise ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx ; Q: Timeout ?
|
|
jnz Enh_ClockFalling ; N: continue looping.
|
|
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp Enh_Complete ; Time out error.
|
|
|
|
Enh_ClockStartRise:
|
|
|
|
mov ecx, DataInfo.LowHighTimeout
|
|
|
|
Enh_ClockRising:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1 ?
|
|
jnz Enh_CollectData ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx ; Q: Timeout ?
|
|
jnz Enh_ClockRising ; N: continue looping.
|
|
|
|
mov eax, ERROR_CLOCKRISING
|
|
jmp Enh_Complete ; Time out error.
|
|
|
|
Enh_CollectData:
|
|
|
|
shr al, 5 ; move data to lower 3 bits
|
|
test ebx, ENH_CLOCK_MIDPACKET ; Q: in mid-packet ?
|
|
jnz Enh_MidPacket ; Y: jump.
|
|
test ebx, ENH_CLOCK_COMPLETE ; Q: is packet complete ?
|
|
jnz Enh_Success ; Y: jump.
|
|
|
|
shrd edi, eax, 3 ; shift data into edi.
|
|
shl ebx, 1 ; advance clock counter.
|
|
jmp Enh_CheckClkState
|
|
|
|
;---------------------------------------------------------------------;
|
|
; This section of code compensates for when the clock cycle count is ;
|
|
; on a ULONG boundary. This happens on the 11th clock cycle. Two bits ;
|
|
; of data belong in the 1st ULONG and one bit belong in the 2nd ULONG ;
|
|
;---------------------------------------------------------------------;
|
|
|
|
Enh_MidPacket:
|
|
|
|
shrd edi, eax, 2 ; put 2 bits in 1st ULONG.
|
|
mov [esi], edi ; Save 1st ULONG in packet ptr.
|
|
xor edi, edi ; zero out edi.
|
|
shr al, 2 ; move 3rd bit over.
|
|
shrd edi, eax, 1 ; put 3rd bit in 2nd ULONG.
|
|
shl ebx, 1 ; advance clock counter.
|
|
jmp Enh_CheckClkState
|
|
|
|
Enh_Success:
|
|
|
|
shrd edi, eax, 1 ; shift data into edi.
|
|
mov [esi+4], edi
|
|
mov eax, ERROR_SUCCESS
|
|
|
|
Enh_Complete:
|
|
|
|
mov Result, eax
|
|
mov Clks, ebx
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
for (DataPacket->ClocksSampled = 0; Clks >> (DataPacket->ClocksSampled+1); DataPacket->ClocksSampled++)
|
|
;
|
|
DataPacket->LastError = Result;
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_Read3Wide - TimeOut@LowClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_Read3Wide - TimeOut@HighClockStart, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_Read3Wide - TimeOut@ClockFalling, Clk=%ld\n", DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_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 SW3DPRO_ReadData (PPACKETINFO DataPacket)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
PGAMEPORT PortInfo = &DataPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SW3DPRO_ReadData enter\n"));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (STATUS_DEVICE_BUSY);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
DataPacket->ClocksSampled = 0;
|
|
DataPacket->B4Transitions = 0;
|
|
|
|
switch (DataPacket->Mode)
|
|
{
|
|
case IMODE_DIGITAL_STD:
|
|
Result = SW3DPRO_Read1Wide (DataPacket);
|
|
break;
|
|
|
|
case IMODE_DIGITAL_ENH:
|
|
Result = SW3DPRO_Read3Wide (DataPacket);
|
|
break;
|
|
|
|
default:
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_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 PUCHAR | RawData | Pointer to raw packet data
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SW3DPRO_ValidateData (PUCHAR RawData)
|
|
{
|
|
LONG i = 0;
|
|
LONG Check = 0;
|
|
PULONG Phase = (PULONG)RawData;
|
|
LONG Result = ERROR_SUCCESS;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SW3DPRO_ValidateData enter\n"));
|
|
|
|
if (((*Phase & 0x80808080L) != 0x80L) || (*(Phase+1) & 0x80808080L))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_ValidateData - Phase bits non-zero: %X:%X\n", *(Phase+1), *Phase));
|
|
Result = ERROR_PHASEBITS;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < GAME_PACKET_SIZE; i++)
|
|
{
|
|
Check += LOW_NIBBLE(RawData[i]);
|
|
Check += HIGH_NIBBLE(RawData[i]);
|
|
}
|
|
if (LOW_NIBBLE(Check))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_ValidateData - Checksum failed: %X:%X\n", *(Phase+1), *Phase));
|
|
Result = ERROR_CHECKSUM;
|
|
}
|
|
}
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Converts raw packet information to HID report
|
|
// @parm UCHAR[] | Data | Pointer to raw data buffer
|
|
// @parm PDEVICE_PACKET | Report | Pointer to device packet
|
|
// @rdesc Returns nothing
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID SW3DPRO_ProcessData (UCHAR Data[], PDEVICE_PACKET Report)
|
|
{
|
|
ULONG B1, B2;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SW3DPRO_ProcessData enter\n"));
|
|
|
|
//
|
|
// Process X Axis
|
|
//
|
|
|
|
Report->dwX = Data[GAME_X7_X9_BYTE] & GAME_X7_X9_BITS;
|
|
Report->dwX <<= 4;
|
|
Report->dwX |= Data[GAME_X0_X6_BYTE] & GAME_X0_X6_BITS;
|
|
|
|
//
|
|
// Process Y Axis
|
|
//
|
|
|
|
Report->dwY = Data[GAME_Y7_Y9_BYTE] & GAME_Y7_Y9_BITS;
|
|
Report->dwY <<= 7;
|
|
Report->dwY |= Data[GAME_Y0_Y6_BYTE] & GAME_Y0_Y6_BITS;
|
|
|
|
//
|
|
// Process R Axis
|
|
//
|
|
|
|
Report->dwR = Data[GAME_R7_R8_BYTE] & GAME_R7_R8_BITS;
|
|
Report->dwR <<= 4;
|
|
Report->dwR |= Data[GAME_R0_R6_BYTE] & GAME_R0_R6_BITS;
|
|
|
|
//
|
|
// Process Z Axis
|
|
//
|
|
|
|
Report->dwZ = Data[GAME_T7_T9_BYTE] & GAME_T7_T9_BITS;
|
|
Report->dwZ <<= 7;
|
|
Report->dwZ |= Data[GAME_T0_T6_BYTE] & GAME_T0_T6_BITS;
|
|
|
|
//
|
|
// Process Buttons
|
|
//
|
|
|
|
B1 = (~Data[GAME_B7_BYTE] & GAME_B7_BITS) << 1;
|
|
B2 = ~Data[GAME_B0_B6_BYTE] & GAME_B0_B6_BITS;
|
|
Report->dwButtons = (B2 | B1) & ((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;
|
|
}
|
|
|
|
//
|
|
// Process Hatswitch
|
|
//
|
|
|
|
Report->dwPOV = (Data[GAME_H0_H2_BYTE] & GAME_H0_H2_BITS)>>4;
|
|
Report->dwPOV |= (Data[GAME_H3_BYTE] & GAME_H3_BITS)>>3;
|
|
Report->dwPOV = POV_Values[Report->dwPOV];
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Driver entry point for device
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SW3DPRO_DriverEntry (VOID)
|
|
{
|
|
MsGamePrint((DBG_INFORM,"SW3DPRO: SW3DPRO_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 SW3DPRO_ConnectDevice (PGAMEPORT PortInfo)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
ULONG i = MAX_CONNECT_ATTEMPTS;
|
|
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_ConnectDevice enter\n"));
|
|
|
|
DataInfo.PortInfo = IdInfo.PortInfo = *PortInfo;
|
|
|
|
//
|
|
// Convert registry timing values
|
|
//
|
|
|
|
SW3DPRO_Calibrate (PortInfo);
|
|
|
|
//
|
|
// SW3DPRO Connection method (try these steps twice)
|
|
//
|
|
|
|
do
|
|
{
|
|
//
|
|
// 1. Reset to "known" state
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "SW3DPRO_ConnectDevice - resetting device\n"));
|
|
SW3DPRO_ResetDevice (&DataInfo.PortInfo);
|
|
DataInfo.Mode = IdInfo.Mode = IMODE_DIGITAL_STD;
|
|
|
|
//
|
|
// 2. Delay 1 millisecond.
|
|
//
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
//
|
|
// 3. Put into digital mode
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "SW3DPRO_ConnectDevice - going digital\n"));
|
|
SW3DPRO_GoDigital (&DataInfo, GoDigitalMediumTiming);
|
|
|
|
//
|
|
// 4. Delay 1 millisecond.
|
|
//
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC+500));
|
|
|
|
//
|
|
// 5. Get the ID string.
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "SW3DPRO_ConnectDevice - getting ID string\n"));
|
|
|
|
if (!SW3DPRO_GetId (&IdInfo))
|
|
{
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 6. Mark device found and return
|
|
//
|
|
|
|
MidasInfo.NumDevices = 1;
|
|
return (STATUS_SUCCESS);
|
|
|
|
} while (--i);
|
|
|
|
//
|
|
// Return error
|
|
//
|
|
|
|
MidasInfo.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 SW3DPRO_ReadReport (PGAMEPORT PortInfo, PDEVICE_PACKET Report)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "SW3DPRO_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, "SW3DPRO_ReadReport - port collision\n"));
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
goto ReadReportExit;
|
|
}
|
|
|
|
//
|
|
// Get a packet and check for errors
|
|
//
|
|
|
|
ntStatus = SW3DPRO_ReadData (&DataInfo);
|
|
if (NT_SUCCESS(ntStatus) && SW3DPRO_ValidateData (DataInfo.Data))
|
|
{
|
|
memcpy (ValidData, DataInfo.Data, sizeof (ValidData));
|
|
}
|
|
else if (ntStatus != STATUS_DEVICE_BUSY)
|
|
{
|
|
DataInfo.Failures++;
|
|
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
MsGamePrint ((DBG_SEVERE, "SW3DPRO_ReadReport - Invalid packet\n"));
|
|
}
|
|
else
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "SW3DPRO_ReadReport - Port busy or in use\n"));
|
|
}
|
|
|
|
// ---------------
|
|
ReadReportExit:
|
|
// ---------------
|
|
|
|
SW3DPRO_ProcessData (ValidData, Report);
|
|
|
|
return (ntStatus);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for Pnp Start Device
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SW3DPRO_StartDevice (PGAMEPORT PortInfo)
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_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 SW3DPRO_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware)
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "SW3DPRO_StopDevice enter\n"));
|
|
|
|
if (TouchHardware)
|
|
SW3DPRO_ResetDevice (PortInfo);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//**************************************************************************
|
|
#endif // SAITEK
|
|
//**************************************************************************
|