/**
 * @file hv_drv_UsbCoreHub.c
 * @brief usb core hub driver
 * @details This module provide interfaces for usb hub.
 * @author HiView SoC Software Team
 * @version 0.0.1
 * @date 20230417
 * @copyright Copyright(c),2022-9, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                         <th>Date             <th>Details
 * <tr><td>HiView SoC Software Team   <td>20230417       <td>Create and first Version.
 * </table>
 **/

#include "hv_drv_UsbBitWise.h"

#include "hv_drv_UsbErrNo.h"
#include "hv_drv_UsbCoreDevice.h"
#include "hv_drv_UsbCh9.h"

#include "hv_drv_UsbCoreQuirks.h"
#include "hv_drv_UsbCoreHcd.h"
#include "hv_drv_UsbCoreHub.h"
#include "hv_drv_UsbMusbRegs.h"

boolean g_test_mode = false;
#define USB_VENDOR_GENESYS_LOGIC            0x05e3
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND    0x01

static struct usb_hub      *s_pHub;
HV_VOS_SEMAPHORE_S         *g_pstUsbPortCheckSeamphone = NULL;


/* Note that hdev or one of its children must be locked! */
struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev) {
    if (!hdev || !hdev->actconfig || !hdev->maxchild)
        return NULL;
    return usb_get_intfdata(hdev->actconfig->interface[0]);
}

INLINE char *portspeed(struct usb_hub *hub, int portstatus) {
    if (hub_is_superspeed(hub->hdev))
        return "5.0 Gb/s";
    if (portstatus & USB_PORT_STAT_HIGH_SPEED)
        return "480 Mb/s";
    else if (portstatus & USB_PORT_STAT_LOW_SPEED)
        return "1.5 Mb/s";
    else
        return "12 Mb/s";
}

static const char *const speed_names[] = {
    "unknown",
    "low-speed",
    "full-speed",
    "high-speed",
    "wireless-speed",
    "super-speed",
};

INLINE const char *usb_speed_string(enum usb_device_speed speed) {
    if (speed < 0 || speed > USB_SPEED_SUPER) {
        speed = USB_SPEED_UNKNOWN;
    }
    return speed_names[speed];
}

void usb_set_device_state(struct usb_device *dev,
                          enum usb_device_state new_state) {
    HANDLE sc = spin_lock_irq();
    if (dev->state == USB_STATE_NOTATTACHED) {
        ;
    } else if (dev->state != USB_STATE_NOTATTACHED) {
        if (dev->parent) {
            ;   /* not root hub */
        }
        dev->state = new_state;
    }

    spin_unlock_irq(sc);
}

void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
                                    struct usb_hub_descriptor *desc) {
    struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
    enum usb_port_connect_type connect_type;
    int i;

    if (!hub)
        return;

    if (!hub_is_superspeed(hdev)) {
        for (i = 1; i <= hdev->maxchild; i++) {
            struct usb_port *port_dev = hub->ports[i - 1];

            connect_type = port_dev->connect_type;
            if (connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
                u8 mask = 1 << (i%8);

                if (!(desc->u.hs.DeviceRemovable[i/8] & mask)) {
                    desc->u.hs.DeviceRemovable[i/8]    |= mask;
                }
            }
        }
    } else {
        u16 port_removable = (desc->u.ss.DeviceRemovable);

        for (i = 1; i <= hdev->maxchild; i++) {
            struct usb_port *port_dev = hub->ports[i - 1];

            connect_type = port_dev->connect_type;
            if (connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
                u16 mask = 1 << i;

                if (!(port_removable & mask)) {
                    port_removable |= mask;
                }
            }
        }

        desc->u.ss.DeviceRemovable = port_removable;
    }
}

#define USB_STS_TIMEOUT     1000
#define USB_STS_RETRIES     5

static int get_hub_status(struct usb_device *hdev,
                          struct usb_hub_status *data) {
    int i, status = -ETIMEDOUT;

    for (i = 0; i < USB_STS_RETRIES &&
            (status == -ETIMEDOUT || status == -EPIPE); i++) {
        status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                                 USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
                                 data, sizeof(*data), USB_STS_TIMEOUT);
    }
    return status;
}

static int get_hub_descriptor(struct usb_device *hdev, void *data) {
    int i, ret, size;
    unsigned dtype;

    if (hub_is_superspeed(hdev)) {
        dtype = USB_DT_SS_HUB;
        size = USB_DT_SS_HUB_SIZE;
    } else {
        dtype = USB_DT_HUB;
        size = sizeof (struct usb_hub_descriptor);
    }

    for (i = 0; i < 3; i++) {
        ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                              USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
                              dtype << 8, 0, data, size, USB_CTRL_GET_TIMEOUT);
        if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
            return ret;
    }

    return -EINVAL;
}

static int hub_hub_status(struct usb_hub *hub,
                          u16 *status, u16 *change) {
    int ret;

    ret = get_hub_status(hub->hdev, &hub->status->hub);
    if (ret < 0) {
        if (ret != -ENODEV) {
            USB_LOGE("%s failed (err = %d)\n", __FUNCTION__, ret);
        }
    } else {
        *status = (hub->status->hub.wHubStatus);
        *change = (hub->status->hub.wHubChange);
        ret = 0;
    }

    return ret;
}

static int clear_hub_feature(struct usb_device *hdev, int feature) {
    return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                           USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, 1000);
}

int usb_clear_port_feature(struct usb_device *hdev,
                           int port1, int feature) {
    return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                           USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
                           NULL, 0, 1000);
}

static int set_port_feature(struct usb_device *hdev,
                            int port1, int feature) {
    return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                           USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
                           NULL, 0, 1000);
}

static void hub_power_on(struct usb_hub *hub, boolean do_delay) {
    int port1;
    for (port1 = 1; port1 <= hub->hdev->maxchild; port1++) {
        if (test_bit(port1, hub->power_bits)) {
            set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
        } else {
            usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
        }
    }

    if (do_delay)
        USB_MSleep(hub_power_on_good_delay(hub));
}

#define HUB_DEBOUNCE_TIMEOUT    2000
#define HUB_DEBOUNCE_STEP     50
#define HUB_DEBOUNCE_STABLE  1000

enum hub_activation_type {
    HUB_INIT, HUB_INIT2, HUB_INIT3,     /* INITs must come first */
    HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type);

static int get_port_status(struct usb_device *hdev, int port1,
                           struct usb_port_status *data) {
    int i, status = -ETIMEDOUT;

    for (i = 0; i < USB_STS_RETRIES &&
            (status == -ETIMEDOUT || status == -EPIPE); i++) {
        status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                                 USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
                                 data, sizeof(*data), USB_STS_TIMEOUT);
    }
    return status;
}

static int hub_port_status(struct usb_hub *hub, int port1,
                           u16 *status, u16 *change) {
    int ret;

    ret = get_port_status(hub->hdev, port1, &hub->status->port);
    if (ret < 4) {
        if (ret != -ENODEV) {
            USB_LOGE("%s, failed (err = %d) \n", __FUNCTION__, ret);
        }
        if (ret > 0) {
            ret = -EIO;
        }
    } else {
        *status = (hub->status->port.wPortStatus);
        *change = (hub->status->port.wPortChange);

        ret = 0;
    }
    return ret;
}

/*
 * Check if device connected.
 * Returns 1 for exist, 0 for not exist
 */
int usb_device_exist(struct usb_device *udev) {
    struct usb_device *hdev = udev->parent;
    if (is_root_hub(hdev)) {
        struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
        return hcd->driver->hub_connect_state();
    } else {
        struct usb_hub  *hub = usb_hub_to_struct_hub(hdev);
        if(hub != NULL) //modify for tscancode
        {
            u16 status, change;
            hub_port_status(hub, udev->portnum, &status, &change);

            return ((status & USB_PORT_STAT_CONNECTION) ? 1 : 0);
        }
        else
        {
            return 0;
        }
    }
}

enum hub_quiescing_type {
    HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
};

#define PORT_RESET_TRIES        5
#define SET_ADDRESS_TRIES       2
#define GET_DESCRIPTOR_TRIES    2
#define SET_CONFIG_TRIES        4

#define HUB_ROOT_RESET_TIME     50  /* times are in msec */
#define HUB_SHORT_RESET_TIME    10
#define HUB_BH_RESET_TIME       50
#define HUB_LONG_RESET_TIME     200
#define HUB_RESET_TIMEOUT       800

static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) {
    struct usb_device *hdev = hub->hdev;
    int i;

    hub->quiescing = 1;

    if (type != HUB_SUSPEND) {
        for (i = 0; i < hdev->maxchild; i++) {
            if (hub->ports[i]->child) {
                usb_disconnect(&hub->ports[i]->child);
            }
        }
    }
}

static void port_event(struct usb_hub *hub, int port1);
void hub_port_check(struct usb_hub *hub) {
    struct usb_device *hdev;
    u16 hubstatus, hubchange;
    int i;

    if (g_pstUsbPortCheckSeamphone == NULL)
    {
        g_pstUsbPortCheckSeamphone = Hv_Vos_InitSemaphore(1, 1);
    }
    Hv_Vos_TimedAcquireSemaphore(g_pstUsbPortCheckSeamphone, 5000);

    hdev = hub->hdev;

    if (unlikely(hub->disconnected)) {
        goto failed;
    }

    if (hdev->state == USB_STATE_NOTATTACHED) {
        hub->error = -ENODEV;
        hub_quiesce(hub, HUB_DISCONNECT);
        goto failed;
    }

    if (hub->quiescing) {
        goto failed;
    }

    for (i = 1; i <= hdev->maxchild; i++) {
        USB_LOGV("port_event for %d port of %d ports", i, hdev->maxchild);
        port_event(hub, i); /* check any port */
    }

    if (test_and_clear_bit(0, hub->event_bits) == 0) {
        /* do nothing */
    } else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) {
        USB_LOGE("%s, get status failed.\n", __FUNCTION__);
    } else {
        if (hubchange & HUB_CHANGE_LOCAL_POWER) {
            clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
            if (hubstatus & HUB_STATUS_LOCAL_POWER) {
                hub->limited_power = 1;
            } else {
                hub->limited_power = 0;
            }
        }

        if (hubchange & HUB_CHANGE_OVERCURRENT) {
            u16 status = 0;
            u16 unused;

            clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
            USB_MSleep(500);    /* Cool down */
            hub_power_on(hub, true);
            hub_hub_status(hub, &status, &unused);
            if (status & HUB_STATUS_OVERCURRENT) {
                USB_LOGE("%s, over-current condition.\n");
            }
        }
    }

    Hv_Vos_ReleaseSemaphoreFromISR(g_pstUsbPortCheckSeamphone);

failed:
    return;
}

static void hub_timer_proc(unsigned long data) {
    struct usb_hub  *hub = (struct usb_hub *) data;
    hub_port_check(hub);
}


static void hub_activate(struct usb_hub *hub,
                         enum hub_activation_type type) {
    struct usb_device   *hdev = hub->hdev;
    int ret;
    int port1 = 0;
    int status = 0;
    unsigned delay = 0;
    boolean need_debounce_delay = false;

    if (type == HUB_INIT2) {
        goto init2;
    }
    if (type == HUB_INIT3) {
        goto init3;
    }

    if (type != HUB_RESUME) {
        if (hdev->parent && hub_is_superspeed(hdev)) {
            ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                                  HUB_SET_DEPTH, USB_RT_HUB,
                                  hdev->level - 1, 0, NULL, 0,
                                  USB_CTRL_SET_TIMEOUT);
            if (ret < 0) {
                USB_LOGE("set hub depth failed\n");
            }
        }

        if (type == HUB_INIT) {
            delay = hub_power_on_good_delay(hub);

            hub_power_on(hub, false);
            USB_MSleep(delay);
            hub_activate(hub, HUB_INIT2);
            return;
        } else if (type == HUB_RESET_RESUME) {
            /* TODO? */
            hub_power_on(hub, true);
        } else {
            hub_power_on(hub, true);
        }
    }
init2:
    /*
     * Check each port and set hub->change_bits to let hub_wq know
     * which ports need attention.
     */
    for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
        struct usb_port *port_dev = hub->ports[port1 - 1];
        struct usb_device *udev = port_dev->child;
        u16 portstatus = 0, portchange = 0;

        status = hub_port_status(hub, port1, &portstatus, &portchange);
        /* FIXME: do not forget to delete me. */
        USB_ASSERT(status == 0);

        if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) {
            USB_LOGV("status 0x%04x, change 0x%04x", portstatus, portchange);
        }

        if ((portstatus == USB_PORT_STAT_ENABLE) && (
                    type != HUB_RESUME ||
                    !(portstatus & USB_PORT_STAT_CONNECTION) ||
                    !udev ||
                    udev->state == USB_STATE_NOTATTACHED)) {
            portstatus &= ~USB_PORT_STAT_ENABLE;
            if (!hub_is_superspeed(hdev)) {
                usb_clear_port_feature(hdev, port1,
                                       USB_PORT_FEAT_ENABLE);
            }
        }

        /* Clear status-change flags; we'll debounce later */
        if (portchange & USB_PORT_STAT_C_CONNECTION) {
            need_debounce_delay = true;
            usb_clear_port_feature(hdev, port1,
                                   USB_PORT_FEAT_C_CONNECTION);
        }
        if (portchange & USB_PORT_STAT_C_ENABLE) {
            need_debounce_delay = true;
            usb_clear_port_feature(hdev, port1,
                                   USB_PORT_FEAT_C_ENABLE);
        }
        if (portchange & USB_PORT_STAT_C_RESET) {
            need_debounce_delay = true;
            usb_clear_port_feature(hdev, port1,
                                   USB_PORT_FEAT_C_RESET);
        }
        if (portchange & USB_PORT_STAT_C_BH_RESET) {
            need_debounce_delay = true;
            usb_clear_port_feature(hdev, port1,
                                   USB_PORT_FEAT_C_BH_PORT_RESET);
        }

        /* We can forget about a "removed" device when there's a
         * physical disconnect or the connect status changes.
         */
        if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
                (portchange & USB_PORT_STAT_C_CONNECTION)) {
            clear_bit(port1, hub->removed_bits);
        }

        if (!udev || udev->state == USB_STATE_NOTATTACHED) {
            if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
                    (portstatus & USB_PORT_STAT_OVERCURRENT)) {
                set_bit(port1, hub->change_bits);
            }
        } else if (portstatus & USB_PORT_STAT_ENABLE) {
            boolean port_resumed = (portstatus & USB_PORT_STAT_LINK_STATE)
                                   == USB_SS_PORT_LS_U0;

            if (portchange || (hub_is_superspeed(hdev) && port_resumed)) {
                set_bit(port1, hub->change_bits);
            }
        } else if (udev->persist_enabled) {
            if (test_bit(port1, hub->power_bits)) {
                set_bit(port1, hub->change_bits);
            }
        } else {
            usb_set_device_state(udev, USB_STATE_NOTATTACHED);
            set_bit(port1, hub->change_bits);
        }
    }

    if (need_debounce_delay) {
        delay = HUB_DEBOUNCE_STABLE;

        if (type == HUB_INIT2) {
            USB_MSleep(delay);
            hub_activate(hub, HUB_INIT3);
            return;
        } else {
            USB_MSleep(delay);
        }
    }
init3:
    hub->quiescing = 0;
    if (g_test_mode) {
        set_port_feature(hub->hdev, 0x401, USB_PORT_FEAT_TEST);
    } else {
    }

    return;
}

static int hub_configure(struct usb_hub *hub,
                         struct usb_endpoint_descriptor *endpoint) {
    struct usb_hcd *hcd;
    struct usb_device *hdev = hub->hdev;
    u16 hubstatus, hubchange;
    u16 wHubCharacteristics;
    char *message = "out of memory";
    unsigned int pipe;
    int maxp, ret, i;
    unsigned int maxchild;

    hub->status = USB_MALLOC(sizeof (*(hub->status)));
    if (!hub->status) {
        ret = -ENOMEM;
        goto failed;
    }
    HV_MEMSET((void*)hub->status, 0, sizeof (*(hub->status)));

    hub->descriptor = USB_MALLOC(sizeof (*(hub->descriptor)));
    if (!hub->descriptor) {
        ret = -ENOMEM;
        goto failed;
    }
    HV_MEMSET((void*)hub->descriptor, 0, sizeof (*(hub->descriptor)));

    ret = get_hub_descriptor(hdev, hub->descriptor);
    if (ret < 0) {
        message = "can't read hub descriptor.";
        ret = -ENODEV;
        goto failed;
    } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
        message = "hub has too many ports.";
        ret = -ENODEV;
        goto failed;
    } else if (hub->descriptor->bNbrPorts == 0) {
        message = "hub doesn't have any ports.";
        ret = -ENODEV;
        goto failed;
    }

    maxchild = hub->descriptor->bNbrPorts;

    hub->ports = USB_MALLOC(maxchild * sizeof (struct usb_port *));
    if (!hub->ports) {
        ret = -ENOMEM;
        goto failed;
    }

    HV_MEMSET((void*)hub->ports, 0, maxchild * sizeof (struct usb_port *));
    wHubCharacteristics = (hub->descriptor->wHubCharacteristics);

    switch (wHubCharacteristics & HUB_CHAR_LPSM) {
    case HUB_CHAR_COMMON_LPSM:
        break;
    case HUB_CHAR_INDV_PORT_LPSM:
        break;
    case HUB_CHAR_NO_LPSM:
    case HUB_CHAR_LPSM:
        break;
    }

    switch (wHubCharacteristics & HUB_CHAR_OCPM) {
    case HUB_CHAR_COMMON_OCPM:
        break;
    case HUB_CHAR_INDV_PORT_OCPM:
        break;
    case HUB_CHAR_NO_OCPM:
    case HUB_CHAR_OCPM:
        break;
    }

    /* USB_HUB_PR_HS_NO_TT (0) */
    switch (hdev->descriptor.bDeviceProtocol) {
    case USB_HUB_PR_FS: /* USB_HUB_PR_HS_NO_TT */
        break;
    default:
        break;
    }

    if (wHubCharacteristics & HUB_CHAR_PORTIND) {
        hub->has_indicators = 1;
    }

    ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
    if (ret) {
        message = "can't get hub status.";
        goto failed;
    }

    hcd = bus_to_hcd(hdev->bus);
    //hcd = hcd;

    ret = hub_hub_status(hub, &hubstatus, &hubchange);
    if (ret < 0) {
        message = "can't get hub status";
        goto failed;
    }

    pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
    maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
    USB_ASSERT(maxp <= 8);
    maxp = USB_MIN(maxp, 8);

    for (i = 0; i < (int)maxchild; i++) {
        ret = usb_hub_create_port_device(hub, i + 1);
        if (ret < 0) {
            break;
        }
    }

    hdev->maxchild = i;
    if (ret < 0) {
        goto failed;
    }

    usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
    hub_activate(hub, HUB_INIT);

    return 0;

failed:
    USB_LOGE("config failed, %s (err %d)\n", message, ret);
    return ret;
}

static boolean hub_port_warm_reset_required(struct usb_hub *hub,
        int port1, u16 portstatus) {
    u16 link_state;

    if (!hub_is_superspeed(hub->hdev)) {
        return false;
    }

    if (test_bit(port1, hub->warm_reset_bits)) {
        return true;
    }

    link_state = portstatus & USB_PORT_STAT_LINK_STATE;

    return link_state == USB_SS_PORT_LS_SS_INACTIVE
           || link_state == USB_SS_PORT_LS_COMP_MOD;
}

/*
 * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
 * @dev: newly addressed device (in ADDRESS state)
 *
 * This is only called by <usb_new_device()>
 *
 * Return: 0 if successful. A negative error number otherwise.
 */
static int usb_enumerate_device(struct usb_device *udev) {
    int err;

    if (udev->config == NULL) {
        err = usb_get_configuration(udev);
        if (err < 0) {
            return err;
        } else {
            udev->is_connect = 1;
        }
    }

    udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
    if (udev->product != NULL)
    {
        USB_LOGI("Enumerate dev %d, Product<%s>", udev->devnum, udev->product);
    }

    udev->manufacturer = usb_cache_string(udev, udev->descriptor.iManufacturer);
    if (udev->manufacturer != NULL)
    {
        USB_LOGI("Enumerate dev %d, Manufacturer<%s>", udev->devnum, udev->manufacturer);
    }

    udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
    if (udev->serial != NULL)
    {
        USB_LOGI("Enumerate dev %d, SerialNumber<%s>", udev->devnum, udev->serial);
    }

    return 0;
}

static void set_usb_port_removable(struct usb_device *udev) {
    struct usb_device *hdev = udev->parent;
    struct usb_hub  *hub;
    u8  port = udev->portnum;
    u16 wHubCharacteristics;
    boolean removable = true;

    if (!hdev) {
        return;
    }
    hub = usb_hub_to_struct_hub(hdev);
    if(hub == NULL){  //modify for tscancode
        return;
    }

    wHubCharacteristics = (hub->descriptor->wHubCharacteristics);
    if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) {
        return;
    } else {
        /* as usual, we do not support compound device. */
        USB_ASSERT(0);
    }

    if (hub_is_superspeed(hdev)) {
        if ((hub->descriptor->u.ss.DeviceRemovable)
                & (1 << port)) {
            removable = false;
        }
    } else {
        if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
            removable = false;
    }

    if (removable) {
        udev->removable = USB_DEVICE_REMOVABLE;
    } else {
        udev->removable = USB_DEVICE_FIXED;
    }

    switch (hub->ports[udev->portnum - 1]->connect_type) {
    case USB_PORT_CONNECT_TYPE_HOT_PLUG:
        udev->removable = USB_DEVICE_REMOVABLE;
        break;
    case USB_PORT_CONNECT_TYPE_HARD_WIRED:
        udev->removable = USB_DEVICE_FIXED;
        break;
    default: /* use what was set above */
        break;
    }
}

int usb_new_device(struct usb_device *udev) {
    int err;

    err = usb_enumerate_device(udev);
    if (err < 0) {
        goto failed;
    }

    /* not root hub set removable */
    if (udev->parent) {
        set_usb_port_removable(udev);
    }
    err = device_add(&udev->dev);
    if (err) {
        goto failed;
    }

    return 0;

failed:
    usb_set_device_state(udev, USB_STATE_NOTATTACHED);
    return err;
}

int usb_remove_device(struct usb_device *udev) {
    usb_set_device_state(udev, USB_STATE_NOTATTACHED);
    device_del(&udev->dev);
    return 0;
}

static void usb_ep0_reinit(struct usb_device *udev) {
    usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true);
    usb_disable_endpoint(udev, 0 + USB_DIR_IN, true);
    usb_enable_endpoint(udev, &udev->ep0, true);
}

static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) {
    struct usb_port *port_dev = hub->ports[port1 - 1];
    struct usb_device *hdev = hub->hdev;
    int ret = 0;

    if (port_dev->child && set_state)
        usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
    if (!hub->error) {
            ret = usb_clear_port_feature(hdev, port1,
                                         USB_PORT_FEAT_ENABLE);
    }
    if (ret && ret != -ENODEV)
        USB_LOGE("cannot disable (err = %d)\n", ret);
    return ret;
}

static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) {
    hub_port_disable(hub, port1, 0);

    set_bit(port1, hub->change_bits);
}

static int hub_port_init(struct usb_hub *hub, struct usb_device *udev,
                         int port1, int retry_counter);

static int usb_reset_and_verify_device(struct usb_device *udev) {
    struct usb_device   *parent_hdev = udev->parent;
    struct usb_hub      *parent_hub;
    int     i, ret = 0;
    int wait;
    int     port1 = udev->portnum;

    if (udev->state == USB_STATE_NOTATTACHED ||
            udev->state == USB_STATE_SUSPENDED) {
        return -EINVAL;
    }

    if (!parent_hdev)
        return -EISDIR;

    parent_hub = usb_hub_to_struct_hub(parent_hdev);

    for (i = 0; i < SET_CONFIG_TRIES; ++i) {
        if (udev->bus->hc_needreset &&
                is_root_hub(parent_hdev)) {
            usb_repower_and_reset_hcd(udev);
            USB_MSleep(200);
        }

        wait = 3;
RESCAN:
     
        if (!usb_device_exist(udev)) {
            if (--wait) {
                USB_MSleep(300 * (wait + 1));
                goto RESCAN;
            }
            ret = -ENODEV;
            break;
        }

        usb_ep0_reinit(udev);
        ret = hub_port_init(parent_hub, udev, port1, i);
        if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV) {
            break;
        }
        udev->bus->hc_needreset = 1;
    }
    udev->bus->hc_needreset = 0;

    if (ret < 0)
        goto re_enumerate;

    if (!udev->actconfig)
        goto done;

    ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                          USB_REQ_SET_CONFIGURATION, 0,
                          udev->actconfig->desc.bConfigurationValue, 0,
                          NULL, 0, USB_CTRL_SET_TIMEOUT);
    if (ret < 0) {
        goto re_enumerate;
    }

    usb_set_device_state(udev, USB_STATE_CONFIGURED);
    for (i = 0; i < udev->actconfig->desc.bNumInterfaces; ++i) {
        struct usb_host_config *config = udev->actconfig;
        struct usb_interface *intf = config->interface[i];
        struct usb_interface_descriptor *desc;

        desc = &intf->cur_altsetting->desc;
        if (desc->bAlternateSetting == 0) {
            usb_disable_interface(udev, intf, true);
            usb_enable_interface(udev, intf, true);
            ret = 0;
        } else {
            intf->resetting_device = 1;
            ret = usb_set_interface(udev, desc->bInterfaceNumber,
                                    desc->bAlternateSetting);
            intf->resetting_device = 0;
        }
        if (ret < 0) {
            goto re_enumerate;
        }
    }

done:
    return 0;

re_enumerate:
    hub_port_logical_disconnect(parent_hub, port1);
    return ret;
}

int usb_reset_device(struct usb_device *udev) {
    int i, ret;
    struct usb_host_config *config = udev->actconfig;

    if (udev->state == USB_STATE_NOTATTACHED ||
            udev->state == USB_STATE_SUSPENDED) {
        return -EINVAL;
    }

    if (!udev->parent) {
        return -EISDIR;
    }

    if (config) {
        for (i = 0; i < config->desc.bNumInterfaces; ++i) {
            struct usb_interface *cintf = config->interface[i];
            struct usb_driver *drv;
            int unbind = 0;

            if (cintf->dev.driver) {
                drv = to_usb_driver(cintf->dev.driver);
                if (drv->pre_reset && drv->post_reset) {
                    unbind = (drv->pre_reset) (cintf);
                } else if (cintf->condition == USB_INTERFACE_BOUND) {
                    unbind = 1;
                }
                if (unbind) {
                    usb_forced_unbind_intf(cintf);
                }
            }
        }
    }

    ret = usb_reset_and_verify_device(udev);

    if (config) {
        for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
            struct usb_interface *cintf = config->interface[i];
            struct usb_driver *drv;
            int rebind = cintf->needs_binding;

            if (!rebind && cintf->dev.driver) {
                drv = to_usb_driver(cintf->dev.driver);
                if (drv->post_reset)
                    rebind = (drv->post_reset)(cintf);
                else if (cintf->condition ==
                         USB_INTERFACE_BOUND)
                    rebind = 1;
                if (rebind)
                    cintf->needs_binding = 1;
            }
        }

        usb_unbind_and_rebind_marked_interfaces(udev);
    }

    return ret;
}

static void release_devnum(struct usb_device *udev) {
    if (udev->devnum > 0) {
        HANDLE sc = spin_lock_irq();
        clear_bit(udev->devnum, udev->bus->devmap.devicemap);
        if (udev->devnum < udev->bus->devnum_next) {
            udev->bus->devnum_next = udev->devnum;
        }
        udev->devnum = -1;
        spin_unlock_irq(sc);
    }
}

static void update_devnum(struct usb_device *udev, int devnum) {
    udev->devnum = devnum;
}

static void choose_devnum(struct usb_device *udev) {
    int     devnum;
    HANDLE sc;
    struct usb_bus *bus = udev->bus;

    devnum = find_next_zero_bit(bus->devmap.devicemap, 128, bus->devnum_next);
    if (devnum >= 128) {
        devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
    }

    sc = spin_lock_irq();
    bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
    if (devnum < 128) {
        set_bit(devnum, bus->devmap.devicemap);
        udev->devnum = devnum;
    }
    spin_unlock_irq(sc);
}

static void hub_disconnect_children(struct usb_device *udev) {
    struct usb_hub *hub = usb_hub_to_struct_hub(udev);
    int i;

    if(hub != NULL){ //modify for tscancode
        for (i = 0; i < udev->maxchild; i++) {
            if (hub->ports[i]->child) {
                usb_disconnect(&hub->ports[i]->child);
            }
        }
    }
}

void usb_disconnect(struct usb_device **pdev) {
    struct usb_device *udev = *pdev;
    HANDLE sc;
    usb_set_device_state(udev, USB_STATE_NOTATTACHED);

    /* FIXME lock device here */
    hub_disconnect_children(udev);
    usb_disable_device(udev, 0);
    /* unlock device */

    device_del(&udev->dev);
    release_devnum(udev);

    sc = spin_lock_irq();
    *pdev = NULL;
    spin_unlock_irq(sc);

    put_device(&udev->dev);
}

static void hub_port_finish_reset(struct usb_hub *hub, int port1,
                                  struct usb_device *udev, int *status) {
    switch (*status) {
    case 0:
        /* TRSTRCY = 10 ms; plus some extra */
        USB_MSleep(10 + 40);
        if (udev) {
            struct usb_hcd *hcd = bus_to_hcd(udev->bus);

            update_devnum(udev, 0);
            /* The xHC may think the device is already reset,
             * so ignore the status.
             */
            if (hcd->driver->reset_device)
                hcd->driver->reset_device(hcd, udev);
        }
    /* FALL THROUGH */
    case -ENOTCONN:
    case -ENODEV:
        usb_clear_port_feature(hub->hdev,
                               port1, USB_PORT_FEAT_C_RESET);
        if (hub_is_superspeed(hub->hdev)) {
            usb_clear_port_feature(hub->hdev, port1,
                                   USB_PORT_FEAT_C_BH_PORT_RESET);
            usb_clear_port_feature(hub->hdev, port1,
                                   USB_PORT_FEAT_C_PORT_LINK_STATE);
            usb_clear_port_feature(hub->hdev, port1,
                                   USB_PORT_FEAT_C_CONNECTION);
        }
        if (udev)
            usb_set_device_state(udev, *status
                                 ? USB_STATE_NOTATTACHED
                                 : USB_STATE_DEFAULT);
        break;
    }
}

static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                               struct usb_device *udev, unsigned int delay, boolean warm) {
    int delay_time, ret;
    u16 portstatus;
    u16 portchange;
    unsigned int devctl;

    for (delay_time = 0;
            delay_time < HUB_RESET_TIMEOUT;
            delay_time += delay) {
        /* wait to give the device a chance to reset */
        USB_MSleep(delay);

        /* read and decode port status */
        ret = hub_port_status(hub, port1, &portstatus, &portchange);
        if (ret < 0)
            return ret;

        /* The port state is unknown until the reset completes. */
        if (!(portstatus & USB_PORT_STAT_RESET))
            break;

        /* switch to the long delay after two short delay failures */
        if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
            delay = HUB_LONG_RESET_TIME;

    }

    if ((portstatus & USB_PORT_STAT_RESET))
        return -EBUSY;

    if (hub_port_warm_reset_required(hub, port1, portstatus)) {
        return -ENOTCONN;
    }

    /* Device went away? */
    if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
        return -ENOTCONN;
    }

    /* bomb out completely if the connection bounced.  A USB 3.0
     * connection may bounce if multiple warm resets were issued,
     * but the device may have successfully re-connected. Ignore it.
     */
    if (!hub_is_superspeed(hub->hdev) &&
            (portchange & USB_PORT_STAT_C_CONNECTION)) {
        return -ENOTCONN;
    }

    if (!(portstatus & USB_PORT_STAT_ENABLE))
        return -EBUSY;

    if (!udev)
        return 0;

    if (hub_is_superspeed(hub->hdev))
        udev->speed = USB_SPEED_SUPER;
    else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
        udev->speed = USB_SPEED_HIGH;
    else if (portstatus & USB_PORT_STAT_LOW_SPEED)
        udev->speed = USB_SPEED_LOW;
    else
        udev->speed = USB_SPEED_FULL;
    return 0;
}

static int hub_port_reset(struct usb_hub *hub, int port1,
                          struct usb_device *udev, unsigned int delay, boolean warm) {
    int i, status;
    u16 portchange, portstatus;

    if (!hub_is_superspeed(hub->hdev)) {
        if (warm) {
            return -EINVAL;
        }
    } else if (!warm) {
        status = hub_port_status(hub, port1, &portstatus, &portchange);
        if (status < 0) {
            goto done;
        }

        if (hub_port_warm_reset_required(hub, port1, portstatus)) {
            /* !!SHOULD NEVER HAPPEN!! */
            warm = true;
        }
    }

    clear_bit(port1, hub->warm_reset_bits);
    for (i = 0; i < PORT_RESET_TRIES; i++) {
        status = set_port_feature(hub->hdev, port1, (warm ?
                                  USB_PORT_FEAT_BH_PORT_RESET:
                                  USB_PORT_FEAT_RESET));
        if (status == -ENODEV) {
            ;   /* the hub is gone */
        } else if (status) {
            USB_LOGE("can not reset port (err = %d).\n", status);
        } else {
            status = hub_port_wait_reset(hub, port1, udev, delay, warm);
            if (status && status != -ENOTCONN && status != -ENODEV) {
                USB_LOGE("port wait reset faild.(err = %d)\n", status);
            }
        }

        if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
            hub_port_finish_reset(hub, port1, udev, &status);
            if (!hub_is_superspeed(hub->hdev)) {
                goto done;
            }

            /* for usb3 hub */
        }
    }
    USB_LOGE("not enable port reset.");

done:
    return status;
}

static int hub_set_address(struct usb_device *udev, int devnum) {
    int retval;

    if (udev->state == USB_STATE_ADDRESS) {
        return 0;
    }
    if (udev->state != USB_STATE_DEFAULT) {
        return -EINVAL;
    }
    retval = usb_control_msg(udev, usb_sndaddr0pipe(),
                             USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL,
                             0, USB_CTRL_SET_TIMEOUT);
    if (retval == 0) {
        update_devnum(udev, devnum);
        usb_set_device_state(udev, USB_STATE_ADDRESS);
        usb_ep0_reinit(udev);
    }
    return retval;
}

static int hub_port_init(struct usb_hub *hub, struct usb_device *udev,
                         int port1, int retry_counter) {
    struct usb_device   *hdev = hub->hdev;
    int i, j, retval;
#define GET_DESCRIPTOR_BUFSIZE  64
    u8  tempbuf[GET_DESCRIPTOR_BUFSIZE];
    unsigned int    delay = HUB_SHORT_RESET_TIME;
    enum usb_device_speed   oldspeed = udev->speed;
    const char *speed;
    int devnum = udev->devnum;

    /* root hub port */
    if (!hdev->parent) {
        delay = HUB_ROOT_RESET_TIME;
    }

    if (oldspeed == USB_SPEED_LOW) {
        delay = HUB_LONG_RESET_TIME;
    }

    retval = hub_port_reset(hub, port1, udev, delay, false);
    if (retval < 0) {
        goto failed;
    }
    /* speed is known */    

    retval = -ENODEV;
    if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
        /* never happen */
        USB_ASSERT(0);
        goto failed;
    }
    
    oldspeed = udev->speed;

    switch (udev->speed) {
    case USB_SPEED_SUPER:
    case USB_SPEED_WIRELESS:
        udev->ep0.desc.wMaxPacketSize = 512;
        break;
    case USB_SPEED_HIGH:
        udev->ep0.desc.wMaxPacketSize = 64;
        break;
    case USB_SPEED_FULL:
        udev->ep0.desc.wMaxPacketSize = 64;
        break;
    case USB_SPEED_LOW:
        udev->ep0.desc.wMaxPacketSize = 8;
        break;
    default:
        goto failed;
    }
    speed = usb_speed_string(udev->speed);

    for (i = 0; i < GET_DESCRIPTOR_TRIES; (i++, USB_MSleep(100))) {
        struct usb_device_descriptor *buf;
        int r = 0;

        buf = (void *)tempbuf;
        HV_MEMSET(buf, 0, GET_DESCRIPTOR_BUFSIZE);

        for (j = 0; j < 3; j++) {
            buf->bMaxPacketSize0 = 0;
            r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                                USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                                USB_DT_DEVICE << 8, 0, buf,
                                GET_DESCRIPTOR_BUFSIZE, USB_CTRL_GET_TIMEOUT);
            switch (buf->bMaxPacketSize0) {
            case 8:
            case 16:
            case 32:
            case 64:
            case 255:
                if (buf->bDescriptorType == USB_DT_DEVICE) {
                    r = 0;
                    break;
                }
            default:
                if (r == 0)
                    r = -EPROTO;
                break;
            }
            if (r == 0) {
                break;
            }
        }

        udev->descriptor.bMaxPacketSize0 = buf->bMaxPacketSize0;
        retval = hub_port_reset(hub, port1, udev, delay, false);
        if (retval < 0) {
            goto failed;
        } else if (oldspeed != udev->speed) {
            retval = -ENODEV;
            goto failed;
        }
        if (r) {
            retval = -EMSGSIZE;
            continue;
        }

        for (j = 0; j < SET_ADDRESS_TRIES; j++) {
            retval = hub_set_address(udev, devnum);
            if (retval >= 0)
                break;
            USB_MSleep(200);
        }
        if (retval < 0) {
            goto failed;
        }
        if (udev->speed == USB_SPEED_SUPER) {
            devnum = udev->devnum;
        }
        USB_MSleep(10);
        break;
    }
#undef GET_DESCRIPTOR_BUFSIZE
    if (retval < 0) {
        goto failed;
    }

    if (udev->speed == USB_SPEED_SUPER &&
            (udev->descriptor.bcdUSB < 0x0300)) {
        hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
        retval = -EINVAL;
        goto failed;
    }

    if (udev->descriptor.bMaxPacketSize0 == 0xff ||
            udev->speed == USB_SPEED_SUPER) {
        i = 512;
    } else {
        i = udev->descriptor.bMaxPacketSize0;
    }

    if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
        if (udev->speed == USB_SPEED_LOW ||
                !(i == 8 || i == 16 || i == 32 || i == 64)) {
            retval = -EMSGSIZE;
            goto failed;
        }
        if (udev->speed == USB_SPEED_FULL)
            USB_LOGE("ep0 maxpacket = %d\n", i);
        else
            USB_LOGV("Using ep0 maxpacket: %d\n", i);
        udev->ep0.desc.wMaxPacketSize = i;
        usb_ep0_reinit(udev);
    }


    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
    if (retval < (signed) sizeof(udev->descriptor)) {
        if (retval >= 0) {
            retval = -ENOMSG;
        }
        goto failed;
    } else {
        USB_LOGI("Dev %d, Vendor ID:0x%X, Product ID:0x%X, DeviceClass:0x%X", udev->devnum,
                udev->descriptor.idVendor, udev->descriptor.idProduct, udev->descriptor.bDeviceClass);
    }

    if ((udev->descriptor.bcdUSB >= 0x201)) {
        /* wireless device? */
        USB_LOGV("%s, really a wireless device? bcdUSB is 0x%04x.\n",
                __FUNCTION__, udev->descriptor.bcdUSB);
    }

    retval = 0;
failed:
    if (retval) {
        hub_port_disable(hub, port1, 0);
        update_devnum(udev, devnum);
    }
    return retval;
}

static int port_is_power_on(struct usb_hub *hub, u16 portstatus) {
    int ret = 0;

    if (hub_is_superspeed(hub->hdev)) {
        ret = (portstatus & USB_SS_PORT_STAT_POWER) ? 1 : 0;
    } else {
        ret = (portstatus & USB_PORT_STAT_POWER) ? 1 : 0;
    }

    return ret;
}

static void
check_highspeed(struct usb_hub *hub, struct usb_device *udev, int port1) {
    ;   /* TODO */
}

static int usb_connect(struct usb_hub *hub, int port1) {
    int status = 0, i;
    struct usb_device *hdev = hub->hdev;
    struct usb_port *port_dev = hub->ports[port1 - 1];
    struct usb_device *udev = port_dev->child;
    HANDLE sc;
    
    USB_LOGV("usb_connect, port:%d", port1);
    for (i = 0; i < SET_CONFIG_TRIES; i++) {
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        if (!udev) {
            return -ENOMEM;
        }

        usb_set_device_state(udev, USB_STATE_POWERED);
        udev->level = hdev->level + 1;
        //is there forgot full & high??
        if (hub_is_superspeed(hub->hdev)) {
            udev->speed = USB_SPEED_SUPER;
        } else {
            udev->speed = USB_SPEED_UNKNOWN;
        }

        choose_devnum(udev);
        if (udev->devnum <= 0) {
            status = -ENOTCONN;     /* do not retry */
            goto loop;
        }

        status = hub_port_init(hub, udev, port1, i);
        if (status < 0) {
            goto loop;
        }

        if (udev->quirks & USB_QUIRK_DELAY_INIT) {
            USB_MSleep(1000);
        }

        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
            /* TODO port power check */
        }

        if ((udev->descriptor.bcdUSB) >= 0x0200
                && udev->speed == USB_SPEED_FULL) {
            check_highspeed(hub, udev, port1);
        }

        status = 0;

        sc = spin_lock_irq();
        if (hdev->state == USB_STATE_NOTATTACHED) {
            status = -ENOTCONN;
        } else {
            port_dev->child = udev;
        }
        spin_unlock_irq(sc);
        if (!status) {
            status = usb_new_device(udev);
            if (status) {
                sc = spin_lock_irq();
                port_dev->child = NULL;
                spin_unlock_irq(sc);
            }
        }

        if (status) {
            goto loop_disable;
        }

        return 0;
loop_disable:
        hub_port_disable(hub, port1, 1);
loop:
        usb_ep0_reinit(udev);
        release_devnum(udev);
        if(udev != NULL)
        {
            USB_FREE(udev);
            udev = NULL;
        }
        if ((status == -ENOTCONN) || (status == -ENOTSUPP))
            break;
    }

    if (hub->hdev->parent) {
        if (status != -ENOTCONN && status != -ENODEV) {
            USB_LOGE("unable to enumerate USB device.");
        }
    }

    return status;
}

static void hub_port_connect(struct usb_hub *hub, int port1,
                             u16 portstatus, u16 portchange) {
    int status;
    struct usb_device *hdev = hub->hdev;
    struct usb_port *port_dev = hub->ports[port1 - 1];
    struct usb_device *udev = port_dev->child;

    if (udev) {
        usb_disconnect(&port_dev->child);
    }

    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            (portchange & USB_PORT_STAT_C_CONNECTION)) {
        clear_bit(port1, hub->removed_bits);
    }

    if (portchange & (USB_PORT_STAT_C_CONNECTION |
                      USB_PORT_STAT_C_ENABLE)) {
        status = hub_port_debounce_be_stable(hub, port1);
        if (status < 0) {
            portstatus &= ~USB_PORT_STAT_CONNECTION;
        } else {
            portstatus = status;
        }
    }

    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            test_bit(port1, hub->removed_bits)) {
        if (hub_is_port_power_switchable(hub)
                && !port_is_power_on(hub, portstatus)) {
            set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
        }

        if (portstatus & USB_PORT_FEAT_ENABLE)
            goto done;

        return;
    }

    if (!usb_connect(hub, port1))
        return;

done:

    hub_port_disable(hub, port1, 1);
}

static void hub_port_connect_change(struct usb_hub *hub, int port1,
                                    u16 portstatus, u16 portchange) {
    struct usb_port *port_dev = hub->ports[port1 - 1];
    struct usb_device *udev = port_dev->child;
    int status = -ENODEV;


    /* Try to resuscitate an existing device */
    if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
            udev->state != USB_STATE_NOTATTACHED) {
        if (portstatus & USB_PORT_STAT_ENABLE) {
            status = 0;     /* Nothing to do */
        } else {
            /* Don't resuscitate */;
        }
    }
    clear_bit(port1, hub->change_bits);

    /* successfully revalidated the connection */
    if (status == 0)
        return;

    hub_port_connect(hub, port1, portstatus, portchange);
}

static void port_event(struct usb_hub *hub, int port1) {
    int connect_change;
    struct usb_port *port_dev = hub->ports[port1 - 1];
    struct usb_device *udev = port_dev->child;
    struct usb_device *hdev = hub->hdev;
    u16 portstatus, portchange;

    connect_change = test_bit(port1, hub->change_bits);
    clear_bit(port1, hub->event_bits);

    if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
        goto finish;

    if (portchange & USB_PORT_STAT_C_CONNECTION) {
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
        connect_change = 1;
    }

    if (portchange & USB_PORT_STAT_C_ENABLE) {
        if (!connect_change) {
            USB_LOGV("%s, enable change, status 0x%08x.\n", __FUNCTION__, portstatus);
        }
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);

        if (!(portstatus & USB_PORT_STAT_ENABLE)
                && !connect_change && udev) {
            USB_LOGE("%s, disabled by hub, re-enabling...\n", __FUNCTION__);
            connect_change = 1;
        }
    }

    if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
        u16 status = 0;
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_OVER_CURRENT);

        USB_MSleep(100);    /* cool down */
        hub_power_on(hub, true);
        hub_port_status(hub, port1, &portstatus, &portchange);
        if (status & USB_PORT_STAT_OVERCURRENT) {
            USB_LOGE("%s, over-current condition.\n", __FUNCTION__);
        }
    }

    if (portchange & USB_PORT_STAT_C_RESET) {
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_RESET);
    }
    if ((portchange & USB_PORT_STAT_C_BH_RESET)
            && hub_is_superspeed(hdev)) {
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_BH_PORT_RESET);
    }
    if (portchange & USB_PORT_STAT_C_LINK_STATE) {
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_PORT_LINK_STATE);
    }
    if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
        usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
    }

    /* should never happen. for 3.0 hub only */
    if (hub_port_warm_reset_required(hub, port1, portstatus)) {
        if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
                || udev->state == USB_STATE_NOTATTACHED) {
            if (hub_port_reset(hub, port1, NULL,
                               HUB_BH_RESET_TIME, true) < 0) {
                (void) hub_port_disable(hub, port1, 1);
            }
        } else {
            usb_reset_device(udev);
            connect_change = 0;
        }
    }

finish:
    if (connect_change) {
        hub_port_connect_change(hub, port1, portstatus, portchange);
    }
}

int hub_port_debounce(struct usb_hub *hub, int port1, boolean must_be_connected) {
    int ret;
    u16 portchange, portstatus;
    u32 connection = 0xffff;
    int total_time, stable_time = 0;

    for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
        ret = hub_port_status(hub, port1, &portstatus, &portchange);
        if (ret < 0) {
            return ret;
        }

        if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
                (portstatus & USB_PORT_STAT_CONNECTION) == connection) {
            if (!must_be_connected ||
                    (connection == USB_PORT_STAT_CONNECTION))
                stable_time += HUB_DEBOUNCE_STEP;
            if (stable_time >= HUB_DEBOUNCE_STABLE)
                break;
        } else {
            stable_time = 0;
            connection = portstatus & USB_PORT_STAT_CONNECTION;
        }

        if (portchange & USB_PORT_STAT_C_CONNECTION) {
            usb_clear_port_feature(hub->hdev, port1,
                                   USB_PORT_FEAT_C_CONNECTION);
        }

        if (total_time >= HUB_DEBOUNCE_TIMEOUT) {
            break;
        }

        USB_MSleep(HUB_DEBOUNCE_STEP);
    }

    if (stable_time < HUB_DEBOUNCE_STABLE) {
        return -ETIMEDOUT;
    }

    return portstatus;
}

static void hub_disconnect(struct usb_interface *intf) {
    struct usb_hub *hub = (struct usb_hub *) usb_get_intfdata(intf);
    struct usb_device *hdev = interface_to_usbdev(intf);
    int port1;
    HANDLE sc;
    if (!hub) {
        return;
    }

    hub->disconnected = 1;
    hub->error = 0;
    hub_quiesce(hub, HUB_DISCONNECT);

    sc = spin_lock_irq();
    port1 = hdev->maxchild;
    hdev->maxchild = 0;
    usb_set_intfdata(intf, NULL);
    spin_unlock_irq(sc);

    for (; port1 > 0; --port1) {
        usb_hub_remove_port_device(hub, port1);
    }

    if(hub->ports != NULL)
    {
        USB_FREE(hub->ports);
        hub->ports = NULL;
    }
    if(hub->descriptor != NULL)
    {
        USB_FREE(hub->descriptor);
        hub->descriptor = NULL;
    }
    if(hub->status != NULL)
    {
        USB_FREE(hub->status);
        hub->status = NULL;
    }
    if(hub != NULL)
    {
        USB_FREE(hub);
        hub = NULL;
    }
}

static int hub_probe(struct usb_interface *interface, const struct usb_device_id *id) {
    struct usb_host_interface *intf;
    struct usb_endpoint_descriptor *epdesc;
    struct usb_device   *hdev;
    struct usb_hub      *hub;
    intf = interface->cur_altsetting;
    hdev = interface_to_usbdev(interface);

    if (hdev->level >= MAX_TOPO_LEVEL) {
        return -E2BIG;
    }

    if (intf->desc.bInterfaceSubClass != 0 &&
            intf->desc.bInterfaceSubClass != 1) {   /* some idiot hub */
descriptor_error:
        return -EIO;
    }

    /* Do not include control endpoint */
    if (intf->desc.bNumEndpoints != 1) {
        goto descriptor_error;
    }

    epdesc = &intf->endpoint[0].desc;
    if (!usb_endpoint_is_int_in(epdesc)) {
        goto descriptor_error;
    }


    hub = (struct usb_hub *)USB_MALLOC(sizeof(*hub));
    if (!hub) {
        return -ENOMEM;
    }
    HV_MEMSET((void*)hub, 0, sizeof(*hub));
    s_pHub = hub;

    hub->intfdev = &interface->dev;
    hub->hdev = hdev;

    usb_set_intfdata(interface, hub);

    if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND) {
        hub->quirk_check_port_auto_suspend = 1;
    }

    if (hub_configure(hub, epdesc) >= 0) {
        //USB_LOGI("hub level:%d", hdev->level);
        return 0;
    }

    hub_disconnect(interface);
    return -ENODEV;
}

static int hub_pre_reset(struct usb_interface *intf) {
    struct usb_hub *hub = usb_get_intfdata(intf);

    hub_quiesce(hub, HUB_POST_RESET);
    hub->in_reset = 1;

    /* pm code here */
    return 0;
}

static int hub_post_reset(struct usb_interface *intf) {
    struct usb_hub *hub = usb_get_intfdata(intf);

    hub->in_reset = 0;
    /* pm relative code */
    hub_activate(hub, HUB_POST_RESET);
    return 0;
}

static const struct usb_device_id hub_id_table[] = {
    {
        USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS,    //match_flags
        USB_VENDOR_GENESYS_LOGIC,    //idVendor
        0,    //idProduct
        0,    //bcdDevice_lo
        0,    //bcdDevice_hi
        0,    //bDeviceClass
        0,    //bDeviceSubClass
        0,    //bDeviceProtocol
        USB_CLASS_HUB, //bInterfaceClass
        0,    //bInterfaceSubClass
        0,    //bInterfaceProtocol
        0,    //bInterfaceNumber
        HUB_QUIRK_CHECK_PORT_AUTOSUSPEND, //driver_info
    },
    {
        USB_DEVICE_ID_MATCH_DEV_CLASS,    //match_flags
        0,    //idVendor
        0,    //idProduct
        0,    //bcdDevice_lo
        0,    //bcdDevice_hi
        USB_CLASS_HUB,    //bDeviceClass
        0,    //bDeviceSubClass
        0,    //bDeviceProtocol
        0,    //bInterfaceClass
        0,    //bInterfaceSubClass
        0,    //bInterfaceProtocol
        0,    //bInterfaceNumber
        0,    //driver_info
    },
    {
        USB_DEVICE_ID_MATCH_INT_CLASS,    //match_flags
        0,    //idVendor
        0,    //idProduct
        0,    //bcdDevice_lo
        0,    //bcdDevice_hi
        0,    //bDeviceClass
        0,    //bDeviceSubClass
        0,    //bDeviceProtocol
        USB_CLASS_HUB, //bInterfaceClass
        0,    //bInterfaceSubClass
        0,    //bInterfaceProtocol
        0,    //bInterfaceNumber
        0,    //driver_info
    },
    {
        0,    //match_flags
        0,    //idVendor
        0,    //idProduct
        0,    //bcdDevice_lo
        0,    //bcdDevice_hi
        0,    //bDeviceClass
        0,    //bDeviceSubClass
        0,    //bDeviceProtocol
        0,    //bInterfaceClass
        0,    //bInterfaceSubClass
        0,    //bInterfaceProtocol
        0,    //bInterfaceNumber
        0,    //driver_info
    },
};

static struct usb_driver hub_driver = {
    "hub",          //name
    hub_probe,      //probe
    hub_disconnect, //disconnect
    hub_pre_reset,  //pre_reset
    hub_post_reset, //post_reset
    hub_id_table,   //id_table
};

int usb_hub_init(void) {
    usb_register_driver(&hub_driver);

    return 0;
}

struct usb_hub* usb_get_hub(void) {
    return s_pHub;
}

void usb_hub_deinit(void)
{
    if(s_pHub)
    {
        if(s_pHub->intfdev)
        {
            hub_disconnect(to_usb_interface(s_pHub->intfdev));
        }
        if(s_pHub->hdev)
        {
            USB_FREE(s_pHub->hdev);
        }
    }

    
}

