/* SCCSID = "src/dev/usb/UHCI/UHCISO.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:  Isohronous I/O request accepting/processing routines. */
/*                                                                            */
/*   FUNCTION: These routines accepts I/O requests for execution: opens       */
/*             request, closes request, cancels request, retrieves            */
/*             information about current request status, processes 'buffer    */
/*             data processed' interrupts and sends notification message      */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             AccIsoReq                                                      */
/*             CreateIsoStruct                                                */
/*             ReleaseIsoStruct                                               */
/*             AddIsoBuffsToSchedule                                          */
/*             IsIsoInterrupt                                                 */
/*             ProcessIsoIRQ                                                  */
/*             CancelIsoRequests                                              */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/10/15  MB                                                      */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include        "uhci.h"

static void OpenIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void CloseIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL );
static ULONG GetTargetAddr( USBRB FAR *ioRB );
static ISOINFO FAR * GetIsoInfo( RP_GENIOCTL FAR *pRP_GENIOCTL );
static VOID GetBuffInfo( USBRB FAR *ioRB, ISOINFO FAR *isoInfo, USHORT frameIndex );
static void AccIsoBuffer( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void CancelIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void IsoReqInfo( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void AddIsoTDsToSchedule( ISOINFO FAR *isoInfo );
static void DetachIsoTDs( ISOINFO FAR *isoInfo );
static VOID IsoSendNotification(ISOINFO FAR *isoInfo, NOTIFDATA FAR *notifyData);

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AccIsoReq                                        */
/*                                                                    */
/* DESCRIPTIVE NAME:  Accept isohronous I/O request worker routine    */
/*                                                                    */
/* FUNCTION:  This checks request type and calls apropriate worker    */
/*            routine to process isohronous request.                  */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AccIsoReq                                           */
/*    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_PARMERR - 1) request queue type is not        */
/*                                isohronous;                         */
/*                                2) request token type is not IN or  */
/*                                OUT.                                */
/*                                                                    */
/* INTERNAL REFERENCES:  OpenIsoReq                                   */
/*    ROUTINES:          CloseIsoReq                                  */
/*                       CancelIsoReq                                 */
/*                       IsoReqInfo                                   */
/*                       AccIsoBuffer                                 */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void AccIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   USHORT            reqQueingType;
   USHORT            tokenType;

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

   // check request type
   reqQueingType=ioRB->flags&USRB_FLAGS_DETT_MASK;
   if (reqQueingType!=USRB_FLAGS_DET_ISOHR)
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_PARMERR;
      return;
   }

   // check token type
   tokenType=ioRB->flags&~USRB_FLAGS_TTYPE_MASK;
   switch (tokenType)
   {
   case USRB_FLAGS_TTYPE_IN:
   case USRB_FLAGS_TTYPE_OUT:
      break;
   default:
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_PARMERR;
      return;
   }
#ifdef   DEBUG         
   dsPrint4(DBG_DETAILED,"UHCI: AccIsoReq isoFlags=%lx,frmLen=%d,isoBuffst=%d,rqFlgs=%x\r\n",
            ioRB->isoFlags,ioRB->isoFrameLength,
            ioRB->isoBuffers,ioRB->flags);
#endif

   // call worker routine
   switch (ioRB->isoFlags)
   {
   case USRB_ISOFLAGS_OPEN:
      OpenIsoReq(pRP_GENIOCTL);
      break;
   case USRB_ISOFLAGS_CLOSE:
      CloseIsoReq(pRP_GENIOCTL);
      break;
   case USRB_ISOFLAGS_CANCEL:
      CancelIsoReq(pRP_GENIOCTL);
      break;
   case USRB_ISOFLAGS_INFO:
      IsoReqInfo(pRP_GENIOCTL);
      break;
   default:
      AccIsoBuffer(pRP_GENIOCTL);
      break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  OpenIsoReq                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Open isohrounous request                        */
/*                                                                    */
/* FUNCTION:  This routine:                                           */
/*            1) checks for already opened isohronous request for     */
/*               the same device and endpoint;                        */
/*            2) allocates isohronous request data structures and     */
/*               initializes them;                                    */
/*            3) adds allocated TDs to host shcedule.                 */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  OpenIsoReq                                          */
/*    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_ADDRINV - 1) already opened iso I/O request   */
/*                                 found for the same device&endpoint */
/*           USB_IDC_RC_PARMERR - isoFrameLength exceeds target       */
/*                                maxpacket size                      */
/*           USB_IDC_RC_ALLOCERR - failed to allocate request data    */
/*                                 structures                         */
/*           USB_IDC_RC_NOBANDWIDTH - not enought bandwidth to serve  */
/*                                 request                            */
/*                                                                    */
/* INTERNAL REFERENCES:  GetIsoInfo                                   */
/*    ROUTINES:          CreateIsoStruct                              */
/*                       AddIsoTDsToSchedule                          */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void OpenIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   ISOINFO FAR       *isoInfo;

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

   // search for existing request with the same device address and endpoint address
   isoInfo=GetIsoInfo( pRP_GENIOCTL );
   if (isoInfo)
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ADDRINV;
      return;
   }
   else
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;

   if (ioRB->isoFrameLength>ioRB->maxPacketSize)
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_PARMERR;
      return;
   }

   isoInfo=CreateIsoStruct( ioRB );
   if (!isoInfo)   // failed to allocate required data structures
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ALLOCERR;
      return;
   }

   // check available bandwidth
   isoInfo->tdProcessingTime=PacketTTime(ioRB->maxPacketSize,
                                         FALSE, isoInfo->td[0].token, USRB_FLAGS_DET_ISOHR);
   if (!CheckBandwidth( NULL, isoInfo->tdProcessingTime ))
   {
      ReleaseIsoStruct( isoInfo );
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_NOBANDWIDTH;
      return;
   }
   else
      gIsoPktSize+=isoInfo->tdProcessingTime;

   // save structure pointer
   isoInfo->previous=gLastIso;
   if (!gFirstIso)
      gFirstIso=isoInfo;
   gLastIso=isoInfo;

   // attach new TDs to host schedule
   AddIsoTDsToSchedule( isoInfo );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CloseIsoReq                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Close Isohronous request                        */
/*                                                                    */
/* FUNCTION:  This routine:                                           */
/*            1) detaches TDs from host schedule;                     */
/*            2) releases request data structures;                    */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  CloseIsoReq                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  GetIsoInfo                                   */
/*    ROUTINES:          DetachIsoTDs                                 */
/*                       ReleaseIsoStruct                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void CloseIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   ISOINFO FAR       *isoInfo;

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

   isoInfo=GetIsoInfo( pRP_GENIOCTL );
   if (!isoInfo)
      return;

   // detach TDs from host schedule
   DetachIsoTDs( isoInfo );

   // delete info block from iso data chain
   if (isoInfo->next)
      isoInfo->next->previous=isoInfo->previous;
   else
      gLastIso=isoInfo->previous;
   if (isoInfo->previous)
      isoInfo->previous->next=isoInfo->next;
   else
      gFirstIso=isoInfo->next;

   // decrease isohronous bandwdith
   if (gIsoPktSize<=isoInfo->tdProcessingTime)
      gIsoPktSize=0;
   else
      gIsoPktSize-=isoInfo->tdProcessingTime;

   ReleaseIsoStruct( isoInfo );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CreateIsoStruct                                  */
/*                                                                    */
/* 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 :  CreateIsoStruct                                     */
/*    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 **************************/
ISOINFO FAR *CreateIsoStruct( USBRB FAR *ioRB )
{
   USHORT            isoSize;
   ULONG             isoPhysicalAddress, roundedAddr, tdPhysAddr, length;
   ISOINFO FAR       *isoInfo=NULL;
   SEL               isoGDTSel;
   USHORT            frameIndex;
   USHORT            offset, tdOffset;

   if (!ioRB->isoBuffers)
      return (NULL);  // wrong number of buffers

   // calculate data structure size and add extra 16 bytes (sizeof(ISOTD)) to allow TD array alignment
   isoSize=sizeof(ISOINFO)+ (ioRB->isoBuffers-1)*sizeof(ISOBUFFS) + sizeof(ISOTD);

   /* Allocate in high memory first.  If it fails allocate in low mem */
   if ( DevHelp_AllocPhys(isoSize, MEMTYPE_ABOVE_1M, &isoPhysicalAddress) ) // Allocate high
   {
      /* If that fails, allocate low 
      If that fails, installation fails */
      if ( DevHelp_AllocPhys(isoSize, MEMTYPE_BELOW_1M, &isoPhysicalAddress) )
      {
         return (NULL);
      }
   }

   // allocate GDT selector
   if ( DevHelp_AllocGDTSelector(&isoGDTSel, 1) )
   {
      DevHelp_FreePhys(isoPhysicalAddress);
      return (NULL);
   }

   // Map to GDT selector to isoInfo data block
   if ( DevHelp_PhysToGDTSelector(isoPhysicalAddress,  // Physical address
                                  isoSize,             // Length
                                  isoGDTSel) )         // Selector to map
   {
      DevHelp_FreeGDTSelector(isoGDTSel);
      DevHelp_FreePhys(isoPhysicalAddress);
      return (NULL);
   }
   else
   {
      // align TD list on 16 byte boundary
      isoInfo = MAKEP(isoGDTSel,0);  /* set to virtual GDT pointer */
      setmem((PSZ)isoInfo,0,isoSize);
      tdOffset=((UCHAR FAR *)&isoInfo->td)-((UCHAR FAR *)isoInfo);
      tdPhysAddr=isoPhysicalAddress+tdOffset;
      roundedAddr=tdPhysAddr&UHCI_LP_MASK;
      if (roundedAddr<tdPhysAddr)
         roundedAddr+=16;
      offset=(USHORT)(roundedAddr-tdPhysAddr);
      isoInfo = MAKEP(isoGDTSel,offset);  /* set to virtual GDT pointer */

      isoInfo->next=NULL;
      isoInfo->previous=NULL;
      isoInfo->physAddress=isoPhysicalAddress;
      isoInfo->gdtSelector=isoGDTSel;

      isoInfo->deviceAddress=ioRB->deviceAddress;
      isoInfo->endPointId=ioRB->endPointId;
      isoInfo->flags=ioRB->flags;
      // set request ID - endpoint and device addresses
      isoInfo->targetDevAddr=GetTargetAddr( ioRB );
      isoInfo->maxBuffers=ioRB->isoBuffers;                // (18)
      isoInfo->currBuffCount=0;             // (20)
      isoInfo->activeBuffIndex=0;           // (22)
      isoInfo->lastUsedFrame=UHCI_UNUSED_FRAMEINDEX;             // (24)
      isoInfo->isoFrameLength=ioRB->isoFrameLength;

      isoInfo->usbIDC=ioRB->usbIDC;  // Address of IRQ processing routine to be called for this request
      isoInfo->usbDS=ioRB->usbDS;         // DS value for IRQ processing routine
      isoInfo->category=ioRB->category;      // callers category (used in IRQ extension calls)
      isoInfo->requestData1=ioRB->requestData1;  // data to be stored within request
      isoInfo->requestData2=ioRB->requestData2;  // data to be stored within request
      isoInfo->requestData3=ioRB->requestData3;  // data to be stored within request

      DevHelp_VirtToPhys((PVOID)&isoInfo->td, &isoInfo->tdPhyAddr);
      // fill out 1st TD with default values
      isoInfo->td[0].LP=UHCI_LP_T;                  // (00) inactive entry
      isoInfo->td[0].ctrlStat=TD_CTRLSTAT_IOS;      // (04) isohronous TD
      // set transfer length, target address and token type (IN or OUT)
      length=ioRB->isoFrameLength-1;
      isoInfo->td[0].token=length<<21;   // maxlength
      isoInfo->td[0].token|=isoInfo->targetDevAddr;  // (08) TD token
      if ((ioRB->flags&~USRB_FLAGS_TTYPE_MASK)==USRB_FLAGS_TTYPE_IN)
         isoInfo->td[0].token|=TD_TOKEN_PID_IN;
      else
         isoInfo->td[0].token|=TD_TOKEN_PID_OUT;
      isoInfo->td[0].bufferPointer=NULL;            // (12) TD buffer pointer
      for (frameIndex=1; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
         movmem((PSZ)&isoInfo->td[frameIndex], (PSZ)&isoInfo->td[0], sizeof(isoInfo->td[0]));
   }

   return (isoInfo);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ReleaseIsoStruct                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Release isohronous request data structure       */
/*                                                                    */
/* FUNCTION:  This routine releases allocated for request data        */
/*            structure physical memory and associated GDT selector.  */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ReleaseIsoStruct                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ISOINFO FAR *isoInfo - far pointer to isohronous request   */
/*                                data structure                      */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_FreeGDTSelector                      */
/*    ROUTINES:          DevHelp_FreePhys                             */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
VOID ReleaseIsoStruct( ISOINFO FAR *isoInfo )
{
   ULONG    physicalAddress;
   SEL      selector;

   physicalAddress=isoInfo->physAddress;
   selector=isoInfo->gdtSelector;

   DevHelp_FreeGDTSelector(selector);
   DevHelp_FreePhys(physicalAddress);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetTargetAddr                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get target device and enpoint address           */
/*                                                                    */
/* FUNCTION:  This routine extracts device and endpoint addresses     */
/*            from I/O request block and returns joint address in     */
/*            form used in transfer descriptor (TD).                  */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetTargetAddr                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  USBRB FAR *ioRB - USB I/O request block address            */
/*                                                                    */
/* EXIT-NORMAL:  joint device and endpoint address                    */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static ULONG GetTargetAddr( USBRB FAR *ioRB )
{
   return (CreateTokenAddress( ioRB->deviceAddress, ioRB->endPointId ));
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetBuffInfo                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get current buffer information                  */
/*                                                                    */
/* FUNCTION:  This routine searches for buffer descriptor in request  */
/*            data block and sets buffer's virtual address and        */
/*            processed data length in I/O request block.             */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetBuffInfo                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:   USBRB FAR *ioRB - I/O request block address               */
/*          ISOINFO FAR *isoInfo - isohronous request data address    */
/*          USHORT frameIndex - buffer's frame index                  */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets buffers virtual address in ioRB->buffer1 and        */
/*           processed data length in ioRB->buffer1Length. In case    */
/*           buffers address is not find in buffer list, NULL value   */
/*           is used for virtual address and 0 for data length.       */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                       GetTopLevelQH                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static VOID GetBuffInfo( USBRB FAR *ioRB, ISOINFO FAR *isoInfo, USHORT frameIndex )
{
   USHORT            bufferIndex;
   ULONG             lastBufferAddress;

   ioRB->buffer1=NULL;
   ioRB->buffer1Length=0;
   lastBufferAddress=isoInfo->td[frameIndex].bufferPointer;
   // search for current buffer descriptor and calculate transfered data length
   for (bufferIndex=0; bufferIndex<isoInfo->currBuffCount; bufferIndex++)
   {
      if (lastBufferAddress<isoInfo->bufInfo[bufferIndex].buffPhysAddr ||
          lastBufferAddress>=isoInfo->bufInfo[bufferIndex].lastByteAddr)
         continue;
      ioRB->buffer1=isoInfo->bufInfo[bufferIndex].buffVirtAddr;
      ioRB->buffer1Length=(USHORT)(lastBufferAddress-isoInfo->bufInfo[bufferIndex].buffPhysAddr+
                                   isoInfo->isoFrameLength);
      break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetIsoInfo                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Isohronous Request Information data address */
/*                                                                    */
/* FUNCTION:  This routine searches isohronous request data list      */
/*            for matching device, endpoint addresses, transfer       */
/*            direction and returns data block address.               */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetIsoInfo                                          */
/*    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_ADDRINV - information data block not found    */
/*                                                                    */
/* INTERNAL REFERENCES:  GetTargetAddr                                */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static ISOINFO FAR * GetIsoInfo( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR      *ioRB;
   ULONG          targetDevAddr;
   ISOINFO FAR *  isoInfo;

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

   // search for required request data info block
   targetDevAddr=GetTargetAddr( ioRB );
   for (isoInfo=gFirstIso; isoInfo; isoInfo=isoInfo->next)
   {
      if (isoInfo->targetDevAddr==targetDevAddr &&
          (isoInfo->flags&~USRB_FLAGS_TTYPE_MASK)==(ioRB->flags&~USRB_FLAGS_TTYPE_MASK))
         break;
   }
   if (!isoInfo)
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ADDRINV;

   return (isoInfo);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AccIsoBuffer                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Accept Isphronous Request Data Buffer           */
/*                                                                    */
/* FUNCTION:  This routine searhes for I/O request block, adds new    */
/*            buffer to list and starts buffer pocessing thread.      */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AccIsoBuffer                                        */
/*    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_ALLOCERR - number of buffers exceeds maximum. */
/*                                                                    */
/* INTERNAL REFERENCES:  GetIsoInfo                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_VirtToPhys                           */
/*    ROUTINES:          DevHelp_ArmCtxHook                           */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void AccIsoBuffer( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   ISOINFO FAR       *isoInfo;
   USHORT            newBuffIndex;

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

   isoInfo=GetIsoInfo( pRP_GENIOCTL );
   if (!isoInfo)
      return;

   if (isoInfo->currBuffCount>=isoInfo->maxBuffers)
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ALLOCERR;
      return;
   }

   newBuffIndex=isoInfo->currBuffCount;
   isoInfo->currBuffCount++;
   isoInfo->bufInfo[newBuffIndex].buffVirtAddr=ioRB->buffer1; 
   isoInfo->bufInfo[newBuffIndex].buffLength=ioRB->buffer1Length;
   DevHelp_VirtToPhys(isoInfo->bufInfo[newBuffIndex].buffVirtAddr,
                      &isoInfo->bufInfo[newBuffIndex].buffPhysAddr);
   isoInfo->bufInfo[newBuffIndex].lastByteAddr=
   isoInfo->bufInfo[newBuffIndex].buffPhysAddr+
   (isoInfo->bufInfo[newBuffIndex].buffLength/isoInfo->isoFrameLength)*isoInfo->isoFrameLength;
   isoInfo->bufInfo[newBuffIndex].lastUsedAddr=
   isoInfo->bufInfo[newBuffIndex].buffPhysAddr-isoInfo->isoFrameLength;
   isoInfo->bufInfo[newBuffIndex].irqFrameIndex=UHCI_UNUSED_FRAMEINDEX;
   isoInfo->bufInfo[newBuffIndex].requestData1=ioRB->requestData1;   // save data
   isoInfo->bufInfo[newBuffIndex].requestData2=ioRB->requestData2;   // to be used
   isoInfo->bufInfo[newBuffIndex].requestData3=ioRB->requestData3;   // in reports to caller

   DevHelp_ArmCtxHook( 0, gAddIsoHookHandle );  // aquire task time to add buffers to schedule

#ifdef   DEBUG         
   dsPrint4(DBG_SPECIFIC,"UHCI: AccIsoBuffer Count=%d,len=%d,Addr=%lx(%lx)\r\n",
            isoInfo->currBuffCount,isoInfo->bufInfo[newBuffIndex].buffLength,
            (ULONG)isoInfo->bufInfo[newBuffIndex].buffVirtAddr,
            isoInfo->bufInfo[newBuffIndex].buffPhysAddr);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CancelIsoReq                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Cancel Isohronous Request processing            */
/*                                                                    */
/* FUNCTION:  This routine stops request buffer processing and        */
/*            returns last processed buffer address and length.       */
/*            2) fills in TD chain;                                   */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  CancelIsoReq                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  GetIsoInfo                                   */
/*    ROUTINES:          GetBuffInfo                                  */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static void CancelIsoReq( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   ISOINFO FAR       *isoInfo;
   USHORT            currFrameIndex;
   USHORT            frameIndex;
   USHORT            flags;

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

   flags=CLISave();
   isoInfo=GetIsoInfo( pRP_GENIOCTL );

   if (isoInfo)
   {
      // get last serviced frame index
      inp16(gusbFrnumReg,currFrameIndex);
      currFrameIndex--;
      currFrameIndex&=UHCI_FRNUM_MASK;

      // disable all TD's
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         isoInfo->td[frameIndex].ctrlStat&=~TD_CTRLSTAT_STATUS_ACTIVE;
         isoInfo->td[frameIndex].ctrlStat&=~TD_CTRLSTAT_IOC;
      }

      // get last buffer adress and transferred data length
      GetBuffInfo( ioRB, isoInfo, currFrameIndex );

      // clear buffer information
      isoInfo->currBuffCount=0;
      isoInfo->activeBuffIndex=0;
      isoInfo->lastUsedFrame=UHCI_UNUSED_FRAMEINDEX;
   }
   STIRestore(flags);
}
#pragma optimize("", on)


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  IsoReqInfo                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Retrieve Isohronous Request procssing status    */
/*                                                                    */
/* FUNCTION:  This routine locates request data block and retrieves   */
/*            last processed frame buffer information (address and    */
/*            number of processed data bytes).                        */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  IsoReqInfo                                          */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  GetIsoInfo                                   */
/*    ROUTINES:          GetBuffInfo                                  */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static void IsoReqInfo( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *ioRB;
   ISOINFO FAR       *isoInfo;
   USHORT            currFrameIndex;

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

   isoInfo=GetIsoInfo( pRP_GENIOCTL );
   if (!isoInfo)
      return;

   // get last serviced frame index
   inp16(gusbFrnumReg,currFrameIndex);
   currFrameIndex--;
   currFrameIndex&=UHCI_FRNUM_MASK;

   GetBuffInfo( ioRB, isoInfo, currFrameIndex );
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AddIsoBuffsToSchedule                            */
/*                                                                    */
/* DESCRIPTIVE NAME:  Add isohronous request buffers to host schedule */
/*                                                                    */
/* FUNCTION:  This routine is called in separate thread to start      */
/*            isohronous buffer processing.                           */
/*            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 :  AddIsoBuffsToSchedule                               */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  none                                                       */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  isohronous request TD list are updated                   */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:          none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
VOID AddIsoBuffsToSchedule( void )
{
   ISOINFO FAR       *isoInfo;
   USHORT            currFrameIndex, nextFrameIndex, notificationIndex;
   BOOL              bufferAdded=TRUE;

   while (bufferAdded)
   {
      bufferAdded=FALSE;
      for (isoInfo=gFirstIso; isoInfo; isoInfo=isoInfo->next)
      {
         if (!isoInfo->currBuffCount || isoInfo->activeBuffIndex>=isoInfo->currBuffCount)
            continue;   // no active buffers for this request

         // get frame index for buffer  to be added
         inp16(gusbFrnumReg,currFrameIndex);
         currFrameIndex&=UHCI_FRNUM_MASK;
         if (isoInfo->lastUsedFrame==UHCI_UNUSED_FRAMEINDEX ||   // 1st call
             !(isoInfo->td[isoInfo->lastUsedFrame].ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE)) // failed
            nextFrameIndex=currFrameIndex+ISO_SERVICE_DELAY;
         else
            nextFrameIndex=isoInfo->lastUsedFrame+1;
         nextFrameIndex&=UHCI_FRNUM_MASK;
         if (nextFrameIndex==currFrameIndex)
         {  // set 'interrupt on complete' flag to arm buffer processing
            if (isoInfo->bufInfo[isoInfo->activeBuffIndex].lastUsedAddr+isoInfo->isoFrameLength<
                isoInfo->bufInfo[isoInfo->activeBuffIndex].lastByteAddr)   // part of buffer processed
            {
               // ioc index - ISO_IRQ_DELAY frames before last added buffer frame
               if (currFrameIndex>ISO_IRQ_DELAY)
                  notificationIndex=currFrameIndex-ISO_IRQ_DELAY;
               else
                  notificationIndex=UHCI_FRNUM_MASK+currFrameIndex-ISO_IRQ_DELAY;
               notificationIndex&=UHCI_FRNUM_MASK;
               // ajust index to arm only active entries
               for (;!(isoInfo->td[notificationIndex].ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE);
                   notificationIndex=(notificationIndex+1)&UHCI_FRNUM_MASK)
               {
                  if (notificationIndex==currFrameIndex)
                     break;
               }
               isoInfo->td[notificationIndex].ctrlStat|=TD_CTRLSTAT_IOC;
               isoInfo->bufInfo[isoInfo->activeBuffIndex].irqFrameIndex=notificationIndex;
            }
            continue;   // schedule full - no more entries can be added
         }

         // get buffer address to be added to schedule
         if (isoInfo->bufInfo[isoInfo->activeBuffIndex].lastUsedAddr+isoInfo->isoFrameLength>=
             isoInfo->bufInfo[isoInfo->activeBuffIndex].lastByteAddr)   // all processed
         {
            isoInfo->activeBuffIndex++;
            if (isoInfo->activeBuffIndex>=isoInfo->currBuffCount)
               continue;
         }
         isoInfo->bufInfo[isoInfo->activeBuffIndex].lastUsedAddr+=isoInfo->isoFrameLength;

         // add buffer to schedule and mark active
         isoInfo->td[nextFrameIndex].bufferPointer=isoInfo->bufInfo[isoInfo->activeBuffIndex].lastUsedAddr;
         isoInfo->td[nextFrameIndex].ctrlStat&=~TD_CTRLSTAT_STATUS_MASK;
         isoInfo->td[nextFrameIndex].ctrlStat|=TD_CTRLSTAT_STATUS_ACTIVE;
         if (isoInfo->bufInfo[isoInfo->activeBuffIndex].lastUsedAddr+isoInfo->isoFrameLength>=
             isoInfo->bufInfo[isoInfo->activeBuffIndex].lastByteAddr)   // last sub-buffer
         {
            isoInfo->td[nextFrameIndex].ctrlStat|=TD_CTRLSTAT_IOC;
            isoInfo->bufInfo[isoInfo->activeBuffIndex].irqFrameIndex=nextFrameIndex;
#ifdef   DEBUG         
            dsPrint2(DBG_SPECIFIC,"UHCI: AddIsoBuffsToSchedule last buff=%x,frame=%x\r\n",
                     isoInfo->activeBuffIndex, nextFrameIndex);
#endif
         }
         else
            isoInfo->td[nextFrameIndex].ctrlStat&=~TD_CTRLSTAT_IOC;
         isoInfo->lastUsedFrame=nextFrameIndex;
         bufferAdded=TRUE;
      }
   }
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AddIsoTDsToSchedule                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Add TD list to host schedule                    */
/*                                                                    */
/* FUNCTION:  This routine expands USB host schedule entries with     */
/*            a new list of transfer decriptors.                      */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AddIsoTDsToSchedule                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ISOINFO FAR *isoInfo - isohronous request data block addr  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void AddIsoTDsToSchedule( ISOINFO FAR *isoInfo )
{
   USHORT      frameIndex;
   ISOINFO FAR *previous;

   if ((previous=isoInfo->previous)) // add as last in iso request chain
   {
      // add next TD/QH address into current request TDs link fields
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         isoInfo->td[frameIndex].LP=previous->td[frameIndex].LP;
      }

      // update last active iso request TD to activate new TDs
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         previous->td[frameIndex].LP=isoInfo->tdPhyAddr+frameIndex*sizeof(previous->td[frameIndex]);
      }
   }
   else  // add as 1st in iso request chain
   {
      // add next TD/QH address into current request TDs link fields
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         isoInfo->td[frameIndex].LP=gFrameListAddr[frameIndex];
      }

      // update last active iso request TD to activate new TDs
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         gFrameListAddr[frameIndex]=isoInfo->tdPhyAddr+frameIndex*sizeof(previous->td[frameIndex]);
      }
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DetachIsoTDs                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detach TD list from host schedule               */
/*                                                                    */
/* FUNCTION:  This routine detaches given request TD list from USB    */
/*            host schedule.                                          */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  DetachIsoTDs                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ISOINFO FAR *isoInfo - isohronous request data block addr  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void DetachIsoTDs( ISOINFO FAR *isoInfo )
{
   USHORT      frameIndex;
   ISOINFO FAR *previous;

   if ((previous=isoInfo->previous))
   {
      // update previous active iso request TD to deactivate specified TDs
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         previous->td[frameIndex].LP=isoInfo->td[frameIndex].LP;
      }
   }
   else
   {
      // update last active iso request TD to deactivate specified TDs
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         gFrameListAddr[frameIndex]=isoInfo->td[frameIndex].LP;
      }
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  IsIsoInterrupt                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Is Isohronous Interrupt?                        */
/*                                                                    */
/* FUNCTION:  This routine is called during IRQ processing to         */
/*            find out requests raised "transfer finished" iterrupt.  */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Interrupt Time                                            */
/*                                                                    */
/* ENTRY POINT :  IsIsoInterrupt                                      */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  none                                                       */
/*                                                                    */
/* EXIT-NORMAL:  TRUE if there are finished, unprocessed isohronous   */
/*                    transfer descriptor(s)                          */
/*               FALSE if there are no finished isohronous TD(s)      */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:          none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
BOOL IsIsoInterrupt( void )
{
   BOOL           isIso=FALSE;
   ISOINFO FAR    *isoInfo;
   USHORT         buffIndex, irqFrameIndex;

   // check all the requests
   for (isoInfo=gFirstIso; isoInfo && !isIso; isoInfo=isoInfo->next)
   {
      // check all the request buffers
      for (buffIndex=0; buffIndex<isoInfo->currBuffCount && !isIso; buffIndex++)
      {
         irqFrameIndex=isoInfo->bufInfo[buffIndex].irqFrameIndex;
         if (irqFrameIndex!=UHCI_UNUSED_FRAMEINDEX) // buffer in service
            isIso= !(isoInfo->td[irqFrameIndex].ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE);
      }
   }

   return (isIso);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessIsoIRQ                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Process Finished Isohronous request(s)          */
/*                                                                    */
/* FUNCTION:  This routine is called in separate thread to finish     */
/*            isohronous buffer processing and start new thread       */
/*            to add buffers to shcedule.                             */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessIsoIRQ                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  none                                                       */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:          none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
VOID ProcessIsoIRQ( void )
{
   ISOINFO FAR    *isoInfo;
   USHORT         buffIndex, irqFrameIndex;
   NOTIFDATA      notificationData;
   USHORT         flags;

   // check all the isohronous requests and their buffer lists
   // send notification message when buffer processing finished
   for (isoInfo=gFirstIso; isoInfo; isoInfo=isoInfo->next)
   {
      for (buffIndex=0; buffIndex<isoInfo->currBuffCount; buffIndex++)
      {
         irqFrameIndex=isoInfo->bufInfo[buffIndex].irqFrameIndex;
         if (irqFrameIndex==UHCI_UNUSED_FRAMEINDEX) // not yet scheduled
            continue;
         if (isoInfo->td[irqFrameIndex].ctrlStat&TD_CTRLSTAT_STATUS_ACTIVE) // item still active
            continue;
         if (isoInfo->bufInfo[buffIndex].lastUsedAddr+isoInfo->isoFrameLength>=
             isoInfo->bufInfo[buffIndex].lastByteAddr)   // all buffer data processed
         {
            // delete processed buffer from buffer list and send notification
            notificationData.buffVirtAddr=isoInfo->bufInfo[buffIndex].buffVirtAddr;
            notificationData.buffLength=isoInfo->bufInfo[buffIndex].buffLength;
            notificationData.requestData1=isoInfo->bufInfo[buffIndex].requestData1;
            notificationData.requestData2=isoInfo->bufInfo[buffIndex].requestData2;
            notificationData.requestData3=isoInfo->bufInfo[buffIndex].requestData3;

            // clear processed buffer info
            flags=CLISave();
            if (buffIndex<isoInfo->currBuffCount-1)
            {
               movmem((PSZ)&isoInfo->bufInfo[buffIndex], (PSZ)&isoInfo->bufInfo[buffIndex+1],
                      (isoInfo->currBuffCount-buffIndex-1)*sizeof(isoInfo->bufInfo[buffIndex+1]) );
            }
            isoInfo->currBuffCount--;
            if (isoInfo->activeBuffIndex>buffIndex)
               isoInfo->activeBuffIndex--;
            buffIndex--;
            STIRestore(flags);

            // send 'buffer processing finished' notification
            IsoSendNotification(isoInfo, &notificationData);
         }
      }
   }
#ifdef   DEBUG         
   dsPrint2(DBG_SPECIFIC,"UHCI: ProcessIsoIRQ bAddr=%lx,l=%d\r\n",
            (ULONG)notificationData.buffVirtAddr, notificationData.buffLength);
#endif

   DevHelp_ArmCtxHook( 0, gAddIsoHookHandle );  // aquire task time to add new buffers to schedule
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  IsoSendNotification                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Add isohronous request buffers to host schedule */
/*                                                                    */
/* FUNCTION:  This routine is called in separate thread to start      */
/*            isohronous buffer processing.                           */
/*            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 :  IsoSendNotification                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  none                                                       */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  isohronous request TD list are updated                   */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:          none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static VOID IsoSendNotification(ISOINFO FAR *isoInfo, NOTIFDATA FAR *notifyData)
{
   RP_GENIOCTL    rpIRQ;
   USBRB          irqRB;

   //  fill in request block
   irqRB.controllerId=ghcdID; // set controller ID in request block
   irqRB.deviceAddress=isoInfo->deviceAddress; // set device address
   irqRB.endPointId=isoInfo->endPointId;    // set endpoint address

   irqRB.status=0;        // clear request status flags

   // set flags field value to match TD status
   irqRB.flags=isoInfo->flags;

   if (notifyData)
   {
      irqRB.buffer1=notifyData->buffVirtAddr;   // Virtual address of data buffer
      irqRB.buffer1Length=notifyData->buffLength;     // Buffer length in bytes
      irqRB.requestData1=notifyData->requestData1;  // data stored within request
      irqRB.requestData2=notifyData->requestData2;  // data stored within request
      irqRB.requestData3=notifyData->requestData3;  // data stored within request
   }
   else
   {
      irqRB.buffer1=NULL;   // Virtual address of data buffer
      irqRB.buffer1Length=0;     // Buffer length in bytes
      irqRB.requestData1=isoInfo->requestData1;  // data stored within request
      irqRB.requestData2=isoInfo->requestData2;  // data stored within request
      irqRB.requestData3=isoInfo->requestData3;  // data stored within request
   }
   irqRB.buffer2=NULL;        // Virtual address of second data buffer.
   irqRB.buffer2Length=0;     // Buffer length in bytes
   irqRB.maxPacketSize=0;     // ignored for finished requests
   irqRB.maxErrorCount=0;     // current error count value
   irqRB.usbIDC=(PUSBIDCEntry)isoInfo->usbIDC;        // Address of IRQ processing routine to be called for this request
   irqRB.usbDS=isoInfo->usbDS; // ; set data segment value
   irqRB.category=isoInfo->category; // I/O issuer category

   setmem((PSZ)&rpIRQ, 0, sizeof(rpIRQ));
   rpIRQ.rph.Cmd=CMDGenIOCTL;   // IOCTL
   if (!notifyData)
      rpIRQ.rph.Status=USB_IDC_RC_CANCELED;
   else
      rpIRQ.rph.Status=USB_IDC_RC_OK;

   // call IRQ extension routine to finish 'request ended' processing
   rpIRQ.Category=irqRB.category;
   rpIRQ.Function=USB_IDC_FUNCTION_PRCIRQ;
   rpIRQ.ParmPacket=(PVOID)&irqRB;
   USBCallIDC( irqRB.usbIDC, irqRB.usbDS, (RP_GENIOCTL FAR *)&rpIRQ );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CancelIsoRequests                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Cancel Isohronous I/O requests                  */
/*                                                                    */
/* FUNCTION:  This routine is called to cancel isohronous I/O         */
/*            requests for specific device address (in case of 0      */
/*            device address all the active isohronous requests are   */
/*            canceled).                                              */
/*                                                                    */
/* NOTES: Interrupts are disabled during host schedule update         */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  CancelIsoRequests                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  UCHAR deviceAddress - device address                       */
/*         UCHAR endPointId - device endpoint ID (use                 */
/*                            USBCANCEL_CANCEL_ALL to cancel requests */
/*                            for all device endpoints)               */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:          none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
VOID CancelIsoRequests(UCHAR deviceAddress, UCHAR endPointId)
{
   ISOINFO FAR    *isoInfo;
   USHORT         frameIndex;

   for (isoInfo=gFirstIso; isoInfo; isoInfo=isoInfo->next)
   {
      if (isoInfo->deviceAddress!=deviceAddress && deviceAddress)
         continue;
      if (isoInfo->endPointId!=endPointId && deviceAddress!=USBCANCEL_CANCEL_ALL)
         continue;

      // disable all TD's
      for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
      {
         isoInfo->td[frameIndex].ctrlStat&=~TD_CTRLSTAT_STATUS_ACTIVE;
         isoInfo->td[frameIndex].ctrlStat&=~TD_CTRLSTAT_IOC;
      }

      // clear buffer information
      isoInfo->currBuffCount=0;
      isoInfo->activeBuffIndex=0;
      isoInfo->lastUsedFrame=UHCI_UNUSED_FRAMEINDEX;

      IsoSendNotification(isoInfo, NULL);
   }
}

