/* SCCSID = "src/dev/usb/USBD/USBIRQ.C, usb, c.basedd, currbld 98/07/10" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1997, 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  USBIRQ.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  USB stack IRQ processing extension routines.          */
/*                                                                            */
/*   FUNCTION: These routines handle IRQ processing for locally initiated     */
/*				   I/O requests (to process device attachments/detachments and    */
/*             HUB Class service requests). Processing starts when host       */
/*             controller is reset consists of the following steps:           */
/*             1) USB device descriptor retrieving from device at address 0;  */
/*             2) device address assignment;                                  */
/*             3) reading device configuration length;                        */
/*             4) reading complete configuration data;                        */
/*             5) all non-hub device configuration data are passed to class   */
/*                drivers to continue initialization;                         */
/*             6) hub device configuration data reading;                      */
/*             7) hub is configured to its 1st configuration given in         */
/*                descriptor;                                                 */
/*             8) all hub ports are switched on if necessary;                 */
/*             9) 'status changed' pipe is opened;                            */
/*            10) port status information reading for all ports reported      */
/*                status changed;                                             */
/*            11) detached devices are deleted from USB stack;                */
/*            12) ports with devices attached are reset and enabled;          */
/*                                                                            */
/*   NOTES:                                                                   */
/*             HUB services ensures that only single I/O request  for         */
/*             default device address is active.                              */
/*                                                                            */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             USBProcessIRQ       IRQ processing routine switch              */
/*             ClearUSBDStalledPipe                                           */
/*             GetDescCompleted                                               */
/*             SetAddrCompleted                                               */
/*             GetConfCompleted                                               */
/*             SetConfCompleted                                               */
/*             GetConfLenCompleted                                            */
/*             GetHConfLenCompleted                                           */
/*             GetHConfCompleted                                              */
/*             HubStatusChanged                                               */
/*             HubPortStatusRetrieved                                         */
/*             PortStatusChanged                                              */
/*             ExtConfSet                                                     */
/*             ExtIntfaceSet                                                  */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/01/16  MB              Original developer.                     */
/*          98/08/14  MB              Modified to read in all the             */
/*                                    configuration data                      */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "usb.h"

static void SuppressProcessing( ULONG eventId, ULONG waitTime );
static void CreateDeviceName ( PSZ template, PSZ name, UCHAR ctrlId, UCHAR parentHubAddr, UCHAR deviceAddress );
static void DetachHubPortDevices( UCHAR controllerId, USHORT parentHubIndex, UCHAR portNum );
static void FreeDeviceEntry( USHORT deviceIndex );
static void FreeHubEntry( USHORT hubIndex );
static void DetachDevice( UCHAR controllerId, USHORT parentHubIndex, UCHAR portNum );
static void DetachSingle(USHORT deviceIndex, BOOL dontCallClassDrv);

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  USBProcessIRQ                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  local I/O request IRQ processor                 */
/*                                                                    */
/* FUNCTION:  This routine is processes all completed USBD local      */
/*            device enumeration I/O requests - getting device        */
/*            descriptor, setting device address, reading             */
/*            configuration data. Particularly for HUB devices        */
/*            it reads in hub configuration data, selects hub         */
/*            configuration, reads status changed pipe and processes  */
/*            hub port status changes, starts device attachment/      */
/*            detachment process. Local requests ended with stalled   */
/*            flag set are cleared up by reseting corresponding pipe. */
/*            USBProcessIRQ stops processing for all internal USBD    */
/*            cancelled requests.                                     */
/*                                                                    */
/* NOTES:     USBProcessIRQ is called from HCD layer to finish IRQ    */
/*            processing                                              */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  USBProcessIRQ                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  ClearUSBDStalledPipe                         */
/*    ROUTINES:          GetDescCompleted                             */
/*                       SetAddrCompleted                             */
/*                       GetConfCompleted                             */
/*                       SetConfCompleted                             */
/*                       GetConfLenCompleted                          */
/*                       GetHConfLenCompleted                         */
/*                       GetHConfCompleted                            */
/*                       HubStatusChanged                             */
/*                       HubPortStatusRetrieved                       */
/*                       PortStatusChanged                            */
/*                       ExtConfSet                                   */
/*                       ExtIntfaceSet                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void USBProcessIRQ( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       irqSwitch;
   USHORT       deviceIndex, hubIndex;

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

#ifdef   DEBUG
   dsPrint4(DBG_HLVLFLOW, "USBD: USBProcessIRQ Switch %lx, r1=%lx, r2=%lx. Status=%x\r\n",
            processedRB->requestData1,processedRB->requestData2,processedRB->requestData3,pRP_GENIOCTL->rph.Status);
#endif

   if (processedRB->status&USRB_STATUS_STALLED)
      ClearUSBDStalledPipe(pRP_GENIOCTL);

   // discontinue processing for all canceled internal USBD requests
   switch (irqSwitch)
   {
   case USBD_IRQ_STATUS_SETDEVCONF: // external call - 'canceled' status will be processed by worker rtne
   case USBD_IRQ_STATUS_SETDEVINTF: // external call - 'canceled' status will be processed by worker rtne
      break;
   default:
      if (pRP_GENIOCTL->rph.Status==USB_IDC_RC_CANCELED)
         return;  // stop processing for canceled requests
      break;
   }

   // check device & hub indexes
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
   if (deviceIndex>=gNoOfDeviceDataEntries || (hubIndex>=gNoOfHubDataEntries && hubIndex!=HUB_NO_HUB_INDEX))
      return;

   // call required worker routine
   switch (irqSwitch)
   {
   case USBD_IRQ_STATUS_GETDESC:
      GetDescCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_SETADDR:
      SetAddrCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_GETCONF:
      GetConfCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_SETCONF:
      SetConfCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_GETCONFLEN:
      GetConfLenCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_GTHCONFL:
      GetHConfLenCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_GTHCONF:
      GetHConfCompleted( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_CHANGED:
      HubStatusChanged( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_GETPRTST:
      HubPortStatusRetrieved( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_PORTCHG:
      PortStatusChanged( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_SETDEVCONF:
      ExtConfSet( pRP_GENIOCTL );
      break;
   case USBD_IRQ_STATUS_SETDEVINTF:
      ExtIntfaceSet( pRP_GENIOCTL );
      break;
   default:
      break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ClearUSBDStalledPipe                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Clears USBD owned stalled communication pipe    */
/*                                                                    */
/* FUNCTION:  This routine creates I/O request to clear pipe stalled  */
/*            condition.                                              */
/*                                                                    */
/* NOTES:   This routine does not clear stalled condition for device  */
/*          on default address (0).                                   */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ClearUSBDStalledPipe                                */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*           USB_IDC_RC_ADDRINV - unknown device address (default     */
/*                                address also is treated as unkknown)*/
/*                                                                    */
/* INTERNAL REFERENCES:  ClearStalledPipe                             */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:          setmem                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void ClearUSBDStalledPipe( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR            *processedRB;
   RP_GENIOCTL          rp_USBReq;
   USBClearStalled      clearRB;
   USHORT               deviceIndex;

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

#ifdef   DEBUG
   dsPrint3(DBG_IRQFLOW, "USBD: ClearUSBDStalledPipe: clearing ctrlid=%d,address=%d,endpoint=%d\r\n",
            processedRB->controllerId, processedRB->deviceAddress, processedRB->endPointId);
#endif

   clearRB.controllerId=processedRB->controllerId;    // controller ID
   clearRB.deviceAddress=processedRB->deviceAddress;  // stalled USB device address
   clearRB.endPointId=processedRB->endPointId;        // stalled endpoint ID
   clearRB.clientIDCAddr=NULL;         // no irq notification for this request
   clearRB.clientDS=0;                 // no irq notification for this request
   clearRB.irqSwitchValue=0;           // no irq notification for this request
   clearRB.requestData2=0;             // no irq notification for this request
   clearRB.requestData3=0;             // no irq notification for this request
   clearRB.category=USB_IDC_CATEGORY_USBD;   // IRQ processor category
   clearRB.clearStalled=NULL;          // clear stalled pipe request packet buffer

   // find out device entry for stalled device and use its reservation for setup packet buffer
   for (deviceIndex=0; deviceIndex<gNoOfDeviceDataEntries; deviceIndex++)
   {
      if (gDevices[deviceIndex].ctrlID!=clearRB.controllerId)
         continue;
      if (gDevices[deviceIndex].deviceAddress==clearRB.deviceAddress)
      {  // set clear  packet buffer address
         clearRB.clearStalled=(SetupPacket FAR *)&gDevices[deviceIndex].clearStalled;
         break;
      }
   }

   if (!clearRB.clearStalled)  // buffer address not set -> device address unknown
   {
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ADDRINV;
      return;
   }

   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&clearRB;

   ClearStalledPipe( (RP_GENIOCTL FAR *)&rp_USBReq );

   if (rp_USBReq.rph.Status==USB_IDC_RC_OK)
      processedRB->status&=~USRB_STATUS_STALLED;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetDeviceDesc                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Device Descriptor retrieving routine            */
/*                                                                    */
/* FUNCTION:  This routine allocates new address and fills in new     */
/*            device entry, cancels all requests to device with       */
/*            default device address, issues I/O request to read in   */
/*            device descriptor data using default device address     */
/*            (address 0).                                            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetDeviceDesc                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*         UCHAR controllerId                                         */
/*         BOOL lowSpeedDevice                                        */
/*         USHORT parentHubIndex                                      */
/*         UCHAR portNum                                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*           USB_IDC_RC_ALLOCERR - 1) no space for new device entry;  */
/*                                 2) no new device address available;*/
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:          DefAddrFailed                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:          USBCallIDC                                   */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetDeviceDesc( RP_GENIOCTL FAR *pRP_GENIOCTL, UCHAR controllerId, BOOL lowSpeedDevice, USHORT parentHubIndex, UCHAR portNum )
{
   USBRB             hcdReqBlock;
   RP_GENIOCTL       rp_USBReq;
   UCHAR             devAddress;
   USHORT            deviceIndex;
   USBCancel         cancelIO;

   // detach all we have linked to this hub port
   DetachHubPortDevices( controllerId, parentHubIndex, portNum );

   // allocate new device address - get smallest unused address for current controller
   for (devAddress=1; devAddress<=USB_MAX_DEVICE_ADDRESS; devAddress++)
   {
      for (deviceIndex=0; deviceIndex<gNoOfDeviceDataEntries; deviceIndex++)
      {
         if (gDevices[deviceIndex].ctrlID!=controllerId)
            continue;
         if (gDevices[deviceIndex].deviceAddress==devAddress)
            break;
      }
      if (deviceIndex>=gNoOfDeviceDataEntries)
         break;
   }
   if (devAddress>USB_MAX_DEVICE_ADDRESS)
   {  // set error code if no more available device addresses
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ALLOCERR;
#ifdef   DEBUG
      dsPrint(DBG_CRITICAL, "USBD: GetDeviceDesc: - can't allocate new device address\r\n");
#endif
      return;
   }

   // get free entry in device table
   for (deviceIndex=0; deviceIndex<gNoOfDeviceDataEntries; deviceIndex++)
   {
      if (!gDevices[deviceIndex].deviceAddress)
         break;
   }
   if (deviceIndex+1>=gMaxDevices)
   {  // set error code if no space in device table for a new device
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_ALLOCERR;
#ifdef   DEBUG
      dsPrint(DBG_CRITICAL, "USBD: GetDeviceDesc: - can't allocate new device entry\r\n");
#endif
      return;
   }
   if (deviceIndex>=gNoOfDeviceDataEntries)
      gNoOfDeviceDataEntries=deviceIndex+1;

   // fill in device table entry
   gDevices[deviceIndex].ctrlID=controllerId;                  // controller ID
   gDevices[deviceIndex].deviceAddress=devAddress;             // device address
   gDevices[deviceIndex].bConfigurationValue=0;                // 0 - not configured
   gDevices[deviceIndex].bInterfaceNumber=0;                   // default interface
   gDevices[deviceIndex].lowSpeedDevice=(UCHAR)lowSpeedDevice; // low speed device flag
   gDevices[deviceIndex].parentHubIndex=parentHubIndex;        // index to parent hub in hub table
   gDevices[deviceIndex].portNum=portNum;                      // port number device is attached to
   gDevices[deviceIndex].rmDevHandle=NULL;                     // Resource Manager device handle
   // clear device descriptor
   setmem((PSZ)&gDevices[deviceIndex].descriptor,0, sizeof(gDevices[deviceIndex].descriptor));

#ifdef   DEBUG
   dsPrint3(DBG_DETAILED, "USBD: GetDeviceDesc: - new device address %d, index=%d, LowSpeed=%d\r\n",
            devAddress, deviceIndex, gDevices[deviceIndex].lowSpeedDevice);
   dsPrint2(DBG_DETAILED, "USBD: GetDeviceDesc: - ParentHubUndex=%d, PortNum=%d\r\n",
            gDevices[deviceIndex].parentHubIndex, gDevices[deviceIndex].portNum);
#endif

   // cancel all pending I/O request to device with default  address
   cancelIO.controllerId=controllerId;
   cancelIO.deviceAddress=USB_DEFAULT_DEV_ADDR;
   cancelIO.endPointId=USBCANCEL_CANCEL_ALL;

   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_CANCEL;
   rp_USBReq.ParmPacket=(PVOID)&cancelIO;
   USBCallIDC( gHostControllers[controllerId].usbIDC,
               gHostControllers[controllerId].usbDS, (RP_GENIOCTL FAR *)&rp_USBReq );  

   //  fill in request block
   hcdReqBlock.controllerId=gDevices[deviceIndex].ctrlID;
   hcdReqBlock.deviceAddress=USB_DEFAULT_DEV_ADDR;    // use default address to read data from unconfigured devices
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;     // 0, default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   if (lowSpeedDevice)
      hcdReqBlock.flags|=USRB_FLAGS_DET_LOWSPEED;
   hcdReqBlock.buffer1=(PUCHAR)&gGetDescriptor;       // setup packet address
   hcdReqBlock.buffer1Length=sizeof(gGetDescriptor);  // setup packet length
   hcdReqBlock.buffer2=(PUCHAR)&gDevices[deviceIndex].descriptor;        // device descriptor address
   hcdReqBlock.buffer2Length=sizeof(gDevices[deviceIndex].descriptor);   // descriptor length
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;      // Required service frequency in ms.
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE;    // maximum packet size to be used for specified endpoint
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;     // maximum error count allowed to complete request
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;          // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS();  // set data segment value of IRQ processing routine
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_GETDESC;  // USBD I/O call type ID - get descriptor
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,parentHubIndex);   // save device and parent hub indexes
   hcdReqBlock.requestData3=0;           // not used

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

#ifdef   DEBUG   
   dsPrint4(DBG_IRQFLOW, "USBD: GetDeviceDesc: CtrlID %d, LowSpeed %d,ParentHubIndex=%d,PortNum=%d\r\n", controllerId,
            lowSpeedDevice, parentHubIndex, portNum);
#endif

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {  // release device entry and stop processing if failed to get device descriptor
      DefAddrFailed( pRP_GENIOCTL );
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetDescCompleted                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Device decriptor retrieved routine              */
/*                                                                    */
/* FUNCTION:  This routine continues device enumeration process:      */
/*            checks device descriptor, starts address assigment      */
/*            I/O request.                                            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetDescCompleted                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:          DefAddrFailed                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetDescCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;
   USHORT       deviceIndex, hubIndex;

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

   // get device and hub indexes out of request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK || !gDevices[deviceIndex].descriptor.bLength)
   {  // release device entry and stop processing if failed to read device descriptor
#ifdef   DEBUG
      dsPrint4(DBG_CRITICAL, "USBD: GetDescCompleted: failed - CtrlID %d, a=%d, PHIndx=%d,Port%d\r\n",
               processedRB->controllerId, gDevices[deviceIndex].deviceAddress, hubIndex, gDevices[deviceIndex].portNum);
#endif
      DefAddrFailed( pRP_GENIOCTL );
      return;
   }

   // fill in request block to set device address
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=USB_DEFAULT_DEV_ADDR; // use default address for unconfigured device
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;  // 0, default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   if (gDevices[deviceIndex].lowSpeedDevice)
      hcdReqBlock.flags|=USRB_FLAGS_DET_LOWSPEED;
   hcdReqBlock.buffer1=(PUCHAR)&gSetAddress[hcdReqBlock.controllerId];       // pointer to set device address setup packet
   gSetAddress[hcdReqBlock.controllerId].wValue=gDevices[deviceIndex].deviceAddress; // set desired address in setup packet 
   hcdReqBlock.buffer1Length=sizeof(gSetAddress[hcdReqBlock.controllerId]); // setup packet size
   hcdReqBlock.buffer2=(PUCHAR)NULL;   // 2nd buffer is not used for set address request
   hcdReqBlock.buffer2Length=0;        // 2nd buffer is not used for set address request
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to set address
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to set address
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to set address
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_SETADDR;  // USBD I/O call type ID - set device address
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex); // save device and parent hub indexes
   hcdReqBlock.requestData3=0;                        // configuration data offset

   // clear configuration index and configuration data buffer
   gGetConfiguration[hcdReqBlock.controllerId].wValue=MAKEUSHORT(0,DESC_CONFIGURATION);
   setmem((PSZ)&gDevices[deviceIndex].configurationData,0,
          sizeof(gDevices[deviceIndex].configurationData));

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

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {  // release device entry and stop processing if failed to set address
      DefAddrFailed( pRP_GENIOCTL );
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SetAddrCompleted                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Device address assignment completed             */
/*                                                                    */
/* FUNCTION:  This routine continues device enumeration process:      */
/*            checks previous I/O operation return code, starts       */
/*            device descriptor data length retrieving process.       */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SetAddrCompleted                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:          DefAddrFailed                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void SetAddrCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;
   USHORT       deviceIndex, hubIndex, hubDeviceIndex;
   USHORT       status;
   USHORT       configurationOffset;

   processedRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   status=pRP_GENIOCTL->rph.Status;

   // get device and hub indexes from request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
   // get configuration data offset from USB request block
   configurationOffset=LOUSHORT(processedRB->requestData3);

#ifdef   DEBUG
   dsPrint4(DBG_IRQFLOW, "USBD: SetAddrCompleted - entered -dind=%d,hinf=%d,Status=%x, off %x\r\n",
            deviceIndex,hubIndex, status, configurationOffset);
#endif

   if (status!=USB_IDC_RC_OK)
   {  // failed to set device address - release entry in device table and stop processing
      DefAddrFailed( pRP_GENIOCTL );
#ifdef   DEBUG
      dsPrint(DBG_CRITICAL, "USBD: SetAddrCompleted - failed to set device address\r\n");
#endif
      return;
   }
   else
   {
      if (hubIndex!=HUB_NO_HUB_INDEX &&
          !configurationOffset)  // 21/09/98 MB
      {
         // continue parent hub status changed processing
         pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;
         hubDeviceIndex=gHubs[hubIndex].deviceIndex;
         processedRB->requestData2=MAKEULONG(hubDeviceIndex,hubIndex);
         gHubs[hubIndex].defAddressInUse=FALSE;
         HubStatusChanged( pRP_GENIOCTL );  // continue hub status change processing
      }
   }

   // fill in request block to retrieve device configuration data length
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   gGetConfiguration[hcdReqBlock.controllerId].wLength=4;   // retrieve only total descriptor length field
   hcdReqBlock.buffer1=(PUCHAR)&gGetConfiguration[hcdReqBlock.controllerId];       // pointer to get configuration setup packet
   hcdReqBlock.buffer1Length=sizeof(gGetConfiguration[hcdReqBlock.controllerId]);  // size of setup packet
   hcdReqBlock.buffer2=(PUCHAR)&gDevices[deviceIndex].configurationData+configurationOffset;   // pointer to configuration data buffer
   hcdReqBlock.buffer2Length=4;  // read only 1st 4 bytes to get total descriptor length
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service interval to get descriptor length
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to get descriptor length
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to get descriptor length
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;           // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_GETCONFLEN;  // USBD I/O call type ID - get configuration length
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and parent hub indexes
   hcdReqBlock.requestData3=MAKEULONG(configurationOffset,0);  //  configuration data offset

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

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DefAddrFailed                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Default Address operation failed                */
/*                                                                    */
/* FUNCTION:  This routine is called to process error situations for  */
/*            operations on default device address to:                */
/*            1) disable port device is attached to;                  */
/*            2) free entry in device table;                          */
/*            3) continue parent hub processing                       */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  DefAddrFailed                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void DefAddrFailed( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex, hubDeviceIndex;
   USHORT       status;

   processedRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   status=pRP_GENIOCTL->rph.Status;
   pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;

   // get device and hub indexes from request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
#ifdef   DEBUG
   dsPrint2(DBG_IRQFLOW, "USBD: DefAddrFailed: - DevIndex=%d,HubIndex=%d\r\n",deviceIndex, hubIndex);
#endif

   // free device entry
   FreeDeviceEntry( deviceIndex );

   // disable hub port
   if (hubIndex!=HUB_NO_HUB_INDEX)
   {
      hubDeviceIndex=gHubs[hubIndex].deviceIndex;
      processedRB->requestData2=MAKEULONG(hubDeviceIndex,hubIndex);

      gHubs[hubIndex].defAddressInUse=FALSE;   // free default address
#ifdef   DEBUG
      dsPrint3(DBG_IRQFLOW, "USBD: DefAddrFailed: - SetPort DevIndex=%d,HubIndex=%d, Port=%d\r\n",
               hubDeviceIndex, hubIndex, gHubs[hubIndex].portNum);
#endif
      if (!gHubs[hubIndex].deviceAddress)  // hub device detached
         return;

      // reset current port to start enumeration once more
      HubSetPortFeature( pRP_GENIOCTL, PORT_RESET, 0 );

      // continue with parent hub status change processing
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;
      HubStatusChanged( pRP_GENIOCTL );  // continue hub status change processing
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetConfLenCompleted                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Configuration Length completed              */
/*                                                                    */
/* FUNCTION:  This routine continues device enumeration process:      */
/*            checks previous I/O operation return code, starts       */
/*            device descriptor retrieving process.                   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetConfLenCompleted                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetConfLenCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;
   USHORT       deviceIndex, hubIndex;
   USHORT       configurationOffset;
   DeviceConfiguration  *devConf;

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

   // get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
   // get configuration data offset from USB request block
   configurationOffset=LOUSHORT(processedRB->requestData3);

   devConf=(DeviceConfiguration *)(gDevices[deviceIndex].configurationData+configurationOffset);
   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK || !devConf->wTotalLength ||
       devConf->wTotalLength>(USHORT)(MAX_CONFIG_LENGTH-(USHORT)configurationOffset))
   {  // release device entry if failed to get configuration data or configuration is to long to process
      FreeDeviceEntry( deviceIndex );
#ifdef   DEBUG
      dsPrint1(DBG_CRITICAL, "USBD: GetConfLenCompleted: - failed to read configuration for DevIndex=%d\r\n",deviceIndex);
#endif
      return;
   }

#ifdef   DEBUG
   dsPrint1(DBG_DETAILED, "USBD: GetConfLenCompleted: - total configuration length=%d\r\n",devConf->wTotalLength);
#endif

   // fill in request block to retrieve device configuration data
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   gGetConfiguration[hcdReqBlock.controllerId].wLength=devConf->wTotalLength;    //  set current configuartion length
   hcdReqBlock.buffer1=(PUCHAR)&gGetConfiguration[hcdReqBlock.controllerId];     // pointer to get configuration setup packet
   hcdReqBlock.buffer1Length=sizeof(gGetConfiguration[hcdReqBlock.controllerId]);// size of setup packet
   hcdReqBlock.buffer2=(PUCHAR)&gDevices[deviceIndex].configurationData+configurationOffset;      // pointer to configuration data buffer
   hcdReqBlock.buffer2Length=gGetConfiguration[hcdReqBlock.controllerId].wLength; // configuration buffer length
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;    // use default service time to get device configuration
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE;  // use default packet size to get device configuration
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;   // use maximum error retry count to get device configuration
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;        // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_GETCONF;  // USBD I/O call type ID - get configuration data
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and parent hub indexes
   hcdReqBlock.requestData3=0;                        // not used

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

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetConfCompleted                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Device configuration retrieving completed       */
/*                                                                    */
/* FUNCTION:  This routine continues device enumeration process       */
/*            by analyzing previous I/O completion code and then      */
/*            proceeds separately for HUB devices and non-hub devices:*/
/*            1) for non-hub device GetConfCompleted calls all the    */
/*               registered Device Class drivers to inform them on    */
/*               new attached device; registers device with RM        */
/*            2) for hub devices GetConfCompleted initiates           */
/*               configuration selection process.                     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetConfCompleted                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*           USB_IDC_RC_EXCEEDSMAX - set if no more space in hub      */
/*                                   device table                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:          CreateDeviceName                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:          USBCallIDC                                   */
/*                       RMCreateDevice                               */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetConfCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR            *processedRB;
   USBRB                hcdReqBlock;
   RP_GENIOCTL          rp_USBReq;
   USHORT               deviceIndex, hubIndex, classIndex;
   USHORT               configurationOffset;
   DeviceConfiguration  *devConf;

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

   // get device adn hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
   // get configuration data offset from USB request block
   configurationOffset=LOUSHORT(processedRB->requestData3);

   devConf=(DeviceConfiguration *)(gDevices[deviceIndex].configurationData+configurationOffset);
   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK || devConf->bLength==0)
   {  // release device entry if failed to get configuration data and stop processing
      FreeDeviceEntry( deviceIndex );
#ifdef   DEBUG
      dsPrint1(DBG_CRITICAL, "USBD: GetConfCompleted: - failed to read configuration for DevIndex=%d\r\n",deviceIndex);
#endif
      return;
   }

   // 98/08/14 MB - added to process all configurations
   if ((UCHAR)(LOBYTE(gGetConfiguration[processedRB->controllerId].wValue)+1)!=
       gDevices[deviceIndex].descriptor.bNumConfigurations)
   {
      processedRB->requestData3=MAKEULONG(configurationOffset+devConf->wTotalLength,0);
      gGetConfiguration[processedRB->controllerId].wValue=
      MAKEUSHORT(LOBYTE(gGetConfiguration[processedRB->controllerId].wValue)+1,DESC_CONFIGURATION);
      SetAddrCompleted( pRP_GENIOCTL );   // initiate read configuration length request
      return;
   }

   devConf=(DeviceConfiguration *)gDevices[deviceIndex].configurationData;
   if (gDevices[deviceIndex].descriptor.bDeviceClass==DEV_CLASS_HUB)
   {  // process HUB device - add new entry in Hub data table  
      for (hubIndex=0; hubIndex<gNoOfHubDataEntries; hubIndex++)
      {
         if (!gHubs[hubIndex].deviceAddress)
            break;
      }
      if (hubIndex+1>=gMaxHubDevices)
      {  // no space in Hub data entry table - set error return code and return
         pRP_GENIOCTL->rph.Status=USB_IDC_RC_EXCEEDSMAX;
         return;
      }
      if (hubIndex>=gNoOfHubDataEntries)
         gNoOfHubDataEntries=hubIndex+1;

      // fill in hub entry
      gHubs[hubIndex].ctrlID=processedRB->controllerId;                    // controller ID
      gHubs[hubIndex].deviceAddress=gDevices[deviceIndex].deviceAddress;   // hub device address
      gHubs[hubIndex].deviceIndex=deviceIndex;                             // index to hub device in device table
      gHubs[hubIndex].listeningStatusPipe=FALSE;                           // not listening status change pipe
      gHubs[hubIndex].defAddressInUse=FALSE;                               // not using default device address
      gHubs[hubIndex].defAddressUseDelayed=FALSE;                          // true if queued for default address use
#ifdef   DEBUG
      dsPrint2(DBG_IRQFLOW, "USBD: GetConfCompleted: - setting HubConf for deviceIndex=%d, hubIndex=%d\r\n",
               deviceIndex, hubIndex);
#endif
      // fill in request block to set configuration for hub class device
      hcdReqBlock.controllerId=processedRB->controllerId;
      hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
      hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
      hcdReqBlock.status=0;        // not used
      hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
      gSetConfiguration[hcdReqBlock.controllerId].wValue=devConf->bConfigurationValue; // use 1st configuration in conf data
      hcdReqBlock.buffer1=(PUCHAR)&gSetConfiguration[hcdReqBlock.controllerId];        // pointer to set configuration setup packet
      hcdReqBlock.buffer1Length=sizeof(gSetConfiguration[hcdReqBlock.controllerId]);   // setup packet size
      hcdReqBlock.buffer2=NULL;     // no additonal data to be sent to/from host
      hcdReqBlock.buffer2Length=0;  // to complete this request
      hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to set hub configuration
      hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to set hub configuration
      hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to set hub configuration
      hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
      hcdReqBlock.usbDS=GetDS(); // set data segment value
      hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
      hcdReqBlock.requestData1=USBD_IRQ_STATUS_SETCONF;  // USBD I/O call type ID - set hub configuration
      hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub indexes
      hcdReqBlock.requestData3=0;           // not used
      setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
      rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
      rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
      rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
      rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

      USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
      pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
   }
   else
   {
      // Call Class Device drivers to finish device setup and register device with RM
      UCHAR        ResourceBuf[ 12 ];
      PAHRESOURCE  pResourceList = (PAHRESOURCE)ResourceBuf;
      USBCDServe   classDriverInfo;
      APIRET       rc;
      UCHAR        deviceName[32];
      UCHAR        parentHubAddr;

      classDriverInfo.pDeviceInfo=(DeviceInfo FAR *)(gDevices+deviceIndex);

      setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
      rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
      rp_USBReq.Category=USB_IDC_CATEGORY_CLASS;
      rp_USBReq.Function=USB_IDC_FUNCTION_CHKSERV;
      rp_USBReq.ParmPacket=(PVOID)&classDriverInfo;
#ifdef   DEBUG
      if (!(gDevices[deviceIndex].descriptor.idVendor==0x046a &&   // ignore Cherry G80-3000 as kbd
            gDevices[deviceIndex].descriptor.idProduct==0x0001 &&  // to use it
            gDevices[deviceIndex].descriptor.bcdDevice==0x0100))   // only as hub
#endif
         for (classIndex=0; classIndex<gNoOfRegisteredClassDrvs; classIndex++)
         {
            USBCallIDC( gClassDrivers[classIndex].usbIDC,
                        gClassDrivers[classIndex].usbDS, (RP_GENIOCTL FAR *)&rp_USBReq );
         }

      // crete RM device with device address dependent name
      setmem((PSZ)&ResourceBuf, 0, sizeof(ResourceBuf));
      pResourceList->NumResource = 0;
      if (gDevices[deviceIndex].parentHubIndex==HUB_NO_HUB_INDEX)
         parentHubAddr=0;
      else
         parentHubAddr=gHubs[gDevices[deviceIndex].parentHubIndex].deviceAddress;
      CreateDeviceName ( deviceName, gUSBDeviceName, gDevices[deviceIndex].ctrlID,
                         parentHubAddr, gDevices[deviceIndex].deviceAddress );
      gDeviceStruct[processedRB->controllerId].DevDescriptName=deviceName;

      rc=RMCreateDevice(gHostControllers[processedRB->controllerId].hDriver,
                        &gDevices[deviceIndex].rmDevHandle,
                        &gDeviceStruct[processedRB->controllerId],
                        gHostControllers[processedRB->controllerId].hAdapter,
                        pResourceList);

#ifdef   DEBUG
      if (rc)
         dsPrint1(DBG_IRQFLOW, "USBD: GetConfCompleted - failed to add device - RM code -%x\r\n", rc);
#endif
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CreateDeviceName                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Create device name                              */
/*                                                                    */
/* FUNCTION:  This routine creates device name used in RM calls       */
/*            based on name template, controller ID, hub address and  */
/*            device address.                                         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   CreateDeviceName                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PSZ template - name template address                       */
/*         PSZ name - created name                                    */
/*         UCHAR ctrlId - contreller ID                               */
/*         UCHAR parentHubAddr - parent hub adress                    */
/*         UCHAR deviceAddress - device address                       */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  ConvertCharToStr                             */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void CreateDeviceName ( PSZ template, PSZ name, UCHAR ctrlId, UCHAR parentHubAddr, UCHAR deviceAddress )
{
   for (;*name; template++, name++)
      *template=*name;

   ConvertCharToStr( ctrlId, template );
   while ( *(++template) );
   *template='.'; template++;
   ConvertCharToStr( parentHubAddr, template );
   while ( *(++template) );
   *template='.'; template++;
   ConvertCharToStr( deviceAddress, template );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SetConfCompleted                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Set HUB device configuration completed          */
/*                                                                    */
/* FUNCTION:  This routine continues HUB device setup process:        */
/*            checks previous I/O return code and initiates hub class */
/*            configuration data retrieving process.                  */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SetConfCompleted                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void SetConfCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;
   USHORT       deviceIndex, hubIndex;

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

   // get device index from address set configuration request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {  // failed to set device configuration - release entries in device and hub tables
      FreeDeviceEntry( deviceIndex );
      FreeHubEntry( hubIndex );
#ifdef   DEBUG
      dsPrint3(DBG_CRITICAL, "USBD: SetConfCompleted: - failed to set hub configuration DeviceIndex=%d, HUBIndex=%d,Configuration value=%d\r\n",\
               deviceIndex, hubIndex, gSetConfiguration[processedRB->controllerId].wValue);
#endif
      return;
   }

   // save hub device configuration value
   gDevices[deviceIndex].bConfigurationValue=(UCHAR)gSetConfiguration[processedRB->controllerId].wValue;

   // fill in request block to get hub configuration data length for hub class device
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // 0, default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   setmem((PSZ)&gHubs[hubIndex].hubDescriptor, 0, sizeof(gHubs[hubIndex].hubDescriptor));
   hcdReqBlock.buffer1=(PUCHAR)&gGetHubConfigurationLength[hcdReqBlock.controllerId];       // pointer to get configuration setup packet
   hcdReqBlock.buffer1Length=sizeof(gGetHubConfigurationLength[hcdReqBlock.controllerId]);  // setup packet size
   hcdReqBlock.buffer2=(PUCHAR)&gHubs[hubIndex].hubDescriptor;       // pointer to configuration buffer
   hcdReqBlock.buffer2Length=gGetHubConfigurationLength[hcdReqBlock.controllerId].wLength;  // configuration buffer size - only to read in descriptor length
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to get hub configuration length
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to get hub configuration length
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to get hub configuration length
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set current (USBD) layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_GTHCONFL; // USBD I/O call type ID - get hub configuration data length
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub indexes
   hcdReqBlock.requestData3=0;   // not used
   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetHConfLenCompleted                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Hub device configuration completed          */
/*                                                                    */
/* FUNCTION:  This routine continues HUB device setup process:        */
/*            checks previous I/O return code and initiates hub class */
/*            configuration data retrieving process.                  */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetHConfLenCompleted                                */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetHConfLenCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;
   USHORT       deviceIndex, hubIndex;
   UCHAR        descriptorLength;

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

   // get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

   descriptorLength=gHubs[hubIndex].hubDescriptor.bLength;
   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK && !descriptorLength)
   {  // failed to get hub configuration - release entries in device and hub tables
      FreeDeviceEntry( deviceIndex );
      FreeHubEntry( hubIndex );
#ifdef   DEBUG
      dsPrint(DBG_CRITICAL, "USBD: GetHConfLenCompleted - failed to get hub configuration length\r\n");
#endif
      return;
   }
#ifdef   DEBUG
   dsPrint2(DBG_IRQFLOW, "USBD: GetHConfLenCompleted -  hub configuration length=%d, a=%d\r\n",
            descriptorLength, gDevices[deviceIndex].deviceAddress);
#endif

   // clear number of powerd on ports
   gHubs[hubIndex].poweredOnPorts=0;

   // fill in request block to get hub configuration data for hub class device
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   setmem((PSZ)&gHubs[hubIndex].hubDescriptor, 0, sizeof(gHubs[hubIndex].hubDescriptor));
   gHubs[hubIndex].getPortStatus.bmRequestType=REQTYPE_XFERDIR_DEVTOHOST|REQTYPE_TYPE_STANDARD|REQTYPE_TYPE_CLASS;    // Characteristics of request
   gHubs[hubIndex].getPortStatus.bRequest=REQ_GET_DESCRIPTOR;         // get hub descriptor request
   gHubs[hubIndex].getPortStatus.wValue=MAKEUSHORT(0, DESC_HUB);      // hub descriptor
   gHubs[hubIndex].getPortStatus.wIndex=0;  // 
   gHubs[hubIndex].getPortStatus.wLength=descriptorLength;  // status block size
   hcdReqBlock.buffer1=(PUCHAR)&gHubs[hubIndex].getPortStatus;       // pointer to get status setup packet
   hcdReqBlock.buffer1Length=sizeof(gHubs[hubIndex].getPortStatus);  // setup packet size
   hcdReqBlock.buffer2=(PUCHAR)&gHubs[hubIndex].hubDescriptor;       // pointer to configuration buffer
   hcdReqBlock.buffer2Length=descriptorLength;  // configuration descriptor buffer size size
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to get hub configuration length
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to get hub configuration length
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to get hub configuration length
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_GTHCONF;  // USBD I/O call type ID - get hub configuration data
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub indexes
   hcdReqBlock.requestData3=0;           // not used
   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetHConfCompleted                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Retrieving Hub configuration completed          */
/*                                                                    */
/* FUNCTION:  This routine finishes Hub configuration:                */
/*            checks previous I/O return code, powers on hub ports,   */
/*            registers new Hub as RM device and starts listening     */
/*            'Status Changed' pipe.                                  */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetHConfCompleted                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  ListenStatusChangedPipe                      */
/*    ROUTINES:          HubSetPortFeature                            */
/*                       CreateDeviceName                             */
/*                       SuppressProcessing                           */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMCreateDevice                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetHConfCompleted( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex;
   APIRET       rc;
   UCHAR        ResourceBuf[ 12 ];
   UCHAR        deviceName[32];
   PSZ          nameTemplate;
   PAHRESOURCE  pResourceList = (PAHRESOURCE)ResourceBuf;
   struct   HubConfiguration
   {
      DeviceConfiguration  configuration;
      DeviceInterface      interface;
      DeviceEndpoint       endPoint[1];
   }           *hubConfiguration;
   UCHAR       endPointIndex;
   UCHAR       parentHubAddr;

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

   // get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK && !gHubs[hubIndex].hubDescriptor.bLength)
   {  // failed to get hub configuration - release entries in device and hub tables
      FreeDeviceEntry( deviceIndex );
      FreeHubEntry( hubIndex );
#ifdef   DEBUG
      dsPrint(DBG_CRITICAL, "USBD: GetHConfCompleted - failed to retrieve hub configuration\r\n");
#endif
      return;
   }

#ifdef   DEBUG
   dsPrint2(DBG_DETAILED, "USBD: GetHConfCompleted - numports %d, poweredon=%d\r\n",
            gHubs[hubIndex].hubDescriptor.bNbrPorts, gHubs[hubIndex].poweredOnPorts);
#endif
   // switch on all the hub ports
   if (gHubs[hubIndex].poweredOnPorts<gHubs[hubIndex].hubDescriptor.bNbrPorts)
   {
      switch (gHubs[hubIndex].hubDescriptor.wHubCharacteristics&HUB_DESC_CHAR_POWER_MASK)
      {
      case HUB_DESC_CHAR_POWER_GANGED:
      case HUB_DESC_CHAR_POWER_INDIV:
         gHubs[hubIndex].poweredOnPorts++;
         gHubs[hubIndex].portNum=(UCHAR)gHubs[hubIndex].poweredOnPorts;
         HubSetPortFeature( pRP_GENIOCTL, PORT_POWER, USBD_IRQ_STATUS_GTHCONF );
         return;
      default: // all ports are powered on when is hub powered on
         gHubs[hubIndex].poweredOnPorts=gHubs[hubIndex].hubDescriptor.bNbrPorts;
         break;
      }
   }

   if (gHubs[hubIndex].poweredOnPorts==gHubs[hubIndex].hubDescriptor.bNbrPorts)
   {  // delay processing to stabilize power supply
      ULONG    powerOnDelay;

      powerOnDelay=USB_POWER_ON_DELAYT2+gHubs[hubIndex].hubDescriptor.bPwrOn2PwrGood*2;
#ifdef   DEBUG
      dsPrint1(DBG_DETAILED, "USBD: GetHConfCompleted: delaying for %ld ms\r\n",
               powerOnDelay);
#endif
      SuppressProcessing( (ULONG)(HubInfo FAR *)&gHubs[hubIndex],powerOnDelay );
      gHubs[hubIndex].poweredOnPorts++;
   }

   // get hub 'Status Changed pipe' address
   hubConfiguration=(struct HubConfiguration *)gDevices[deviceIndex].configurationData;
   for (endPointIndex=0; endPointIndex<hubConfiguration->interface.bNumEndpoints; endPointIndex++)
   {
      if ((hubConfiguration->endPoint[endPointIndex].bmAttributes&DEV_ENDPT_ATTRMASK)!=DEV_ENDPT_INTRPT)
         continue;
      gHubs[hubIndex].statusPipeID=(UCHAR)(hubConfiguration->endPoint[endPointIndex].bEndpointAddress&DEV_ENDPT_ADDRMASK);
      break;
   }

   // build new device name to be used by RM
   pResourceList->NumResource = 0;
   if (gDevices[deviceIndex].deviceAddress==1)
      nameTemplate=gRootHubDeviceName;
   else
      nameTemplate=gHubDeviceName;
   if (gDevices[deviceIndex].parentHubIndex==HUB_NO_HUB_INDEX)
      parentHubAddr=0;
   else
      parentHubAddr=gHubs[gDevices[deviceIndex].parentHubIndex].deviceAddress;

   CreateDeviceName ( deviceName, nameTemplate, gDevices[deviceIndex].ctrlID,
                      parentHubAddr, gDevices[deviceIndex].deviceAddress );
   gDeviceStruct[processedRB->controllerId].DevDescriptName=deviceName;

   rc=RMCreateDevice(ghDriver,
                     &gDevices[deviceIndex].rmDevHandle,
                     gDeviceStruct+processedRB->controllerId,
                     ghAdapter,
                     pResourceList);

#ifdef   DEBUG
   if (rc)
      dsPrint1(DBG_CRITICAL, "USBD: GetHConfCompleted - failed to add hub device - RM code - %x\r\n", rc);
#endif

   ListenStatusChangedPipe( pRP_GENIOCTL );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SuppressProcessing                               */
/*                                                                    */
/* DESCRIPTIVE NAME:  Suppresses thread execution                     */
/*                                                                    */
/* FUNCTION:  This routine suppresses thread execution for speciefied */
/*            time period.                                            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SuppressProcessing                                  */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ULONG eventId - value used as identifier                   */
/*         ULONG waitTime - required wait time in ms                  */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_ProcBlock                            */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void SuppressProcessing( ULONG eventId, ULONG waitTime )
{
   USHORT   rc, repeatCount;

   for (repeatCount=0; repeatCount<PROCBLOCK_RETRIES; repeatCount++)
   {
      CLISave();
      rc=DevHelp_ProcBlock(eventId,waitTime,WAIT_IS_INTERRUPTABLE);
      if (rc==WAIT_TIMED_OUT)
         break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ListenStatusChangedPipe                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  Starts listening in Status Changed pipe         */
/*                                                                    */
/* FUNCTION:  This routine starts listening 'Status Changed' pipe.    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ListenStatusChangedPipe                             */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void ListenStatusChangedPipe( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;
   UCHAR        byteCountInStatusBitmap;

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

   // get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

   if (gHubs[hubIndex].listeningStatusPipe)   // pipe already opened
      return;

   byteCountInStatusBitmap=(UCHAR)((UCHAR)((gHubs[hubIndex].hubDescriptor.bNbrPorts+1)>>3)+1);
   setmem((PSZ)&gHubs[hubIndex].portChangeBitmap, 0, byteCountInStatusBitmap);

   // fill in request block to start listening to 'Status Changed' pipe
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gHubs[hubIndex].deviceAddress;
   hcdReqBlock.endPointId=gHubs[hubIndex].statusPipeID;    // use hub status changed interrupt pipe ID
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_IN|USRB_FLAGS_DET_INTRPT;
   hcdReqBlock.buffer1=(PUCHAR)&gHubs[hubIndex].portChangeBitmap;       // address of hub status changed buffer
   hcdReqBlock.buffer1Length=byteCountInStatusBitmap;  // status changed buffer length
   hcdReqBlock.buffer2=NULL;     // no additonal data to be sent to/from host
   hcdReqBlock.buffer2Length=0;  // to complete this request
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to read "status changed" data
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to read "status changed" data
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to read "status changed" data
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_CHANGED;  // USBD I/O call type ID - read hub status changed data
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub indexes
   hcdReqBlock.requestData3=0;           // index in hub device table to current hub
   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

#ifdef   DEBUG
   dsPrint3(DBG_IRQFLOW, "USBD: ListenStatusChangedPipe - deviceindex=%d,hubIndex=%d,bitmaplength=%d\r\n",
            deviceIndex,hubIndex,byteCountInStatusBitmap);
#endif
   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;

   gHubs[hubIndex].listeningStatusPipe= (pRP_GENIOCTL->rph.Status==USB_IDC_RC_OK);


#ifdef   DEBUG
   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
      dsPrint4(DBG_CRITICAL, "USBD: ListenStatusChangedPipe - failed devInd=%d,hInd=%d,bLen=%d, Status=%x\r\n",
               deviceIndex,hubIndex,byteCountInStatusBitmap,pRP_GENIOCTL->rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  HubStatusChanged                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Hub device port status changed                  */
/*                                                                    */
/* FUNCTION:  This routine is called when hub has detected status     */
/*            change in its downstream ports. Port status reading     */
/*            is initiated for a single per call port with reported   */
/*            changes. If all the ports has been processed it reopens */
/*            Status Changed pipe.                                    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  HubStatusChanged                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  ListenStatusChangedPipe                      */
/*    ROUTINES:          GetDeviceDesc                                */
/*                       GetPortStatus                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void HubStatusChanged( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex;
   UCHAR        byteCountInStatusBitmap, bitmapStatusIndex, portNum, bitMap;
   UCHAR        bitPos, bitMask, ctrlID;
   BOOL         lowSpeedDevice;

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

   //  get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

#ifdef   DEBUG
//   if(gHubs[hubIndex].listeningStatusPipe)
   dsPrint4(DBG_IRQFLOW, "USBD: HubStatusChanged entered. lstatus=%d,dindx=%d,hubind=%d,cbtmap=%x\r\n",
            gHubs[hubIndex].listeningStatusPipe,deviceIndex,hubIndex,
            gHubs[hubIndex].portChangeBitmap[0]);
#endif

   if (pRP_GENIOCTL->rph.Status==USB_IDC_RC_OK)
   {
      byteCountInStatusBitmap=(UCHAR)((UCHAR)((gHubs[hubIndex].hubDescriptor.bNbrPorts+1)>>3)+1);

      // Get port no to process
      portNum=0;
      for (bitmapStatusIndex=0; bitmapStatusIndex<byteCountInStatusBitmap; bitmapStatusIndex++)
      {
         bitMap=gHubs[hubIndex].portChangeBitmap[bitmapStatusIndex];
         bitMask=1;
         for (bitPos=0; bitPos<8; bitPos++)
         {
            if (bitMap&bitMask)
               break;
            portNum++;
            bitMask= (UCHAR)(bitMask<<1);
         }
         gHubs[hubIndex].portChangeBitmap[bitmapStatusIndex]=(UCHAR)(bitMap & ~bitMask);
         if (bitPos<8)
            break;
      }
      if (bitmapStatusIndex>=byteCountInStatusBitmap)
         portNum=0;
   }
   else
   {
      portNum=0;
#ifdef   DEBUG
      dsPrint2(DBG_CRITICAL, "USBD: HubStatusChanged failed. devindex=%d,hubind=%d\r\n",deviceIndex,hubIndex);
#endif
   }

   gHubs[hubIndex].portNum=portNum;
   gHubs[hubIndex].listeningStatusPipe=FALSE;

   if (!portNum)
   {
      if (hubIndex!=HUB_NO_HUB_INDEX && gHubs[hubIndex].deviceAddress &&
          pRP_GENIOCTL->rph.Status==USB_IDC_RC_OK)
      {
         processedRB->requestData2=MAKEULONG(deviceIndex,hubIndex);
         processedRB->requestData3=0;
         ListenStatusChangedPipe( pRP_GENIOCTL );  // re-create "Status Changed" pipe
      }

      ctrlID=gHubs[hubIndex].ctrlID;
      // find out unprocessed hub ports with changed status
      for (hubIndex=0; hubIndex<gNoOfHubDataEntries; hubIndex++)
      {
         if (!gHubs[hubIndex].deviceAddress)
            continue;
         if (gHubs[hubIndex].ctrlID!=ctrlID)
            continue;
         if (!gHubs[hubIndex].listeningStatusPipe && gHubs[hubIndex].portNum && gHubs[hubIndex].defAddressUseDelayed)
            break;
      }
      if (hubIndex<gNoOfHubDataEntries)
      {  //  process device attached to this port
         gHubs[hubIndex].defAddressInUse=TRUE;
         gHubs[hubIndex].defAddressUseDelayed=FALSE;
         lowSpeedDevice=(gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_LOW_SPEED)!=0;
         GetDeviceDesc( pRP_GENIOCTL, processedRB->controllerId, lowSpeedDevice,
                        hubIndex, gHubs[hubIndex].portNum );
      }
      return;
   }

   gHubs[hubIndex].resetRetries=0;     // reset completed retry count

   GetPortStatus( pRP_GENIOCTL ); // read port status
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetPortStatus                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get port status routine                         */
/*                                                                    */
/* FUNCTION:  This routine is called to read port status and status   */
/*            change data.                                            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetPortStatus                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void GetPortStatus( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;

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

   //  get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

#ifdef   DEBUG
   dsPrint3(DBG_IRQFLOW, "USBD: GetPortStatus entered. dindx=%d,hubind=%d,Port=%d\r\n",
            deviceIndex,hubIndex,gHubs[hubIndex].portNum);
#endif

   // fill in request block to read port status information
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   // fill in hub class get status block
   gHubs[hubIndex].getPortStatus.bmRequestType=REQTYPE_XFERDIR_DEVTOHOST|REQTYPE_TYPE_CLASS|REQTYPE_TYPE_HUBCLASS_PORT;    // Characteristics of request
   gHubs[hubIndex].getPortStatus.bRequest=REQ_GET_STATUS;         // get status request
   gHubs[hubIndex].getPortStatus.wValue=0;                        // not used for hub request
   gHubs[hubIndex].getPortStatus.wIndex=gHubs[hubIndex].portNum;  // port of interest
   gHubs[hubIndex].getPortStatus.wLength=sizeof(gHubs[hubIndex].portStatus);  // status block size
   hcdReqBlock.buffer1=(PUCHAR)&gHubs[hubIndex].getPortStatus;       // pointer to get status setup packet
   hcdReqBlock.buffer1Length=sizeof(gHubs[hubIndex].getPortStatus);  // setup packet size
   hcdReqBlock.buffer2=(PUCHAR)&gHubs[hubIndex].portStatus;          // status buffer address
   hcdReqBlock.buffer2Length=sizeof(gHubs[hubIndex].portStatus);     // status buffer length
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to get port status
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to get port status
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to get port status
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_GETPRTST; // USBD I/O call type ID - read port status information
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub indexes
   hcdReqBlock.requestData3=0;                        // not used
   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  HubPortStatusRetrieved                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  Hub port status information retrieved           */
/*                                                                    */
/* FUNCTION:  In case of error in status retrieving routine calls     */
/*            HubStatusChanged to continue processing of other hub    */
/*            ports. Othervise all changed properties are             */
/*            acknowledged and routine starts processing both device  */
/*            connected/disconnected port states.                     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  HubPortStatusRetrieved                              */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*                                                                    */
/* INTERNAL REFERENCES:  HubStatusChanged                             */
/*    ROUTINES:          HubAckStatusChanges                          */
/*                       HubSetPortFeature                            */
/*                       GetDeviceDesc                                */
/*                       DetachSingle                                 */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_ProcBlock                            */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void HubPortStatusRetrieved( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex, hIndex;
   BOOL         lowSpeedDevice;

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

   //  get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {  // failed to read port status information
#ifdef   DEBUG
      dsPrint4(DBG_CRITICAL, "USBD: HubPortStatusRetrieved Failed for Port%d, Status=%x,devIndx=%d, hubindx=%d\r\n",
               gHubs[hubIndex].portNum, pRP_GENIOCTL->rph.Status, deviceIndex, hubIndex);
#endif
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;// clear error code to bypass error processing in HubStatusChanged
      HubStatusChanged( pRP_GENIOCTL );      // continue hub status change processing
      return;
   }

#ifdef   DEBUG
   dsPrint3(DBG_IRQFLOW, "USBD: HubPortStatusRetrieved Port%d Status=%x,PortChange=%x",
            gHubs[hubIndex].portNum, gHubs[hubIndex].portStatus.wPortStatus,gHubs[hubIndex].portStatus.wPortChange);
   dsPrint2(DBG_IRQFLOW, "(%x,%x)\r\n",
            gHubs[hubIndex].portStatus.wPortStatus,gHubs[hubIndex].portStatus.wPortChange);
#endif

   if (gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_CONNECTION)
   {  // if device is attached to port try to reset port turning it into "enable" status
      if (!(gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_OVER_CURRENT))
      {
         if (!(gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_ENABLE))
         {
            if (!(gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_RESET) && !gHubs[hubIndex].resetRetries)
            {
#ifdef   DEBUG
               dsPrint(DBG_IRQFLOW, "USBD: HubPortStatusRetrieved: resetting device\r\n");
#endif
               gHubs[hubIndex].resetRetries++;         
               HubSetPortFeature( pRP_GENIOCTL, PORT_RESET, USBD_IRQ_STATUS_PORTCHG );
               return;
            }
            else
            {  // wait until reset completes
#ifdef   DEBUG
               dsPrint(DBG_IRQFLOW, "USBD: HubPortStatusRetrieved: reading port status\r\n");
#endif
               gHubs[hubIndex].resetRetries++;
               if (gHubs[hubIndex].resetRetries<=HUB_RESET_RETRIES)
               {
                  GetPortStatus( pRP_GENIOCTL ); // read port status
               }
               else
                  HubStatusChanged( pRP_GENIOCTL );  // continue hub status change processing
               return;
            }
         }
      }
      else
      {
         DevHelp_Beep(2000, 1);  // over current detected
      }
   }

   if (gHubs[hubIndex].portStatus.wPortChange)
   {  // acknowledge all the changes
#ifdef   DEBUG
      dsPrint(DBG_IRQFLOW, "USBD: HubPortStatusRetrieved called to ack\r\n");
#endif
      HubAckStatusChanges( pRP_GENIOCTL );
      return;
   }

   if (gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_CONNECTION)
   {
      // device detected - read in its device descriptor
#ifdef   DEBUG
      dsPrint(DBG_IRQFLOW, "USBD: HubPortStatusRetrieved called to add new\r\n");
#endif

      // check whether default address (0) is in use
      for (hIndex=0; hIndex<gNoOfHubDataEntries; hIndex++)
      {
         if (!gHubs[hIndex].deviceAddress)
            continue;
         if (gHubs[hubIndex].ctrlID!=gHubs[hIndex].ctrlID)
            continue;
         if (gHubs[hIndex].defAddressInUse)
            break;
      }
      if (hIndex>=gNoOfHubDataEntries)
      {  //  default address is not used - start device setup
         gHubs[hubIndex].defAddressInUse=TRUE;
         lowSpeedDevice=(gHubs[hubIndex].portStatus.wPortStatus&STATUS_PORT_LOW_SPEED)!=0;
         SuppressProcessing( (ULONG)(HubInfo FAR *)&gHubs[hubIndex],HUB_RESET_WAITTIME );
         GetDeviceDesc( pRP_GENIOCTL, processedRB->controllerId, lowSpeedDevice,
                        hubIndex, gHubs[hubIndex].portNum );
      }
      else
         gHubs[hubIndex].defAddressUseDelayed=TRUE;
   }
   else
   {
      // no device attached to port - free all assoaciated I/O requests,
      // device/hub data entries and RM devices, notify class drivers
      DetachHubPortDevices( processedRB->controllerId, hubIndex, gHubs[hubIndex].portNum );

      pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;
      HubStatusChanged( pRP_GENIOCTL );  // continue hub status change processing 
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DetachHubPortDevices                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detach devices attached to given hub port       */
/*                                                                    */
/* FUNCTION:  This routine processes "device detached" request -      */
/*            deletes RM resources for current device and in case of  */
/*            hub devices deletes also connected to hub devices.      */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   DetachHubPortDevices                               */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  UCHAR controllerId - device controller ID                  */
/*         USHORT parentHubIndex - index to parent hub in hub table   */
/*         UCHAR portNum - port to be processed                       */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  DetachSingle                                 */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMDestroyDevice                              */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void DetachHubPortDevices( UCHAR controllerId, USHORT parentHubIndex, UCHAR portNum )
{
   DetachDevice( controllerId, parentHubIndex, portNum );

   // decrease number device entries if possible
   for (; gNoOfDeviceDataEntries>0; gNoOfDeviceDataEntries--)
   {
      if (gDevices[gNoOfDeviceDataEntries-1].deviceAddress)
         break;
   }
   // decrease number hub entries if possible
   for (; gNoOfHubDataEntries>0; gNoOfHubDataEntries--)
   {
      if (gHubs[gNoOfHubDataEntries-1].deviceAddress)
         break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  FreeDeviceEntry                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Free Device Data Entry                          */
/*                                                                    */
/* FUNCTION:  This routine processes releases device data entry by    */
/*            device address field to 0.                              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   FreeDeviceEntry                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  USHORT deviceIndex - index to device entry to be released  */
//*                                                                   */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  decreases index to last used device data entry           */
/*           in gNoOfDeviceDataEntries                                */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void FreeDeviceEntry( USHORT deviceIndex )
{
   if (deviceIndex<gNoOfDeviceDataEntries)
   {
      gDevices[deviceIndex].deviceAddress=0;
   }

   // decrease number device entries if possible
   for (; gNoOfDeviceDataEntries>0; gNoOfDeviceDataEntries--)
   {
      if (gDevices[gNoOfDeviceDataEntries-1].deviceAddress)
         break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  FreeHubEntry                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Free Device Data Entry                          */
/*                                                                    */
/* FUNCTION:  This routine processes releases hub device data entry   */
/*            by hub device address field to 0.                       */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   FreeHubEntry                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  USHORT hubIndex - index to hub device entry to be released */
//*                                                                   */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  decreases index to last used hub data entry              */
/*           in gNoOfHubDataEntries                                   */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void FreeHubEntry( USHORT hubIndex )
{
   if (hubIndex<gNoOfHubDataEntries)
   {
      gHubs[hubIndex].deviceAddress=0;
   }

   // decrease number hub entries if possible
   for (; gNoOfHubDataEntries>0; gNoOfHubDataEntries--)
   {
      if (gHubs[gNoOfHubDataEntries-1].deviceAddress)
         break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DetachDevice                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detach device                                   */
/*                                                                    */
/* FUNCTION:  This routine processes "device detached" request -      */
/*            deletes RM resources for current device and in case of  */
/*            hub devices deletes also connected to hub devices.      */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   DetachDevice                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  UCHAR controllerId - device controller ID                  */
/*         USHORT parentHubIndex - index to parent hub in hub table   */
/*         UCHAR portNum - port to be processed, if 0 routine         */
/*                         processes all hub ports                    */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  DetachSingle                                 */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMDestroyDevice                              */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void DetachDevice( UCHAR controllerId, USHORT parentHubIndex, UCHAR portNum )
{
   USHORT    deviceIndex, hubIndex;
   APIRET    rc;
   UCHAR     deviceAddress;

#ifdef   DEBUG
   dsPrint3(DBG_IRQFLOW, "USBD: DetachDevice controllerId %d, parentHubIndex %d, portNum%d\r\n",
            controllerId, parentHubIndex, portNum);
#endif
   for (deviceIndex=0; deviceIndex<gNoOfDeviceDataEntries; deviceIndex++)
   {
      deviceAddress=gDevices[deviceIndex].deviceAddress;
      if (!deviceAddress)
         continue;
      if (gDevices[deviceIndex].ctrlID!=controllerId)
         continue;
      if (gDevices[deviceIndex].parentHubIndex!=parentHubIndex)
         continue;
      if (portNum && gDevices[deviceIndex].portNum!=portNum)
         continue;
#ifdef   DEBUG
      rc=0;
#endif
      if (gDevices[deviceIndex].descriptor.bDeviceClass==DEV_CLASS_HUB)
      {
         DetachDevice( controllerId, deviceIndex, 0 );   // process all attached to this hub devices

         // delete hub data entry
         for (hubIndex=0; hubIndex<gNoOfHubDataEntries; hubIndex++)
         {
            if (gHubs[hubIndex].deviceAddress==deviceAddress)
            {
               if (gHubs[hubIndex].defAddressInUse)
               {   //   cancel pending default address request
                  USBCancel      cancelIO;
                  RP_GENIOCTL    rp_USBReq;

                  cancelIO.controllerId=controllerId;
                  cancelIO.deviceAddress=USB_DEFAULT_DEV_ADDR;
                  cancelIO.endPointId=USBCANCEL_CANCEL_ALL;

                  setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
                  rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
                  rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
                  rp_USBReq.Function=USB_IDC_FUNCTION_CANCEL;
                  rp_USBReq.ParmPacket=(PVOID)&cancelIO;
                  USBCallIDC( gHostControllers[controllerId].usbIDC,
                              gHostControllers[controllerId].usbDS, (RP_GENIOCTL FAR *)&rp_USBReq );  
               }
               FreeHubEntry( hubIndex );
               break;
            }
         }
         if (gDevices[deviceIndex].rmDevHandle)
            rc=RMDestroyDevice(ghDriver, gDevices[deviceIndex].rmDevHandle);
         gDevices[deviceIndex].rmDevHandle=NULL;
         DetachSingle(deviceIndex,TRUE);
      }
      else
      {
         if (gDevices[deviceIndex].rmDevHandle)
            rc=RMDestroyDevice(gHostControllers[controllerId].hDriver, gDevices[deviceIndex].rmDevHandle);
         gDevices[deviceIndex].rmDevHandle=NULL;
         DetachSingle(deviceIndex,FALSE);
      }

#ifdef   DEBUG
      if (rc)
         dsPrint1(DBG_CRITICAL, "USBD: RMDestroyDevice - failed to destroy device - RM code - %x\r\n", rc);
      dsPrint2(DBG_IRQFLOW, "USBD: DetachDevice devaddress %d, index %d\r\n", deviceAddress,deviceIndex);
#endif

      if (deviceIndex+1==gNoOfDeviceDataEntries)
         gNoOfDeviceDataEntries--;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DetachSingle                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detach single device                            */
/*                                                                    */
/* FUNCTION:  This routine calls all the registered class drivers     */
/*            to inform on detached device, free device entry and     */
/*            cancel all I/O requests to that device.                 */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   DetachSingle                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  USHORT deviceIndex - index in device table for device to   */
/*                              be processed                          */
/*         BOOL dontCallClassDrv - if TRUE suppresses Class driver    */
/*                                 calls (for HUB devices)            */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:          USBCallIDC                                   */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void DetachSingle(USHORT deviceIndex, BOOL dontCallClassDrv)
{
   USBDetach      detachDeviceData;
   USBCancel      cancelIO;
   RP_GENIOCTL    rp_USBReq;
   USHORT         classIndex;
   UCHAR          controllerId, deviceAddress;

   controllerId=gDevices[deviceIndex].ctrlID;
   deviceAddress=gDevices[deviceIndex].deviceAddress;
#ifdef   DEBUG
   dsPrint2(DBG_IRQFLOW, "USBD: DetachSingle controllerId %d, deviceAddress %d\r\n", controllerId,deviceAddress);
#endif

   // inform all the registered Class Device drivers that device has been detached
   detachDeviceData.controllerId=controllerId;
   detachDeviceData.deviceAddress=deviceAddress;

   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_CLASS;
   rp_USBReq.Function=USB_IDC_FUNCTION_DETDEV;
   rp_USBReq.ParmPacket=(PVOID)&detachDeviceData;

   if (!dontCallClassDrv)
      for (classIndex=0; classIndex<gNoOfRegisteredClassDrvs; classIndex++)
      {
#ifdef   DEBUG
         dsPrint1(DBG_DETAILED, "USBD: DetachSingle calling class driver %d\r\n", classIndex);
#endif
         USBCallIDC( gClassDrivers[classIndex].usbIDC,
                     gClassDrivers[classIndex].usbDS, (RP_GENIOCTL FAR *)&rp_USBReq );
      }

   FreeDeviceEntry( deviceIndex ); // delete device entry

   // cancel all I/O requests for device to be detached
   cancelIO.controllerId=controllerId;
   cancelIO.deviceAddress=deviceAddress;
   cancelIO.endPointId=USBCANCEL_CANCEL_ALL;

   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_CANCEL;
   rp_USBReq.ParmPacket=(PVOID)&cancelIO;

   USBCallIDC( gHostControllers[controllerId].usbIDC,
               gHostControllers[controllerId].usbDS, (RP_GENIOCTL FAR *)&rp_USBReq );

}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  HubAckStatusChanges                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Acknowledge hub port status changes             */
/*                                                                    */
/* FUNCTION:  This routine is called from HubPortStatusRetrieved      */
/*            repeatedely until all change flags are cleared (changes */
/*            are acknowledged).                                      */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   HubAckStatusChanges                                */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void HubAckStatusChanges( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex, featureSelector;
   UCHAR        portNum;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;

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

   // get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
   portNum=gHubs[hubIndex].portNum;

   // set feature selector value and clear corresponding flag bit in port change status word
   if (gHubs[hubIndex].portStatus.wPortChange&STATUS_PORT_CONNECTION)
   {
      featureSelector=C_PORT_CONNECTION;
      gHubs[hubIndex].portStatus.wPortChange&=~STATUS_PORT_CONNECTION;
   }
   else
      if (gHubs[hubIndex].portStatus.wPortChange&STATUS_PORT_ENABLE)
   {
      featureSelector=C_PORT_ENABLE;
      gHubs[hubIndex].portStatus.wPortChange&=~STATUS_PORT_ENABLE;
   }
   else
      if (gHubs[hubIndex].portStatus.wPortChange&STATUS_PORT_SUSPEND)
   {
      featureSelector=C_PORT_SUSPEND;
      gHubs[hubIndex].portStatus.wPortChange&=~STATUS_PORT_SUSPEND;
   }
   else
      if (gHubs[hubIndex].portStatus.wPortChange&STATUS_PORT_OVER_CURRENT)
   {
      featureSelector=C_PORT_OVER_CURRENT;
      gHubs[hubIndex].portStatus.wPortChange&=~STATUS_PORT_OVER_CURRENT;
   }
   else
      if (gHubs[hubIndex].portStatus.wPortChange&STATUS_PORT_RESET)
   {
      featureSelector=C_PORT_RESET;
      gHubs[hubIndex].portStatus.wPortChange&=~STATUS_PORT_RESET;
   }
   else
      return;

   // fill in request block to clear selected port feature
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   gHubs[hubIndex].getPortStatus.bmRequestType=REQTYPE_TYPE_CLASS|REQTYPE_TYPE_HUBCLASS_PORT;    // Characteristics of request
   gHubs[hubIndex].getPortStatus.bRequest=REQ_CLEAR_FEATURE;   // clear hub feature block
   gHubs[hubIndex].getPortStatus.wValue=featureSelector;       // clear feature selector
   gHubs[hubIndex].getPortStatus.wIndex=(USHORT)portNum;       // destination port number
   gHubs[hubIndex].getPortStatus.wLength=0;                    // no extra in/out data
   hcdReqBlock.buffer1=(PUCHAR)&gHubs[hubIndex].getPortStatus;       // pointer to clear feature setup packet
   hcdReqBlock.buffer1Length=sizeof(gHubs[hubIndex].getPortStatus);  // setup packet size
   hcdReqBlock.buffer2=NULL;     // no additonal data to be sent to/from host
   hcdReqBlock.buffer2Length=0;  // to complete this request
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to clear hub feature
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to clear hub feature
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to clear hub feature
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;       // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=USBD_IRQ_STATUS_PORTCHG; // USBD I/O call type ID - read port status information
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub indexes
   hcdReqBlock.requestData3=0;   // not used
   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  HubSetPortFeature                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Enable hub port features                        */
/*                                                                    */
/* FUNCTION:  This routine is called repeatedely from                 */
/*            HubPortStatusRetrieved to move port into 'enabled'      */
/*            status.                                                 */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   HubSetPortFeature                                  */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*         USHORT featureSelector                                     */
/*         ULONG irqSwitch                                            */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  USBAcceptIO                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  setmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void HubSetPortFeature( RP_GENIOCTL FAR *pRP_GENIOCTL, USHORT featureSelector, ULONG irqSwitch )
{
   USBRB FAR    *processedRB;
   USHORT       deviceIndex, hubIndex;
   UCHAR        portNum;
   USBRB        hcdReqBlock;
   RP_GENIOCTL  rp_USBReq;

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

   // get device and hub indexes from USB request block
   deviceIndex=LOUSHORT(processedRB->requestData2);
   hubIndex=HIUSHORT(processedRB->requestData2);
   portNum=gHubs[hubIndex].portNum;

   // fill in request block to set port feature
   hcdReqBlock.controllerId=processedRB->controllerId;
   hcdReqBlock.deviceAddress=gDevices[deviceIndex].deviceAddress;
   hcdReqBlock.endPointId=USB_DEFAULT_CTRL_ENDPT;    // default control endpoint
   hcdReqBlock.status=0;        // not used
   hcdReqBlock.flags=USRB_FLAGS_TTYPE_SETUP;
   gHubs[hubIndex].getPortStatus.bmRequestType=REQTYPE_TYPE_CLASS;    // Characteristics of request
   if (gHubs[hubIndex].portNum)   // if port specified send request to hub port
      gHubs[hubIndex].getPortStatus.bmRequestType|=REQTYPE_TYPE_HUBCLASS_PORT;
   gHubs[hubIndex].getPortStatus.bRequest=REQ_SET_FEATURE;  // set port(hub) feature request
   gHubs[hubIndex].getPortStatus.wValue=featureSelector;    // port(hub) feature selector
   gHubs[hubIndex].getPortStatus.wIndex=(USHORT)portNum;    // destination port number (0 for hub)
   gHubs[hubIndex].getPortStatus.wLength=0;                 // no additional data to be transferred
   hcdReqBlock.buffer1=(PUCHAR)&gHubs[hubIndex].getPortStatus;       // pointer to set port feature setup packet
   hcdReqBlock.buffer1Length=sizeof(gHubs[hubIndex].getPortStatus);  // setup packet size
   hcdReqBlock.buffer2=NULL;     // no additonal data to be sent to/from host
   hcdReqBlock.buffer2Length=0;  // to complete this request
   hcdReqBlock.serviceTime=USB_DEFAULT_SRV_INTV;   // use default service frequency to set hub feature
   hcdReqBlock.maxPacketSize=USB_DEFAULT_PKT_SIZE; // use default packet size to set hub feature
   hcdReqBlock.maxErrorCount=USB_MAX_ERROR_COUNT;  // use maximum error retry count to set hub feature
   hcdReqBlock.usbIDC=(PUSBIDCEntry)USBDidc;       // set USBD IDC routine to finish IRQ processing
   hcdReqBlock.usbDS=GetDS(); // set data segment value
   hcdReqBlock.category=USB_IDC_CATEGORY_USBD;        // set USBD layer as IRQ processor
   hcdReqBlock.requestData1=irqSwitch; // USBD I/O call type ID
   hcdReqBlock.requestData2=MAKEULONG(deviceIndex,hubIndex);   // save device and hub index
   hcdReqBlock.requestData3=0;   // not used
   setmem((PSZ)&rp_USBReq, 0, sizeof(rp_USBReq));
   rp_USBReq.rph.Cmd=CMDGenIOCTL;   // IOCTL
   rp_USBReq.Category=USB_IDC_CATEGORY_HOST;
   rp_USBReq.Function=USB_IDC_FUNCTION_ACCIO;
   rp_USBReq.ParmPacket=(PVOID)&hcdReqBlock;

   USBAcceptIO( (RP_GENIOCTL FAR *)&rp_USBReq );
   pRP_GENIOCTL->rph.Status=rp_USBReq.rph.Status;

#ifdef   DEBUG
   dsPrint3(DBG_IRQFLOW, "USBD: HubSetPortFeature - a=%d, Port%d, selector:%d",
            gDevices[deviceIndex].deviceAddress, gHubs[hubIndex].portNum,featureSelector);
   dsPrint2(DBG_IRQFLOW, ",wPortStatus=%x, Status=%x\r\n",
            gHubs[hubIndex].portStatus.wPortStatus, pRP_GENIOCTL->rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  PortStatusChanged                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Hub Port status change routine                  */
/*                                                                    */
/* FUNCTION:  This routine is called from IRQ processor to continue   */
/*            port status change processing.                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   PortStatusChanged                                  */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  HubStatusChanged                             */
/*    ROUTINES:          GetPortStatus                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void PortStatusChanged( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR    *processedRB;

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

   if (pRP_GENIOCTL->rph.Status!=USB_IDC_RC_OK)
   {  // failed to read port status information
#ifdef   DEBUG
      dsPrint2(DBG_CRITICAL, "USBD: PortStatusChanged failed. a=%d, Status=%x\r\n",
               processedRB->deviceAddress, pRP_GENIOCTL->rph.Status);
#endif
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;  // to bypass error processing in HubStatusChanged
      HubStatusChanged( pRP_GENIOCTL );  // continue hub status change processing
      return;
   }

   GetPortStatus( pRP_GENIOCTL );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ExtConfSet                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Configuration Set processing routine            */
/*                                                                    */
/* FUNCTION:  This routine informs Class drivers that required        */
/*            configuration is set.                                   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   ExtConfSet                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void ExtConfSet( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR               *processedRB;
   SetupPacket FAR         *setPacket;
   USHORT                  deviceIndex, classIndex;
   USHORT                  callersDS, irqSwitch;

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

   // get device index from request block
   deviceIndex=(USHORT)processedRB->requestData2;
   callersDS=LOUSHORT(processedRB->requestData3);
   irqSwitch=HIUSHORT(processedRB->requestData3);

   if (pRP_GENIOCTL->rph.Status==USB_IDC_RC_OK)
   {  // failed to read port status information
      setPacket=(SetupPacket FAR *)processedRB->buffer1;
      gDevices[deviceIndex].bConfigurationValue=(UCHAR)setPacket->wValue;

#ifdef   DEBUG
      dsPrint2(DBG_IRQFLOW, "USBD: ExtConfSet %d for deviceIndex=%d\r\n",
               gDevices[deviceIndex].bConfigurationValue, deviceIndex);
#endif
   }
   else
   {
      gDevices[deviceIndex].bConfigurationValue=0; // 23/09/98 MB - reset configuration value if failed
#ifdef   DEBUG
      dsPrint1(DBG_CRITICAL, "USBD: ExtConfSet Status=%x\r\n",pRP_GENIOCTL->rph.Status);
#endif
   }
   // get callers class driver IDC data (entry address and DS value)
   for (classIndex=0; classIndex<gNoOfRegisteredClassDrvs; classIndex++)
   {
      if (gClassDrivers[classIndex].usbDS==callersDS)
         break;
   }
   if (classIndex<gNoOfRegisteredClassDrvs)
   {  // call Class driver if identified
      pRP_GENIOCTL->Category=(UCHAR)HIUSHORT(processedRB->requestData1);
      processedRB->requestData1=irqSwitch;
      processedRB->requestData2=0;
      processedRB->requestData3=0;
      USBCallIDC( gClassDrivers[classIndex].usbIDC,
                  gClassDrivers[classIndex].usbDS, pRP_GENIOCTL );
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ExtIntfaceSet                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Interface Set processing routine                */
/*                                                                    */
/* FUNCTION:  This routine informs Class drivers that required        */
/*            interface is set.                                       */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :   ExtIntfaceSet                                      */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  none                                                 */
/*                                                                    */
/* EXIT-ERROR:  none                                                  */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void ExtIntfaceSet( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR         *processedRB;
   SetupPacket FAR   *setPacket;
   USHORT            deviceIndex, classIndex;
   USHORT            callersDS, irqSwitch;

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

   // get device index from request block
   deviceIndex=(USHORT)processedRB->requestData2;
   callersDS=LOUSHORT(processedRB->requestData3);
   irqSwitch=HIUSHORT(processedRB->requestData3);

   if (pRP_GENIOCTL->rph.Status==USB_IDC_RC_OK)
   {  // failed to read port status information
      setPacket=(SetupPacket FAR *)processedRB->buffer1;
      gDevices[deviceIndex].bInterfaceNumber=(UCHAR)setPacket->wValue;
   }

   // get callers class driver IDC data (entry address and DS value)
   for (classIndex=0; classIndex<gNoOfRegisteredClassDrvs; classIndex++)
   {
      if (gClassDrivers[classIndex].usbDS==callersDS)
         break;
   }
   if (classIndex<gNoOfRegisteredClassDrvs)
   {  // call Class driver if identified
      pRP_GENIOCTL->Category=(UCHAR)HIUSHORT(processedRB->requestData1);
      processedRB->requestData1=irqSwitch;
      processedRB->requestData2=0;
      processedRB->requestData3=0;
      USBCallIDC( gClassDrivers[classIndex].usbIDC,
                  gClassDrivers[classIndex].usbDS, pRP_GENIOCTL );
   }
}
