/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1998                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  UHCIRQ.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  UHCD IRQ processing routine.                          */
/*                                                                            */
/*   FUNCTION: These routine handles USB Host controller generated             */
/*             hardware IRQ's.                                                */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             UHCIInterrupt                                                  */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/01/31  MB                                                      */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "uhci.h"

static BOOL TDFinished(TD *tdList, ULONG currHeadElement);
static BOOL CheckTD(TD *tdList);

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIInterrupt                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  UHCI compliant USB Host Controller              */
/*                    interrupt service routine.                      */
/*                                                                    */
/* FUNCTION:  The function of this routine is to service USB Host     */
/*            hardware interrupts.                                    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  UHCIInterrupt                                        */
/*    LINKAGE:  CALL FAR                                              */
/*                                                                    */
/* INPUT:  NONE                                                       */
/*                                                                    */
/* EXIT-NORMAL:  NC - my device generated interrupt (claim int.)      */
/*                                                                    */
/* EXIT-ERROR:  CY - my device did not generate interrupt             */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void FAR UHCIInterrupt()
{
   USHORT   usbStatus, port1Status, port2Status;

   inp16(gusbStsReg, usbStatus);

   inp16(gusbPortSc1Reg, port1Status);
   inp16(gusbPortSc2Reg, port2Status);

#ifdef   DEBUG
   dsPrint3( DBG_DETAILED, "UHCI: UHCIInterrupt Status=%x,Port1=%x, Port2=%x\r\n",
             usbStatus, port1Status, port2Status );
#endif

   if(!(usbStatus&(UHCI_USBSTS_HCPE|UHCI_USBSTS_HSE|UHCI_USBSTS_RDE|UHCI_USBSTS_USBEI|UHCI_USBSTS_USBI)))
   {  // this is not interrupt caused by USB host
      STC();   // set carry flag to indicate that it is not our interrupt
      return;
   }
   
   // save interrupt flags that need special processing (hardware error, processing error, resume detect)
   if(usbStatus&UHCI_USBSTS_HCPE)
      gInterruptFlags|=UHCI_USBSTS_HCPE;
   if(usbStatus&UHCI_USBSTS_HSE)
      gInterruptFlags|=UHCI_USBSTS_HSE;
   if(usbStatus&UHCI_USBSTS_RDE)
      gInterruptFlags|=UHCI_USBSTS_RDE;

   DevHelp_ArmCtxHook( 0, gCTXHookHandle );  // aquire task time to process completed I/O requests

   // clear interrupt flags
   usbStatus|=UHCI_USBSTS_HCPE|UHCI_USBSTS_HSE|UHCI_USBSTS_RDE|UHCI_USBSTS_USBEI|UHCI_USBSTS_USBI;
   outp16(gusbStsReg, usbStatus);

   CLC();                                 /* Clear the carry flag to indicate */
                                          /* to kernel that we have claimed   */
                                          /* (serviced) the interrupt.        */
   DevHelp_EOI( gusbIRQ );                /* issue end of interrupt */

}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  DefAddrTimeOut                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Default address request timed out               */
/*                                                                    */
/*                                                                    */
/* FUNCTION:  The function of this routine is to mark I/O request     */
/*            to device with default address (address 0) failed       */
/*            with time out flag on and initiate schedule processing  */
/*            thread.                                                 */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Timer interrupt time                                      */
/*                                                                    */
/* ENTRY POINT:  DefAddrTimeOut                                       */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  NONE                                                       */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS: This routine arms host schedule processing thread         */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void DefAddrTimeOut(void)
{
   TD       *currTD;

#ifdef   DEBUG
   dsPrint( DBG_CRITICAL, "UHCI: DefAddrTimeOut\r\n" );
#endif

   if(!g0QH)
      return;

   for(currTD=(TD *)g0QH->qeLinkVirt; currTD; currTD=currTD->virtLP)
   {
      currTD->ctrlStat&=~TD_CTRLSTAT_STATUS_ACTIVE;   // mark inactive (completed)
      currTD->ctrlStat|=TD_CTRLSTAT_STATUS_CRC;       // set timed out flag
   }
   g0QH=NULL;

   DevHelp_ArmCtxHook( 0, gCTXHookHandle );  // aquire task time to process completed I/O requests
}

void CheckQH(void)
{
   USHORT   tdIndex;
   QH       *qh;
   BOOL     noFinishedTDs=TRUE;

   for(tdIndex=0; tdIndex<gTDCount; tdIndex++)
   {
      qh=(QH *)(gTDListStart+tdIndex);
      if(qh->qhLink==UHCI_LP_UNUSED)
         continue;
      if(qh->elementType==ELEMENT_TYPE_QH_BOT)
      {
         if(TDFinished((TD *)qh->qeLinkVirt, qh->qeLink))
         {  // process completed request: 1) detach QH; 2) process TD's
            DetachQH(qh);
            FinshIO((TD *)qh->qeLinkVirt,FALSE);
            qh->qhLink=UHCI_LP_UNUSED; // release QH
            qh->elementType=0;
            noFinishedTDs=FALSE;
            if(qh==g0QH)   // default address request ended
               g0QH=NULL;
         }
      }
   }
#ifdef   DEBUG
   if(noFinishedTDs)
      dsPrint(DBG_DETAILED, "UHCI: CheckQ - no finished requests\r\n");
#endif
}

static BOOL TDFinished(TD *tdList, ULONG currHeadElement)
{
   TD             *currTD;
   ULONG          currLen, maxLen;

   if(!tdList)
      return(FALSE);

   currHeadElement&=UHCI_LP_MASK;
   if(!currHeadElement) // all TD's processed
      return(TRUE);
   // check all the request chain for completion
   for(currTD=tdList; currTD; currTD=currTD->virtLP)
   {
      if(currHeadElement!=currTD->phyTDAddr)
         continue;
      if(!(currTD->ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE))  // completed
      {
         if(currTD->ctrlStat&TD_CTRLSTAT_SPD && (currTD->token&TD_TOKEN_PID_MASK)==TD_TOKEN_PID_IN)
         {
            currLen=currTD->ctrlStat&TD_CTRLSTAT_ACTLEN_MASK;
            maxLen=currTD->token>>21;
#ifdef   DEBUG
      dsPrint4(DBG_DETAILED, "UHCI: CheckQ - currlen=%lx, maxLen=%lx, chead=%lx, tdAdd=%lx\r\n",
               currLen,maxLen,currHeadElement,currTD->phyTDAddr);
      dsPrint3(DBG_DETAILED, "UHCI: CheckQ - currTD=%lx, ctrlStat=%lx, token=%lx\r\n",
               (USHORT)currTD,currTD->ctrlStat,currTD->token);
#endif
            if(currLen<=maxLen && maxLen!=USB_ZERO_PACKET)   // processed
               currTD=NULL;
         }
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_ERRMASK)  // finished with error
            currTD=NULL;
      }
      else
      {
         if(FALSE && (currTD->token&UHCI_LP_T) && ((currTD->token&TD_TOKEN_PID_MASK)==TD_TOKEN_PID_IN))
         {
            currLen=currTD->ctrlStat&TD_CTRLSTAT_ACTLEN_MASK;
            maxLen=currTD->token>>21;
         #ifdef   DEBUGx
            dsPrint4(DBG_DETAILED, "UHCI: CheckQ(a) - currlen=%lx, maxLen=%lx, chead=%lx, tdAdd=%lx\r\n",
                  currLen,maxLen,currHeadElement,currTD->phyTDAddr);
            dsPrint3(DBG_DETAILED, "UHCI: CheckQ(a) - currTD=%lx, ctrlStat=%lx, token=%lx\r\n",
                  (USHORT)currTD,currTD->ctrlStat,currTD->token);
         #endif
            if(currLen==maxLen)   // processed
               currTD=NULL;
         }
      }
      break;
   }

   return(currTD==NULL);
}

BOOL FinshIO(TD *tdList, BOOL cancelled)
{
   TD             *currTD, *nextTD;
   RP_GENIOCTL    rpIRQ;
   USBRB          irqRB;
   USHORT         packetSize;
   BOOL           activeTD;
   BOOL           dataToggle=FALSE;

   if(!tdList)
      return(FALSE);

   //  fill in request block
   irqRB.controllerId=ghcdID;
   irqRB.deviceAddress=(UCHAR)((tdList->token&TD_TOKEN_DEVADDRESS_MASK)>>8); // use default address to read data from unconfigured devices
   irqRB.endPointId=(UCHAR)((tdList->token&TD_TOKEN_ENDPOINT_MASK)>>15);    // 0, default control endpoint
   
   irqRB.status=0;        // request status flags

   activeTD= (tdList->ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE)!=0;

   irqRB.flags=0;
   if(tdList->ctrlStat&TD_CTRLSTAT_LSD)
      irqRB.flags|=USRB_FLAGS_DET_LOWSPEED;
   if(tdList->ctrlStat&TD_CTRLSTAT_IOS)
      irqRB.flags|=USRB_FLAGS_DET_ISOHR;

   if((tdList->token&TD_TOKEN_PID_MASK)==TD_TOKEN_PID_IN)
      irqRB.flags|=USRB_FLAGS_TTYPE_IN;
   if((tdList->token&TD_TOKEN_PID_MASK)==TD_TOKEN_PID_OUT)
      irqRB.flags|=USRB_FLAGS_TTYPE_OUT;
   if((tdList->token&TD_TOKEN_PID_MASK)==TD_TOKEN_PID_SETUP)
      irqRB.flags|=USRB_FLAGS_TTYPE_SETUP;

   irqRB.buffer1=(PUCHAR)tdList->virtBufferPointer;       // Virtual address of data buffer
   irqRB.buffer1Length=0; // Buffer length in bytes
   irqRB.buffer2=NULL;       // Virtual address of second data buffer.
   irqRB.buffer2Length=0; // Buffer length in bytes
   irqRB.serviceTime=0;   // Required service frequency for isohronous requests in ms. Valid range - [1,1024].
   irqRB.maxPacketSize=0;   // maximum packet size to be used for specified endpoint
   irqRB.maxErrorCount=(UCHAR)((tdList->ctrlStat&TD_CTRLSTAT_ERRCNT_MASK)>>27);
   irqRB.usbIDC=(PUSBIDCEntry)tdList->usbIDC;        // Address of IRQ processing routine to be called for this request
   irqRB.usbDS=tdList->usbDS; // ; set data segment value
   irqRB.category=tdList->category; // I/O issuer category
   irqRB.requestData1=tdList->requestData1;  // data to be stored within request
   irqRB.requestData2=tdList->requestData2;  //  data to be stored within request
   irqRB.requestData3=tdList->requestData3;  //  data to be stored within request

   for(currTD=tdList; currTD; currTD=nextTD)
   {
#ifdef   DEBUG
if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_ERRMASK)
{
      dsPrint4(DBG_CRITICAL, "UHCI: IRQ TD=%x,r1=%lx,r2=%lx,r3=%lx",(USHORT)currTD,
               currTD->requestData1, currTD->requestData2, currTD->requestData3);
      dsPrint4(DBG_CRITICAL,",a=%d,p=%d,stat=%lx,token=%lx\r\n",
               irqRB.deviceAddress, irqRB.endPointId, currTD->ctrlStat, currTD->token);
}
#endif
      if(!(currTD->ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE))
      {
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_STALLED)
            irqRB.status|=USRB_STATUS_STALLED;
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_DBUFERR)
            irqRB.status|=USRB_STATUS_BUFFERR;
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_BABBLE)
            irqRB.status|=USRB_STATUS_BABBLE;
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_NAK)
            irqRB.status|=USRB_STATUS_NAK;
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_CRC)
            irqRB.status|=USRB_STATUS_CRC;
         if(currTD->ctrlStat&TD_CTRLSTAT_STATUS_BITSTUFF)
            irqRB.status|=USRB_STATUS_BITSTUFF;
      }

      if(!(currTD->ctrlStat&TD_CTRLSTAT_STATUS_ERRMASK))
      {
         dataToggle=(currTD->token&TD_TOKEN_DATA_TOGGLE)!=0;
      }

      packetSize=(USHORT)(currTD->ctrlStat&TD_CTRLSTAT_ACTLEN_MASK);
      if(packetSize!=USB_ZERO_PACKET)
      {
         packetSize++;
         if(packetSize>irqRB.maxPacketSize)
            irqRB.maxPacketSize=packetSize;
      }
      else
         packetSize=0;

      if(irqRB.flags&USRB_FLAGS_TTYPE_SETUP && (currTD->token&TD_TOKEN_PID_MASK)!=TD_TOKEN_PID_SETUP)
      {
         if(!irqRB.buffer2)
            irqRB.buffer2=(PUCHAR)currTD->virtBufferPointer;
         irqRB.buffer2Length+=packetSize;
      }
      else
         irqRB.buffer1Length+=packetSize;

      nextTD=currTD->virtLP;
      currTD->elementType=0;
      currTD->LP=UHCI_LP_UNUSED;  // free TD
   }
   if(dataToggle)
      irqRB.flags|=USRB_FLAGS_DET_DTGGLEON;

#ifdef   DEBUG
      dsPrint1(DBG_SPECIFIC, "UHCI: IRQ datatoggle=%d\r\n",dataToggle);
#endif


   setmem((PSZ)&rpIRQ, 0, sizeof(rpIRQ));
   rpIRQ.rph.Cmd=CMDGenIOCTL;   // IOCTL
   if(activeTD && cancelled)
      rpIRQ.rph.Status=USB_IDC_RC_CANCELED;
   else if(irqRB.status)  // set return code
      rpIRQ.rph.Status=USB_IDC_RC_IOFAILED;
   else
      rpIRQ.rph.Status=USB_IDC_RC_OK;
   
   rpIRQ.Category=irqRB.category;
   rpIRQ.Function=USB_IDC_FUNCTION_PRCIRQ;
   rpIRQ.ParmPacket=(PVOID)&irqRB;
   USBCallIDC( irqRB.usbIDC, irqRB.usbDS, (RP_GENIOCTL FAR *)&rpIRQ );

   return(TRUE);
}

