/*
 * usb_task
 *
 * Copyright (C) 2022 Texas Instruments Incorporated
 * 
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/

/******************************************************************************
 *
 * The vUSBTask configures the USB peripheral of the TM4C123GH6PM MCU to
 * operate in USB device mode and enumerate as a Bulk device.  A counting
 * semaphore is also created which will be used by the USB handler to track
 * if multiple interrupts occur while data is being processed.  To use a
 * counting semaphore, configUSE_COUNTING_SEMAPHORES must be defined and set
 * to 1 in FreeRTOSConfig.h.
 *
 * The prvUSBBulkDataEcho waits for the counting semaphore to be given from the
 * USB handler on RX interrupt and then it processes the data received.  In
 * order to allow the data to be processed by the task instead of within the
 * interrupt handler, calls to the USB library are made to get current state
 * information about the USB stack to determine how many bytes of data need to
 * be processed.
 *
 * The data is read from the USB buffer and the case is reserved before being
 * output back over the USB Bulk interface as an echoed message.  This is just
 * to provide a simple demonstration of both TX and RX functionality.
 *
 * This example project requires the installation of the TivaWare Windows USB
 * Examples.  The installer can be found on www.ti.com/tool/SW-TM4C and is
 * labeled as SW-USB-win-2.2.0.295.msi.  After installing the USB examples,
 * navigate to the chosen installation folder and find usb_bulk_example.exe.
 * On opening, the application will look to connect to a TM4C USB Device, so
 * have this example project loaded and running before starting the demo
 * application.  Once open, just send messages with the application to see them
 * get echoed back.
 *
 */

/* Standard includes. */
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

/* Hardware includes. */
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_uart.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdbulk.h"
#include "utils/uartstdio.h"
#include "usb_bulk_structs.h"
/*-----------------------------------------------------------*/

/*
 * Counting semaphore used to inform the bulk layer another interrupt has been
 * received.
 */
SemaphoreHandle_t xCountSemaphore = NULL;

/*
 * The tasks as described in the comments at the top of this file.
 */
static void prvUSBBulkDataEcho( void *pvParameters );

/*
 * Called by main() to create the USB application.
 */
void vUSBTask( void );

/* 
 * Configure the USB peripheral to function as a USB Device in Bulk mode.
 */
static void prvConfigureUSB( void );
/*-----------------------------------------------------------*/

void vUSBTask( void )
{
    /* Configure the USB peripheral and enumerate it as a Bulk Device. */
    prvConfigureUSB();

    /* Create a counting semaphore that is five spaces deep to indicate how
     * many USB interrupts have arrived indicating more data is available for
     * processing by the USB task. */
    xCountSemaphore = xSemaphoreCreateCounting( 5, 0 );

    if( xCountSemaphore != NULL )
    {
        /* The xTaskCreate parameters in order are:
         *  - The function that implements the task.
         *  - The text name for the output task - for debug only as it is not
         *    used by the kernel.
         *  - The size of the stack to allocate to the task.
         *  - The parameter passed to the task - just to check the functionality.
         *  - The priority assigned to the task.
         *  - The task handle is not required, so NULL is passed. */
        xTaskCreate( prvUSBBulkDataEcho,
                    "USB Bulk",
                    configMINIMAL_STACK_SIZE,
                    NULL,
                    tskIDLE_PRIORITY + 1,
                    NULL );
    }
}
/*-----------------------------------------------------------*/

void prvUSBBulkDataEcho( void *pvParameters )
{
uint32_t ui32Loop, ui32Space, ui32Count, ui32Available;
uint32_t ui32ReadIndex;
uint32_t ui32WriteIndex;
tUSBRingBufObject sTxRing;
tUSBRingBufObject sRxRing;

    for(;;)
    {
        if( xSemaphoreTake( xCountSemaphore, portMAX_DELAY ) == pdPASS )
        {
            /* Get the current buffer information to allow us to write
             * directly to the transmit buffer. */
            USBBufferInfoGet(&g_sTxBuffer, &sTxRing);

            /* Get the current buffer information to allow us to read
             * directly from the receive buffer. */
            USBBufferInfoGet(&g_sRxBuffer, &sRxRing);

            /* How much space is there in the transmit buffer? */
            ui32Space = USBBufferSpaceAvailable(&g_sTxBuffer);
            ui32WriteIndex = sTxRing.ui32WriteIndex;

            /* How many bytes of data is in the receive buffer? */
            ui32Available = USBBufferDataAvailable(&g_sRxBuffer);
            ui32ReadIndex = sRxRing.ui32ReadIndex;

            /* How many characters can we process this time round? */
            ui32Loop = (ui32Space < ui32Available) ? ui32Space : ui32Available;
            ui32Count = ui32Loop;

            while(ui32Loop)
            {
                /* Copy from the receive buffer to the transmit buffer
                 * converting character case on the way. */

                /* Is this a lower case character? */
                if((g_pui8USBRxBuffer[ui32ReadIndex] >= 'a') &&
                        (g_pui8USBRxBuffer[ui32ReadIndex] <= 'z'))
                {
                    /* Convert to upper case and write to the transmit
                     * buffer. */
                    g_pui8USBTxBuffer[ui32WriteIndex] =
                            (g_pui8USBRxBuffer[ui32ReadIndex] - 'a') + 'A';
                }
                else
                {
                    /* Is this an upper case character? */
                    if((g_pui8USBRxBuffer[ui32ReadIndex] >= 'A') &&
                            (g_pui8USBRxBuffer[ui32ReadIndex] <= 'Z'))
                    {
                        /* Convert to lower case and write to the transmit
                         * buffer. */
                        g_pui8USBTxBuffer[ui32WriteIndex] =
                                (g_pui8USBRxBuffer[ui32ReadIndex] - 'Z') + 'z';
                    }
                    else
                    {
                        /* Copy the received character to the transmit
                         * buffer. */
                        g_pui8USBTxBuffer[ui32WriteIndex] =
                                g_pui8USBRxBuffer[ui32ReadIndex];
                    }
                }

                /* Move to the next character taking care to adjust the
                 * pointer for the buffer wrap if necessary. */
                ui32WriteIndex++;
                ui32WriteIndex = (ui32WriteIndex == BULK_BUFFER_SIZE) ?
                        0 : ui32WriteIndex;

                ui32ReadIndex++;
                ui32ReadIndex = (ui32ReadIndex == BULK_BUFFER_SIZE) ?
                        0 : ui32ReadIndex;

                ui32Loop--;
            }

            /* We've processed the data in place so now send the processed
             * data back to the host. */
            USBBufferDataWritten(&g_sTxBuffer, ui32Count);

            /* Since we processed the received data, update the receive
             * buffer to reflect how much data was processed. */
            USBBufferDataRemoved(&g_sRxBuffer, ui32Count);
        }
    }
}
/*-----------------------------------------------------------*/

static void prvConfigureUSB( void )
{
    /* Enable the peripherals used by this application. */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);

    /* Initialize the transmit and receive buffers. */
    USBBufferInit(&g_sTxBuffer);
    USBBufferInit(&g_sRxBuffer);

    /* Forcing device mode so that the VBUS and ID pins are not used or
     * monitored by the USB controller. For USB OTG, this function should
     * not be called.  If the USB Host will supply power, and the LaunchPad
     * power jumper is set to "OTG", this function should not be called. */
    USBStackModeSet(0, eUSBModeForceDevice, 0);

    /* Pass the device information to the USB library and place the device
     * on the bus. */
    USBDBulkInit(0, &g_sBulkDevice);
}
/*-----------------------------------------------------------*/

void vApplicationTickHook( void )
{
    /* This function will be called by each tick interrupt if
        configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h.  User code can be
        added here, but the tick hook is called from an interrupt context, so
        code must not attempt to block, and only the interrupt safe FreeRTOS API
        functions can be used (those that end in FromISR()). */

    /* Only the full demo uses the tick hook so there is no code is
        executed here. */
}


