/* SCCSID = "src/dev/usb/USBKBD/KBDIDC.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:  KBDIDC.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  USB keyboard driver IDC routines                      */
/*                                                                            */
/*   FUNCTION: These routines handle the IDC for the USB keyboard driver.     */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             KBDidc         IDC Entry Point                                 */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/06/30  LR                                                      */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "kbd.h"

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  KBDidc                                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  USB keyboard IDC entry point                    */
/*                                                                    */
/* FUNCTION:  This routine is the USB keyboard IDC entry point        */
/*            router/handler. IDC function requests are routed to the */
/*            appropriate worker routine. The address of this routine */
/*            is returned to other device drivers via the DevHlp      */
/*            AttachDD call.                                          */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  KBDidc                                              */
/*    LINKAGE  :  CALL FAR                                            */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  KBD IDC RC OK                                        */
/*                                                                    */
/* EXIT-ERROR:  device driver error code                              */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void FAR KBDidc (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   USHORT status;

#ifdef DEBUG
   dsPrint2 (DBG_HLVLFLOW, "USBKBD: IDC Category = 0x%x, Function = 0x%x\r\n",
             pRP_GENIOCTL->Category, pRP_GENIOCTL->Function);
#endif

   status = pRP_GENIOCTL->rph.Status; 
   pRP_GENIOCTL->rph.Status = USB_IDC_RC_OK;

   if (pRP_GENIOCTL->rph.Cmd != CMDGenIOCTL ||
       !pRP_GENIOCTL->ParmPacket)
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_PARMERR;

   if (pRP_GENIOCTL->rph.Status == USB_IDC_RC_OK)
      if (pRP_GENIOCTL->Category == USB_IDC_CATEGORY_CLIENT)
         switch (pRP_GENIOCTL->Function)
         {
         case USB_IDC_FUNCTION_ACCIO:
            KBDlegIO (pRP_GENIOCTL);
            break;
         case USB_IDC_FUNCTION_PRCIRQ:
            pRP_GENIOCTL->rph.Status = status;
            KBDirq (pRP_GENIOCTL);
            break;
         case USB_IDC_FUNCTION_CHKSERV:
            KBDserv (pRP_GENIOCTL);
            break;
         case USB_IDC_FUNCTION_DETDEV:
            KBDdet (pRP_GENIOCTL);
            break;
         default:
            pRP_GENIOCTL->rph.Status = USB_IDC_RC_WRONGFUNC;
         }
      else pRP_GENIOCTL->rph.Status = USB_IDC_RC_WRONGCAT;

#ifdef DEBUG
   dsPrint3 (DBG_HLVLFLOW, "USBKBD: IDC Category = 0x%x, Function = 0x%x, Status = 0x%x\r\n",
             pRP_GENIOCTL->Category, pRP_GENIOCTL->Function, pRP_GENIOCTL->rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SetLEDs                                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  USB keyboard IDC entry point                    */
/*                                                                    */
/* FUNCTION:  This routine is the USB keyboard IDC entry point        */
/*            router/handler. IDC function requests are routed to the */
/*            appropriate worker routine. The address of this routine */
/*            is returned to other device drivers via the DevHlp      */
/*            AttachDD call.                                          */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SetLEDs                                             */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  UCHAR LEDstate                                             */
/*                                                                    */
/* EXIT-NORMAL:                                                       */
/*                                                                    */
/* EXIT-ERROR:                                                        */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void SetLEDs (BYTE LEDstate)
{
   USHORT      kbdIndex;
   USBRB       rbHID;     // I/O request block
   RP_GENIOCTL rpHID;     // request packet

   if (LEDstate != gLEDs) gLEDs = LEDstate;

   if (gDevice)
   {
      if (gKbdIndex != FULL_WORD) kbdIndex = gKbdIndex;
      else                        return;
   }
   else kbdIndex = NULL;

   for (; kbdIndex < MAX_KBDS; kbdIndex++)

      if (gKBD[kbdIndex].active)
      {

         setmem (gKBD[kbdIndex].LEDs, NULL, sizeof (gKBD[kbdIndex].LEDs));

         gKBD[kbdIndex].LEDs[gKBD[kbdIndex].LEDoff[UI_LED_NLOCK] / BITS_IN_BYTE] |=
         (gLEDs & LEG_LED_NLOCK)?
         BIT_0 << gKBD[kbdIndex].LEDoff[UI_LED_NLOCK] % BITS_IN_BYTE : NULL;

         gKBD[kbdIndex].LEDs[gKBD[kbdIndex].LEDoff[UI_LED_CLOCK] / BITS_IN_BYTE] |=
         (gLEDs & LEG_LED_CLOCK)?
         BIT_0 << gKBD[kbdIndex].LEDoff[UI_LED_CLOCK] % BITS_IN_BYTE : NULL;

         gKBD[kbdIndex].LEDs[gKBD[kbdIndex].LEDoff[UI_LED_SLOCK] / BITS_IN_BYTE] |=
         (gLEDs & LEG_LED_SLOCK)?
         BIT_0 << gKBD[kbdIndex].LEDoff[UI_LED_SLOCK] % BITS_IN_BYTE : NULL;

#ifdef DEBUG
         dsPrint2 (DBG_DETAILED, "USBKBD: SetLEDs = 0x%x, kbdIndex = %d\r\n",
                   gKBD[kbdIndex].LEDs[0], kbdIndex);
#endif

         rbHID.buffer1 = (PUCHAR)&gKBD[kbdIndex].setLEDpack;
         rbHID.buffer1Length = sizeof(gKBD[kbdIndex].setLEDpack);
         rbHID.buffer2 = (PUCHAR)&gKBD[kbdIndex].LEDs;
         rbHID.buffer2Length = gKBD[kbdIndex].outReportLength;

         rbHID.controllerId  = gKBD[kbdIndex].controllerID;
         rbHID.deviceAddress = gKBD[kbdIndex].kbdAddr;
         rbHID.endPointId    = USB_DEFAULT_CTRL_ENDPT;
         rbHID.status        = 0; // not used
         rbHID.flags         = USRB_FLAGS_TTYPE_SETUP;
         rbHID.serviceTime   = USB_DEFAULT_SRV_INTV;
         rbHID.maxPacketSize = USB_DEFAULT_PKT_SIZE;
         rbHID.maxErrorCount = USB_MAX_ERROR_COUNT;

         rbHID.usbIDC = (PUSBIDCEntry)KBDidc;
         rbHID.usbDS = GetDS();

         rbHID.category = USB_IDC_CATEGORY_CLIENT;
         rbHID.requestData1 = KBD_IRQ_STATUS_SETACK;
         rbHID.requestData2 = MAKEULONG (kbdIndex, 0);
         rbHID.requestData3 = 0;                // not used

         setmem ((PSZ)&rpHID, 0, sizeof(rpHID));
         rpHID.rph.Cmd = CMDGenIOCTL;
         rpHID.Category = USB_IDC_CATEGORY_CLASS;
         rpHID.Function = USB_IDC_FUNCTION_ACCIO;
         rpHID.ParmPacket = (PVOID)&rbHID;

         USBCallIDC (gpHIDIDC, gdsHIDIDC, (RP_GENIOCTL FAR *)&rpHID);

#ifdef DEBUG
         dsPrint3 (DBG_DETAILED, "USBKBD: SetLEDs = 0x%x, kbdIndex = %d, Status = 0x%x\r\n",
                   gKBD[kbdIndex].LEDs[0], kbdIndex, rpHID.rph.Status);
#endif

         if (gDevice) break;
      }
}

/******************* END  OF  SPECIFICATIONS **************************/

void SetTypematic (BYTE typematic)
{
   gTypeDelay = (BYTE)((((typematic & TYPEMATIC_DELAY) >>5) + 1) * 250 / 4);

   gTypeRate   = DURATION_MIN;
   gTypeRate <<= (typematic & TYPEMATIC_B) >>3;
   gTypeRate  *= ((typematic & TYPEMATIC_A) + 8) * 417 / 400;

#ifdef DEBUG
   dsPrint3 (DBG_DETAILED, "USBKBD: Typematic = 0x%x, Delay = %d, Rate = %d (*4 ms)\r\n",
             typematic, gTypeDelay, gTypeRate);
#endif
}

/******************* END  OF  SPECIFICATIONS **************************/

void SetIdleTime (USHORT kbdIndex, USHORT kbdIRQstatus)
{
   USBRB       rbHID;     // I/O request block
   RP_GENIOCTL rpHID;     // request packet

#ifdef DEBUG
   dsPrint3 (DBG_SPECIFIC, "USBKBD: SetIdleTime = %d, kbdIndex = %d, IRQ = %d\r\n",
             HIBYTE(gKBD[kbdIndex].setITpack.wValue), kbdIndex, kbdIRQstatus);
#endif

   rbHID.buffer1 = (PUCHAR)&gKBD[kbdIndex].setITpack;
   rbHID.buffer1Length = sizeof(gKBD[kbdIndex].setITpack);
   rbHID.buffer2 = NULL;
   rbHID.buffer2Length = NULL;

   rbHID.controllerId  = gKBD[kbdIndex].controllerID;
   rbHID.deviceAddress = gKBD[kbdIndex].kbdAddr;
   rbHID.endPointId    = USB_DEFAULT_CTRL_ENDPT;
   rbHID.status        = 0; // not used
   rbHID.flags         = USRB_FLAGS_TTYPE_SETUP;
   rbHID.serviceTime   = USB_DEFAULT_SRV_INTV;
   rbHID.maxPacketSize = USB_DEFAULT_PKT_SIZE;
   rbHID.maxErrorCount = USB_MAX_ERROR_COUNT;

   rbHID.usbIDC = (PUSBIDCEntry)KBDidc;       // Address of IRQ processing routine to be called for this request
   rbHID.usbDS = GetDS();

   rbHID.category = USB_IDC_CATEGORY_CLIENT;         // set client layer as IRQ processor
   rbHID.requestData1 = MAKEULONG (kbdIRQstatus, 0);
   rbHID.requestData2 = MAKEULONG (kbdIndex, 0);
   rbHID.requestData3 = 0;                        // not used
// rbHID.dsPhyAddr = 0;                  // data segment physical address

   setmem((PSZ)&rpHID, 0, sizeof(rpHID));
   rpHID.rph.Cmd = CMDGenIOCTL;
   rpHID.Category = USB_IDC_CATEGORY_CLASS;
   rpHID.Function = USB_IDC_FUNCTION_ACCIO;
   rpHID.ParmPacket = (PVOID)&rbHID;

   USBCallIDC (gpHIDIDC, gdsHIDIDC, (RP_GENIOCTL FAR *)&rpHID);

#ifdef DEBUG
   dsPrint4 (DBG_SPECIFIC, "USBKBD: SetIdleTime = %d, kbdIndex = %d, IRQ = %d, Status = 0x%x\r\n",
             HIBYTE(gKBD[kbdIndex].setITpack.wValue), kbdIndex, kbdIRQstatus, rpHID.rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  KBDlegIO                                         */
/*                                                                    */
/* DESCRIPTIVE NAME:  Legacy DD keyboard driver I/O                   */
/*                                                                    */
/* FUNCTION:  This routine processes I/O requests from the legacy     */
/*            device-dependent keyboard driver (IBMKBD.sys)           */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  KBDlegIO                                            */
/*    LINKAGE  :                                                      */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  KBD IDC RC OK                                        */
/*                                                                    */
/* EXIT-ERROR:  device driver error code                              */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void KBDlegIO (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   USBLegIO kbdata = *(USBLegIO FAR *)pRP_GENIOCTL->ParmPacket;

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBKBD: KBDlegIO Flags = %d\r\n", kbdata.flags);
#endif

   switch (kbdata.flags)
   {
   case SET_LEDS:
      SetLEDs (*kbdata.buffPtr);
      break;
   case SET_TYPEMATIC:
      SetTypematic (*kbdata.buffPtr);
      break;
   default:
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_WRONGCAT;
   }

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBKBD: KBDlegIO Status = %x\r\n", pRP_GENIOCTL->rph.Status);
#endif
}

/******************* END  OF  SPECIFICATIONS **************************/

USHORT ModOffset (RP_GENIOCTL FAR *pRP_GENIOCTL, USHORT kbdIndex)
{
   USHORT index, result;

   for (index = UI_LCTRL; index <= UI_RGUI; index++)
   {

      result = GetUsageOffset (pRP_GENIOCTL,
                               HID_REPORT_TAGS_MAIN_INPUT,
                               gKBD[kbdIndex].inReportID, gKBD[kbdIndex].inInterface,
                               HID_USAGE_PAGE_KEYBOARD,
                               index);

      if (result == FULL_WORD && index != UI_LGUI && index != UI_RGUI) break;
      else
      {
         if (result / BITS_IN_BYTE + 1 > sizeof (gKBD[kbdIndex].buffer))
         {
            result = FULL_WORD;
            break;
         }
      }
      gKBD[kbdIndex].modOff[index-UI_LCTRL] = (BYTE)result;
      if (index == UI_RGUI && result == FULL_WORD) result = NULL;
   }
   return result;
}

/******************* END  OF  SPECIFICATIONS **************************/

USHORT KeyOffset (RP_GENIOCTL FAR *pRP_GENIOCTL, USHORT kbdIndex)
{
   USHORT index, result;

   result = GetUsageOffset (pRP_GENIOCTL,
                            HID_REPORT_TAGS_MAIN_INPUT,
                            gKBD[kbdIndex].inReportID, gKBD[kbdIndex].inInterface,
                            HID_USAGE_PAGE_KEYBOARD,
                            UI_CLOCK);
   if (result != FULL_WORD)
   {
      for (index = UI_NUSBSL; index > UI_MIN; index--)
         if (GetUsageOffset (pRP_GENIOCTL,
                             HID_REPORT_TAGS_MAIN_INPUT,
                             gKBD[kbdIndex].inReportID, gKBD[kbdIndex].inInterface,
                             HID_USAGE_PAGE_KEYBOARD,
                             index)                  != result)
         {
            result = FULL_WORD;
            break;
         }
   }
   return (result / BITS_IN_BYTE);
}

/******************* END  OF  SPECIFICATIONS **************************/

USHORT LEDoffset (RP_GENIOCTL FAR *pRP_GENIOCTL, USHORT kbdIndex)
{
   USHORT index, result;

   for (index = UI_LED_NLOCK; index <= UI_LED_SLOCK; index++)
   {

      result = GetUsageOffset (pRP_GENIOCTL,
                               HID_REPORT_TAGS_MAIN_OUTPUT,
                               gKBD[kbdIndex].outReportID, gKBD[kbdIndex].outInterface,
                               HID_USAGE_PAGE_LEDS,
                               index);

      if (result == FULL_WORD) break;
      else
      {
         if (result / BITS_IN_BYTE + 1 > sizeof(gKBD[kbdIndex].LEDs))
         {
            result = FULL_WORD;
            break;
         }
         else gKBD[kbdIndex].LEDoff[index] = (BYTE)result;
      }
   }
   if (result != FULL_WORD) gKBD[kbdIndex].LEDoff[UI_LED_UNDEF] = NULL;
   return result;
}

/******************* END  OF  SPECIFICATIONS **************************/

void GetKbdIndex (void)
{
   USHORT kbdIndex;

   if (gNoOfKBDs >= gDevice)
   {
      for (kbdIndex = NULL, gKbdIndex = NULL; kbdIndex < MAX_KBDS; kbdIndex++)
         if (gKBD[kbdIndex].active)
            if (++gKbdIndex == gDevice)
            {
               gKbdIndex = kbdIndex;
               break;
            }
   }
   else gKbdIndex = FULL_WORD;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  KBDserv                                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  USB keyboard service check                      */
/*                                                                    */
/* FUNCTION:  This routine is used to deterime whether the            */
/*            USB keyboard driver can service a specified device.     */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  KBDserv                                             */
/*    LINKAGE  :                                                      */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  KBD IDC RC OK                                        */
/*                                                                    */
/* EXIT-ERROR:  device driver error code                              */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void KBDserv (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   USHORT              index, kbdIndex;
   USBHIDServe    FAR *pServData;
   ReportItemData FAR *pItem;

   if (gNoOfKBDs < MAX_KBDS)
   {
      for (kbdIndex = 0; kbdIndex < MAX_KBDS; kbdIndex++) if (!gKBD[kbdIndex].active) break;
   }
   else
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;

#ifdef DEBUG
      dsPrint2 (DBG_CRITICAL, "USBKBD: KBDserv, gNoOfKBDs = %d, Status = 0x%x\r\n",
                gNoOfKBDs, pRP_GENIOCTL->rph.Status);
#endif

      return;
   }

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBKBD: KBDserv, kbdIndex = %d\r\n", kbdIndex);
#endif

   pServData = (USBHIDServe FAR *)pRP_GENIOCTL->ParmPacket;

   index = pServData->reportItemIndex;
   while (index != LAST_INDEX)
   {
      pItem = pServData->itemData + index;

      if (pItem->mainType == HID_REPORT_TAGS_MAIN_INPUT &&
          pItem->itemFeatures.usagePage == HID_USAGE_PAGE_KEYBOARD &&
          pItem->localFeatures.usageMin == UI_LCTRL &&
          pItem->itemFlags == HID_REPORT_ITEM_VARIABLE)
      {
         gKBD[kbdIndex].inReportID  = pItem->itemFeatures.reportID;
         gKBD[kbdIndex].inInterface = pItem->interface;
         if (ModOffset (pRP_GENIOCTL, kbdIndex) != FULL_WORD) break;
      }
      index = pItem->indexToNextItem;
   }
   if (index == LAST_INDEX)
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }

   index = pServData->reportItemIndex;
   while (index != LAST_INDEX)
   {
      pItem = pServData->itemData + index;

      if (pItem->mainType == HID_REPORT_TAGS_MAIN_INPUT &&
          pItem->itemFeatures.usagePage == HID_USAGE_PAGE_KEYBOARD &&
          pItem->localFeatures.usageMin == UI_MIN)
      {
         if ((gKBD[kbdIndex].keyOff = KeyOffset (pRP_GENIOCTL, kbdIndex)) != FULL_WORD) break;
      }
      index = pItem->indexToNextItem;
   }
   if (index == LAST_INDEX)
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }

   if ((gKBD[kbdIndex].inReportLength = GetReportLength (pRP_GENIOCTL,
                                                         HID_REPORT_TAGS_MAIN_INPUT,
                                                         gKBD[kbdIndex].inReportID,
                                                         gKBD[kbdIndex].inInterface))
       > sizeof(gKBD[kbdIndex].buffer))
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }

   index = pServData->reportItemIndex;
   while (index != LAST_INDEX)
   {
      pItem = pServData->itemData + index;

      if (pItem->mainType == HID_REPORT_TAGS_MAIN_OUTPUT &&
          pItem->itemFeatures.usagePage == HID_USAGE_PAGE_LEDS &&
          pItem->localFeatures.usageMin == UI_LED_NLOCK)
      {
         gKBD[kbdIndex].outReportID  = pItem->itemFeatures.reportID;
         gKBD[kbdIndex].outInterface = pItem->interface;
         if (LEDoffset (pRP_GENIOCTL, kbdIndex) != FULL_WORD) break;
      }
      index = pItem->indexToNextItem;
   }
   if (index == LAST_INDEX)
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }

   if ((gKBD[kbdIndex].outReportLength = GetReportLength (pRP_GENIOCTL,
                                                          HID_REPORT_TAGS_MAIN_OUTPUT,
                                                          gKBD[kbdIndex].outReportID,
                                                          gKBD[kbdIndex].outInterface))
       > sizeof(gKBD[kbdIndex].LEDs))
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }
   index = pServData->reportItemIndex;
   while (index != LAST_INDEX)
   {
      pItem = pServData->itemData + index;

      if (pItem->mainType == HID_REPORT_TAGS_MAIN_COLL) break;

      index = pItem->indexToNextItem;
   }
   if (index != LAST_INDEX) if (!(pItem->itemFeatures.usagePage == HID_USAGE_PAGE_GDESKTOP &&
                                  pItem->localFeatures.usageMin == HID_GDESKTOP_USAGE_KEYBOARD  &&
                                  pItem->localFeatures.usageMax == HID_GDESKTOP_USAGE_KEYBOARD))
      {
         pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
         return;
      }
   gKBD[kbdIndex].kbdAddr = pServData->pDeviceInfo->deviceAddress;
   gKBD[kbdIndex].controllerID = pServData->pDeviceInfo->ctrlID;
   gKBD[kbdIndex].interruptPipeAddress =
   GetInterruptPipeAddr (pServData->pDeviceInfo->configurationData, 
                         pServData->pDeviceInfo->descriptor.bNumConfigurations,
                         pServData->pDeviceInfo->bConfigurationValue,
                         gKBD[kbdIndex].inInterface);

   gKBD[kbdIndex].setITpack.bmRequestType = REQTYPE_TYPE_CLASS | REQTYPE_RECIPIENT_INTERFACE;
   gKBD[kbdIndex].setITpack.bRequest = HID_REQUEST_SET_IDLE;
   gKBD[kbdIndex].setITpack.wValue = MAKEUSHORT (gKBD[kbdIndex].inReportID, DURATION_INFINITY);
   gKBD[kbdIndex].setITpack.wIndex = gKBD[kbdIndex].inInterface;
   gKBD[kbdIndex].setITpack.wLength = NULL;

   gKBD[kbdIndex].setLEDpack.bmRequestType = REQTYPE_TYPE_CLASS | REQTYPE_RECIPIENT_INTERFACE;
   gKBD[kbdIndex].setLEDpack.bRequest = HID_REQUEST_SET_REPORT;
   gKBD[kbdIndex].setLEDpack.wValue = MAKEUSHORT (gKBD[kbdIndex].outReportID, HID_REPORT_TYPE_OUTPUT);
   gKBD[kbdIndex].setLEDpack.wIndex = gKBD[kbdIndex].outInterface;
   gKBD[kbdIndex].setLEDpack.wLength = gKBD[kbdIndex].outReportLength;

   setmem ((PSZ)&gKBD[kbdIndex].prevBuff[gKBD[kbdIndex].keyOff], UI_ERROR,
           gKBD[kbdIndex].inReportLength - gKBD[kbdIndex].keyOff);

   gKBD[kbdIndex].active = TURNON;
   gNoOfKBDs++;

   SetIdleTime (kbdIndex, KBD_IRQ_STATUS_IDLESET);

   if (gDevice)
   {
      index = gKbdIndex;
      GetKbdIndex();
      if (gKbdIndex != FULL_WORD && gKbdIndex != index) LegKBDCall (pRP_GENIOCTL);
   }
   else if (gNoOfKBDs == BIT_0) LegKBDCall (pRP_GENIOCTL);

   pRP_GENIOCTL->rph.Status = USB_IDC_RC_OK;

#ifdef DEBUG
   dsPrint3 (DBG_DETAILED, "USBKBD: KBDServ, kbdIndex = %d, keyOff = %d, Status = 0x%x\r\n",
             kbdIndex, gKBD[kbdIndex].keyOff, pRP_GENIOCTL->rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  KBDdet                                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  USB keyboard detach                             */
/*                                                                    */
/* FUNCTION:  This routine is used to deterime whether the            */
/*            USB keyboard driver can service a specified device.     */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  KBDdet                                             */
/*    LINKAGE  :                                                      */
/*                                                                    */
/* INPUT:  RP_GENIOCTL FAR *pRP_GENIOCTL                              */
/*                                                                    */
/* EXIT-NORMAL:  KBD IDC RC OK                                        */
/*                                                                    */
/* EXIT-ERROR:  device driver error code                              */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void KBDdet (RP_GENIOCTL FAR *pRP_GENIOCTL)
{
   BYTE           kbdAddr;
   USHORT         kbdIndex;
   USBDetach FAR *pDetData;

   pDetData = (USBDetach FAR *)pRP_GENIOCTL->ParmPacket;
   kbdAddr = pDetData->deviceAddress;

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBKBD: KBDdet, kbdAddr = %d\r\n", kbdAddr);
#endif

   if (gNoOfKBDs && pDetData->controllerId == NULL)
   {
      for (kbdIndex = NULL; kbdIndex < MAX_KBDS; kbdIndex++)
      {
         if (gKBD[kbdIndex].active && gKBD[kbdIndex].kbdAddr == kbdAddr)
         {
            gKBD[kbdIndex].active = TURNOFF;
            gNoOfKBDs--;
            pRP_GENIOCTL->rph.Status = USB_IDC_RC_OK;
            break;
         }
      }
      if (gDevice)
      {
         kbdIndex = gKbdIndex;
         GetKbdIndex();
         if (gKbdIndex == FULL_WORD) LegKBDCall (pRP_GENIOCTL);
         else if (gKbdIndex != kbdIndex)  SetLEDs    (gLEDs);
      }
      else if (!gNoOfKBDs) LegKBDCall (pRP_GENIOCTL);
   }
   else   pRP_GENIOCTL->rph.Status = USB_IDC_RC_PARMERR;

#ifdef DEBUG
   dsPrint3 (DBG_DETAILED, "USBKBD: KBDdet, kbdAddr = %d, kbdIndex = %d, Status = 0x%x\r\n",
             kbdAddr, kbdIndex, pRP_GENIOCTL->rph.Status);
#endif
}

