/* SCCSID = "src/dev/usb/USBKBD/KBDIRQ.C, usb, c.basedd 98/07/10" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1997, 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  KBDIRQ.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  IDC routine to process legacy driver requests         */
/*                                                                            */
/*   FUNCTION:                                                                */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             KBDirq                                                         */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/06/30  LR                                                      */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "kbd.h"

void KBDirq (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   USBRB FAR *processedRB;

   processedRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;

#ifdef DEBUG
   dsPrint2 (DBG_IRQFLOW, "USBKBD: IRQ = %d, kbdIndex = %d\r\n",
             processedRB->requestData1, processedRB->requestData2);
#endif

   if (pRP_GENIOCTL->rph.Status != USB_IDC_RC_OK)
   {
      if (processedRB->status & USRB_STATUS_STALLED)
      {
         if (processedRB->requestData1 == KBD_IRQ_STATUS_STALLED)
         {
#ifdef DEBUG
            dsPrint2 (DBG_CRITICAL, "USBKBD: IRQ = %d STALLED! kbdIndex = %d\r\n",
                      processedRB->requestData1, processedRB->requestData2);
#endif
            return;
         }
         else KBDClearStalled (pRP_GENIOCTL);
      }
      return;
   }
   switch (processedRB->requestData1)
   {
   case KBD_IRQ_STATUS_IDLESET:
      SetLEDs (gLEDs);
      ReadInterruptPipe (pRP_GENIOCTL);
      break;
   case KBD_IRQ_STATUS_DURATION:

      break;
   case KBD_IRQ_STATUS_INTPIPE:
      InterruptDataReceived (pRP_GENIOCTL);
      ReadInterruptPipe (pRP_GENIOCTL);
      break;
   case KBD_IRQ_STATUS_SETACK:

      break;
   case KBD_IRQ_STATUS_STALLED:
      ReadInterruptPipe (pRP_GENIOCTL);
      break;
   default:;
#ifdef DEBUG
      dsPrint1 (DBG_CRITICAL, "USBKBD: IRQ = %d ???\r\n", processedRB->requestData1);
#endif
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  InterruptDataReceived                            */
/*                                                                    */
/* DESCRIPTIVE NAME:  Data received on keyboard interrupt pipe        */
/*                                                                    */
/* FUNCTION:  This routine is called when report has been received    */
/*            throught keyboard interrupt pipe. Received data is      */
/*            processed and key scan codes passed to legacy driver.   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  InterruptDataReceived                               */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  sets return code in pRP_GENIOCTL->rph.Status             */
/*                                                                    */
/* INTERNAL REFERENCES:  ReadInterruptPipe                            */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

#pragma optimize("eglt", off)

void InterruptDataReceived (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   USBRB FAR *processedRB;
   BYTE      *pIntData;
   BYTE  FAR *pScanCode;
   USHORT     kbdIndex, iprev, icurr, limit;
   BYTE       repetition, modKey, key, duration, *pModByte;

   processedRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   kbdIndex = LOUSHORT (processedRB->requestData2);

   if (gDevice) if (kbdIndex != gKbdIndex) return;

   pIntData = (BYTE *)&gKBD[kbdIndex].buffer;

   if (gKBD[kbdIndex].inReportID && pIntData[FIRST] != gKBD[kbdIndex].inReportID) return;

#ifdef DEBUG
   dsPrint4 (DBG_IRQFLOW, "USBKBD: IntData = 0x%x, %d, %d, kbdIndex = %d\r\n",
             pIntData[0], pIntData[gKBD[kbdIndex].keyOff], pIntData[gKBD[kbdIndex].keyOff+1],
             kbdIndex);
#endif

   pScanCode = (BYTE FAR *)&processedRB->requestData3;
   pModByte = (BYTE *)&gKBD[kbdIndex].LEDoff[UI_LED_UNDEF];
   limit = gKBD[kbdIndex].inReportLength - gKBD[kbdIndex].keyOff;

   for (icurr = 0, repetition = TURNON; icurr < gKBD[kbdIndex].inReportLength; icurr++)
      if (pIntData[icurr] != gKBD[kbdIndex].prevBuff[icurr])
      {
         repetition = TURNOFF;
         break;
      }
   if (!repetition)
   {
/*
    Modifier Keys (left and right): Ctrl, Shift, Alt, and GUI
    1-bit variable fields
*/
      for (icurr = UI_LCTRL; icurr <= UI_RGUI; icurr++)
      {
         if ((iprev = gKBD[kbdIndex].modOff[icurr-UI_LCTRL]) != (BYTE)FULL_WORD)
         {
            modKey = (BYTE)(iprev % BITS_IN_BYTE); // bit #
            modKey = (BYTE)(BIT_0 << modKey);      // bit mask
            iprev /= BITS_IN_BYTE;                 // byte index

            if (gKBD[kbdIndex].prevBuff[iprev] & modKey ^ pIntData[iprev] & modKey)
            {
               iprev  = pIntData[iprev] & modKey;
               modKey = (BYTE)icurr;
               break;
            }
            else modKey = NULL;
         }
      }
      if (modKey)
      {
         pScanCode[FIRST]  = SCAN_E0;
         pScanCode[SECOND] = NULL;

         switch (modKey)
         {
         case UI_LCTRL:
            pScanCode[FIRST] = SCAN_CTRL;
            if (iprev) *pModByte |=  LCTRL;
            else       *pModByte &= ~LCTRL;
            break;
         case UI_LSHIFT:
            pScanCode[FIRST] = SCAN_LSHIFT;
            if (iprev) *pModByte |=  LSHIFT;
            else       *pModByte &= ~LSHIFT;
            break;
         case UI_LALT:
            pScanCode[FIRST] = SCAN_ALT;
            if (iprev) *pModByte |=  LALT;
            else       *pModByte &= ~LALT;
            break;
         case UI_LGUI:
            pScanCode[SECOND] = SCAN_LGUI;
            if (iprev) *pModByte |=  LGUI;
            else       *pModByte &= ~LGUI;
            break;
         case UI_RCTRL:
            pScanCode[SECOND] = SCAN_CTRL;
            if (iprev) *pModByte |=  RCTRL;
            else       *pModByte &= ~RCTRL;
            break;
         case UI_RSHIFT:
            pScanCode[FIRST] = SCAN_RSHIFT;
            if (iprev) *pModByte |=  RSHIFT;
            else       *pModByte &= ~RSHIFT;
            break;
         case UI_RALT:
            pScanCode[SECOND] = SCAN_ALT;
            if (iprev) *pModByte |=  RALT;
            else       *pModByte &= ~RALT;
            break;
         case UI_RGUI:
            pScanCode[SECOND] = SCAN_RGUI;
            if (iprev) *pModByte |=  RGUI;
            else       *pModByte &= ~RGUI;
         } // switch (modKey)
         if (!iprev)
         {
            if (pScanCode[FIRST] != SCAN_E0) pScanCode[FIRST]  |= SCAN_REL;
            else                             pScanCode[SECOND] |= SCAN_REL;
         }
         LegKBDCall (pRP_GENIOCTL);
      } // if (modKey)
/*
    Previous Keys
*/
      for (iprev = 0; iprev < limit; iprev++)
      {
         pScanCode[FIRST]  = SCAN_E0;
         pScanCode[SECOND] = NULL;

         if (key = gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev])
         {
            for (icurr = 0; icurr < limit; icurr++)
               if (key == pIntData[gKBD[kbdIndex].keyOff+icurr]) break;
            while (icurr == limit)
            {
               if (key == UI_KENTER || key == UI_APPL)
               {
                  pScanCode[SECOND] = gScanCode[key] | (BYTE)SCAN_REL;
                  LegKBDCall (pRP_GENIOCTL);
                  break;
               }
               if (key >= UI_INS && key <= UI_UP || key == UI_KSLASH)
               {
                  pScanCode[SECOND] = gScanCode[key] | (BYTE)SCAN_REL;
                  LegKBDCall (pRP_GENIOCTL);
                  if (*pModByte & LSHIFT)
                  {
                     pScanCode[SECOND] = SCAN_LSHPR ^ (BYTE)SCAN_REL;
                     LegKBDCall (pRP_GENIOCTL);
                  }
                  else if (*pModByte & RSHIFT)
                  {
                     pScanCode[SECOND] = SCAN_RSHPR ^ (BYTE)SCAN_REL;
                     LegKBDCall (pRP_GENIOCTL);
                  }
                  break;
               }
               if (key == UI_PAUSE) break;
               if (key == UI_PRTSCR)
               {
                  if (*pModByte & LALT || *pModByte & RALT)
                     pScanCode[FIRST] = SCAN_SYSRQ | (BYTE)SCAN_REL;
                  else
                  {
                     if (*pModByte & LSHIFT || *pModByte & RSHIFT ||
                         *pModByte & LCTRL  || *pModByte & RCTRL)
                        pScanCode[SECOND] = SCAN_ASTER | (BYTE)SCAN_REL;
                     else
                     {
                        pScanCode[SECOND] = SCAN_ASTER | (BYTE)SCAN_REL;
                        LegKBDCall (pRP_GENIOCTL);
                        pScanCode[SECOND] = SCAN_LSHIFT | (BYTE)SCAN_REL;
                     }
                  }
                  LegKBDCall (pRP_GENIOCTL);
                  break;
               }
               pScanCode[FIRST]  = gScanCode[key] | (BYTE)SCAN_REL;
               if (pScanCode[FIRST] != SCAN_REL) LegKBDCall (pRP_GENIOCTL);
               break;
            } // while
         }    // if (key)
      }       // for (iprev...
   }          // if (!repetition)
/*
   Current Keys
*/
   for (icurr = 0; icurr < limit; icurr++)
   {
      pScanCode[FIRST]  = SCAN_E0;
      pScanCode[SECOND] = NULL;
      if ((key = pIntData[gKBD[kbdIndex].keyOff+icurr]) > UI_MAX)
         key = pIntData[gKBD[kbdIndex].keyOff+icurr]  = UI_MIN;
      if (key)
      {
         for (iprev = 0; iprev < limit; iprev++)
            if (gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] == key) break;
         while (iprev == limit || repetition)
         {
            if (key == UI_KENTER || key == UI_APPL)
            {
               pScanCode[SECOND] = gScanCode[key];
               LegKBDCall (pRP_GENIOCTL);
               break;
            }
            if (key >= UI_INS && key <= UI_UP || key == UI_KSLASH)
            {
               if (*pModByte & LSHIFT)
               {
                  pScanCode[SECOND] = SCAN_LSHPR;
                  LegKBDCall (pRP_GENIOCTL);
               }
               else if (*pModByte & RSHIFT)
               {
                  pScanCode[SECOND] = SCAN_RSHPR;
                  LegKBDCall (pRP_GENIOCTL);
               }
               pScanCode[SECOND] = gScanCode[key];
               LegKBDCall (pRP_GENIOCTL);
               break;
            }
            if (key == UI_PAUSE)
            {
               /*
                  Generates one stream of scan codes without corresponding up scan codes.
                  Actually the up scan codes are part of this stream.
               */
               if (*pModByte & LCTRL || *pModByte & RCTRL)
               {                                           // Ctrl+Pause = Break
                  pScanCode[SECOND] = SCAN_BREAK2;
                  LegKBDCall (pRP_GENIOCTL);
                  pScanCode[SECOND] |= SCAN_REL;
               }
               else
               {                                           // Pause
                  pScanCode[FIRST] = SCAN_E1;
                  LegKBDCall (pRP_GENIOCTL);
                  pScanCode[FIRST] = SCAN_PAUSE2;
                  LegKBDCall (pRP_GENIOCTL);
                  pScanCode[FIRST] = SCAN_PAUSE3;
                  LegKBDCall (pRP_GENIOCTL);
                  pScanCode[FIRST] = SCAN_E1;
                  LegKBDCall (pRP_GENIOCTL);
                  pScanCode[FIRST] = SCAN_PAUSE2 | SCAN_REL;
                  LegKBDCall (pRP_GENIOCTL);
                  pScanCode[FIRST] = SCAN_PAUSE3 | SCAN_REL;
               }
               LegKBDCall (pRP_GENIOCTL);
               break;
            }
            if (key == UI_PRTSCR)
            {
               if (*pModByte & LALT || *pModByte & RALT) pScanCode[FIRST] = SCAN_SYSRQ;
               else
               {
                  if (*pModByte & LSHIFT || *pModByte & RSHIFT ||
                      *pModByte & LCTRL  || *pModByte & RCTRL)
                     pScanCode[SECOND] = SCAN_ASTER;
                  else
                  {
                     pScanCode[SECOND] = SCAN_LSHIFT;
                     LegKBDCall (pRP_GENIOCTL);
                     pScanCode[SECOND] = SCAN_ASTER;
                  }
               }
               LegKBDCall (pRP_GENIOCTL);
               break;
            }
            pScanCode[FIRST] = gScanCode[key];
            if (pScanCode[FIRST]) LegKBDCall (pRP_GENIOCTL);
            break;
         } // while
      }    // if (key)
   }       // for (icurr...
   for (iprev = 0; iprev < gKBD[kbdIndex].inReportLength; iprev++)
      gKBD[kbdIndex].prevBuff[iprev] = pIntData[iprev];

   for (iprev = icurr = 0; iprev < limit; iprev++)
   {
      if (gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] >  UI_ERROR &&
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] != UI_CLOCK &&
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] != UI_SLOCK &&
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] != UI_NLOCK &&
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] != UI_PAUSE)   icurr++;

      if (gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] == UI_CLOCK ||
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] == UI_SLOCK ||
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] == UI_NLOCK ||
          gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff+iprev] == UI_PAUSE)   gKBD[kbdIndex].active |= ACTIVE;
   }
   if (!icurr)
   {
      if (!(gKBD[kbdIndex].active & ACTIVE))
      {
         if (repetition)
         {
            gKBD[kbdIndex].active >>= TURNON;
            duration = DURATION_INFINITY;
         }
         else
         {
            gKBD[kbdIndex].active = PRESET;
            duration = gTypeDelay;
         }
      }
      else
      {
         if (gKBD[kbdIndex].active & PRESET) duration = repetition? gTypeDelay : (BYTE)DURATION_INFINITY;
//0630            if (gKBD[kbdIndex].active & PRESET) duration = gTypeDelay;
         else                                duration = DURATION_INFINITY;
      }
   }
   else
   { // (!icurr)
      gKBD[kbdIndex].active |= ACTIVE;
      if (gKBD[kbdIndex].active & PRESET) duration = gTypeRate;
      else                                duration = repetition? gTypeRate : gTypeDelay;
   }
   if (HIBYTE(gKBD[kbdIndex].setITpack.wValue) != duration)
   {
      gKBD[kbdIndex].setITpack.wValue &= FULL_BYTE;
      gKBD[kbdIndex].setITpack.wValue |= duration << BITS_IN_BYTE;
      SetIdleTime (kbdIndex, KBD_IRQ_STATUS_DURATION);
   }
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ReadInterruptPipe                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Read interrupt pipe routine                     */
/*                                                                    */
/* FUNCTION:  This routine opens  keyboard interrupt pipe to          */
/*            receive status reports.                                 */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ReadInterruptPipe                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void ReadInterruptPipe (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBRB FAR  *processedRB;
   USBRB       hcdReqBlock;
   RP_GENIOCTL rp_USBReq;
   USHORT      deviceIndex;

   processedRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;

   deviceIndex = LOUSHORT(processedRB->requestData2);

   setmem((PSZ)gKBD[deviceIndex].buffer, UI_RESERV, sizeof(gKBD[deviceIndex].buffer));

   hcdReqBlock.controllerId = processedRB->controllerId;
   hcdReqBlock.deviceAddress = processedRB->deviceAddress; // use default address to set address for unconfigured devices
   hcdReqBlock.endPointId = gKBD[deviceIndex].interruptPipeAddress;
   hcdReqBlock.status = 0; // not used
   hcdReqBlock.flags = USRB_FLAGS_TTYPE_IN | USRB_FLAGS_DET_INTRPT;
   if (!(processedRB->flags & USRB_FLAGS_DET_DTGGLEON)) hcdReqBlock.flags |= USRB_FLAGS_DET_DTGGLEON;
   hcdReqBlock.buffer1 = (PUCHAR)gKBD[deviceIndex].buffer;
   hcdReqBlock.buffer1Length = gKBD[deviceIndex].inReportLength;
   hcdReqBlock.buffer2 = NULL;     // no additonal data to be sent to/from host
   hcdReqBlock.buffer2Length = 0;  // to complete this request
   hcdReqBlock.serviceTime = USB_DEFAULT_SRV_INTV;
   hcdReqBlock.maxPacketSize = USB_DEFAULT_PKT_SIZE;
   hcdReqBlock.maxErrorCount = USB_MAX_ERROR_COUNT;
   hcdReqBlock.usbIDC = (PUSBIDCEntry)KBDidc; // Address of IRQ processing routine to be called for this request
   hcdReqBlock.usbDS = GetDS();
   hcdReqBlock.category = USB_IDC_CATEGORY_CLIENT;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1 = KBD_IRQ_STATUS_INTPIPE;  // USBD I/O call type ID - set device address
   hcdReqBlock.requestData2 = MAKEULONG(deviceIndex, 0);        // index in device table to current device
   hcdReqBlock.requestData3 = 0;                        // not used
// hcdReqBlock.dsPhyAddr = 0;                  // data segment physical address

   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd = CMDGenIOCTL;
   rp_USBReq.Category = USB_IDC_CATEGORY_CLASS;
   rp_USBReq.Function = USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket = (PVOID)&hcdReqBlock;

   USBCallIDC (gpHIDIDC, gdsHIDIDC, (RP_GENIOCTL FAR *)&rp_USBReq);

#ifdef DEBUG
   dsPrint3 (DBG_IRQFLOW, "USBKBD: ReadInterruptPipe kbdIndex = %d, endpt = %d, Status = %x\r\n",
             deviceIndex, hcdReqBlock.endPointId, pRP_GENIOCTL->rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  KBDClearStalled                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Clears stalled communication pipe               */
/*                                                                    */
/* FUNCTION:  This routine calls USBD driver to clear stalled pipe.   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  KBDClearStalled                                     */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:          USBCallIDC                                   */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void KBDClearStalled (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   USBRB FAR *processedRB;

   processedRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBKBD: KBDClearStalled, kbdIndex = %d\r\n", processedRB->requestData2);
#endif

   processedRB->requestData1 = KBD_IRQ_STATUS_STALLED;

   pRP_GENIOCTL->Category = USB_IDC_CATEGORY_CLASS;
   pRP_GENIOCTL->Function = USB_IDC_FUNCTION_CLRSTALL;

   USBCallIDC (gpHIDIDC, gdsHIDIDC, pRP_GENIOCTL);
}

