⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 poll.c

📁 一个简单实现windows游戏杆的驱动示例-hidgame
💻 C
📖 第 1 页 / 共 2 页
字号:
/*---
Copyright (c) 1998 - 1999  Microsoft Corporation

Module Name:

    poll.c

Abstract: This module contains the routines to poll an analog gameport device

Environment:

    Kernel mode


--*/

#include "hidgame.h"


/*****************************************************************************
 *
 *  @doc    EXTERNAL
 *
 *  @func   NTSTATUS  | HidAnalogPoll |
 *
 *          Polling routine for analog joysticks. 
 *  <nl>Polls the analog device for position and button information.
 *  The position  information in analog devices is conveyed by the
 *  duration of a pulse width. Each axis occupies one bit position.
 *  The read operation is started by writing a value to the joystick
 *  io address. Immediately thereafter we begin examing the values
 *  returned and the elapsed time.
 *
 *  This sort of device has a few limitations:
 *
 *  First, button information is not latched by the device, so if a
 *  button press which occurrs in between polls it will be lost.
 *  There is really no way to prevent this short of devoting
 *  the entire cpu to polling.  In reality this does not cause a problem.
 *
 *  Second, since it is necessary to measure the duration of the axis pulse, 
 *  the most accurate results would be obtained using the smallest possible 
 *  sense loop and no interruptions of this loop.   
 *  The typical range of pulse lengths is from around 10 uSecs to 1500 uSecs 
 *  but depending on the joystick and gameport, this could extend to at least 
 *  8000 uSecs.  Keeping interrupts disabled for this length of time causes 
 *  many problems, like modems losing connections to sound break ups.
 *
 *  Third, because each iteration of the poll loop requires an port read, the 
 *  speed of the loop is largely constrained by the speed of the IO bus.  
 *  This also means that when there is contention for the IO bus, the loop 
 *  will be slowed down.  IO contention is usually caused by DMAs (or FDMAs)
 *  which result in a significant slow down.
 *
 *  Forth, because of the previous two problems, the poll loop may be slowed 
 *  down or interrupted at any time so an external time source is needed to 
 *  measure the pulse width for each axis.  The only cross-platform high 
 *  resolution timer is the read with KeQueryPerformanceCounter.  
 *  Unfortunately the implementation of this often uses a 1.18MHz 8253 timer 
 *  which requires 3 IO accesses to read, compounding the third problem and 
 *  even then, the result may need to be reread if the counters were in the 
 *  wrong state.  Current CPUs have on board counters that can be used to 
 *  provide very accurate timing and more recent HAL implementations tend to 
 *  use these to implement KeQueryPerformanceCounter so this will be a problem 
 *  on less systems as time goes on.  In the majority of cases, a poor 
 *  KeQueryPerformanceCounter implementation is made irrelevant by testing 
 *  for the availability of a CPU time stamp counter on Intel architechtures 
 *  and using it directly if it is available.
 *
 *  The algorithm implemented here is not the most obvious but works as 
 *  follows:
 *  
 *  Once started, the axes read a value of one until the completion of their 
 *  pulse.  The axes are the four lower bits in the byte read from the port.
 *  The state of the axes in each iteration of the poll loop is therefore 
 *  represented as a value between 0 and 15.  The important time for each 
 *  axis is the time at which it changes from 1 to 0.  This is done by using 
 *  the value representing the state of the axes to index an array into which 
 *  time values are stored.  For each axis, the duration of its pulse width is 
 *  the latest time stored in the array at an index with the bit for that axis 
 *  set.  However since interrupts can occur at any time, it is not possible 
 *  to simultaneously read the port value and record that time in an atomic 
 *  operation the in each iteration, the current time is stored in two arrays, 
 *  one using the index before the time was recorded and the other using the 
 *  index after the time was recorded.
 *  Once all the axes being monitored have become 0, or a timeout value is 
 *  reached, the data left in the arrays is analysed to find the best 
 *  estimate for the transition time for each axis.  If the times before and 
 *  after the transition differ by too much, it is judged that an interrupt 
 *  must have occured so the last known good axis value is returned unless 
 *  that falls outside the range in which it is known that the transition 
 *  occured.
 *          
 *  This routine cannot be pageable as HID can make reads at dispatch-level.
 *
 *  @parm   IN PDEVICE_EXTENSION | DeviceExtension | 
 *
 *          Pointer to the device extension. 
 *
 *  @parm   IN UCHAR | resisitiveInputMask |
 *      
 *          Mask that describes the axes lines that are to be polled
 * 
 *  @parm   IN BOOLEAN | fApproximate |
 *
 *          Boolean value indicating if it is OK to approximate some
 *          value of the current axis state with the last axis state
 *          if polling was not successful (we took an interrput during polling) 
 *
 *  @parm   IN OUT ULONG | Axis[MAX_AXES] |
 *          
 *          The state of the axes. On entry the last axis state is passed
 *          into this routine. If the fApproximate flag is turned on, we can
 *          make use of the last axis state to "guess" the current axis state.
 *
 *  @parm   OUT UCHAR | Button[PORT_BUTTONS]|
 *
 *          Receives the state of the buttons. 0x0 specifies the button is not
 *          pressed and 0x1 indicates an armed button state.  
 *
 *  @rvalue   STATUS_SUCCESS  | success
 *  @rvalue   STATUS_DEVICE_NOT_CONNECTED | Device Failed to Quiesce 
 *          ( not connected ) This is a failure code.
 *  @rvalue   STATUS_TIMEOUT  | Could not determine exact transition time for 
 *          one or more axis.  This is a success code.
 *
 *****************************************************************************/
/*
 *  Tell a compiler that we "a" won't use any aliasing and "t" want fast code
 */
#pragma optimize( "at", on )

/*
 *  Disable warning for variable used before set as it is hard for a compiler 
 *  to see that TimeNow is always initialized before it is used.
 */
#pragma warning( disable:4701 )

NTSTATUS  INTERNAL
    HGM_AnalogPoll
    (
    IN      PDEVICE_EXTENSION   DeviceExtension,
    IN      UCHAR               resistiveInputMask,
    IN      BOOLEAN             fApproximate,
    IN  OUT ULONG               Axis[MAX_AXES],
        OUT UCHAR               Button[PORT_BUTTONS]
    )

{
    ULONG               BeforeTimes[MAX_AXES*MAX_AXES];
    ULONG               AfterTimes[MAX_AXES*MAX_AXES];
    ULONGLONG           CounterFreq;
    PUCHAR              GameContext;
    NTSTATUS            ntStatus = STATUS_SUCCESS;


    /*  
     *  To improve compiler optimization, we cast the ReadAccessor function to 
     *  return a ULONG instead of a UCHAR.  This means that the result must 
     *  always be masked before use but this would be done anyway to remove 
     *  the parts of the UCHAR we are not interested in.  
     */
typedef ULONG (*PHIDGAME_READPORT) ( PVOID  GameContext );

    PHIDGAME_READPORT   ReadPort;
    ULONG               portLast, portMask;

    
    HGM_DBGPRINT( FILE_POLL | HGM_FENTRY, \
                    ("HGM_AnalogPoll DeviceExtension=0x%x, resistiveInputMask=0x%x",\
                     DeviceExtension, resistiveInputMask ));
    

    portMask = (ULONG)(resistiveInputMask & 0xf);

    /*
     *  Initialize Times to recognizable value
     */
    memset( (PVOID)BeforeTimes, 0, sizeof( BeforeTimes ) );
    memset( (PVOID)AfterTimes, 0, sizeof( AfterTimes ) );

    /*
     *  Find where our port and data area are, and related parameters
     */
    GameContext = DeviceExtension->GameContext;
    ReadPort = (PHIDGAME_READPORT)(*DeviceExtension->ReadAccessor);

    /*
     *  get the buttons (not forgetting that the top 3 bytes are garbage)
     */
    portLast = ReadPort(GameContext);
    Button[0] = (UCHAR)(( portLast & 0x10 ) == 0x0);
    Button[1] = (UCHAR)(( portLast & 0x20 ) == 0x0);
    Button[2] = (UCHAR)(( portLast & 0x40 ) == 0x0);
    Button[3] = (UCHAR)(( portLast & 0x80 ) == 0x0);

    portLast = portMask;

    /*
     *  Start the pots 
     *  (any debug output from here until the completion of the 
     *  while( portLast ) loop will destroy the axis data)
     */
    (*DeviceExtension->WriteAccessor)(GameContext, JOY_START_TIMERS);

    /*
     *  Keep reading until all the pots we care about are zero or we time out
     */

    {
        ULONG   TimeNow;
        ULONG   TimeStart;
        ULONG   TimeOut = DeviceExtension->ScaledTimeout/Global.CounterScale;
        ULONG   portVal = portMask;
        
        TimeStart = Global.ReadCounter(NULL).LowPart;
        
        while( portLast )
        {
            TimeNow = Global.ReadCounter(NULL).LowPart - TimeStart;
            AfterTimes[portLast] = TimeNow;
            portLast = portVal;
            portVal  = ReadPort(GameContext) & portMask;
            BeforeTimes[portVal] = TimeNow;

            if( TimeNow >= TimeOut ) break;
        } 

        if( portLast && ( TimeNow >= TimeOut ) )
        {
            HGM_DBGPRINT( FILE_POLL |  HGM_BABBLE, \
                ("HGM_AnalogPoll: TimeNow: 0x%08x TimeOut: 0x%08x", TimeNow, TimeOut ) );
        }
    }

    {
        LONG    axisIdx;

        for( axisIdx = 3; axisIdx>=0; axisIdx-- )
        {
            ULONG   axisMask;

            axisMask = 1 << axisIdx;
            if( axisMask & portMask )
            {
                if( axisMask & portLast )
                {
                    /*
                     *  Whether or not a hit was taken, this axis did not 
                     *  quiesce.  So update the axis time so that next poll 
                     *  the last value will be a timeout in case a hit is 
                     *  taken over both the transition and the timeout.  
                     */
                    Axis[axisIdx] = AXIS_TIMEOUT;
                    ntStatus = STATUS_DEVICE_NOT_CONNECTED;

                    HGM_DBGPRINT( FILE_POLL |  HGM_WARN, \
                                    ("HGM_AnalogPoll: axis %x still set at timeout", axisMask ) );
                }
                else
                {
                    ULONG       timeIdx;
                    ULONG       beforeThresholdTime;
                    ULONG       afterThresholdTime;
                    ULONG       delta;

                    afterThresholdTime = beforeThresholdTime = 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -