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.
2182 lines
66 KiB
2182 lines
66 KiB
//**************************************************************************
|
|
//
|
|
// SWLEDZEP.C -- Xena Gaming Project
|
|
//
|
|
// Version 3.XX
|
|
//
|
|
// Copyright (c) 1997 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// @doc
|
|
// @module SWLEDZEP.C | Gameport mini-driver for Sidewinder LedZep Force Feedback
|
|
//**************************************************************************
|
|
|
|
#include "msgame.h"
|
|
#include "swforce.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Definitions
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef SAITEK
|
|
#define DEVICENAME "SAIWHEEL"
|
|
#define DEVICE_PID 0x0016
|
|
#define HARDWARE_ID L"Gameport\\SaiTekForceFeedbackWheel\0\0"
|
|
#else
|
|
#define DEVICENAME "SWLEDZEP"
|
|
#define DEVICE_PID 0x0015
|
|
#define HARDWARE_ID L"Gameport\\SideWinderForceFeedbackWheel\0\0"
|
|
#endif
|
|
|
|
//
|
|
// Packet Constants
|
|
//
|
|
|
|
#define GAME_PACKET_SIZE 5
|
|
#define GAME_PACKET_BUTTONS 8
|
|
#define GAME_PACKET_AXIS 4
|
|
|
|
#define GAME_X0_X7_BYTE 0 // Packet[0] bits
|
|
#define GAME_X0_X7_BITS 0xff
|
|
|
|
#define GAME_X8_X9_BYTE 1 // Packet[1] bits
|
|
#define GAME_X8_X9_BITS 0x03
|
|
#define GAME_YA0_YA5_BYTE 1
|
|
#define GAME_YA0_YA5_BITS 0xfc
|
|
|
|
#define GAME_YB0_YB5_BYTE 2 // Packet[2] bits
|
|
#define GAME_YB0_YB5_BITS 0x3f
|
|
#define GAME_B0_B1_BYTE 2
|
|
#define GAME_B0_B1_BITS 0xc0
|
|
|
|
#define GAME_B2_B8_BYTE 3 // Packet[3] bits
|
|
#define GAME_B2_B8_BITS 0x7f
|
|
#define GAME_ERR_BYTE 3
|
|
#define GAME_ERR_BITS 0x80
|
|
|
|
#define GAME_PPO_BYTE 4 // Packet[4] bits
|
|
#define GAME_PPO_BITS 0x01
|
|
|
|
#define ENH_CLOCK_COMPLETE 0x0400
|
|
|
|
//
|
|
// ID Constants
|
|
//
|
|
|
|
#define GAME_ID_CLOCKS 8
|
|
|
|
//
|
|
// Status Constants
|
|
//
|
|
|
|
#define STATUS_CLOCK_COMPLETE 0x0040
|
|
|
|
//
|
|
// Timing Constants
|
|
//
|
|
|
|
#define PACKET_START_TIMEOUT 500
|
|
#define PACKET_LOWHIGH_TIMEOUT 75
|
|
#define PACKET_HIGHLOW_TIMEOUT 150
|
|
#define ID_START_TIMEOUT 500
|
|
#define ID_LOWHIGH_TIMEOUT 75
|
|
#define ID_HIGHLOW_TIMEOUT 150
|
|
#define MAX_CLOCK_DUTY_CYCLE 50
|
|
#define STATUS_START_TIMEOUT 500
|
|
#define STATUS_LOWHIGH_TIMEOUT 75
|
|
#define STATUS_HIGHLOW_TIMEOUT 150
|
|
#define STATUS_GATE_TIMEOUT 3000
|
|
|
|
//
|
|
// Joystick Extents
|
|
//
|
|
|
|
#define EXTENTS_X_MIN 0
|
|
#define EXTENTS_X_MAX 0x3ff
|
|
#define EXTENTS_YA_MIN 0
|
|
#define EXTENTS_YA_MAX 0x3f
|
|
#define EXTENTS_YB_MIN 0
|
|
#define EXTENTS_YB_MAX 0x3f
|
|
|
|
//
|
|
// Speed Data
|
|
//
|
|
|
|
#define NUM_ERROR_SAMPLES 100
|
|
#define MIN_ERROR_RATE 5
|
|
#define MAX_ERROR_RATE 15
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Types
|
|
//---------------------------------------------------------------------------
|
|
|
|
typedef struct
|
|
{
|
|
#pragma pack(1)
|
|
ULONG ProductId:12; // @field Device identifier
|
|
ULONG Version:11; // @field Firmware version
|
|
ULONG OddParityBit:1; // @field Parity bit (odd)
|
|
ULONG Unused:8; // @field unused
|
|
#pragma pack()
|
|
} SWLEDZEP_ID, *PSWLEDZEP_ID;
|
|
|
|
typedef struct
|
|
{ // @struct SWLEDZEP_STATUS | Sidwinder Wheel Status
|
|
#pragma pack(1)
|
|
ULONG Effect:7; // @field Last effect
|
|
ULONG Status:13; // @field Status flags
|
|
ULONG Parity:1; // @field Parity bit (odd)
|
|
ULONG Unused:8; // @field unused
|
|
#pragma pack()
|
|
} SWLEDZEP_STATUS, *PSWLEDZEP_STATUS;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Procedures
|
|
//---------------------------------------------------------------------------
|
|
|
|
static VOID SWLEDZEP_Calibrate (PGAMEPORT PortInfo);
|
|
static BOOLEAN SWLEDZEP_ResetDevice (PGAMEPORT PortInfo);
|
|
|
|
static BOOLEAN SWLEDZEP_ReadId (PPACKETINFO IdPacket);
|
|
static BOOLEAN SWLEDZEP_GetId (PPACKETINFO IdPacket);
|
|
|
|
static BOOLEAN SWLEDZEP_ReadStatus (PPACKETINFO StatusPacket);
|
|
static BOOLEAN SWLEDZEP_GetStatus (PPACKETINFO StatusPacket);
|
|
|
|
static NTSTATUS SWLEDZEP_ReadData (PPACKETINFO DataPacket);
|
|
static VOID SWLEDZEP_ProcessData (UCHAR Data[], PDEVICE_PACKET Report);
|
|
static VOID SWLEDZEP_ProcessDataError (PGAMEPORT PortInfo, ULONG Error);
|
|
|
|
static LONG SWLEDZEP_DecrementDevice (PGAMEPORT PortInfo);
|
|
static BOOLEAN SWLEDZEP_SetDeviceSpeed (PGAMEPORT PortInfo, LONG Speed);
|
|
|
|
static NTSTATUS SWLEDZEP_ForceReset (PGAMEPORT PortInfo);
|
|
static NTSTATUS SWLEDZEP_ForceId (PGAMEPORT PortInfo, PVOID IdString);
|
|
static NTSTATUS SWLEDZEP_ForceStatus (PGAMEPORT PortInfo, PVOID Status);
|
|
static NTSTATUS SWLEDZEP_ForceAckNak (PGAMEPORT PortInfo, PULONG AckNak);
|
|
static NTSTATUS SWLEDZEP_ForceNakAck (PGAMEPORT PortInfo, PULONG NakAck);
|
|
static NTSTATUS SWLEDZEP_ForceSync (PGAMEPORT PortInfo, PULONG Sync);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Services
|
|
//---------------------------------------------------------------------------
|
|
|
|
static NTSTATUS SWLEDZEP_DriverEntry (VOID);
|
|
static NTSTATUS SWLEDZEP_ConnectDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS SWLEDZEP_StartDevice (PGAMEPORT PortInfo);
|
|
static NTSTATUS SWLEDZEP_ReadReport (PGAMEPORT PortInfo, PDEVICE_PACKET Report);
|
|
static NTSTATUS SWLEDZEP_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware);
|
|
static NTSTATUS SWLEDZEP_GetFeature (PGAMEPORT PortInfo, HID_REPORT_ID ReportId, PVOID ReportBuffer, ULONG ReportSize, PULONG Returned);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Alloc_text pragma to specify routines that can be paged out.
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, SWLEDZEP_DriverEntry)
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Private Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
// HID Descriptors
|
|
//
|
|
|
|
#define HID_USAGE_VEHICLE_STEERING ((USAGE) 0xC8)
|
|
#define HID_USAGE_VEHICLE_ACCELERATOR ((USAGE) 0xC4)
|
|
#define HID_USAGE_VEHICLE_BRAKE ((USAGE) 0xC5)
|
|
|
|
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)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// JOYINFOEX
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_APP, // COLLECTION (Application)
|
|
HIDP_REPORT_ID_1, MSGAME_INPUT_JOYINFOEX,
|
|
|
|
// 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
|
|
HIDP_GLOBAL_USAGE_PAGE_1, HID_USAGE_PAGE_SIMULATION, // USAGE_PAGE (Simulation Controls)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_VEHICLE_STEERING, // USAGE (Steering)
|
|
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)
|
|
|
|
// dwY
|
|
HIDP_GLOBAL_USAGE_PAGE_1, HID_USAGE_PAGE_SIMULATION, // USAGE_PAGE (Simulation Controls)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_VEHICLE_ACCELERATOR,// USAGE (Accelerator)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0x3F, 0x00, 0x00, 0x00, // LOGICAL_MAXIMUM (63)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0x3F, 0x00, 0x00, 0x00, // PHYSICAL_MAXIMUM (63)
|
|
HIDP_GLOBAL_UNIT_2, 0x00, 0x00, // UNIT (None)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (16)
|
|
HIDP_MAIN_INPUT_1, 0x02, // INPUT (Data,Var,Abs)
|
|
|
|
// dwZ
|
|
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)
|
|
|
|
// dwR
|
|
HIDP_GLOBAL_USAGE_PAGE_1, HID_USAGE_PAGE_SIMULATION, // USAGE_PAGE (Simulation Controls)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_VEHICLE_BRAKE, // USAGE (Brake)
|
|
HIDP_GLOBAL_LOG_MIN_1, 0x00, // LOGICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_LOG_MAX_4, 0x3F, 0x00, 0x00, 0x00, // LOGICAL_MAXIMUM (63)
|
|
HIDP_GLOBAL_PHY_MIN_1, 0x00, // PHYSICAL_MINIMUM (0)
|
|
HIDP_GLOBAL_PHY_MAX_4, 0x3F, 0x00, 0x00, 0x00, // PHYSICAL_MAXIMUM (63)
|
|
HIDP_GLOBAL_UNIT_2, 0x00, 0x00, // UNIT (None)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (16)
|
|
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_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
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, 0x09, // USAGE_MAXIMUM (Button 9)
|
|
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)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetID
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_GETID,
|
|
|
|
// cBytes
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x00, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwProductID
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x01, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwFWVersion
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x02, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetStatus
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_GETSTATUS,
|
|
|
|
// cBytes
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x03, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwXVel
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x04, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwYVel
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x05, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwXAccel
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x06, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwYAccel
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x07, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwEffect
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x08, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
// dwDeviceStatus
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x09, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetAckNak
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_GETACKNAK,
|
|
|
|
// ULONG
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x0A, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetNakAck
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_GETNAKACK,
|
|
|
|
// ULONG
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x0B, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetSync
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_GETSYNC,
|
|
|
|
// ULONG
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x0C, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//---------------------------------------------------------------------------
|
|
// DoReset
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_RESET,
|
|
|
|
// ULONG
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x0D, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x03, // FEATURE (Cnst,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_COLLECTION
|
|
|
|
//---------------------------------------------------------------------------
|
|
// GetVersion
|
|
//---------------------------------------------------------------------------
|
|
|
|
HIDP_GLOBAL_USAGE_PAGE_2, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
|
HIDP_LOCAL_USAGE_1, HID_USAGE_GENERIC_JOYSTICK, // USAGE (Joystick)
|
|
|
|
HIDP_MAIN_COLLECTION, HIDP_MAIN_COLLECTION_LINK, // COLLECTION (Link)
|
|
HIDP_REPORT_ID_1, MSGAME_FEATURE_GETVERSION,
|
|
|
|
// ULONG
|
|
HIDP_GLOBAL_REPORT_SIZE, 0x20, // REPORT_SIZE (20)
|
|
HIDP_GLOBAL_REPORT_COUNT_1,0x01, // REPORT_COUNT (1)
|
|
HIDP_LOCAL_USAGE_1, 0x0E, // USAGE (Vendor Defined1)
|
|
HIDP_MAIN_FEATURE_1, 0x02, // FEATURE (Data,Var,Abs)
|
|
|
|
HIDP_MAIN_ENDCOLLECTION, // END_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, ya, yb centered
|
|
GAME_X0_X7_BITS,
|
|
((GAME_X8_X9_BITS>>1)&GAME_X8_X9_BITS)|((GAME_YA0_YA5_BITS>>1)&GAME_YA0_YA5_BITS),
|
|
((GAME_YB0_YB5_BITS>>1)&GAME_YB0_YB5_BITS),
|
|
0,
|
|
GAME_PPO_BITS
|
|
};
|
|
//
|
|
// Raw Id Buffer
|
|
//
|
|
|
|
static SWLEDZEP_ID RawId =
|
|
{
|
|
0
|
|
};
|
|
|
|
//
|
|
// Raw Status Buffer
|
|
//
|
|
|
|
static SWLEDZEP_STATUS RawStatus =
|
|
{
|
|
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,
|
|
0, // No interrupt delay used
|
|
MAX_CLOCK_DUTY_CYCLE,
|
|
STATUS_START_TIMEOUT,
|
|
STATUS_HIGHLOW_TIMEOUT,
|
|
STATUS_LOWHIGH_TIMEOUT,
|
|
STATUS_GATE_TIMEOUT
|
|
};
|
|
|
|
static ULONG StatusGateTimeout;
|
|
|
|
//
|
|
// Data Packet Info
|
|
//
|
|
|
|
static PACKETINFO DataInfo =
|
|
{
|
|
sizeof (PACKETINFO), // Size of structure
|
|
DEVICENAME, // Name of device
|
|
MSGAME_TRANSACT_NONE, // Transaction type
|
|
IMODE_DIGITAL_ENH, // Interface mode
|
|
WHEEL_SPEED_48K, // 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_ENH, // Interface mode
|
|
WHEEL_SPEED_48K, // 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
|
|
};
|
|
|
|
//
|
|
// Status Packet Info
|
|
//
|
|
|
|
static PACKETINFO StatusInfo =
|
|
{
|
|
sizeof (PACKETINFO), // Size of structure
|
|
DEVICENAME, // Name of device
|
|
MSGAME_TRANSACT_NONE, // Transaction type
|
|
IMODE_DIGITAL_ENH, // Interface mode
|
|
WHEEL_SPEED_48K, // 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 (RawStatus), // Size of raw status buffer
|
|
&RawStatus // Pointer to Raw data
|
|
};
|
|
|
|
//
|
|
// Services Table
|
|
//
|
|
|
|
static DRIVERSERVICES Services =
|
|
{
|
|
SWLEDZEP_DriverEntry, // DriverEntry
|
|
SWLEDZEP_ConnectDevice, // ConnectDevice
|
|
SWLEDZEP_StartDevice, // StartDevice
|
|
SWLEDZEP_ReadReport, // ReadReport
|
|
SWLEDZEP_StopDevice, // StopDevice
|
|
SWLEDZEP_GetFeature // GetFeature
|
|
};
|
|
|
|
//
|
|
// Last Valid Data
|
|
//
|
|
|
|
static UCHAR ValidData[GAME_PACKET_SIZE] =
|
|
{ // no buttons; x, ya, yb centered
|
|
GAME_X0_X7_BITS,
|
|
((GAME_X8_X9_BITS>>1)&GAME_X8_X9_BITS)|((GAME_YA0_YA5_BITS>>1)&GAME_YA0_YA5_BITS),
|
|
((GAME_YB0_YB5_BITS>>1)&GAME_YB0_YB5_BITS) | GAME_B0_B1_BITS,
|
|
GAME_B2_B8_BITS,
|
|
GAME_PPO_BITS
|
|
};
|
|
|
|
//
|
|
// Speed Variables
|
|
//
|
|
|
|
static ULONG NextSample = 0;
|
|
static ULONG NumberSamples = 0;
|
|
static ULONG SampleAccumulator = 0;
|
|
static ULONG SampleBuffer[NUM_ERROR_SAMPLES] = {0};
|
|
|
|
//
|
|
// Reset Flag
|
|
//
|
|
|
|
static BOOLEAN ResetComplete = FALSE;
|
|
|
|
//
|
|
// Hardware ID String
|
|
//
|
|
|
|
static WCHAR HardwareId[] = HARDWARE_ID;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Public Data
|
|
//---------------------------------------------------------------------------
|
|
|
|
public DEVICEINFO LedZepInfo =
|
|
{
|
|
&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_FIRST, // 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 SWLEDZEP_Calibrate (PGAMEPORT PortInfo)
|
|
{
|
|
MsGamePrint((DBG_INFORM,"%s: %s_Calibrate Enter\n", DEVICENAME, DEVICENAME));
|
|
|
|
//
|
|
// Convert timing values to counts
|
|
//
|
|
|
|
DataInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: DataInfo.StartTimeout = %ld\n", DEVICENAME, DataInfo.StartTimeout));
|
|
DataInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: DataInfo.LowHighTimeout = %ld\n", DEVICENAME, DataInfo.LowHighTimeout));
|
|
DataInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.PacketHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: DataInfo.HighLowTimeout = %ld\n", DEVICENAME, DataInfo.HighLowTimeout));
|
|
IdInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.IdStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: IdInfo.StartTimeout = %ld\n", DEVICENAME, IdInfo.StartTimeout));
|
|
IdInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.IdLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: IdInfo.LowHighTimeout=%ld\n", DEVICENAME, IdInfo.LowHighTimeout));
|
|
IdInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.IdHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: IdInfo.HighLowTimeout=%ld\n", DEVICENAME, IdInfo.HighLowTimeout));
|
|
DataInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "%s: DataInfo.ClockDutyCycle = %ld\n", DEVICENAME, DataInfo.ClockDutyCycle));
|
|
IdInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "%s: IdInfo.ClockDutyCycle = %ld\n", DEVICENAME, IdInfo.ClockDutyCycle));
|
|
StatusInfo.ClockDutyCycle = TIMER_CalibratePort (PortInfo, Delays.MaxClockDutyCycle);
|
|
MsGamePrint((DBG_VERBOSE, "%s: StatusInfo.ClockDutyCycle = %ld\n", DEVICENAME, StatusInfo.ClockDutyCycle));
|
|
StatusInfo.StartTimeout = TIMER_CalibratePort (PortInfo, Delays.StatusStartTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: StatusInfo.StartTimeout = %ld\n", DEVICENAME, StatusInfo.StartTimeout));
|
|
StatusInfo.LowHighTimeout = TIMER_CalibratePort (PortInfo, Delays.StatusLowHighTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: StatusInfo.LowHighTimeout=%ld\n", DEVICENAME, StatusInfo.LowHighTimeout));
|
|
StatusInfo.HighLowTimeout = TIMER_CalibratePort (PortInfo, Delays.StatusHighLowTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: StatusInfo.HighLowTimeout=%ld\n", DEVICENAME, StatusInfo.HighLowTimeout));
|
|
StatusGateTimeout = TIMER_CalibratePort (PortInfo, Delays.StatusGateTimeout);
|
|
MsGamePrint((DBG_VERBOSE, "%s: StatusGateTimeout=%ld\n", DEVICENAME, StatusGateTimeout));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Resets device to known state
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWLEDZEP_ResetDevice (PGAMEPORT PortInfo)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_ResetDevice enter\n", DEVICENAME));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
DataInfo.Speed = IdInfo.Speed = StatusInfo.Speed = WHEEL_SPEED_48K;
|
|
|
|
if (PORTIO_PulseAndWaitForIdleHandshake (PortInfo, DataInfo.ClockDutyCycle, 4))
|
|
{
|
|
PORTIO_Write (PortInfo, 0);
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
DataInfo.LastError = ERROR_SUCCESS;
|
|
Result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DataInfo.LastError = ERROR_HANDSHAKING;
|
|
MsGamePrint ((DBG_SEVERE, "%s_ResetDevice - PulseAndWaitForHandshake failed\n", DEVICENAME));
|
|
}
|
|
|
|
DataInfo.Transaction = MSGAME_TRANSACT_RESET;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
if (!Result)
|
|
MsGamePrint ((DBG_SEVERE, "%s_ResetDevice - PulseAndWaitForIdleHandshake failed\n", DEVICENAME));
|
|
|
|
MSGAME_PostTransaction (&DataInfo);
|
|
|
|
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 SWLEDZEP_ReadId (PPACKETINFO IdPacket)
|
|
{
|
|
ULONG Data = 0L;
|
|
ULONG Clks = GAME_ID_CLOCKS;
|
|
LONG Result = ERROR_HANDSHAKING;
|
|
PGAMEPORT PortInfo = &IdPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_ReadId enter\n", DEVICENAME));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
IdPacket->B4Transitions = 0;
|
|
|
|
if (!PORTIO_PulseAndWaitForIdleHandshake (PortInfo, IdInfo.ClockDutyCycle, 2))
|
|
goto ReadIdExit;
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov edx, PortInfo ; load gameport adddress
|
|
|
|
xor eax, eax ; edx = port address
|
|
mov ebx, GAME_ID_CLOCKS ; ebx = no of clocks to receive.
|
|
xor edi, edi ; clear B4 transition counter
|
|
xor esi, esi ; clear data accumulator
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, IdInfo.StartTimeout
|
|
|
|
ID_ClockStart:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jz ID_ClockStart_1 ; N: jump
|
|
loop ID_ClockStart ; else keep looping
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp ID_Error ; Time out error.
|
|
|
|
ID_ClockStart_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jnz ID_Data ; Y: jump
|
|
loop ID_ClockStart_1 ; else keep looping
|
|
mov eax, ERROR_HIGHCLOCKSTART
|
|
jmp ID_Error ; Time out error.
|
|
|
|
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, 5
|
|
shrd esi, eax,3
|
|
dec ebx
|
|
jne ID_ClockCheck
|
|
shr esi, 8 ; only 24 bits
|
|
|
|
; ID_Success:
|
|
|
|
mov IdInfo.B4Transitions, edi
|
|
mov eax, ERROR_SUCCESS
|
|
mov edx, IdInfo.Data
|
|
mov [edx], esi
|
|
jmp ID_Complete
|
|
|
|
ID_Error:
|
|
|
|
mov edx, IdInfo.Data
|
|
mov [edx], dword ptr 0
|
|
|
|
ID_Complete:
|
|
|
|
mov Result, eax
|
|
mov Data, esi
|
|
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, "%s_ReadId - SUCCEEDED, Data=%ld", DEVICENAME, Data));
|
|
break;
|
|
|
|
case ERROR_HANDSHAKING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadId - TimeOut@Handshaking\n", DEVICENAME));
|
|
break;
|
|
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadId - TimeOut@LowClockStart, Data=%ld,Clk=%ld\n", DEVICENAME, Data,IdPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadId - TimeOut@HighClockStart, Data=%ld,Clk=%ld\n", DEVICENAME, Data,IdPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadId - TimeOut@ClockFalling, Data=%ld,Clk=%ld\n", DEVICENAME, Data,IdPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadId - TimeOut@ClockRising, Data=%ld,Clk=%ld\n", DEVICENAME, Data,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 SWLEDZEP_GetId (PPACKETINFO IdPacket)
|
|
{
|
|
BOOLEAN Result;
|
|
PSWLEDZEP_ID Pnp;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_GetId enter\n", DEVICENAME));
|
|
|
|
IdPacket->Attempts++;
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
Result = SWLEDZEP_ReadId (IdPacket);
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
if (Result)
|
|
{
|
|
Pnp = (PSWLEDZEP_ID)IdPacket->Data;
|
|
if ((Pnp->ProductId != DEVICE_PID) || !DEVICE_IsOddParity (Pnp, sizeof(SWLEDZEP_ID)))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetId - Id did not match or parity error\n", DEVICENAME));
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!Result)
|
|
IdPacket->Failures++;
|
|
|
|
if (PORTIO_IsClockActive (&IdPacket->PortInfo, IdInfo.ClockDutyCycle))
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay (ONE_MILLI_SEC));
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads data packet from gameport
|
|
// @parm PPACKETINFO | DataPacket| Data packet parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ReadData (PPACKETINFO DataPacket)
|
|
{
|
|
LONG Result;
|
|
LONG Clks = 1L;
|
|
PGAMEPORT PortInfo = &DataPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s_ReadData enter\n", DEVICENAME));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (STATUS_DEVICE_BUSY);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov edx, PortInfo ; load gameport adddress
|
|
|
|
mov esi, DataInfo.Data
|
|
mov ebx, 1
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, DataInfo.StartTimeout
|
|
|
|
Enh_ClockStartState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jz Enh_ClockStartState_1 ; N: jump
|
|
loop Enh_ClockStartState ; else keep looping
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp Enh_Complete ; Time out error.
|
|
|
|
Enh_ClockStartState_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jnz Enh_CollectData ; Y: jump
|
|
loop Enh_ClockStartState_1 ; else keep looping
|
|
mov eax, ERROR_HIGHCLOCKSTART
|
|
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_COMPLETE ; Q: in end-packet ?
|
|
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_Success:
|
|
|
|
shrd edi, eax, 2 ; put 2 bits in 1st ULONG.
|
|
mov [esi], edi ; Save 1st ULONG in packet ptr.
|
|
shr al, 2 ; move 3rd bit over.
|
|
mov byte ptr [esi+4], al
|
|
|
|
mov eax, PortInfo ; wait for clock to settle
|
|
push eax
|
|
call PORTIO_WaitClockLow
|
|
|
|
push DataInfo.ClockDutyCycle
|
|
mov eax, PortInfo ; wait for clock to settle
|
|
push eax
|
|
call PORTIO_IsClockActive
|
|
|
|
or al, al
|
|
mov eax, ERROR_EXTRACLOCKS ; probably gamepads
|
|
jne Enh_Complete
|
|
|
|
mov eax, PortInfo ; wait for clock to settle
|
|
push eax
|
|
call PORTIO_WaitClockHigh
|
|
|
|
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->TimeStamp = TIMER_GetTickCount ();
|
|
DataPacket->LastError = Result;
|
|
DataPacket->Transaction = MSGAME_TRANSACT_DATA;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadData - TimeOut@LowClockStart, Clk=%ld\n", DEVICENAME, DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadData - TimeOut@HighClockStart, Clk=%ld\n", DEVICENAME, DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadData - TimeOut@ClockFalling, Clk=%ld\n", DEVICENAME, DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadData - TimeOut@ClockRising, Clk=%ld\n", DEVICENAME, DataPacket->ClocksSampled));
|
|
break;
|
|
|
|
case ERROR_EXTRACLOCKS:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadData - Extra Clocks, Clk=%ld\n", DEVICENAME, DataPacket->ClocksSampled));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
MSGAME_PostTransaction (DataPacket);
|
|
|
|
if (Result)
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @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 SWLEDZEP_ProcessData (UCHAR Data[], PDEVICE_PACKET Report)
|
|
{
|
|
ULONG B1, B2;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s_ProcessData enter\n", DEVICENAME));
|
|
|
|
//
|
|
// Process X Axis
|
|
//
|
|
|
|
Report->dwX = Data[GAME_X8_X9_BYTE] & GAME_X8_X9_BITS;
|
|
Report->dwX <<= 8;
|
|
Report->dwX |= Data[GAME_X0_X7_BYTE] & GAME_X0_X7_BITS;
|
|
|
|
//
|
|
// Process Y Axis
|
|
//
|
|
|
|
Report->dwY = (Data[GAME_YA0_YA5_BYTE] & GAME_YA0_YA5_BITS)>>2;
|
|
|
|
//
|
|
// Process R Axis
|
|
//
|
|
|
|
Report->dwR = Data[GAME_YB0_YB5_BYTE] & GAME_YB0_YB5_BITS;
|
|
|
|
//
|
|
// Process Buttons
|
|
//
|
|
|
|
B1 = (~Data[GAME_B0_B1_BYTE] & GAME_B0_B1_BITS)>>6;
|
|
B2 = (~Data[GAME_B2_B8_BYTE] & GAME_B2_B8_BITS)<<2;
|
|
Report->dwButtons = (B2 | B1);
|
|
// R,L,C,B,A,Z,Y,X Order
|
|
Report->dwButtons = (Report->dwButtons & 0x7) | ((Report->dwButtons & 0xf0)>>1) | ((Report->dwButtons & 0x8)<<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 Decrements device speed at port
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns new device speed
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
LONG SWLEDZEP_DecrementDevice (PGAMEPORT PortInfo)
|
|
{
|
|
LONG Clks = 0;
|
|
LONG Result = ERROR_HANDSHAKING;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_DecrementDevice enter\n", DEVICENAME));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
if (!PORTIO_PulseAndWaitForHandshake (PortInfo, DataInfo.ClockDutyCycle, 3))
|
|
goto DecrementDeviceExit;
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov edx, PortInfo ; load gameport adddress
|
|
|
|
xor eax, eax ; data accumulator
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, DataInfo.StartTimeout
|
|
|
|
DD_ClockStart:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jz DD_ClockStart_1 ; N: jump
|
|
loop DD_ClockStart ; else keep looping
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp DD_Complete ; Time out error.
|
|
|
|
DD_ClockStart_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jnz DD_Success ; Y: jump
|
|
loop DD_ClockStart_1 ; else keep looping
|
|
mov eax, ERROR_HIGHCLOCKSTART
|
|
jmp DD_Complete ; Time out error.
|
|
|
|
DD_Success:
|
|
|
|
shr al, 5
|
|
dec al
|
|
and eax, WHEEL_SPEED_BITS
|
|
cmp eax, WHEEL_SPEED_RANGE
|
|
jb DD_Complete
|
|
dec al
|
|
and eax, WHEEL_SPEED_BITS
|
|
|
|
DD_Complete:
|
|
|
|
mov Result, eax
|
|
mov Clks, ebx
|
|
|
|
pop edi
|
|
pop esi
|
|
}
|
|
|
|
// --------------------
|
|
DecrementDeviceExit:
|
|
// --------------------
|
|
|
|
DataInfo.LastError = Result;
|
|
DataInfo.Transaction = MSGAME_TRANSACT_SPEED;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_HANDSHAKING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_DecrementDevice - TimeOut@Handshaking\n", DEVICENAME));
|
|
break;
|
|
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_DecrementDevice - TimeOut@LowClockStart, Clk=%ld\n", DEVICENAME, Clks));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_DecrementDevice - TimeOut@HighClockStart, Clk=%ld\n", DEVICENAME, Clks));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_DecrementDevice - TimeOut@ClockFalling, Clk=%ld\n", DEVICENAME, Clks));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_DecrementDevice - TimeOut@ClockRising, Clk=%ld\n", DEVICENAME, Clks));
|
|
break;
|
|
|
|
default:
|
|
MsGamePrint ((DBG_CONTROL, "%s_DecrementDevice - SUCCEEDED, Speed=%ld\n", DEVICENAME, Result));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
MSGAME_PostTransaction (&DataInfo);
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Sets new device speed
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm ULONG | Speed | Desired device speed
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWLEDZEP_SetDeviceSpeed (PGAMEPORT PortInfo, LONG Speed)
|
|
{
|
|
LONG Result;
|
|
ULONG Tries;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_SetDeviceSpeed enter\n", DEVICENAME));
|
|
|
|
//
|
|
// Zero error processing counters
|
|
//
|
|
|
|
NextSample = 0;
|
|
NumberSamples = 0;
|
|
SampleAccumulator = 0;
|
|
for (Tries = 0; Tries < NUM_ERROR_SAMPLES; Tries++)
|
|
SampleBuffer[Tries] = 0;
|
|
|
|
//
|
|
// Try changing speed only enough times as range
|
|
//
|
|
|
|
for (Tries = 0; Tries < WHEEL_SPEED_RANGE; Tries++)
|
|
{
|
|
if (DataInfo.Speed == Speed)
|
|
return (TRUE);
|
|
|
|
Result = SWLEDZEP_DecrementDevice (PortInfo);
|
|
if (Result < 0)
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s_DecrementDevice failed on %ld attempt\n", DEVICENAME, (ULONG)Tries));
|
|
return (FALSE);
|
|
}
|
|
|
|
DataInfo.Speed = IdInfo.Speed = StatusInfo.Speed = Result;
|
|
}
|
|
|
|
MsGamePrint ((DBG_SEVERE, "%s_SetDeviceSpeed failed after %ld attempts\n", DEVICENAME, (ULONG)Tries));
|
|
return (FALSE);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Processes packet results and changes device speed as neccessary
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm ULONG | Error | Error flag (true is error)
|
|
// @rdesc Returns nothing
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID SWLEDZEP_ProcessDataError (PGAMEPORT PortInfo, ULONG Error)
|
|
{
|
|
ULONG Average;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s_ProcessDataError enter\n", DEVICENAME));
|
|
|
|
//
|
|
// Update running accumulated errors
|
|
//
|
|
|
|
SampleAccumulator -= SampleBuffer[NextSample];
|
|
SampleBuffer[NextSample] = Error;
|
|
SampleAccumulator += Error;
|
|
|
|
//
|
|
// Increment and wrap next error counter
|
|
//
|
|
|
|
if (++NextSample >= NUM_ERROR_SAMPLES)
|
|
NextSample = 0;
|
|
|
|
//
|
|
// Increment number samples and exit if not full
|
|
//
|
|
|
|
if (NumberSamples < NUM_ERROR_SAMPLES)
|
|
{
|
|
NumberSamples++;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Calculate moving average
|
|
//
|
|
|
|
Average = (SampleAccumulator*100)/NumberSamples;
|
|
|
|
//
|
|
// Lower speed if too many errors
|
|
//
|
|
|
|
if ((Average > MAX_ERROR_RATE) && (DataInfo.Speed > WHEEL_SPEED_66K))
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s_ProcessDataError - average error = %ld\n", DEVICENAME, Average));
|
|
SWLEDZEP_SetDeviceSpeed (PortInfo, DataInfo.Speed-1);
|
|
}
|
|
|
|
//
|
|
// Raise speed if too few errors
|
|
//
|
|
|
|
else if ((Average < MIN_ERROR_RATE) && (DataInfo.Speed < WHEEL_SPEED_98K))
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s_ProcessDataError - average error = %ld\n", DEVICENAME, Average));
|
|
SWLEDZEP_SetDeviceSpeed (PortInfo, DataInfo.Speed+1);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads and validates device status
|
|
// @parm PPACKETINFO | StatusPacket | Status Packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWLEDZEP_GetStatus (PPACKETINFO StatusPacket)
|
|
{
|
|
BOOLEAN Result;
|
|
PSWLEDZEP_STATUS Status;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_GetStatus Enter\n", DEVICENAME));
|
|
|
|
StatusPacket->Attempts++;
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
Result = SWLEDZEP_ReadStatus (StatusPacket);
|
|
|
|
if (Result)
|
|
{
|
|
Status = (PSWLEDZEP_STATUS)StatusPacket->Data;
|
|
if (!DEVICE_IsOddParity (Status, sizeof(SWLEDZEP_STATUS)))
|
|
{
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetStatus - Parity error\n", DEVICENAME));
|
|
Result = FALSE;
|
|
}
|
|
else MsGamePrint ((DBG_INFORM, "%s_GetStatus - Status = 0x%X\n", DEVICENAME, (long)Status->Status));
|
|
}
|
|
|
|
if (!Result)
|
|
StatusPacket->Failures++;
|
|
|
|
if (PORTIO_IsClockActive (&StatusPacket->PortInfo, StatusInfo.ClockDutyCycle))
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay (ONE_MILLI_SEC));
|
|
|
|
return (Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Reads status packet from gameport
|
|
// @parm PPACKETINFO | StatusPacket| Status packet parameters
|
|
// @rdesc True if successful, False otherwise
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOLEAN SWLEDZEP_ReadStatus (PPACKETINFO StatusPacket)
|
|
{
|
|
ULONG Status;
|
|
LONG Clks = 1L;
|
|
LONG Result = ERROR_HANDSHAKING;
|
|
PGAMEPORT PortInfo = &StatusPacket->PortInfo;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s_ReadStatus enter\n", DEVICENAME));
|
|
|
|
if (!PORTIO_AcquirePort (PortInfo))
|
|
return (FALSE);
|
|
PORTIO_MaskInterrupts ();
|
|
|
|
StatusPacket->ClocksSampled = 0;
|
|
StatusPacket->B4Transitions = 0;
|
|
|
|
if (!PORTIO_PulseAndWaitForIdleHandshake (PortInfo, StatusInfo.ClockDutyCycle, 1))
|
|
goto ReadStatusExit;
|
|
|
|
PORTIO_Write (PortInfo, 0);
|
|
|
|
__asm
|
|
{
|
|
push edi
|
|
push esi
|
|
|
|
mov edx, PortInfo ; load gameport adddress
|
|
|
|
mov esi, StatusInfo.Data
|
|
mov ebx, 1
|
|
|
|
; make sure clock is "high" before sampling clocks...
|
|
|
|
mov ecx, StatusInfo.StartTimeout
|
|
|
|
Stat_ClockStartState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jz Stat_ClockStartState_1 ; N: jump
|
|
loop Stat_ClockStartState ; else keep looping
|
|
mov eax, ERROR_LOWCLOCKSTART
|
|
jmp Stat_Complete ; Time out error.
|
|
|
|
Stat_ClockStartState_1:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1
|
|
jnz Stat_CollectData ; Y: jump
|
|
loop Stat_ClockStartState_1 ; else keep looping
|
|
mov eax, ERROR_HIGHCLOCKSTART
|
|
jmp Stat_Complete ; Time out error.
|
|
|
|
Stat_CheckClkState:
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
test al, CLOCK_BIT_MASK
|
|
jz Stat_ClockStartRise
|
|
|
|
;Stat_ClockStartFall:
|
|
|
|
mov ecx, StatusInfo.HighLowTimeout
|
|
|
|
Stat_ClockFalling:
|
|
|
|
test al,CLOCK_BIT_MASK ; Q: Clock Low ?
|
|
jz Stat_ClockStartRise ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx ; Q: Timeout ?
|
|
jnz Stat_ClockFalling ; N: continue looping.
|
|
|
|
mov eax, ERROR_CLOCKFALLING
|
|
jmp Stat_Complete ; Time out error.
|
|
|
|
Stat_ClockStartRise:
|
|
|
|
mov ecx, StatusInfo.LowHighTimeout
|
|
|
|
Stat_ClockRising:
|
|
|
|
test al, CLOCK_BIT_MASK ; Q: Clock = 1 ?
|
|
jnz Stat_CollectData ; Y: jump.
|
|
|
|
push edx ; read byte from gameport
|
|
call PORTIO_Read
|
|
|
|
dec ecx ; Q: Timeout ?
|
|
jnz Stat_ClockRising ; N: continue looping.
|
|
|
|
mov eax, ERROR_CLOCKRISING
|
|
jmp Stat_Complete ; Time out error.
|
|
|
|
Stat_CollectData:
|
|
|
|
shr al, 5 ; move data to lower 3 bits
|
|
test ebx, STATUS_CLOCK_COMPLETE ; Q: is packet complete ?
|
|
jnz Stat_Success ; Y: jump.
|
|
|
|
shrd edi, eax, 3 ; shift data into edi.
|
|
shl ebx, 1 ; advance clock counter.
|
|
jmp Stat_CheckClkState
|
|
|
|
Stat_Success:
|
|
|
|
shrd edi, eax, 3 ; shift data into edi.
|
|
shr edi, 11
|
|
mov dword ptr [esi], edi
|
|
mov Status, edi
|
|
|
|
mov eax, PortInfo ; wait for clock to settle
|
|
push eax
|
|
call PORTIO_WaitClockLow
|
|
|
|
push StatusInfo.ClockDutyCycle
|
|
mov eax, PortInfo ; wait for clock to settle
|
|
push eax
|
|
call PORTIO_IsClockActive
|
|
|
|
or al, al
|
|
mov eax, ERROR_EXTRACLOCKS ; probably gamepads
|
|
jne Stat_Complete
|
|
|
|
mov eax, PortInfo ; wait for clock to settle
|
|
push eax
|
|
call PORTIO_WaitClockHigh
|
|
|
|
mov eax, ERROR_SUCCESS
|
|
|
|
Stat_Complete:
|
|
|
|
mov Result, eax
|
|
mov Clks, ebx
|
|
|
|
pop esi
|
|
pop edi
|
|
}
|
|
|
|
// ----------------
|
|
ReadStatusExit:
|
|
// ----------------
|
|
|
|
for (StatusPacket->ClocksSampled = 0; Clks >> (StatusPacket->ClocksSampled+1); StatusPacket->ClocksSampled++);
|
|
StatusPacket->TimeStamp = TIMER_GetTickCount ();
|
|
StatusPacket->LastError = Result;
|
|
StatusPacket->LastError = Result;
|
|
StatusPacket->Transaction = MSGAME_TRANSACT_STATUS;
|
|
|
|
PORTIO_UnMaskInterrupts ();
|
|
PORTIO_ReleasePort (PortInfo);
|
|
|
|
#if (DBG==1)
|
|
switch (Result)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
MsGamePrint ((DBG_VERBOSE, "%s_ReadStatus - SUCCEEDED, Data=0x%X,Clk=%ld\n", DEVICENAME, (ULONG)Status,Clks));
|
|
break;
|
|
|
|
case ERROR_LOWCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadStatus - TimeOut@LowClockStart\n", DEVICENAME));
|
|
break;
|
|
|
|
case ERROR_HIGHCLOCKSTART:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadStatus - TimeOut@HighClockStart\n", DEVICENAME));
|
|
break;
|
|
|
|
case ERROR_CLOCKFALLING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadStatus - TimeOut@ClockFalling, Clk=%ld\n", DEVICENAME, Clks));
|
|
break;
|
|
|
|
case ERROR_CLOCKRISING:
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadStatus - TimeOut@ClockRising, Clk=%ld\n", DEVICENAME, Clks));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
MSGAME_PostTransaction (StatusPacket);
|
|
|
|
return (!Result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Force feedback reset service
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ForceReset (PGAMEPORT PortInfo)
|
|
{
|
|
if (!SWLEDZEP_ResetDevice (PortInfo))
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Force feedback status service
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PVOID | Id | Id output buffer
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ForceId (PGAMEPORT PortInfo, PVOID Id)
|
|
{
|
|
PPRODUCT_ID pProduct = (PPRODUCT_ID)Id;
|
|
PSWLEDZEP_ID pLedZep = (PSWLEDZEP_ID)&RawId;
|
|
|
|
if (!SWLEDZEP_ReadId (&IdInfo))
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
|
|
pProduct->cBytes = sizeof (PRODUCT_ID);
|
|
pProduct->dwProductID = pLedZep->ProductId;
|
|
pProduct->dwFWVersion = pLedZep->Version;
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Force feedback status service
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PVOID | Status | Status output buffer
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ForceStatus (PGAMEPORT PortInfo, PVOID Status)
|
|
{
|
|
PJOYCHANNELSTATUS pChannel = (PJOYCHANNELSTATUS)Status;
|
|
PSWLEDZEP_STATUS pLedZep = (PSWLEDZEP_STATUS)&RawStatus;
|
|
|
|
if (!SWLEDZEP_ReadStatus (&StatusInfo))
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
|
|
pChannel->cBytes = sizeof (JOYCHANNELSTATUS);
|
|
pChannel->dwXVel = 0;
|
|
pChannel->dwYVel = 0;
|
|
pChannel->dwXAccel = 0;
|
|
pChannel->dwYAccel = 0;
|
|
pChannel->dwEffect = pLedZep->Effect;
|
|
pChannel->dwDeviceStatus = pLedZep->Status;
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Force feedback acknak service
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PULONG | AckNak | AckNak
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ForceAckNak (PGAMEPORT PortInfo, PULONG AckNak)
|
|
{
|
|
if (!PORTIO_GetAckNak (PortInfo, StatusGateTimeout, (PUCHAR)AckNak))
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Force feedback NakAck service
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PULONG | NakAck | NakAck
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ForceNakAck (PGAMEPORT PortInfo, PULONG NakAck)
|
|
{
|
|
if (!PORTIO_GetNakAck (PortInfo, StatusGateTimeout, (PUCHAR)NakAck))
|
|
return (STATUS_DEVICE_NOT_CONNECTED);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Force feedback sync service
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm PULONG | NakAck | NakAck
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_ForceSync (PGAMEPORT PortInfo, PULONG Sync)
|
|
{
|
|
*Sync = PORTIO_Read (PortInfo);
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Driver entry point for device
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_DriverEntry (VOID)
|
|
{
|
|
MsGamePrint((DBG_INFORM,"%s: %s_DriverEntry Enter\n", DEVICENAME, DEVICENAME));
|
|
|
|
//
|
|
// 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 SWLEDZEP_ConnectDevice (PGAMEPORT PortInfo)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
ULONG i = MAX_CONNECT_ATTEMPTS;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_ConnectDevice enter\n", DEVICENAME));
|
|
|
|
DataInfo.PortInfo = IdInfo.PortInfo = StatusInfo.PortInfo = *PortInfo;
|
|
|
|
//
|
|
// Convert registry timing values
|
|
//
|
|
|
|
SWLEDZEP_Calibrate (PortInfo);
|
|
|
|
//
|
|
// Connection method (try these steps twice)
|
|
//
|
|
|
|
do
|
|
{
|
|
//
|
|
// 1. Delay 1 millisecond.
|
|
//
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
//
|
|
// 2. Get the ID string.
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DeviceConnectProc getting ID string\n", DEVICENAME));
|
|
if (!SWLEDZEP_GetId (&IdInfo))
|
|
continue;
|
|
|
|
//
|
|
// 3. Delay 1 millisecond.
|
|
//
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
//
|
|
// 4. Reset device (tri-state midi so we don't get unintended forces)
|
|
//
|
|
|
|
if (!ResetComplete)
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "%s_ConnectDevice - resetting device\n", DEVICENAME));
|
|
if (!SWLEDZEP_ResetDevice (&DataInfo.PortInfo))
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 5. Delay 1 millisecond.
|
|
//
|
|
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
//
|
|
// 6. Set speed to 98K for starters
|
|
//
|
|
|
|
MsGamePrint ((DBG_CONTROL, "%s: DeviceConnectProc setting device speed\n", DEVICENAME));
|
|
SWLEDZEP_SetDeviceSpeed (&DataInfo.PortInfo, WHEEL_SPEED_98K);
|
|
TIMER_DelayMicroSecs (TIMER_GetDelay(ONE_MILLI_SEC));
|
|
|
|
//
|
|
// 7. Mark device found and return
|
|
//
|
|
|
|
LedZepInfo.NumDevices = 1;
|
|
ResetComplete = TRUE;
|
|
return (STATUS_SUCCESS);
|
|
|
|
} while (--i);
|
|
|
|
//
|
|
// Return error
|
|
//
|
|
|
|
LedZepInfo.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 SWLEDZEP_ReadReport (PGAMEPORT PortInfo, PDEVICE_PACKET Report)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
MsGamePrint ((DBG_VERBOSE, "%s_ReadReport enter\n", DEVICENAME));
|
|
|
|
//
|
|
// 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, "%s_ReadReport - port collision\n", DEVICENAME));
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
goto ReadReportExit;
|
|
}
|
|
|
|
//
|
|
// Get a packet and check for errors
|
|
//
|
|
|
|
ntStatus = SWLEDZEP_ReadData (&DataInfo);
|
|
if (NT_SUCCESS(ntStatus) && DEVICE_IsOddParity (DataInfo.Data, GAME_PACKET_SIZE))
|
|
{
|
|
memcpy (ValidData, DataInfo.Data, sizeof (ValidData));
|
|
SWLEDZEP_ProcessDataError (&DataInfo.PortInfo, FALSE);
|
|
}
|
|
else if (ntStatus != STATUS_DEVICE_BUSY)
|
|
{
|
|
DataInfo.Failures++;
|
|
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
MsGamePrint ((DBG_SEVERE, "%s_ReadReport - Invalid packet or parity error\n", DEVICENAME));
|
|
SWLEDZEP_ProcessDataError (&DataInfo.PortInfo, TRUE);
|
|
}
|
|
else
|
|
{
|
|
MsGamePrint ((DBG_CONTROL, "SWLEDZEP_ReadReport - Port busy or in use\n"));
|
|
}
|
|
|
|
// ---------------
|
|
ReadReportExit:
|
|
// ---------------
|
|
|
|
SWLEDZEP_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 SWLEDZEP_StartDevice (PGAMEPORT PortInfo)
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "%s_StartDevice enter\n", DEVICENAME));
|
|
|
|
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 SWLEDZEP_StopDevice (PGAMEPORT PortInfo, BOOLEAN TouchHardware)
|
|
{
|
|
MsGamePrint ((DBG_INFORM, "%s_StopDevice enter\n", DEVICENAME));
|
|
|
|
UNREFERENCED_PARAMETER (PortInfo);
|
|
UNREFERENCED_PARAMETER (TouchHardware);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// @func Device handler for HID Feature requests
|
|
// @parm PGAMEPORT | PortInfo | Gameport parameters
|
|
// @parm HID_REPORT_ID | ReportId | HID Feature Id
|
|
// @parm PVOID | ReportBuffer | Output buffer pointer
|
|
// @parm ULONG | ReportSize | Output buffer size
|
|
// @parm PULONG | Returned | Bytes returned pointer
|
|
// @rdesc Returns NT status code
|
|
// @comm Private function
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS SWLEDZEP_GetFeature (PGAMEPORT PortInfo, HID_REPORT_ID ReportId, PVOID ReportBuffer, ULONG ReportSize, PULONG Returned)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature enter\n", DEVICENAME));
|
|
|
|
//
|
|
// Handle feature codes
|
|
//
|
|
|
|
switch (ReportId)
|
|
{
|
|
case MSGAME_INPUT_JOYINFOEX:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature JoyInfoEx\n", DEVICENAME));
|
|
if (ReportSize < sizeof (DEVICE_PACKET)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature JoyInfoEx Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ReadReport (PortInfo, ReportBuffer);
|
|
*Returned += sizeof (DEVICE_PACKET);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_GETID:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature GetId\n", DEVICENAME));
|
|
if (ReportSize < sizeof(PRODUCT_ID)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetId Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ForceId (PortInfo, ReportBuffer);
|
|
*Returned += sizeof(PRODUCT_ID);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_GETSTATUS:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature GetStatus\n", DEVICENAME));
|
|
if (ReportSize < sizeof(JOYCHANNELSTATUS)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetStatus Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ForceStatus (PortInfo, ReportBuffer);
|
|
*Returned += sizeof(JOYCHANNELSTATUS);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_GETACKNAK:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature GetAckNak\n", DEVICENAME));
|
|
if (ReportSize < sizeof(ULONG)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetAckNak Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ForceAckNak (PortInfo, ReportBuffer);
|
|
*Returned += sizeof(ULONG);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_GETNAKACK:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature GetNakAck\n", DEVICENAME));
|
|
if (ReportSize < sizeof(ULONG)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetNakAck Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ForceNakAck (PortInfo, ReportBuffer);
|
|
*Returned += sizeof(ULONG);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_GETSYNC:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature GetSync\n", DEVICENAME));
|
|
if (ReportSize < sizeof(ULONG)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetSync Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ForceSync (PortInfo, ReportBuffer);
|
|
*Returned += sizeof(ULONG);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_RESET:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature Reset\n", DEVICENAME));
|
|
if (ReportSize < sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetVersion Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
ntStatus = SWLEDZEP_ForceReset (PortInfo);
|
|
}
|
|
break;
|
|
|
|
case MSGAME_FEATURE_GETVERSION:
|
|
MsGamePrint ((DBG_INFORM, "%s_GetFeature GetVersion\n", DEVICENAME));
|
|
if (ReportSize < sizeof(ULONG)+sizeof(HID_REPORT_ID))
|
|
{
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature GetVersion Bad Buffer Size = %lu\n", DEVICENAME, ReportSize));
|
|
}
|
|
else
|
|
{
|
|
*((PULONG)ReportBuffer) = 0x20000;
|
|
*Returned += sizeof(ULONG);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
MsGamePrint ((DBG_SEVERE, "%s_GetFeature Invalid ReportId = %lu\n", DEVICENAME, ReportId));
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
return (ntStatus);
|
|
}
|
|
|