//*****************************************************************************
//
// usbdhidkeyb.c - USB HID Keyboard device class driver.
//
// Copyright (c) 2008-2012 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 9453 of the Stellaris USB Library.
//
//*****************************************************************************

#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/device/usbdevice.h"
#include "usblib/usbhid.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidkeyb.h"

//*****************************************************************************
//
//! \addtogroup hid_keyboard_device_class_api
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
// The following is the HID report structure definition that is passed back
// to the host.
//
//*****************************************************************************
static const unsigned char g_pucKeybReportDescriptor[]=
{
    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 class descriptor table.  For the keyboard class, we have only a
// single report descriptor.
//
//*****************************************************************************
static const unsigned char * const g_pKeybClassDescriptors[] =
{
    g_pucKeybReportDescriptor
};

//*****************************************************************************
//
// The HID descriptor for the keyboard device.
//
//*****************************************************************************
static const tHIDDescriptor g_sKeybHIDDescriptor =
{
    9,                                     // bLength
    USB_HID_DTYPE_HID,                     // bDescriptorType
    0x111,                                 // bcdHID (version 1.11 compliant)
    0,                                     // bCountryCode (not localized)
    1,                                     // bNumDescriptors
    {
        {
            USB_HID_DTYPE_REPORT,                  // Report descriptor
            sizeof(g_pucKeybReportDescriptor)      // Size of report descriptor
        }
    }
};

//*****************************************************************************
//
// Forward references for keyboard device callback functions.
//
//*****************************************************************************
static unsigned long HIDKeyboardRxHandler(void *pvCBData,
                                          unsigned long ulEvent,
                                          unsigned long ulMsgData,
                                          void *pvMsgData);
static unsigned long HIDKeyboardTxHandler(void *pvCBData,
                                          unsigned long ulEvent,
                                          unsigned long ulMsgData,
                                          void *pvMsgData);

//*****************************************************************************
//
// Main HID device class event handler function.
//
// \param pvCBData is the event callback pointer provided during USBDHIDInit().
// This is a pointer to our HID device structure (&g_sHIDKeybDevice).
// \param ulEvent identifies the event we are being called back for.
// \param ulMsgData 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 unsigned long
HIDKeyboardRxHandler(void *pvCBData, unsigned long ulEvent,
                     unsigned long ulMsgData, void *pvMsgData)
{
    tHIDKeyboardInstance *psInst;
    tUSBDHIDKeyboardDevice *psDevice;

    //
    // Make sure we didn't get a NULL pointer.
    //
    ASSERT(pvCBData);

    //
    // Get a pointer to our instance data
    //
    psDevice = (tUSBDHIDKeyboardDevice *)pvCBData;
    psInst = psDevice->psPrivateHIDKbdData;

    //
    // Which event were we sent?
    //
    switch (ulEvent)
    {
        //
        // The host has connected to us and configured the device.
        //
        case USB_EVENT_CONNECTED:
        {
            psInst->ucUSBConfigured = true;

            //
            // Pass the information on to the client.
            //
            psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_CONNECTED,
                                  0, (void *)0);

            break;
        }

        //
        // The host has disconnected from us.
        //
        case USB_EVENT_DISCONNECTED:
        {
            psInst->ucUSBConfigured = false;

            //
            // Pass the information on to the client.
            //
            psDevice->pfnCallback(psDevice->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 ulMsgValue parameter in this case.  Set the report pointer
            // in *pvMsgData and return the length of the report in bytes.
            //
            *(unsigned char **)pvMsgData = psInst->pucReport;
            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((unsigned long)pvMsgData == KEYB_OUT_REPORT_SIZE )
            {
                //
                // Yes - return our pointer.
                //
                return((unsigned long)psInst->pucDataBuffer);
            }
            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->ucLEDStates != psInst->pucDataBuffer[0])
            {
                //
                // Note the new LED states.
                //
                psInst->ucLEDStates = psInst->pucDataBuffer[0];

                //
                // Pass the information on to the client.
                //
                psDevice->pfnCallback(psDevice->pvCBData,
                                      USBD_HID_KEYB_EVENT_SET_LEDS,
                                      psInst->pucDataBuffer[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->ucProtocol = ulMsgData;
            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->ucProtocol);
        }

        //
        // Pass ERROR, SUSPEND and RESUME to the client unchanged.
        //
        case USB_EVENT_ERROR:
        case USB_EVENT_SUSPEND:
        case USB_EVENT_RESUME:
        {
            return(psDevice->pfnCallback(psDevice->pvCBData, ulEvent,
                                         ulMsgData, pvMsgData));
        }

        //
        // We ignore all other events.
        //
        default:
        {
            break;
        }
    }
    return(0);
}

//*****************************************************************************
//
// HID device class transmit channel event handler function.
//
// \param pvCBData is the event callback pointer provided during USBDHIDInit().
// This is a pointer to our HID device structure (&g_sHIDKeybDevice).
// \param ulEvent identifies the event we are being called back for.
// \param ulMsgData 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 unsigned long
HIDKeyboardTxHandler(void *pvCBData, unsigned long ulEvent,
                     unsigned long ulMsgData, void *pvMsgData)
{
    tHIDKeyboardInstance *psInst;
    tUSBDHIDKeyboardDevice *psDevice;
    tUSBDHIDDevice *psHIDDevice;
    unsigned long ulCount;

    //
    // Make sure we didn't get a NULL pointer.
    //
    ASSERT(pvCBData);

    //
    // Get a pointer to our instance data
    //
    psDevice = (tUSBDHIDKeyboardDevice *)pvCBData;
    psInst = psDevice->psPrivateHIDKbdData;
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Which event were we sent?
    //
    switch (ulEvent)
    {
        //
        // 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.
                //
                ulCount = USBDHIDReportWrite((void *)psHIDDevice,
                                             psInst->pucReport,
                                             KEYB_IN_REPORT_SIZE, true);

                //
                // If we scheduled the report for transmission, clear the
                // change flag.
                //
                if(ulCount != 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.
            //
            psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_TX_COMPLETE,
                                  ulMsgData, (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 ucUsageCode 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 tBoolean
AddKeyToPressedList(tHIDKeyboardInstance *psInst, unsigned char ucUsageCode)
{
    unsigned long ulLoop;
    tBoolean 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(ulLoop = 0; ulLoop < (unsigned long)psInst->ucKeyCount; ulLoop++)
    {
        //
        // Is this key already included in the list of keys in the pressed
        // state?
        //
        if(ucUsageCode == psInst->pucKeysPressed[ulLoop])
        {
            //
            // 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(ulLoop >= psInst->ucKeyCount)
    {
        if(psInst->ucKeyCount < KEYB_MAX_CHARS_PER_REPORT)
        {
            //
            // We have room so store the new key press in the list.
            //
            psInst->pucKeysPressed[psInst->ucKeyCount] = ucUsageCode;
            psInst->ucKeyCount++;
            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 ucUsageCode 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 tBoolean
RemoveKeyFromPressedList(tHIDKeyboardInstance *psInst,
                         unsigned char ucUsageCode)
{
    unsigned long ulLoop;
    unsigned long ulPos;

    //
    // Keep the compiler happy by setting ulPos to something.
    //
    ulPos = 0;

    //
    // Find the usage code in the current list.
    //
    for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++)
    {
        if(psInst->pucKeysPressed[ulLoop] == ucUsageCode)
        {
            ulPos = ulLoop;
            break;
        }
    }

    //
    // If we dropped out at the end of the loop, we couldn't find the code so
    // just return false.
    //
    if(ulLoop == 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(ulLoop = (ulPos + 1); ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++)
    {
        psInst->pucKeysPressed[ulLoop - 1] = psInst->pucKeysPressed[ulLoop];
    }

    //
    // Clear the last entry in the array and adjust the number of keys in the
    // array.
    //
    psInst->pucKeysPressed[KEYB_MAX_CHARS_PER_REPORT - 1] =
        HID_KEYB_USAGE_RESERVED;
    psInst->ucKeyCount--;

    //
    // Tell the caller we were successful.
    //
    return(true);
}

//*****************************************************************************
//
//! Initializes HID keyboard device operation for a given USB controller.
//!
//! \param ulIndex is the index of the USB controller which is to be
//! initialized for HID keyboard device operation.
//! \param psDevice 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 psDevice 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 psDevice pointer on success.
//
//*****************************************************************************
void *
USBDHIDKeyboardInit(unsigned long ulIndex,
                    const tUSBDHIDKeyboardDevice *psDevice)
{
    void *pvRetcode;
    tUSBDHIDDevice *psHIDDevice;

    //
    // Check parameter validity.
    //
    ASSERT(psDevice);
    ASSERT(psDevice->ppStringDescriptors);
    ASSERT(psDevice->psPrivateHIDKbdData);
    ASSERT(psDevice->pfnCallback);

    //
    // Get a pointer to the HID device data.
    //
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Call the common initialization routine.
    //
    pvRetcode = USBDHIDKeyboardCompositeInit(ulIndex, psDevice);

    //
    // 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(ulIndex, psHIDDevice);

        return((void *)psDevice);
    }
    else
    {
        return((void *)0);
    }
}

//*****************************************************************************
//
//! Initializes HID keyboard device operation for a given USB controller.
//!
//! \param ulIndex is the index of the USB controller which is to be
//! initialized for HID keyboard device operation.
//! \param psDevice points to a structure containing parameters customizing
//! the operation of the HID keyboard 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.
//!
//! \return Returns zero on failure or a non-zero instance value that should be
//! used with the remaining USB HID Keyboard APIs.
//
//*****************************************************************************
void *
USBDHIDKeyboardCompositeInit(unsigned long ulIndex,
                             const tUSBDHIDKeyboardDevice *psDevice)
{
    tHIDKeyboardInstance *psInst;
    unsigned long ulLoop;
    tUSBDHIDDevice *psHIDDevice;

    //
    // Check parameter validity.
    //
    ASSERT(psDevice);
    ASSERT(psDevice->ppStringDescriptors);
    ASSERT(psDevice->psPrivateHIDKbdData);
    ASSERT(psDevice->pfnCallback);

    //
    // Get a pointer to our instance data
    //
    psInst = psDevice->psPrivateHIDKbdData;

    //
    // Initialize the various fields in our instance structure.
    //
    psInst->ucUSBConfigured = 0;
    psInst->ucProtocol = USB_HID_PROTOCOL_REPORT;
    psInst->sReportIdle.ucDuration4mS = 125;
    psInst->sReportIdle.ucReportID = 0;
    psInst->sReportIdle.ulTimeSinceReportmS = 0;
    psInst->sReportIdle.usTimeTillNextmS = 0;
    psInst->ucLEDStates = 0;
    psInst->ucKeyCount = 0;
    for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++)
    {
        psInst->pucKeysPressed[ulLoop] = HID_KEYB_USAGE_RESERVED;
    }

    psInst->eKeyboardState = HID_KEYBOARD_STATE_UNCONFIGURED;

    //
    // Get a pointer to the HID device data.
    //
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Initialize the HID device class instance structure based on input from
    // the caller.
    //
    psHIDDevice->usPID = psDevice->usPID;
    psHIDDevice->usVID = psDevice->usVID;
    psHIDDevice->usMaxPowermA = psDevice->usMaxPowermA;
    psHIDDevice->ucPwrAttributes = psDevice->ucPwrAttributes;
    psHIDDevice->ucSubclass = USB_HID_SCLASS_BOOT;
    psHIDDevice->ucProtocol = USB_HID_PROTOCOL_KEYB;
    psHIDDevice->ucNumInputReports = 1;
    psHIDDevice->psReportIdle = 0;
    psHIDDevice->pfnRxCallback = HIDKeyboardRxHandler;
    psHIDDevice->pvRxCBData = (void *)psDevice;
    psHIDDevice->pfnTxCallback = HIDKeyboardTxHandler;
    psHIDDevice->pvTxCBData = (void *)psDevice;
    psHIDDevice->bUseOutEndpoint = false,
    psHIDDevice->psHIDDescriptor = &g_sKeybHIDDescriptor;
    psHIDDevice->ppClassDescriptors = g_pKeybClassDescriptors;
    psHIDDevice->ppStringDescriptors = psDevice->ppStringDescriptors;
    psHIDDevice->ulNumStringDescriptors = psDevice->ulNumStringDescriptors;
    psHIDDevice->psPrivateHIDData = &psInst->sHIDInstance;
    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(ulIndex, psHIDDevice));
}

//*****************************************************************************
//
//! Shuts down the HID keyboard device.
//!
//! \param pvInstance 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
//! pvInstance instance may not me used in any other call to the HID keyboard
//! device other than USBDHIDKeyboardInit().
//!
//! \return None.
//
//*****************************************************************************
void
USBDHIDKeyboardTerm(void *pvInstance)
{
    tUSBDHIDKeyboardDevice *psDevice;
    tUSBDHIDDevice *psHIDDevice;

    ASSERT(pvInstance);

    //
    // Get a pointer to the device.
    //
    psDevice = (tUSBDHIDKeyboardDevice *)pvInstance;

    //
    // Get a pointer to the HID device data.
    //
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Mark the device as no longer configured.
    //
    psDevice->psPrivateHIDKbdData->ucUSBConfigured = 0;

    //
    // Terminate the low level HID driver.
    //
    USBDHIDTerm(psHIDDevice);
}

//*****************************************************************************
//
//! Sets the client-specific pointer parameter for the keyboard callback.
//!
//! \param pvInstance 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 pvInstance 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 *pvInstance, void *pvCBData)
{
    void *pvOldCBData;
    tUSBDHIDKeyboardDevice *psKeyboard;

    //
    // Check for a NULL pointer in the device parameter.
    //
    ASSERT(pvInstance);

    //
    // Get a pointer to our keyboard device.
    //
    psKeyboard = (tUSBDHIDKeyboardDevice *)pvInstance;

    //
    // 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 pvInstance is the pointer to the device instance structure as
//! returned by USBDHIDKeyboardInit().
//! \param ucModifiers 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 ucUsageCode 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.
//
//*****************************************************************************
unsigned long
USBDHIDKeyboardKeyStateChange(void *pvInstance, unsigned char ucModifiers,
                              unsigned char ucUsageCode, tBoolean bPress)
{
    tBoolean bRetcode;
    unsigned long ulLoop;
    unsigned long ulCount;
    tHIDKeyboardInstance *psInst;
    tUSBDHIDKeyboardDevice *psDevice;
    tUSBDHIDDevice *psHIDDevice;

    psDevice = (tUSBDHIDKeyboardDevice *)pvInstance;

    //
    // Get a pointer to the HID device data.
    //
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Assume all is well until we determine otherwise.
    //
    bRetcode = true;

    //
    // Get a pointer to our instance data
    //
    psInst = psDevice->psPrivateHIDKbdData;

    //
    // Update the global keyboard report with the information passed.
    //
    psInst->pucReport[0] = ucModifiers;
    psInst->pucReport[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(ucUsageCode != 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, ucUsageCode);
        }
        else
        {
            //
            // A key has been released - remove it from the list.
            //
            bRetcode = RemoveKeyFromPressedList(psInst, ucUsageCode);

            //
            // 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(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++)
        {
            psInst->pucReport[2 + ulLoop] = (bRetcode ?
                  psInst->pucKeysPressed[ulLoop] : HID_KEYB_USAGE_ROLLOVER);
        }
    }

    //
    // If we are not configured, return an error here before trying to send
    // anything.
    //
    if(!psInst->ucUSBConfigured)
    {
        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;
        ulCount = USBDHIDReportWrite((void *)psHIDDevice,
                                     psInst->pucReport, KEYB_IN_REPORT_SIZE,
                                     true);

        //
        // Did we schedule a packet for transmission correctly?
        //
        if(!ulCount)
        {
            //
            // 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 pvInstance is the pointer to the keyboard device instance structure.
//! \param ucPower 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 *pvInstance, unsigned char ucPower)
{
    tUSBDHIDKeyboardDevice *psDevice;
    tUSBDHIDDevice *psHIDDevice;

    ASSERT(pvInstance);

    //
    // Get the keyboard device pointer.
    //
    psDevice = (tUSBDHIDKeyboardDevice *)pvInstance;

    //
    // Get a pointer to the HID device data.
    //
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Pass the request through to the lower layer.
    //
    USBDHIDPowerStatusSet((void *)psHIDDevice, ucPower);
}

//*****************************************************************************
//
//! Requests a remote wake up to resume communication when in suspended state.
//!
//! \param pvInstance 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.
//
//*****************************************************************************
tBoolean
USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance)
{
    tUSBDHIDKeyboardDevice *psDevice;
    tUSBDHIDDevice *psHIDDevice;

    ASSERT(pvInstance);

    //
    // Get the keyboard device pointer.
    //
    psDevice = (tUSBDHIDKeyboardDevice *)pvInstance;

    //
    // Get a pointer to the HID device data.
    //
    psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice;

    //
    // Pass the request through to the lower layer.
    //
    return(USBDHIDRemoteWakeupRequest((void *)psHIDDevice));
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
