/*
* @file hv_drv_UsbCoreHub.h
* @brief Header file of usb hub.
*
* @author HiView SoC Software Team
* @version 1.0.0
* @date 2022-06-15
*/

#ifndef __HV_DRV_USB_CORE_HUB_H
#define __HV_DRV_USB_CORE_HUB_H

#include "hv_drv_UsbCoreDevice.h"
#include "hv_drv_UsbCoreCommonUsb.h"
#include "hv_drv_UsbCoreCh11.h"

#include "hv_drv_UsbCoreUsb.h"

/**
 * struct usb port - kernel's representation of a usb port
 * @child: usb device attached to the port
 * @dev: generic device interface
 * @port_owner: port's owner
 * @peer: related usb2 and usb3 ports (share the same connector)
 * @req: default pm qos request for hubs without port power control
 * @connect_type: port's connect type
 * @location: opaque representation of platform connector location
 * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
 * @portnum: port index num based one
 * @is_superspeed cache super-speed status
 **/
struct usb_port {
    struct usb_device           *child;
    struct device               dev;
    enum usb_port_connect_type  connect_type;
    usb_port_location_t         location;
    u8                          portnum;
    unsigned int                is_superspeed:1;
};
#define to_usb_port(d)  container_of(d, struct usb_port, dev)

/**
 * struct usb_hub - representation of a usb hub device
 * @device: the "interface device"
 * @hdev: the usb hub
 * @status: buffer for status reports
 */
struct usb_hub {
    struct device       *intfdev;
    struct usb_device   *hdev;
    struct timer_list   event_timer;

    union {
        struct usb_hub_status   hub;
        struct usb_port_status  port;
    }*status;

    int         error;      /* last reported error */
    int         nerrors;    /* track consecutive errors */

    unsigned long       event_bits[1];  /* status change bitmask */
    unsigned long       change_bits[1]; /* ports with logical connect change */
    unsigned long       removed_bits[1];    /* ports with removed device present */
    unsigned long       wakeup_bits[1]; /* ports that have signaled remote wakeup */
    unsigned long       power_bits[1];  /* ports that are powered */
    unsigned long       child_usage_bits[1];    /* ports powered on for children */
    unsigned long       warm_reset_bits[1];     /* ports requesting warm reset recovery */
#if USB_MAXCHILDREN > 31    /* 8*sizeof(unsigned long)-1 */
#error event_bits[] is too short!
#endif

    struct usb_hub_descriptor   *descriptor;

    unsigned        limited_power:1;
    unsigned        quiescing:1;
    unsigned        disconnected:1;
    unsigned        in_reset:1;
    unsigned        quirk_check_port_auto_suspend:1;

    unsigned        has_indicators:1;
    u8              indicator[USB_MAXCHILDREN];

    struct usb_port **ports;
};


extern int usb_hub_create_port_device(struct usb_hub *hub, int port1);
extern void usb_hub_remove_port_device(struct usb_hub *hub, int port1);
extern int usb_hub_set_port_power(struct usb_device *hdev,
                                  struct usb_hub *hub,
                                  int port1, boolean set);
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
extern int usb_device_exist(struct usb_device *udev);
extern int hub_port_debounce(struct usb_hub *hub, int port1,
                             boolean must_be_connected);
extern int usb_clear_port_feature(struct usb_device *hdev,
                                  int port1, int feature);
extern void hub_port_check(struct usb_hub *hub);

struct usb_hub* usb_get_hub(void);

INLINE boolean hub_is_port_power_switchable(struct usb_hub *hub) {
    u16 hcs;

    if (!hub)
        return false;
    hcs = hub->descriptor->wHubCharacteristics;
    return ((hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
}

INLINE int hub_is_superspeed(struct usb_device *hdev) {
    return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
}

INLINE unsigned hub_power_on_good_delay(struct usb_hub *hub) {
    unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;

    /* Wait at least 100 msec for power to become stable */
    return USB_MAX(delay, 100U);
}

INLINE int hub_port_debounce_be_connected(struct usb_hub *hub,
        int port1) {
    return hub_port_debounce(hub, port1, true);
}

INLINE int hub_port_debounce_be_stable(struct usb_hub *hub,
                                       int port1) {
    return hub_port_debounce(hub, port1, false);
}

#endif
