/* SCCSID = "src/dev/usb/UHCI/UHCACCIO.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:  UHCACCIO.C                                            */
/*                                                                            */
/*   DESCRIPTIVE NAME:  I/O request accepting routines.                       */
/*                                                                            */
/*   FUNCTION: These routines accepts I/O request for execution:              */
/*             1) root hub request processing are based on driver's internal  */
/*                data and Host Adapter I/O register data;                    */
/*             2) non-root hub requests are added to schedule for processing  */
/*                by Host Adapter;                                            */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             UHCIAcceptIO                                                   */
/*             FreeTDsQH                                                      */
/*             DetachQH                                                       */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/01/31  MB                                                      */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#pragma optimize("ceglt", on)

#include        "uhci.h"

static TD *CreateTDList( USBRB FAR *ioRB, PULONG packetTime );
static USHORT AddToSchedule( QH *topQH, TD *firstTD, ULONG packetTime, BOOL defAddress );
static USHORT AddIsohronous( QH *topQH, TD *firstTD, ULONG packetTime, BOOL absFrameIndex, USHORT isohronousDelay );  // 21/08/98 MB
static BOOL AddToControlQueue( QH *topQH, TD *firstTD );
static TD *AllocateTD( UCHAR tdType, BOOL disableInterrupts );
static QH *GetTopLevelQH(USHORT serviceTime, BOOL lowSpeedDevice, USHORT requestType, USHORT maxPacketSize);
static QH *GetStructQH( QH *topQH, BOOL lowSpeedDevice, USHORT requestType, USHORT maxPacketSize);
static QH *GetTopQHInfo(USHORT frameIndex, PULONG FAR *schAddr);

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIAcceptIO                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Accept I/O PDD-PDD IDC worker routine           */
/*                                                                    */
/* FUNCTION:  This routine:                                           */
/*            1) passes all root hub requests to UHCIRootHub;         */
/*            2) fills in TD chain;                                   */
/*            3) searches for top level QH satisfying service timing; */
/*            4) adds TD chain to schedule;                           */
/*            5) for isohronous requests USB request block service    */
/*               time field is set to current frame index;            */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  UHCIAcceptIO                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*           USB_IDC_RC_IOFAILED - 1) host not reset;                 */
/*                                 2) host is not running.            */
/*           USB_IDC_RC_ALLOCERR - failed to allocate QH/TD chain     */
/*           USB_IDC_RC_NOBANDWIDTH - not enough bandwidth to schedule*/
/*                                 current request                    */
/*                                                                    */
/* INTERNAL REFERENCES:  AccNonIsoReq                                 */
/*    ROUTINES:          AccRootHubReq                                */
/*                       CreateTDList                                 */
/*                       GetTopLevelQH                                */
/*                       AddToSchedule                                */
/*                       FreeTDsQH                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
void UHCIAcceptIO( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   USHORT            cmdRegister;
   USHORT            reqQueingType;
   BOOL              interruptStatus;
   USHORT            flags;

   if (!gHostReset)
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_IOFAILED;
      return;
   }

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

   // process root hub request
   AccRootHubReq( pRP_GENIOCTL );

   if (pRP_GENIOCTL->rph.Status&STATUS_DONE)  // processed by root hub
   {
#ifdef   DEBUG         
      dsPrint4(DBG_DETAILED,"UHCI: UHCIAcceptIO (R) a=%d, e=%d,f=%x,i=%d, ",
               ioRB->deviceAddress, ioRB->endPointId, ioRB->flags, ioRB->serviceTime);
      dsPrint2(DBG_DETAILED,"l1=%d, l2=%d\r\n",
               ioRB->buffer1Length, ioRB->buffer2Length);
#endif
      return;
   }

   inp16(gusbCmdReg, cmdRegister);  // if host not running reject I/O request
   if (!(cmdRegister&UHCI_USBCMD_RS))
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_IOFAILED;
      return;
   }

   pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;

   reqQueingType=ioRB->flags&USRB_FLAGS_DETT_MASK;

   // set flags to prevent interrupt enabling on return
   interruptStatus=gInterruptsDisabled;
   _asm
   {
      pushf
      pop   flags
   }
   if (!gInterruptsDisabled && !(flags&EFLAGS_IF))
   {
      gInterruptsDisabled=TRUE;
   }

   // process I/O request
   switch (reqQueingType)
   {
   case USRB_FLAGS_DET_ISOHR: // process isohronous request
      AccIsoReq( pRP_GENIOCTL );
      break;
   default: // process non-isohronous request
      AccNonIsoReq( pRP_GENIOCTL );
      break;
   }

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {
#ifdef   DEBUG
      dsPrint3(DBG_CRITICAL,"UHCI: UHCIAcceptIO failed. a=%d,e=%d, Status=%x\r\n",
               ioRB->deviceAddress, ioRB->endPointId, pRP_GENIOCTL->rph.Status);
#endif
   }

   if (!interruptStatus && gInterruptsDisabled)
   {
      gInterruptsDisabled=FALSE;
   }
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AccNonIsoReq                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Accept I/O PDD-PDD IDC worker routine           */
/*                                                                    */
/* FUNCTION:  This routine:                                           */
/*            1) passes all root hub requests to UHCIRootHub;         */
/*            2) fills in TD chain;                                   */
/*            3) searches for top level QH satisfying service timing; */
/*            4) adds TD chain to schedule;                           */
/*            5) for isohronous requests USB request block service    */
/*               time field is set to current frame index;            */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AccNonIsoReq                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*           USB_IDC_RC_IOFAILED - 1) host not reset;                 */
/*                                 2) hosr is not running.            */
/*           USB_IDC_RC_ALLOCERR - failed to allocate QH/TD chain     */
/*           USB_IDC_RC_NOBANDWIDTH - not enough bandwidth to schedule*/
/*                                 current request                    */
/*                                                                    */
/* INTERNAL REFERENCES:  AccRootHubReq                                */
/*    ROUTINES:          CreateTDList                                 */
/*                       GetTopLevelQH                                */
/*                       AddToSchedule                                */
/*                       FreeTDsQH                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
void AccNonIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   TD                *firstTD=NULL;
   QH                *topQH;
   BOOL              lowSpeedDevice;
   USHORT            reqQueingType;
   ULONG             packetTime;

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

   reqQueingType=ioRB->flags&USRB_FLAGS_DETT_MASK;

   lowSpeedDevice= (ioRB->flags&USRB_FLAGS_DET_LOWSPEED)!=0;

   firstTD=CreateTDList( ioRB, &packetTime );   // create TD list and calculate transfer time

#ifdef   DEBUG         
   dsPrint4(DBG_DETAILED,"UHCI: UHCIAcceptIO a=%d, e=%d,f=%x,i=%d, ",
            ioRB->deviceAddress, ioRB->endPointId, ioRB->flags, ioRB->serviceTime);
   dsPrint4(DBG_DETAILED,"l1=%d, l2=%d, bw=%ld,td=%x\r\n",
            ioRB->buffer1Length, ioRB->buffer2Length, packetTime, (USHORT)firstTD);
#endif

   if (!firstTD)
   {  // failed to create complete TD chain
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ALLOCERR;
   }
   else
   {
      // get top level QH address to satisfy request timing requirements and request type
      if (!gInterruptsDisabled)
         CLI();
      topQH=GetTopLevelQH(ioRB->serviceTime, lowSpeedDevice, ioRB->flags, ioRB->maxPacketSize);
      if (!topQH)
      {  // can't find/allocate top level QH
         pRP_GENIOCTL->rph.Status=USB_IDC_RC_ALLOCERR;
      }
      else
      {
         // add TD list to schedule
         switch (reqQueingType)
         {
         case USRB_FLAGS_DET_BULK:
         case USRB_FLAGS_DET_INTRPT:   // bulk & interrupt request
            pRP_GENIOCTL->rph.Status=AddToSchedule( topQH, firstTD, packetTime, ioRB->deviceAddress==0 );
            break;
         case USRB_FLAGS_DET_ISOHR: // must be processed by special routine without TD creation
            pRP_GENIOCTL->rph.Status=USB_IDC_RC_PARMERR;
            break;
         default: // control request - add to existing request QH or create new QH
            if (AddToControlQueue( topQH, firstTD ))
               pRP_GENIOCTL->rph.Status=AddToSchedule( topQH, firstTD, packetTime, ioRB->deviceAddress==0 );
            break;
         }
      }
      if (!gInterruptsDisabled)
         STI();
#ifdef   DEBUG         
      dsPrint1(DBG_DETAILED,"UHCI: UHCIAcceptIO topqh=%x\r\n", (USHORT)topQH);
#endif
   }

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {  // free TD/QH if failed to process
      FreeTDsQH(firstTD, TRUE);
   }
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CreateTDList                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Creates TD list for given I/O request           */
/*                                                                    */
/* FUNCTION:  This routine fills in TD chain.                         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  CreateTDList                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  USBRB FAR    *ioRB                                         */
/*         PULONG       packetTime                                    */
/*                                                                    */
/* EXIT-NORMAL:  pointer to created TD list                           */
/*                                                                    */
/* EXIT-ERROR:  NULL                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  AllocateTD                                   */
/*    ROUTINES:          PacketTTime                                  */
/*                       FreeTDs                                      */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static TD *CreateTDList( USBRB FAR *ioRB, PULONG packetTime )
{
   TD                *firstTD=NULL, *lastTD=NULL, *newTD;
   USHORT            buffLen, currBuffLen, maxPacketSize, maxBuffer, sizeForCalc;
   LONG              length, errCount, phyBuff;
   PUCHAR            currBuffAddr;
   UCHAR             maxPasses, passIndex;
   SetupPacket FAR   *setupPacket;
   BOOL              lowSpeedDevice;
   BOOL              failed=FALSE, dataToggle;
   USHORT            reqQueingType;
   PUCHAR            buffAddrs[3];
   USHORT            packetSizes[3], bufferLength[3];
   ULONG             packetType[3];
   ULONG             currPTime, token;

   *packetTime=0; // packet transmission time (ns)

   lowSpeedDevice= (ioRB->flags&USRB_FLAGS_DET_LOWSPEED)!=0;

   reqQueingType=ioRB->flags&USRB_FLAGS_DETT_MASK;

   // set initial data toggle value based on request type
   switch (reqQueingType)
   {
   case USRB_FLAGS_DET_BULK:
   case USRB_FLAGS_DET_INTRPT:
      dataToggle=(ioRB->flags&USRB_FLAGS_DET_DTGGLEON)==0;
      break;
   case USRB_FLAGS_DET_ISOHR:
      dataToggle=FALSE;
      break;
   default: // control request
      dataToggle=TRUE;
      break;
   }

   // set buffer adresses to create TD chain, add extra for setup packets to complete data toggle
   maxPasses=1;
   buffAddrs[0]=ioRB->buffer1;
   packetSizes[0]=ioRB->buffer1Length;
   bufferLength[0]=ioRB->buffer1Length;
   switch (ioRB->flags&~USRB_FLAGS_TTYPE_MASK)
   {
   case USRB_FLAGS_TTYPE_IN:
      packetType[0]=TD_TOKEN_PID_IN;
      break;
   case USRB_FLAGS_TTYPE_OUT:
      packetType[0]=TD_TOKEN_PID_OUT;
      break;
   case USRB_FLAGS_TTYPE_SETUP:
      packetType[0]=TD_TOKEN_PID_SETUP;
      break;
   default: // wrong request type
#ifdef   DEBUG         
      dsPrint1(DBG_CRITICAL,"UHCI: UHCIAcceptIO wrong request type rbFlags=%x\r\n",
               (LONG)(ioRB->flags&~USRB_FLAGS_TTYPE_MASK));
#endif
      return (NULL);
   }
   if (ioRB->flags&USRB_FLAGS_TTYPE_SETUP)
   {
      setupPacket=(SetupPacket FAR *)ioRB->buffer1;
      if (ioRB->buffer2Length>0 && ioRB->buffer2)
      {
         maxPasses=3;
         buffAddrs[1]=ioRB->buffer2;   // data phase packet
         packetSizes[1]=ioRB->buffer2Length;
         bufferLength[1]=ioRB->buffer2Length;
         if (setupPacket->bmRequestType&REQTYPE_XFERDIR_DEVTOHOST)
            packetType[1]=TD_TOKEN_PID_IN;
         else
            packetType[1]=TD_TOKEN_PID_OUT;
         buffAddrs[2]=ioRB->buffer1;   // extra data toggle packet
         packetSizes[2]=USB_ZERO_PACKET+1;
         bufferLength[2]=USB_ZERO_PACKET+1;
         if (setupPacket->bmRequestType&REQTYPE_XFERDIR_DEVTOHOST)
            packetType[2]=TD_TOKEN_PID_OUT;
         else
            packetType[2]=TD_TOKEN_PID_IN;
      }
      else
      {
         maxPasses=2;   // extra data toggle packet
         buffAddrs[1]=ioRB->buffer1;
         packetSizes[1]=USB_ZERO_PACKET+1;
         bufferLength[1]=USB_ZERO_PACKET+1;
         packetType[1]=TD_TOKEN_PID_IN;
      }
      if ((ioRB->flags&USRB_FLAGS_DET_AEPKT))
         maxPasses--;
   }

#ifdef   DEBUG
   for (passIndex=0; passIndex<maxPasses; passIndex++)
   {
      dsPrint4(DBG_DETAILED,"UHCI: UHCIAcceptIO buff=%lx, bufl=%d, psize=%d,token=%x\r\n",
               (LONG)buffAddrs[passIndex], packetSizes[passIndex], bufferLength[passIndex], packetType[passIndex]);
   }
#endif

   // create all request TDs
   for (passIndex=0; passIndex<maxPasses && !failed; passIndex++)
   {
      currBuffAddr=buffAddrs[passIndex];
      maxPacketSize=packetSizes[passIndex];
      maxBuffer=bufferLength[passIndex];
      token=packetType[passIndex];
      if (ioRB->maxPacketSize && maxPacketSize!=USB_ZERO_PACKET+1)
         maxPacketSize=ioRB->maxPacketSize;

      // calculate packet transfer time
      sizeForCalc= maxPacketSize<maxBuffer ? maxPacketSize : maxBuffer;
      currPTime=PacketTTime(sizeForCalc, lowSpeedDevice, token, reqQueingType);
      if (*packetTime<currPTime)
         *packetTime=currPTime;

      // build TD chain
      for (buffLen=maxBuffer; buffLen>0; buffLen-=currBuffLen)
      {
         if (buffLen>=maxPacketSize)
            currBuffLen=maxPacketSize;
         else
            currBuffLen=buffLen;

         newTD=AllocateTD(ELEMENT_TYPE_TD, TRUE);
         if (!newTD)
         {
            failed=TRUE;
            break;
         }
         newTD->elementType=ELEMENT_TYPE_TD;

         if (!(ioRB->flags&USRB_FLAGS_DET_ISOHR))   //  isohronous request always uses data toggle value of 0
            dataToggle=!dataToggle;

         // set TD status field
         newTD->ctrlStat=TD_CTRLSTAT_STATUS_ACTIVE;
         if (lowSpeedDevice)
            newTD->ctrlStat|=TD_CTRLSTAT_LSD;
         errCount=ioRB->maxErrorCount; errCount= errCount<<27;
         newTD->ctrlStat|=errCount&TD_CTRLSTAT_ERRCNT_MASK;
         newTD->ctrlStat|=USB_ZERO_PACKET;  // set transfered byte count to nothing transfered
         if (reqQueingType==USRB_FLAGS_DET_ISOHR)   // 25/08/98 MB
            newTD->ctrlStat|=TD_CTRLSTAT_IOS;

         // set TD token field
         length=currBuffLen-1;
         newTD->token=length<<21;   // maxlength
         newTD->token|=CreateTokenAddress( ioRB->deviceAddress, ioRB->endPointId );
         newTD->token|=token; //  set TD request token type
         if (dataToggle || length==USB_ZERO_PACKET)
            newTD->token|=TD_TOKEN_DATA_TOGGLE;

         newTD->deviceAddress=ioRB->deviceAddress;
         newTD->endPointId=ioRB->endPointId;

         if ((newTD->token&TD_TOKEN_PID_MASK)==TD_TOKEN_PID_IN)
            newTD->ctrlStat|=TD_CTRLSTAT_SPD;

         newTD->usbIDC=ioRB->usbIDC;  // Address of IRQ processing routine to be called for this request
         newTD->usbDS=ioRB->usbDS;    // DS value for IRQ processing routine
         newTD->category=ioRB->category;  // save callers category ID
         newTD->requestData1=ioRB->requestData1;  // data to be stored within request
         newTD->requestData2=ioRB->requestData2;  // data to be stored within request
         newTD->requestData3=ioRB->requestData3;  // data to be stored within request

         // convert buffer's virtual address to physical address
         DevHelp_VirtToPhys(currBuffAddr, (PULONG)&phyBuff);

         newTD->bufferPointer=phyBuff;
         newTD->virtBufferPointer=currBuffAddr;
         currBuffAddr+=currBuffLen;

         // set link fields to add new TD to chain
         newTD->LP=UHCI_LP_T; // last TD flag            
         newTD->virtLP=NULL;  // last TD flag
         if (lastTD)
         {
            lastTD->virtLP=newTD;
            lastTD->LP=newTD->phyTDAddr;
         }
         else
         {
            firstTD=newTD;
         }
         lastTD=newTD;
      }
   }

   if (failed)
   {  // release allocated TD chain if failed to allocated complete chain
      FreeTDsQH(firstTD, TRUE);
      firstTD=NULL;
   }
   else
   {
      if (lastTD)
      {   // set interrupt complete only for the last TD in chain
         lastTD->ctrlStat|=TD_CTRLSTAT_IOC;
         lastTD->elementType=ELEMENT_TYPE_LASTD; // set last TD type
      }
   }

   return (firstTD);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  FreeTDsQH                                        */
/*                                                                    */
/* DESCRIPTIVE NAME:  Free QH/TD list                                 */
/*                                                                    */
/* FUNCTION:  This routine frees TD list or single QH.                */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  FreeTDs                                             */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  TD *firstTD - pointer to 1st TD in list or QH              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  gLastTDId is adjusted to point to last QH/TD element,    */
/*           g0QH is set to NULL if released, QH is deleted from      */
/*           active QH list (gFirstBottomQH and gLastBottomQH are     */
/*           updated if necessarry)                                   */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
VOID FreeTDsQH( TD *firstTD, BOOL disableInterrupts )
{
   TD       *currTD;
   USHORT   tdIndex;
   BOOL     bottomQH=FALSE;

   switch (firstTD->elementType)
   {
   case ELEMENT_TYPE_TD:   // free TD chain
   case ELEMENT_TYPE_LASTD:
      for (currTD=firstTD; currTD; currTD=currTD->virtLP)
      {
         currTD->elementType=0;
         currTD->LP=UHCI_LP_UNUSED;
      }
      break;
   case ELEMENT_TYPE_QH_BOT:
      bottomQH=TRUE;
   default: // free single QH
      firstTD->elementType=0;
      firstTD->LP=UHCI_LP_UNUSED;
      break;
   }

   // adjust last used TD/QH item index
   if (disableInterrupts && !gInterruptsDisabled)
      CLI();
   for (tdIndex=gLastTDId; (tdIndex>TOP_QH_COUNT) && (gTDListStart[tdIndex].LP==UHCI_LP_UNUSED); tdIndex--);
   gLastTDId=tdIndex;
   if (bottomQH)
   {
      if (g0QH==(QH *)firstTD) // reset default address request if released
         g0QH=NULL;

      // delete QH from bottm level QH list
      if (((QH *)firstTD)->prevBotQH)
         ((QH *)firstTD)->prevBotQH->nextBotQH=((QH *)firstTD)->nextBotQH;
      else
         gFirstBottomQH=((QH *)firstTD)->nextBotQH;
      if (((QH *)firstTD)->nextBotQH)
         ((QH *)firstTD)->nextBotQH->prevBotQH=((QH *)firstTD)->prevBotQH;
      else
         gLastBottomQH=((QH *)firstTD)->prevBotQH;
   }
   if (disableInterrupts && !gInterruptsDisabled)
      STI();
}
#pragma optimize("eglt", off)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AddToSchedule                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Add QH to schedule                              */
/*                                                                    */
/* FUNCTION:  This routine fills in TD chain.                         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AddToSchedule                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  QH *topQH - top QH the request to be added                 */
/*         TD *firstTD - request TD chain                             */
/*         ULONG packetTime - required bandwitdh time                 */
/*         BOOL defAddress - TRUE for default address requests        */
/*                                                                    */
/* EXIT-NORMAL:  USB_IDC_RC_OK - QH added to schedule                 */
/*                                                                    */
/* EXIT-ERROR:  USB_IDC_RC_NOBANDWIDTH - QH is not added to schedule -*/
/*              no enought bandwidth to execute request               */
/*              USB_IDC_RC_ALLOCERR - failed to allocate QH           */
/*                                                                    */
/* EFFECTS:  g0QH, g0Time varibles are set if defAddress==TRUE        */
/*                                                                    */
/* INTERNAL REFERENCES:  CheckBandwidth                               */
/*    ROUTINES:          AllocateTD                                   */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static USHORT AddToSchedule( QH *topQH, TD *firstTD, ULONG packetTime, BOOL defAddress )
{
   QH       *lastQH, *newQH;
   USHORT   rc=USB_IDC_RC_OK;

   if (!CheckBandwidth( topQH, packetTime ))
      rc=USB_IDC_RC_NOBANDWIDTH;

   if (rc==USB_IDC_RC_OK)
   {
      newQH=(QH *)AllocateTD(ELEMENT_TYPE_QH_BOT, TRUE);
      if (!newQH)
         rc=USB_IDC_RC_ALLOCERR;
   }

   if (rc==USB_IDC_RC_OK)
   {
      // element pointer always links to new TD chain
      newQH->qeLink=(firstTD->phyTDAddr&UHCI_LP_MASK);   // Queue Element link pointer (phys)
      newQH->qeLinkVirt=(QH *)firstTD;                   // Queue Element link pointer (virt)
      // queue pointer always links to next structure QH
      newQH->qhLink=topQH->qhLink;                       // Queue Head link pointer (phys)
      newQH->qhLinkVirt=NULL;                            // Queue Head link pointer (virt)
      newQH->minTime=0; // not used for bottom level QH    
      newQH->maxTime=0; // not used for bottom level QH
      newQH->elementType=ELEMENT_TYPE_QH_BOT;   // bottom level QH
      newQH->pktBandwidth=packetTime;           // time required to execute TD
      newQH->parentQH=NULL;

      if (topQH->qeLink&UHCI_LP_T)
      {  // add as 1st bottom level QH in list
         newQH->parentQH=topQH;
         topQH->qeLinkVirt=newQH;
         topQH->qeLink=(newQH->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
      }
      else
      {  // add as last QH chain element
         for (lastQH=topQH->qeLinkVirt; lastQH; lastQH=lastQH->qhLinkVirt)
         {
            if (!lastQH->qhLinkVirt) // last element found
            {
               newQH->parentQH=lastQH;
               lastQH->qhLinkVirt=newQH;
               lastQH->qhLink|=UHCI_LP_T;
               lastQH->qhLink=(newQH->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
               break;
            }
         }
      }

      if (defAddress && rc==USB_IDC_RC_OK)
      {  // save default address request block and reset timeout value
         g0QH=newQH;
         g0Time=DEFADDR_TIME_OUT;
      }
   }
   return (rc);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CheckBandwidth                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Check frame bandwidth                           */
/*                                                                    */
/* FUNCTION:  This routine checks schedule for available bandwidth    */
/*            to execute current request.                             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  CheckBandwidth                                      */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  QH *topQH - QH to be checked                               */
/*         ULONG packetTime - required bandwidth time                 */
/*                                                                    */
/* EXIT-NORMAL:  TRUE - QH can be added to schedule                   */
/*                                                                    */
/* EXIT-ERROR:  FALSE - QH can't be added to schedule - not enought   */
/*              bandwidth                                             */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
BOOL CheckBandwidth( QH *topQH, ULONG packetTime )
{
   QH       *qh;
   ULONG    totalPTime=gIsoPktSize; 
   BOOL     bWdthEnought;

   // calculate used bandwidth
   for (qh=gFirstBottomQH; qh; qh=qh->nextBotQH)
   {
      if (!(qh->qeLink&UHCI_LP_MASK)  )
         continue;   // ignore already completed requests
      if (qh->elementType==ELEMENT_TYPE_QH_BOT)
         totalPTime+=qh->pktBandwidth;
   }
   bWdthEnought= (totalPTime+packetTime<=MAX_FRAME_TTIME);

#ifdef   DEBUG
   if (!bWdthEnought)
      dsPrint2(DBG_CRITICAL,"UHCI: CheckBandwidth - not enought bandwidth, used %ld, extra req %ld\r\n",
               totalPTime,packetTime);
#endif

   return (bWdthEnought);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AddToControlQueue                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Add QH to control queue schedule                */
/*                                                                    */
/* FUNCTION:  This routine expands TD chain for the same device       */
/*            address, returns FALSE if no such requests.             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AddToControlQueue                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  QH *topQH - pointer to top QH satifying timing requests    */
/*         TD *firstTD - pointer to expansion TD list                 */
/*                                                                    */
/* EXIT-NORMAL:  FALSE - TD list added to existing request QH         */
/*                                                                    */
/* EXIT-ERROR:  TRUE - there are no existing TD list with the same    */
/*              device address as firstTD                             */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static BOOL AddToControlQueue( QH *topQH, TD *firstTD )
{
   BOOL     addQHtoSchedule=TRUE;
   QH       *currQH;
   TD       *controlTD, *lastTD;
   UCHAR    tdAddress;

   tdAddress=firstTD->deviceAddress;

   for (currQH=topQH->qeLinkVirt; currQH; currQH=currQH->qhLinkVirt)
   {
      if (!(controlTD=(TD *)currQH->qeLinkVirt))
         continue;
      if (tdAddress==controlTD->deviceAddress)
         break;
   }
   if (currQH)  // control request exists - expand list with curent TD chain
   {
      for (lastTD=firstTD; lastTD && lastTD->virtLP; lastTD=lastTD->virtLP);

      for (; controlTD; controlTD=controlTD->virtLP)
      {
         if (!controlTD->virtLP)
         {
            lastTD->virtLP=controlTD->virtLP;
            lastTD->LP=controlTD->LP;
            controlTD->virtLP=firstTD; // add TD to virtual address list
            controlTD->LP=firstTD->phyTDAddr;   // add TD to physical address list
            if (currQH->qeLink&UHCI_LP_T)       // change element pointer
               currQH->qeLink=firstTD->phyTDAddr&UHCI_LP_MASK; // if processing is already finished
            break;
         }
      }
      addQHtoSchedule=FALSE;
   }

   return (addQHtoSchedule);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AllocateTD                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Allocate Transfer Descriptor                    */
/*                                                                    */
/* FUNCTION:  This routine allocates single TD/QH block from TD array.*/
/*                                                                    */
/* NOTES: TD/QH blocks are allocated from prefined static data array. */
/*        This array is divided into 3 segments: top QH segment,      */
/*        bottom QH segment and 'others' segment. Last segment is     */
/*        used also as overflow area.                                 */
/*                                                                    */
/* CONTEXT: Task Time, optionally with interrupts disabled            */
/*                                                                    */
/* ENTRY POINT :  AllocateTD                                          */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:   BOOL  topLevel ; if TRUE block is allocated from the      */
/*                      first reserved TOP_QH_COUNT TD array blocks   */
/*          BOOL  disableInterrupts ; if set to TRUE routine disables */
/*                      interrupts durring TD array scanning          */
/*                                                                    */
/* EXIT-NORMAL:  pointer to allocated TD/QH block                     */
/*                                                                    */
/* EXIT-ERROR:  NULL - no free blocks available                       */
/*                                                                    */
/* EFFECTS:  gLastTDId is modified to point to last used entry in TD  */
/*           array.                                                   */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static TD *AllocateTD( UCHAR tdType, BOOL disableInterrupts )
{
   USHORT   tdIndex, tdStartIndex;
   register TD       *currTD;

   switch (tdType)
   {
   case ELEMENT_TYPE_QH_TOP:
      tdStartIndex=0;
      break;
   case ELEMENT_TYPE_QH_BOT:
      tdStartIndex=TOP_QH_COUNT;
      break;
   default:
      tdStartIndex=TOP_QH_COUNT+BOT_QH_COUNT;
      break;
   }

   if (disableInterrupts && !gInterruptsDisabled)
      CLI();
   for (tdIndex=tdStartIndex,currTD=gTDListStart+tdStartIndex; tdIndex<gTDCount; tdIndex++,currTD++)
   {
      if (currTD->LP==UHCI_LP_UNUSED)
      {
         currTD->LP=UHCI_LP_T; // mark allocated
         if (gLastTDId<tdIndex)
            gLastTDId=tdIndex;
         if (tdType==ELEMENT_TYPE_QH_BOT)
         {  // add bottom level QH to QH chain
            if (gLastBottomQH)
               gLastBottomQH->nextBotQH=(QH *)currTD;
            else
               gFirstBottomQH=(QH *)currTD;
            ((QH *)currTD)->prevBotQH=gLastBottomQH;
            ((QH *)currTD)->nextBotQH=NULL;
            gLastBottomQH=(QH *)currTD;
            gLastBottomQH->pktBandwidth=0;
         }
         if (disableInterrupts && !gInterruptsDisabled)
            STI();
#ifdef   DEBUG
         dsPrint2(DBG_SPECIFIC,"UHCI: AllocateTD TD=%lx,(%d)\r\n",(LONG)(PSZ)(gTDListStart+tdIndex),tdIndex);
#endif
         return (currTD);
      }
   }
#ifdef   DEBUG
   dsPrint(DBG_CRITICAL,"UHCI: AllocateTD failed\r\n");
#endif
   if (disableInterrupts && !gInterruptsDisabled)
      STI();
   return (NULL);
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetTopLevelQH                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get top level QH                                */
/*                                                                    */
/* FUNCTION:  This routine searches for existing top level QH or      */
/*            allocates and adds to schedule new one. Returns pointer */
/*            structure level QH.                                     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time with interrupts disabled                        */
/*                                                                    */
/* ENTRY POINT :  GetTopLevelQH                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:   USHORT serviceTime - required service time in miliseconds */
/*          BOOL lowSpeedDevice - TRUE for low speed devices          */
/*          USHORT requestType - request queue type (isohronous,      */
/*                               interrupt, control, bulk)            */
/*          USHORT maxPacketSize - maximum packet size for            */
/*                                 this request                       */
/*                                                                    */
/* EXIT-NORMAL:  pointer to structure QH block                        */
/*                                                                    */
/* EXIT-ERROR:  NULL - no free blocks available to allocate new QHs   */
/*                                                                    */
/* EFFECTS:  incorporates new QH structure in host schedule           */
/*                                                                    */
/* INTERNAL REFERENCES:  GetStructQH                                  */
/*    ROUTINES:          FreeTDsQH                                    */        
/*                       GetTopQHInfo                                 */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#define  QH_ALLOC_COUNT_MAX    7
#define  QH_ALLOC_COUNT_MIN    4

static QH *GetTopLevelQH(USHORT serviceTime, BOOL lowSpeedDevice, USHORT requestType, USHORT maxPacketSize)
{
   USHORT   tdIndex;
   QH       *qhTop, *prevQH, *currQH;
   QH       *newQH[QH_ALLOC_COUNT_MAX];
   USHORT   schStep, frameIndex, minTime, maxTime;
   USHORT   qhIndex;
   BOOL     allocOK=TRUE;
   PULONG   schEntryAddr;

   if (serviceTime==0)   // treat 0 as minimal service interval - 1ms
      serviceTime=1;
   // check for existing top level QH with required timing parameters
   for (tdIndex=0; tdIndex<=gLastTDId; tdIndex++)
   {
      if (gTDListStart[tdIndex].LP==UHCI_LP_UNUSED)
         continue;
      if (gTDListStart[tdIndex].elementType!=ELEMENT_TYPE_QH_TOP)
         continue;
      qhTop=(QH *)(gTDListStart+tdIndex);
      if (qhTop->maxTime<serviceTime)
         continue;
      if (qhTop->minTime<serviceTime)
      {
         return (GetStructQH( qhTop, lowSpeedDevice, requestType, maxPacketSize));
      }
   }

   // top level QH not exists, create new one

   // get required QH service interval
   for (schStep=1, maxTime=1, minTime=0; schStep<=TOP_QH_COUNT; schStep=schStep++,maxTime=maxTime<<1)
   {
      if (serviceTime<=maxTime)
         break;
      minTime=maxTime;
   }
   if (schStep>TOP_QH_COUNT)
   {  // invalid servicing interval
      return (NULL);
   }

   // allocate new top level QH and 6 structure QHs
   for (qhIndex=0; qhIndex<QH_ALLOC_COUNT_MAX; qhIndex++)
   {
      if ((maxTime!=1 && qhIndex<QH_ALLOC_COUNT_MIN) || maxTime==1)
      {  // allocate reclaim QHs only for minimal service time top QH
         if (qhIndex)
            newQH[qhIndex]=(QH *)AllocateTD(ELEMENT_TYPE_QH_STRUC, FALSE);
         else
            newQH[qhIndex]=(QH *)AllocateTD(ELEMENT_TYPE_QH_TOP, FALSE);
         allocOK= newQH[qhIndex]!=NULL;
      }
      else
         newQH[qhIndex]=NULL;
   }
   if (!allocOK)
   {  // if failed to allocate - release allocated entries and return NULL
      for (qhIndex=0; qhIndex<QH_ALLOC_COUNT_MAX; qhIndex++)
      {
         if (newQH[qhIndex])
            FreeTDsQH((TD *)newQH[qhIndex], FALSE);
      }
      return (NULL);
   }

   // initialize QHs
   for (qhIndex=0; qhIndex<QH_ALLOC_COUNT_MAX; qhIndex++)
   {
      if (!newQH[qhIndex])
         continue;
      newQH[qhIndex]->qhLink=UHCI_LP_Q|UHCI_LP_T;  // Queue Head link pointer
      newQH[qhIndex]->qeLink=UHCI_LP_Q|UHCI_LP_T;  // Queue Element link pointer
      newQH[qhIndex]->qhLinkVirt=NULL;             // Queue Head link pointer
      newQH[qhIndex]->qeLinkVirt=NULL;             // Queue Element link pointer
      if (qhIndex)
      {
         newQH[qhIndex]->minTime=0; // not used for structure QHs
         newQH[qhIndex]->maxTime=0; // not used for structure QHs
         newQH[qhIndex]->elementType=ELEMENT_TYPE_QH_STRUC;
      }
      else
      {
         newQH[qhIndex]->minTime=minTime; // top level QH serves request if minTime<serviceTime<=maxTime
         newQH[qhIndex]->maxTime=maxTime; // top level QH serves request if minTime<serviceTime<=maxTime
         newQH[qhIndex]->elementType=ELEMENT_TYPE_QH_TOP;
      }
      newQH[qhIndex]->parentQH=NULL;   // parent QH address is always NULL for top level QHs for except reclamation QHs
   }

   newQH[0]->qeLink=newQH[1]->phyTDAddr|UHCI_LP_Q;  // Queue Element link pointer
   newQH[0]->qeLinkVirt=newQH[1];             // Queue Element link pointer
   newQH[0]->intrptQH=newQH[1];        // (24) interrupt QH virtual address
   newQH[0]->controlQH=newQH[2];       // (26) controll QH virtual address
   newQH[0]->bulkQH=newQH[3];          // (28) bulk QH virtual address
   newQH[0]->rIntrptQH=newQH[4];       // (30) reclamation interrupt QH virtual address
   newQH[0]->rControlQH=newQH[5];      // (32) reclamation control QH virtual address
   newQH[0]->rBulkQH=newQH[6];         // (34) reclamation bulk QH virtual address
   newQH[1]->qhLink=newQH[2]->phyTDAddr|UHCI_LP_Q;  // Queue Element link pointer
   newQH[1]->qhLinkVirt=newQH[2];             // Queue Element link pointer
   newQH[2]->qhLink=newQH[3]->phyTDAddr|UHCI_LP_Q;  // Queue Element link pointer
   newQH[2]->qhLinkVirt=newQH[3];             // Queue Element link pointer
   if (newQH[4])
   {  // reclamation links (used only for top QH with service interval of 1 ms)
      newQH[3]->qhLink=newQH[4]->phyTDAddr|UHCI_LP_Q;  // Queue Element link pointer
      newQH[3]->qhLinkVirt=newQH[4];             // Queue Element link pointer
      newQH[4]->qhLinkVirt=newQH[5];             // Queue Element link pointer
      newQH[4]->qhLink=newQH[5]->phyTDAddr|UHCI_LP_Q;  // Queue Element link pointer
      newQH[4]->parentQH=newQH[0];  // used only to serve reclaim QHs
      newQH[5]->qhLink=newQH[6]->phyTDAddr|UHCI_LP_Q;  // Queue Element link pointer
      newQH[5]->qhLinkVirt=newQH[6];             // Queue Element link pointer
      newQH[5]->parentQH=newQH[0];  // used only to serve reclaim QHs
      newQH[6]->parentQH=newQH[0];  // used only to serve reclaim QHs
//      newQH[6]->qhLink=newQH[4]->phyTDAddr|UHCI_LP_Q;  // reclaim back link
//      newQH[6]->qhLinkVirt=newQH[4];             //  reclaim back link
   }

   qhTop=newQH[0];

   // add top level queue head to schedule
   schStep=minTime;
   if (!schStep)
      schStep=1;

   for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex+=schStep)
   {
      currQH=GetTopQHInfo(frameIndex, &schEntryAddr);
      if (!currQH)
      {  // add QH to empty schedule entry
         *schEntryAddr=(qhTop->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
      }
      else
      {  // add QH into existing QH list keeping it sorted by service time
         prevQH=NULL;
         for (; currQH; currQH=currQH->qhLinkVirt)
         {
            if (currQH==qhTop) // already in queue
               break;
            if (currQH->maxTime<=qhTop->minTime) //  QHs with max servicing time are 1st in list
            {
               qhTop->bulkQH->qhLinkVirt=currQH;
               qhTop->bulkQH->qhLink=(currQH->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
               qhTop->qhLinkVirt=currQH;
               qhTop->qhLink=(currQH->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
               if (!prevQH)
               {  // add as first QH
                  *schEntryAddr=(qhTop->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
               }
               else
               {  // insert in the middle of queue
                  prevQH->qhLinkVirt=qhTop;
                  prevQH->qhLink|=UHCI_LP_T;
                  prevQH->qhLink=(qhTop->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
                  prevQH->bulkQH->qhLinkVirt=qhTop;
                  prevQH->bulkQH->qhLink|=UHCI_LP_T;
                  prevQH->bulkQH->qhLink=(qhTop->phyTDAddr&UHCI_LP_MASK)|UHCI_LP_Q;
               }
               break;
            }
            prevQH=currQH;
         }
      }
   }

   // return matching with request type structure QH address
   return (GetStructQH( qhTop, lowSpeedDevice, requestType, maxPacketSize));
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetStructQH                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get structure QH                                */
/*                                                                    */
/* FUNCTION:  This routine returns pointer structure QH to be used    */
/*            for I/O request with given characteristics              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time with interrupts disabled                        */
/*                                                                    */
/* ENTRY POINT :  GetStructQH                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:   QH *topQH - pointer to top level QH                       */
/*          BOOL lowSpeedDevice - TRUE for low speed devices          */
/*          USHORT requestType - request queue type (isohronous,      */
/*                               interrupt, control, bulk)            */
/*          USHORT maxPacketSize - maximum packet size for            */
/*                                 this request                       */
/*                                                                    */
/* EXIT-NORMAL:  pointer to structure QH block                        */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  incorporates new QH structure in host schedule           */
/*                                                                    */
/* INTERNAL REFERENCES:  GetStructQH                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static QH *GetStructQH( QH *topQH, BOOL lowSpeedDevice, USHORT requestType, USHORT maxPacketSize)
{
   QH    *structQH=NULL;

   switch (requestType&USRB_FLAGS_DETT_MASK)
   {
   case USRB_FLAGS_DET_ISOHR:
      structQH=topQH;   // 25/08/98 MB - not used for isohronous requests
      break;  
   case USRB_FLAGS_DET_INTRPT:   // interrupt transfer queue
      if (maxPacketSize>gMaxReclPktSize || lowSpeedDevice || !topQH->rIntrptQH)
         structQH=topQH->intrptQH;
      else
         structQH=topQH->rIntrptQH;
      break;
   case USRB_FLAGS_DET_BULK:  // bulk transfer queue
      if (maxPacketSize>gMaxReclPktSize || lowSpeedDevice || !topQH->rBulkQH)
         structQH=topQH->bulkQH;
      else
         structQH=topQH->rBulkQH;
      break;
   default: // control transfer queue
      if (maxPacketSize>gMaxReclPktSize || lowSpeedDevice || !topQH->rControlQH)
         structQH=topQH->controlQH;
      else
         structQH=topQH->rControlQH;
      break;
   }

#ifdef   DEBUG         
   dsPrint2(DBG_DETAILED,"UHCI: GetStructQH topQH=%x,structQH=%x\r\n",
            (USHORT)topQH, (USHORT)structQH);
#endif

   return (structQH);
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetTopQHInfo                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get QH address on given physical address        */
/*                                                                    */
/* FUNCTION:  This routine converts physical QH address to virtual    */
/*            address using local TD/QH array data                    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetTopQHInfo                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:   USHORT frameIndex - schedule frame index                  */
/*          PULONG FAR *schAddr - address to store new frame entry    */
/*                                                                    */
/* EXIT-NORMAL:  QHs virtual address                                  */
/*                                                                    */
/* EXIT-ERROR:  NULL, if there are no matches in UHCI TD/QH table     */
/*              with given physical address                           */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static QH *GetTopQHInfo(USHORT frameIndex, PULONG FAR *schAddr)
{
   USHORT   tdIndex;
   QH       *qh=NULL;
   ULONG    phyAddress;

   phyAddress=gFrameListAddr[frameIndex];
   *schAddr=&gFrameListAddr[frameIndex];

   if (phyAddress!=UHCI_LP_UNUSED)
   {
      if (!(phyAddress&UHCI_LP_Q) && gLastIso)   // isohronous TD physical address specified
      {
         phyAddress=gLastIso->td[frameIndex].LP;
         *schAddr=&gLastIso->td[frameIndex].LP;
      }
      phyAddress&=UHCI_LP_MASK;  // zero flag bits

      // check all the queue head/TD array
      for (tdIndex=0; tdIndex<=gLastTDId; tdIndex++)
      {
         if (gTDListStart[tdIndex].LP==UHCI_LP_UNUSED)
            continue;
         if (gTDListStart[tdIndex].phyTDAddr!=phyAddress)
            continue;
         qh=(QH *)(gTDListStart+tdIndex);
         break;
      }
   }

   return (qh);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DetachQH                                         */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detach QH from host schedule                    */
/*                                                                    */
/* FUNCTION:  This routine detaches given QH from host controller's   */
/*            schedule                                                */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time with interrupts disabled                        */
/*                                                                    */
/* ENTRY POINT :  DetachQH                                            */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:   QH *qh - pointer to QH to be detached from schedule       */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  modifies host controller schedule                        */
/*                                                                    */
/* INTERNAL REFERENCES:  GetStructQH                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void DetachQH(QH *qh)
{
   QH    *parentQH;

   parentQH=qh->parentQH;
   if (parentQH)   // regular QH element
   {
      if (parentQH->elementType==ELEMENT_TYPE_QH_TOP)
      {  // if parent QH is top level QH empty its element list
         parentQH->qhLink|=UHCI_LP_T;
         parentQH->qhLink=qh->qhLink;
         parentQH->qhLinkVirt=qh->qhLinkVirt;
      }
      else if (parentQH->elementType==ELEMENT_TYPE_QH_STRUC)
      {  // if parent QH is queue structure level QH delete QH from its element list
         if (parentQH->qhLink==qh->qhLink) // last bottom level QH
         {
            parentQH->qeLink=UHCI_LP_T;   // terminate list
            parentQH->qeLinkVirt=NULL;
         }
         else  // QH is not last in list
         {
            parentQH->qeLink|=UHCI_LP_T;
            parentQH->qeLink=qh->qhLink;
            parentQH->qeLinkVirt=qh->qhLinkVirt;
         }
      }
      else
      {  // if parent QH is bottom level QH delete current QH from its qhead list
         parentQH->qhLink|=UHCI_LP_T;
         parentQH->qhLink=qh->qhLink;
         parentQH->qhLinkVirt=qh->qhLinkVirt;
      }
   }
   if (qh->qhLinkVirt)   // change parent QH pointer to reflect QH's new position
      qh->qhLinkVirt->parentQH=parentQH;

   FreeTDsQH((TD *)qh, TRUE); // release detached QH
}
