text: //#############################################################################
  // FILE: usbdhidkeyb.c
  // TITLE: USB HID Keyboard device class driver
  //#############################################################################
  // $TI Release: F2806x Support Library v2.04.00.00 $
  // $Release Date: Thu Oct 18 15:47:20 CDT 2018 $
  // $Copyright:
  // Copyright (C) 2009-2018 Texas Instruments Incorporated - http://www.ti.com/
  //
  // Redistribution and use in source and binary forms, with or without 
  // modification, are permitted provided that the following conditions 
  // are met:
  // 
  //   Redistributions of source code must retain the above copyright 
  //   notice, this list of conditions and the following disclaimer.
  // 
  //   Redistributions in binary form must reproduce the above copyright
  //   notice, this list of conditions and the following disclaimer in the 
  //   documentation and/or other materials provided with the   
  //   distribution.
  // 
  //   Neither the name of Texas Instruments Incorporated nor the names of
  //   its contributors may be used to endorse or promote products derived
  //   from this software without specific prior written permission.
  // 
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  // $
  //#############################################################################
  
  #include <stdbool.h>
  #include <stdint.h>
  #include "inc/hw_types.h"
  #include "driverlib/debug.h"
  #include "driverlib/usb.h"
  #include "include/usblib.h"
  #include "include/usblibpriv.h"
  #include "include/device/usbdevice.h"
  #include "include/usbhid.h"
  #include "include/device/usbdhid.h"
  #include "include/device/usbdhidkeyb.h"
  
  //*****************************************************************************
  //
  //! \addtogroup hid_keyboard_device_class_api
  //! @{
  //
  //*****************************************************************************
  
  //*****************************************************************************
  //
  // HID device configuration descriptor.
  //
  // It is vital that the configuration descriptor bConfigurationValue field
  // (byte 6) is 1 for the first configuration and increments by 1 for each
  // additional configuration defined here.  This relationship is assumed in the
  // device stack for simplicity even though the USB 2.0 specification imposes
  // no such restriction on the bConfigurationValue values.
  //
  // Note that this structure is deliberately located in RAM since we need to
  // be able to patch some values in it based on client requirements.
  //
  //*****************************************************************************
  static uint8_t g_pui8KeybDescriptor[] =
  {
      //
      // Configuration descriptor header.
      //
      9,                          // Size of the configuration descriptor.
      USB_DTYPE_CONFIGURATION,    // Type of this descriptor.
      USBShort(34),               // The total size of this full structure.
      1,                          // The number of interfaces in this
                                  // configuration.
      1,                          // The unique value for this configuration.
      5,                          // The string identifier that describes this
                                  // configuration.
      USB_CONF_ATTR_SELF_PWR,     // Bus Powered, Self Powered, remote wake up.
      250,                        // The maximum power in 2mA increments.
  };
  
  //*****************************************************************************
  //
  // The remainder of the configuration descriptor is stored in flash since we
  // don't need to modify anything in it at runtime.
  //
  //*****************************************************************************
  static uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] =
  {
      //
      // HID Device Class Interface Descriptor.
      //
      9,                          // Size of the interface descriptor.
      USB_DTYPE_INTERFACE,        // Type of this descriptor.
      0,                          // The index for this interface.
      0,                          // The alternate setting for this interface.
      1,                          // The number of endpoints used by this
                                  // interface.
      USB_CLASS_HID,              // The interface class
      USB_HID_SCLASS_BOOT,        // The interface sub-class.
      USB_HID_PROTOCOL_KEYB,      // The interface protocol for the sub-class
                                  // specified above.
      4,                          // The string index for this interface.
  };
  
  static const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] =
  {
      //
      // Interrupt IN endpoint descriptor
      //
      7,                          // The size of the endpoint descriptor.
      USB_DTYPE_ENDPOINT,         // Descriptor type is an endpoint.
      USB_EP_DESC_IN | USBEPToIndex(USB_EP_1),
      USB_EP_ATTR_INT,            // Endpoint is an interrupt endpoint.
      USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)),
                                  // The maximum packet size.
      16,                         // The polling interval for this endpoint.
  };
  
  static const uint8_t g_pui8HIDOutEndpoint[HIDOUTENDPOINT_SIZE] =
  {
      //
      // Interrupt OUT endpoint descriptor
      //
      7,                          // The size of the endpoint descriptor.
      USB_DTYPE_ENDPOINT,         // Descriptor type is an endpoint.
      USB_EP_DESC_OUT | USBEPToIndex(USB_EP_2),
      USB_EP_ATTR_INT,            // Endpoint is an interrupt endpoint.
      USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)),
                                  // The maximum packet size.
      16,                         // The polling interval for this endpoint.
  };
  
  //*****************************************************************************
  //
  // The following is the HID report structure definition that is passed back
  // to the host.
  //
  //*****************************************************************************
  static const uint8_t g_pui8KeybReportDescriptor[] =
  {
      UsagePage(USB_HID_GENERIC_DESKTOP),
      Usage(USB_HID_KEYBOARD),
      Collection(USB_HID_APPLICATION),
  
          //
          // Modifier keys.
          // 8 - 1 bit values indicating the modifier keys (ctrl, shift...)
          //
          ReportSize(1),
          ReportCount(8),
          UsagePage(USB_HID_USAGE_KEYCODES),
          UsageMinimum(224),
          UsageMaximum(231),
          LogicalMinimum(0),
          LogicalMaximum(1),
          Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | USB_HID_INPUT_ABS),
  
          //
          // One byte of rsvd data required by HID spec.
          //
          ReportCount(1),
          ReportSize(8),
          Input(USB_HID_INPUT_CONSTANT),
  
          //
          // Keyboard LEDs.
          // 5 - 1 bit values.
          //
          ReportCount(5),
          ReportSize(1),
          UsagePage(USB_HID_USAGE_LEDS),
          UsageMinimum(1),
          UsageMaximum(5),
          Output(USB_HID_OUTPUT_DATA | USB_HID_OUTPUT_VARIABLE |
                 USB_HID_OUTPUT_ABS),
          //
          // 1 - 3 bit value to pad out to a full byte.
          //
          ReportCount(1),
          ReportSize(3),
          Output(USB_HID_OUTPUT_CONSTANT), //LED report padding
  
          //
          // The Key buffer.
          // 6 - 8 bit values to store the current key state.
          //
          ReportCount(6),
          ReportSize(8),
          LogicalMinimum(0),
          LogicalMaximum(101),
          UsagePage(USB_HID_USAGE_KEYCODES),
          UsageMinimum (0),
          UsageMaximum (101),
          Input(USB_HID_INPUT_DATA | USB_HID_INPUT_ARRAY),
      EndCollection
  };
  
  //*****************************************************************************
  //
  // The HID descriptor for the keyboard device.
  //
  //*****************************************************************************
  static const tHIDDescriptor g_sKeybHIDDescriptor =
  {
      9,                              // bLength
      USB_HID_DTYPE_HID,              // bDescriptorType
      0x0011,                             // bcdHID (version 1.11 compliant)
      0x0001,
      0,                              // bCountryCode (not localized)
      1,                              // bNumDescriptors
      {
          {
              USB_HID_DTYPE_REPORT,   // Report descriptor
              {
                  (sizeof(g_pui8KeybReportDescriptor) & 0xFF),  	// Size of report descriptor
                  ((sizeof(g_pui8KeybReportDescriptor) & 0xFF00) >> 8)
              }
          }
      }
  };
  
  //*****************************************************************************
  //
  // The HID configuration descriptor is defined as four or five sections
  // depending upon the client's configuration choice.  These sections are:
  //
  // 1.  The 9 byte configuration descriptor (RAM).
  // 2.  The interface descriptor (RAM).
  // 3.  The HID report and physical descriptors (provided by the client)
  //     (FLASH).
  // 4.  The mandatory interrupt IN endpoint descriptor (FLASH).
  // 5.  The optional interrupt OUT endpoint descriptor (FLASH).
  //
  //*****************************************************************************
  static const tConfigSection g_sHIDConfigSection =
  {
      sizeof(g_pui8KeybDescriptor),
      g_pui8KeybDescriptor
  };
  
  static const tConfigSection g_sHIDInterfaceSection =
  {
      sizeof(g_pui8HIDInterface),
      g_pui8HIDInterface
  };
  
  static const tConfigSection g_sHIDInEndpointSection =
  {
      sizeof(g_pui8HIDInEndpoint),
      g_pui8HIDInEndpoint
  };
  
  static const tConfigSection g_sHIDOutEndpointSection =
  {
      sizeof(g_pui8HIDOutEndpoint),
      g_pui8HIDOutEndpoint
  };
  
  //*****************************************************************************
  //
  // Place holder for the user's HID descriptor block.
  //
  //*****************************************************************************
  static tConfigSection g_sHIDDescriptorSection =
  {
     sizeof(g_sKeybHIDDescriptor),
     (const uint8_t *)&g_sKeybHIDDescriptor
  };
  
  //*****************************************************************************
  //
  // This array lists all the sections that must be concatenated to make a
  // single, complete HID configuration descriptor.
  //
  //*****************************************************************************
  static const tConfigSection *g_psHIDSections[] =
  {
      &g_sHIDConfigSection,
      &g_sHIDInterfaceSection,
      &g_sHIDDescriptorSection,
      &g_sHIDInEndpointSection,
      &g_sHIDOutEndpointSection
  };
  
  #define NUM_HID_SECTIONS        ((sizeof(g_psHIDSections) /                   \
                                    sizeof(g_psHIDSections[0])) - 1)
  
  //*****************************************************************************
  //
  // The header for the single configuration we support.  This is the root of
  // the data structure that defines all the bits and pieces that are pulled
  // together to generate the configuration descriptor.  Note that this must be
  // in RAM since we need to include or exclude the final section based on
  // client supplied initialization parameters.
  //
  //*****************************************************************************
  static tConfigHeader g_sHIDConfigHeader =
  {
      NUM_HID_SECTIONS,
      g_psHIDSections
  };
  
  //*****************************************************************************
  //
  // Configuration Descriptor.
  //
  //*****************************************************************************
  static const tConfigHeader * const g_ppsHIDConfigDescriptors[] =
  {
      &g_sHIDConfigHeader
  };
  
  //*****************************************************************************
  //
  // The HID class descriptor table.  For the keyboard class, we have only a
  // single report descriptor.
  //
  //*****************************************************************************
  static const uint8_t * const g_pui8KeybClassDescriptors[] =
  {
      g_pui8KeybReportDescriptor
  };
  
  //*****************************************************************************
  //
  // Forward references for keyboard device callback functions.
  //
  //*****************************************************************************
  static uint32_t HIDKeyboardRxHandler(void *pvKeyboardDevice, uint32_t ui32Event,
                                       uint32_t ui32MsgData, void *pvMsgData);
  static uint32_t HIDKeyboardTxHandler(void *pvKeyboardDevice, uint32_t ui32Event,
                                       uint32_t ui32MsgData, void *pvMsgData);
  
  //*****************************************************************************
  //
  // Main HID device class event handler function.
  //
  // \param pvKeyboardDevice is the event callback pointer provided during
  //  USBDHIDInit().This is a pointer to our HID device structure
  // (&g_sHIDKeybDevice).
  // \param ui32Event identifies the event we are being called back for.
  // \param ui32MsgData is an event-specific value.
  // \param pvMsgData is an event-specific pointer.
  //
  // This function is called by the HID device class driver to inform the
  // application of particular asynchronous events related to operation of the
  // keyboard HID device.
  //
  // \return Returns a value which is event-specific.
  //
  //*****************************************************************************
  static uint32_t
  HIDKeyboardRxHandler(void *pvKeyboardDevice, uint32_t ui32Event,
                       uint32_t ui32MsgData, void *pvMsgData)
  {
      tHIDKeyboardInstance *psInst;
      tUSBDHIDKeyboardDevice *psKeyboardDevice;
  
      //
      // Make sure we did not get a NULL pointer.
      //
      ASSERT(pvKeyboardDevice);
  
      //
      // Get a pointer to our instance data
      //
      psKeyboardDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
      psInst = &psKeyboardDevice->sPrivateData;
  
      //
      // Which event were we sent?
      //
      switch (ui32Event)
      {
          //
          // The host has connected to us and configured the device.
          //
          case USB_EVENT_CONNECTED:
          {
              psInst->ui8USBConfigured = true;
  
              //
              // Pass the information on to the client.
              //
              psKeyboardDevice->pfnCallback(psKeyboardDevice->pvCBData,
                                            USB_EVENT_CONNECTED, 0, (void *)0);
  
              break;
          }
  
          //
          // The host has disconnected from us.
          //
          case USB_EVENT_DISCONNECTED:
          {
              psInst->ui8USBConfigured = false;
  
              //
              // Pass the information on to the client.
              //
              psKeyboardDevice->pfnCallback(psKeyboardDevice->pvCBData,
                                            USB_EVENT_DISCONNECTED, 0,
                                            (void *)0);
  
              break;
          }
  
          //
          // The host is polling us for a particular report and the HID driver
          // is asking for the latest version to transmit.
          //
          case USBD_HID_EVENT_IDLE_TIMEOUT:
          case USBD_HID_EVENT_GET_REPORT:
          {
              //
              // We only support a single input report so we don't need to check
              // the ui32MsgValue parameter in this case.  Set the report pointer
              // in *pvMsgData and return the length of the report in bytes.
              //
              *(uint8_t **)pvMsgData = psInst->pui8Report;
              return(KEYB_IN_REPORT_SIZE);
          }
  
          //
          // The device class driver has completed sending a report to the
          // host in response to a Get_Report request.
          //
          case USBD_HID_EVENT_REPORT_SENT:
          {
              //
              // We have nothing to do here.
              //
              break;
          }
  
          //
          // This event is sent in response to a host Set_Report request.  We
          // must return a pointer to a buffer large enough to receive the
          // report into.
          //
          case USBD_HID_EVENT_GET_REPORT_BUFFER:
          {
              //
              // Are we being asked for a report that is shorter than the storage
              // we have set aside for this?  The only output report we define is
              // 8 bits long so we really expect to see a length of 1 passed.
              //
              if((uint32_t)pvMsgData == KEYB_OUT_REPORT_SIZE )
              {
                  //
                  // Yes - return our pointer.
                  //
                  return((uint32_t)psInst->pui8DataBuffer);
              }
              else
              {
                  //
                  // We are being passed a report that is longer than the
                  // only report we expect so return NULL.  This causes the
                  // device class driver to stall the request.
                  //
                  return(0);
              }
          }
  
          //
          // This event indicates that the host has sent us an Output or
          // Feature report and that the report is now in the buffer we provided
          // on the previous USBD_HID_EVENT_GET_REPORT_BUFFER callback.
          //
          case USBD_HID_EVENT_SET_REPORT:
          {
              //
              // Inform the application if the keyboard LEDs have changed.
              //
              if(psInst->ui8LEDStates != psInst->pui8DataBuffer[0])
              {
                  //
                  // Note the new LED states.
                  //
                  psInst->ui8LEDStates = psInst->pui8DataBuffer[0];
  
                  //
                  // Pass the information on to the client.
                  //
                  psKeyboardDevice->pfnCallback(
                                              psKeyboardDevice->pvCBData,
                                              USBD_HID_KEYB_EVENT_SET_LEDS,
                                              psInst->pui8DataBuffer[0],
                                              (void *)0);
              }
              break;
          }
  
          //
          // The host is asking us to set either boot or report protocol (not
          // that it makes any difference to this particular mouse).
          //
          case USBD_HID_EVENT_SET_PROTOCOL:
          {
              psInst->ui8Protocol = ui32MsgData;
              break;
          }
  
          //
          // The host is asking us to tell it which protocol we are currently
          // using, boot or request.
          //
          case USBD_HID_EVENT_GET_PROTOCOL:
          {
              return(psInst->ui8Protocol);
          }
  
          //
          // Pass ERROR, SUSPEND and RESUME to the client unchanged.
          //
          case USB_EVENT_ERROR:
          case USB_EVENT_SUSPEND:
          case USB_EVENT_RESUME:
          {
              return(psKeyboardDevice->pfnCallback(
                                           psKeyboardDevice->pvCBData,
                                           ui32Event, ui32MsgData, pvMsgData));
          }
  
          //
          // We ignore all other events.
          //
          default:
          {
              break;
          }
      }
      return(0);
  }
  
  //*****************************************************************************
  //
  // HID device class transmit channel event handler function.
  //
  // \param pvKeyboardDevice is the event callback pointer provided during
  // USBDHIDInit().  This is a pointer to our HID device structure
  // (&g_sHIDKeybDevice).
  // \param ui32Event identifies the event we are being called back for.
  // \param ui32MsgData is an event-specific value.
  // \param pvMsgData is an event-specific pointer.
  //
  // This function is called by the HID device class driver to inform the
  // application of particular asynchronous events related to report
  // transmissions made using the interrupt IN endpoint.
  //
  // \return Returns a value which is event-specific.
  //
  //*****************************************************************************
  static uint32_t
  HIDKeyboardTxHandler(void *pvKeyboardDevice, uint32_t ui32Event,
                       uint32_t ui32MsgData, void *pvMsgData)
  {
      tHIDKeyboardInstance *psInst;
      tUSBDHIDKeyboardDevice *psHIDKbDevice;
      tUSBDHIDDevice *psHIDDevice;
      uint32_t ui32Count;
  
      //
      // Make sure we did not get a NULL pointer.
      //
      ASSERT(pvKeyboardDevice);
  
      //
      // Get a pointer to our instance data
      //
      psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
      psInst = &psHIDKbDevice->sPrivateData;
      psHIDDevice = &psInst->sHIDDevice;
  
      //
      // Which event were we sent?
      //
      switch (ui32Event)
      {
          //
          // A report transmitted via the interrupt IN endpoint was acknowledged
          // by the host.
          //
          case USB_EVENT_TX_COMPLETE:
          {
              //
              // Do we have any pending changes needing transmitted?
              //
              if(psInst->bChangeMade)
              {
                  //
                  // Yes - go ahead and send another report immediately.
                  //
                  ui32Count = USBDHIDReportWrite((void *)psHIDDevice,
                                               psInst->pui8Report,
                                               KEYB_IN_REPORT_SIZE, true);
  
                  //
                  // If we scheduled the report for transmission, clear the
                  // change flag.
                  //
                  if(ui32Count != 0)
                  {
                      psInst->bChangeMade = false;
                  }
              }
              else
              {
                  //
                  // Our last transmission is complete and we have nothing more
                  // to send.
                  //
                  psInst->eKeyboardState = HID_KEYBOARD_STATE_IDLE;
              }
  
              //
              // Pass the event on to the client.
              //
              psHIDKbDevice->pfnCallback(psHIDKbDevice->pvCBData,
                                         USB_EVENT_TX_COMPLETE, ui32MsgData,
                                         (void *)0);
  
              break;
          }
  
          //
          // We ignore all other events related to transmission of reports via
          // the interrupt IN endpoint.
          //
          default:
          {
              break;
          }
      }
  
      return(0);
  }
  
  //*****************************************************************************
  //
  // Add the supplied usage code to the list of keys currently in the pressed
  // state.
  //
  // \param ui8UsageCode is the HID usage code of the newly pressed key.
  //
  // This function adds the supplied usage code to the global list of keys which
  // are currently pressed (assuming it is not already noted as pressed and that
  // there is space in the list to hold the new information).  The return code
  // indicates success if the list did not overflow and failure if the list
  // already contains as many pressed keys as can be reported.
  //
  // \return Returns \b true if the usage code was successfully added to the
  // list or \b false if there was insufficient space to hold the new key
  // press (in which case the caller should report a roll over error to the
  // host).
  //
  //*****************************************************************************
  static bool
  AddKeyToPressedList(tHIDKeyboardInstance *psInst, uint8_t ui8UsageCode)
  {
      uint32_t ui32Loop;
      bool bRetcode;
  
      //
      // Assume all is well until we determine otherwise.
      //
      bRetcode = true;
  
      //
      // Look through the list of existing pressed keys to see if the new one
      // is already there.
      //
      for(ui32Loop = 0; ui32Loop < (uint32_t)psInst->ui8KeyCount; ui32Loop++)
      {
          //
          // Is this key already included in the list of keys in the pressed
          // state?
          //
          if(ui8UsageCode == psInst->pui8KeysPressed[ui32Loop])
          {
              //
              // Yes - drop out.
              //
              break;
          }
      }
  
      //
      // If we exited the loop at the end of the existing key presses, this
      // key does not exist already so add it if space exists.
      //
      if(ui32Loop >= psInst->ui8KeyCount)
      {
          if(psInst->ui8KeyCount < KEYB_MAX_CHARS_PER_REPORT)
          {
              //
              // We have room so store the new key press in the list.
              //
              psInst->pui8KeysPressed[psInst->ui8KeyCount] = ui8UsageCode;
              psInst->ui8KeyCount++;
              bRetcode = true;
          }
          else
          {
              //
              // We have no room for the new key - declare a rollover error.
              //
              bRetcode = false;
          }
      }
  
      return(bRetcode);
  }
  
  //*****************************************************************************
  //
  // Remove the supplied usage code from the list of keys currently in the
  // pressed state.
  //
  // \param ui8UsageCode is the HID usage code of the newly released key.
  //
  // This function removes the supplied usage code from the global list of keys
  // which are currently pressed.  The return code indicates whether the key was
  // found in the list.  On exit, the list will have been cleaned up to ensure
  // that all key presses are contiguous starting at the first entry.
  //
  // \return Returns \b true if the usage code was found and removed from the
  // list or \b false if the code was not found.  The caller need not pass a new
  // report to the host if \b false is returned since the key list will not have
  // changed.
  //
  //*****************************************************************************
  static bool
  RemoveKeyFromPressedList(tHIDKeyboardInstance *psInst,
                           uint8_t ui8UsageCode)
  {
      uint32_t ui32Loop;
      uint32_t ui32Pos;
  
      //
      // Keep the compiler happy by setting ui32Pos to something.
      //
      ui32Pos = 0;
  
      //
      // Find the usage code in the current list.
      //
      for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++)
      {
          if(psInst->pui8KeysPressed[ui32Loop] == ui8UsageCode)
          {
              ui32Pos = ui32Loop;
              break;
          }
      }
  
      //
      // If we dropped out at the end of the loop, we could not find the code so
      // just return false.
      //
      if(ui32Loop == KEYB_MAX_CHARS_PER_REPORT)
      {
          return(false);
      }
  
      //
      // Now shuffle all the values to the right of the usage code we found
      // down one position to fill the gap left by removing it.
      //
      for(ui32Loop = (ui32Pos + 1); ui32Loop < KEYB_MAX_CHARS_PER_REPORT;
          ui32Loop++)
      {
          psInst->pui8KeysPressed[ui32Loop - 1] =
                                          psInst->pui8KeysPressed[ui32Loop];
      }
  
      //
      // Clear the last entry in the array and adjust the number of keys in the
      // array.
      //
      psInst->pui8KeysPressed[KEYB_MAX_CHARS_PER_REPORT - 1] =
                                                      HID_KEYB_USAGE_RESERVED;
      psInst->ui8KeyCount--;
  
      //
      // Tell the caller we were successful.
      //
      return(true);
  }
  
  //*****************************************************************************
  //
  //! Initializes HID keyboard device operation for a given USB controller.
  //!
  //! \param ui32Index is the index of the USB controller which is to be
  //! initialized for HID keyboard device operation.
  //! \param psHIDKbDevice points to a structure containing parameters
  //! customizing the operation of the HID keyboard device.
  //!
  //! An application wishing to offer a USB HID keyboard interface to a USB host
  //! must call this function to initialize the USB controller and attach the
  //! keyboard device to the USB bus.  This function performs all required USB
  //! initialization.
  //!
  //! On successful completion, this function will return the \e psHIDKbDevice
  //! pointer passed to it.  This must be passed on all future calls to the HID
  //! keyboard device driver.
  //!
  //! When a host connects and configures the device, the application callback
  //! will receive \b USB_EVENT_CONNECTED after which calls can be made to
  //! USBDHIDKeyboardKeyStateChange() to report key presses and releases to the
  //! USB host.
  //!
  //! \note The application must not make any calls to the lower level USB device
  //! interfaces if interacting with USB via the USB HID keyboard device class
  //! API.  Doing so will cause unpredictable (though almost certainly
  //! unpleasant) behavior.
  //!
  //! \return Returns NULL on failure or the psHIDKbDevice pointer on success.
  //
  //*****************************************************************************
  void *
  USBDHIDKeyboardInit(uint32_t ui32Index, tUSBDHIDKeyboardDevice *psHIDKbDevice)
  {
      void *pvRetcode;
      tUSBDHIDDevice *psHIDDevice;
      tConfigDescriptor *pConfigDesc;
  
      //
      // Check parameter validity.
      //
      ASSERT(psHIDKbDevice);
      ASSERT(psHIDKbDevice->ppui8StringDescriptors);
      ASSERT(psHIDKbDevice->pfnCallback);
  
      //
      // Get a pointer to the HID device data.
      //
      psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice;
  
      //
      // Call the common initialization routine.
      //
      pvRetcode = USBDHIDKeyboardCompositeInit(ui32Index, psHIDKbDevice, 0);
  
      pConfigDesc = (tConfigDescriptor *)g_pui8KeybDescriptor;
      pConfigDesc->bmAttributes = psHIDKbDevice->ui8PwrAttributes;
      pConfigDesc->bMaxPower =  (uint8_t)(psHIDKbDevice->ui16MaxPowermA / 2);
  
      //
      // If we initialized the HID layer successfully, pass our device pointer
      // back as the return code, otherwise return NULL to indicate an error.
      //
      if(pvRetcode)
      {
          //
          // Initialize the lower layer HID driver and pass it the various
          // structures and descriptors necessary to declare that we are a
          // keyboard.
          //
          pvRetcode = USBDHIDInit(ui32Index, psHIDDevice);
  
          return((void *)psHIDKbDevice);
      }
      else
      {
          return((void *)0);
      }
  }
  
  //*****************************************************************************
  //
  //! Initializes HID keyboard device operation for a given USB controller.
  //!
  //! \param ui32Index is the index of the USB controller which is to be
  //! initialized for HID keyboard device operation.
  //! \param psHIDKbDevice points to a structure containing parameters
  //! customizing the operation of the HID keyboard device.
  //! \param psCompEntry is the composite device entry to initialize when
  //! creating a composite device.
  //!
  //! This call is very similar to USBDKeyboardInit() except that it is used for
  //! initializing an instance of the HID keyboard device for use in a composite
  //! device.  If this HID keyboard is part of a composite device, then the
  //! \e psCompEntry should point to the composite device entry to initialize.
  //! This is part of the array that is passed to the USBDCompositeInit()
  //! function.
  //!
  //! \return Returns zero on failure or a non-zero instance value that should be
  //! used with the remaining USB HID Keyboard APIs.
  //
  //*****************************************************************************
  void *
  USBDHIDKeyboardCompositeInit(uint32_t ui32Index,
                               tUSBDHIDKeyboardDevice *psHIDKbDevice,
                               tCompositeEntry *psCompEntry)
  {
      tHIDKeyboardInstance *psInst;
      uint32_t ui32Loop;
      tUSBDHIDDevice *psHIDDevice;
  
      //
      // Check parameter validity.
      //
      ASSERT(psHIDKbDevice);
      ASSERT(psHIDKbDevice->ppui8StringDescriptors);
      ASSERT(psHIDKbDevice->pfnCallback);
  
      //
      // Get a pointer to our instance data
      //
      psInst = &psHIDKbDevice->sPrivateData;
  
      //
      // Initialize the various fields in our instance structure.
      //
      psInst->ui8USBConfigured = 0;
      psInst->ui8Protocol = USB_HID_PROTOCOL_REPORT;
      psInst->sReportIdle.ui8Duration4mS = 125;
      psInst->sReportIdle.ui8ReportID = 0;
      psInst->sReportIdle.ui32TimeSinceReportmS = 0;
      psInst->sReportIdle.ui16TimeTillNextmS = 0;
      psInst->ui8LEDStates = 0;
      psInst->ui8KeyCount = 0;
      for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++)
      {
          psInst->pui8KeysPressed[ui32Loop] = HID_KEYB_USAGE_RESERVED;
      }
  
      psInst->eKeyboardState = HID_KEYBOARD_STATE_UNCONFIGURED;
  
      //
      // Get a pointer to the HID device data.
      //
      psHIDDevice = &psInst->sHIDDevice;
  
      //
      // Initialize the HID device class instance structure based on input from
      // the caller.
      //
      psHIDDevice->ui16PID = psHIDKbDevice->ui16PID;
      psHIDDevice->ui16VID = psHIDKbDevice->ui16VID;
      psHIDDevice->ui16MaxPowermA = psHIDKbDevice->ui16MaxPowermA;
      psHIDDevice->ui8PwrAttributes = psHIDKbDevice->ui8PwrAttributes;
      psHIDDevice->ui8Subclass = USB_HID_SCLASS_BOOT;
      psHIDDevice->ui8Protocol = USB_HID_PROTOCOL_KEYB;
      psHIDDevice->ui8NumInputReports = 1;
      psHIDDevice->psReportIdle = 0;
      psHIDDevice->pfnRxCallback = HIDKeyboardRxHandler;
      psHIDDevice->pvRxCBData = (void *)psHIDKbDevice;
      psHIDDevice->pfnTxCallback = HIDKeyboardTxHandler;
      psHIDDevice->pvTxCBData = (void *)psHIDKbDevice;
      psHIDDevice->bUseOutEndpoint = false,
  
      psHIDDevice->psHIDDescriptor = &g_sKeybHIDDescriptor;
      psHIDDevice->ppui8ClassDescriptors = g_pui8KeybClassDescriptors;
      psHIDDevice->ppui8StringDescriptors =
                                          psHIDKbDevice->ppui8StringDescriptors;
      psHIDDevice->ui32NumStringDescriptors =
                                          psHIDKbDevice->ui32NumStringDescriptors;
      psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors;
  
      psHIDDevice->psReportIdle = &psInst->sReportIdle;
  
      //
      // Initialize the lower layer HID driver and pass it the various structures
      // and descriptors necessary to declare that we are a keyboard.
      //
      return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry));
  }
  
  //*****************************************************************************
  //
  //! Shuts down the HID keyboard device.
  //!
  //! \param pvKeyboardDevice is the pointer to the device instance structure
  //! as returned by USBDHIDKeyboardInit().
  //!
  //! This function terminates HID keyboard operation for the instance supplied
  //! and removes the device from the USB bus.  Following this call, the
  //! \e pvKeyboardDevice instance may not me used in any other call to the HID
  //! keyboard device other than USBDHIDKeyboardInit().
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  USBDHIDKeyboardTerm(void *pvKeyboardDevice)
  {
      tUSBDHIDKeyboardDevice *psHIDKbDevice;
      tUSBDHIDDevice *psHIDDevice;
  
      ASSERT(pvKeyboardDevice);
  
      //
      // Get a pointer to the device.
      //
      psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
  
      //
      // Get a pointer to the HID device data.
      //
      psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice;
  
      //
      // Mark the device as no shorter configured.
      //
      psHIDKbDevice->sPrivateData.ui8USBConfigured = 0;
  
      //
      // Terminate the low level HID driver.
      //
      USBDHIDTerm(psHIDDevice);
  }
  
  //*****************************************************************************
  //
  //! Sets the client-specific pointer parameter for the keyboard callback.
  //!
  //! \param pvKeyboardDevice is the pointer to the device instance structure
  //! as returned by USBDHIDKeyboardInit().
  //! \param pvCBData is the pointer that client wishes to be provided on each
  //! event sent to the keyboard callback function.
  //!
  //! The client uses this function to change the callback pointer passed in
  //! the first parameter on all callbacks to the \e pfnCallback function
  //! passed on USBDHIDKeyboardInit().
  //!
  //! If a client wants to make runtime changes in the callback pointer, it must
  //! ensure that the \e pvKeyboardDevice structure passed to
  //! USBDHIDKeyboardInit() resides in RAM.  If this structure is in flash,
  //! callback data changes will not be possible.
  //!
  //! \return Returns the previous callback pointer that was set for this
  //! instance.
  //
  //*****************************************************************************
  void *
  USBDHIDKeyboardSetCBData(void *pvKeyboardDevice, void *pvCBData)
  {
      void *pvOldCBData;
      tUSBDHIDKeyboardDevice *psKeyboard;
  
      //
      // Check for a NULL pointer in the device parameter.
      //
      ASSERT(pvKeyboardDevice);
  
      //
      // Get a pointer to our keyboard device.
      //
      psKeyboard = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
  
      //
      // Save the old callback pointer and replace it with the new value.
      //
      pvOldCBData = psKeyboard->pvCBData;
      psKeyboard->pvCBData = pvCBData;
  
      //
      // Pass the old callback pointer back to the caller.
      //
      return(pvOldCBData);
  }
  
  //*****************************************************************************
  //
  //! Reports a key state change to the USB host.
  //!
  //! \param pvKeyboardDevice is the pointer to the device instance structure
  //! as returned by USBDHIDKeyboardInit().
  //! \param ui8Modifiers contains the states of each of the keyboard modifiers
  //! (left/right shift, ctrl, alt or GUI keys).  Valid values are logical OR
  //! combinations of the labels \b HID_KEYB_LEFT_CTRL, \b HID_KEYB_LEFT_SHIFT,
  //! \b HID_KEYB_LEFT_ALT, \b HID_KEYB_LEFT_GUI, \b HID_KEYB_RIGHT_CTRL, \b
  //! HID_KEYB_RIGHT_SHIFT, \b HID_KEYB_RIGHT_ALT and \b HID_KEYB_RIGHT_GUI.
  //! Presence of one of these bit flags indicates that the relevant modifier
  //! key is pressed and absence indicates that it is released.
  //! \param ui8UsageCode is the usage code of the key whose state has changed.
  //! If only modifier keys have changed, \b HID_KEYB_USAGE_RESERVED should be
  //! passed in this parameter.
  //! \param bPress is \b true if the key has been pressed or \b false if it has
  //! been released.  If only modifier keys have changed state, this parameter is
  //! ignored.
  //!
  //! This function adds or removes a key usage code from the list of keys
  //! currently pressed and schedules a report transmission to the host to
  //! inform it of the new keyboard state.  If the maximum number of simultaneous
  //! key presses are already recorded, the report to the host will contain the
  //! rollover error code, HID_KEYB_USAGE_ROLLOVER instead of key usage codes
  //! and the caller will receive return code KEYB_ERR_TOO_MANY_KEYS.
  //!
  //! \return Returns \b KEYB_SUCCESS if the key usage code was added to or
  //! removed from the current list successfully.  \b KEYB_ERR_TOO_MANY_KEYS is
  //! returned if an attempt is made to press a 7th key (the BIOS keyboard
  //! protocol can report no more than 6 simultaneously pressed keys).  If called
  //! before the USB host has configured the device, \b KEYB_ERR_NOT_CONFIGURED
  //! is returned and, if an error is reported while attempting to transmit the
  //! report, \b KEYB_ERR_TX_ERROR is returned.  If an attempt is made to remove
  //! a key from the pressed list (by setting parameter \e bPressed to \b false)
  //! but the key usage code is not found, \b KEYB_ERR_NOT_FOUND is returned.
  //
  //*****************************************************************************
  uint32_t
  USBDHIDKeyboardKeyStateChange(void *pvKeyboardDevice, uint8_t ui8Modifiers,
                                uint8_t ui8UsageCode, bool bPress)
  {
      bool bRetcode;
      uint32_t ui32Loop, ui32Count;
      tHIDKeyboardInstance *psInst;
      tUSBDHIDKeyboardDevice *psHIDKbDevice;
      tUSBDHIDDevice *psHIDDevice;
  
      psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
  
      //
      // Get a pointer to the HID device data.
      //
      psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice;
  
      //
      // Assume all is well until we determine otherwise.
      //
      bRetcode = true;
  
      //
      // Get a pointer to our instance data
      //
      psInst = &psHIDKbDevice->sPrivateData;
  
      //
      // Update the global keyboard report with the information passed.
      //
      psInst->pui8Report[0] = ui8Modifiers;
      psInst->pui8Report[1] = 0;
  
      //
      // Were we passed a usage code for a new key press or release or was
      // this call just telling us about a modifier change?
      //
      if(ui8UsageCode != HID_KEYB_USAGE_RESERVED)
      {
          //
          // Has a key been pressed or released?
          //
          if(bPress)
          {
              //
              // A key has been pressed - add it to the list if there is space an
              // and the key is not already in the list.
              //
              bRetcode = AddKeyToPressedList(psInst, ui8UsageCode);
          }
          else
          {
              //
              // A key has been released - remove it from the list.
              //
              bRetcode = RemoveKeyFromPressedList(psInst, ui8UsageCode);
  
              //
              // The return code here indicates whether the key was found.  If it
              // wasn't, the list has not changes so merely exit at this point
              // without sending anything to the host.
              //
              if(!bRetcode)
              {
                  return(KEYB_ERR_NOT_FOUND);
              }
          }
  
          //
          // Build the report from the current list of keys.  If we added a key
          // and got a bad return code indicating a roll over error, we need to
          // send a roll over report
          //
          for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++)
          {
              psInst->pui8Report[2 + ui32Loop] = (bRetcode ?
                  psInst->pui8KeysPressed[ui32Loop] : HID_KEYB_USAGE_ROLLOVER);
          }
      }
  
      //
      // If we are not configured, return an error here before trying to send
      // anything.
      //
      if(!psInst->ui8USBConfigured)
      {
          return(KEYB_ERR_NOT_CONFIGURED);
      }
  
      //
      // Only send a report if the transmitter is currently free.
      //
      if(USBDHIDTxPacketAvailable((void *)psHIDDevice))
      {
          //
          // Send the report to the host.
          //
          psInst->eKeyboardState = HID_KEYBOARD_STATE_SEND;
          ui32Count = USBDHIDReportWrite((void *)psHIDDevice,
                                         psInst->pui8Report, KEYB_IN_REPORT_SIZE,
                                         true);
  
          //
          // Did we schedule a packet for transmission correctly?
          //
          if(!ui32Count)
          {
              //
              // No - report the error to the caller.
              //
              return(KEYB_ERR_TX_ERROR);
          }
      }
      else
      {
          //
          // We can't send the report immediately so mark the instance so that
          // it is sent next time the transmitter is free.
          //
          psInst->bChangeMade = true;
      }
  
      //
      // If we get this far, the key information was sent successfully.  Are
      // too many keys currently pressed, though?
      //
      return(bRetcode ? KEYB_SUCCESS : KEYB_ERR_TOO_MANY_KEYS);
  }
  
  //*****************************************************************************
  //
  //! Reports the device power status (bus or self powered) to the USB library.
  //!
  //! \param pvKeyboardDevice is the pointer to the keyboard device instance
  //! structure.
  //! \param ui8Power indicates the current power status, either
  //! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR.
  //!
  //! Applications which support switching between bus or self powered
  //! operation should call this function whenever the power source changes
  //! to indicate the current power status to the USB library.  This information
  //! is required by the USB library to allow correct responses to be provided
  //! when the host requests status from the device.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  USBDHIDKeyboardPowerStatusSet(void *pvKeyboardDevice, uint8_t ui8Power)
  {
      tUSBDHIDKeyboardDevice *psHIDKbDevice;
      tUSBDHIDDevice *psHIDDevice;
  
      ASSERT(pvKeyboardDevice);
  
      //
      // Get the keyboard device pointer.
      //
      psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
  
      //
      // Get a pointer to the HID device data.
  
      psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice;
  
      //
      // Pass the request through to the lower layer.
      //
      USBDHIDPowerStatusSet((void *)psHIDDevice, ui8Power);
  }
  
  //*****************************************************************************
  //
  //! Requests a remote wake up to resume communication when in suspended state.
  //!
  //! \param pvKeyboardDevice is the pointer to the keyboard device instance
  //! structure.
  //!
  //! When the bus is suspended, an application which supports remote wake up
  //! (advertised to the host via the configuration descriptor) may call this
  //! function to initiate remote wake up signaling to the host.  If the remote
  //! wake up feature has not been disabled by the host, this will cause the bus
  //! to resume operation within 20mS.  If the host has disabled remote wake up,
  //! \b false will be returned to indicate that the wake up request was not
  //! successful.
  //!
  //! \return Returns \b true if the remote wake up is not disabled and the
  //! signaling was started or \b false if remote wake up is disabled or if
  //! signaling is currently ongoing following a previous call to this function.
  //
  //*****************************************************************************
  bool
  USBDHIDKeyboardRemoteWakeupRequest(void *pvKeyboardDevice)
  {
      tUSBDHIDKeyboardDevice *psHIDKbDevice;
      tUSBDHIDDevice *psHIDDevice;
  
      ASSERT(pvKeyboardDevice);
  
      //
      // Get the keyboard device pointer.
      //
      psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice;
  
      //
      // Get a pointer to the HID device data.
      //
      psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice;
  
      //
      // Pass the request through to the lower layer.
      //
      return(USBDHIDRemoteWakeupRequest((void *)psHIDDevice));
  }
  
  //*****************************************************************************
  //
  // Close the Doxygen group.
  //! @}
  //
  //*****************************************************************************
  