/***********************************************************************************

    Filename:     usb_framework.h

    Description:  USB library common functionality.

***********************************************************************************/

#ifndef USBFRAMEWORK_H
#define USBFRAMEWORK_H
/** \addtogroup module_usb_framework USB Framework (usbfw)
 * \brief This module contains USB status information, functions for initialization, USB device reset
 * handling, and most importantly, the framework for transfers on endpoint 0, FIFO access, and endpoint
 * control.
 *
 * \section section_init Framework Initialization
 * The framework and the USB peripheral unit can be initialized once the crystal oscillator (48 MHz for
 * CC1111/CC2511, 32 MHz for CC2531) is running. This is done by \ref usbfwInit(), which:
 * \li Initializes framework variables
 * \li Enables the USB peripheral unit by setting the \c SLEEP.USB_EN bit
 * \li Enables the pull-up resistor via a GPIO pin
 *
 * When completing the last step, the host will be recognize the USB device, and start the enumeration
 * process. To reply to the shortly incoming standard requests, the call to \ref usbfwInit() must be
 * followed immediately by USB Interrupt \ref section_usbirq_initialization.
 *
 * \section section_endpoint_0_transfers Endpoint 0 Transfers
 * The USB interface uses endpoint 0 to perform setup requests of the standard, vendor and class types.
 * Such transfers consist of three phases:
 * - A setup phase, where an 8-byte \ref USB_SETUP_HEADER is transferred to the device.
 * - An IN/OUT data phase, if the length field of the \ref USB_SETUP_HEADER structure is non-zero.
 * - A handshake phase, where the application can stall the endpoint to indicate error conditions.
 *
 * The setup handler, \ref usbfwSetupHandler(), takes care of the low-level parts of these transfers,
 * including the IN/OUT buffering during the data phase (when there is one). The transfers fall into two
 * categories:
 * - Most standard requests are processed internally, with little or no intervention form the user
 *   application. This is done by the \ref module_usb_standard_requests module.
 * - Vendor and class requests are application specific and must always be processed by the application.
 *   Whenever such a request is received, the following hooks will be called between the phases:
 *     - \ref usbcrHookProcessOut(): Class requests with OUT data phase
 *     - \ref usbcrHookProcessIn(): Class requests with IN data phase
 *     - \ref usbvrHookProcessOut(): Vendor requests with OUT data phase
 *     - \ref usbvrHookProcessIn(): Vendor requests with IN data phase
 *
 * \section section_setup_handler_usage Setup Handler Usage
 * This section describes what is required to make the vendor and class request hooks work. This
 * information also applies to the standard requests that needs application processing
 * (\ref usbsrHookSetDescriptor(), \ref usbsrHookSynchFrame(), and some cases of
 * \ref usbsrHookSetFeature() and \ref usbsrHookClearFeature()).
 *
 * The transactions are made using a clean and efficient interface, consisting of two data structures and
 * the endpoint 0 status byte:
 * - The endpoint status is initially \ref EP_IDLE, which basically means that the setup handler is ready
 *   for a new setup phase (a new request). Upon an incoming request, the processing hook is called, and
 *   the \ref usbSetupHeader structure contains the 8 bytes received during the setup phase. At this
 *   point there are four different outcomes:
 *     - If the request is unknown or contains errors, the endpoint should be stalled. This is done by
 *       setting the endpoint status to \ref EP_STALL.
 *     - If there is no data phase (the length field is zero), the endpoint status should just remain in
 *       it's current state, \ref EP_IDLE.
 *     - If the request has an IN data phase, the \ref usbSetupData structure must be prepared. This
 *       includes setting a pointer to where IN data should be taken from, and the number of bytes to be
 *       transferred (usually the same number as indicated by the length field, but it can also be a
 *       lower number). The endpoint state is then changed to \ref EP_TX.
 *     - If the request has an OUT data phase, the \ref usbSetupData structure must be prepared. This
 *       includes setting a pointer to where OUT data should be stored, and the number of bytes to be
 *       transferred (always the same number as indicated by the length field). The endpoint state is
 *       then changed to \ref EP_RX.
 * - When the data phase is complete, the processing hook function will be called a second time to notify
 *   the application. Under normal conditions the endpoint status will be either \ref EP_TX or \ref EP_RX,
 *   and does not need to be changed any further (as this is done automatically upon return). If the
 *   endpoint status is \ref EP_CANCEL, it means that the USB host cancelled the setup transfer.
 *
 * The following code examples illustrate practical usage (more code is available in the application
 * example projects):
 *
 * \par Example 1: Endpoint 0 Requests With OUT Data phase
 *
 * \code
 * uint8 pLcdBuffer[358];
 *
 * void usbvrHookProcessOut(void) {
 *
 *    // When this vendor request is received, we should either update a part of pLcdBuffer[] or leave
 *    // it as it is, and then refresh the LCD display. The index field of the setup header contains the
 *    // index of the first character to be updated, and the length field how many characters to update.
 *    if (usbSetupHeader.request == VR_LCD_UPDATE) {
 *
 *       // First the endpoint status is EP_IDLE... (we have just received the Setup packet)
 *       if (usbfwData.ep0Status == EP_IDLE) {
 *
 *          // There is no new data -> Just refresh the display
 *          if (usbSetupHeader.length == 0) {
 *             lcdRefresh();
 *             // There is no change to the endpoint status in this case
 *
 *          // The PC wants to send data that will be stored outside pLcdBuffer -> stall the endpoint!
 *          } else if ((usbSetupHeader.length > sizeof(pLcdBuffer) ||
 *                     (usbSetupHeader.index >= sizeof(pLcdBuffer) ||
 *                     ((usbSetupHeader.index + usbSetupHeader.length) > sizeof(pLcdBuffer)) {
 *             usbfwData.ep0Status = EP_STALL;
 *
 *          // Prepare for OUT data phase, setup the data buffer to receive the LCD data
 *          } else {
 *             usbSetupData.pBuffer = &pLcdBuffer[usbSetupHeader.index];
 *             usbSetupData.bytesLeft = usbSetupHeader.length;
 *             usbfwData.ep0Status = EP_RX;
 *          }
 *
 *       // Then the endpoint status is EP_RX (remember: we did that here when setting up the buffer)
 *       } else if (usbfwData.ep0Status == EP_RX) {
 *          // usbfwSetupHandler() has now updated pLcdBuffer, so all we need to do is refresh the LCD
 *          lcdRefresh();
 *          // usbfwData.ep0Status is automatically reset to EP_IDLE when returning to usbfwSetupHandler()
 *       }
 *
 *    // Unknown vendor request?
 *    } else {
 *       usbfwData.ep0Status = EP_STALL;
 *    }
 * }
 * \endcode
 *
 * \par Example 2: Endpoint 0 Requests With IN Data phase
 *
 * \code
 * uint8 keyBufferPos;
 * BOOL blockKeyboard;
 * char pKeyBuffer[150];
 *
 * void usbvrProcessIn(void) {
 *
 *    // When this vendor request is received, we should send all registered key-strokes, and reset the
 *    // position counter. New keystrokes are blocked during the transfer to avoid overwriting the buffer
 *    // before it has been sent to the host.
 *    if (usbSetupHeader.request == VR_GET_KEYS) {
 *
 *       // First the endpoint status is EP_IDLE...
 *       if (usbfwData.ep0Status == EP_IDLE) {
 *
 *          // Make sure that we do not send more than the PC asked for
 *          if (usbSetupHeader.length < keyBufferPos) {
 *             usbfwData.ep0Status = EP_STALL;
 *
 *          // Otherwise...
 *          } else {
 *             // Block for new keystrokes
 *             blockKeyboard = TRUE;
 *
 *             // Setup the buffer
 *             usbSetupData.pBuffer = pKeyBuffer;
 *             usbSetupData.bytesLeft = keyBufferPos;
 *             usbfwData.ep0Status = EP_TX;
 *
 *             // Reset the position counter
 *             keyBufferPos = 0;
 *          }
 *
 *       // Then the endpoint status is EP_TX (remember: we did that here when setting up the buffer)
 *       } else if (usbfwData.ep0Status == EP_TX) {
 *
 *          // pKeyBuffer has now been sent to the host, so new keystrokes can safely be registered
 *          blockKeyboard = FALSE;
 *
 *          // usbfwData.ep0Status is automatically reset to EP_IDLE when returning to usbfwSetupHandler()
 *       }
 *
 *    // Unknown request?
 *    } else {
 *       usbfwData.ep0Status = EP_STALL;
 *    }
 * }
 * \endcode
 *
 * If automated data transfer is not desired, the application should set \c usbfwData.ep0Status to
 * either \ref EP_MANUAL_RX or \ref EP_MANUAL_TX instead of \ref EP_RX or \ref EP_TX, respectively. Until
 * the transfer is completed, the processing hook function (e.g. \ref usbvrHookProcessIn()) will be called
 * at every endpoint 0 interrupt.
 * @{
 */
#include "usb_board_cfg.h"
#include "usb_framework_structs.h"

#ifdef EXTERN
   #undef EXTERN
#endif
#ifdef USBFRAMEWORK_C
   #define EXTERN ///< Definition used only for usb_framework.c
#else
   #define EXTERN extern ///< Definition used in other source files to declare external
#endif


//-------------------------------------------------------------------------------------------------------
/// \name Module Data
//@{
EXTERN USBFW_DATA __xdata usbfwData; ///< USBFW internal module data

//@}
//-------------------------------------------------------------------------------------------------------


/// \name Setup Handler Data
//@{
EXTERN USB_SETUP_DATA __xdata usbSetupData; ///< Setup handler data phase configuration
EXTERN USB_SETUP_HEADER __xdata usbSetupHeader; ///< Setup header
//@}
//-------------------------------------------------------------------------------------------------------


//-------------------------------------------------------------------------------------------------------
/// \name Request Type Fields
//@{

// Field masks
#define RT_MASK_DIR       0x80  ///< Request direction bit mask
#define RT_MASK_TYPE      0x60  ///< Request type bit mask
#define RT_MASK_RECIP     0x1F  ///< Request recipient bit mask

// Direction field
#define RT_DIR_IN         0x80  ///< IN Request
#define RT_DIR_OUT        0x00  ///< OUT Request

// Type field
#define RT_TYPE_STD       0x00  ///< Standard Request
#define RT_TYPE_CLASS     0x20  ///< Class Request
#define RT_TYPE_VEND      0x40  ///< Vendor Request

// Recipient field
#define RT_RECIP_DEV      0x00  ///< Device Request
#define RT_RECIP_IF       0x01  ///< Interface Request
#define RT_RECIP_EP       0x02  ///< Endpoint Request
#define RT_RECIP_OTHER    0x03  ///< Other Request

// Type + direction
#define RT_STD_OUT        (RT_TYPE_STD | RT_DIR_OUT)    ///< Standard request, direction is OUT
#define RT_STD_IN         (RT_TYPE_STD | RT_DIR_IN)     ///< Standard request, direction is IN
#define RT_VEND_OUT       (RT_TYPE_VEND | RT_DIR_OUT)   ///< Vendor request, direction is OUT
#define RT_VEND_IN        (RT_TYPE_VEND | RT_DIR_IN)    ///< Vendor request, direction is IN
#define RT_CLASS_OUT      (RT_TYPE_CLASS | RT_DIR_OUT)  ///< Class request, direction is OUT
#define RT_CLASS_IN       (RT_TYPE_CLASS | RT_DIR_IN)   ///< Class request, direction is IN

// Direction + recepient
#define RT_OUT_DEVICE     (RT_DIR_OUT | RT_RECIP_DEV)   ///< Request made to device, direction is OUT
#define RT_IN_DEVICE      (RT_DIR_IN | RT_RECIP_DEV)    ///< Request made to device, direction is IN
#define RT_OUT_INTERFACE  (RT_DIR_OUT | RT_RECIP_IF)    ///< Request made to interface, direction is OUT
#define RT_IN_INTERFACE   (RT_DIR_IN | RT_RECIP_IF)     ///< Request made to interface, direction is IN
#define RT_OUT_ENDPOINT   (RT_DIR_OUT | RT_RECIP_EP)    ///< Request made to endpoint, direction is OUT
#define RT_IN_ENDPOINT    (RT_DIR_IN | RT_RECIP_EP)     ///< Request made to endpoint, direction is IN
//@}
//-------------------------------------------------------------------------------------------------------


//-------------------------------------------------------------------------------------------------------
/// \name Vendor and Class Request Hooks
/// Unused hooks must stall endpoint 0.
//@{

/// Hook which is called upon reception of a class request with OUT data phase
void usbcrHookProcessOut(void);
/// Hook which is called upon reception of a class request with IN data phase
void usbcrHookProcessIn(void);
/// Hook which is called upon reception of a vendor request with OUT data phase
void usbvrHookProcessOut(void);
/// Hook which is called upon reception of a vendor request with IN data phase
void usbvrHookProcessIn(void);

//@}
//-------------------------------------------------------------------------------------------------------


//-------------------------------------------------------------------------------------------------------
/// \name Endpoint Access Macros
/// Note that the endpoint control registers are indexed, meaning that an endpoint must be selected
/// before the control operations listed below can be used. Interrupts using any of these macros, must
/// save the current selection and restore it upon return.
//@{

/// Selects which IN/OUT endpoint (by index 0 to 5) to operate on
#define USBFW_SELECT_ENDPOINT(n)            (USBINDEX = (n))
/// Gets the currently selected IN/OUT endpoint
#define USBFW_GET_SELECTED_ENDPOINT()       (USBINDEX)

/// Stalls the selected IN endpoint
#define USBFW_STALL_IN_ENDPOINT() st (\
    USBCSIL = USBCSIL_SEND_STALL; \
    usbfwData.pEpInStatus[USBINDEX - 1] = EP_HALT; )

/// Returns the stall condition for the selected IN endpoint
#define USBFW_IN_ENDPOINT_STALLED()         (USBCSIL & USBCSIL_SEND_STALL)
/// Flushes the FIFO for the selected IN endpoint (flush twice when using double-buffering)
#define USBFW_FLUSH_IN_ENDPOINT() st (\
    USBCSIL = USBCSIL_FLUSH_PACKET; \
    while (USBCSIL & USBCSIL_FLUSH_PACKET); )

/// Arms the selected IN endpoint, so that contents of the endpoint FIFO can be sent to the host
#define USBFW_ARM_IN_ENDPOINT()             (USBCSIL = USBCSIL_INPKT_RDY)
/// Is the selected IN endpoint disarmed?
#define USBFW_IN_ENDPOINT_DISARMED()        !(USBCSIL & USBCSIL_INPKT_RDY)
/// Is the FIFO for the selected IN endpoint empty?
#define USBFW_IN_ENDPOINT_FIFO_EMPTY()      !(USBCSIL & USBCSIL_PKT_PRESENT)

/// Stalls the selected OUT endpoint
#define USBFW_STALL_OUT_ENDPOINT() st ( \
    USBCSOL = USBCSOL_SEND_STALL; \
    usbfwData.pEpOutStatus[USBINDEX - 1] = EP_HALT; \
}

/// Returns the stall condition for the selected OUT endpoint
#define USBFW_OUT_ENDPOINT_STALLED()        (USBCSOL & USBCSOL_SEND_STALL)
/// Flushes the FIFO for the selected OUT endpoint (flush twice when using double-buffering)
#define USBFW_FLUSH_OUT_ENDPOINT() st(\
    USBCSOL = USBCSOL_FLUSH_PACKET; \
    while (USBCSOL & USBCSOL_FLUSH_PACKET); )

/// Arms the selected OUT endpoint, so that the FIFO can receive data from the host
#define USBFW_ARM_OUT_ENDPOINT()            (USBCSOL = 0)
/// Is the selected OUT endpoint disarmed? If so, there is data waiting in the FIFO
#define USBFW_OUT_ENDPOINT_DISARMED()       (USBCSOL & USBCSOL_OUTPKT_RDY)
/// Returns the number of bytes currently in the FIFO of the selected OUT endpoint, low byte
#define USBFW_GET_OUT_ENDPOINT_COUNT_LOW()  (USBCNTL)
/// Returns the number of bytes currently in the FIFO of the selected OUT endpoint, high byte
#define USBFW_GET_OUT_ENDPOINT_COUNT_HIGH() (USBCNTH)

//@}
//-------------------------------------------------------------------------------------------------------

// Little endian
#define LOBYTEPTR(w)  ( (uint8 __generic *)(&(w)) + 1 )
// Big endian
//#define LOBYTEPTR(w)  ( (uint8 __generic *)&(w) )



//-------------------------------------------------------------------------------------------------------
// Function prototypes
void usbfwInit(void);
void usbfwResetHandler(void);
void usbfwSetupHandler(void);
void usbfwSetAllEpStatus(EP_STATUS status);
void usbfwWriteFifo(uint8 volatile __xdata *pFifo, uint8 count, void __generic *pData);
void usbfwReadFifo(uint8 volatile __xdata *pFifo, uint8 count, void __generic *pData);
//-------------------------------------------------------------------------------------------------------

//@}

/*
+------------------------------------------------------------------------------
|  Copyright 2004-2007 Texas Instruments Incorporated. All rights reserved.
|
|  IMPORTANT: Your use of this Software is limited to those specific rights
|  granted under the terms of a software license agreement between the user who
|  downloaded the software, his/her employer (which must be your employer) and
|  Texas Instruments Incorporated (the "License"). You may not use this Software
|  unless you agree to abide by the terms of the License. The License limits
|  your use, and you acknowledge, that the Software may not be modified, copied
|  or distributed unless embedded on a Texas Instruments microcontroller or used
|  solely and exclusively in conjunction with a Texas Instruments radio
|  frequency transceiver, which is integrated into your product. Other than for
|  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
|  works of, modify, distribute, perform, display or sell this Software and/or
|  its documentation for any purpose.
|
|  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
|  PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
|  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
|  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
|  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
|  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING
|  BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
|  CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
|  SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
|  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
|
|  Should you have any questions regarding your right to use this Software,
|  contact Texas Instruments Incorporated at www.TI.com.
|
+------------------------------------------------------------------------------
*/

#endif
