/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright (C) 2014 Renesas Electronics Corporation. All rights reserved.
*******************************************************************************/
/*******************************************************************************
* File Name    : usb0_cfg.c
* $Rev: 682 $
* $Date:: 2014-01-23 09:15:07 +0900#$
* Device(s)    :
* Tool-Chain   :
* OS           : None
* H/W Platform :
* Description  : USB Sample Program
* Operation    :
* Limitations  :
*******************************************************************************/


/*******************************************************************************
Includes   <System Includes> , "Project Includes"
*******************************************************************************/
#include <stdio.h>
#include "usb_hdrv_rza1.h"
#include "usb_hmscd_rza1.h"
#include "usb_haudio_rza1.h"
#include "usb_hub_rza1.h"
#include "usb_hmscd_fsi.h"

#include "iodefine.h"
#include "rza_io_regrw.h"

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


/*******************************************************************************
Macro definitions
*******************************************************************************/
#define USB_D_DESC_SIZE    40u
#define USB_C_DESC_SIZE    2048u


/*******************************************************************************
Imported global variables and functions (from other files)
*******************************************************************************/
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl1[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl2[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl3[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl4[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl5[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl6[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl7[];
extern USB_HOST_CFG_PIPETBL_t     usb0_mscd_ep_tbl8[];
extern USB_HOST_CFG_PIPETBL_t     usb0_hub_ep_tbl1[];
extern USB_HOST_CFG_PIPETBL_t     usb0_hub_ep_tbl2[];
extern USB_HOST_CFG_PIPETBL_t     usb0_audio_ep_tbl1[];
extern USB_HOST_CFG_PIPETBL_t     usb0_audio_ep_tbl2[];
extern USB_HOST_CFG_PIPETBL_t     usb0_audio_ep_tbl3[];
extern USB_HOST_CFG_PIPETBL_t     usb0_audio_ep_tbl4[];
extern void Set_USB_MSCD_Connect_Info(bool Connect);
extern void Set_USB_Audio_Connect_Info(bool Connect);
/*******************************************************************************
Exported global variables and functions (to be accessed by other files)
*******************************************************************************/
int32_t usb0_init( uint16_t speed );
void usb0_bus_reset( void );
void usb0_hub_port_reset( uint16_t device_address );
void usb0_hub_port_power( uint16_t hub_address, uint16_t port, uint16_t sw );
void usb0_hub_recovery( uint16_t hub_address, uint16_t port);


/*******************************************************************************
Private global variables and functions
*******************************************************************************/
static USB_HMGR_REGIST_t     g_usb0_hmgr_regist;
static uint16_t              g_usb0_device_status[ USB_MAX_DEVICE + 1 ];    /* 0: detach            */
                                                                            /* 1: attach USB Memory */
                                                                            /* 2: attach USB HUB    */
                                                                            /* 3: attach USB AUDIO  */

static USB_HCD_CONFIG_t      g_usb0_hcd_config;
static uint32_t              g_usb0_device_desc_buffer[ USB_D_DESC_SIZE / sizeof( uint32_t ) ];
static uint32_t              g_usb0_config_desc_buffer[ USB_C_DESC_SIZE / sizeof( uint32_t ) ];

static USB_HUB_REGIST_t      g_usb0_hub_regist;
static USB_HUB_CONFIG_t      g_usb0_hub_config;

static USB_HMSCD_REGIST_t    g_usb0_mscd_regist[ USB_MAX_DEVICE + 1 ];
static USB_HAUDIO_REGIST_t   g_usb0_audio_regist[ USB_MAX_DEVICE + 1 ];

static void       usb0_set_hmgr_regist( void );
static void       usb0_set_hub_regist( void );
static int32_t    usb0_set_hmscd_regist( uint16_t device_address );
static int32_t    usb0_set_haudio_regist( uint16_t device_address );

static void       usb0_callback_configuration( uint32_t result,uint32_t pointer );
static void       usb0_callback_detach( uint32_t result, uint32_t device_address );
static void       usb0_callback_device_attach_proc( uint32_t device_address, uint32_t no_arg );
static void       usb0_callback_huberror( uint32_t result, uint32_t pointer );
static void       usb0_callback_device_attach( uint32_t arg1, uint32_t arg2 );
static void       usb0_callback_dummy( uint32_t arg1, uint32_t arg2 );

static int32_t    usb0_get_device_descriptor( uint16_t device_address,uint8_t *buffer );
static int32_t    usb0_search_config_desc( uint16_t device_address,
                                           uint16_t config_num,
                                           uint16_t *interface_index,
                                           uint16_t *interface_alt,
                                           uint8_t  *g_usb0_config_desc_buffer );
static int32_t    usb0_search_interface_desc( uint16_t *interface_index,
                                              uint16_t *interface_alt,
                                              uint8_t  *g_usb0_config_desc_buffer );
static void       usb0_output_device_descinfo( uint8_t *device );
static void       usb0_output_config_descinfo( uint8_t *config );
static void       usb0_output_endpoint_descinfo( uint8_t* Table );


/*******************************************************************************
* Function Name: usb0_init
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
int32_t usb0_init( uint16_t speed )
{
    int32_t     ret;
    uint16_t    device;

    /* ---- P2_9 : USB VBUS ---- */
    /* Port initialize */
    RZA_IO_RegWrite_16(&GPIO.PIBC2, 0, GPIO_PIBC2_PIBC29_SHIFT, GPIO_PIBC2_PIBC29);
    RZA_IO_RegWrite_16(&GPIO.PBDC2, 0, GPIO_PBDC2_PBDC29_SHIFT, GPIO_PBDC2_PBDC29);
    RZA_IO_RegWrite_16(&GPIO.PM2,   1, GPIO_PM2_PM29_SHIFT,     GPIO_PM2_PM29);
    RZA_IO_RegWrite_16(&GPIO.PMC2,  0, GPIO_PMC2_PMC29_SHIFT,   GPIO_PMC2_PMC29);
    RZA_IO_RegWrite_16(&GPIO.PIPC2, 0, GPIO_PIPC2_PIPC29_SHIFT, GPIO_PIPC2_PIPC29);
    /* Mode : Port mode                          */
    /* Terminal output level : Low level output */
    /* Port mode : Output mode                   */
    RZA_IO_RegWrite_16(&GPIO.PIBC2, 0, GPIO_PIBC2_PIBC29_SHIFT, GPIO_PIBC2_PIBC29);
    RZA_IO_RegWrite_16(&GPIO.P2,    1, GPIO_P2_P29_SHIFT,       GPIO_P2_P29);
    RZA_IO_RegWrite_16(&GPIO.PM2,   0, GPIO_PM2_PM29_SHIFT,     GPIO_PM2_PM29);

    usb0_fsi_init();

    for( device = 0; device < (USB_MAX_DEVICE + 1); device++ )
    {
        g_usb0_device_status[ device ] = USB_IF_CLS_NOT;
    }

    usb0_set_hub_regist();
    g_usb0_hub_config.bus_reset_wait_time = 200;
    g_usb0_hub_config.detect_time         = 200;
    g_usb0_hub_config.enu_retry           = 100;
    g_usb0_hub_config.control_timeout     = 6000;
    usb0_api_hub_registration( &g_usb0_hub_regist, &g_usb0_hub_config );

    usb0_set_hmgr_regist();
    usb0_api_hmgr_registration( &g_usb0_hmgr_regist );

    g_usb0_hcd_config.bus_reset_wait_time = 200;
    g_usb0_hcd_config.fbcfg_fwait         = 3;
    g_usb0_hcd_config.detect_time         = 200;
    g_usb0_hcd_config.control_timeout     = 30000;
    g_usb0_hcd_config.bus_reset_width     = 50;
    g_usb0_hcd_config.enu_retry           = 100;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE0] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE1] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE2] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE3] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE4] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE5] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE6] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE7] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE8] = 3;
    g_usb0_hcd_config.pipe_err_cnt[USB_PIPE9] = 3;

    g_usb0_hcd_config.control_mode = USB_ILLEGAL_RESPONSE;
    g_usb0_hcd_config.speed_mode   = speed;

    printf("\nStart USB Host Driver. Ver.0.01\n");
    printf("USB Driver port 0 Open = " );

    ret = usb0_api_hmgr_open( &g_usb0_hcd_config );
    if( ret == USB_SUCCESS )
    {
        printf( "Pass  %d\n", ret );
    }
    else
    {
        printf( "Error %d\n", ret );
    }

    printf( "Wait USB Connection...\n" );
    printf("\n");

    return 0;
}

/*******************************************************************************
* Function Name: usb0_set_hmgr_regist
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_set_hmgr_regist( void )
{
    g_usb0_hmgr_regist.device_powered_proc = ( USB_CALLBACK_HMGR )usb0_callback_device_attach;
    g_usb0_hmgr_regist.device_default_proc = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hmgr_regist.device_address_proc = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hmgr_regist.device_config_proc  = ( USB_CALLBACK_HMGR )usb0_callback_configuration;
    g_usb0_hmgr_regist.device_detach_proc  = ( USB_CALLBACK_HMGR )usb0_callback_detach;
    g_usb0_hmgr_regist.device_attach_proc  = ( USB_CALLBACK_HMGR )usb0_callback_device_attach_proc;
    g_usb0_hmgr_regist.device_suspend_proc = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hmgr_regist.device_resume_proc  = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
}

/*******************************************************************************
* Function Name: usb0_set_hub_regist
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_set_hub_regist( void )
{
    g_usb0_hub_regist.device_powered_proc = ( USB_CALLBACK_HMGR )usb0_callback_device_attach;
    g_usb0_hub_regist.device_default_proc = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hub_regist.device_address_proc = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hub_regist.device_config_proc  = ( USB_CALLBACK_HMGR )usb0_callback_configuration;
    g_usb0_hub_regist.device_detach_proc  = ( USB_CALLBACK_HMGR )usb0_callback_detach;
    g_usb0_hub_regist.device_attach_proc  = ( USB_CALLBACK_HMGR )usb0_callback_device_attach_proc;
    g_usb0_hub_regist.device_suspend_proc = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hub_regist.device_resume_proc  = ( USB_CALLBACK_HMGR )usb0_callback_dummy;
    g_usb0_hub_regist.device_error_proc   = ( USB_CALLBACK_HMGR )usb0_callback_huberror;
}

/*******************************************************************************
* Function Name: usb0_set_hmscd_regist
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static int32_t usb0_set_hmscd_regist( uint16_t device_address )
{
    switch ( device_address )
    {
    case 1:     g_usb0_mscd_regist[ 1].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl1;    break;
    case 2:     g_usb0_mscd_regist[ 2].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl2;    break;
    case 3:     g_usb0_mscd_regist[ 3].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl3;    break;
    case 4:     g_usb0_mscd_regist[ 4].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl4;    break;
    case 5:     g_usb0_mscd_regist[ 5].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl5;    break;
    case 6:     g_usb0_mscd_regist[ 6].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl6;    break;
    case 7:     g_usb0_mscd_regist[ 7].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl7;    break;
    case 8:     g_usb0_mscd_regist[ 8].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_mscd_ep_tbl8;    break;
    default :     return -1;
    }

    g_usb0_mscd_regist[device_address].time_out = 6000;

    return 0;
}

/*******************************************************************************
* Function Name: usb0_set_hmscd_regist
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static int32_t usb0_set_haudio_regist( uint16_t device_address )
{
    switch ( device_address )
    {
    case 1:     g_usb0_audio_regist[ 1].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_audio_ep_tbl1;    break;
    case 2:     g_usb0_audio_regist[ 2].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_audio_ep_tbl2;    break;
    case 3:     g_usb0_audio_regist[ 3].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_audio_ep_tbl3;    break;
    case 4:     g_usb0_audio_regist[ 4].pipe_table = (USB_HOST_CFG_PIPETBL_t *)&usb0_audio_ep_tbl4;    break;
    default :     return -1;
    }

    g_usb0_audio_regist[device_address].time_out = 500;

    return 0;
}

/*******************************************************************************
* Function Name: usb0_callback_configuration
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_callback_configuration( uint32_t result, uint32_t pointer )
{
    int32_t                        ret_apl;
    int32_t                        ret;
    USB_HOST_DEVICE_CFG_INFO_t     *cfg_info;
    uint16_t                       interface_index;
    uint16_t                       interface_alt;
    uint8_t                        *descriptor;
    USB_HOST_CFG_PIPETBL_t         *pipe_table;

    cfg_info = (USB_HOST_DEVICE_CFG_INFO_t *)pointer;

    if( result == USB_PROCESS_SUCCESS )
    {
        if( cfg_info->speed_mode == USB_HSCONNECT )
        {
            printf( " High-speed device is connected port=0\n" );
        }
        else if( cfg_info->speed_mode == USB_FSCONNECT )
        {
            printf( " Full-speed device is connected port=0\n" );
        }
        else
        {
            printf( " Low-speed device is connected port=0\n" );
        }

        descriptor = ( uint8_t* )g_usb0_device_desc_buffer;

        ret_apl = usb0_get_device_descriptor( cfg_info->device_address, descriptor );
        if( ret_apl < 0 )
        {
            return;
        }

        usb0_output_device_descinfo( descriptor );

        ret_apl = usb0_search_config_desc(  cfg_info->device_address,
                                            (uint16_t)descriptor[17],
                                            &interface_index,
                                            &interface_alt,
                                            (uint8_t *)g_usb0_config_desc_buffer );

        usb0_output_config_descinfo( (uint8_t *)g_usb0_config_desc_buffer );

        if( ret_apl < 0 )
        {
            return;
        }

        if( ret_apl == 0 )
        {
            ret_apl = usb0_set_hmscd_regist( cfg_info->device_address );
            if( ret_apl < 0 )
            {
                return;
            }

            ret = usb0_api_hmscd_registration( cfg_info->device_address, &g_usb0_mscd_regist[ cfg_info->device_address ] );
            if( ret != USB_SUCCESS )
            {
                printf( "### ERROR: MSCD DEVICE ADDRESS ERROR device address %d\n", cfg_info->device_address);
                return;
            }

            ret = usb0_api_hmscd_open( cfg_info->device_address,
                                       (uint16_t *)g_usb0_config_desc_buffer,
                                       interface_index,
                                       interface_alt,
                                       USB_HMSCD_NO );

            if( ret == USB_SUCCESS )
            {
                g_usb0_device_status[ cfg_info->device_address ] = USB_IF_CLS_MASS;
                printf( "MSCD CONNECT device address %d port=0\n", cfg_info->device_address);
                usb0_fsi_drive_search( cfg_info->device_address );
                Set_USB_MSCD_Connect_Info(true);
            }
            else
            {
                printf( "### ERROR: MSCD OPEN ERROR\n" );
            }
        }
        else if( ret_apl == 1 )
        {
            if( cfg_info->device_address != 1 )
            {
                printf( "### ERROR: Hub-device can be connected to only root port devadr=%d\n", cfg_info->device_address );
                return;
            }

            if( g_usb0_device_status[ cfg_info->device_address ] == USB_IF_CLS_NOT )
            {
                if( cfg_info->device_address == 1 )
                {
                    pipe_table = usb0_hub_ep_tbl1;
                }
                else if( cfg_info->device_address == 2 )
                {
                    pipe_table = usb0_hub_ep_tbl2;
                }
                else
                {
                    printf( "### HUB OPEN ERROR : hub address=%d\n", cfg_info->device_address );
                    return;
                }
            }
            else
            {
                printf( "### HUB OPEN ERROR : already hub attach\n" );
                return;
            }

            ret = usb0_api_hub_open( cfg_info->device_address,
                                     (uint16_t *)g_usb0_config_desc_buffer,
                                     interface_index,
                                     interface_alt,
                                     pipe_table );
            if( ret == USB_SUCCESS )
            {
                g_usb0_device_status[ cfg_info->device_address ] = USB_IF_CLS_HUB;
                printf( "HUB CONNECT device address %d port=0\n", cfg_info->device_address);
            }
            else
            {
                printf( "### HUB OPEN ERROR\n" );
            }
        }
        else if( ret_apl == 2 )
        {
            ret_apl = usb0_set_haudio_regist( cfg_info->device_address );
            if( ret_apl < 0 )
            {
                return;
            }

            usb0_api_haudio_registration( cfg_info->device_address, &g_usb0_audio_regist[ cfg_info->device_address ] );

            ret = usb0_api_haudio_open( cfg_info->device_address,
                                        (uint16_t *)g_usb0_config_desc_buffer,
                                        USB_HAUDIO_YES,
                                        cfg_info->speed_mode );
            if( ret == USB_SUCCESS )
            {
                g_usb0_device_status[ cfg_info->device_address ] = USB_IF_CLS_AUDIO;
                printf( "AUDIO CONNECT device address %d port=0\n", cfg_info->device_address);
                Set_USB_Audio_Connect_Info(true);
            }
            else
            {
                printf( "### ERROR: AUDIO OPEN ERROR\n" );
            }
        }
        else
        {
            printf( "### USB Device Enumeration ERROR\n" );
        }
    }
    else
    {
        printf( "### USB Device Enumeration ERROR\n" );
        if( cfg_info->speed_mode == USB_HSCONNECT )
        {
            printf( " High-speed device is connected port=0\n" );
        }
        else if( cfg_info->speed_mode == USB_FSCONNECT )
        {
            printf( " Full-speed device is connected port=0\n" );
        }
        else
        {
            printf( " Low-speed device is connected port=0\n" );
        }
    }
}

/*******************************************************************************
* Function Name: usb0_get_device_descriptor
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static int32_t usb0_get_device_descriptor( uint16_t device_address, uint8_t *buffer )
{
    int32_t    ret;

    ret = usb0_api_class_GetDescriptor( device_address,
                                        (uint16_t)USB_DEVICE_DESC,
                                        buffer,
                                        USB_D_DESC_SIZE );
    if( ret != USB_SUCCESS )
    {
        printf( "### ERROR Get Device Descriptor\n" );
        return -1;
    }

    if( buffer[1] != USB_DT_DEVICE )
    {
        return -1;
    }

    return 0;
}

/*******************************************************************************
* Function Name: usb0_search_config_desc
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static int32_t usb0_search_config_desc( uint16_t device_address,
                                        uint16_t config_num,
                                        uint16_t *interface_index,
                                        uint16_t *interface_alt,
                                        uint8_t  *descriptor )
{
    uint16_t    config_index;
    int32_t     ret;
    uint16_t    type, size;

    for( config_index = 0 ; config_index < config_num ; ++config_index )
    {
        /* Get the first 9-byte of Configuration Descriptor */
        type = (uint16_t)( (uint16_t)USB_CONFIGURATION_DESC | config_index );
        size = (uint16_t)0x0009;
        ret    = usb0_api_class_GetDescriptor( device_address,
                                               type,
                                               descriptor,
                                               size );
        if( ret == USB_SUCCESS )
        {
            if( descriptor[1] == USB_DT_CONFIGURATION )
            {
                /* Get the total size of Configuration Descriptor */
                size = (uint16_t)( ( (uint16_t)descriptor[ 3 ] << 8 ) +
                                    (uint16_t)descriptor[ 2 ] );

                if( size <= sizeof(g_usb0_config_desc_buffer) )
                {
                    ret = usb0_api_class_GetDescriptor( device_address,
                                                        type,
                                                        descriptor,
                                                        size     );
                    if( ret == USB_SUCCESS )
                    {
                        ret = usb0_search_interface_desc( interface_index,
                                                          interface_alt,
                                                          descriptor );
                        break;
                    }
                    else
                    {
                        /* usb0_api_class_GetDescriptor API error */
                        printf("### ERROR Get Device Descriptor API\n");
                    }
                }
                else
                {
                    printf("### ERROR few memory size of Configuration Descriptor\n");
                }
            }
            else
            {
                /* Descriptor is not Configuration Descriptor */
                printf("### ERROR NO Configuration Descriptor\n");
            }
        }
        else
        {
            /* usb0_api_class_GetDescriptor 9-byte API error */
            printf("### ERROR Get Device Descriptor API\n");
        }
    }

    return ret;
}

/*******************************************************************************
* Function Name: usb0_search_interface_desc
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static int32_t usb0_search_interface_desc(  uint16_t *interface_index,
                                            uint16_t *interface_alt,
                                            uint8_t  *descriptor )
{
    uint16_t    desc_point;
    uint16_t    desc_size;

    if( descriptor[1] != USB_DT_CONFIGURATION )
    {
        return -1;
    }

    desc_point = 0;

    desc_size  = (uint16_t)( descriptor[2] +
                            ((uint16_t)descriptor[3] << 8) );

    while( desc_point < desc_size )
    {
        if( descriptor[desc_point + 1] == USB_DT_INTERFACE )
        {
            if( descriptor[desc_point + 5] == USB_IF_CLS_MASS )
            {
                *interface_index = descriptor[desc_point + 2];
                *interface_alt   = descriptor[desc_point + 3];
                return 0;
            }
            else if( descriptor[desc_point + 5] == USB_IF_CLS_HUB )
            {
                printf( "Interface Class HUB Device\n" );
                *interface_index = descriptor[desc_point + 2];
                *interface_alt   = descriptor[desc_point + 3];
                return 1;
            }
            else if( descriptor[desc_point + 5] == USB_IF_CLS_AUDIO )
            {
                printf( "Interface Class Audio Device\n" );
                *interface_index = descriptor[desc_point + 2];
                *interface_alt   = descriptor[desc_point + 3];
                return 2;
            }
            else
            {
                printf( "Interface Class UNKNOWN Device\n" );
            }
        }

        if( descriptor[desc_point] == 0 )
        {
            printf( "### ERROR Interface Descriptor Size Error\n" );
            break;
        }

        desc_point    +=  descriptor[desc_point];
    }
    printf( "### ERROR Not found Supported Storage Interface Descriptor \n" );

    return -1;
}

/*******************************************************************************
* Function Name: usb0_callback_detach
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_callback_detach( uint32_t result, uint32_t device_address )
{
    if( g_usb0_device_status[ device_address ] == USB_IF_CLS_MASS )
    {
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;

        printf( "Detach DEVICE MEMORY %d port=0\n", device_address );

        usb0_api_hmscd_close( ( uint16_t )device_address );

        usb0_fsi_clear( ( uint16_t )device_address );

        Set_USB_MSCD_Connect_Info(false);
    }
    else if( g_usb0_device_status[ device_address ] == USB_IF_CLS_HUB )
    {
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;

        printf( "Detach DEVICE HUB %d port=0\n", device_address );

        usb0_api_hub_close( ( uint16_t )device_address );

        Set_USB_MSCD_Connect_Info(false);
        Set_USB_Audio_Connect_Info(false);
    }
    else if( g_usb0_device_status[ device_address ] == USB_IF_CLS_AUDIO )
    {
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;

        printf( "Detach DEVICE AUDIO %d port=0\n", device_address );

        usb0_api_haudio_close( ( uint16_t )device_address );

        Set_USB_Audio_Connect_Info(false);
    }
    else
    {
        printf( "Detach DEVICE UNKNOWN %d port=0\n", device_address );
    }
}

/*******************************************************************************
* Function Name: usb0_callback_huberror
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_callback_huberror( uint32_t result, uint32_t pointer )
{
    USB_HUB_ERROR_INFO_t    *err_info;

    err_info = (USB_HUB_ERROR_INFO_t *)pointer;

    if( g_usb0_device_status[ err_info->hub_address ] == USB_IF_CLS_HUB )
    {
        switch( err_info->info )
        {
        case USB_HUB_INFO_HUB_POWER_LOST:
            printf( "%d HUB POWER LOST\n", err_info->hub_address );
            break;
        case USB_HUB_INFO_HUB_OVER_CURRENT:
            printf( "%d HUB OVER CURRENT\n", err_info->hub_address );
            break;
        case USB_HUB_INFO_PORT_OVER_CURRENT:
            printf( "%d HUB %d PORT OVER CURRENT\n", err_info->hub_address, err_info->port );
            break;
        case USB_HUB_INFO_HUB_INTERRUPT:
            printf( "%d HUB INTERRUPT TRANSFER ERROR\n", err_info->hub_address );
            break;
        default:
            break;
        }
    }
}

/*******************************************************************************
* Function Name: usb0_callback_device_attach
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_callback_device_attach( uint32_t arg1, uint32_t arg2 )
{
    printf("Attach DEVICE UNKNOWN\n");
}

/*******************************************************************************
* Function Name: usb0_callback_device_attach_proc
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_callback_device_attach_proc( uint32_t device_address, uint32_t no_arg )
{
    if( g_usb0_device_status[ device_address ] == USB_IF_CLS_MASS )
    {
        usb0_fsi_drive_search( (uint16_t)device_address );
    }
}

/*******************************************************************************
* Function Name: usb0_callback_dummy
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_callback_dummy( uint32_t arg1, uint32_t arg2 )
{
    /* nothing to do */
}

/*******************************************************************************
* Function Name: usb0_bus_reset
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_bus_reset( void )
{
    int32_t     err;
    uint16_t    device_address;

    device_address = 1;    /* device address is 1(fix) */

    if( g_usb0_device_status[ device_address ] == USB_IF_CLS_MASS )
    {
        printf( "RESET DEVICE Memory %d port=0\n", device_address );
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;
        usb0_api_hmscd_close( device_address );
        usb0_fsi_clear( device_address );
    }
    else if( g_usb0_device_status[ device_address ] == USB_IF_CLS_HUB )
    {
        printf( "RESET DEVICE HUB %d port=0\n", device_address );
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;
        usb0_api_hub_close( device_address );
    }
    else
    {
        printf( "RESET DEVICE UNKNOWN %d port=0\n", device_address );
    }

    err = usb0_api_hmgr_bus_reset();
    if( err == USB_SUCCESS )
    {
        printf("USB BUS RESET SUCCESS\n");
    }
    else
    {
        printf("USB BUS RESET ERROR\n");
    }
}

/*******************************************************************************
* Function Name: usb0_hub_port_reset
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_hub_port_reset( uint16_t device_address )
{
    int32_t     err;
    uint16_t    hub_address;
    uint16_t    port;

    if( g_usb0_device_status[ device_address ] == USB_IF_CLS_MASS )
    {
        printf( "RESET DEVICE Memory %d port=0\n", device_address );
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;
        usb0_api_hmscd_close( device_address );
        usb0_fsi_clear( device_address );
    }
    else if( g_usb0_device_status[ device_address ] == USB_IF_CLS_HUB )
    {
        printf( "RESET DEVICE HUB %d port=0\n", device_address );
        g_usb0_device_status[ device_address ] = USB_IF_CLS_NOT;
        usb0_api_hub_close( device_address );
    }
    else
    {
        printf( "RESET DEVICE UNKNOWN %d port=0\n", device_address );
    }

    err = usb0_api_hub_get_port( device_address, &hub_address, &port );
    if( err == USB_ERROR )
    {
        printf("USB BUS RESET ERROR cannot find USB address\n");
        return;
    }

    err = usb0_api_hub_bus_reset( hub_address, port );
    if( err == USB_HUB_SUCCESS )
    {
        printf("HUB PORT RESET SUCCESS\n");
    }
    else
    {
        printf("HUB PORT RESET ERROR\n");
    }
}

/*******************************************************************************
* Function Name: usb0_hub_port_power
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_hub_port_power( uint16_t hub_address, uint16_t port, uint16_t sw )
{
    int32_t    err;

    err = usb0_api_hub_port_power( hub_address, port, sw );
    if( err == USB_HUB_SUCCESS ){
        printf("HUB PORT POWER SUCCESS\n");
    }
    else{
        printf("HUB PORT POWER ERROR\n");
    }
}

/*******************************************************************************
* Function Name: usb0_hub_recovery
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
void usb0_hub_recovery( uint16_t hub_address, uint16_t port)
{
    int32_t              err;
    volatile uint16_t    *device;
    uint16_t             hub_adr;
    uint16_t             dev_adr;
    uint16_t             hub_port;

    for(dev_adr = 1; dev_adr < (USB_MAX_DEVICE + 1); dev_adr++)
    {
        err = usb0_api_hub_get_port(dev_adr,&hub_adr,&hub_port);
        if( ( err == USB_HUB_SUCCESS ) &&
            ( hub_adr == hub_address ) &&
            ( hub_port == port)           )
        {
            break;
        }
    }

    if(dev_adr == (USB_MAX_DEVICE + 1))
    {
        return;
    }

    err = usb0_api_hub_port_power( hub_address, port, USB_HUB_PORT_POWER_OFF );

    device = &g_usb0_device_status[ dev_adr];

    while( *device != USB_IF_CLS_NOT)
    {
        /* nothing to do */
    }

    usb0_userdef_os_dly_tsk(1000);

    err = usb0_api_hub_port_power( hub_address, port, USB_HUB_PORT_POWER_ON );
}

/*******************************************************************************
* Function Name: usb0_output_device_descinfo
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_output_device_descinfo( uint8_t* device )
{
    printf( "    Device descriptor fields\n" );
    printf( "        bcdUSB              : %02x.%02x\n",
                        device[0x03],device[0x02] );
    printf( "        bDeviceClass        : 0x%02x\n", device[0x04] );
    printf( "        bDeviceSubClass     : 0x%02x\n", device[0x05] );
    printf( "        bProtocolCode       : 0x%02x\n", device[0x06] );
    printf( "        bMaxPacletSize      : 0x%02x\n", device[0x07] );
    printf( "        idVendor            : 0x%02x%02x\n",
                        device[0x09],device[0x08] );
    printf( "        idProduct           : 0x%02x%02x\n",
                        device[0x0b],device[0x0a] );
    printf( "        bcdDevice           : 0x%02x%02x\n",
                        device[0x0d],device[0x0c] );
    printf( "        iSerialNumber       : 0x%02x\n", device[0x10] );
    printf( "        bNumConfigurations  : 0x%02x\n", device[0x11] );
    printf( "\n" );
}

/*******************************************************************************
* Function Name: usb0_output_config_descinfo
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_output_config_descinfo( uint8_t* config )
{
    uint16_t    TotalLength;
    uint16_t    TotalLength1;

    TotalLength = 0;
    TotalLength1 = (uint16_t)(config[2] + (config[3] << 8));

    while( TotalLength < TotalLength1 )
    {
        switch( config[TotalLength + 1] )
        {
        case    0x02:
            printf( "    Configuration descriptor fields\n" );
            printf( "        Configuration Value  : 0x%02x\n", config[TotalLength + 0x05] );
            printf( "        Number of Interface  : 0x%02x\n", config[TotalLength + 0x04] );
            break;
        case    0x04:
            printf( "    Interface descriptor fields\n" );
            printf( "        Interface descriptor size : 0x%02x\n", config[TotalLength + 0] );
            printf( "        Interface number : 0x%02x\n", config[TotalLength + 2] );
            printf( "        Interface alt number : 0x%02x\n", config[TotalLength + 3] );
            printf( "        Interface endpoint number : 0x%02x\n", config[TotalLength + 4] );

            switch( config[TotalLength + 5] )
            {
            case    1:
                printf( "     This device has Audio Class Interface.\n" );
                break;
            case    2:
                printf( "     This device has CDC-Control Class Interface.\n" );
                break;
            case    3:
                printf( "     This device has HID Class Interface.\n" );
                break;
            case    5:
                printf( "     This device has Physical Class Interface.\n" );
                break;
            case    6:
                printf( "     This device has Image Class Interface.\n" );
                break;
            case    7:
                printf( "     This device has Printer Class Interface.\n" );
                break;
            case    8:
                printf( "     Interface class     : Mass Storage\n" );
                switch( config[TotalLength + 6] ){
                case    0x02:
                    printf( "     Interface subclass  : SFF-8020i\n" );
                    break;
                case    0x05:
                    printf( "     Interface subclass  : SFF-8070i\n" );
                    break;
                case    0x06:
                    printf( "     Interface subclass  : MSCD_SCSI transparent command set\n" );
                    break;
                default:
                    printf( "### Interface subclass is %x , not support.\n",
                                config[TotalLength+6] );
                }
                if( config[TotalLength+7] == 0x50 )
                {
                    printf( "        Interface protocol  : BOT \n" );
                }
                else
                {
                    printf( "### Interface protocol is %x , not support.\n",
                                    config[TotalLength+7] );
                }
                break;
            case    9:
                printf( "     This device has HUB Class Interface.\n" );
                break;
            case    10:
                printf( "     This device has CDC-Data Class Interface.\n" );
                break;
            case    11:
                printf( "     This device has Chi/Smart Class Interface.\n" );
                break;
            case    13:
                printf( "     This device has Content-Sequritty Class Interface.\n" );
                break;
            case    14:
                printf( "     This device has Video Class Interface.\n" );
                break;
            case    255:
                printf( "     Interface class     : Vendor Specific\n" );
                break;
            case    0:
                printf( "     Interface class     : Interface class error\n" );
                break;
            default:
                printf( "     This device has not USB Device Class Interface.\n" );
                break;
            }
            break;
        case    0x05:
            usb0_output_endpoint_descinfo( ( uint8_t* )&config[TotalLength] );
            break;
        default:
            break;
        }

        if(config[TotalLength] == 0)
        {
            return;
        }

        TotalLength += config[TotalLength];
    }
}

/*******************************************************************************
* Function Name: usb0_output_endpoint_descinfo
* Description  :
* Arguments    :
* Return Value :
*******************************************************************************/
static void usb0_output_endpoint_descinfo( uint8_t* Table )
{
    uint16_t    Epnum;
    uint16_t    PipeMaxp;

    switch( (uint8_t)(Table[3] & USB_EP_TRNS_MASK) )
    {
    case    USB_EP_ISO:
        printf( "        ISOCHRONOUS" );
        break;
    case    USB_EP_BULK:
        printf( "        BULK" );
        break;
    case    USB_EP_INT:
        printf( "        INTERRUPT" );
        break;
    default:
        printf( "### Control pipe is not support.\n" );
        break;
    }

    if ( (uint8_t)(Table[2] & USB_EP_DIR_MASK) == USB_EP_IN )
    {
        printf( " IN  endpoint" );
    }
    else
    {
        printf( " OUT endpoint" );
    }

    Epnum    = (uint16_t)( (uint16_t)Table[2]&USB_EP_NUM_MASK );
    PipeMaxp = (uint16_t)( (uint16_t)Table[4] |
                          (uint16_t)((uint16_t)(Table[5]) << 8) );
    printf( " is %d. MaxPktSize is %d. \n",Epnum, PipeMaxp );
    switch( (uint8_t)(Table[3] & (uint8_t)USB_EP_TRNS_MASK) )
    {
    case    0x01:
    case    0x03:
        printf( "          interval time is %d\n",Table[6] );
        break;
    default:
        break;
    }
}

/* End of File */
