/******************************************************************************
*
*  System Name : ARM USB Mass Storage Library
*  File Name   : usb0_mscd_bot.c
*  Contents    : BOT Prtotocol functions for USB Mass Storage Class Driver
*  Version     : 0.00.01
*  Device      : ARM
*  Tool-Chain  : 
*  OS          : 
*
*  Note        : 
*                     
*  Copyright (C) 201 Renesas Electronics Corporation and 
*  Renesas Solutions Corp. All rights reserved.
*
*  History     : Feb.12,2014 Ver.0.00.01
******************************************************************************/


/*******************************************************************************
Includes   <System Includes> , "Project Includes"
*******************************************************************************/
#include "usb_hmscd_rza1.h"
#include "usb0_hmscd.h"


/*******************************************************************************
Typedef definitions
*******************************************************************************/


/*******************************************************************************
Macro definitions
*******************************************************************************/


/*******************************************************************************
Imported global variables and functions (from other files)
*******************************************************************************/
extern int32_t    usb0_class_ClearStall( uint16_t device_address, uint16_t Pipe, uint32_t time_out );


/*******************************************************************************
Exported global variables and functions (to be accessed by other files)
*******************************************************************************/


/*******************************************************************************
Private global variables and functions
*******************************************************************************/
static USB_HCD_REQUEST_t    g_usb0_hmscd_trans_data[ USB_HMSCD_MAX_ADDRESS + 1 ];
static USB_HCD_REQUEST_t    g_usb0_hmscd_receive_data[ USB_HMSCD_MAX_ADDRESS + 1 ];

static int32_t    usb0_hmscd_hcd_request_result( USB_HCD_REQUEST_t *usb_hcd_request_msg );
static int32_t    usb0_hmscd_wait( uint16_t device_address );
static int32_t    usb0_hmscd_get_csw2( uint16_t device_address, uint8_t *cswstatus );


/*******************************************************************************
* Function Name: usb0_hmscd_send_cbw
* Description  : Executes the CBW transmission to the device specified by the
*              : argument device_address.
* Arguments    : uint16_t device_address : Device address
* Return Value : int32_t USB_HMSCD_SUCCESS     : Normal end
*              :         USB_HMSCD_ERROR       : Error end
*              :         USB_HMSCD_STALL       : Stall
*              :         USB_HMSCD_DETACH      : USB device detach
*              :         USB_HMSCD_COM_ERR     : USB communication error
*              :         USB_HMSCD_NAK_TIMEOUT : Time-out (Device response)
*******************************************************************************/
int32_t usb0_hmscd_send_cbw( uint16_t device_address )
{
    int32_t              ret;
    int32_t              mscd_retval;
    USB_HCD_REQUEST_t    *msg;

    /* Already get semaphore */

    usb0_hmscd_cbw_tag_count( device_address );

    g_usb0_hmscd_trans_data[device_address].devadr           = device_address;
    g_usb0_hmscd_trans_data[device_address].transfer_address = (void *)&g_usb0_hmscd_cbw[device_address];
    g_usb0_hmscd_trans_data[device_address].complete_proc    = (USB_CALLBACK_HCD_REQUEST)&usb0_hmscd_hcd_request_result;
    g_usb0_hmscd_trans_data[device_address].transfer_length  = (uint32_t)31;
    g_usb0_hmscd_trans_data[device_address].pipe_number      = (uint16_t)g_usb0_hmscd_global[device_address].mscd_out_pipe;
    g_usb0_hmscd_trans_data[device_address].setup_packet     = 0;

    usb0_userdef_os_prcv_mbx( (uint32_t)(USB_HMSCD + device_address), (void **)&msg );

    ret = usb0_api_hcd_request( &g_usb0_hmscd_trans_data[device_address] );
    if( ret == USB_HMSCD_SUCCESS )
    {
        mscd_retval = usb0_hmscd_wait( device_address );
        switch( mscd_retval )
        {
        case    USB_HCD_DATA_NONE:
            ret = USB_HMSCD_SUCCESS;
            break;

        case    USB_HCD_DATA_STALL:
            ret = USB_HMSCD_STALL;
            break;

        case    USB_HCD_DATA_DETACH:
            ret = USB_HMSCD_DETACH;
            break;

        case    USB_HCD_PIPE_COM_ERR:
            ret = USB_HMSCD_COM_ERR;
            break;

        case    USB_HMSCD_NAK_TIMEOUT:
            usb0_api_hcd_force_end( g_usb0_hmscd_global[device_address].mscd_out_pipe );

            ret = USB_HMSCD_NAK_TIMEOUT;
            break;
        default:
            ret = USB_HMSCD_ERROR;
            break;
        }
    }
    else
    {
        ret = USB_HMSCD_ERROR;
    }

    /* Release semaphore at upper layer */

    return ret;
}

/*******************************************************************************
* Function Name: usb0_hmscd_get_data
* Description  : Sends the data receive request to the device specified by the
*              : argument device_address.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *buffer         : Transmit buffer address
*              : uint32_t size           : Transmit request data size (byte count)
*              : uint32_t *bytes         : Byte count of the read data
* Return Value : int32_t USB_HMSCD_SUCCESS     : Normal end
*              :         USB_HMSCD_ERROR       : Error end
*              :         USB_HMSCD_STALL       : Stall
*              :         USB_HMSCD_DETACH      : USB device detach
*              :         USB_HMSCD_COM_ERR     : USB communication error
*              :         USB_HMSCD_NAK_TIMEOUT : Time-out (Device response)
*******************************************************************************/
int32_t usb0_hmscd_get_data( uint16_t device_address, uint8_t *buffer, 
                             uint32_t size, uint32_t *bytes )
{
    int32_t    ret;
    int32_t    mscd_retval;

    /* Already get semaphore */

    g_usb0_hmscd_receive_data[device_address].devadr           = device_address;
    g_usb0_hmscd_receive_data[device_address].transfer_address = (void *)buffer;
    g_usb0_hmscd_receive_data[device_address].complete_proc    = (USB_CALLBACK_HCD_REQUEST)&usb0_hmscd_hcd_request_result;
    g_usb0_hmscd_receive_data[device_address].transfer_length  = (uint32_t)size;
    g_usb0_hmscd_receive_data[device_address].pipe_number      = (uint16_t)g_usb0_hmscd_global[device_address].mscd_in_pipe;

    ret = usb0_api_hcd_request( &g_usb0_hmscd_receive_data[device_address] );
    if( ret == USB_HMSCD_SUCCESS )
    {
        mscd_retval = usb0_hmscd_wait( device_address );
        switch( mscd_retval )
        {
        case    USB_HCD_DATA_OK:
            *bytes = g_usb0_hmscd_receive_data[device_address].transfer_length;
            ret = USB_HMSCD_SUCCESS;
            break;

        case    USB_HCD_DATA_STALL:
            usb0_class_ClearStall( device_address, 
                                   g_usb0_hmscd_global[device_address].mscd_in_pipe,
                                   g_usb0_hmscd_global[device_address].mscd_regist->time_out );
            ret = USB_HMSCD_STALL;
            break;

        case    USB_HCD_DATA_DETACH:
            ret = USB_HMSCD_DETACH;
            break;

        case    USB_HCD_PIPE_COM_ERR:
            ret = USB_HMSCD_COM_ERR;
            break;

        case    USB_HMSCD_NAK_TIMEOUT:
            usb0_api_hcd_force_end( g_usb0_hmscd_global[device_address].mscd_in_pipe );

            ret = USB_HMSCD_NAK_TIMEOUT;
            break;

        default:
            ret = USB_HMSCD_ERROR;
            break;
        }
    }
    else
    {
        ret = USB_HMSCD_ERROR;
    }

    /* Release semaphore at upper layer */

    return ret;
}

/*******************************************************************************
* Function Name: usb0_hmscd_send_data
* Description  : Sends the data transmit request to the device specified by the
*              : argument device_address.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *buffer         : Transmit buffer address
*              : uint32_t size           : Transmit request data size (byte count)
*              : uint32_t *bytes         : Transmit data size (byte count)
* Return Value : int32_t USB_HMSCD_SUCCESS     : Normal end
*              :         USB_HMSCD_ERROR       : Error end
*              :         USB_HMSCD_STALL       : Stall
*              :         USB_HMSCD_DETACH      : USB device detach
*              :         USB_HMSCD_COM_ERR     : USB communication error
*              :         USB_HMSCD_NAK_TIMEOUT : Time-out (Device response)
*******************************************************************************/
int32_t usb0_hmscd_send_data( uint16_t device_address, uint8_t *buffer, 
                              uint32_t size, uint32_t *bytes )
{
    int32_t    ret;
    int32_t    mscd_retval;

    /* Already get semaphore */

    g_usb0_hmscd_trans_data[device_address].devadr           = device_address;
    g_usb0_hmscd_trans_data[device_address].transfer_address = (void *)buffer;
    g_usb0_hmscd_trans_data[device_address].complete_proc    = (USB_CALLBACK_HCD_REQUEST)&usb0_hmscd_hcd_request_result;
    g_usb0_hmscd_trans_data[device_address].transfer_length  = size;
    g_usb0_hmscd_trans_data[device_address].pipe_number      = (uint16_t)g_usb0_hmscd_global[device_address].mscd_out_pipe;

    ret = usb0_api_hcd_request( &g_usb0_hmscd_trans_data[device_address] );
    if( ret == USB_HMSCD_SUCCESS )
    {
        mscd_retval = usb0_hmscd_wait( device_address );
        switch( mscd_retval )
        {
        case    USB_HCD_DATA_NONE:
            *bytes = g_usb0_hmscd_trans_data[device_address].transfer_length;
            ret = USB_HMSCD_SUCCESS;
            break;

        case    USB_HCD_DATA_STALL:
            usb0_class_ClearStall( device_address, 
                                   g_usb0_hmscd_global[device_address].mscd_out_pipe,
                                   g_usb0_hmscd_global[device_address].mscd_regist->time_out );
            ret = USB_HMSCD_STALL;
            break;

        case    USB_HCD_DATA_DETACH:
            ret = USB_HMSCD_DETACH;
            break;

        case    USB_HCD_PIPE_COM_ERR:
            ret = USB_HMSCD_COM_ERR;
            break;

        case    USB_HMSCD_NAK_TIMEOUT:
            usb0_api_hcd_force_end( g_usb0_hmscd_global[device_address].mscd_out_pipe );

            ret = USB_HMSCD_NAK_TIMEOUT;
            break;

        default:
            ret = USB_HMSCD_ERROR;
            break;
        }
    }
    else
    {
        ret = USB_HMSCD_ERROR;
    }

    /* Release semaphore at upper layer */

    return    ret;
}

/*******************************************************************************
* Function Name: usb0_hmscd_get_csw
* Description  : Executes the CSW reception from the device specified by the
*              :argument device_address.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *cswstatus      : CSW command status
* Return Value : int32_t USB_HMSCD_SUCCESS                 : Normal end
*              :         USB_HMSCD_ERROR                   : Error end
*              :         USB_HMSCD_STALL                   : Stall
*              :         USB_HMSCD_SUCCESS_CSW_PHASE_ERROR : CSW phase error
*              :         USB_HMSCD_DETACH                  : USB device detach
*              :         USB_HMSCD_COM_ERR                 : USB communication error
*              :         USB_HMSCD_NAK_TIMEOUT             : Time-out (Device response)
*******************************************************************************/
int32_t usb0_hmscd_get_csw( uint16_t device_address, uint8_t *cswstatus )
{
    int32_t    ret;

    ret = usb0_hmscd_get_csw2( device_address, cswstatus );
    if( ret == USB_HMSCD_STALL )
    {
        ret = usb0_hmscd_get_csw2( device_address, cswstatus );
    }
    else if( ret == USB_HMSCD_NAK_TIMEOUT )
    {
        if( g_usb0_hmscd_cbw[device_address].cbw_cb[ 0 ] == USB_HMSCD_CMD_TEST_UNIT_READY )
        {
            ret = usb0_hmscd_get_csw2( device_address, cswstatus );
            if( ret == USB_HMSCD_STALL )
            {
                ret = usb0_hmscd_get_csw2( device_address, cswstatus );
            }
        }
    }

    return ret;
}

/*******************************************************************************
* Function Name: usb0_hmscd_get_csw2
* Description  : Executes the CSW reception from the device specified by the
*              :argument device_address.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *cswstatus      : CSW command status
* Return Value : int32_t USB_HMSCD_SUCCESS                 : Normal end
*              :         USB_HMSCD_ERROR                   : Error end
*              :         USB_HMSCD_STALL                   : Stall
*              :         USB_HMSCD_SUCCESS_CSW_PHASE_ERROR : CSW phase error
*              :         USB_HMSCD_DETACH                  : USB device detach
*              :         USB_HMSCD_COM_ERR                 : USB communication error
*              :         USB_HMSCD_NAK_TIMEOUT             : Time-out (Device response)
*******************************************************************************/
static int32_t usb0_hmscd_get_csw2( uint16_t device_address,uint8_t *cswstatus )
{
    int32_t              ret;
    int32_t              mscd_retval;
    USB_HCD_REQUEST_t    *msg;

    /* Already get semaphore */

    g_usb0_hmscd_receive_data[device_address].devadr           = device_address;
    g_usb0_hmscd_receive_data[device_address].transfer_address = (void *)&g_usb0_hmscd_csw[device_address];
    g_usb0_hmscd_receive_data[device_address].complete_proc    = (USB_CALLBACK_HCD_REQUEST)&usb0_hmscd_hcd_request_result;
    g_usb0_hmscd_receive_data[device_address].transfer_length  = (uint32_t)13;
    g_usb0_hmscd_receive_data[device_address].pipe_number      = (uint16_t)g_usb0_hmscd_global[device_address].mscd_in_pipe;

    usb0_userdef_os_prcv_mbx( (uint32_t)(USB_HMSCD + device_address), (void **)&msg );

    ret = usb0_api_hcd_request( &g_usb0_hmscd_receive_data[device_address] );
    if( ret == USB_HMSCD_SUCCESS )
    {
        mscd_retval = usb0_hmscd_wait( device_address );
        switch( mscd_retval )
        {
            case    USB_HCD_DATA_OK:
                /* CSW Check */
                ret = usb0_hmscd_check_csw( device_address,cswstatus );
                break;

            case    USB_HCD_DATA_STALL:
                usb0_class_ClearStall( device_address, 
                                       g_usb0_hmscd_global[device_address].mscd_in_pipe, 
                                       g_usb0_hmscd_global[device_address].mscd_regist->time_out );
                ret = USB_HMSCD_STALL;
                break;

            case    USB_HCD_DATA_DETACH:
                ret = USB_HMSCD_DETACH;
                break;

            case    USB_HCD_PIPE_COM_ERR:
                ret = USB_HMSCD_COM_ERR;
                break;

            case    USB_HMSCD_NAK_TIMEOUT:
                usb0_api_hcd_force_end( g_usb0_hmscd_global[device_address].mscd_in_pipe );
                ret = USB_HMSCD_NAK_TIMEOUT;
                break;

            default:
                ret = USB_HMSCD_ERROR;
                break;
        }
    }
    else
    {
        ret = USB_HMSCD_ERROR;
    }

    /* Release semaphore at upper layer */

    return ret;
}

/*******************************************************************************
* Function Name: usb0_hmscd_wait
* Description  : Wait Message from HCD
* Arguments    : uint16_t device_address : Device address
* Return Value : int32_t USB_HMSCD_ERROR       : Error end
*              :         USB_HMSCD_NAK_TIMEOUT : Time-out (Device response)
*              :         others : operation result
*******************************************************************************/
static int32_t usb0_hmscd_wait( uint16_t device_address )
{
    int32_t              err;
    USB_HCD_REQUEST_t    *msg;

    if( g_usb0_hmscd_global[device_address].mscd_regist->time_out == 0 )
    {
        err = usb0_userdef_os_rcv_mbx( (uint32_t)(USB_HMSCD + device_address), (void **)&msg );
        if ( err != USB_HMSCD_SUCCESS )
        {
            return USB_HMSCD_ERROR;
        }
    }
    else
    {
        err = usb0_userdef_os_trcv_mbx( (uint32_t)(USB_HMSCD + device_address), (void **)&msg,
                                        (uint32_t)g_usb0_hmscd_global[device_address].mscd_regist->time_out );
        if( err == USB_HMSCD_TIMEOUT )
        {
            return USB_HMSCD_NAK_TIMEOUT;
        }
        else if( err != USB_HMSCD_SUCCESS )
        {
            return USB_HMSCD_ERROR;
        }
    }

    return msg->status;
}

/*******************************************************************************
* Function Name: usb0_hmscd_hcd_request_result
* Description  : Callback function used by HCD
* Arguments    : USB_HCD_REQUEST_t *usb_hcd_request_msg : message
* Return Value : int32_t USB_HMSCD_SUCCESS : Error end
*              :         USB_HMSCD_ERROR   : OS error
*******************************************************************************/
static int32_t usb0_hmscd_hcd_request_result( USB_HCD_REQUEST_t *usb_hcd_request_msg )
{
    int32_t      err;
    uint16_t     device_address;
    void         *ptr;

    device_address = usb_hcd_request_msg->devadr;

    usb_hcd_request_msg->msg_header = USB_HCD_REQUEST_MESSAGE;
    ptr = (void *)usb_hcd_request_msg;
    err = usb0_userdef_os_snd_mbx( (uint32_t)(USB_HMSCD + device_address), (void *)ptr );

    return err;
}

/* End of File */

