/* SCCSID = "src/dev/usb/UHCI/UHCIRHUB.C, usb, c.basedd 98/07/10" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1997, 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  UHCIRHUB.C                                            */
/*                                                                            */
/*   DESCRIPTIVE NAME:  Software USB Root Hub support routines                */
/*                                                                            */
/*   FUNCTION: These routines emulate root hub for UHCI compliant host        */
/*             controller.                                                    */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             UHCIRootHub                                                    */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/01/16  MB              Original developer.                     */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "uhci.h"

static void ProcessGetDescriptor( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void GetDeviceDescriptor( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessSetAddress( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessSetConfiguration( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void GetDeviceConfiguration( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessGetHubStatusCBits( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessGetHubDescriptor( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void GetHubConfiguration( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessGetHubStatus( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessClearHubFeature( RP_GENIOCTL FAR *pRP_GENIOCTL );
static void ProcessSetHubFeature( RP_GENIOCTL FAR *pRP_GENIOCTL );

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  AccRootHubReq                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Accept root hub request                         */
/*                                                                    */
/* FUNCTION:  This routine is accepts I/O request to root hub if      */
/*            addressed. Accepts only single request at a time.       */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  AccRootHubReq                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  stores request data in gRootHubRB and sets request       */
/*           not processed flag in gRootHubRP.rph.Status              */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void AccRootHubReq( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;
   USHORT             rootReqIndex;
   BOOL           allRequestsFinished=TRUE;

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

   if (gRootHubAddress && ioRB->deviceAddress!=gRootHubAddress && gRootHubConfig)
   {  // request for device other than root hub
      pRP_GENIOCTL->rph.Status&=~STATUS_DONE;
      return;
   }

   // ignore "clear stalled" request without notification
   if (!ioRB->usbIDC && !ioRB->usbDS && (ioRB->flags&~USRB_FLAGS_TTYPE_MASK)==USRB_FLAGS_TTYPE_SETUP)
   {
      setupRQ=(SetupPacket FAR *)ioRB->buffer1;
      if ((setupRQ->bmRequestType&REQTYPE_TYPE_MASK)==REQTYPE_TYPE_STANDARD &&
          setupRQ->bRequest==REQ_CLEAR_FEATURE &&
          setupRQ->wValue==ENDPOINT_STALL)
         return;
   }

   if (gRootReqStatus!=ROOT_HUB_NOREQ) // search for free request block
   {
      for (rootReqIndex=0; rootReqIndex<ROOT_MAX_REQ; rootReqIndex++)
      {
         if (!gRootHubRP[rootReqIndex].rph.Status)
            break;
      }
   }
   else
      rootReqIndex=0;

   if (rootReqIndex>=ROOT_MAX_REQ)
   {  // root hub request buffer contains unprocessed request - reject new request
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_IOFAILED;
#ifdef   DEBUG
      dsPrint(DBG_CRITICAL, "UHCI: AccRootHubReq - rejecting. Too many active requests.\r\n");
#endif
      return;
   }

   // accept root hub request
   movmem((PSZ)&gRootHubRB[rootReqIndex], (PSZ)ioRB, sizeof(gRootHubRB[rootReqIndex]));
   gRootHubRP[rootReqIndex].rph.Status=USB_IDC_RC_OK; // request block in use

   gRootReqStatus=ROOT_HUB_REQ;   // set request not processed

   DevHelp_ArmCtxHook( 0, gRHubHookHandle );
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIRootHub                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  software root hub emulation                     */
/*                                                                    */
/* FUNCTION:  This routine dispatches I/O requests to root hub        */
/*            request worker routines and calls IRQ extension         */
/*            routine to finish request processing.                   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  UHCIRootHub                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets return code in pRP_GENIOCTL->rph.Status             */
/*                                                                    */
/* INTERNAL REFERENCES:  ProcessGetDescriptor                         */
/*    ROUTINES:          ProcessSetAddress                            */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void UHCIRootHub( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;

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

   pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;

   ioRB->status=0; // clear request error status flags

   switch (ioRB->flags&~USRB_FLAGS_TTYPE_MASK)
   {
   case USRB_FLAGS_TTYPE_IN:
      ProcessGetHubStatusCBits(pRP_GENIOCTL);   // read hub status changed data
      break;
   case USRB_FLAGS_TTYPE_OUT:
      ioRB->status|=USRB_STATUS_STALLED;   // request not supported
      break;
   case USRB_FLAGS_TTYPE_SETUP:  // process setup requests
      setupRQ=(SetupPacket FAR *)ioRB->buffer1;

      if ((setupRQ->bmRequestType&REQTYPE_TYPE_MASK)==REQTYPE_TYPE_CLASS)
      {  // hub class specific requests
         switch (setupRQ->bRequest)
         {
         case REQ_GET_DESCRIPTOR:
            ProcessGetHubDescriptor(pRP_GENIOCTL);
            break;
         case REQ_GET_STATUS:
            ProcessGetHubStatus(pRP_GENIOCTL);
            break;

         case REQ_CLEAR_FEATURE:
            ProcessClearHubFeature( pRP_GENIOCTL );
            break;
         case REQ_SET_FEATURE:
            ProcessSetHubFeature( pRP_GENIOCTL );
            break;
         case REQ_SET_DESCRIPTOR:
         default:
            break;
         }
      }
      else
      {  // USB general requests
         switch (setupRQ->bRequest)
         {
         case REQ_GET_DESCRIPTOR:
            ProcessGetDescriptor(pRP_GENIOCTL);
            break;
         case REQ_SET_ADDRESS:
            ProcessSetAddress(pRP_GENIOCTL);
            break;
         case REQ_SET_CONFIGURATION:
            ProcessSetConfiguration(pRP_GENIOCTL);
            break;
         case REQ_CLEAR_FEATURE:
         case REQ_SET_FEATURE:
         case REQ_SET_DESCRIPTOR:
         case REQ_SET_INTERFACE:
         case REQ_SYNCH_FRAME:
            break;   // accept all unknown set/clear requests
         case REQ_GET_CONFIGURATION:
         case REQ_GET_STATUS:
         case REQ_GET_INTERFACE:
         default: // refuse unknown get requests
            ioRB->status|=USRB_STATUS_STALLED;   // request not supported
            ioRB->buffer2Length=0;
            break;
         }
      }
      break;
   default:
      break;
   }

   if (ioRB->status)  // error detected during processing
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_IOFAILED;

   // call IRQ extension routine to finish request processing
   if (pRP_GENIOCTL->rph.Status&STATUS_DONE)
   {
      RP_GENIOCTL    savedRP;

      movmem((PSZ)&savedRP, (PSZ)pRP_GENIOCTL, sizeof(savedRP));
      savedRP.Category=ioRB->category;
      savedRP.Function=USB_IDC_FUNCTION_PRCIRQ;
      USBCallIDC( ioRB->usbIDC, ioRB->usbDS, (RP_GENIOCTL FAR *)&savedRP );
      pRP_GENIOCTL->rph.Status=0;   // mark as finished
   }
   else
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_OK;   // not yet finished
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessGetDescriptor                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get descriptor routine                          */
/*                                                                    */
/* FUNCTION:  This routine dispatches general USB 'get descriptor'    */
/*            requests to specific worker routines.                   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessGetDescriptor                                */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets stalled flag and clears retrieved byte count in     */
/*           request ioRB for unsupported requests                    */
/*                                                                    */
/* INTERNAL REFERENCES:  GetDeviceDescriptor                          */
/*    ROUTINES:          GetDeviceConfiguration                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void ProcessGetDescriptor( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;

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

   switch (HIBYTE(setupRQ->wValue))
   {
   case DESC_DEVICE:
      GetDeviceDescriptor( pRP_GENIOCTL );   // get root hub device descriptor
      break;
   case DESC_CONFIGURATION:
      GetDeviceConfiguration( pRP_GENIOCTL );   // get root hub configuration descriptors
      break;
   case DESC_STRING:
   case DESC_INTERFACE:
   case DESC_ENDPOINT:
   default: // unsupported request
      ioRB->status|=USRB_STATUS_STALLED;
      ioRB->buffer2Length=0;
      break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetDeviceDescriptor                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get device descriptor routine                   */
/*                                                                    */
/* FUNCTION:  This routine copies root hub device description into    */
/*            callers buffer.                                         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetDeviceDescriptor                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets actual number of bytes returned in data buffer      */
/*           length field.                                            */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  movmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void GetDeviceDescriptor( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   USHORT             bytesToMove=sizeof(gRootDescriptor);

   ioRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   if (bytesToMove>ioRB->buffer2Length)
      bytesToMove=ioRB->buffer2Length;
   ioRB->buffer2Length=bytesToMove;   // actual number of bytes returned

   movmem(ioRB->buffer2, (PSZ)&gRootDescriptor, bytesToMove);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetDeviceConfiguration                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get device configuration routine                */
/*                                                                    */
/* FUNCTION:  This routine copies root hub device configuration data  */
/*            into callers buffer.                                    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetDeviceConfiguration                              */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets actual number of bytes returned in data buffer      */
/*           length field.                                            */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  movmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void GetDeviceConfiguration( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   USHORT             bytesToMove=sizeof(gRootConfiguration);

   ioRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   if (bytesToMove>ioRB->buffer2Length)
      bytesToMove=ioRB->buffer2Length;
   ioRB->buffer2Length=bytesToMove;   // actual number of bytes returned

   movmem(ioRB->buffer2, (PSZ)&gRootConfiguration, bytesToMove);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessSetAddress                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Sets root hub device address                    */
/*                                                                    */
/* FUNCTION:  This routine sets root hub USB address to specified in  */
/*            request block setup packet.                             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessSetAddress                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets gRootHubAddress global variable to required address */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void ProcessSetAddress( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;

   ioRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   setupRQ=(SetupPacket FAR *)ioRB->buffer1;
   gRootHubAddress=LOUCHAR(setupRQ->wValue);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessSetConfiguration                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  Configures root hub device                      */
/*                                                                    */
/* FUNCTION:  This routine sets root hub configuration value          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessSetConfiguration                             */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets gRootHubConfig global variable to required          */
/*           configuration value                                      */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void ProcessSetConfiguration( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;

   ioRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   setupRQ=(SetupPacket FAR *)ioRB->buffer1;
   gRootHubConfig=(UCHAR)setupRQ->wValue;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessGetHubStatusCBits                         */
/*                                                                    */
/* DESCRIPTIVE NAME:  Retrieves hub status changed bitmap             */
/*                                                                    */
/* FUNCTION:  This routine reads USB host I/O registers and sets      */
/*            status changed bitmap flags on if port status has been  */
/*            changed                                                 */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessGetHubStatusCBits                            */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  clears pRP_GENIOCTL->rph.Status status done flags if no  */
/*           changes in root hub port status is detected             */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static void ProcessGetHubStatusCBits( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   USHORT             port1Status, port2Status;
   UCHAR              portChangeBitmap=0;

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

   if (ioRB->buffer1Length<1)
      pRP_GENIOCTL->rph.Status=USB_IDC_RC_PARMERR;
   else
   {
      inp16(gusbPortSc1Reg, port1Status);
      inp16(gusbPortSc2Reg, port2Status);
      if ((port1Status&UHCI_PORTSC_CSC) || (port1Status&UHCI_PORTSC_PEDC))
         portChangeBitmap|=UHCI_PORT1_STATUS_CHANGED; // port 1 status changed
      if ((port2Status&UHCI_PORTSC_CSC) || (port2Status&UHCI_PORTSC_PEDC))
         portChangeBitmap|=UHCI_PORT2_STATUS_CHANGED; // port 2 status changed
      if (portChangeBitmap)
      {
         *(UCHAR FAR *)(ioRB->buffer1)=portChangeBitmap;
         ioRB->buffer1Length=1;     // current status change bitmap length
      }
   }

   if (!portChangeBitmap && pRP_GENIOCTL->rph.Status!=USB_IDC_RC_PARMERR)
      pRP_GENIOCTL->rph.Status&=~STATUS_DONE;   // clear 'done' flag if nothing changed
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessGetHubDescriptor                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get hub descriptor routine                      */
/*                                                                    */
/* FUNCTION:  This routine dispatches USB hub class 'get descriptor'  */
/*            requests to specific worker routines.                   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessGetHubDescriptor                             */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets stalled flag and clears retrieved byte count in     */
/*           request ioRB for unsupported requests                    */
/*                                                                    */
/* INTERNAL REFERENCES:  GetHubConfiguration                          */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void ProcessGetHubDescriptor( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;

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

   switch (HIBYTE(setupRQ->wValue))
   {
   case DESC_HUB:
      GetHubConfiguration( pRP_GENIOCTL );
      break;
   default: // unsupported request
      ioRB->status|=USRB_STATUS_STALLED;
      ioRB->buffer2Length=0;
      break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetHubConfiguration                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Retrieves root hub device class specific        */
/*                    configuration                                   */
/*                                                                    */
/* FUNCTION:  This routine retrieves root hub class specific          */
/*            configuration data                                      */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetHubConfiguration                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets actual number of bytes returned in data buffer      */
/*           length field.                                            */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  movmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
static void GetHubConfiguration( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   USHORT             bytesToMove=sizeof(gRootHubConfiguration);

   ioRB=(USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   if (bytesToMove>ioRB->buffer2Length)
      bytesToMove=ioRB->buffer2Length;
   ioRB->buffer2Length=bytesToMove;   // actual number of bytes returned

   movmem(ioRB->buffer2, (PSZ)&gRootHubConfiguration, bytesToMove);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessGetHubStatus                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Retrieves hub/hub port status                   */
/*                                                                    */
/* FUNCTION:  This routine retrieves root hub/root hub port status    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessGetHubStatus                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets actual number of bytes returned in data buffer      */
/*           length field.                                            */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  movmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static void ProcessGetHubStatus( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;
   PortStatus         currStatus;

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

   if ((setupRQ->bmRequestType&REQTYPE_TYPE_HUBCLASS_PORT)==REQTYPE_TYPE_HUBCLASS_PORT)
   {  // get hub port status
      USHORT   portAddress, portStatus;

      switch (setupRQ->wIndex)
      {
      case 1:
         portAddress=gusbPortSc1Reg;
         break;
      case 2:
         portAddress=gusbPortSc2Reg;
         break;
      default: // wrong port number specified
         ioRB->status|=USRB_STATUS_STALLED;
         break;
      }

      if (!(ioRB->status&USRB_STATUS_STALLED))
      {
         inp16(portAddress, portStatus);

         currStatus.wPortStatus=0;
         if (portStatus&UHCI_PORTSC_CCS)
            currStatus.wPortStatus|=STATUS_PORT_CONNECTION;
         if (portStatus&UHCI_PORTSC_SUSPEND)
            currStatus.wPortStatus|=STATUS_PORT_SUSPEND;
         if (portStatus&UHCI_PORTSC_LSDA)
            currStatus.wPortStatus|=STATUS_PORT_LOW_SPEED;
         if (portStatus&UHCI_PORTSC_PED)
         {
            currStatus.wPortStatus|=STATUS_PORT_ENABLE;
            currStatus.wPortStatus|=STATUS_PORT_RESET;
         }
         if (portStatus&UHCI_PORTSC_RESET)
            currStatus.wPortStatus|=STATUS_PORT_RESET;
         currStatus.wPortStatus|=STATUS_PORT_POWER;

         currStatus.wPortChange=0;    
         if (portStatus&UHCI_PORTSC_CSC)
            currStatus.wPortChange|=STATUS_PORT_CONNECTION;
         if (portStatus&UHCI_PORTSC_PEDC)
            currStatus.wPortChange|=STATUS_PORT_ENABLE;
         if (portStatus&UHCI_PORTSC_RESUMEDET)
            currStatus.wPortChange|=STATUS_PORT_SUSPEND;
      }
   }
   else
   {  // get hub status
      // wPortStatus=0 - local power supply good, all power operations normal
      // wPortChange=0 - no change has occured on local power status, no change has occured on local Over-Current indicator
      currStatus.wPortStatus=0;
      currStatus.wPortChange=0;    
   }

   if (!(ioRB->status&USRB_STATUS_STALLED))
   {
      if (sizeof(currStatus)<=ioRB->buffer2Length)
         ioRB->buffer2Length=sizeof(currStatus);   // actual number of bytes returned
      movmem(ioRB->buffer2, (PSZ)&currStatus, ioRB->buffer2Length);
   }
   else
      ioRB->buffer2Length=0;
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessClearHubFeature                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  Clears root hub/root hub port feature           */
/*                                                                    */
/* FUNCTION:  This routine clears root hub/root hub port status       */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessClearHubFeature                              */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets actual number of bytes returned in data buffer      */
/*           length field.                                            */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  movmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static void ProcessClearHubFeature( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;
   PortStatus         currStatus={0,0};

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

   if ((setupRQ->bmRequestType&REQTYPE_TYPE_HUBCLASS_PORT)==REQTYPE_TYPE_HUBCLASS_PORT)
   {  // get hub port status
      USHORT   portAddress, portStatus, expectedStatus;

      switch (setupRQ->wIndex)
      {
      case 1:
         portAddress=gusbPortSc1Reg;
         break;
      case 2:
         portAddress=gusbPortSc2Reg;
         break;
      default: // wrong port number specified
         ioRB->status|=USRB_STATUS_STALLED;
         break;
      }

      if (!(ioRB->status&USRB_STATUS_STALLED))
      {
         inp16(portAddress, portStatus);

         expectedStatus=portStatus;
         portStatus&=~UHCI_PORTSC_PEDC;   // clear WC flag to prevent its change
         portStatus&=~UHCI_PORTSC_CSC;    // clear WC flag to prevent its change
         switch (setupRQ->wValue)
         {
         case C_PORT_SUSPEND: 
            portStatus&=~UHCI_PORTSC_SUSPEND;
            expectedStatus&=~UHCI_PORTSC_SUSPEND;
            break;
         case C_PORT_ENABLE: 
            portStatus|=UHCI_PORTSC_PEDC; // wc flag
            expectedStatus&=~UHCI_PORTSC_PEDC;
            break;
         case C_PORT_RESET:
            portStatus&=~UHCI_PORTSC_RESET;
            expectedStatus&=~UHCI_PORTSC_RESET;
            break;
         case C_PORT_CONNECTION: 
            portStatus|=UHCI_PORTSC_CSC; // wc flag
            expectedStatus&=~UHCI_PORTSC_CSC;
            break;
         case C_PORT_OVER_CURRENT: 
         default:
            break;
         }
         outp16(portAddress, portStatus);

         inp16(portAddress, portStatus);

         if (portStatus!=expectedStatus)   // failed to clear status
            ioRB->status|=USRB_STATUS_STALLED;
      }
   }
   else
   {
      switch (setupRQ->wValue)
      {
      case C_HUB_LOCAL_POWER:
         break; 
      case C_HUB_OVER_CURRENT:
         break;
      default:
         break;
      }
   }
}
#pragma optimize("", on)

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ProcessSetHubFeature                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Sets root hub/root hub port feature             */
/*                                                                    */
/* FUNCTION:  This routine sets on root hub/root hub port feature     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ProcessSetHubFeature                                */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets actual number of bytes returned in data buffer      */
/*           length field.                                            */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  movmem                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
#pragma optimize("eglt", off)
static void ProcessSetHubFeature( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   USBRB FAR          *ioRB;
   SetupPacket FAR    *setupRQ;
   PortStatus         currStatus={0,0};
   LONG               retryCount=0;

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

   if ((setupRQ->bmRequestType&REQTYPE_TYPE_HUBCLASS_PORT)==REQTYPE_TYPE_HUBCLASS_PORT)
   {  // get hub port status
      USHORT   portAddress, portStatus, newStatus;
      USHORT   expectedFlagsOn=0x0000, expectedFlagsOff=0xffff;

      switch (setupRQ->wIndex)
      {
      case 1:
         portAddress=gusbPortSc1Reg;
         break;
      case 2:
         portAddress=gusbPortSc2Reg;
         break;
      default: // wrong port number specified
         ioRB->status|=USRB_STATUS_STALLED;
         break;
      }

      if (!(ioRB->status&USRB_STATUS_STALLED))
      {
         inp16(portAddress, portStatus);

         portStatus&=~UHCI_PORTSC_PEDC;   // clear WC flag to prevent its change
         portStatus&=~UHCI_PORTSC_CSC;    // clear WC flag to prevent its change

         switch (setupRQ->wValue)
         {
         case PORT_SUSPEND: 
            portStatus|=UHCI_PORTSC_SUSPEND;
            expectedFlagsOn|=UHCI_PORTSC_SUSPEND;
            outp16(portAddress, portStatus); // set port feature
            break;
         case PORT_ENABLE: 
            portStatus|=UHCI_PORTSC_PED;
            expectedFlagsOn|=UHCI_PORTSC_PED;
            outp16(portAddress, portStatus); // set port feature
            break;
         case C_PORT_ENABLE: 
            portStatus|=UHCI_PORTSC_RESET;
            outp16(portAddress, portStatus); // set port feature
            USBExecutionStall( 10 );   // 10 ms delay
            portStatus&=~UHCI_PORTSC_RESET;
            outp16(portAddress, portStatus); // set port feature
            USBExecutionStall( 10 );   // 10 ms delay
            expectedFlagsOn&=~UHCI_PORTSC_RESET;
            break;
         case PORT_RESET:
            newStatus=UHCI_PORTSC_RESET;
            outp16(portAddress, newStatus);
            USBExecutionStall( 11 );   // 11 ms delay to complete reset
            newStatus=UHCI_PORTSC_PED;
            outp16(portAddress, newStatus);
            for (retryCount=0;retryCount<ROOT_HUB_FEATURE_RETRIES;retryCount++)
            {
               USBExecutionStall( 1 );   // 1 ms delay
               inp16(portAddress, portStatus);
               if ((portStatus&UHCI_PORTSC_PED) && (portStatus&UHCI_PORTSC_CSC))
                  break;
               outp16(portAddress, newStatus);
            }
            expectedFlagsOn|=UHCI_PORTSC_PED;
            break;
         case C_PORT_CONNECTION: 
         case C_PORT_OVER_CURRENT: 
         default:
            break;
         }

         inp16(portAddress, newStatus);

         if ((expectedFlagsOn&newStatus)!=expectedFlagsOn ||
             (expectedFlagsOff|newStatus)!=expectedFlagsOff)   // failed to set feature
            ioRB->status|=USRB_STATUS_STALLED;
#ifdef   DEBUG
         if (ioRB->status&USRB_STATUS_STALLED)
         {
            dsPrint3(DBG_CRITICAL, "UHCI: ProcessSetHubFeature - failed. Port%d, feature %d, status %0x\r\n",
                     setupRQ->wIndex, setupRQ->wValue, newStatus);
         }
#endif
      }
   }
   else
   {
      switch (setupRQ->wValue)
      {
      case C_HUB_LOCAL_POWER:
         break; 
      case C_HUB_OVER_CURRENT:
         break;
      default:
         break;
      }
   }
}
#pragma optimize("", on)


