#include "uwscmd.h"

// EXTERNAL PROTOTYPES                          
extern void WRITE_REGISTER_ULONG(unsigned long *x, unsigned long y);

extern unsigned long READ_REGISTER_ULONG(void *x);


// INTERNAL PROTOTYPES
int REMIrqRoutine(PDRIVER_INFO pDriverNode, ULONG MouseIntStatus);
int QueueCurrentInput(PDRIVER_INFO pDriverNode);


/* Function Specification *********************************************/
/*                                                                    */
/*  Name:  REMIrqRoutine                                              */
/*                                                                    */
/*  Description:   This is the interrupt service routine invoked by   */
/*                 sysSp_pciIntrHandler. It copys any remote mouse    */
/*                 and or keyboard data from the SP's data area to    */
/*                 the device drivers local storage                   */   
/*  Parameters:                                                       */
/*                                                                    */
/*  Returns:                                                          */
/*     TRUE   - if the interrrupt was handled                         */
/*     FALSE  - if not handled                                        */
/*                                                                    */
/* End Function Specification *****************************************/
int REMIrqRoutine(PDRIVER_INFO pDriverNode, ULONG MouseIntStatus)
{
   ULONG  queue_read_ptr;
   ULONG  queue_write_ptr;
   ULONG  flags = 0;

   USHORT PrevMouseButtonState;
   USHORT CurrMouseButtonState;

   int    loop_count = 0;
   void  *pMsgAddr;

   int   rc = FALSE;

   INPUT_MESSAGE_HEADER  input_message;
   REMOTE_INPUT_DATA     InputDataStruct;

   if (DBG_MOUSE)
   {
       printk(KERN_CRIT "DBG_MOUSE: REMIrqRoutine: Entry, called by sysSp_pciIntrHandler. \n");
   }

   queue_read_ptr  = READ_REGISTER_ULONG( (PULONG)(pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_Q_READ_PTR_OFFSET) );
   queue_write_ptr = READ_REGISTER_ULONG( (PULONG)(pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_Q_WRITE_PTR_OFFSET) );

   if (DBG_MOUSE)
   {
      //printk(KERN_CRIT "DBG_MOUSE: REMIrqRoutine - MouseIntStatus = %li, q_read_ptr = %li, q_write_ptr = %li.\n",
      //                   MouseIntStatus, queue_read_ptr, queue_write_ptr);

      if ( queue_read_ptr == queue_write_ptr ) 
      {
          printk(KERN_CRIT "DBG_MOUSE: SP's mouse queue is empty.\n");
      }
      else if (( ( queue_read_ptr == 0) && (queue_write_ptr == MAX_Q_ENTRIES - 1) ) ||
                  ( queue_read_ptr == queue_write_ptr + 1)) 
      {
          printk(KERN_CRIT "DBG_MOUSE: SP's mouse queue is FULL.\n");
      }
   }

   //*************************************************************************************
   // While there is data in the SP mouse queue. Get each element and transfer it to the *
   // internal mouse queue in the driver.                                                *
   //*************************************************************************************
   while( ( queue_read_ptr != queue_write_ptr ) &&                              // queue is not empty
          ( loop_count < 4 * MAX_Q_ENTRIES )    &&                              // avoid infinite loop
          ( (queue_read_ptr  >= 0) && (queue_read_ptr  < MAX_Q_ENTRIES) )  &&   // bounds check
          ( (queue_write_ptr >= 0) && (queue_write_ptr < MAX_Q_ENTRIES)) )      // bounds check
   {
      loop_count++;

      pMsgAddr = pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_Q_BEGIN_OFFSET + (queue_read_ptr * CONDOR_MOUSE_QUEUE_ENTRY_SIZE);

      if (DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Reading SP mouse data from address %p.\n", pMsgAddr);

      // Copy a block of remote mouse/keyboard data from the SP's memory into our local input message structure
      memcpy(&input_message, pMsgAddr, sizeof(INPUT_MESSAGE_HEADER));

      if ( input_message.input_type == INPUT_TYPE_MOUSE ) 
      { 
          // Initialize InputDataStruct structure for mouse input
          InputDataStruct.InputType                        = INPUT_TYPE_MOUSE;
          InputDataStruct.remote_input.m_input.UnitId      = 0;
          InputDataStruct.remote_input.m_input.Flags       = MOUSE_MOVE_ABSOLUTE;
          InputDataStruct.remote_input.m_input.ButtonFlags = 0;                               // PHR_186364 
          InputDataStruct.remote_input.m_input.ButtonData  = 0;                               // PHR_186364 
          InputDataStruct.remote_input.m_input.RawButtons  = 0;
        //InputDataStruct.remote_input.m_input.Buttons     = 0;                               // PHR_186364 

          //*************************************************************************************
          // Update the current state of the mouse buttons with button transition data. If the  *
          // state of a given button has changed since we received the last packet, set the     *
          // button up/down bit in the Button field                                             *
          //*************************************************************************************
          spin_lock_irqsave( &(pDriverNode->RemoteDataLock), flags );
          PrevMouseButtonState           = pDriverNode->PrevMouseButtonState;
          CurrMouseButtonState           = input_message.mouse_buttons;
          pDriverNode->PrevMouseButtonState = CurrMouseButtonState;
          spin_unlock_irqrestore( &(pDriverNode->RemoteDataLock), flags );

          //*************************************************************************************
          // Double Clicks are detected separetely by the JAVA applet in VNC task. Report and   *
          // handle those separately.....                                                       *
          //*************************************************************************************
          if ( input_message.mouse_buttons == MOUSE_DOUBLE_CLICK_EVENT ) 
          {
               InputDataStruct.remote_input.m_input.ButtonFlags       = MOUSE_DOUBLE_CLICK_EVENT;
               InputDataStruct.remote_input.m_input.ExtraInformation  = MOUSE_DOUBLE_CLICK_EVENT;

               // Clear the state so that a spurious right mouse up is not detected next time. 
               spin_lock_irqsave( &(pDriverNode->RemoteDataLock), flags );
               pDriverNode->PrevMouseButtonState = 0;  
               spin_unlock_irqrestore( &(pDriverNode->RemoteDataLock), flags );

               if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Double click of mouse button detected.\n");

          }
          else   // not a double click, check for mouse button transition
          {
               InputDataStruct.remote_input.m_input.ExtraInformation  = 0;

               if (TRANSITION_DOWN(REMOTE_LEFT))
               {
                   if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Left mouse button down detected.\n");
                   InputDataStruct.remote_input.m_input.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN ;
               }
               else if (TRANSITION_UP(REMOTE_LEFT))
               {
                   if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Left mouse button up detected.\n");
                   InputDataStruct.remote_input.m_input.ButtonFlags |= MOUSE_LEFT_BUTTON_UP ;
               }
               if (TRANSITION_DOWN(REMOTE_MIDDLE))
               {
                   if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Middle mouse button down detected.\n");
                   InputDataStruct.remote_input.m_input.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN ;
               }
               else if (TRANSITION_UP(REMOTE_MIDDLE))
               {
                   if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Middle mouse button up detected.\n");
                   InputDataStruct.remote_input.m_input.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP ;
               }
               if (TRANSITION_DOWN(REMOTE_RIGHT))
               {
                   if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Right mouse button down detected.\n");
                   InputDataStruct.remote_input.m_input.ButtonFlags  |=  MOUSE_RIGHT_BUTTON_DOWN ;
               }
               else if (TRANSITION_UP(REMOTE_RIGHT))
               {
                   if(DBG_MOUSE) printk(KERN_CRIT "DBG_MOUSE: Right mouse button up detected.\n");
                   InputDataStruct.remote_input.m_input.ButtonFlags  |=  MOUSE_RIGHT_BUTTON_UP ;
               }
          }  // end else not a double click

          //  set the mouse x,y coordinates
          InputDataStruct.remote_input.m_input.LastX = input_message.in_data.m_data.mouse_x ;
          InputDataStruct.remote_input.m_input.LastY = input_message.in_data.m_data.mouse_y ;

          if(DBG_MOUSE)
          {
             printk(KERN_CRIT "*******************************************************************\n");
             printk(KERN_CRIT "DBG_MOUSE: REMIrqRoutine received the following remote mouse data: \n");
             printk(KERN_CRIT "InputDataStruct.InputType                        = %li\n",InputDataStruct.InputType);
             printk(KERN_CRIT "InputDataStruct.remote_input.m_input.ButtonFlags = %i  \n",InputDataStruct.remote_input.m_input.ButtonFlags );
             printk(KERN_CRIT "InputDataStruct.remote_input.m_input.LastX       = %li \n",InputDataStruct.remote_input.m_input.LastX); 
             printk(KERN_CRIT "InputDataStruct.remote_input.m_input.LastY       = %li \n",InputDataStruct.remote_input.m_input.LastY ); 
             printk(KERN_CRIT "*******************************************************************\n");
          }

      }
      else if ( input_message.input_type == INPUT_TYPE_KEYBD ) // else this is keboard data
      {
          // Initialize InputDataStruct structure for keyboard input
          InputDataStruct.InputType                        = INPUT_TYPE_KEYBD;
          InputDataStruct.remote_input.k_input.UnitId      = 0;
          InputDataStruct.remote_input.k_input.Keysym_code = input_message.in_data.k_data.keySym; 
          InputDataStruct.remote_input.k_input.Keybd_flags = input_message.in_data.k_data.keyFlag; 
          InputDataStruct.remote_input.k_input.Keybd_down  = input_message.in_data.k_data.keyDown;

          if(DBG_MOUSE)
          {
              printk(KERN_CRIT "******************************************************************\n");
              printk(KERN_CRIT "DBG_MOUSE: REMIrqRoutine received the following rmt keyboard data: \n" );
              printk(KERN_CRIT "InputDataStruct.InputType                        = %li \n", InputDataStruct.InputType );   
              printk(KERN_CRIT "InputDataStruct.remote_input.k_input.Keysym_code = %li \n", InputDataStruct.remote_input.k_input.Keysym_code );
              printk(KERN_CRIT "InputDataStruct.remote_input.k_input.Keybd_flags = %x  \n", InputDataStruct.remote_input.k_input.Keybd_flags );
              printk(KERN_CRIT "InputDataStruct.remote_input.k_input.Keybd_down  = %x  \n", InputDataStruct.remote_input.k_input.Keybd_down  );
              printk(KERN_CRIT "******************************************************************\n");
          }
      }
      else   // Not mouse or keyboard data so invalid data type was received
      {
          printk(KERN_CRIT "IBMASM ERROR: REMIrqRoutine received invalid remote data type.\n");
          break;
      }

      spin_lock_irqsave( &(pDriverNode->RemoteDataLock), flags );

      // Copy the current remote data into our local remote data buffer
      memcpy(&(pDriverNode->CurrentRemoteInput), &InputDataStruct, sizeof(REMOTE_INPUT_DATA));

      spin_unlock_irqrestore( &(pDriverNode->RemoteDataLock), flags );

      //**********************************************************************************
      // Queue the current remote input data onto our local remote input data queue if   *
      // QueueCurrentInput returns FALSE the queue was full so we have to ignore the new *
      // remote input data so set the loop_count to 0 break out of the while and set the *
      // rc to TRUE since the interrupt has been handled.                                *
      //**********************************************************************************
      if (QueueCurrentInput(pDriverNode) == FALSE)
      {
          loop_count = 0;
          break;                                             // exit the while and return
      }

      //*************************************************************************************
      // Update the queue_read_ptr in the SP mouse queue. This tells the SP, how much data  *
      // we have read. queue_read_ptr always points to the next one that we have to read.   *
      //*************************************************************************************
      queue_read_ptr++;

      if ( queue_read_ptr == MAX_Q_ENTRIES )
      {
          queue_read_ptr = 0;    // The circular buffer has wrapped. 

          if (DBG_MOUSE) 
          {
              printk(KERN_CRIT "DBG_MOUSE: The SP's mouse queue has wrapped.\n");
          }


      }

      WRITE_REGISTER_ULONG( (PULONG)(pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_Q_READ_PTR_OFFSET), queue_read_ptr);

      queue_write_ptr = READ_REGISTER_ULONG( (PULONG)(pDriverNode->pRemoteDataBaseAddr + CONDOR_MOUSE_Q_WRITE_PTR_OFFSET) );

   } // end while    (queue_read_ptr != queue_write_ptr) 

   rc = TRUE;
   
   return rc;
}



/*************************************************************************************/
/*  Name:  QueueCurrentInput                                                         */
/*                                                                                   */
/*  Description:  This routine queues the current input data to be                   */
/*                processed by a DPC outside the ISR                                 */
/*                                                                                   */
/*  Parameters:                                                                      */
/*                None                                                               */
/*                                                                                   */
/*  Returns:                                                                         */
/*               TRUE  - The current remote input data was queued                    */
/*               FALSE - The current remote input data was not queued                */
/*                                                                                   */
/*************************************************************************************/
int QueueCurrentInput(PDRIVER_INFO pDriverNode)
{
    int   rc    = FALSE;
    ULONG flags = 0;

    // if the input data queue is full, ignore the new data and return FALSE
    if ((pDriverNode->pRemoteDataIn == pDriverNode->pRemoteDataOut) && (pDriverNode->RemoteInputCount != 0))
    {
        printk(KERN_CRIT "ibmasm ERROR: QueueCurrentInput detected the local input data queue is full\n");
        rc = FALSE;
    }
    else 
    {
        spin_lock_irqsave( &(pDriverNode->RemoteDataLock), flags );

        *(pDriverNode->pRemoteDataIn) = (pDriverNode->CurrentRemoteInput);
        pDriverNode->RemoteInputCount += 1;
        pDriverNode->pRemoteDataIn++;

        if (pDriverNode->pRemoteDataIn == pDriverNode->pRemoteDataEnd) 
        {
            if (DBG_MOUSE)
            {
               printk(KERN_CRIT "DBG_MOUSE: QueueCurrentInput - Remote Data queue wrapped.\n");
            }

            pDriverNode->pRemoteDataIn = pDriverNode->pRemoteDataStart;
        }

        spin_unlock_irqrestore( &(pDriverNode->RemoteDataLock), flags );

        rc = TRUE;
    }

    return(rc);
}                    // end QueueCurrentInput


