/******************************************************************************
*
*  System Name : ARM USB Mass Storage Library
*  File Name   : usb0_mscd_func.c
*  Contents    : Sub functions for USB Mass Storage Class Driver
*  Version     : 0.00.01
*  Device      : ARM
*  Tool-Chain  : 
*  OS          : 
*
*  Note        : 
*                     
*  Copyright (C) 2014 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)
*******************************************************************************/


/*******************************************************************************
Exported global variables and functions (to be accessed by other files)
*******************************************************************************/
USB_HMSCD_CBW_t       g_usb0_hmscd_cbw[ USB_HMSCD_MAX_ADDRESS + 1 ] __attribute__ ((section ("BSS_NC_HMSCD")));
USB_HMSCD_CSW_t       g_usb0_hmscd_csw[ USB_HMSCD_MAX_ADDRESS + 1 ] __attribute__ ((section ("BSS_NC_HMSCD")));
uint32_t              g_usb0_hmscd_work[ USB_HMSCD_MAX_ADDRESS + 1 ][ 32 / sizeof( uint32_t ) ] __attribute__ ((section ("BSS_NC_HMSCD")));

USB_HMSCD_GLOBAL_t    g_usb0_hmscd_global[ USB_HMSCD_MAX_ADDRESS + 1 ];


/*******************************************************************************
Private global variables and functions
*******************************************************************************/


/*******************************************************************************
* Function Name: usb0_hmscd_cbw_init
* Description  : Initializes the CBW transmit buffer in the mass storage control
*              : driver management area, the CBW signature, and the CBW Tag number.
* Arguments    : uint16_t device_address : Device address
* Return Value : none
*******************************************************************************/
void usb0_hmscd_cbw_init( uint16_t device_address )
{
    uint32_t    loop;
    uint8_t     *ptr;

    g_usb0_hmscd_global[device_address].cbw_tag = 1;

    ptr = (uint8_t *)&g_usb0_hmscd_cbw[device_address];
    for( loop = 0; loop < sizeof(g_usb0_hmscd_cbw[device_address]); loop++ )
    {
        *ptr = 0;
        ptr++;
    }

    g_usb0_hmscd_cbw[device_address].cbw_sig = MSDC_CBW_SIG;
    g_usb0_hmscd_cbw[device_address].cbw_tag = g_usb0_hmscd_global[device_address].cbw_tag;
}

/*******************************************************************************
* Function Name: usb0_hmscd_check_bot_descriptor
* Description  : Judges the descriptor information stored in the argument Table.
*              : Executes the BOT descriptor check only for the interface descriptor
*              : information and returns the result to return value.
*              : As for the information except the interface descriptor,
*              : DEVDRV_ERROR is returned.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *Table          : Descriptor storage address
* Return Value : int32_t  USB_HMSCD_SUCCESS : Normal end
*              :          USB_HMSCD_ERROR   : Error end
*******************************************************************************/
int32_t usb0_hmscd_check_bot_descriptor( uint16_t device_address, uint8_t *Table )
{
    if( Table[ 1 ] != USB_DT_INTERFACE )
    {
        /* not Interface Descriptor */
        return USB_HMSCD_ERROR;
    }

    /* ==== Interface Descriptor ==== */

    if ( Table[ 5 ] != USB_IF_CLS_MASS )
    {
        /* not mass storage class */
        return USB_HMSCD_ERROR;
    }

    /* check Interface Descriptor ( subclass ) */
    if ( (Table[ 6 ] == MSCD_ATAPI) || (Table[ 6 ] == MSCD_SFF8020I) )
    {
        /* MSCD_ATAPI Command */
        g_usb0_hmscd_global[device_address].cbw_command_type = MSCD_ATAPI;
    }
    else if ( Table[ 6 ] == MSCD_SCSI )
    {
        /* MSCD_SCSI Command */
        g_usb0_hmscd_global[device_address].cbw_command_type = MSCD_SCSI;
    }
    else
    {
        /* Sub Class is unknown */
        g_usb0_hmscd_global[device_address].cbw_command_type = MSCD_UNKNOWN_SUB;
        return USB_HMSCD_ERROR;
    }

    /* check Interface Descriptor ( protocol )*/
    if ( Table[ 7 ] != MSCD_BOT )
    {
        /* not Bulk-Only Transport Protocol */
        return USB_HMSCD_ERROR;
    }

    if ( Table[ 4 ] < MSCD_TOTAL_EP )
    {
        /* Ther Number of Endpointnot is not 2 */
        return USB_HMSCD_ERROR;
    }

    return USB_HMSCD_SUCCESS;
}

/*******************************************************************************
* Function Name: usb0_hmscd_pipe_info
* Description  : Searches for the endpoint descriptor information using the argument
*              : Table, which rewrites a part of the pipe information stored in the pipe
*              : definition table and enables it as pipe number to be used.
*              : * Bulk transfer only at this point.
*              : Error end except for the endpoint descriptor.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t* Table          : Descriptor information storage area
*              : uint16_t length         : Descriptor size
*              :                         :   - Interface descriptor size
* Return Value : int32_t  DEVDRV_SUCCESS : Normal end
*              :          DEVDRV_ERROR   : Error end
*******************************************************************************/
int32_t usb0_hmscd_pipe_info( uint16_t device_address, uint8_t *Table, uint16_t length  )
{
    uint16_t    ofset;
    uint16_t    retval;
    int32_t     ret = USB_HMSCD_SUCCESS;
    uint16_t    bulend_cnt;
    USB_HOST_CFG_PIPETBL_t    *ptr;

    if( Table[ 1 ] != USB_DT_INTERFACE )
    {
        /* not Interface Descriptor */
        return USB_HMSCD_ERROR;
    }

    ptr = (USB_HOST_CFG_PIPETBL_t *)g_usb0_hmscd_global[device_address].mscd_regist->pipe_table;

    usb0_api_hcd_clear_eptype(ptr);

    /* check Endpoint Descriptor */
    g_usb0_hmscd_global[device_address].mscd_out_pipe = USB_PIPE0;
    g_usb0_hmscd_global[device_address].mscd_in_pipe  = USB_PIPE0;

    ofset = Table[ 0 ];
    bulend_cnt = 0;
    do
    {
        if( ( Table[ ofset + 1 ] == USB_DT_ENDPOINT                ) &&
            ((Table[ ofset + 3 ] & USB_EP_TRNS_MASK) == USB_EP_BULK)    )
        {
            /* ==== find Endpoint Descriptor ==== */
            /* Bulk-Endpoint */
            bulend_cnt++;

            if( bulend_cnt == 1 )
            {
                /* 1st Bulk-Endpoint */
                retval = usb0_api_hcd_set_pipe_info( device_address, 
                                                    ptr, (uint8_t *)&Table[ ofset ] );
                if( retval == USB_PIPE_OUT )
                {
                    /* Out Bulk-Endpoint */
                    g_usb0_hmscd_global[device_address].mscd_out_pipe = ptr->pipe_number;
                }
                else if( retval == USB_PIPE_IN )
                {
                    /* In Bulk-Endpoint */
                    g_usb0_hmscd_global[device_address].mscd_in_pipe  = ptr->pipe_number;
                }
                else
                {
                    /* Configuration Information Error */
                    ret = USB_HMSCD_ERROR;
                    break;
                }
            }
            else
            {
                /* 2nd Bulk-Endpoint */
                ptr++;
                retval = usb0_api_hcd_set_pipe_info( device_address,
                                                      ptr, (uint8_t *)&Table[ ofset ] );
                if( retval == USB_PIPE_OUT )
                {
                    /* Out Bulk-Endpoint */
                    g_usb0_hmscd_global[device_address].mscd_out_pipe = ptr->pipe_number;
                }
                else if( retval == USB_PIPE_IN )
                {
                    /* In Bulk-Endpoint */
                    g_usb0_hmscd_global[device_address].mscd_in_pipe  = ptr->pipe_number;
                }
                else
                {
                    /* Configuration Information Error */
                    ret = USB_HMSCD_ERROR;
                    break;
                }
            }
        }

        if(Table[ ofset ] == 0)
        {
            return USB_HMSCD_ERROR;
        }

        /* Search Next Endpoint Descriptor */
        ofset += Table[ ofset ];

    } while( (bulend_cnt < 2) && (ofset < length) );

    if( (ret == USB_HMSCD_SUCCESS) && (bulend_cnt == 2) )
    {
        if( (g_usb0_hmscd_global[device_address].mscd_in_pipe  != USB_PIPE0) &&
            (g_usb0_hmscd_global[device_address].mscd_out_pipe != USB_PIPE0)    )
        {
            return USB_HMSCD_SUCCESS;
        }
    }

    /* find some error */
    return USB_HMSCD_ERROR;
}

/*******************************************************************************
* Function Name: usb0_hmscd_no_data
* Description  : Executes the CBW transmission and the CSW reception to the device
*              : specified by the argument device_address.
*              : Return value to the argument bytes is only 0 which is set by this function.
* Arguments    : uint16_t device_address : Device address
*              : uint32_t *bytes         : Byte count of the read data
*              : uint8_t  *cswstatus     : CSW command status
* Return Value : int32_t USB_HMSCD_SUCCESS                 : Normal end
*              :         USB_HMSCD_ERROR                   : Error end (Cannot do anything)
*              :                                           : (Get bus reset)
*              :         USB_HMSCD_TAGERR                  : Tag numbers of CBW and CSW are different
*              :                                           : (Function retry)
*              :         USB_HMSCD_STALL                   : Stall (hmscd_mass_strage_reset)
*              :         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_no_data( uint16_t device_address, uint32_t *bytes, uint8_t *cswstatus )
{
    int32_t    mscd_retval;

    mscd_retval = usb0_hmscd_send_cbw( device_address );
    if ( mscd_retval != USB_HMSCD_SUCCESS )
    {
        /* CBW error     */
        /* nothing to do */
    }
    else
    {
        mscd_retval = usb0_hmscd_get_csw( device_address, cswstatus );
        *bytes = 0;
    }

    return mscd_retval;
}

/*******************************************************************************
* Function Name: usb0_hmscd_data_in
* Description  : Executes the CBW transmission, the data reception, and the CSW
*              : reception to the device specified by the argument device_address.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *buffer         : Receive data storage address
*              : uint32_t size           : Transmit request data size (byte count)
*              : uint32_t *bytes         : Receive data size (byte count)
*              : uint8_t *cswstatus      : CSW command status
* Return Value : int32_t USB_HMSCD_SUCCESS                 : Normal end
*              :         USB_HMSCD_ERROR                   : Error end (Cannot do anything)
*              :                                           : (Get bus reset)
*              :         USB_HMSCD_TAGERR                  : Tag numbers of CBW and CSW are different
*              :                                           : (Function retry)
*              :         USB_HMSCD_STALL                   : Stall (hmscd_mass_strage_reset)
*              :         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_data_in( uint16_t device_address, uint8_t *buffer, uint32_t size,
                            uint32_t *bytes, uint8_t *cswstatus )
{
    int32_t    mscd_retval;

    mscd_retval = usb0_hmscd_send_cbw( device_address );
    if ( mscd_retval != USB_HMSCD_SUCCESS )
    {
        /* CBW error     */
        /* nothing to do */
    }
    else
    {
        mscd_retval = usb0_hmscd_get_data( device_address,buffer, size, bytes );
        if ( mscd_retval != USB_HMSCD_SUCCESS )
        {
            if ( mscd_retval == USB_HMSCD_STALL )
            {
                /* If STALL, execute GetCSW() */
                mscd_retval = usb0_hmscd_get_csw( device_address, cswstatus );
            }
        }
        else
        {
            mscd_retval = usb0_hmscd_get_csw( device_address, cswstatus );
        }
    }

    return mscd_retval;
}

/*******************************************************************************
* Function Name: usb0_hmscd_data_out
* Description  : Executes the CBS transmission, the data transmission, and the CSW
*              : reception to the device specified by the argument device_address.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *buffer         : Receive data storage address
*              : uint32_t size           : Transmit request data size (byte count)
*              : uint32_t *bytes         : Transmit data size
*              : uint8_t *cswstatus      : CSW command status
* Return Value : int32_t USB_HMSCD_SUCCESS                 : Normal end
*              :         USB_HMSCD_ERROR                   : Error end (Cannot do anything)
*              :                                           : (Get bus reset)
*              :         USB_HMSCD_TAGERR                  : Tag numbers of CBW and CSW are different
*              :                                           : (Function retry)
*              :         USB_HMSCD_STALL                   : Stall (hmscd_mass_strage_reset)
*              :         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_data_out( uint16_t device_address, uint8_t *buffer, uint32_t size,
                             uint32_t *bytes, uint8_t *cswstatus )
{
    int32_t    mscd_retval;

    mscd_retval = usb0_hmscd_send_cbw( device_address );
    if ( mscd_retval != USB_HMSCD_SUCCESS )
    {
        /* CBW error     */
        /* nothing to do */
    }
    else
    {
        mscd_retval = usb0_hmscd_send_data( device_address,buffer, size, bytes );
        if ( mscd_retval != USB_HMSCD_SUCCESS )
        {
            if ( mscd_retval == USB_HMSCD_STALL )
            {
                /* If STALL, execute GetCSW() */
                mscd_retval = usb0_hmscd_get_csw( device_address, cswstatus );
            }
        }
        else
        {
            mscd_retval = usb0_hmscd_get_csw( device_address, cswstatus );
        }
    }

    return mscd_retval;
}

/*******************************************************************************
* Function Name: usb0_hmscd_cbw_tag_count
* Description  : The CBW TAG number of the device specified by the argument
*              : device_address is incremented by 1.
*              : Originally counts the TAG number of the device specified by the
*              : argument device_address as multiple device support.
* Arguments    : uint16_t device_address : Device address (unused)
* Return Value : none
*******************************************************************************/
void usb0_hmscd_cbw_tag_count( uint16_t device_address )
{
    g_usb0_hmscd_global[device_address].cbw_tag++;
    if( g_usb0_hmscd_global[device_address].cbw_tag == 0 )
    {
        g_usb0_hmscd_global[device_address].cbw_tag = 1;
    }
}

/*******************************************************************************
* Function Name: usb0_hmscd_check_csw
* Description  : Checks the received CSW data and returns the command status to
*              : the argument cswstatus.
*              : The argument device_address is used in the error processing.
* Arguments    : uint16_t device_address : Device address
*              : uint8_t *cswstatus      : CSW command status
* Return Value : int32_t USB_HMSCD_SUCCESS                 : Normal end
*              :         USB_HMSCD_TAGERR                  : Tag numbers of CBW and CSW are different
*              :         USB_HMSCD_SUCCESS_CSW_PHASE_ERROR : CSW phase error
*******************************************************************************/
int32_t usb0_hmscd_check_csw( uint16_t device_address, uint8_t *cswstatus ) 
{
    int32_t    ret;

    /* CSW Check                     */
    /* Get size of data not received */
    g_usb0_hmscd_global[device_address].csw_data_residue = ( (uint32_t)(g_usb0_hmscd_csw[device_address].csw_data_res_lo      ) |
                                                             (uint32_t)(g_usb0_hmscd_csw[device_address].csw_data_res_ml <<  8) |
                                                             (uint32_t)(g_usb0_hmscd_csw[device_address].csw_data_res_mh << 16) |
                                                             (uint32_t)(g_usb0_hmscd_csw[device_address].csw_data_res_hi << 24)   );

    if ( g_usb0_hmscd_csw[device_address].csw_sig != MSDC_CSW_SIG )
    {
        return USB_HMSCD_TAGERR;
    }

    if ( g_usb0_hmscd_csw[device_address].csw_tag != g_usb0_hmscd_cbw[device_address].cbw_tag )
    {
        return USB_HMSCD_TAGERR;
    }

    *cswstatus = g_usb0_hmscd_csw[device_address].csw_status;

    switch ( g_usb0_hmscd_csw[device_address].csw_status )
    {
    case    USB_HMSCD_CSW_PASS:
        ret = USB_HMSCD_SUCCESS;
        break;
    case    USB_HMSCD_CSW_FAIL:
        ret = USB_HMSCD_SUCCESS;
        break;
    case    USB_HMSCD_CSW_PHASE_ERROR:
        ret = USB_HMSCD_SUCCESS_CSW_PHASE_ERROR;
        break;
    default:
        ret = USB_HMSCD_TAGERR;
        break;
    }

    return ret;
}

/*******************************************************************************
* Function Name: usb0_hmscd_set_dev_sts
* Description  : Sets the status data specified by the argument data to the host
*              : control device status area in the mass storage control driver
*              : management area.
* Arguments    : uint16_t device_address : Device address
* Return Value : none
*******************************************************************************/
void usb0_hmscd_set_dev_sts( uint16_t device_address, uint16_t data )
{
    g_usb0_hmscd_global[device_address].mscd_dev_status = data;
}

/*******************************************************************************
* Function Name: usb0_hmscd_get_dev_sts
* Description  : Obtains the current device status from the host control device
*              : status area in the mass storage control driver management area,
*              : and returns it as return value.
* Arguments    : uint16_t device_address : Device address
* Return Value : uint16_t : Status
*******************************************************************************/
uint16_t usb0_hmscd_get_dev_sts( uint16_t device_address )
{
    if(device_address == 0)
    {
        return 0xff;
    }

    if(device_address < USB_HMSCD_MAX_ADDRESS + 1)
    {
        return g_usb0_hmscd_global[device_address].mscd_dev_status;
    }

    return 0xff;
}

/* End of File */
