Source file: /~heha/ewa/Motor/Maxmaus4.zip/usblib/usbdhid.cpp

// TITLE: USB HID device class driver
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
extern "C"{
#include "debug.h"
}
#include "usb.h"
#include "usbdevice.h"
#include "usbdhid.h"

//*****************************************************************************
//
// The subset of endpoint status flags that we consider to be reception
// errors.  These are passed to the client via USB_EVENT_ERROR if seen.
//
//*****************************************************************************
#define USB_RX_ERROR_FLAGS      (USBERR_DEV_RX_DATA_ERROR |                   \
                                 USBERR_DEV_RX_OVERRUN |                      \
                                 USBERR_DEV_RX_FIFO_FULL)

//*****************************************************************************
//
// Marker used to indicate that a given HID descriptor cannot be found in the
// client-supplied list.
//
//*****************************************************************************
#define HID_NOT_FOUND           0xFFFFFFFF

//*****************************************************************************
//
// Flags that may appear in ui16DeferredOpFlags to indicate some operation that
// has been requested but could not be processed at the time it was received.
// Each deferred operation is defined as the bit number that should be set in
// tHIDInstance->ui16DeferredOpFlags to indicate that the operation is pending.
//
//*****************************************************************************
#define HID_DO_PACKET_RX        5
#define HID_DO_SEND_IDLE_REPORT 6

//*****************************************************************************
//
// Endpoints to use for each of the required endpoints in the driver.
//
//*****************************************************************************
#define INT_IN_ENDPOINT         USB_EP_3
#define INT_OUT_ENDPOINT        USB_EP_3

//*****************************************************************************
// Forward references for device handler callbacks
//*****************************************************************************
//static void HandleEndpoints(tUSBDHIDDevice*pvHIDInstance, uint32_t ui32Status);
//static void HandleDevice(tUSBDHIDDevice*pvHIDInstance, uint32_t ui32Request,void *pvRequestData);

//*****************************************************************************
//
// Set or clear deferred operation flags in an "atomic" manner.
//
// \param pui16DeferredOp points to the flags variable which is to be modified.
// \param ui16Bit indicates which bit number is to be set or cleared.
// \param bSet indicates the state that the flag must be set to.  If \b true,
// the flag is set, if \b false, the flag is cleared.
//
// This function safely sets or clears a bit in a flag variable.  The operation
// makes use of bitbanding to ensure that the operation is atomic (no read-
// modify-write is required).
//
// \return None.
//
//*****************************************************************************
static void
SetDeferredOpFlag(volatile uint16_t *pui16DeferredOp, uint16_t ui16Bit,
                  bool bSet)
{
    //
    // Set the flag bit to 1 or 0 using a bitband access.
    //
#ifdef __TMS320C28XX__
    HWREGBITHW(pui16DeferredOp, ui16Bit, bSet ? 1 : 0);
#else
    HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0;
#endif

}
#if 0
//*****************************************************************************
//
// This function is called to clear the counter used to keep track of the time
// elapsed since a given report was last sent.
//
// \param psHIDDevice points to the HID device structure whose report timer is
// to be cleared.
// \param ui8ReportID is the first byte of the report to be sent.  If this
// device offers more than one input report, this value is used to find the
// relevant report timer structure in the psHIDDevice structure.
//
// \return None.
//
//*****************************************************************************
void tUSBDHIDDevice::ClearReportTimer(uint8_t ui8ReportID)const {
  psReportIdle[0].ui32TimeSinceReportmS = 0;
}

//*****************************************************************************
//
// This function is called to clear the idle period timers for each input
// report supported by the device.
//
// \param psHIDDevice points to the HID device structure whose timers are to be
// cleared.
// \param ui32TimemS is the elapsed time in milliseconds since the last call
// to this function.
//
// \return None.
//
//*****************************************************************************
void tUSBDHIDDevice::ClearIdleTimers()const {
  psReportIdle[0].ui16TimeTillNextmS = psReportIdle[0].ui8Duration4mS * 4;
}

//*****************************************************************************
//
// This function is called periodically to allow us to process the report idle
// timers.
//
// \param psHIDDevice points to the HID device structure whose timers are to be
// updated.
// \param ui32ElapsedmS indicates the number of milliseconds that have elapsed
// since the last call to this function.
//
// \return None.
//
//*****************************************************************************
void tUSBDHIDDevice::ProcessIdleTimers(uint32_t ui32ElapsedmS) {
    uint32_t ui32SizeReport;
    uint8_t*pvReport;
    bool bDeferred;

    // We have not had to defer any report transmissions yet.
    bDeferred = false;
    // Look at each of the input report idle timers in turn.
        // Update the time since the last report was sent.
        psReportIdle[0].ui32TimeSinceReportmS += ui32ElapsedmS;
        // Is this timer running?
        if (psReportIdle[0].ui8Duration4mS)        {
            // Yes - is it about to expire?
            if (psReportIdle[0].ui16TimeTillNextmS <= ui32ElapsedmS) {
                // The timer is about to expire.  Can we send a report right
                // now?
                if (iHIDTxState == eHIDStateIdle &&
                   !bSendInProgress) {
                    //
                    // We can send a report so send a message to the
                    // application to retrieve its latest report for
                    // transmission to the host.
                    //
                    ui32SizeReport = RxCallback(
                               USBD_HID_EVENT_IDLE_TIMEOUT,
                               psReportIdle[0].ui8ReportID,
                               &pvReport);

                    //
                    // Schedule the report for transmission.
                    //
                    ReportWrite(pvReport,ui32SizeReport, true);

                    //
                    // Reload the timer for the next period.
                    //
                    psReportIdle[0].ui16TimeTillNextmS =
                        psReportIdle[0].ui8Duration4mS * 4;
                }
                else
                {
                    //
                    // We can't send the report straight away so flag it for
                    // transmission as soon as the previous transmission ends.
                    //
                    psReportIdle[0].ui16TimeTillNextmS = 0;
                    bDeferred = true;
                }
            }
            else
            {
                //
                // The timer is not about to expire.  Update the time till the
                // next report transmission.
                //
                psReportIdle[0].ui16TimeTillNextmS -=
                                                                ui32ElapsedmS;
            }
        }

    //
    // If we had to defer transmission of any report, remember this so that we
    // will process it as soon as possible.
    //
    SetDeferredOpFlag(&ui16DeferredOpFlags, HID_DO_SEND_IDLE_REPORT,bDeferred);
}

void tUSBDHIDDevice::SetIdleTimeout(uint8_t ui8ReportID, uint8_t ui8Timeout4mS) {
    // Remember that we have not found any report that needs to be sent
    // immediately.
    bool bReportNeeded = false;
    // Search through all the input reports looking for ones that fit the
    // requirements.
    {
      tHIDReportIdle *psIdle = psReportIdle;

        //
        // If the report ID passed matches the report ID in the idle timer
        // control structure or we were passed a report ID of zero, which
        // indicates that all timers are to be set...
        //
        if(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID))
        {
            //
            // Save the new duration for the idle timer.
            //
            psIdle->ui8Duration4mS = ui8Timeout4mS;

            //
            // Are we enabling the idle timer?  If so, fix up the time until it
            // needs to fire.
            //
            if(ui8Timeout4mS)
            {
                //
                // Determine what the timeout is for this report given the time
                // since the last report of this type was sent.
                //
                if(psIdle->ui32TimeSinceReportmS >=
                   ((uint32_t)ui8Timeout4mS * 4))
                {
                    psIdle->ui16TimeTillNextmS = 0;
                    bReportNeeded = true;
                }
                else
                {
                    psIdle->ui16TimeTillNextmS =
                                        (((uint16_t)ui8Timeout4mS * 4) -
                                         psIdle->ui32TimeSinceReportmS);
                }
            }
        }
    }

    //
    // If we get to here and bReportNeeded is true, this means we need to
    // send back at least one of the input reports as soon as possible.  Try
    // to do this immediately.
    //
    if(bReportNeeded)
    {
        ProcessIdleTimers(0);
    }
}

//*****************************************************************************
//
// Find the idle timeout for a given HID input report.
//
// \param psHIDDevice points to the HID device whose report idle timeout is to
// be found.
// \param ui8ReportID identifies the report whose timeout is requested.  If 0,
// the timeout for the first report is returns, regardless of its ID (or
// whether it has one).
//
// This function returns the current idle timeout for a given HID input report.
// The value returned is expressed in terms of 4mS intervals.  Convert to
// milliseconds by multiplying by 4.  If the return value is 0, this indicates
// that an infinite timeout is currently set and the device will not send the
// report unless a state change occurs.
//
// \return Returns the current idle timeout for the given report.
//
//*****************************************************************************
uint32_t tUSBDHIDDevice::GetIdleTimeout(uint8_t ui8ReportID)const{
    // Search through all the input reports looking for ones that fit the
    // requirements.
    {
      tHIDReportIdle *psIdle = psReportIdle;

        //
        // If the report ID passed matches the report ID in the idle timer
        // control structure or we were passed a report ID of zero, which
        // indicates that all timers are to be set...
        //
        if (!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID))
        {
            //
            // We found a report matching the required ID or we were not passed
            // an ID and we are looking at the first report information.
            //
            return psIdle->ui8Duration4mS;
        }
    }

    //
    // If we drop out, the report could not be found so we need to indicate
    // an error.
    //
    return HID_NOT_FOUND;
}

//*****************************************************************************
//
// Schedule transmission of the next packet forming part of an input report.
//
// \param psHIDInst points to the HID device instance whose input report is to
// be sent.
//
// This function is called to transmit the next packet of an input report
// passed to the driver via a call to USBDHIDReportWrite.  If any data remains
// to be sent, a USB packet is written to the FIFO and scheduled for
// transmission to the host.  The function ensures that reports are sent as
// a sequence of full packets followed by either a single int16_t packet or a
// packet with no data to indicate the end of the transaction.
//
//*****************************************************************************
int32_t tUSBDHIDDevice::ScheduleReportTransmission() {
    // Set the number of bytes to send this iteration.
    uint32_t ui32NumBytes = ui16InReportSize - ui16InReportIndex;
    // Limit individual transfers to the maximum packet size for the endpoint.
    if(ui32NumBytes > 64) ui32NumBytes = 64;
    // Where are we sending this data from?
    uint8_t *pui8Data = pui8InReportData + ui16InReportIndex;
    // Put the data in the correct FIFO.
    int32_t i32Retcode = USBEndpointDataPut(ui32USBBase,
                                        ui8INEndpoint,
                                        pui8Data, ui32NumBytes);
    if (i32Retcode != -1) {
        // Update the count and index ready for the next time round.
        ui16InReportIndex += ui32NumBytes;
        // Send out the current data.
        i32Retcode = USBEndpointDataSend(ui32USBBase,
                                             ui8INEndpoint,
                                             USB_TRANS_IN);
    }

    //
    // Tell the caller how we got on.
    //
    return i32Retcode;
}
#endif
//*****************************************************************************
//
// Receives notifications related to data received from the host.
//
// \param psHIDDevice is the device instance whose endpoint is to be processed.
// \param ui32Status is the USB interrupt status that caused this function to
// be called.
//
// This function is called from HandleEndpoints for all interrupts signaling
// the arrival of data on the interrupt OUT endpoint (in other words, whenever
// the host has sent us a packet of data).  We inform the client that a packet
// is available and, on return, check to see if the packet has been read.  If
// not, we schedule another notification to the client for a later time.
//
// \return Returns \b true on success or \b false on failure.
//
//*****************************************************************************
bool tUSBDHIDDevice::ProcessDataFromHost(uint32_t ui32Status) {
    uint32_t ui32Size;
    //
    // Get the endpoint status to see why we were called.
    //
    uint32_t ui32EPStatus = USBEndpointStatus(USB_BASE, ui8OUTEndpoint);

    //
    // Clear the status bits.
    //
    USBDevEndpointStatusClear(USB_BASE, ui8OUTEndpoint,
                                  ui32EPStatus);

    //
    // Has a packet been received?
    //
    if(ui32EPStatus & USB_DEV_RX_PKT_RDY)
    {
        //
        // Set the flag we use to indicate that a packet read is pending.  This
        // will be cleared if the packet is read.  If the client does not read
        // the packet in the context of the USB_EVENT_RX_AVAILABLE callback,
        // the event will be signaled later during tick processing.
        //
        SetDeferredOpFlag(&ui16DeferredOpFlags, HID_DO_PACKET_RX,
                          true);

        //
        // How big is the packet we have just been sent?
        //
        ui32Size = USBEndpointDataAvail(ui32USBBase, ui8OUTEndpoint);
        // The receive channel is not blocked so let the caller know
        // that a packet is waiting.  The parameters are set to indicate
        // that the packet has not been read from the hardware FIFO yet.
        RxCallback(USB_EVENT_RX_AVAILABLE, ui32Size,0);
    }else{
        //
        // No packet was received.  Some error must have been reported.  Check
        // and pass this on to the client if necessary.
        //
        if (ui32EPStatus & USB_RX_ERROR_FLAGS)
        {
            // This is an error we report to the client so...
            RxCallback(USB_EVENT_ERROR,ui32EPStatus & USB_RX_ERROR_FLAGS,0);
        }
        return false;
    }
    return true;
}

//*****************************************************************************
//
// Receives notifications related to data sent to the host.
//
// \param psHIDDevice is the device instance whose endpoint is to be processed.
// \param ui32Status is the USB interrupt status that caused this function to
// be called.
//
// This function is called from HandleEndpoints for all interrupts originating
// from the interrupt IN endpoint (in other words, whenever data has been
// transmitted to the USB host).  We examine the cause of the interrupt and,
// if due to completion of a transmission, notify the client.
//
// \return Returns \b true on success or \b false on failure.
//
//*****************************************************************************
bool tUSBDHIDDevice::ProcessDataToHost(uint32_t ui32Status) {
    uint32_t ui32EPStatus;

    // Get the endpoint status to see why we were called.
    ui32EPStatus = USBEndpointStatus(ui32USBBase,ui8INEndpoint);
    // Clear the status bits.
    USBDevEndpointStatusClear(ui32USBBase,ui8INEndpoint,ui32EPStatus);
    // Our last packet was transmitted successfully.  Is there any more data to
    // send or have we finished sending the whole report?  We know we finished
    // if the ui16InReportIndex has reached the ui16InReportSize value.
    //
    if (ui16InReportSize == ui16InReportIndex) {
        // We finished sending the last report so are idle once again.
        iHIDTxState = eHIDStateIdle;
        // Notify the client that the report transmission completed.
        RxCallback(USB_EVENT_TX_COMPLETE,ui16InReportSize,0);
        // Do we have any reports to send as a result of idle timer timeouts?
        if (ui16DeferredOpFlags & ((uint32_t)1 << HID_DO_SEND_IDLE_REPORT)) {
            // Yes - send reports for any timers that expired recently.
//            ProcessIdleTimers(0);
        }
    }else{
        // There must be more data or a zero length packet waiting to be sent
        // so go ahead and do this.
//        ScheduleReportTransmission();
    }
    return true;
}

//*****************************************************************************
// Called by the USB stack for any activity involving one of our endpoints
// other than EP0.  This function is a fan out that merely directs the call to
// the correct handler depending upon the endpoint and transaction direction
// signaled in ui32Status.
//*****************************************************************************
void tUSBDHIDDevice::HandleEndpoints(uint32_t ui32Status) {
    // Handler for the interrupt OUT data endpoint.
    if(ui32Status & ((uint32_t)0x10000 << USBEPToIndex(ui8OUTEndpoint)))
    {
        // Data is being sent to us from the host.
        ProcessDataFromHost(ui32Status);
    }
    // Handler for the interrupt IN data endpoint.
    if(ui32Status & ((uint32_t)1 << USBEPToIndex(ui8INEndpoint)))
    {
        ProcessDataToHost(ui32Status);
    }
}

//*****************************************************************************
//
// Called by the USB stack whenever a configuration change occurs.
//
//*****************************************************************************
void tUSBDHIDDevice::cbConfigChange(uint32_t ui32Info) {
    iHIDRxState = eHIDStateIdle;
    iHIDTxState = eHIDStateIdle;

    //
    // If we are not currently connected let the client know we are open for
    // business.
    //
    if (!bConnected)   {
        // Pass the connected event to the client.
        RxCallback(USB_EVENT_CONNECTED, 0, 0);
    }
    // Clear the idle timers for each input report.
//    ClearIdleTimers();
    // Remember that we are connected.
    bConnected = true;
}

//*****************************************************************************
// Device instance specific handler.
//*****************************************************************************
void tUSBDHIDDevice::HandleDevice(uint32_t ui32Request, void *pvRequestData) {
    uint8_t *pui8Data = (uint8_t *)pvRequestData;
    switch(ui32Request) {
        // This was an interface change event.
        case USB_EVENT_COMP_IFACE_CHANGE:
        {
            ui8Interface = pui8Data[1];
            break;
        }
        // This was an endpoint change event.
        case USB_EVENT_COMP_EP_CHANGE:
        {
            // Determine if this is an IN or OUT endpoint that has changed.
            if(pui8Data[0] & 0x80) {
                ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f));
            }else{
                // Extract the new endpoint number.
                ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f);
            }
            break;
        }
    }
}

//*****************************************************************************
//
// This function is called by the USB device stack whenever the device is
// disconnected from the host.
//
//*****************************************************************************
void tUSBDHIDDevice::cbDisconnectHandler() {
    // If we are not currently connected so let the client know we are open for business.
  if (bConnected) RxCallback(USB_EVENT_DISCONNECTED, 0, 0);
        // Pass the disconnected event to the client.
  bConnected = false;    // Remember that we are no longer connected.
  tDeviceInfo::cbDisconnectHandler();
}

//*****************************************************************************
//
// This function is called by the USB device stack whenever a request for a
// non-standard descriptor is received.
//
// \param pvHIDInstance is the instance data for this request.
// \param psUSBRequest points to the request received.
//
// This call parses the provided request structure and determines which
// descriptor is being requested.  Assuming the descriptor can be found, it is
// scheduled for transmission via endpoint zero.  If the descriptor cannot be
// found, the endpoint is stalled to indicate an error to the host.
//
//*****************************************************************************
bool tUSBDHIDDevice::cbGetDescriptor(tUSBRequest *psUSBRequest) {
  const tUSBDHIDDevice *psHIDDevice = this;
    // Which type of class descriptor are we being asked for?
  switch (psUSBRequest->wValueH) {
        // This is a request for a HID report or physical descriptor.
    case 0x22: {
            // Find the index to the descriptor that is being queried.
      const uint8_t*data=psHIDDevice->desc.hidrep;
      uint32_t size=psHIDDevice->desc.hid[7]|psHIDDevice->desc.hid[8]<<8;
      psUSBRequest->limitLength(size);
            // Send the data via endpoint 0.
      SendDataEP0(data,size);
    }return true;
        // This is a request for the HID descriptor (as found in the
        // configuration descriptor following the relevant interface).
    case 0x21: {
            // How big is the HID descriptor?
      const uint8_t*data=psHIDDevice->desc.hid;
      uint32_t size=*data;
      psUSBRequest->limitLength(size);
      SendDataEP0(data,size);
    }return true;
  }
  return false;        // This was an unknown request so stall.
}

//*****************************************************************************
//
// This function is called by the USB device stack whenever a non-standard
// request is received.
//
// \param pvHIDInstance is the instance data for this HID device.
// \param psUSBRequest points to the request received.
//
// This call parses the provided request structure.  Assuming the request is
// understood, it is handled and any required response generated.  If the
// request cannot be handled by this device class, endpoint zero is stalled to
// indicate an error to the host.
//
//*****************************************************************************
bool tUSBDHIDDevice::cbRequestHandler(tUSBRequest *psUSBRequest) {
    // Make sure the request was for this interface.
  if (psUSBRequest->wIndexL) return false;
    // Determine the type of request.
  switch (psUSBRequest->wRequest()) {
        // A Get Report request is used by the host to poll a device for its
        // current state.
    case 0x01C0: {	//USBREQ_GET_REPORT
            // Get the latest report from the application.
      uint8_t *pui8Report;
      uint32_t ui32Size = RxCallback(
       		USBD_HID_EVENT_GET_REPORT,psUSBRequest->wValue(), &pui8Report);
            // Need to ACK the data on end point 0 in this case.
      USBDevEndpointDataAck(ui32USBBase, USB_EP_0, true);
            // ..then send back the requested report.
      bGetRequestPending = true;
      SendDataEP0(pui8Report, ui32Size);
    }return true;
    case 0x06A1: return cbGetDescriptor(psUSBRequest);
  }
  return false;
}
//*****************************************************************************
//
// This function is called by the USB device stack whenever the device is
// reset.  If we are currently connected, send a disconnect event at this
// point.
//
//*****************************************************************************
void tUSBDHIDDevice::cbResetHandler() {
    // Merely call the disconnect handler.  This causes a disconnect message to
    // be sent to the client if we think we are currently connected.
  cbDisconnectHandler();
}

//*****************************************************************************
// This function is called by the USB device stack whenever the bus is put into
// suspend state.
//*****************************************************************************
void tUSBDHIDDevice::cbSuspendHandler() {
  tDeviceInfo::cbSuspendHandler();
  RxCallback(USB_EVENT_SUSPEND, 0,0);	// bubble event
}

//*****************************************************************************
// This function is called by the USB device stack whenever the bus is taken
// out of suspend state.
//*****************************************************************************
void tUSBDHIDDevice::cbResumeHandler() {
  RxCallback(USB_EVENT_RESUME, 0, 0);
  tDeviceInfo::cbResumeHandler();
}

//*****************************************************************************
// This function is called periodically and provides us with a time reference
// and method of implementing delayed or time-dependent operations.
//
// \param pvHIDInstance is the instance data for this request.
// \param ui32TimemS is the elapsed time in milliseconds since the last call
// to this function.
//
// \return None.
//
//*****************************************************************************
void tUSBDHIDDevice::TickHandler(uint32_t ui32TimemS) {
  uint32_t ui32Size;
    // If we are connected, process our idle timers.
  if (bConnected) {
//        ProcessIdleTimers(ui32TimemS);
  }
    // Do we have a deferred receive waiting
  if (ui16DeferredOpFlags & ((uint32_t)1 << HID_DO_PACKET_RX)) {
        // Yes - how big is the waiting packet?
    ui32Size = USBEndpointDataAvail(USB_BASE, ui8OUTEndpoint);
        // Tell the client that there is a packet waiting for it.
    RxCallback(USB_EVENT_RX_AVAILABLE, ui32Size,0);
  }
}

//*****************************************************************************
//
//! Initializes HID device operation for a given USB controller.
//!
//! \param ui32Index is the index of the USB controller which is to be
//! initialized for HID device operation.
//! \param psHIDDevice points to a structure containing parameters customizing
//! the operation of the HID device.
//!
//! An application wishing to offer a USB HID interface to a host system
//! must call this function to initialize the USB controller and attach the
//! device to the USB bus.  This function performs all required USB
//! initialization.
//!
//! On successful completion, this function will return the \e psHIDDevice
//! pointer passed to it.  This must be passed on all future calls from the
//! application to the HID device class driver.
//!
//! The USB HID device class API offers the application a report-based transmit
//! interface for Input reports.  Output reports may be received via the
//! control endpoint or via a dedicated Interrupt OUT endpoint.  If using the
//! dedicated endpoint, report data is delivered to the application packet-by-
//! packet.  If the application uses reports longer than \b USBDHID_MAX_PACKET
//! bytes and would rather receive full reports, it may use a USB buffer above
//! the receive channel to allow full reports to be read.
//!
//! Transmit Operation:
//!
//! Calls to USBDHIDReportWrite() pass complete reports to the driver for
//! transmission.  These will be transmitted to the host using as many USB
//! packets as are necessary to complete the transmission.
//!
//! Once a full Input report has been acknowledged by the USB host, a
//! \b USB_EVENT_TX_COMPLETE event is sent to the application transmit callback
//! to inform it that another report may be transmitted.
//!
//! Receive Operation (when using a dedicated interrupt OUT endpoint):
//!
//! An incoming USB data packet will result in a call to the application
//! callback with event \b USB_EVENT_RX_AVAILABLE.  The application must then
//! call USBDHIDPacketRead(), passing a buffer capable of holding the received
//! packet.  The size of the packet may be determined by calling function
//! USBDHIDRxPacketAvailable() prior to reading the packet.
//!
//! Receive Operation (when not using a dedicated OUT endpoint):
//!
//! If no dedicated OUT endpoint is used, Output and Feature reports are sent
//! from the host using the control endpoint, endpoint zero.  When such a
//! report is received, \b USBD_HID_EVENT_GET_REPORT_BUFFER is sent to the
//! application which must respond with a buffer large enough to hold the
//! report.  The device class driver will then copy the received report into
//! the supplied buffer before sending \b USBD_HID_EVENT_SET_REPORT to indicate
//! that the report is now available.
//!
//! \note The application must not make any calls to the low level USB device
//! interface if interacting with USB via the USB HID device class API.  Doing
//! so will cause unpredictable (though almost certainly unpleasant) behavior.
//!
//! \return Returns NULL on failure or the \e psHIDDevice pointer on success.
//
//*****************************************************************************
void * tUSBDHIDDevice::Init() {
  ASSERT(ppui8StringDescriptors);
    // Initialize the device information structure.
    //*****************************************************************************
    // The device information structure for the USB HID devices.
    //*****************************************************************************
//    static const tCustomHandlers g_sHIDHandlers = {
//        HandleEndpoints,
//        HandleDevice    };

    // Default the endpoints zero before looking for them in the configuration
    // descriptor.
    //
  ui8Interface = 0;
  ui8INEndpoint = 1;
  ui8OUTEndpoint = 0;
    // Initialize the composite entry that is used by the composite device
    // class.

  ui32USBBase = USB_BASE;
  iHIDRxState = eHIDStateUnconfigured;
  iHIDTxState = eHIDStateUnconfigured;
  ui16DeferredOpFlags = 0;
  bConnected = false;
  bGetRequestPending = false;
  bSendInProgress = false;
  ui16InReportIndex = 0;
  ui16InReportSize = 0;
  pui8InReportData = 0;
  ui16OutReportSize = 0;
  pui8OutReportData = 0;
    // Initialize the device info structure for the HID device.
  DeviceInfoInit();
    // Initialize the input report idle timers if any input reports exist.
//  ClearIdleTimers();
    // Initialize the USB tick module, this will prevent it from being
    // initialized later in the call to USBDCDInit();
  InternalUSBTickInit();
    // Return the pointer to the instance indicating that everything went well.
  tDeviceInfo::Init();
  return this;
}

//*****************************************************************************
//! Shuts down the HID device.
//! This function terminates HID operation for the instance supplied and
//! removes the device from the USB bus.  This function should not be called
//! if the HID device is part of a composite device and instead the
//! USBDCompositeTerm() function should be called for the full composite
//! device.
//*****************************************************************************
void tUSBDHIDDevice::Term() {
  tDeviceInfo::Term();
  ui32USBBase = 0;
}

//*****************************************************************************
//
//! Transmits a HID device report to the USB host via the HID interrupt IN
//! endpoint.
//!
//! \param pvHIDInstance is the pointer to the device instance structure as
//! returned by USBDHIDInit().
//! \param pi8Data points to the first byte of data which is to be transmitted.
//! \param ui32Length is the number of bytes of data to transmit.
//! \param bLast is ignored in this implementation.  This parameter is required
//! to ensure compatibility with other device class drivers and USB buffers.
//!
//! This function schedules the supplied data for transmission to the USB
//! host in a single USB transaction using as many packets as it takes to send
//! all the data in the report.  If no transmission is currently ongoing,
//! the first packet of data is immediately copied to the relevant USB endpoint
//! FIFO for transmission.  Whenever all the report data has been acknowledged
//! by the host, a \b USB_EVENT_TX_COMPLETE event will be sent to the
//! application transmit callback indicating that another report can now be
//! transmitted.
//!
//! The caller must ensure that the data pointed to by \e pui8Data remains
//! accessible and unaltered until the \b USB_EVENT_TX_COMPLETE is received.
//!
//! \return Returns the number of bytes actually scheduled for transmission.
//! At this level, this will either be the number of bytes passed or 0 to
//! indicate a failure.
//
//*****************************************************************************
uint32_t tUSBDHIDDevice::ReportWrite(uint8_t *pi8Data, uint32_t ui32Length, bool bLast) {
// Set a flag indicating that we are currently in the process of sending a packet.
  bSendInProgress = true;
    // Can we send the data provided?
  if (iHIDTxState != eHIDStateIdle) {
        // We are in the middle of sending another report.  Return 0 to
        // indicate that we can't send this report until the previous one
        // finishes.
    bSendInProgress = false;
    return 0;
  }
    // Clear the elapsed time since this report was last sent.
//  if (ui32Length) ClearReportTimer(*pi8Data);
    // Keep track of the whereabouts of the report so that we can send it in
    // multiple packets if necessary.
  pui8InReportData = pi8Data;
  ui16InReportIndex = 0;
  ui16InReportSize = ui32Length;
    // Schedule transmission of the first packet of the report.
  iHIDTxState = eHIDStateWaitData;
  int32_t i32Retcode = -1;//ScheduleReportTransmission();
    // Clear the flag we use to indicate that we are in the midst of sending
    // a packet.
  bSendInProgress = false;
    // Did an error occur while trying to send the data?
  if (i32Retcode == -1) return 0;
        // No - tell the caller we sent all the bytes provided.
  return ui32Length;
}

//*****************************************************************************
//
//! Reads a packet of data received from the USB host via the interrupt OUT
//! endpoint (if in use).
//!
//! \param pvHIDInstance is the pointer to the device instance structure as
//! returned by USBDHIDInit().
//! \param pi8Data points to a buffer into which the received data will be
//! written.
//! \param ui32Length is the size of the buffer pointed to by pi8Data.
//! \param bLast indicates whether the client will make a further call to
//! read additional data from the packet.
//!
//! This function reads up to \e ui32Length bytes of data received from the USB
//! host into the supplied application buffer.  If the driver detects that the
//! entire packet has been read, it is acknowledged to the host.
//!
//! The \e bLast parameter is ignored in this implementation since the end of
//! a packet can be determined without relying upon the client to provide
//! this information.
//!
//! \return Returns the number of bytes of data read.
//
//*****************************************************************************
uint32_t tUSBDHIDDevice::PacketRead(uint8_t *pi8Data, uint32_t ui32Length, bool bLast) {
    uint32_t ui32EPStatus, ui32Count, ui32Pkt;
    int32_t i32Retcode;

    // Does the relevant endpoint FIFO have a packet waiting for us?
    ui32EPStatus = USBEndpointStatus(ui32USBBase, ui8OUTEndpoint);
    if(ui32EPStatus & USB_DEV_RX_PKT_RDY)
    {
        //
        // How many bytes are available for us to receive?
        //
        ui32Pkt = USBEndpointDataAvail(ui32USBBase,ui8OUTEndpoint);
        // Get as much data as we can.
        ui32Count = ui32Length;
        i32Retcode = USBEndpointDataGet(ui32USBBase,ui8OUTEndpoint,
                                            pi8Data, &ui32Count);
        // Did we read the last of the packet data?
        if(ui32Count == ui32Pkt) {
            // Clear the endpoint status so that we know no packet is waiting.
            USBDevEndpointStatusClear(ui32USBBase,ui8OUTEndpoint,ui32EPStatus);
            // Acknowledge the data, thus freeing the host to send the
            // next packet.
            USBDevEndpointDataAck(ui32USBBase,ui8OUTEndpoint, true);
            // Clear the flag we set to indicate that a packet read is pending.
            SetDeferredOpFlag(&ui16DeferredOpFlags,HID_DO_PACKET_RX, false);
        }
        // If all went well, tell the caller how many bytes they got.
        if(i32Retcode != -1) {
            return ui32Count;
        }
    }
    // No packet was available or an error occurred while reading so tell
    // the caller no bytes were returned.
    return 0;
}

//*****************************************************************************
//
//! Returns the number of free bytes in the transmit buffer.
//!
//! \param pvHIDInstance is the pointer to the device instance structure as
//! returned by USBDHIDInit().
//!
//! This function indicates to the caller whether or not it is safe to send a
//! new report using a call to USBDHIDReportWrite().  The value returned will
//! be the maximum USB packet size (\b USBDHID_MAX_PACKET) if no transmission
//! is currently outstanding or 0 if a transmission is in progress.  Since the
//! function USBDHIDReportWrite() can accept full reports longer than a single
//! USB packet, the caller should be aware that the returned value from this
//! class driver, unlike others, does not indicate the maximum size of report
//! that can be written but is merely an indication that another report can be
//! written.
//!
//! \return Returns 0 if an outgoing report is still being transmitted or
//! \b USBDHID_MAX_PACKET if no transmission is currently in progress.
//
//*****************************************************************************
uint32_t tUSBDHIDDevice::TxPacketAvailable() {
    // Do we have a packet transmission currently ongoing?
  if (iHIDTxState != eHIDStateIdle)  return 0;
        // We are not ready to receive a new packet so return 0.
        //
        // We can receive a packet so return the max packet size for the
        // relevant endpoint.
        //
  return 64;
}

//*****************************************************************************
//
//! Determines whether a packet is available and, if so, the size of the
//! buffer required to read it.
//!
//! \param pvHIDInstance is the pointer to the device instance structure as
//! returned by USBDHIDInit().
//!
//! This function may be used to determine if a received packet remains to be
//! read and allows the application to determine the buffer size needed to
//! read the data.
//!
//! \return Returns 0 if no received packet remains unprocessed or the
//! size of the packet if a packet is waiting to be read.
//
//*****************************************************************************
uint32_t tUSBDHIDDevice::RxPacketAvailable() {
    uint32_t ui32EPStatus, ui32Size;
    // Does the relevant endpoint FIFO have a packet waiting for us?
  ui32EPStatus = USBEndpointStatus(ui32USBBase,ui8OUTEndpoint);
  if(ui32EPStatus & USB_DEV_RX_PKT_RDY) {
        // Yes - a packet is waiting.  How big is it?
    ui32Size = USBEndpointDataAvail(ui32USBBase, ui8OUTEndpoint);
    return ui32Size;
  }
        // There is no packet waiting to be received.
  return 0;
}

Detected encoding: ASCII (7 bit)2