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

#ifndef __HV_DRV_USB_CORE_HCD_H
#define __HV_DRV_USB_CORE_HCD_H

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

#define USB_HUB_ENABLE

#ifdef USB_HUB_ENABLE
#define MAX_TOPO_LEVEL  2
#else
#define MAX_TOPO_LEVEL  1
#endif

struct usb_hcd;

struct usb_tt;  /* do not support */

struct hc_driver {
    const char  *description;   /* "ehci-hcd" etc */
    const char  *product_desc;  /* product/vendor string */
    int flags;
#define HCD_MEMORY      0x0001      /* HC regs use memory (else I/O) */
#define HCD_LOCAL_MEM   0x0002      /* HC needs local memory */
#define HCD_SHARED      0x0004      /* Two (or more) usb_hcds share HW */
#define HCD_USB11       0x0010      /* USB 1.1 */
#define HCD_USB2        0x0020      /* USB 2.0 */
#define HCD_USB25       0x0030      /* Wireless USB 1.0 (USB 2.5)*/
#define HCD_USB3        0x0040      /* USB 3.0 */
#define HCD_MASK        0x0070      /* SPEED MASK */
#define HCD_BH          0x0100      /* URB complete in BH context */
    int (*start) (struct usb_hcd *hcd);
    void (*stop) (struct usb_hcd *hcd);
    int (*reset_device)(struct usb_hcd *, struct usb_device *);
    int (*get_frame_number) (struct usb_hcd *hcd);
    int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb);
    int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb, int status);
    int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
    int (*hub_control) (struct usb_hcd *hcd,
                        u16 typeReq, u16 wValue, u16 wIndex,
                        char *buf, u16 wLength);
    void (*endpoint_reset) (struct usb_hcd *hcd,
                            struct usb_host_endpoint *ep);
    int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt);
    void (*urb_free) (struct usb_hcd *hcd, struct urb *urb);
    void (*repower_and_reset) (struct usb_hcd *hcd);
    int (*hub_connect_state) (void);
};

struct usb_hcd {
    struct usb_bus self;    /* hcd is a bus */
    const char    *product_desc;    /* product/vendor string */
    struct urb *status_urb; /* the current status urb */

    const struct hc_driver *driver;

    unsigned long       flags;
#define HCD_FLAG_HW_ACCESSIBLE      0   /* at full power */
#define HCD_FLAG_POLL_RH            2   /* poll for rh status? */
#define HCD_FLAG_POLL_PENDING       3   /* status has changed? */
#define HCD_FLAG_WAKEUP_PENDING     4   /* root hub is resuming? */
#define HCD_FLAG_RH_RUNNING         5   /* root hub is running? */
#define HCD_FLAG_DEAD               6   /* controller has died? */

    int speed;              /* host controller speed */
    size_t hcd_priv_size;   /* hcd private data size */
    void *hcd_priv;         /* hcd private data, for <musb> etc. */
    int state;
#   define  __ACTIVE        0x01
#   define  __SUSPEND       0x04
#   define  __TRANSIENT     0x80

#   define  HC_STATE_HALT       0
#   define  HC_STATE_RUNNING    (__ACTIVE)
#   define  HC_STATE_QUIESCING  (__SUSPEND|__TRANSIENT|__ACTIVE)
#   define  HC_STATE_RESUMING   (__SUSPEND|__TRANSIENT)
#   define  HC_STATE_SUSPENDED  (__SUSPEND)

    unsigned int    power_budget;
    u8  authorized_default:1;

    /* support the new root-hub polling mechanism */
    u8  uses_new_polling:1;
    u8  has_tt:1;

    u8  rh_registered:1;   /* is root hub registered? */
    u8  rh_pollable:1;     /* may we poll the root hub */

    struct usb_hcd      *shared_hcd;
    struct usb_hcd      *primary_hcd;
};

INLINE int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) {
    return hcd->driver->flags & HCD_BH;
}

INLINE struct usb_hcd *bus_to_hcd(struct usb_bus *bus) {
    return container_of(bus, struct usb_hcd, self);
}

INLINE struct usb_bus *hcd_to_bus(struct usb_hcd *hcd) {
    return &hcd->self;
}

INLINE int is_root_hub(struct usb_device *udev) {
    return (udev->parent == NULL);
}


/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
#define DeviceRequest \
        ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
#define DeviceOutRequest \
        ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)

#define InterfaceRequest \
        ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)

#define EndpointRequest \
        ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define EndpointOutRequest \
        ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)

/* class requests from the USB 2.0 hub spec, table 11-15 */
/* GetBusState and SetHubDescriptor are optional, omitted */
#define ClearHubFeature     (0x2000 | USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature    (0x2300 | USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor    (0xa000 | USB_REQ_GET_DESCRIPTOR)
#define GetHubStatus        (0xa000 | USB_REQ_GET_STATUS)
#define GetPortStatus       (0xa300 | USB_REQ_GET_STATUS)
#define SetHubFeature       (0x2000 | USB_REQ_SET_FEATURE)
#define SetPortFeature      (0x2300 | USB_REQ_SET_FEATURE)


/*-------------------------------------------------------------------------*/

/* class requests from USB 3.0 hub spec, table 10-5 */
#define SetHubDepth         (0x3000 | HUB_SET_DEPTH)
#define GetPortErrorCount   (0x8000 | HUB_GET_PORT_ERR_COUNT)

/*
 * Generic bandwidth allocation constants/support
 */
#define FRAME_TIME_USECS    1000L
#define BitTime(bytecount)  (7 * 8 * bytecount / 6) /* with integer truncation */
/* Trying not to use worst-case bit-stuffing
 * of (7/6 * 8 * bytecount) = 9.33 * bytecount */
/* bytecount = data payload byte count */

#define NS_TO_US(ns)    DIV_ROUND_UP(ns, 1000L)
/* convert nanoseconds to microseconds, rounding up */

/*
 * Full/low speed bandwidth allocation constants/support.
 */
#define BW_HOST_DELAY       1000L       /* nanoseconds */
#define BW_HUB_LS_SETUP     333L        /* nanoseconds */
/* 4 full-speed bit times (est.) */

#define FRAME_TIME_BITS             12000L  /* frame = 1 millisecond */
#define FRAME_TIME_MAX_BITS_ALLOC   (90L * FRAME_TIME_BITS / 100L)
#define FRAME_TIME_MAX_USECS_ALLOC  (90L * FRAME_TIME_USECS / 100L)

/*
 * Ceiling [nano/micro]seconds (typical) for that many bytes at high speed
 * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
 * to preallocate bandwidth)
 */
#define USB2_HOST_DELAY 5   /* nsec, guess */
#define HS_NSECS(bytes) (((55 * 8 * 2083) \
            + (2083UL * (3 + BitTime(bytes))))/1000 \
            + USB2_HOST_DELAY)
#define HS_NSECS_ISO(bytes) (((38 * 8 * 2083) \
            + (2083UL * (3 + BitTime(bytes))))/1000 \
            + USB2_HOST_DELAY)
#define HS_USECS(bytes)     NS_TO_US(HS_NSECS(bytes))
#define HS_USECS_ISO(bytes) NS_TO_US(HS_NSECS_ISO(bytes))


extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev);
extern int usb_add_hcd(struct usb_hcd *hcd);
extern void usb_remove_hcd(struct usb_hcd *hcd);
extern void usb_destroy_hcd(struct usb_hcd *hcd);
extern void usb_hcd_reset_endpoint(struct usb_device *udev,
                                   struct usb_host_endpoint *ep);
extern int usb_hcd_submit_urb(struct urb *urb);
extern void usb_hcd_free_urb(struct urb *urb);
extern int usb_hcd_unlink_urb(struct urb *urb, int status);
extern int usb_get_configuration(struct usb_device *udev);
extern void usb_disconnect(struct usb_device **pdev);
extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd,
                                    struct urb *urb, int status);
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
extern void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb);
extern void usb_hcd_giveback_urb(struct usb_hcd *hcd,
                                 struct urb *urb, int status);
extern void usb_repower_and_reset_hcd(struct usb_device *dev);

/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
#define usb_dotoggle(dev, ep, out)  ((dev)->toggle[out] ^= (1 << (ep)))
#define usb_settoggle(dev, ep, out, bit) \
            ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | \
             ((bit) << (ep)))

#endif
