/**HEADER********************************************************************
* 
* Copyright (c) 2008, 2013 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED 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 FREESCALE OR ITS 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.
*
**************************************************************************
*
* $FileName: usb_host_hub_sm.c$
* $Version : 
* $Date    : 
*
* Comments:
*
*   This file contains the implementation of hub state machine on host.
*
*END************************************************************************/
#include "usb_host_config.h"
#if USBCFG_HOST_HUB
#include "usb.h"
#include "usb_host_stack_interface.h"
#include "usb_host_hub.h"
#include "usb_host_hub_prv.h"
#include "usb_host_hub_sm.h"

#define USBCFG_HUB_TASK_PRIORITY    7
#define USBCFG_HUB_TASK_STACKSIZE   1000
#define USB_HUB_TASK_NAME           "HUB Task"

#define USB_HUB_EVENT_CTRL           (0x01)
#define USB_HUB_EVENT_DELETE         (0x02)

#if ((OS_ADAPTER_ACTIVE_OS == OS_ADAPTER_MQX) || (OS_ADAPTER_ACTIVE_OS == OS_ADAPTER_SDK))          /* USB stack running on MQX */
#if !(USE_RTOS)
#define USB_HUB_TASK_ADDRESS 			   usb_hub_task
#else
#define USB_HUB_TASK_ADDRESS              usb_hub_task_stun
#endif

#elif (OS_ADAPTER_ACTIVE_OS == OS_ADAPTER_BM)        /* USB stack running on BM  */
#define USB_HUB_TASK_ADDRESS              usb_hub_task
#endif


volatile uint32_t g_hub_task = 0xFFFFFFFF;

volatile HUB_DEVICE_STRUCT_PTR g_hub_link = NULL;

OS_Sem_handle             g_hub_sem = NULL;

extern usb_host_handle usb_host_dev_mng_get_host(usb_device_instance_handle dev_handle);
extern uint8_t usb_host_dev_mng_get_level(usb_device_instance_handle dev_handle);
extern uint8_t usb_host_dev_mng_get_address(usb_device_instance_handle dev_handle);

extern usb_status usb_host_dev_mng_attach(usb_host_handle handle,HUB_DEVICE_STRUCT_PTR hub, uint8_t speed, uint8_t hub_no, uint8_t port_no, uint8_t level, usb_device_instance_handle* dev_handle_ptr);

extern usb_status usb_host_dev_mng_detach(usb_host_handle handle, uint8_t hub_no, uint8_t port_no);

void usb_host_hub_device_sm(void*, void *, uint8_t *, uint32_t, usb_status);
static void usb_host_hub_int_callback(void*, void *, uint8_t *, uint32_t, usb_status);
static void usb_host_hub_rescan(HUB_DEVICE_STRUCT_PTR hub_instance, HUB_COMMAND* hub_com);
static void usb_host_hub_get_bitmap(HUB_DEVICE_STRUCT_PTR hub_instance, HUB_COMMAND* hub_com);


static void usb_host_hub_rescan(HUB_DEVICE_STRUCT_PTR hub_instance, HUB_COMMAND* hub_com)
{
    HUB_PORT_STRUCT_PTR            port_status_ptr = NULL;
    uint8_t                        i;

    for (i = 0; i < hub_instance->HUB_PORT_NR; i++)
    {
        port_status_ptr = hub_instance->HUB_PORTS + i;
        if ((port_status_ptr->STATUS & (1 << PORT_CONNECTION)) &&
           !((hub_instance->HUB_PORTS + i)->APP_STATUS & HUB_PORT_ATTACHED))
        {
            /* if some device is attached since startup, then start session */
            hub_instance->port_iterator = i;
            hub_instance->STATE = HUB_GET_PORT_STATUS_ASYNC;
            //(hub_instance->HUB_PORTS + i)->APP_STATUS = 0;
            if (USB_OK != usb_class_hub_get_port_status(hub_com, hub_instance->port_iterator + 1, hub_instance->port_status_buffer, 4))
            {
#if _DEBUG
                printf("ERROR usb_class_hub_get_port_status in HUB_GET_PORT_STATUS_PROCESS\r\n");
#endif
            }
            else
            {
#if _DEBUG
                printf("get port %d status 0x%x\r\n", i, port_status_ptr->STATUS);
#endif
                hub_instance->in_control = 1;
            }
            break;
        }
    }

    if (hub_instance->in_control == 0)
    {
        usb_host_hub_get_bitmap(hub_instance, hub_com);
    }
}

static void usb_host_hub_get_bitmap(HUB_DEVICE_STRUCT_PTR hub_instance, HUB_COMMAND* hub_com)
{
    if ((hub_instance->in_recv == 0))
    {
        hub_com->CLASS_PTR      = hub_instance->class_handle;
        hub_com->CALLBACK_FN    = usb_host_hub_int_callback;
        hub_com->CALLBACK_PARAM = hub_instance;
        if (USB_OK != usb_class_hub_recv_bitmap(hub_com, hub_instance->bit_map_buffer, hub_instance->HUB_PORT_NR / 8 + 1))
        {
#if _DEBUG
            printf("error in hub usb_class_hub_recv_bitmap\r\n");
#endif
            hub_instance->STATE = HUB_NONE;
        }
        else
        {
            hub_instance->in_recv = 1;
        }
    }
}


//static void usb_host_hub_control_callback(void*, void *, pointer, uint32_t, usb_status);

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_hub_get_instance
* Returned Value : None
* Comments       :
*     called when a hub has been attached, detached, etc.
*END*--------------------------------------------------------------------*/
usb_status usb_host_hub_get_instance
    (
         /* [IN] pointer to device instance */
         usb_device_instance_handle      dev_handle,

         /* [IN] pointer to interface descriptor */
         usb_interface_descriptor_handle intf_handle,

         /* [OUT] pointer to CLASS_CALL_STRUCT to be filled in */
         HUB_DEVICE_STRUCT_PTR *          hub_instance_ptr
    )
{
    HUB_DEVICE_STRUCT_PTR instance_ptr = NULL;
    //HUB_DEVICE_STRUCT_PTR target_ptr = NULL;
    
    OS_Lock();
    
    instance_ptr = g_hub_link;
    while (instance_ptr != NULL)
    {
        if ((instance_ptr->DEV_HANDLE != dev_handle) ||
           (instance_ptr->INTF_HANDLE != intf_handle))
        {
            instance_ptr = instance_ptr->next;
        }
        else
        {
            *hub_instance_ptr = instance_ptr;
            OS_Unlock();
            return USB_OK;
        }
    }
    OS_Unlock();
    *hub_instance_ptr = NULL;
    return USBERR_ERROR;
}

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_hub_create_instance
* Returned Value : None
* Comments       :
*     called when a hub has been attached, detached, etc.
*END*--------------------------------------------------------------------*/
static HUB_DEVICE_STRUCT_PTR usb_host_hub_create_instance
    (
         /* [IN] pointer to device instance */
         usb_device_instance_handle      dev_handle,

         /* [IN] pointer to interface descriptor */
         usb_interface_descriptor_handle intf_handle
    )
{
    HUB_DEVICE_STRUCT_PTR hub_instance = NULL;

    hub_instance = (HUB_DEVICE_STRUCT_PTR)OS_Mem_alloc_zero(sizeof(HUB_DEVICE_STRUCT));
    if (hub_instance == NULL)
    {
#if _DEBUG
        printf("OS_Mem_alloc_zero error in usb_host_hub_create_instance\r\n");
#endif
        return NULL;
    }
    
    hub_instance->hub_descriptor_buffer = (uint8_t *)OS_Mem_alloc_uncached_zero(7 + MAX_HUB_PORT_NUMBER / 8 + 1);
    if (hub_instance->hub_descriptor_buffer == NULL)
    {
#if _DEBUG
        printf("failed to get memory for hub descriptor\r\n");
#endif
    }
    
    hub_instance->port_status_buffer = (uint8_t *)OS_Mem_alloc_uncached_zero(4);
    if (hub_instance->port_status_buffer == NULL)
    {
#if _DEBUG
        printf("failed to get memory for port status\r\n");
#endif
    }
    
    hub_instance->bit_map_buffer = (uint8_t *)OS_Mem_alloc_uncached_zero(MAX_HUB_PORT_NUMBER / 8 + 1);
    if (hub_instance->bit_map_buffer == NULL)
    {
#if _DEBUG
        printf("failed to get memory for bit map\r\n");
#endif
    }
    
    hub_instance->DEV_HANDLE = dev_handle;
    hub_instance->INTF_HANDLE = intf_handle;
    hub_instance->in_recv  = 0;
    hub_instance->host_handle = usb_host_dev_mng_get_host(dev_handle);
    hub_instance->hub_level = usb_host_dev_mng_get_level(dev_handle);
    
    return hub_instance;
}

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_hub_destroy_instance
* Returned Value : None
* Comments       :
*     called when a hub has been attached, detached, etc.
*END*--------------------------------------------------------------------*/
usb_status usb_host_hub_destroy_instance
    (
         HUB_DEVICE_STRUCT_PTR instance_ptr
    )
{
    if (instance_ptr == NULL)
    {
        return USBERR_ERROR;
    }
    if (instance_ptr->HUB_PORTS != NULL)
    {
        OS_Mem_free(instance_ptr->HUB_PORTS);
        instance_ptr->HUB_PORTS = NULL;
    }
    if (instance_ptr->hub_descriptor_buffer != NULL)
    {
        OS_Mem_free(instance_ptr->hub_descriptor_buffer);
        instance_ptr->hub_descriptor_buffer = NULL;
    }
    
    if (instance_ptr->port_status_buffer != NULL)
    {
        OS_Mem_free(instance_ptr->port_status_buffer);
        instance_ptr->port_status_buffer = NULL;
    }

    if (instance_ptr->bit_map_buffer != NULL)
    {
        OS_Mem_free(instance_ptr->bit_map_buffer);
        instance_ptr->bit_map_buffer = NULL;
    }

    OS_Mem_free(instance_ptr);
    return USB_OK;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_task
*  Returned Value : none
*  Comments       :
*        KHCI task
*END*-----------------------------------------------------------------*/
static void usb_hub_task(void* hub_instance)
{
    HUB_DEVICE_STRUCT_PTR instance_ptr;
    HUB_DEVICE_STRUCT_PTR pre;
    
    {
        if(OS_Sem_wait(g_hub_sem, 0) != OS_EVENT_OK)
        {
            return;
        }

        OS_Lock();
        pre = instance_ptr = g_hub_link;
        
        while (instance_ptr != NULL)
        {
            if (instance_ptr->to_be_deleted != 1)
            {
                pre = instance_ptr;
                instance_ptr = instance_ptr->next; 
            }
            else
            {
                if ((instance_ptr->in_control == 0) &&
                    (instance_ptr->in_recv == 0))
                {
                    if (instance_ptr == g_hub_link)
                    {
                        g_hub_link = instance_ptr->next;
                    }
                    else
                    {
                        pre->next = instance_ptr->next;
                    }
                    if (USB_OK != usb_host_close_dev_interface(instance_ptr->host_handle, instance_ptr->DEV_HANDLE, instance_ptr->INTF_HANDLE, instance_ptr->class_handle))
                    {
#if _DEBUG
                        printf("error in hub usb_host_close_dev_interface\r\n");
#endif
                    }
                    printf("level %d hub disconnected\r\n", instance_ptr->hub_level);
                    usb_host_hub_destroy_instance(instance_ptr);
                    break;
                }
                else
                {
                    pre = instance_ptr;
                    instance_ptr = instance_ptr->next; 
                }
            }
        }
        OS_Unlock();  
    }
    return ;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_task_stun
*  Returned Value : none
*  Comments       :
*        KHCI task
*END*-----------------------------------------------------------------*/
#if ((OS_ADAPTER_ACTIVE_OS == OS_ADAPTER_MQX) || ((OS_ADAPTER_ACTIVE_OS == OS_ADAPTER_SDK) && (USE_RTOS))) 
static void usb_hub_task_stun(void* hub_instance)
{
    while(1)
    {
        usb_hub_task(hub_instance);
    }
}
#endif

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_hub_device_event
* Returned Value : None
* Comments       :
*     called when a hub has been attached, detached, etc.
*END*--------------------------------------------------------------------*/
void usb_host_hub_device_event
   (
      /* [IN] pointer to device instance */
      usb_device_instance_handle      dev_handle,

      /* [IN] pointer to interface descriptor */
      usb_interface_descriptor_handle intf_handle,

      /* [IN] code number for event causing callback */
      uint32_t                          event_code
   )
{
    HUB_DEVICE_STRUCT_PTR hub_instance;
    HUB_DEVICE_STRUCT_PTR temp;
    HUB_COMMAND           hub_com;
    uint8_t               i;

    switch (event_code) {
        case USB_ATTACH_EVENT:
            /* Drop through into attach, same processing */
            break;
        case USB_CONFIG_EVENT:
            /* Create 'unsafe' application struct */
            hub_instance = usb_host_hub_create_instance(dev_handle, intf_handle);
            
            if (NULL != hub_instance)
            {
                hub_instance->STATE     = HUB_IDLE;

                if (g_hub_sem == NULL)
                {
                    g_hub_sem = OS_Sem_create(0);
                    if (g_hub_sem == NULL)
                    {
#if _DEBUG
                        printf("create sem for hub error\r\n");
#endif
                    }
                }
                if (g_hub_task == 0xFFFFFFFF)
                {
                    g_hub_task = OS_Task_create(USB_HUB_TASK_ADDRESS, (void*)hub_instance, (uint32_t)USBCFG_HUB_TASK_PRIORITY, USBCFG_HUB_TASK_STACKSIZE, USB_HUB_TASK_NAME, NULL);
                    if (g_hub_task == (uint32_t)OS_TASK_ERROR)
                    {
#if _DEBUG
                        printf("failed to create hub task\r\n");
#endif
                    }
                }     
                
                OS_Lock();
                if (g_hub_link == NULL)
                {
                    g_hub_link = hub_instance;
                }
                else
                {
                    temp = g_hub_link;
                    while (temp->next != NULL)
                    {
                        temp = temp->next;
                    }
                    temp->next = hub_instance;
                }
                OS_Unlock();
                
                if (USB_OK != usb_host_open_dev_interface(hub_instance->host_handle, dev_handle, intf_handle, (class_handle*)&hub_instance->class_handle))
                {
#if _DEBUG                
                    printf("error in hub usb_host_open_dev_interface\r\n");
#endif
                }
                else
                {
#if _DEBUG
                    printf("usb_host_open_dev_interface\r\n");
#endif                    
                }
            }
            break;
            
        case USB_INTF_OPENED_EVENT:
#if _DEBUG
            printf("USB_INTF_OPENED_EVENT\r\n");
#endif
            if (USB_OK != usb_host_hub_get_instance(dev_handle, intf_handle, &hub_instance))
            {
#if _DEBUG            
                printf("ERROR usb_host_hub_get_instance in USB_INTF_OPENED_EVENT\r\n");
#endif
                break;
            }
            else
            {
                printf("level %d hub connected\r\n", hub_instance->hub_level);
            }
            /* set we are in process of getting hub descriptor */
            //hub_instance->STATE    = HUB_BEGIN_GET_DESCRIPTOR_TINY_PROCESS;
            hub_instance->STATE    = HUB_GET_DESCRIPTOR_TINY_PROCESS;
            hub_com.CLASS_PTR      = hub_instance->class_handle;
            hub_com.CALLBACK_FN    = usb_host_hub_device_sm;
            hub_com.CALLBACK_PARAM = hub_instance;
            /* here, we should retrieve information from the hub */
            if (USB_OK != usb_class_hub_get_descriptor(&hub_com, hub_instance->hub_descriptor_buffer, 7))
            {
#if _DEBUG            
                printf("error in hub usb_class_hub_get_descriptor\r\n");
#endif
                hub_instance->STATE = HUB_NONE;
            }
            else
            {
                hub_instance->in_control = 1;
            }
            break;

        case USB_DETACH_EVENT:
            if (USB_OK != usb_host_hub_get_instance(dev_handle, intf_handle, &hub_instance))
            {
#if _DEBUG
                printf("ERROR usb_host_hub_get_instance in USB_INTF_OPENED_EVENT\r\n");
#endif
                break;
            }

            for (i = 0; i < hub_instance->HUB_PORT_NR; i++)
            {
                if ((hub_instance->HUB_PORTS + i)->APP_STATUS & HUB_PORT_ATTACHED)
                {
                    usb_host_dev_mng_detach(hub_instance->host_handle,
                                            usb_host_dev_mng_get_address(hub_instance->DEV_HANDLE),
                                            i + 1);
                }
            }

            OS_Lock();
            hub_instance->to_be_deleted = 1;
            OS_Unlock();

            OS_Sem_post(g_hub_sem);

            break;

        default:
            break;
   } /* EndSwitch */

} /* Enbbody */




/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_hub_device_sm
* Returned Value : None
* Comments       :
*     called when a hub changes state; sm = state machine
*END*--------------------------------------------------------------------*/
void usb_host_hub_device_sm
   (
         /* [IN] structure with USB pipe information on the interface */
         void*           pipe,

         /* [IN] parameters */
         void*           param,

         /* [IN] buffer of data from IN stage */
         uint8_t*          buffer,

         /* [IN] length of data from IN stage */
         uint32_t           len,

         /* [IN] status of transaction */
         usb_status        status
   )
{
    register HUB_DEVICE_STRUCT_PTR hub_instance = (HUB_DEVICE_STRUCT_PTR) param;
    HUB_COMMAND                    hub_com;
    usb_device_instance_handle    dev_handle;
    HUB_DESCRIPTOR_STRUCT_PTR      hub_desc = NULL;
    HUB_PORT_STRUCT_PTR            port_status_ptr = NULL;
    uint8_t                        speed;
    uint8_t                        i;
    uint32_t                       stat;

    hub_instance->in_control = 0;

    if (hub_instance->to_be_deleted == 1)
    {
        OS_Sem_post(g_hub_sem);
        return;
    }

    if (status != USB_OK)
    {
#if _DEBUG    
        printf("last request failed 0x%x\r\n", hub_instance->STATE);
#endif
        return;
    }

    hub_com.CLASS_PTR      = hub_instance->class_handle;
    hub_com.CALLBACK_FN    = usb_host_hub_device_sm;
    hub_com.CALLBACK_PARAM = hub_instance;

    switch (hub_instance->STATE)
    {
        case HUB_IDLE:
        case HUB_BEGIN_GET_DESCRIPTOR_TINY_PROCESS:
        case HUB_BEGIN_GET_DESCRIPTOR_PROCESS:
        case HUB_BEGIN_SET_PORT_FEATURE_PROCESS:
        case HUB_BEGIN_CLEAR_PORT_FEATURE_PROCESS:
        case HUB_BEGIN_GET_PORT_STATUS_PROCESS:
        case HUB_BEGIN_GET_PORT_STATUS_ASYNC:
        case HUB_BEGIN_GET_STATUS_ASYNC:   
            break;
        case HUB_GET_DESCRIPTOR_TINY_PROCESS: 
            /* here we get number of ports from USB data */
            hub_instance->HUB_PORT_NR = ((HUB_DESCRIPTOR_STRUCT_PTR)hub_instance->hub_descriptor_buffer)->BNRPORTS;
            if (hub_instance->HUB_PORT_NR > MAX_HUB_PORT_NUMBER)
            {
#if _DEBUG
                printf("the port number exceeds the MAX_HUB_PORT_NUMBER\r\n");
#endif
                break;
            }
            //hub_instance->STATE = HUB_BEGIN_GET_DESCRIPTOR_PROCESS;
            hub_instance->STATE = HUB_GET_DESCRIPTOR_PROCESS;
            if (USB_OK != usb_class_hub_get_descriptor(&hub_com, hub_instance->hub_descriptor_buffer, 7 + hub_instance->HUB_PORT_NR / 8 + 1))
            {
#if _DEBUG            
                printf("error in hub usb_class_hub_get_descriptor\r\n");
#endif                
                hub_instance->STATE = HUB_NONE;
            }
            else
            {
                hub_instance->in_control = 1;
            }
            break;

        case HUB_GET_DESCRIPTOR_PROCESS: 
            {
                /* here, we get information from the hub and fill info in hub_instance */
                hub_desc = (HUB_DESCRIPTOR_STRUCT_PTR)hub_instance->hub_descriptor_buffer;
                
                hub_instance->HUB_PORTS = (HUB_PORT_STRUCT_PTR)OS_Mem_alloc_zero(hub_instance->HUB_PORT_NR * sizeof(HUB_PORT_STRUCT));
            
                for (i = 0; i < hub_instance->HUB_PORT_NR; i++) 
                {
                    port_status_ptr = hub_instance->HUB_PORTS + i;
                    /* get REMOVABLE bit from the desriptor for appropriate installed port */
                    port_status_ptr->APP_STATUS = hub_desc->DEVICEREMOVABLE[(i + 1) / 8] & (0x01 << ((i + 1) % 8)) ? HUB_PORT_REMOVABLE : 0;
                }
            }

            /* pass fluently to HUB_SET_PORT_FEATURE_PROCESS */
            //hub_instance->STATE = HUB_BEGIN_SET_PORT_FEATURE_PROCESS;
            hub_instance->STATE = HUB_SET_PORT_FEATURE_PROCESS;
            hub_instance->port_iterator = 0;

        case HUB_SET_PORT_FEATURE_PROCESS: 
            if (hub_instance->port_iterator < hub_instance->HUB_PORT_NR) 
            {
                //hub_instance->STATE = HUB_BEGIN_SET_PORT_FEATURE_PROCESS;
                hub_instance->STATE = HUB_SET_PORT_FEATURE_PROCESS;
                
                if (USB_OK != usb_class_hub_set_port_feature(&hub_com, ++hub_instance->port_iterator, PORT_POWER))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_set_port_feature\r\n");
#endif
                    hub_instance->STATE = HUB_NONE;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                break;
            }

            /* pass fluently to HUB_CLEAR_PORT_FEATURE_PROCESS */
            //hub_instance->STATE = HUB_BEGIN_CLEAR_PORT_FEATURE_PROCESS;
            hub_instance->STATE = HUB_CLEAR_PORT_FEATURE_PROCESS;
            hub_instance->port_iterator = 0;
        case HUB_CLEAR_PORT_FEATURE_PROCESS: 
            if (hub_instance->port_iterator < hub_instance->HUB_PORT_NR) 
            {
                //hub_instance->STATE = HUB_BEGIN_CLEAR_PORT_FEATURE_PROCESS;
                hub_instance->STATE = HUB_CLEAR_PORT_FEATURE_PROCESS;
                
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, ++hub_instance->port_iterator, C_PORT_CONNECTION))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_clear_port_feature\r\n");
#endif
                    hub_instance->STATE = HUB_NONE;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                break;
            }

            /* pass fluently to HUB_GET_PORT_STATUS_PROCESS */
            //hub_instance->STATE = HUB_BEGIN_GET_PORT_STATUS_PROCESS;
            hub_instance->STATE = HUB_GET_PORT_STATUS_PROCESS;
            hub_instance->port_iterator = 0;
        case HUB_GET_PORT_STATUS_PROCESS: 
            if (hub_instance->port_iterator)
            {
                port_status_ptr = hub_instance->HUB_PORTS + hub_instance->port_iterator - 1;
                port_status_ptr->STATUS = USB_SHORT_LE_TO_HOST(*(uint32_t*)hub_instance->port_status_buffer);
            }
            if (hub_instance->port_iterator < hub_instance->HUB_PORT_NR)
            {
                //hub_instance->STATE = HUB_BEGIN_GET_PORT_STATUS_PROCESS;
                hub_instance->STATE = HUB_GET_PORT_STATUS_PROCESS;
                
                if (USB_OK != usb_class_hub_get_port_status(&hub_com, ++hub_instance->port_iterator, hub_instance->port_status_buffer, 4))
                {
#if _DEBUG
                    printf("error in hub usb_class_hub_get_port_status\r\n");
#endif                    
                    hub_instance->STATE = HUB_NONE;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                break;
            }
            usb_host_hub_rescan(hub_instance, &hub_com);

            break;
            
        case HUB_BEGIN_PORT_RESET:
            (hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS |= HUB_PORT_BEGINRESET;
            hub_instance->STATE = HUB_GET_PORT_STATUS_ASYNC;
            if (USB_OK != usb_class_hub_get_port_status(&hub_com, hub_instance->port_iterator + 1, hub_instance->port_status_buffer, 4))
            {
#if _DEBUG            
                printf("usb_class_hub_get_port_status failed in usb_host_hub_int_callback\r\n");
#endif
                hub_instance->STATE = HUB_NONE;
            }
            else
            {
                hub_instance->in_control = 1;
            }
            break;

        case HUB_RESET_DEVICE_PORT_PROCESS:
            hub_instance->STATE = HUB_BEGIN_PORT_RESET;
            
            if (USB_OK != usb_class_hub_set_port_feature(&hub_com, hub_instance->port_iterator + 1, PORT_RESET))
            {
#if _DEBUG            
                printf("error in usb_class_hub_set_port_feature of PORT_RESET\r\n");
#endif
            }
            else
            {
                hub_instance->in_control = 1;
            }
            break;

        case HUB_ADDRESS_DEVICE_PORT_PROCESS:
            /* compute speed */
            if ((hub_instance->HUB_PORTS + hub_instance->port_iterator)->STATUS & (1 << PORT_HIGH_SPEED))
                speed = USB_SPEED_HIGH;
            else if ((hub_instance->HUB_PORTS + hub_instance->port_iterator)->STATUS & (1 << PORT_LOW_SPEED))
                speed = USB_SPEED_LOW;
            else
                speed = USB_SPEED_FULL;
            /* FIXME: accessing intf_handle directly without validation to get its host handle is not good method */
            usb_host_dev_mng_attach(
                hub_instance->host_handle,
                hub_instance,
                speed, /* port speed */
                usb_host_dev_mng_get_address(hub_instance->DEV_HANDLE), /* hub address */
                hub_instance->port_iterator + 1, /* hub port */
                hub_instance->hub_level + 1, /* hub level */
                &dev_handle
                );
            //(hub_instance->HUB_PORTS + i)->APP_STATUS |= HUB_PORT_ATTACHED;
            hub_instance->STATE = HUB_NONE;
            usb_host_hub_rescan(hub_instance, &hub_com);

            break;

        case HUB_DETACH_DEVICE_PORT_PROCESS:
            /* FIXME: accessing intf_handle directly without validation to get its host handle is not good method */
            usb_host_dev_mng_detach(
                hub_instance->host_handle,
                usb_host_dev_mng_get_address(hub_instance->DEV_HANDLE), /* hub address */
                hub_instance->port_iterator + 1 /* hub port */
                );

            /* reset the app status */
            (hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS = 0x00;
             hub_instance->STATE = HUB_NONE;
            usb_host_hub_rescan(hub_instance, &hub_com);

            break;
            
        case HUB_GET_PORT_STATUS_ASYNC:
            stat = USB_LONG_LE_TO_HOST(*(uint32_t*)(hub_instance->port_status_buffer));
#if _DEBUG            
            printf("stat 0x%x 0x%x\r\n", stat, (hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS);
#endif
            /* register the current status of the port */
            (hub_instance->HUB_PORTS + hub_instance->port_iterator)->STATUS = stat;

            if ((hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS & HUB_PORT_BEGINRESET)
            {
                if ((1 << C_PORT_CONNECTION) & stat)
                {
                    hub_instance->STATE = HUB_NONE;
                    if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_PORT_CONNECTION))
                    {
#if _DEBUG                    
                        printf("error in hub usb_class_hub_clear_port_feature C_PORT_CONNECTION\r\n");
#endif
                        hub_instance->STATE = HUB_NONE;
                    }
                    else
                    {
                        hub_instance->in_control = 1;
                    }
                    (hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS &= ~HUB_PORT_BEGINRESET;
                }
                else if(!((1 << PORT_CONNECTION) & stat))
                {
                    hub_instance->STATE = HUB_NONE;
                    usb_host_hub_rescan(hub_instance, &hub_com);
                }
                else if (!((1 << C_PORT_RESET) & stat))
                {
                    hub_instance->STATE = HUB_GET_PORT_STATUS_ASYNC;
                    if (USB_OK != usb_class_hub_get_port_status(&hub_com, hub_instance->port_iterator + 1, hub_instance->port_status_buffer, 4))
                    {
#if _DEBUG                    
                        printf("usb_class_hub_get_port_status failed in HUB_WAIT_FOR_PORT_RESET\r\n");
#endif
                        hub_instance->STATE = HUB_NONE;
                    }
                    else
                    {
                        hub_instance->in_control = 1;
                    }
                }
            }
            else
            {
                /* let's see what happened */
                if (((1 << C_PORT_CONNECTION) & stat))
                {
                    /* get if a device on port was attached or detached */
                    if ((hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS & HUB_PORT_ATTACHED)
                    {
                        //hub_instance->STATE = HUB_BEGIN_DETACH_DEVICE_PORT_PROCESS;
                        hub_instance->STATE = HUB_DETACH_DEVICE_PORT_PROCESS;
                    }
                    else 
                    {
                        if((1 << PORT_CONNECTION) & stat)
                        {
                            //hub_instance->STATE = HUB_BEGIN_RESET_DEVICE_PORT_PROCESS;
                            hub_instance->STATE = HUB_RESET_DEVICE_PORT_PROCESS;
                        }
                        else
                        {
                            hub_instance->STATE = HUB_NONE;
                        }
                    }
                    
                    if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_PORT_CONNECTION))
                    {
#if _DEBUG                    
                        printf("error in hub usb_class_hub_clear_port_feature C_PORT_CONNECTION\r\n");
#endif
                        hub_instance->STATE = HUB_NONE;
                    }
                    else
                    {
                        hub_instance->in_control = 1;
                    }
                    break;
                }
                else if((1 << PORT_CONNECTION) & stat)
                {
                   /* get if a device on port was attached or detached */
                    if ((hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS & HUB_PORT_ATTACHED)
                    {
                        //hub_instance->STATE = HUB_BEGIN_DETACH_DEVICE_PORT_PROCESS;
                        hub_instance->STATE = HUB_DETACH_DEVICE_PORT_PROCESS;
                    }
                    else
                    {
                        hub_instance->STATE = HUB_RESET_DEVICE_PORT_PROCESS;
                    }
                    
                    if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, PORT_CONNECTION))
                    {
#if _DEBUG                    
                        printf("error in hub usb_class_hub_clear_port_feature PORT_CONNECTION\r\n");
#endif                        
                        hub_instance->STATE = HUB_NONE;
                    }
                    else
                    {
                        hub_instance->in_control = 1;
                    }
                    break;
                }
            }
            
            if ((1 << C_PORT_RESET) & stat)
            {
                //hub_instance->STATE = HUB_BEGIN_ADDRESS_DEVICE_PORT_PROCESS;
                hub_instance->STATE = HUB_ADDRESS_DEVICE_PORT_PROCESS;
                (hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS &= ~(HUB_PORT_BEGINRESET);
                (hub_instance->HUB_PORTS + hub_instance->port_iterator)->APP_STATUS |= HUB_PORT_ATTACHED;
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_PORT_RESET))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_clear_port_feature C_PORT_RESET2222\r\n");
#endif                    
                    hub_instance->STATE = HUB_NONE;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                
                break;
            }
            else if ((1 << C_PORT_ENABLE) & stat)
            {
                /* unexpected event (error), just ignore the port */
                //hub_instance->STATE = HUB_BEGIN_DETACH_DEVICE_PORT_PROCESS;
                hub_instance->STATE = HUB_DETACH_DEVICE_PORT_PROCESS;
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_PORT_ENABLE))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_clear_port_feature C_PORT_ENABLE\r\n");
#endif
                    hub_instance->STATE = HUB_NONE;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                usb_host_hub_get_bitmap(hub_instance, &hub_com);
                break;
            }
            else if ((1 << C_PORT_OVER_CURRENT) & stat) 
            {
                /* unexpected event (error), just ignore the port */
                hub_instance->STATE = HUB_NONE;
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_PORT_OVER_CURRENT))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_clear_port_feature C_PORT_OVER_CURRENT\r\n");
#endif
                    break;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                //usb_host_hub_get_bitmap(hub_instance, &hub_com);
                break;
            }
            else if ((1 << C_PORT_POWER) & stat) 
            {
                /* unexpected event (error), just ignore the port */
                hub_instance->STATE = HUB_NONE;
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_PORT_POWER))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_clear_port_feature C_PORT_POWER\r\n");
#endif

                }
                else
                {
                    hub_instance->in_control = 1;
                }
                //usb_host_hub_get_bitmap(hub_instance, &hub_com);
                break;
            }
            /* FIXME: handle more events */
            else
            {
                hub_instance->STATE = HUB_NONE;
                usb_host_hub_get_bitmap(hub_instance, &hub_com);
            }

            break;

        case HUB_GET_STATUS_ASYNC:
        {
            HUB_STATUS_STRUCT_PTR hub_stat = (HUB_STATUS_STRUCT_PTR) hub_instance->port_status_buffer;
            uint32_t change = USB_SHORT_LE_TO_HOST(hub_stat->WHUBCHANGE);

            if ((1 << C_HUB_LOCAL_POWER) & change) 
            {
                hub_instance->STATE = HUB_NONE;
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_HUB_LOCAL_POWER))
                {
#if _DEBUG                
                    printf("error in hub usb_class_hub_clear_port_feature C_HUB_LOCAL_POWER\r\n");
#endif
                    break;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                usb_host_hub_get_bitmap(hub_instance, &hub_com);
            }
            else if ((1 << C_HUB_OVER_CURRENT) & change) 
            {
                hub_instance->STATE = HUB_NONE;
                if (USB_OK != usb_class_hub_clear_port_feature(&hub_com, hub_instance->port_iterator + 1, C_HUB_OVER_CURRENT))
                {
#if _DEBUG
                    printf("error in hub usb_class_hub_clear_port_feature C_HUB_OVER_CURRENT\r\n");
#endif
                    break;
                }
                else
                {
                    hub_instance->in_control = 1;
                }
                usb_host_hub_get_bitmap(hub_instance, &hub_com);
            }
            else
            {
                hub_instance->STATE = HUB_NONE;
                usb_host_hub_get_bitmap(hub_instance, &hub_com);
            }
            break;
        }

        default:
            usb_host_hub_rescan(hub_instance, &hub_com);
            break;

    } /* EndSwitch */
    return;
} /* EndBody */



/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_hub_int_callback
* Returned Value : None
* Comments       :
*     Called on interrupt endpoint data reception
*END*--------------------------------------------------------------------*/

static void usb_host_hub_int_callback
   (
      /* [IN] pointer to pipe */
      void*           tr,

      /* [IN] user-defined parameter */
      void*           param,

      /* [IN] buffer address */
      uint8_t*          buffer,

      /* [IN] length of data transferred */
      uint32_t           len,

      /* [IN] status, hopefully USB_OK or USB_DONE */
      usb_status        status
   )
{ /* Body */
    HUB_DEVICE_STRUCT_PTR          hub_instance = (HUB_DEVICE_STRUCT_PTR) param;
    HUB_COMMAND                    hub_com;
    int32_t                         i, j, port = 0;
    uint8_t *                   port_pattern= (uint8_t *) buffer;
#if _DEBUG
    //printf("%d bm 0x%x 0x%x\r\n", hub_instance->hub_level, port_pattern[0], hub_instance->STATE);
#endif
    hub_instance->in_recv = 0;
    
    if (hub_instance->to_be_deleted == 1)
    {
        OS_Sem_post(g_hub_sem);
        return;
    }

    if (status != USB_OK)
    {
#if _DEBUG    
        printf("can't get level %d hub bit map\r\n", hub_instance->hub_level);
#endif
        return ;
    }
    
    hub_com.CLASS_PTR      = hub_instance->class_handle;
    hub_com.CALLBACK_FN    = usb_host_hub_device_sm;
    hub_com.CALLBACK_PARAM = hub_instance;
   
    /* find which port changed its state */
    for (i = 0; i < len; i++) 
    {
        if (!port_pattern[i])
            continue;
        port = i * 8;
        for (j = 0; j < 8; j++, port++) 
        {
            if (port_pattern[i] & 0x01)
                break;
            port_pattern[i] >>= 1;
        }
        break;
    }

    /* The port number, which changed status, is written in variable "port"
    ** Note, that if there are more ports, which changed its status, these will
    ** be invoked later (in next interrupt)
    */

    if (port == 0)
    {
        hub_instance->STATE = HUB_GET_STATUS_ASYNC;
        if (USB_OK != usb_class_hub_get_status(&hub_com, hub_instance->port_status_buffer, 4))
        {
#if _DEBUG        
            printf("usb_class_hub_get_status failed in usb_host_hub_int_callback\r\n");
#endif            
            hub_instance->STATE = HUB_NONE;
        }
        else
        {
#if _DEBUG        
            printf("try to get status\r\n");
#endif            
            hub_instance->in_control = 1;
        }
    }
    else {

        if((hub_instance->STATE != HUB_GET_PORT_STATUS_ASYNC )&&(hub_instance->in_control != 1))
        {
            hub_instance->STATE = HUB_GET_PORT_STATUS_ASYNC;
            hub_instance->port_iterator = port - 1;
            if (USB_OK != usb_class_hub_get_port_status(&hub_com, port, hub_instance->port_status_buffer, 4))
            {
#if _DEBUG            
                printf("usb_class_hub_get_port_status failed in usb_host_hub_int_callback\r\n");
#endif                
                hub_instance->STATE = HUB_NONE;
            }
            else
            {
                hub_instance->in_control = 1;
            }
        }
    }
} /*EndBody */

#endif




